From 2952ccf47b80174880141a7ecfa122089f349b8d Mon Sep 17 00:00:00 2001 From: "Fred T. Hamster" Date: Fri, 13 Jan 2012 19:36:39 -0800 Subject: [PATCH] first check-in of feisty meow codebase. many things broken still due to recent changes, including the product being entirely renamed, coming from the collision of hoople2 and yeti codebases, etc etc. --- core/.cproject | 972 + core/.project | 82 + .../bookmark_tools/bookmark_tree.cpp | 485 + .../bookmark_tools/bookmark_tree.h | 144 + .../bookmark_tools/bookmark_version.rc | 46 + .../example_crash_file_for_marks.csv | 23 + .../bookmark_tools/js_marks_maker.cpp | 347 + .../bookmark_tools/link_parser.cpp | 496 + core/applications/bookmark_tools/makefile | 14 + .../bookmark_tools/marks_checker.cpp | 453 + .../bookmark_tools/marks_maker.cpp | 416 + .../bookmark_tools/marks_sorter.cpp | 232 + core/applications/bookmark_tools/version.ini | 6 + core/applications/bundler/bundle_creator.cpp | 1007 + core/applications/bundler/bundler_version.rc | 46 + core/applications/bundler/common_bundle.cpp | 139 + core/applications/bundler/common_bundle.h | 113 + core/applications/bundler/makefile | 27 + core/applications/bundler/makefile.stub | 24 + core/applications/bundler/manifest_format.txt | 23 + core/applications/bundler/unpacker_stub.cpp | 660 + core/applications/bundler/version.ini | 6 + .../example_application.cpp | 130 + .../applications/example_application/makefile | 13 + core/applications/makefile | 7 + core/applications/nechung/cgi_nechung.cpp | 155 + core/applications/nechung/example.txt | 76 + core/applications/nechung/makefile | 13 + core/applications/nechung/nechung.cpp | 119 + core/applications/nechung/nechung_oracle.cpp | 259 + core/applications/nechung/nechung_oracle.h | 87 + core/applications/nechung/nechung_version.rc | 46 + core/applications/nechung/readme | 7 + core/applications/nechung/test_nechung.sh | 15 + core/applications/nechung/version.ini | 6 + .../applications/utilities/await_app_exit.cpp | 157 + core/applications/utilities/bytedump.cpp | 108 + core/applications/utilities/checker.cpp | 179 + core/applications/utilities/ini_edit.cpp | 126 + core/applications/utilities/makefile | 19 + core/applications/utilities/mdate.cpp | 45 + core/applications/utilities/splitter.cpp | 141 + .../utilities/splitter_test_1.txt | 33 + .../utilities/splitter_test_2.txt | 8 + core/applications/utilities/util_version.rc | 46 + core/applications/utilities/version.ini | 6 + core/library/__build_configuration.h | 54 + core/library/__build_version.h | 29 + core/library/algorithms/makefile | 9 + core/library/algorithms/placeholder.cpp | 4 + core/library/algorithms/shell_sort.h | 65 + .../library/application/application_shell.cpp | 90 + core/library/application/application_shell.h | 101 + core/library/application/base_application.h | 67 + .../library/application/build_configuration.h | 59 + .../library/application/callstack_tracker.cpp | 272 + core/library/application/callstack_tracker.h | 156 + core/library/application/command_line.cpp | 486 + core/library/application/command_line.h | 216 + core/library/application/dll_root.cpp | 136 + core/library/application/hoople_main.h | 122 + core/library/application/hoople_service.cpp | 224 + core/library/application/hoople_service.h | 137 + core/library/application/launch_manager.cpp | 804 + core/library/application/launch_manager.h | 186 + core/library/application/makefile | 12 + core/library/application/memory_checker.cpp | 414 + core/library/application/memory_checker.h | 99 + core/library/application/redirecter.cpp | 506 + core/library/application/redirecter.h | 138 + core/library/application/shared_memory.cpp | 216 + core/library/application/shared_memory.h | 110 + .../application/singleton_application.cpp | 69 + .../application/singleton_application.h | 75 + core/library/application/window_classist.h | 147 + core/library/application/windoze_helper.cpp | 934 + core/library/application/windoze_helper.h | 246 + core/library/basis/array.h | 871 + core/library/basis/astring.cpp | 1096 + core/library/basis/astring.h | 468 + core/library/basis/base_string.h | 98 + core/library/basis/byte_array.h | 132 + core/library/basis/common_outcomes.cpp | 54 + core/library/basis/common_outcomes.h | 82 + core/library/basis/contracts.h | 188 + core/library/basis/definitions.h | 193 + core/library/basis/enhance_cpp.h | 101 + core/library/basis/environment.cpp | 87 + core/library/basis/environment.h | 55 + core/library/basis/functions.h | 157 + core/library/basis/gnu_header.h | 29 + core/library/basis/guards.cpp | 47 + core/library/basis/guards.h | 92 + core/library/basis/makefile | 10 + core/library/basis/mutex.cpp | 111 + core/library/basis/mutex.h | 137 + core/library/basis/outcome.h | 98 + core/library/basis/utf_conversion.cpp | 694 + core/library/basis/utf_conversion.h | 328 + .../application_configuration.cpp | 374 + .../configuration/application_configuration.h | 132 + core/library/configuration/config_watcher.cpp | 153 + core/library/configuration/config_watcher.h | 68 + core/library/configuration/configlet.cpp | 177 + core/library/configuration/configlet.h | 175 + core/library/configuration/configurable.h | 57 + .../configuration/configuration_list.cpp | 97 + .../configuration/configuration_list.h | 70 + core/library/configuration/configurator.cpp | 82 + core/library/configuration/configurator.h | 127 + .../configuration/ini_configurator.cpp | 357 + core/library/configuration/ini_configurator.h | 145 + core/library/configuration/ini_parser.cpp | 237 + core/library/configuration/ini_parser.h | 124 + core/library/configuration/ini_roller.cpp | 110 + core/library/configuration/ini_roller.h | 73 + core/library/configuration/makefile | 11 + .../library/configuration/section_manager.cpp | 113 + core/library/configuration/section_manager.h | 111 + core/library/configuration/system_values.cpp | 207 + core/library/configuration/system_values.h | 117 + .../configuration/table_configurator.cpp | 197 + .../configuration/table_configurator.h | 82 + .../configuration/variable_tokenizer.cpp | 403 + .../configuration/variable_tokenizer.h | 181 + core/library/crypto/blowfish_crypto.cpp | 281 + core/library/crypto/blowfish_crypto.h | 90 + core/library/crypto/makefile | 12 + core/library/crypto/rsa_crypto.cpp | 315 + core/library/crypto/rsa_crypto.h | 102 + core/library/crypto/ssl_init.cpp | 72 + core/library/crypto/ssl_init.h | 55 + core/library/filesystem/byte_filer.cpp | 236 + core/library/filesystem/byte_filer.h | 159 + core/library/filesystem/directory.cpp | 271 + core/library/filesystem/directory.h | 117 + core/library/filesystem/directory_tree.cpp | 948 + core/library/filesystem/directory_tree.h | 224 + core/library/filesystem/file_info.cpp | 230 + core/library/filesystem/file_info.h | 96 + core/library/filesystem/file_time.cpp | 146 + core/library/filesystem/file_time.h | 97 + core/library/filesystem/filename.cpp | 544 + core/library/filesystem/filename.h | 247 + core/library/filesystem/filename_list.cpp | 170 + core/library/filesystem/filename_list.h | 90 + core/library/filesystem/filename_tree.cpp | 52 + core/library/filesystem/filename_tree.h | 63 + core/library/filesystem/heavy_file_ops.cpp | 334 + core/library/filesystem/heavy_file_ops.h | 122 + core/library/filesystem/huge_file.cpp | 280 + core/library/filesystem/huge_file.h | 97 + core/library/filesystem/makefile | 11 + core/library/loggers/combo_logger.cpp | 86 + core/library/loggers/combo_logger.h | 59 + core/library/loggers/console_logger.cpp | 63 + core/library/loggers/console_logger.h | 79 + core/library/loggers/critical_events.cpp | 284 + core/library/loggers/critical_events.h | 205 + core/library/loggers/definitions_wx.h | 44 + core/library/loggers/eol_aware.h | 53 + core/library/loggers/file_logger.cpp | 375 + core/library/loggers/file_logger.h | 139 + core/library/loggers/filter_set.h | 76 + core/library/loggers/logging_filters.h | 41 + core/library/loggers/logging_macros.h | 102 + core/library/loggers/makefile | 10 + core/library/loggers/program_wide_logger.cpp | 35 + core/library/loggers/program_wide_logger.h | 84 + core/library/loggers/standard_log_base.h | 44 + core/library/makefile | 31 + core/library/mathematics/averager.h | 170 + core/library/mathematics/chaos.h | 113 + core/library/mathematics/double_plus.h | 101 + core/library/mathematics/makefile | 9 + core/library/mathematics/math_ops.h | 65 + core/library/mathematics/placeholder.cpp | 15 + core/library/nodes/list.cpp | 270 + core/library/nodes/list.h | 190 + core/library/nodes/makefile | 9 + core/library/nodes/node.cpp | 103 + core/library/nodes/node.h | 137 + core/library/nodes/packable_tree.cpp | 226 + core/library/nodes/packable_tree.h | 74 + core/library/nodes/path.cpp | 97 + core/library/nodes/path.h | 93 + core/library/nodes/symbol_tree.cpp | 227 + core/library/nodes/symbol_tree.h | 109 + core/library/nodes/tree.cpp | 391 + core/library/nodes/tree.h | 164 + .../processes/configured_applications.cpp | 326 + .../processes/configured_applications.h | 123 + core/library/processes/ethread.cpp | 328 + core/library/processes/ethread.h | 184 + core/library/processes/heartbeat.cpp | 102 + core/library/processes/heartbeat.h | 114 + core/library/processes/launch_process.cpp | 345 + core/library/processes/launch_process.h | 102 + core/library/processes/letter.cpp | 57 + core/library/processes/letter.h | 73 + core/library/processes/mail_stop.h | 72 + core/library/processes/mailbox.cpp | 262 + core/library/processes/mailbox.h | 131 + core/library/processes/makefile | 12 + core/library/processes/os_event.h | 53 + core/library/processes/post_office.cpp | 391 + core/library/processes/post_office.h | 115 + core/library/processes/process_control.cpp | 606 + core/library/processes/process_control.h | 105 + core/library/processes/process_entry.cpp | 86 + core/library/processes/process_entry.h | 67 + core/library/processes/rendezvous.cpp | 211 + core/library/processes/rendezvous.h | 76 + core/library/processes/safe_callback.cpp | 174 + core/library/processes/safe_callback.h | 152 + core/library/processes/safe_roller.cpp | 74 + core/library/processes/safe_roller.h | 76 + core/library/processes/state_machine.cpp | 505 + core/library/processes/state_machine.h | 348 + core/library/processes/thread_cabinet.cpp | 231 + core/library/processes/thread_cabinet.h | 104 + core/library/structures/amorph.h | 520 + core/library/structures/bit_vector.cpp | 317 + core/library/structures/bit_vector.h | 162 + core/library/structures/byte_hasher.h | 56 + core/library/structures/checksums.cpp | 98 + core/library/structures/checksums.h | 61 + core/library/structures/hash_table.h | 597 + core/library/structures/int_hash.h | 132 + core/library/structures/makefile | 10 + core/library/structures/matrix.h | 352 + core/library/structures/memory_limiter.cpp | 171 + core/library/structures/memory_limiter.h | 116 + core/library/structures/object_packers.cpp | 279 + core/library/structures/object_packers.h | 178 + core/library/structures/pointer_hash.h | 134 + core/library/structures/roller.h | 125 + core/library/structures/set.h | 315 + core/library/structures/stack.h | 208 + .../structures/static_memory_gremlin.cpp | 250 + .../structures/static_memory_gremlin.h | 199 + core/library/structures/string_array.h | 127 + core/library/structures/string_hash.h | 40 + core/library/structures/string_hasher.cpp | 87 + core/library/structures/string_hasher.h | 54 + core/library/structures/string_table.cpp | 110 + core/library/structures/string_table.h | 82 + core/library/structures/symbol_table.h | 467 + core/library/structures/unique_id.h | 111 + core/library/structures/version_record.cpp | 269 + core/library/structures/version_record.h | 218 + core/library/tests_algorithms/makefile | 12 + core/library/tests_algorithms/test_sorts.cpp | 86 + core/library/tests_basis/checkup.cpp | 52 + core/library/tests_basis/checkup.h | 33 + core/library/tests_basis/makefile | 14 + core/library/tests_basis/test_array.cpp | 929 + core/library/tests_basis/test_boilerplate.cpp | 59 + core/library/tests_basis/test_mutex.cpp | 301 + core/library/tests_basis/test_string.cpp | 1279 + .../tests_basis/test_system_preconditions.cpp | 169 + core/library/tests_configuration/makefile | 10 + .../test_section_manager.cpp | 124 + .../tests_configuration/test_tokenizer.cpp | 285 + core/library/tests_crypto/makefile | 12 + .../tests_crypto/test_blowfish_crypto.cpp | 194 + core/library/tests_crypto/test_rsa_crypto.cpp | 205 + core/library/tests_filesystem/makefile | 13 + .../tests_filesystem/test_byte_filer.cpp | 235 + .../tests_filesystem/test_directory.cpp | 99 + .../tests_filesystem/test_directory_tree.cpp | 207 + .../tests_filesystem/test_file_info.cpp | 111 + .../tests_filesystem/test_file_time.cpp | 107 + .../tests_filesystem/test_filename.cpp | 226 + .../tests_filesystem/test_huge_file.cpp | 110 + core/library/tests_mathematics/makefile | 12 + core/library/tests_mathematics/test_chaos.cpp | 99 + .../tests_mathematics/test_double_plus.cpp | 69 + .../tests_mathematics/test_math_ops.cpp | 60 + core/library/tests_nodes/makefile | 11 + core/library/tests_nodes/test_list.cpp | 140 + core/library/tests_nodes/test_node.cpp | 87 + .../tests_nodes/test_packable_tree.cpp | 204 + core/library/tests_nodes/test_symbol_tree.cpp | 93 + core/library/tests_nodes/test_tree.cpp | 307 + core/library/tests_structures/bogon.cpp | 53 + core/library/tests_structures/bogon.h | 49 + core/library/tests_structures/makefile | 14 + core/library/tests_structures/test_amorph.cpp | 536 + .../tests_structures/test_bit_vector.cpp | 163 + .../tests_structures/test_hash_table.cpp | 540 + .../tests_structures/test_int_hash.cpp | 553 + core/library/tests_structures/test_matrix.cpp | 401 + .../tests_structures/test_memory_limiter.cpp | 154 + .../library/tests_structures/test_packing.cpp | 248 + core/library/tests_structures/test_set.cpp | 74 + core/library/tests_structures/test_stack.cpp | 413 + .../tests_structures/test_string_table.cpp | 314 + .../tests_structures/test_symbol_table.cpp | 591 + .../tests_structures/test_unique_id.cpp | 82 + .../library/tests_structures/test_version.cpp | 86 + core/library/tests_textual/df_1.csv | 112 + core/library/tests_textual/makefile | 15 + .../tests_textual/test_byte_format.cpp | 171 + core/library/tests_textual/test_parse_csv.cpp | 176 + core/library/tests_textual/test_splitter.cpp | 96 + .../tests_textual/test_xml_generator.cpp | 122 + core/library/tests_timely/makefile | 12 + core/library/tests_timely/test_earth_time.cpp | 136 + core/library/textual/byte_formatter.cpp | 324 + core/library/textual/byte_formatter.h | 122 + core/library/textual/list_parsing.cpp | 279 + core/library/textual/list_parsing.h | 72 + core/library/textual/makefile | 10 + core/library/textual/parser_bits.cpp | 223 + core/library/textual/parser_bits.h | 124 + core/library/textual/string_convert.h | 70 + core/library/textual/string_manipulation.cpp | 351 + core/library/textual/string_manipulation.h | 88 + core/library/textual/xml_generator.cpp | 261 + core/library/textual/xml_generator.h | 129 + core/library/textual/xml_parser.cpp | 127 + core/library/textual/xml_parser.h | 82 + core/library/timely/earth_time.cpp | 406 + core/library/timely/earth_time.h | 241 + core/library/timely/makefile | 9 + core/library/timely/stopwatch.cpp | 103 + core/library/timely/stopwatch.h | 94 + core/library/timely/time_control.cpp | 98 + core/library/timely/time_control.h | 48 + core/library/timely/time_stamp.cpp | 160 + core/library/timely/time_stamp.h | 113 + core/library/timely/timer_driver.cpp | 453 + core/library/timely/timer_driver.h | 115 + core/library/unit_test/makefile | 9 + core/library/unit_test/unit_base.cpp | 395 + core/library/unit_test/unit_base.h | 186 + core/library/versions/makefile | 9 + core/library/versions/version.ini.example | 19 + core/library/versions/version_checker.cpp | 371 + core/library/versions/version_checker.h | 117 + core/library/versions/version_ini.cpp | 599 + core/library/versions/version_ini.h | 143 + core/makefile | 7 + core/tools/clam_tools/clamtools_version.rc | 46 + core/tools/clam_tools/makefile | 15 + core/tools/clam_tools/value_tagger.cpp | 1003 + core/tools/clam_tools/version.ini | 6 + core/tools/clam_tools/version_stamper.cpp | 131 + core/tools/clam_tools/vsts_version_fixer.cpp | 363 + core/tools/clam_tools/write_build_config.cpp | 434 + core/tools/clam_tools/write_build_config.h | 82 + core/tools/dependency_tool/Xfuncproto.h | 88 + core/tools/dependency_tool/Xos2defs.h | 74 + core/tools/dependency_tool/Xosdefs.h | 126 + core/tools/dependency_tool/Xw32defs.h | 72 + core/tools/dependency_tool/cppsetup.cpp | 230 + core/tools/dependency_tool/def.h | 195 + core/tools/dependency_tool/ifparser.cpp | 401 + core/tools/dependency_tool/ifparser.h | 70 + core/tools/dependency_tool/imakemdep.h | 718 + core/tools/dependency_tool/include.cpp | 348 + core/tools/dependency_tool/makedep.cpp | 785 + core/tools/dependency_tool/makedep_version.rc | 46 + core/tools/dependency_tool/makedepend.man | 368 + core/tools/dependency_tool/makedepend.txt | 264 + core/tools/dependency_tool/makefile | 17 + core/tools/dependency_tool/parse.cpp | 554 + core/tools/dependency_tool/pr.cpp | 134 + core/tools/dependency_tool/readme.txt | 55 + core/tools/dependency_tool/version.ini | 5 + core/tools/makefile | 7 + core/tools/simple_utilities/create_guid.cpp | 138 + core/tools/simple_utilities/makefile | 27 + core/tools/simple_utilities/playsound.cpp | 73 + core/tools/simple_utilities/short_path.cpp | 58 + .../simple_utilities/simple_utils_version.rc | 46 + core/tools/simple_utilities/sleep_ms.cpp | 77 + core/tools/simple_utilities/version.ini | 6 + core/tools/simple_utilities/zap_process.cpp | 123 + .../solution_solvers/check_resource_ids.sh | 92 + core/tools/solution_solvers/clean_vcxproj.sh | 31 + .../solution_solvers/extract_projects.sh | 13 + .../find_multilisted_projects_in_solutions.sh | 43 + .../solution_solvers/find_output_pathers.sh | 9 + .../solution_solvers/verify_project_file.sh | 83 + database/ansi/globe.ansi | 690 + database/ansi/uponce.ansi | 1 + database/ansi/upupaway.ansi | 2 + .../configuration/azureus/azureus_script.txt | 5 + database/configuration/boinc/etc/init.d/boinc | 215 + .../configuration/boinc/etc/sysconfig/boinc | 16 + database/configuration/boinc/readme.txt | 15 + database/configuration/boot/chos.conf | 62 + database/configuration/boot/lilo.conf | 13 + .../cron/bookmarks_builder.crontab | 9 + .../configuration/cron/chkrootkit.crontab | 12 + database/configuration/cron/doxygen.crontab | 6 + .../cron/linux_config_snarf.crontab | 9 + .../cron/mysql_db_backup.crontab | 7 + .../cron/nechung_fortune.crontab | 6 + .../configuration/cron/overload_check.crontab | 9 + .../configuration/cron/random_sound.crontab | 6 + .../configuration/cron/time_synch.crontab | 15 + .../configuration/cron/uptime_report.crontab | 8 + .../cron/zooty_archive_packer.crontab | 9 + .../database/sql/find_prims_created_by.sql | 13 + .../database/sql/replace_creatorid_fields.sql | 14 + .../usb_ram_disk/fstab_with_usb_drive.txt | 5 + .../devices/zip_disk/fstab_with_zip_disk | 6 + database/configuration/dhcp/dhcpd.conf | 19 + .../firewall/ipchains/firewalling.conf | 30 + .../configuration/firewall/iptables/iptables | 46 + .../init.d/team_city_startup_example | 66 + database/configuration/init.d/trac_startup | 69 + .../inputrc/symlink_completion.txt | 8 + database/configuration/logging/syslog.conf | 55 + database/configuration/nfs_mount/exports | 9 + database/configuration/nfs_mount/fstab | 8 + .../resources/official_saul_panzer_blank.oar | Bin 0 -> 642259 bytes .../quake3/fred_free_for_all.cfg | 171 + .../quake3/fred_t_hamster_maps.cfg | 75 + .../quake3/quake3_server_start.txt | 15 + .../example_rc.local_with_zooty_alerter | 21 + database/configuration/routing/gated.conf | 24 + .../configuration/routing/routing_setup.conf | 22 + database/configuration/samba/bartron.smb.conf | 297 + .../samba/smb_mount_in_fstab.txt | 27 + .../configuration/samba/thewheel.smb.conf | 315 + .../configuration/ssh/make_chroot_jail.sh | 505 + .../subversion/per_svn_repo/passwd | 9 + .../subversion/per_svn_repo/svnserve.conf | 47 + .../subversion/subversion_install.txt | 4 + database/configuration/subversion/svnserve | 92 + .../sysctl/sysctl.conf.real_time_mods | 4 + .../visual_studio/good_compare_string.txt | 8 + .../web_server/example_web_redirect_htaccess | 10 + database/configuration/webcam/reloader.html | 21 + .../configuration/webcam/webcamrc_example | 37 + .../configuration/x_win/start_fishtank.sh | 8 + database/configuration/x_win/start_xroach.sh | 9 + database/forms/check_register.odt | Bin 0 -> 12273 bytes database/fortunes.dat | 37187 ++++++++++++++++ database/patterns/vi/pattern_auto_replacement | 9 + ...attern_fix_commented_formal_parameters.txt | 6 + database/pictures/celtic_destiny14.jpg | Bin 0 -> 44866 bytes database/pictures/clamback.jpg | Bin 0 -> 68846 bytes database/pictures/clamblock2.jpg | Bin 0 -> 75805 bytes database/pictures/clams_tran.gif | Bin 0 -> 9479 bytes database/pictures/freakolitic42celts.jpg | Bin 0 -> 40851 bytes database/pictures/grywarky2.jpg | Bin 0 -> 32566 bytes database/pictures/home_front_b_5.jpg | Bin 0 -> 19125 bytes database/pictures/mongo_sox_ball4.jpg | Bin 0 -> 38338 bytes database/pictures/no_matches.html | 33 + database/pictures/no_matches.jpg | Bin 0 -> 35664 bytes database/sounds/woouoo.wav | Bin 0 -> 6908 bytes docs/bash_tools.html | 123 + docs/binaries_note.txt | 22 + docs/clam_manual/clam_docs.html | 2334 + docs/clam_manual/clam_root.html | 38 + docs/clam_manual/partial_cygwin_for_build.txt | 32 + docs/feisty_meow_dox.config | 1219 + docs/feisty_meow_quick_start.txt | 74 + docs/makefile | 17 + docs/perl_tools.html | 372 + .../text_examples/chinese_simplified_text.txt | 10 + docs/text_examples/korean_text.txt | 12 + docs/text_examples/readme.txt | 7 + docs/text_examples/russian_text.txt | 4 + docs/text_examples/tibetan_text.txt | 13 + graphiq/library/geometric/angle.h | 240 + graphiq/library/geometric/cartesian_objects.h | 74 + graphiq/library/geometric/circle.cpp | 77 + graphiq/library/geometric/circle.h | 71 + graphiq/library/geometric/ellipse.cpp | 120 + graphiq/library/geometric/ellipse.h | 72 + graphiq/library/geometric/line.h | 133 + graphiq/library/geometric/makefile | 9 + graphiq/library/geometric/math_bits.cpp | 52 + graphiq/library/geometric/math_bits.h | 85 + graphiq/library/geometric/point.h | 237 + graphiq/library/geometric/polygon.cpp | 50 + graphiq/library/geometric/polygon.h | 60 + graphiq/library/geometric/rectangle.h | 379 + .../library/geometric/screen_rectangle.cpp | 83 + graphiq/library/geometric/screen_rectangle.h | 95 + graphiq/library/geometric/triangle.cpp | 112 + graphiq/library/geometric/triangle.h | 64 + graphiq/library/geometric/warper.h | 291 + graphiq/library/makefile | 9 + graphiq/library/tests_geometric/makefile | 11 + .../library/tests_geometric/test_angle.cpp | 77 + .../library/tests_geometric/test_ellipse.cpp | 83 + .../library/tests_geometric/test_geometry.cpp | 95 + .../library/tests_geometric/test_point.cpp | 74 + .../library/tests_geometric/test_warper.cpp | 69 + graphiq/library/user_interface/menu_base.cpp | 180 + graphiq/library/user_interface/menu_base.h | 158 + graphiq/makefile | 7 + license.txt | 13 + makefile | 12 + octopi/applications/makefile | 7 + .../applications/transporter/find_missing.cpp | 331 + octopi/applications/transporter/makefile | 14 + .../applications/transporter/synch_files.cpp | 86 + .../applications/transporter/transporter.cpp | 379 + .../transporter/transporter_version.rc | 46 + octopi/applications/transporter/version.ini | 6 + octopi/library/cromp/cromp_client.cpp | 664 + octopi/library/cromp/cromp_client.h | 202 + octopi/library/cromp/cromp_common.cpp | 732 + octopi/library/cromp/cromp_common.h | 237 + octopi/library/cromp/cromp_security.cpp | 58 + octopi/library/cromp/cromp_security.h | 49 + octopi/library/cromp/cromp_server.cpp | 816 + octopi/library/cromp/cromp_server.h | 166 + octopi/library/cromp/cromp_transaction.cpp | 280 + octopi/library/cromp/cromp_transaction.h | 87 + octopi/library/cromp/makefile | 11 + octopi/library/makefile | 12 + octopi/library/octopus/entity_data_bin.cpp | 489 + octopi/library/octopus/entity_data_bin.h | 128 + octopi/library/octopus/entity_defs.cpp | 216 + octopi/library/octopus/entity_defs.h | 186 + octopi/library/octopus/identity_infoton.cpp | 82 + octopi/library/octopus/identity_infoton.h | 60 + octopi/library/octopus/identity_tentacle.cpp | 100 + octopi/library/octopus/identity_tentacle.h | 61 + octopi/library/octopus/infoton.cpp | 250 + octopi/library/octopus/infoton.h | 156 + octopi/library/octopus/makefile | 12 + octopi/library/octopus/octopus.cpp | 534 + octopi/library/octopus/octopus.h | 199 + octopi/library/octopus/tentacle.cpp | 190 + octopi/library/octopus/tentacle.h | 187 + octopi/library/octopus/tentacle_helper.h | 101 + octopi/library/octopus/unhandled_request.cpp | 69 + octopi/library/octopus/unhandled_request.h | 80 + octopi/library/sockets/base_address.h | 104 + octopi/library/sockets/internet_address.cpp | 470 + octopi/library/sockets/internet_address.h | 147 + octopi/library/sockets/machine_uid.cpp | 260 + octopi/library/sockets/machine_uid.h | 161 + octopi/library/sockets/makefile | 14 + octopi/library/sockets/range_limiter.cpp | 132 + octopi/library/sockets/range_limiter.h | 87 + octopi/library/sockets/raw_socket.cpp | 475 + octopi/library/sockets/raw_socket.h | 274 + octopi/library/sockets/sequence_tracker.cpp | 302 + octopi/library/sockets/sequence_tracker.h | 77 + octopi/library/sockets/socket_data.h | 82 + octopi/library/sockets/socket_minder.cpp | 597 + octopi/library/sockets/socket_minder.h | 166 + octopi/library/sockets/span_manager.cpp | 216 + octopi/library/sockets/span_manager.h | 92 + octopi/library/sockets/spocket.cpp | 756 + octopi/library/sockets/spocket.h | 245 + octopi/library/sockets/subnet_calculator.cpp | 152 + octopi/library/sockets/subnet_calculator.h | 68 + octopi/library/sockets/tcpip_definitions.h | 144 + octopi/library/sockets/tcpip_stack.cpp | 450 + octopi/library/sockets/tcpip_stack.h | 155 + octopi/library/sockets/throughput_counter.cpp | 135 + octopi/library/sockets/throughput_counter.h | 113 + octopi/library/synchronic/bundle_list.h | 31 + octopi/library/synchronic/list_manager.cpp | 208 + octopi/library/synchronic/list_manager.h | 104 + .../library/synchronic/list_synchronizer.cpp | 73 + octopi/library/synchronic/list_synchronizer.h | 53 + octopi/library/synchronic/makefile | 11 + octopi/library/synchronic/synchronizable.h | 101 + .../library/tentacles/encryption_infoton.cpp | 156 + octopi/library/tentacles/encryption_infoton.h | 99 + .../library/tentacles/encryption_tentacle.cpp | 186 + .../library/tentacles/encryption_tentacle.h | 100 + .../library/tentacles/encryption_wrapper.cpp | 106 + octopi/library/tentacles/encryption_wrapper.h | 89 + octopi/library/tentacles/entity_registry.cpp | 54 + octopi/library/tentacles/entity_registry.h | 105 + .../tentacles/file_transfer_infoton.cpp | 108 + .../library/tentacles/file_transfer_infoton.h | 87 + .../tentacles/file_transfer_tentacle.cpp | 770 + .../tentacles/file_transfer_tentacle.h | 183 + octopi/library/tentacles/key_repository.cpp | 86 + octopi/library/tentacles/key_repository.h | 76 + octopi/library/tentacles/login_tentacle.cpp | 101 + octopi/library/tentacles/login_tentacle.h | 75 + octopi/library/tentacles/makefile | 13 + .../library/tentacles/recursive_file_copy.cpp | 194 + .../library/tentacles/recursive_file_copy.h | 59 + octopi/library/tentacles/security_infoton.cpp | 107 + octopi/library/tentacles/security_infoton.h | 73 + .../tentacles/simple_entity_registry.cpp | 149 + .../tentacles/simple_entity_registry.h | 80 + .../library/tests_sockets/bcast_spocketer.cpp | 266 + .../library/tests_sockets/bcast_spocketer.h | 81 + octopi/library/tests_sockets/makefile | 16 + .../library/tests_sockets/spocket_tester.cpp | 316 + octopi/library/tests_sockets/spocket_tester.h | 76 + .../tests_sockets/t_sockets_version.rc | 46 + octopi/library/tests_sockets/test_address.cpp | 129 + .../tests_sockets/test_bcast_spocket.cpp | 262 + .../tests_sockets/test_enum_adapters.cpp | 65 + .../tests_sockets/test_sequence_tracker.cpp | 119 + .../tests_sockets/test_span_manager.cpp | 158 + octopi/library/tests_sockets/test_spocket.cpp | 257 + .../tests_sockets/test_ucast_spocket.cpp | 265 + octopi/library/tests_sockets/version.ini | 5 + octopi/makefile | 7 + production/3rdparty/curl/COPYING | 21 + production/3rdparty/curl/README | 79 + production/3rdparty/curl/bin/curl-config | 157 + production/3rdparty/curl/include/curl/curl.h | 1450 + .../3rdparty/curl/include/curl/curlver.h | 56 + production/3rdparty/curl/include/curl/easy.h | 81 + .../3rdparty/curl/include/curl/mprintf.h | 55 + production/3rdparty/curl/include/curl/multi.h | 341 + .../3rdparty/curl/include/curl/stdcheaders.h | 34 + production/3rdparty/curl/include/curl/types.h | 1 + production/3rdparty/curl/lib/libcurl-3.dll | Bin 0 -> 181248 bytes production/3rdparty/curl/lib/libcurl.a | Bin 0 -> 240292 bytes production/3rdparty/curl/lib/libcurl.dll.a | Bin 0 -> 29366 bytes .../3rdparty/openssl/include/openssl/aes.h | 127 + .../openssl/include/openssl/applink.c | 94 + .../3rdparty/openssl/include/openssl/asn1.h | 1233 + .../openssl/include/openssl/asn1_mac.h | 571 + .../3rdparty/openssl/include/openssl/asn1t.h | 886 + .../3rdparty/openssl/include/openssl/bio.h | 770 + .../openssl/include/openssl/blowfish.h | 127 + .../3rdparty/openssl/include/openssl/bn.h | 827 + .../3rdparty/openssl/include/openssl/buffer.h | 118 + .../3rdparty/openssl/include/openssl/cast.h | 105 + .../3rdparty/openssl/include/openssl/comp.h | 66 + .../3rdparty/openssl/include/openssl/conf.h | 253 + .../openssl/include/openssl/conf_api.h | 89 + .../3rdparty/openssl/include/openssl/crypto.h | 550 + .../3rdparty/openssl/include/openssl/des.h | 244 + .../openssl/include/openssl/des_old.h | 445 + .../3rdparty/openssl/include/openssl/dh.h | 229 + .../3rdparty/openssl/include/openssl/dsa.h | 279 + .../3rdparty/openssl/include/openssl/dso.h | 368 + .../3rdparty/openssl/include/openssl/dtls1.h | 212 + .../3rdparty/openssl/include/openssl/e_os2.h | 279 + .../3rdparty/openssl/include/openssl/ebcdic.h | 19 + .../3rdparty/openssl/include/openssl/ec.h | 518 + .../3rdparty/openssl/include/openssl/ecdh.h | 123 + .../3rdparty/openssl/include/openssl/ecdsa.h | 270 + .../3rdparty/openssl/include/openssl/engine.h | 785 + .../3rdparty/openssl/include/openssl/err.h | 318 + .../3rdparty/openssl/include/openssl/evp.h | 941 + .../3rdparty/openssl/include/openssl/hmac.h | 108 + .../3rdparty/openssl/include/openssl/idea.h | 100 + .../openssl/include/openssl/krb5_asn.h | 256 + .../3rdparty/openssl/include/openssl/kssl.h | 179 + .../3rdparty/openssl/include/openssl/lhash.h | 200 + .../3rdparty/openssl/include/openssl/md2.h | 91 + .../3rdparty/openssl/include/openssl/md4.h | 116 + .../3rdparty/openssl/include/openssl/md5.h | 116 + .../openssl/include/openssl/obj_mac.h | 3305 ++ .../openssl/include/openssl/objects.h | 1049 + .../3rdparty/openssl/include/openssl/ocsp.h | 614 + .../openssl/include/openssl/opensslconf.h | 206 + .../openssl/include/openssl/opensslv.h | 89 + .../openssl/include/openssl/ossl_typ.h | 174 + .../3rdparty/openssl/include/openssl/pem.h | 737 + .../3rdparty/openssl/include/openssl/pem2.h | 70 + .../3rdparty/openssl/include/openssl/pkcs12.h | 333 + .../3rdparty/openssl/include/openssl/pkcs7.h | 464 + .../openssl/include/openssl/pq_compat.h | 147 + .../3rdparty/openssl/include/openssl/pqueue.h | 95 + .../3rdparty/openssl/include/openssl/rand.h | 140 + .../3rdparty/openssl/include/openssl/rc2.h | 101 + .../3rdparty/openssl/include/openssl/rc4.h | 87 + .../3rdparty/openssl/include/openssl/ripemd.h | 103 + .../3rdparty/openssl/include/openssl/rsa.h | 429 + .../openssl/include/openssl/safestack.h | 1784 + .../3rdparty/openssl/include/openssl/sha.h | 199 + .../3rdparty/openssl/include/openssl/ssl.h | 1955 + .../3rdparty/openssl/include/openssl/ssl2.h | 268 + .../3rdparty/openssl/include/openssl/ssl23.h | 83 + .../3rdparty/openssl/include/openssl/ssl3.h | 555 + .../3rdparty/openssl/include/openssl/stack.h | 109 + .../3rdparty/openssl/include/openssl/store.h | 554 + .../openssl/include/openssl/symhacks.h | 383 + .../3rdparty/openssl/include/openssl/tls1.h | 274 + .../3rdparty/openssl/include/openssl/tmdiff.h | 93 + .../3rdparty/openssl/include/openssl/txt_db.h | 109 + .../3rdparty/openssl/include/openssl/ui.h | 381 + .../openssl/include/openssl/ui_compat.h | 83 + .../3rdparty/openssl/include/openssl/x509.h | 1340 + .../openssl/include/openssl/x509_vfy.h | 530 + .../3rdparty/openssl/include/openssl/x509v3.h | 759 + production/3rdparty/openssl/lib/libeay32.dll | Bin 0 -> 1036288 bytes production/3rdparty/openssl/lib/libeay32.lib | Bin 0 -> 657014 bytes production/3rdparty/openssl/lib/ssleay32.dll | Bin 0 -> 196608 bytes production/3rdparty/openssl/lib/ssleay32.lib | Bin 0 -> 48880 bytes production/3rdparty/openssl/version.txt | 4 + production/3rdparty/zlib/include/zconf.h | 332 + production/3rdparty/zlib/include/zlib.h | 1357 + production/3rdparty/zlib/lib/zdll.exp | Bin 0 -> 6109 bytes production/3rdparty/zlib/lib/zdll.lib | Bin 0 -> 10590 bytes production/3rdparty/zlib/lib/zlib.def | 60 + production/3rdparty/zlib/lib/zlib.lib | Bin 0 -> 104538 bytes production/3rdparty/zlib/lib/zlib1.dll | Bin 0 -> 59904 bytes production/codescan.ini | 44 + production/feisty_meow_config.ini | 90 + production/makefile | 7 + production/msys/bin/awk | 8 + production/msys/bin/basename.exe | Bin 0 -> 16896 bytes production/msys/bin/bash.exe | Bin 0 -> 911646 bytes production/msys/bin/bunzip2 | 8 + production/msys/bin/bzip2.exe | Bin 0 -> 30720 bytes production/msys/bin/cat.exe | Bin 0 -> 19456 bytes production/msys/bin/chmod.exe | Bin 0 -> 32256 bytes production/msys/bin/cls | 9 + production/msys/bin/clsb | 9 + production/msys/bin/cmd | 8 + production/msys/bin/cmp.exe | Bin 0 -> 22528 bytes production/msys/bin/comm.exe | Bin 0 -> 18944 bytes production/msys/bin/cp.exe | Bin 0 -> 53248 bytes production/msys/bin/cut.exe | Bin 0 -> 26112 bytes production/msys/bin/date.exe | Bin 0 -> 51200 bytes production/msys/bin/diff.exe | Bin 0 -> 100352 bytes production/msys/bin/diff3.exe | Bin 0 -> 23552 bytes production/msys/bin/dirname.exe | Bin 0 -> 16896 bytes production/msys/bin/echo | 8 + production/msys/bin/egrep | 8 + production/msys/bin/env.exe | Bin 0 -> 16384 bytes production/msys/bin/ex | 8 + production/msys/bin/expr.exe | Bin 0 -> 79360 bytes production/msys/bin/false.exe | Bin 0 -> 11264 bytes production/msys/bin/fgrep | 8 + production/msys/bin/file.exe | Bin 0 -> 77312 bytes production/msys/bin/find.exe | Bin 0 -> 127488 bytes production/msys/bin/fold.exe | Bin 0 -> 19456 bytes production/msys/bin/ftp | 8 + production/msys/bin/gawk.exe | Bin 0 -> 280576 bytes production/msys/bin/grep.exe | Bin 0 -> 78848 bytes production/msys/bin/gunzip | 8 + production/msys/bin/gzip.exe | Bin 0 -> 51712 bytes production/msys/bin/head.exe | Bin 0 -> 28160 bytes production/msys/bin/id.exe | Bin 0 -> 19968 bytes production/msys/bin/info.exe | Bin 0 -> 154112 bytes production/msys/bin/infokey.exe | Bin 0 -> 20480 bytes production/msys/bin/install-info.exe | Bin 0 -> 28672 bytes production/msys/bin/install.exe | Bin 0 -> 59392 bytes production/msys/bin/install.exe.manifest | 10 + production/msys/bin/less.exe | Bin 0 -> 157931 bytes production/msys/bin/libW11.dll | Bin 0 -> 49152 bytes production/msys/bin/ln.exe | Bin 0 -> 27136 bytes production/msys/bin/lnkcnv | 19 + production/msys/bin/ls.exe | Bin 0 -> 89088 bytes production/msys/bin/lzma.exe | Bin 0 -> 189952 bytes production/msys/bin/m4.exe | Bin 0 -> 125440 bytes production/msys/bin/make.exe | Bin 0 -> 155136 bytes production/msys/bin/makeinfo.exe | Bin 0 -> 224256 bytes production/msys/bin/md5sum.exe | Bin 0 -> 32768 bytes production/msys/bin/mkdir.exe | Bin 0 -> 27136 bytes production/msys/bin/mktemp.exe | Bin 0 -> 8192 bytes production/msys/bin/mount | 197 + production/msys/bin/mount.exe | Bin 0 -> 29218 bytes production/msys/bin/msys-1.0.dll | Bin 0 -> 793600 bytes production/msys/bin/msys-bz2-1.dll | Bin 0 -> 59392 bytes production/msys/bin/msysinfo | 29 + production/msys/bin/msysmnt.exe | Bin 0 -> 751352 bytes production/msys/bin/mv.exe | Bin 0 -> 61440 bytes production/msys/bin/od.exe | Bin 0 -> 39936 bytes production/msys/bin/patch.exe | Bin 0 -> 75776 bytes production/msys/bin/patch.exe.manifest | 10 + production/msys/bin/printf | 8 + production/msys/bin/ps.exe | Bin 0 -> 732346 bytes production/msys/bin/pwd | 8 + production/msys/bin/pwd.exe | Bin 0 -> 126976 bytes production/msys/bin/rm.exe | Bin 0 -> 38400 bytes production/msys/bin/rmdir.exe | Bin 0 -> 17408 bytes production/msys/bin/rvi | 8 + production/msys/bin/rview | 8 + production/msys/bin/rvim | 8 + production/msys/bin/rxvt.exe | Bin 0 -> 211968 bytes production/msys/bin/sed.exe | Bin 0 -> 49152 bytes production/msys/bin/sh.exe | Bin 0 -> 911646 bytes production/msys/bin/short_path.exe | Bin 0 -> 118784 bytes production/msys/bin/sleep.exe | Bin 0 -> 17920 bytes production/msys/bin/sort.exe | Bin 0 -> 49152 bytes production/msys/bin/split.exe | Bin 0 -> 29184 bytes production/msys/bin/start | 8 + production/msys/bin/stty.exe | Bin 0 -> 44544 bytes production/msys/bin/tail.exe | Bin 0 -> 39936 bytes production/msys/bin/tar.exe | Bin 0 -> 158720 bytes production/msys/bin/tee.exe | Bin 0 -> 17408 bytes production/msys/bin/texi2dvi | 657 + production/msys/bin/texindex.exe | Bin 0 -> 11776 bytes production/msys/bin/touch.exe | Bin 0 -> 38400 bytes production/msys/bin/tr.exe | Bin 0 -> 33280 bytes production/msys/bin/true.exe | Bin 0 -> 11264 bytes production/msys/bin/umount | 99 + production/msys/bin/uname.exe | Bin 0 -> 17920 bytes production/msys/bin/uniq.exe | Bin 0 -> 24576 bytes production/msys/bin/unzip.exe | Bin 0 -> 152576 bytes production/msys/bin/vi | 8 + production/msys/bin/view | 8 + production/msys/bin/vim.exe | Bin 0 -> 551936 bytes production/msys/bin/wc.exe | Bin 0 -> 20992 bytes production/msys/bin/which | 66 + production/msys/bin/xargs.exe | Bin 0 -> 25088 bytes production/msys/bin/zap_process.exe | Bin 0 -> 348160 bytes production/msys/bin/zip.exe | Bin 0 -> 120832 bytes production/msys/etc/config.site | 21 + production/msys/etc/fstab | 0 production/msys/etc/fstab.sample | 17 + production/msys/etc/inputrc.default | 50 + production/msys/etc/profile | 60 + production/msys/etc/termcap | 274 + production/msys/m.ico | Bin 0 -> 2238 bytes production/msys/msys.bat | 186 + production/msys/msys.ico | Bin 0 -> 37758 bytes production/msys/msys_green.ico | Bin 0 -> 37758 bytes production/paths.ini | 3 + production/proto_build.ini | 84 + .../autotesting_manifest.txt | 148 + .../create_autotesting_bundle.sh | 16 + .../setup_src/autotesting_tools/makefile | 11 + .../setup_src/autotesting_tools/paths.ini | 3 + .../setup_src/bundle_example/create_bundle.sh | 10 + .../bundle_example/example_manifest.txt | 223 + production/setup_src/bundle_example/makefile | 16 + production/setup_src/makefile | 13 + .../create_whole_build_pack.sh | 23 + .../setup_src/whole_build_package/makefile | 14 + .../whole_build_manifest.txt | 88 + readme.txt | 38 + scripts/archival/pack_3rdparty.sh | 16 + scripts/archival/pack_hoople1.sh | 18 + scripts/archival/pack_hoople2.sh | 18 + scripts/archival/pack_msysbins.sh | 16 + scripts/archival/pack_yeti.sh | 30 + scripts/archival/shared_snarfer.pl | 488 + scripts/archival/snarf_feisty_meow.pl | 67 + scripts/archival/snarf_hoople.pl | 61 + scripts/archival/snarf_hoople1.pl | 61 + scripts/archival/snarf_linux_config.pl | 91 + scripts/archival/snarf_notes.pl | 60 + scripts/archival/snarf_opensim.pl | 59 + scripts/archival/snarf_user.pl | 54 + scripts/archival/snarf_yeti.pl | 45 + scripts/archival/unsnarf.pl | 42 + scripts/bashisms/fred_techniques.txt | 62 + scripts/bashisms/qs_handy_unix_examples.sh | 220 + scripts/bookmarks/create_marks.sh | 50 + scripts/buildor/build_msproj.sh | 8 + scripts/buildor/buildor_add_sentinel.sh | 65 + scripts/buildor/buildor_count_code.sh | 17 + .../buildor/buildor_find_active_includes.sh | 16 + scripts/buildor/buildor_find_bad.sh | 30 + .../buildor/buildor_find_unused_includes.sh | 7 + scripts/buildor/buildor_strip_code.sh | 25 + scripts/buildor/buildor_writable_code.sh | 20 + scripts/buildor/check_makefile.sh | 44 + scripts/buildor/cygwinize.sh | 7 + scripts/buildor/find_in_code_ignore_svn.sh | 4 + scripts/buildor/find_mangled.sh | 6 + scripts/buildor/find_one_word_comments.sh | 26 + scripts/buildor/fix_project_references.py | 644 + scripts/buildor/manifester.sh | 12 + scripts/buildor/seek_all_source.sh | 58 + scripts/buildor/stdbuild.sh | 38 + scripts/buildor/synch_build.pl | 29 + scripts/buildor/synch_to_build.sh | 35 + scripts/buildor/upgrade_hoople.sh | 121 + scripts/buildor/version_utils.sh | 46 + scripts/cgi/call_version_utils.sh | 15 + scripts/cgi/cgi_cat.pl | 30 + scripts/cgi/cgi_display.pl | 29 + scripts/cgi/cgi_nechung.cgi | 3 + scripts/cgi/cgi_show_file_date.sh | 13 + scripts/cgi/cgi_show_referer.sh | 18 + scripts/cgi/count.cgi | 85 + scripts/cgi/grumble_chkrootkit.cgi | 67 + scripts/cgi/nechung.cgi | 5 + scripts/cgi/prepare_cgi_bin_for_apache.sh | 28 + scripts/clam/badness_catcher.sh | 13 + scripts/clam/clam_parms.ini | 7 + scripts/clam/cpp/blank_target.c | 2 + scripts/clam/cpp/buildor_gen_deps.sh | 534 + scripts/clam/cpp/get_version.sh | 33 + scripts/clam/cpp/ms_manifest.sh | 25 + .../cpp/ms_manifests/security_manifest.txt | 18 + .../security_manifest_administrator.txt | 11 + ...ecurity_manifest_administrator_with_ui.txt | 11 + .../security_manifest_highest.txt | 11 + .../ms_manifests/security_manifest_normal.txt | 11 + scripts/clam/cpp/ms_root_dir.sh | 18 + scripts/clam/cpp/postconditions.sh | 66 + scripts/clam/cpp/preconditions.sh | 87 + scripts/clam/cpp/rc_name.sh | 4 + scripts/clam/cpp/rebuild_oldies.sh | 12 + scripts/clam/cpp/rules.def | 737 + scripts/clam/cpp/variables.def | 975 + scripts/clam/cpp/vis_stu/trap_new.addin | 7 + scripts/clam/cpp/vis_stu/untrap_new.addin | 15 + scripts/clam/dotnet/csharper.sh | 8 + scripts/clam/dotnet/postconditions.sh | 107 + scripts/clam/dotnet/preconditions.sh | 41 + scripts/clam/dotnet/readme.txt | 9 + scripts/clam/dotnet/rules.def | 262 + scripts/clam/dotnet/variables.def | 514 + scripts/clam/exit_make.sh | 8 + scripts/clam/rules.def | 169 + scripts/clam/sound_play.sh | 61 + scripts/clam/variables.def | 267 + scripts/core/array_sifter.sh | 99 + scripts/core/bootstrap_shells.sh | 57 + scripts/core/byejob.sh | 45 + scripts/core/byemessage.sh | 41 + scripts/core/common_aliases.txt | 48 + scripts/core/create_tempdir.sh | 39 + scripts/core/date_stringer.sh | 10 + scripts/core/functions.sh | 116 + scripts/core/generate_aliases.pl | 162 + scripts/core/get_parent_dir.sh | 11 + scripts/core/goodbye.sh | 11 + scripts/core/i.sh | 28 + scripts/core/inc_num.pl | 59 + scripts/core/lightweight_unix_login.sh | 8 + scripts/core/profile.sh | 54 + scripts/core/random_iter.sh | 26 + scripts/core/sh_aliases.txt | 123 + scripts/core/shell_header.txt | 14 + scripts/core/unix_login.sh | 50 + scripts/core/unter_alia.sh | 71 + scripts/core/variables.sh | 169 + scripts/custom/c_common_aliases.txt | 8 + scripts/custom/c_variables.sh | 52 + scripts/custom/uva_profile_additions.txt | 11 + scripts/database/backup_mysql_dbs.sh | 5 + scripts/database/call_movie_seeker.sh | 30 + scripts/database/call_movie_stripper.sh | 11 + scripts/database/call_show_stripper.sh | 28 + scripts/database/movie_seeker.sh | 14 + scripts/database/movie_stripper.sh | 9 + scripts/database/show_stripper.sh | 11 + scripts/demo/OS_crusher.bat | 7 + scripts/demo/OS_crusher.sh | 7 + scripts/demo/wma2mp3.sh | 67 + scripts/devices/zaurus_usb.sh | 5 + scripts/email/dump_email_headers.sh | 11 + scripts/email/move_spams_and_check.sh | 35 + scripts/email/scan_spam.sh | 42 + scripts/email/scavenge_emails_from_ldif.sh | 11 + scripts/examples/bashrc_grover | 8 + scripts/examples/bashrc_root | 4 + scripts/examples/bashrc_user | 22 + scripts/examples/bashrc_user_windoz | 25 + scripts/examples/example_registry_ops.sh | 15 + scripts/files/change_endings.pl | 58 + scripts/files/change_prefix.sh | 19 + scripts/files/count_files_in_subdirs.sh | 7 + scripts/files/dir_zero_entries.sh | 28 + scripts/files/dos_perm.sh | 20 + scripts/files/easy_perm.sh | 21 + scripts/files/exe_perm.sh | 21 + scripts/files/filedump.pl | 117 + scripts/files/filename_helper.pl | 433 + scripts/files/find_bad_protection.sh | 54 + scripts/files/find_non_owned.sh | 14 + scripts/files/fix_game_perms.sh | 8 + scripts/files/harsh_perm.sh | 20 + scripts/files/list_dupes.sh | 24 + scripts/files/normal_perm.sh | 21 + scripts/files/order_file_dates.sh | 6 + scripts/files/phrase_replacer.py | 257 + scripts/files/prefix_renamer.sh | 16 + scripts/files/recursive_renlower.sh | 7 + scripts/files/recursive_whack_dupes.sh | 51 + scripts/files/remove_bracket_id.sh | 13 + scripts/files/renlower.pl | 56 + scripts/files/safedel.pl | 135 + scripts/files/show_directory_listing.sh | 51 + scripts/files/show_links.sh | 7 + scripts/files/strip_html_code.sh | 14 + scripts/files/summing_dir.pl | 109 + scripts/files/whack_dupes.sh | 68 + scripts/files/whackem.pl | 64 + scripts/files/zap_the_dir.pl | 118 + scripts/files/zapdirs.pl | 56 + scripts/generator/bootstrap_build.sh | 249 + scripts/generator/build_variables.sh | 276 + scripts/generator/next_version.sh | 96 + scripts/generator/vis_stu_vars.sh | 74 + scripts/generator/whack_build.sh | 68 + scripts/jenkins/empty_parsing_rules.txt | 1 + scripts/jenkins/hoople_unit_parsing_rules.txt | 13 + scripts/legacy/template.pl | 596 + scripts/makefile | 22 + scripts/multimedia/pic_importer.sh | 7 + scripts/multimedia/play_random.sh | 31 + scripts/multimedia/randomly_play.sh | 11 + scripts/multimedia/sound_play.sh | 32 + scripts/notes/info_overload_report.sh | 36 + scripts/opensim/backup_opensim.sh | 15 + scripts/opensim/backup_osgrid.sh | 15 + scripts/opensim/find_opensim_ports.sh | 12 + scripts/opensim/indent_lsl.sh | 4 + scripts/opensim/list_sims.sh | 61 + scripts/opensim/maybe_restart_opensim.sh | 21 + scripts/opensim/opensim_utils.sh | 90 + scripts/opensim/run_opensim.sh | 16 + scripts/opensim/run_osgrid.sh | 13 + scripts/opensim/scan_opensim_log.sh | 21 + scripts/opensim/stop_opensim.sh | 10 + scripts/opensim/stop_osgrid.sh | 9 + scripts/opensim/zap_opensim_main.sh | 12 + scripts/processes/pskill.sh | 24 + scripts/processes/runner.pl | 64 + scripts/rev_control/checkin.sh | 84 + scripts/rev_control/cvs_fix.pl | 46 + scripts/rev_control/cvs_importer.sh | 81 + scripts/rev_control/fix_svn_binaries.sh | 22 + scripts/rev_control/getme.sh | 91 + scripts/rev_control/rev_control.sh | 83 + scripts/rev_control/svn_set_binary.sh | 9 + scripts/rev_control/update_these.sh | 14 + scripts/rip_burn/dvd_image.sh | 33 + scripts/schedule/cal.sh | 27 + scripts/schedule/generate_reminders.pl | 179 + scripts/schedule/start_calendar.sh | 4 + scripts/schedule/time_control.sh | 121 + scripts/security/check_website.sh | 6 + scripts/security/start_tunnels.sh | 46 + scripts/security/tell_zooty_our_ip.sh | 48 + scripts/security/y2038_check.pl | 9 + scripts/system/check_mount.sh | 12 + scripts/system/find_dhcp_unassigned.sh | 12 + scripts/system/machine-update.pl | 1117 + scripts/system/osx_cd_mounter.sh | 29 + scripts/system/redo_nvidia_driver.sh | 135 + scripts/system/write_uptime_report.sh | 6 + scripts/text/add_cr.pl | 75 + scripts/text/cpdiff.pl | 28 + scripts/text/cpdiffnow.pl | 28 + scripts/text/csv_compare.sh | 52 + scripts/text/csv_to_array.sh | 61 + scripts/text/diff_lib.pl | 537 + scripts/text/differ.pl | 125 + scripts/text/list_to_html.pl | 154 + scripts/text/nechung_signature.sh | 7 + scripts/text/new_sig.pl | 43 + scripts/text/smoove_lists.sh | 58 + scripts/text/strip_cr.pl | 75 + scripts/text/text_to_url.pl | 147 + scripts/tty/findterm.sh | 11 + scripts/tty/keep_awake.sh | 9 + scripts/tty/keep_awake_process.sh | 18 + scripts/tty/label_terminal_with_infos.sh | 17 + scripts/tty/lockup.sh | 38 + scripts/tty/set_term_title.sh | 14 + scripts/tty/sftp.sh | 5 + scripts/tty/ssh.sh | 12 + scripts/users/find_user.sh | 24 + scripts/users/findme.sh | 6 + scripts/web/javascript/dtree/dtree.css | 40 + scripts/web/javascript/dtree/dtree.js | 415 + scripts/web/javascript/gruntose/gruntomote.js | 28 + scripts/web/javascript/gruntose/launchers.js | 21 + scripts/web/javascript/gruntose/norse_date.js | 59 + scripts/web/javascript/gruntose/show_file.js | 30 + scripts/web/javascript/jquery/jquery-1.5.1.js | 8316 ++++ scripts/winders/exploder.sh | 20 + scripts/winders/find_64bit_bins.sh | 1 + scripts/winders/locate_function_in_lib.sh | 15 + scripts/winders/zap_msbuilds.sh | 6 + scripts/x_win/get_x_auth.sh | 19 + scripts/x_win/make_display.sh | 7 + scripts/x_win/webcam_snagger.sh | 43 + scripts/x_win/xtfont.sh | 154 + 1075 files changed, 197042 insertions(+) create mode 100644 core/.cproject create mode 100644 core/.project create mode 100644 core/applications/bookmark_tools/bookmark_tree.cpp create mode 100644 core/applications/bookmark_tools/bookmark_tree.h create mode 100644 core/applications/bookmark_tools/bookmark_version.rc create mode 100644 core/applications/bookmark_tools/example_crash_file_for_marks.csv create mode 100644 core/applications/bookmark_tools/js_marks_maker.cpp create mode 100644 core/applications/bookmark_tools/link_parser.cpp create mode 100644 core/applications/bookmark_tools/makefile create mode 100644 core/applications/bookmark_tools/marks_checker.cpp create mode 100644 core/applications/bookmark_tools/marks_maker.cpp create mode 100644 core/applications/bookmark_tools/marks_sorter.cpp create mode 100644 core/applications/bookmark_tools/version.ini create mode 100644 core/applications/bundler/bundle_creator.cpp create mode 100644 core/applications/bundler/bundler_version.rc create mode 100644 core/applications/bundler/common_bundle.cpp create mode 100644 core/applications/bundler/common_bundle.h create mode 100644 core/applications/bundler/makefile create mode 100644 core/applications/bundler/makefile.stub create mode 100644 core/applications/bundler/manifest_format.txt create mode 100644 core/applications/bundler/unpacker_stub.cpp create mode 100644 core/applications/bundler/version.ini create mode 100644 core/applications/example_application/example_application.cpp create mode 100644 core/applications/example_application/makefile create mode 100644 core/applications/makefile create mode 100644 core/applications/nechung/cgi_nechung.cpp create mode 100644 core/applications/nechung/example.txt create mode 100644 core/applications/nechung/makefile create mode 100644 core/applications/nechung/nechung.cpp create mode 100644 core/applications/nechung/nechung_oracle.cpp create mode 100644 core/applications/nechung/nechung_oracle.h create mode 100644 core/applications/nechung/nechung_version.rc create mode 100644 core/applications/nechung/readme create mode 100644 core/applications/nechung/test_nechung.sh create mode 100644 core/applications/nechung/version.ini create mode 100644 core/applications/utilities/await_app_exit.cpp create mode 100644 core/applications/utilities/bytedump.cpp create mode 100644 core/applications/utilities/checker.cpp create mode 100644 core/applications/utilities/ini_edit.cpp create mode 100644 core/applications/utilities/makefile create mode 100644 core/applications/utilities/mdate.cpp create mode 100644 core/applications/utilities/splitter.cpp create mode 100644 core/applications/utilities/splitter_test_1.txt create mode 100644 core/applications/utilities/splitter_test_2.txt create mode 100644 core/applications/utilities/util_version.rc create mode 100644 core/applications/utilities/version.ini create mode 100644 core/library/__build_configuration.h create mode 100644 core/library/__build_version.h create mode 100644 core/library/algorithms/makefile create mode 100644 core/library/algorithms/placeholder.cpp create mode 100644 core/library/algorithms/shell_sort.h create mode 100644 core/library/application/application_shell.cpp create mode 100644 core/library/application/application_shell.h create mode 100644 core/library/application/base_application.h create mode 100644 core/library/application/build_configuration.h create mode 100644 core/library/application/callstack_tracker.cpp create mode 100644 core/library/application/callstack_tracker.h create mode 100644 core/library/application/command_line.cpp create mode 100644 core/library/application/command_line.h create mode 100644 core/library/application/dll_root.cpp create mode 100644 core/library/application/hoople_main.h create mode 100644 core/library/application/hoople_service.cpp create mode 100644 core/library/application/hoople_service.h create mode 100644 core/library/application/launch_manager.cpp create mode 100644 core/library/application/launch_manager.h create mode 100644 core/library/application/makefile create mode 100644 core/library/application/memory_checker.cpp create mode 100644 core/library/application/memory_checker.h create mode 100644 core/library/application/redirecter.cpp create mode 100644 core/library/application/redirecter.h create mode 100644 core/library/application/shared_memory.cpp create mode 100644 core/library/application/shared_memory.h create mode 100644 core/library/application/singleton_application.cpp create mode 100644 core/library/application/singleton_application.h create mode 100644 core/library/application/window_classist.h create mode 100644 core/library/application/windoze_helper.cpp create mode 100644 core/library/application/windoze_helper.h create mode 100644 core/library/basis/array.h create mode 100644 core/library/basis/astring.cpp create mode 100644 core/library/basis/astring.h create mode 100644 core/library/basis/base_string.h create mode 100644 core/library/basis/byte_array.h create mode 100644 core/library/basis/common_outcomes.cpp create mode 100644 core/library/basis/common_outcomes.h create mode 100644 core/library/basis/contracts.h create mode 100644 core/library/basis/definitions.h create mode 100644 core/library/basis/enhance_cpp.h create mode 100644 core/library/basis/environment.cpp create mode 100644 core/library/basis/environment.h create mode 100644 core/library/basis/functions.h create mode 100644 core/library/basis/gnu_header.h create mode 100644 core/library/basis/guards.cpp create mode 100644 core/library/basis/guards.h create mode 100644 core/library/basis/makefile create mode 100644 core/library/basis/mutex.cpp create mode 100644 core/library/basis/mutex.h create mode 100644 core/library/basis/outcome.h create mode 100644 core/library/basis/utf_conversion.cpp create mode 100644 core/library/basis/utf_conversion.h create mode 100644 core/library/configuration/application_configuration.cpp create mode 100644 core/library/configuration/application_configuration.h create mode 100644 core/library/configuration/config_watcher.cpp create mode 100644 core/library/configuration/config_watcher.h create mode 100644 core/library/configuration/configlet.cpp create mode 100644 core/library/configuration/configlet.h create mode 100644 core/library/configuration/configurable.h create mode 100644 core/library/configuration/configuration_list.cpp create mode 100644 core/library/configuration/configuration_list.h create mode 100644 core/library/configuration/configurator.cpp create mode 100644 core/library/configuration/configurator.h create mode 100644 core/library/configuration/ini_configurator.cpp create mode 100644 core/library/configuration/ini_configurator.h create mode 100644 core/library/configuration/ini_parser.cpp create mode 100644 core/library/configuration/ini_parser.h create mode 100644 core/library/configuration/ini_roller.cpp create mode 100644 core/library/configuration/ini_roller.h create mode 100644 core/library/configuration/makefile create mode 100644 core/library/configuration/section_manager.cpp create mode 100644 core/library/configuration/section_manager.h create mode 100644 core/library/configuration/system_values.cpp create mode 100644 core/library/configuration/system_values.h create mode 100644 core/library/configuration/table_configurator.cpp create mode 100644 core/library/configuration/table_configurator.h create mode 100644 core/library/configuration/variable_tokenizer.cpp create mode 100644 core/library/configuration/variable_tokenizer.h create mode 100644 core/library/crypto/blowfish_crypto.cpp create mode 100644 core/library/crypto/blowfish_crypto.h create mode 100644 core/library/crypto/makefile create mode 100644 core/library/crypto/rsa_crypto.cpp create mode 100644 core/library/crypto/rsa_crypto.h create mode 100644 core/library/crypto/ssl_init.cpp create mode 100644 core/library/crypto/ssl_init.h create mode 100644 core/library/filesystem/byte_filer.cpp create mode 100644 core/library/filesystem/byte_filer.h create mode 100644 core/library/filesystem/directory.cpp create mode 100644 core/library/filesystem/directory.h create mode 100644 core/library/filesystem/directory_tree.cpp create mode 100644 core/library/filesystem/directory_tree.h create mode 100644 core/library/filesystem/file_info.cpp create mode 100644 core/library/filesystem/file_info.h create mode 100644 core/library/filesystem/file_time.cpp create mode 100644 core/library/filesystem/file_time.h create mode 100644 core/library/filesystem/filename.cpp create mode 100644 core/library/filesystem/filename.h create mode 100644 core/library/filesystem/filename_list.cpp create mode 100644 core/library/filesystem/filename_list.h create mode 100644 core/library/filesystem/filename_tree.cpp create mode 100644 core/library/filesystem/filename_tree.h create mode 100644 core/library/filesystem/heavy_file_ops.cpp create mode 100644 core/library/filesystem/heavy_file_ops.h create mode 100644 core/library/filesystem/huge_file.cpp create mode 100644 core/library/filesystem/huge_file.h create mode 100644 core/library/filesystem/makefile create mode 100644 core/library/loggers/combo_logger.cpp create mode 100644 core/library/loggers/combo_logger.h create mode 100644 core/library/loggers/console_logger.cpp create mode 100644 core/library/loggers/console_logger.h create mode 100644 core/library/loggers/critical_events.cpp create mode 100644 core/library/loggers/critical_events.h create mode 100644 core/library/loggers/definitions_wx.h create mode 100644 core/library/loggers/eol_aware.h create mode 100644 core/library/loggers/file_logger.cpp create mode 100644 core/library/loggers/file_logger.h create mode 100644 core/library/loggers/filter_set.h create mode 100644 core/library/loggers/logging_filters.h create mode 100644 core/library/loggers/logging_macros.h create mode 100644 core/library/loggers/makefile create mode 100644 core/library/loggers/program_wide_logger.cpp create mode 100644 core/library/loggers/program_wide_logger.h create mode 100644 core/library/loggers/standard_log_base.h create mode 100644 core/library/makefile create mode 100644 core/library/mathematics/averager.h create mode 100644 core/library/mathematics/chaos.h create mode 100644 core/library/mathematics/double_plus.h create mode 100644 core/library/mathematics/makefile create mode 100644 core/library/mathematics/math_ops.h create mode 100644 core/library/mathematics/placeholder.cpp create mode 100644 core/library/nodes/list.cpp create mode 100644 core/library/nodes/list.h create mode 100644 core/library/nodes/makefile create mode 100644 core/library/nodes/node.cpp create mode 100644 core/library/nodes/node.h create mode 100644 core/library/nodes/packable_tree.cpp create mode 100644 core/library/nodes/packable_tree.h create mode 100644 core/library/nodes/path.cpp create mode 100644 core/library/nodes/path.h create mode 100644 core/library/nodes/symbol_tree.cpp create mode 100644 core/library/nodes/symbol_tree.h create mode 100644 core/library/nodes/tree.cpp create mode 100644 core/library/nodes/tree.h create mode 100644 core/library/processes/configured_applications.cpp create mode 100644 core/library/processes/configured_applications.h create mode 100644 core/library/processes/ethread.cpp create mode 100644 core/library/processes/ethread.h create mode 100644 core/library/processes/heartbeat.cpp create mode 100644 core/library/processes/heartbeat.h create mode 100644 core/library/processes/launch_process.cpp create mode 100644 core/library/processes/launch_process.h create mode 100644 core/library/processes/letter.cpp create mode 100644 core/library/processes/letter.h create mode 100644 core/library/processes/mail_stop.h create mode 100644 core/library/processes/mailbox.cpp create mode 100644 core/library/processes/mailbox.h create mode 100644 core/library/processes/makefile create mode 100644 core/library/processes/os_event.h create mode 100644 core/library/processes/post_office.cpp create mode 100644 core/library/processes/post_office.h create mode 100644 core/library/processes/process_control.cpp create mode 100644 core/library/processes/process_control.h create mode 100644 core/library/processes/process_entry.cpp create mode 100644 core/library/processes/process_entry.h create mode 100644 core/library/processes/rendezvous.cpp create mode 100644 core/library/processes/rendezvous.h create mode 100644 core/library/processes/safe_callback.cpp create mode 100644 core/library/processes/safe_callback.h create mode 100644 core/library/processes/safe_roller.cpp create mode 100644 core/library/processes/safe_roller.h create mode 100644 core/library/processes/state_machine.cpp create mode 100644 core/library/processes/state_machine.h create mode 100644 core/library/processes/thread_cabinet.cpp create mode 100644 core/library/processes/thread_cabinet.h create mode 100644 core/library/structures/amorph.h create mode 100644 core/library/structures/bit_vector.cpp create mode 100644 core/library/structures/bit_vector.h create mode 100644 core/library/structures/byte_hasher.h create mode 100644 core/library/structures/checksums.cpp create mode 100644 core/library/structures/checksums.h create mode 100644 core/library/structures/hash_table.h create mode 100644 core/library/structures/int_hash.h create mode 100644 core/library/structures/makefile create mode 100644 core/library/structures/matrix.h create mode 100644 core/library/structures/memory_limiter.cpp create mode 100644 core/library/structures/memory_limiter.h create mode 100644 core/library/structures/object_packers.cpp create mode 100644 core/library/structures/object_packers.h create mode 100644 core/library/structures/pointer_hash.h create mode 100644 core/library/structures/roller.h create mode 100644 core/library/structures/set.h create mode 100644 core/library/structures/stack.h create mode 100644 core/library/structures/static_memory_gremlin.cpp create mode 100644 core/library/structures/static_memory_gremlin.h create mode 100644 core/library/structures/string_array.h create mode 100644 core/library/structures/string_hash.h create mode 100644 core/library/structures/string_hasher.cpp create mode 100644 core/library/structures/string_hasher.h create mode 100644 core/library/structures/string_table.cpp create mode 100644 core/library/structures/string_table.h create mode 100644 core/library/structures/symbol_table.h create mode 100644 core/library/structures/unique_id.h create mode 100644 core/library/structures/version_record.cpp create mode 100644 core/library/structures/version_record.h create mode 100644 core/library/tests_algorithms/makefile create mode 100644 core/library/tests_algorithms/test_sorts.cpp create mode 100644 core/library/tests_basis/checkup.cpp create mode 100644 core/library/tests_basis/checkup.h create mode 100644 core/library/tests_basis/makefile create mode 100644 core/library/tests_basis/test_array.cpp create mode 100644 core/library/tests_basis/test_boilerplate.cpp create mode 100644 core/library/tests_basis/test_mutex.cpp create mode 100644 core/library/tests_basis/test_string.cpp create mode 100644 core/library/tests_basis/test_system_preconditions.cpp create mode 100644 core/library/tests_configuration/makefile create mode 100644 core/library/tests_configuration/test_section_manager.cpp create mode 100644 core/library/tests_configuration/test_tokenizer.cpp create mode 100644 core/library/tests_crypto/makefile create mode 100644 core/library/tests_crypto/test_blowfish_crypto.cpp create mode 100644 core/library/tests_crypto/test_rsa_crypto.cpp create mode 100644 core/library/tests_filesystem/makefile create mode 100644 core/library/tests_filesystem/test_byte_filer.cpp create mode 100644 core/library/tests_filesystem/test_directory.cpp create mode 100644 core/library/tests_filesystem/test_directory_tree.cpp create mode 100644 core/library/tests_filesystem/test_file_info.cpp create mode 100644 core/library/tests_filesystem/test_file_time.cpp create mode 100644 core/library/tests_filesystem/test_filename.cpp create mode 100644 core/library/tests_filesystem/test_huge_file.cpp create mode 100644 core/library/tests_mathematics/makefile create mode 100644 core/library/tests_mathematics/test_chaos.cpp create mode 100644 core/library/tests_mathematics/test_double_plus.cpp create mode 100644 core/library/tests_mathematics/test_math_ops.cpp create mode 100644 core/library/tests_nodes/makefile create mode 100644 core/library/tests_nodes/test_list.cpp create mode 100644 core/library/tests_nodes/test_node.cpp create mode 100644 core/library/tests_nodes/test_packable_tree.cpp create mode 100644 core/library/tests_nodes/test_symbol_tree.cpp create mode 100644 core/library/tests_nodes/test_tree.cpp create mode 100644 core/library/tests_structures/bogon.cpp create mode 100644 core/library/tests_structures/bogon.h create mode 100644 core/library/tests_structures/makefile create mode 100644 core/library/tests_structures/test_amorph.cpp create mode 100644 core/library/tests_structures/test_bit_vector.cpp create mode 100644 core/library/tests_structures/test_hash_table.cpp create mode 100644 core/library/tests_structures/test_int_hash.cpp create mode 100644 core/library/tests_structures/test_matrix.cpp create mode 100644 core/library/tests_structures/test_memory_limiter.cpp create mode 100644 core/library/tests_structures/test_packing.cpp create mode 100644 core/library/tests_structures/test_set.cpp create mode 100644 core/library/tests_structures/test_stack.cpp create mode 100644 core/library/tests_structures/test_string_table.cpp create mode 100644 core/library/tests_structures/test_symbol_table.cpp create mode 100644 core/library/tests_structures/test_unique_id.cpp create mode 100644 core/library/tests_structures/test_version.cpp create mode 100644 core/library/tests_textual/df_1.csv create mode 100644 core/library/tests_textual/makefile create mode 100644 core/library/tests_textual/test_byte_format.cpp create mode 100644 core/library/tests_textual/test_parse_csv.cpp create mode 100644 core/library/tests_textual/test_splitter.cpp create mode 100644 core/library/tests_textual/test_xml_generator.cpp create mode 100644 core/library/tests_timely/makefile create mode 100644 core/library/tests_timely/test_earth_time.cpp create mode 100644 core/library/textual/byte_formatter.cpp create mode 100644 core/library/textual/byte_formatter.h create mode 100644 core/library/textual/list_parsing.cpp create mode 100644 core/library/textual/list_parsing.h create mode 100644 core/library/textual/makefile create mode 100644 core/library/textual/parser_bits.cpp create mode 100644 core/library/textual/parser_bits.h create mode 100644 core/library/textual/string_convert.h create mode 100644 core/library/textual/string_manipulation.cpp create mode 100644 core/library/textual/string_manipulation.h create mode 100644 core/library/textual/xml_generator.cpp create mode 100644 core/library/textual/xml_generator.h create mode 100644 core/library/textual/xml_parser.cpp create mode 100644 core/library/textual/xml_parser.h create mode 100644 core/library/timely/earth_time.cpp create mode 100644 core/library/timely/earth_time.h create mode 100644 core/library/timely/makefile create mode 100644 core/library/timely/stopwatch.cpp create mode 100644 core/library/timely/stopwatch.h create mode 100644 core/library/timely/time_control.cpp create mode 100644 core/library/timely/time_control.h create mode 100644 core/library/timely/time_stamp.cpp create mode 100644 core/library/timely/time_stamp.h create mode 100644 core/library/timely/timer_driver.cpp create mode 100644 core/library/timely/timer_driver.h create mode 100644 core/library/unit_test/makefile create mode 100644 core/library/unit_test/unit_base.cpp create mode 100644 core/library/unit_test/unit_base.h create mode 100644 core/library/versions/makefile create mode 100644 core/library/versions/version.ini.example create mode 100644 core/library/versions/version_checker.cpp create mode 100644 core/library/versions/version_checker.h create mode 100644 core/library/versions/version_ini.cpp create mode 100644 core/library/versions/version_ini.h create mode 100644 core/makefile create mode 100644 core/tools/clam_tools/clamtools_version.rc create mode 100644 core/tools/clam_tools/makefile create mode 100644 core/tools/clam_tools/value_tagger.cpp create mode 100644 core/tools/clam_tools/version.ini create mode 100644 core/tools/clam_tools/version_stamper.cpp create mode 100644 core/tools/clam_tools/vsts_version_fixer.cpp create mode 100644 core/tools/clam_tools/write_build_config.cpp create mode 100644 core/tools/clam_tools/write_build_config.h create mode 100644 core/tools/dependency_tool/Xfuncproto.h create mode 100644 core/tools/dependency_tool/Xos2defs.h create mode 100644 core/tools/dependency_tool/Xosdefs.h create mode 100644 core/tools/dependency_tool/Xw32defs.h create mode 100644 core/tools/dependency_tool/cppsetup.cpp create mode 100644 core/tools/dependency_tool/def.h create mode 100644 core/tools/dependency_tool/ifparser.cpp create mode 100644 core/tools/dependency_tool/ifparser.h create mode 100644 core/tools/dependency_tool/imakemdep.h create mode 100644 core/tools/dependency_tool/include.cpp create mode 100644 core/tools/dependency_tool/makedep.cpp create mode 100644 core/tools/dependency_tool/makedep_version.rc create mode 100644 core/tools/dependency_tool/makedepend.man create mode 100644 core/tools/dependency_tool/makedepend.txt create mode 100644 core/tools/dependency_tool/makefile create mode 100644 core/tools/dependency_tool/parse.cpp create mode 100644 core/tools/dependency_tool/pr.cpp create mode 100644 core/tools/dependency_tool/readme.txt create mode 100644 core/tools/dependency_tool/version.ini create mode 100644 core/tools/makefile create mode 100644 core/tools/simple_utilities/create_guid.cpp create mode 100644 core/tools/simple_utilities/makefile create mode 100644 core/tools/simple_utilities/playsound.cpp create mode 100644 core/tools/simple_utilities/short_path.cpp create mode 100644 core/tools/simple_utilities/simple_utils_version.rc create mode 100644 core/tools/simple_utilities/sleep_ms.cpp create mode 100644 core/tools/simple_utilities/version.ini create mode 100644 core/tools/simple_utilities/zap_process.cpp create mode 100644 core/tools/solution_solvers/check_resource_ids.sh create mode 100644 core/tools/solution_solvers/clean_vcxproj.sh create mode 100644 core/tools/solution_solvers/extract_projects.sh create mode 100644 core/tools/solution_solvers/find_multilisted_projects_in_solutions.sh create mode 100644 core/tools/solution_solvers/find_output_pathers.sh create mode 100644 core/tools/solution_solvers/verify_project_file.sh create mode 100644 database/ansi/globe.ansi create mode 100644 database/ansi/uponce.ansi create mode 100644 database/ansi/upupaway.ansi create mode 100644 database/configuration/azureus/azureus_script.txt create mode 100644 database/configuration/boinc/etc/init.d/boinc create mode 100644 database/configuration/boinc/etc/sysconfig/boinc create mode 100644 database/configuration/boinc/readme.txt create mode 100644 database/configuration/boot/chos.conf create mode 100644 database/configuration/boot/lilo.conf create mode 100644 database/configuration/cron/bookmarks_builder.crontab create mode 100644 database/configuration/cron/chkrootkit.crontab create mode 100644 database/configuration/cron/doxygen.crontab create mode 100644 database/configuration/cron/linux_config_snarf.crontab create mode 100644 database/configuration/cron/mysql_db_backup.crontab create mode 100644 database/configuration/cron/nechung_fortune.crontab create mode 100644 database/configuration/cron/overload_check.crontab create mode 100644 database/configuration/cron/random_sound.crontab create mode 100644 database/configuration/cron/time_synch.crontab create mode 100644 database/configuration/cron/uptime_report.crontab create mode 100644 database/configuration/cron/zooty_archive_packer.crontab create mode 100644 database/configuration/database/sql/find_prims_created_by.sql create mode 100644 database/configuration/database/sql/replace_creatorid_fields.sql create mode 100644 database/configuration/devices/usb_ram_disk/fstab_with_usb_drive.txt create mode 100644 database/configuration/devices/zip_disk/fstab_with_zip_disk create mode 100644 database/configuration/dhcp/dhcpd.conf create mode 100644 database/configuration/firewall/ipchains/firewalling.conf create mode 100644 database/configuration/firewall/iptables/iptables create mode 100644 database/configuration/init.d/team_city_startup_example create mode 100644 database/configuration/init.d/trac_startup create mode 100644 database/configuration/inputrc/symlink_completion.txt create mode 100644 database/configuration/logging/syslog.conf create mode 100644 database/configuration/nfs_mount/exports create mode 100644 database/configuration/nfs_mount/fstab create mode 100644 database/configuration/opensim/resources/official_saul_panzer_blank.oar create mode 100644 database/configuration/quake3/fred_free_for_all.cfg create mode 100644 database/configuration/quake3/fred_t_hamster_maps.cfg create mode 100644 database/configuration/quake3/quake3_server_start.txt create mode 100644 database/configuration/rc.local/example_rc.local_with_zooty_alerter create mode 100644 database/configuration/routing/gated.conf create mode 100644 database/configuration/routing/routing_setup.conf create mode 100644 database/configuration/samba/bartron.smb.conf create mode 100644 database/configuration/samba/smb_mount_in_fstab.txt create mode 100644 database/configuration/samba/thewheel.smb.conf create mode 100644 database/configuration/ssh/make_chroot_jail.sh create mode 100644 database/configuration/subversion/per_svn_repo/passwd create mode 100644 database/configuration/subversion/per_svn_repo/svnserve.conf create mode 100644 database/configuration/subversion/subversion_install.txt create mode 100644 database/configuration/subversion/svnserve create mode 100644 database/configuration/sysctl/sysctl.conf.real_time_mods create mode 100644 database/configuration/visual_studio/good_compare_string.txt create mode 100644 database/configuration/web_server/example_web_redirect_htaccess create mode 100644 database/configuration/webcam/reloader.html create mode 100644 database/configuration/webcam/webcamrc_example create mode 100644 database/configuration/x_win/start_fishtank.sh create mode 100644 database/configuration/x_win/start_xroach.sh create mode 100644 database/forms/check_register.odt create mode 100644 database/fortunes.dat create mode 100644 database/patterns/vi/pattern_auto_replacement create mode 100644 database/patterns/vi/pattern_fix_commented_formal_parameters.txt create mode 100644 database/pictures/celtic_destiny14.jpg create mode 100644 database/pictures/clamback.jpg create mode 100644 database/pictures/clamblock2.jpg create mode 100644 database/pictures/clams_tran.gif create mode 100644 database/pictures/freakolitic42celts.jpg create mode 100644 database/pictures/grywarky2.jpg create mode 100644 database/pictures/home_front_b_5.jpg create mode 100644 database/pictures/mongo_sox_ball4.jpg create mode 100644 database/pictures/no_matches.html create mode 100644 database/pictures/no_matches.jpg create mode 100644 database/sounds/woouoo.wav create mode 100644 docs/bash_tools.html create mode 100644 docs/binaries_note.txt create mode 100644 docs/clam_manual/clam_docs.html create mode 100644 docs/clam_manual/clam_root.html create mode 100644 docs/clam_manual/partial_cygwin_for_build.txt create mode 100644 docs/feisty_meow_dox.config create mode 100644 docs/feisty_meow_quick_start.txt create mode 100644 docs/makefile create mode 100644 docs/perl_tools.html create mode 100644 docs/text_examples/chinese_simplified_text.txt create mode 100644 docs/text_examples/korean_text.txt create mode 100644 docs/text_examples/readme.txt create mode 100644 docs/text_examples/russian_text.txt create mode 100644 docs/text_examples/tibetan_text.txt create mode 100644 graphiq/library/geometric/angle.h create mode 100644 graphiq/library/geometric/cartesian_objects.h create mode 100644 graphiq/library/geometric/circle.cpp create mode 100644 graphiq/library/geometric/circle.h create mode 100644 graphiq/library/geometric/ellipse.cpp create mode 100644 graphiq/library/geometric/ellipse.h create mode 100644 graphiq/library/geometric/line.h create mode 100644 graphiq/library/geometric/makefile create mode 100644 graphiq/library/geometric/math_bits.cpp create mode 100644 graphiq/library/geometric/math_bits.h create mode 100644 graphiq/library/geometric/point.h create mode 100644 graphiq/library/geometric/polygon.cpp create mode 100644 graphiq/library/geometric/polygon.h create mode 100644 graphiq/library/geometric/rectangle.h create mode 100644 graphiq/library/geometric/screen_rectangle.cpp create mode 100644 graphiq/library/geometric/screen_rectangle.h create mode 100644 graphiq/library/geometric/triangle.cpp create mode 100644 graphiq/library/geometric/triangle.h create mode 100644 graphiq/library/geometric/warper.h create mode 100644 graphiq/library/makefile create mode 100644 graphiq/library/tests_geometric/makefile create mode 100644 graphiq/library/tests_geometric/test_angle.cpp create mode 100644 graphiq/library/tests_geometric/test_ellipse.cpp create mode 100644 graphiq/library/tests_geometric/test_geometry.cpp create mode 100644 graphiq/library/tests_geometric/test_point.cpp create mode 100644 graphiq/library/tests_geometric/test_warper.cpp create mode 100644 graphiq/library/user_interface/menu_base.cpp create mode 100644 graphiq/library/user_interface/menu_base.h create mode 100644 graphiq/makefile create mode 100644 license.txt create mode 100644 makefile create mode 100644 octopi/applications/makefile create mode 100644 octopi/applications/transporter/find_missing.cpp create mode 100644 octopi/applications/transporter/makefile create mode 100644 octopi/applications/transporter/synch_files.cpp create mode 100644 octopi/applications/transporter/transporter.cpp create mode 100644 octopi/applications/transporter/transporter_version.rc create mode 100644 octopi/applications/transporter/version.ini create mode 100644 octopi/library/cromp/cromp_client.cpp create mode 100644 octopi/library/cromp/cromp_client.h create mode 100644 octopi/library/cromp/cromp_common.cpp create mode 100644 octopi/library/cromp/cromp_common.h create mode 100644 octopi/library/cromp/cromp_security.cpp create mode 100644 octopi/library/cromp/cromp_security.h create mode 100644 octopi/library/cromp/cromp_server.cpp create mode 100644 octopi/library/cromp/cromp_server.h create mode 100644 octopi/library/cromp/cromp_transaction.cpp create mode 100644 octopi/library/cromp/cromp_transaction.h create mode 100644 octopi/library/cromp/makefile create mode 100644 octopi/library/makefile create mode 100644 octopi/library/octopus/entity_data_bin.cpp create mode 100644 octopi/library/octopus/entity_data_bin.h create mode 100644 octopi/library/octopus/entity_defs.cpp create mode 100644 octopi/library/octopus/entity_defs.h create mode 100644 octopi/library/octopus/identity_infoton.cpp create mode 100644 octopi/library/octopus/identity_infoton.h create mode 100644 octopi/library/octopus/identity_tentacle.cpp create mode 100644 octopi/library/octopus/identity_tentacle.h create mode 100644 octopi/library/octopus/infoton.cpp create mode 100644 octopi/library/octopus/infoton.h create mode 100644 octopi/library/octopus/makefile create mode 100644 octopi/library/octopus/octopus.cpp create mode 100644 octopi/library/octopus/octopus.h create mode 100644 octopi/library/octopus/tentacle.cpp create mode 100644 octopi/library/octopus/tentacle.h create mode 100644 octopi/library/octopus/tentacle_helper.h create mode 100644 octopi/library/octopus/unhandled_request.cpp create mode 100644 octopi/library/octopus/unhandled_request.h create mode 100644 octopi/library/sockets/base_address.h create mode 100644 octopi/library/sockets/internet_address.cpp create mode 100644 octopi/library/sockets/internet_address.h create mode 100644 octopi/library/sockets/machine_uid.cpp create mode 100644 octopi/library/sockets/machine_uid.h create mode 100644 octopi/library/sockets/makefile create mode 100644 octopi/library/sockets/range_limiter.cpp create mode 100644 octopi/library/sockets/range_limiter.h create mode 100644 octopi/library/sockets/raw_socket.cpp create mode 100644 octopi/library/sockets/raw_socket.h create mode 100644 octopi/library/sockets/sequence_tracker.cpp create mode 100644 octopi/library/sockets/sequence_tracker.h create mode 100644 octopi/library/sockets/socket_data.h create mode 100644 octopi/library/sockets/socket_minder.cpp create mode 100644 octopi/library/sockets/socket_minder.h create mode 100644 octopi/library/sockets/span_manager.cpp create mode 100644 octopi/library/sockets/span_manager.h create mode 100644 octopi/library/sockets/spocket.cpp create mode 100644 octopi/library/sockets/spocket.h create mode 100644 octopi/library/sockets/subnet_calculator.cpp create mode 100644 octopi/library/sockets/subnet_calculator.h create mode 100644 octopi/library/sockets/tcpip_definitions.h create mode 100644 octopi/library/sockets/tcpip_stack.cpp create mode 100644 octopi/library/sockets/tcpip_stack.h create mode 100644 octopi/library/sockets/throughput_counter.cpp create mode 100644 octopi/library/sockets/throughput_counter.h create mode 100644 octopi/library/synchronic/bundle_list.h create mode 100644 octopi/library/synchronic/list_manager.cpp create mode 100644 octopi/library/synchronic/list_manager.h create mode 100644 octopi/library/synchronic/list_synchronizer.cpp create mode 100644 octopi/library/synchronic/list_synchronizer.h create mode 100644 octopi/library/synchronic/makefile create mode 100644 octopi/library/synchronic/synchronizable.h create mode 100644 octopi/library/tentacles/encryption_infoton.cpp create mode 100644 octopi/library/tentacles/encryption_infoton.h create mode 100644 octopi/library/tentacles/encryption_tentacle.cpp create mode 100644 octopi/library/tentacles/encryption_tentacle.h create mode 100644 octopi/library/tentacles/encryption_wrapper.cpp create mode 100644 octopi/library/tentacles/encryption_wrapper.h create mode 100644 octopi/library/tentacles/entity_registry.cpp create mode 100644 octopi/library/tentacles/entity_registry.h create mode 100644 octopi/library/tentacles/file_transfer_infoton.cpp create mode 100644 octopi/library/tentacles/file_transfer_infoton.h create mode 100644 octopi/library/tentacles/file_transfer_tentacle.cpp create mode 100644 octopi/library/tentacles/file_transfer_tentacle.h create mode 100644 octopi/library/tentacles/key_repository.cpp create mode 100644 octopi/library/tentacles/key_repository.h create mode 100644 octopi/library/tentacles/login_tentacle.cpp create mode 100644 octopi/library/tentacles/login_tentacle.h create mode 100644 octopi/library/tentacles/makefile create mode 100644 octopi/library/tentacles/recursive_file_copy.cpp create mode 100644 octopi/library/tentacles/recursive_file_copy.h create mode 100644 octopi/library/tentacles/security_infoton.cpp create mode 100644 octopi/library/tentacles/security_infoton.h create mode 100644 octopi/library/tentacles/simple_entity_registry.cpp create mode 100644 octopi/library/tentacles/simple_entity_registry.h create mode 100644 octopi/library/tests_sockets/bcast_spocketer.cpp create mode 100644 octopi/library/tests_sockets/bcast_spocketer.h create mode 100644 octopi/library/tests_sockets/makefile create mode 100644 octopi/library/tests_sockets/spocket_tester.cpp create mode 100644 octopi/library/tests_sockets/spocket_tester.h create mode 100644 octopi/library/tests_sockets/t_sockets_version.rc create mode 100644 octopi/library/tests_sockets/test_address.cpp create mode 100644 octopi/library/tests_sockets/test_bcast_spocket.cpp create mode 100644 octopi/library/tests_sockets/test_enum_adapters.cpp create mode 100644 octopi/library/tests_sockets/test_sequence_tracker.cpp create mode 100644 octopi/library/tests_sockets/test_span_manager.cpp create mode 100644 octopi/library/tests_sockets/test_spocket.cpp create mode 100644 octopi/library/tests_sockets/test_ucast_spocket.cpp create mode 100644 octopi/library/tests_sockets/version.ini create mode 100644 octopi/makefile create mode 100644 production/3rdparty/curl/COPYING create mode 100644 production/3rdparty/curl/README create mode 100644 production/3rdparty/curl/bin/curl-config create mode 100644 production/3rdparty/curl/include/curl/curl.h create mode 100644 production/3rdparty/curl/include/curl/curlver.h create mode 100644 production/3rdparty/curl/include/curl/easy.h create mode 100644 production/3rdparty/curl/include/curl/mprintf.h create mode 100644 production/3rdparty/curl/include/curl/multi.h create mode 100644 production/3rdparty/curl/include/curl/stdcheaders.h create mode 100644 production/3rdparty/curl/include/curl/types.h create mode 100644 production/3rdparty/curl/lib/libcurl-3.dll create mode 100644 production/3rdparty/curl/lib/libcurl.a create mode 100644 production/3rdparty/curl/lib/libcurl.dll.a create mode 100644 production/3rdparty/openssl/include/openssl/aes.h create mode 100644 production/3rdparty/openssl/include/openssl/applink.c create mode 100644 production/3rdparty/openssl/include/openssl/asn1.h create mode 100644 production/3rdparty/openssl/include/openssl/asn1_mac.h create mode 100644 production/3rdparty/openssl/include/openssl/asn1t.h create mode 100644 production/3rdparty/openssl/include/openssl/bio.h create mode 100644 production/3rdparty/openssl/include/openssl/blowfish.h create mode 100644 production/3rdparty/openssl/include/openssl/bn.h create mode 100644 production/3rdparty/openssl/include/openssl/buffer.h create mode 100644 production/3rdparty/openssl/include/openssl/cast.h create mode 100644 production/3rdparty/openssl/include/openssl/comp.h create mode 100644 production/3rdparty/openssl/include/openssl/conf.h create mode 100644 production/3rdparty/openssl/include/openssl/conf_api.h create mode 100644 production/3rdparty/openssl/include/openssl/crypto.h create mode 100644 production/3rdparty/openssl/include/openssl/des.h create mode 100644 production/3rdparty/openssl/include/openssl/des_old.h create mode 100644 production/3rdparty/openssl/include/openssl/dh.h create mode 100644 production/3rdparty/openssl/include/openssl/dsa.h create mode 100644 production/3rdparty/openssl/include/openssl/dso.h create mode 100644 production/3rdparty/openssl/include/openssl/dtls1.h create mode 100644 production/3rdparty/openssl/include/openssl/e_os2.h create mode 100644 production/3rdparty/openssl/include/openssl/ebcdic.h create mode 100644 production/3rdparty/openssl/include/openssl/ec.h create mode 100644 production/3rdparty/openssl/include/openssl/ecdh.h create mode 100644 production/3rdparty/openssl/include/openssl/ecdsa.h create mode 100644 production/3rdparty/openssl/include/openssl/engine.h create mode 100644 production/3rdparty/openssl/include/openssl/err.h create mode 100644 production/3rdparty/openssl/include/openssl/evp.h create mode 100644 production/3rdparty/openssl/include/openssl/hmac.h create mode 100644 production/3rdparty/openssl/include/openssl/idea.h create mode 100644 production/3rdparty/openssl/include/openssl/krb5_asn.h create mode 100644 production/3rdparty/openssl/include/openssl/kssl.h create mode 100644 production/3rdparty/openssl/include/openssl/lhash.h create mode 100644 production/3rdparty/openssl/include/openssl/md2.h create mode 100644 production/3rdparty/openssl/include/openssl/md4.h create mode 100644 production/3rdparty/openssl/include/openssl/md5.h create mode 100644 production/3rdparty/openssl/include/openssl/obj_mac.h create mode 100644 production/3rdparty/openssl/include/openssl/objects.h create mode 100644 production/3rdparty/openssl/include/openssl/ocsp.h create mode 100644 production/3rdparty/openssl/include/openssl/opensslconf.h create mode 100644 production/3rdparty/openssl/include/openssl/opensslv.h create mode 100644 production/3rdparty/openssl/include/openssl/ossl_typ.h create mode 100644 production/3rdparty/openssl/include/openssl/pem.h create mode 100644 production/3rdparty/openssl/include/openssl/pem2.h create mode 100644 production/3rdparty/openssl/include/openssl/pkcs12.h create mode 100644 production/3rdparty/openssl/include/openssl/pkcs7.h create mode 100644 production/3rdparty/openssl/include/openssl/pq_compat.h create mode 100644 production/3rdparty/openssl/include/openssl/pqueue.h create mode 100644 production/3rdparty/openssl/include/openssl/rand.h create mode 100644 production/3rdparty/openssl/include/openssl/rc2.h create mode 100644 production/3rdparty/openssl/include/openssl/rc4.h create mode 100644 production/3rdparty/openssl/include/openssl/ripemd.h create mode 100644 production/3rdparty/openssl/include/openssl/rsa.h create mode 100644 production/3rdparty/openssl/include/openssl/safestack.h create mode 100644 production/3rdparty/openssl/include/openssl/sha.h create mode 100644 production/3rdparty/openssl/include/openssl/ssl.h create mode 100644 production/3rdparty/openssl/include/openssl/ssl2.h create mode 100644 production/3rdparty/openssl/include/openssl/ssl23.h create mode 100644 production/3rdparty/openssl/include/openssl/ssl3.h create mode 100644 production/3rdparty/openssl/include/openssl/stack.h create mode 100644 production/3rdparty/openssl/include/openssl/store.h create mode 100644 production/3rdparty/openssl/include/openssl/symhacks.h create mode 100644 production/3rdparty/openssl/include/openssl/tls1.h create mode 100644 production/3rdparty/openssl/include/openssl/tmdiff.h create mode 100644 production/3rdparty/openssl/include/openssl/txt_db.h create mode 100644 production/3rdparty/openssl/include/openssl/ui.h create mode 100644 production/3rdparty/openssl/include/openssl/ui_compat.h create mode 100644 production/3rdparty/openssl/include/openssl/x509.h create mode 100644 production/3rdparty/openssl/include/openssl/x509_vfy.h create mode 100644 production/3rdparty/openssl/include/openssl/x509v3.h create mode 100644 production/3rdparty/openssl/lib/libeay32.dll create mode 100644 production/3rdparty/openssl/lib/libeay32.lib create mode 100644 production/3rdparty/openssl/lib/ssleay32.dll create mode 100644 production/3rdparty/openssl/lib/ssleay32.lib create mode 100644 production/3rdparty/openssl/version.txt create mode 100644 production/3rdparty/zlib/include/zconf.h create mode 100644 production/3rdparty/zlib/include/zlib.h create mode 100644 production/3rdparty/zlib/lib/zdll.exp create mode 100644 production/3rdparty/zlib/lib/zdll.lib create mode 100644 production/3rdparty/zlib/lib/zlib.def create mode 100644 production/3rdparty/zlib/lib/zlib.lib create mode 100644 production/3rdparty/zlib/lib/zlib1.dll create mode 100644 production/codescan.ini create mode 100644 production/feisty_meow_config.ini create mode 100644 production/makefile create mode 100644 production/msys/bin/awk create mode 100644 production/msys/bin/basename.exe create mode 100644 production/msys/bin/bash.exe create mode 100644 production/msys/bin/bunzip2 create mode 100644 production/msys/bin/bzip2.exe create mode 100644 production/msys/bin/cat.exe create mode 100644 production/msys/bin/chmod.exe create mode 100644 production/msys/bin/cls create mode 100644 production/msys/bin/clsb create mode 100644 production/msys/bin/cmd create mode 100644 production/msys/bin/cmp.exe create mode 100644 production/msys/bin/comm.exe create mode 100644 production/msys/bin/cp.exe create mode 100644 production/msys/bin/cut.exe create mode 100644 production/msys/bin/date.exe create mode 100644 production/msys/bin/diff.exe create mode 100644 production/msys/bin/diff3.exe create mode 100644 production/msys/bin/dirname.exe create mode 100644 production/msys/bin/echo create mode 100644 production/msys/bin/egrep create mode 100644 production/msys/bin/env.exe create mode 100644 production/msys/bin/ex create mode 100644 production/msys/bin/expr.exe create mode 100644 production/msys/bin/false.exe create mode 100644 production/msys/bin/fgrep create mode 100644 production/msys/bin/file.exe create mode 100644 production/msys/bin/find.exe create mode 100644 production/msys/bin/fold.exe create mode 100644 production/msys/bin/ftp create mode 100644 production/msys/bin/gawk.exe create mode 100644 production/msys/bin/grep.exe create mode 100644 production/msys/bin/gunzip create mode 100644 production/msys/bin/gzip.exe create mode 100644 production/msys/bin/head.exe create mode 100644 production/msys/bin/id.exe create mode 100644 production/msys/bin/info.exe create mode 100644 production/msys/bin/infokey.exe create mode 100644 production/msys/bin/install-info.exe create mode 100644 production/msys/bin/install.exe create mode 100644 production/msys/bin/install.exe.manifest create mode 100644 production/msys/bin/less.exe create mode 100644 production/msys/bin/libW11.dll create mode 100644 production/msys/bin/ln.exe create mode 100644 production/msys/bin/lnkcnv create mode 100644 production/msys/bin/ls.exe create mode 100644 production/msys/bin/lzma.exe create mode 100644 production/msys/bin/m4.exe create mode 100644 production/msys/bin/make.exe create mode 100644 production/msys/bin/makeinfo.exe create mode 100644 production/msys/bin/md5sum.exe create mode 100644 production/msys/bin/mkdir.exe create mode 100644 production/msys/bin/mktemp.exe create mode 100644 production/msys/bin/mount create mode 100644 production/msys/bin/mount.exe create mode 100644 production/msys/bin/msys-1.0.dll create mode 100644 production/msys/bin/msys-bz2-1.dll create mode 100644 production/msys/bin/msysinfo create mode 100644 production/msys/bin/msysmnt.exe create mode 100644 production/msys/bin/mv.exe create mode 100644 production/msys/bin/od.exe create mode 100644 production/msys/bin/patch.exe create mode 100644 production/msys/bin/patch.exe.manifest create mode 100644 production/msys/bin/printf create mode 100644 production/msys/bin/ps.exe create mode 100644 production/msys/bin/pwd create mode 100644 production/msys/bin/pwd.exe create mode 100644 production/msys/bin/rm.exe create mode 100644 production/msys/bin/rmdir.exe create mode 100644 production/msys/bin/rvi create mode 100644 production/msys/bin/rview create mode 100644 production/msys/bin/rvim create mode 100644 production/msys/bin/rxvt.exe create mode 100644 production/msys/bin/sed.exe create mode 100644 production/msys/bin/sh.exe create mode 100644 production/msys/bin/short_path.exe create mode 100644 production/msys/bin/sleep.exe create mode 100644 production/msys/bin/sort.exe create mode 100644 production/msys/bin/split.exe create mode 100644 production/msys/bin/start create mode 100644 production/msys/bin/stty.exe create mode 100644 production/msys/bin/tail.exe create mode 100644 production/msys/bin/tar.exe create mode 100644 production/msys/bin/tee.exe create mode 100644 production/msys/bin/texi2dvi create mode 100644 production/msys/bin/texindex.exe create mode 100644 production/msys/bin/touch.exe create mode 100644 production/msys/bin/tr.exe create mode 100644 production/msys/bin/true.exe create mode 100644 production/msys/bin/umount create mode 100644 production/msys/bin/uname.exe create mode 100644 production/msys/bin/uniq.exe create mode 100644 production/msys/bin/unzip.exe create mode 100644 production/msys/bin/vi create mode 100644 production/msys/bin/view create mode 100644 production/msys/bin/vim.exe create mode 100644 production/msys/bin/wc.exe create mode 100644 production/msys/bin/which create mode 100644 production/msys/bin/xargs.exe create mode 100644 production/msys/bin/zap_process.exe create mode 100644 production/msys/bin/zip.exe create mode 100644 production/msys/etc/config.site create mode 100644 production/msys/etc/fstab create mode 100644 production/msys/etc/fstab.sample create mode 100644 production/msys/etc/inputrc.default create mode 100644 production/msys/etc/profile create mode 100644 production/msys/etc/termcap create mode 100644 production/msys/m.ico create mode 100644 production/msys/msys.bat create mode 100644 production/msys/msys.ico create mode 100644 production/msys/msys_green.ico create mode 100644 production/paths.ini create mode 100644 production/proto_build.ini create mode 100644 production/setup_src/autotesting_tools/autotesting_manifest.txt create mode 100644 production/setup_src/autotesting_tools/create_autotesting_bundle.sh create mode 100644 production/setup_src/autotesting_tools/makefile create mode 100644 production/setup_src/autotesting_tools/paths.ini create mode 100644 production/setup_src/bundle_example/create_bundle.sh create mode 100644 production/setup_src/bundle_example/example_manifest.txt create mode 100644 production/setup_src/bundle_example/makefile create mode 100644 production/setup_src/makefile create mode 100644 production/setup_src/whole_build_package/create_whole_build_pack.sh create mode 100644 production/setup_src/whole_build_package/makefile create mode 100644 production/setup_src/whole_build_package/whole_build_manifest.txt create mode 100644 readme.txt create mode 100644 scripts/archival/pack_3rdparty.sh create mode 100644 scripts/archival/pack_hoople1.sh create mode 100644 scripts/archival/pack_hoople2.sh create mode 100644 scripts/archival/pack_msysbins.sh create mode 100644 scripts/archival/pack_yeti.sh create mode 100644 scripts/archival/shared_snarfer.pl create mode 100644 scripts/archival/snarf_feisty_meow.pl create mode 100644 scripts/archival/snarf_hoople.pl create mode 100644 scripts/archival/snarf_hoople1.pl create mode 100644 scripts/archival/snarf_linux_config.pl create mode 100644 scripts/archival/snarf_notes.pl create mode 100644 scripts/archival/snarf_opensim.pl create mode 100644 scripts/archival/snarf_user.pl create mode 100644 scripts/archival/snarf_yeti.pl create mode 100644 scripts/archival/unsnarf.pl create mode 100644 scripts/bashisms/fred_techniques.txt create mode 100644 scripts/bashisms/qs_handy_unix_examples.sh create mode 100644 scripts/bookmarks/create_marks.sh create mode 100644 scripts/buildor/build_msproj.sh create mode 100644 scripts/buildor/buildor_add_sentinel.sh create mode 100644 scripts/buildor/buildor_count_code.sh create mode 100644 scripts/buildor/buildor_find_active_includes.sh create mode 100644 scripts/buildor/buildor_find_bad.sh create mode 100644 scripts/buildor/buildor_find_unused_includes.sh create mode 100644 scripts/buildor/buildor_strip_code.sh create mode 100644 scripts/buildor/buildor_writable_code.sh create mode 100644 scripts/buildor/check_makefile.sh create mode 100644 scripts/buildor/cygwinize.sh create mode 100644 scripts/buildor/find_in_code_ignore_svn.sh create mode 100644 scripts/buildor/find_mangled.sh create mode 100644 scripts/buildor/find_one_word_comments.sh create mode 100644 scripts/buildor/fix_project_references.py create mode 100644 scripts/buildor/manifester.sh create mode 100644 scripts/buildor/seek_all_source.sh create mode 100644 scripts/buildor/stdbuild.sh create mode 100644 scripts/buildor/synch_build.pl create mode 100644 scripts/buildor/synch_to_build.sh create mode 100644 scripts/buildor/upgrade_hoople.sh create mode 100644 scripts/buildor/version_utils.sh create mode 100644 scripts/cgi/call_version_utils.sh create mode 100644 scripts/cgi/cgi_cat.pl create mode 100644 scripts/cgi/cgi_display.pl create mode 100644 scripts/cgi/cgi_nechung.cgi create mode 100644 scripts/cgi/cgi_show_file_date.sh create mode 100644 scripts/cgi/cgi_show_referer.sh create mode 100644 scripts/cgi/count.cgi create mode 100644 scripts/cgi/grumble_chkrootkit.cgi create mode 100644 scripts/cgi/nechung.cgi create mode 100644 scripts/cgi/prepare_cgi_bin_for_apache.sh create mode 100755 scripts/clam/badness_catcher.sh create mode 100644 scripts/clam/clam_parms.ini create mode 100644 scripts/clam/cpp/blank_target.c create mode 100755 scripts/clam/cpp/buildor_gen_deps.sh create mode 100755 scripts/clam/cpp/get_version.sh create mode 100755 scripts/clam/cpp/ms_manifest.sh create mode 100644 scripts/clam/cpp/ms_manifests/security_manifest.txt create mode 100644 scripts/clam/cpp/ms_manifests/security_manifest_administrator.txt create mode 100644 scripts/clam/cpp/ms_manifests/security_manifest_administrator_with_ui.txt create mode 100644 scripts/clam/cpp/ms_manifests/security_manifest_highest.txt create mode 100644 scripts/clam/cpp/ms_manifests/security_manifest_normal.txt create mode 100755 scripts/clam/cpp/ms_root_dir.sh create mode 100755 scripts/clam/cpp/postconditions.sh create mode 100755 scripts/clam/cpp/preconditions.sh create mode 100755 scripts/clam/cpp/rc_name.sh create mode 100755 scripts/clam/cpp/rebuild_oldies.sh create mode 100644 scripts/clam/cpp/rules.def create mode 100644 scripts/clam/cpp/variables.def create mode 100644 scripts/clam/cpp/vis_stu/trap_new.addin create mode 100644 scripts/clam/cpp/vis_stu/untrap_new.addin create mode 100644 scripts/clam/dotnet/csharper.sh create mode 100644 scripts/clam/dotnet/postconditions.sh create mode 100644 scripts/clam/dotnet/preconditions.sh create mode 100644 scripts/clam/dotnet/readme.txt create mode 100644 scripts/clam/dotnet/rules.def create mode 100644 scripts/clam/dotnet/variables.def create mode 100755 scripts/clam/exit_make.sh create mode 100644 scripts/clam/rules.def create mode 100755 scripts/clam/sound_play.sh create mode 100644 scripts/clam/variables.def create mode 100644 scripts/core/array_sifter.sh create mode 100644 scripts/core/bootstrap_shells.sh create mode 100644 scripts/core/byejob.sh create mode 100644 scripts/core/byemessage.sh create mode 100644 scripts/core/common_aliases.txt create mode 100644 scripts/core/create_tempdir.sh create mode 100644 scripts/core/date_stringer.sh create mode 100644 scripts/core/functions.sh create mode 100644 scripts/core/generate_aliases.pl create mode 100644 scripts/core/get_parent_dir.sh create mode 100644 scripts/core/goodbye.sh create mode 100644 scripts/core/i.sh create mode 100644 scripts/core/inc_num.pl create mode 100644 scripts/core/lightweight_unix_login.sh create mode 100644 scripts/core/profile.sh create mode 100644 scripts/core/random_iter.sh create mode 100644 scripts/core/sh_aliases.txt create mode 100644 scripts/core/shell_header.txt create mode 100644 scripts/core/unix_login.sh create mode 100644 scripts/core/unter_alia.sh create mode 100644 scripts/core/variables.sh create mode 100644 scripts/custom/c_common_aliases.txt create mode 100644 scripts/custom/c_variables.sh create mode 100644 scripts/custom/uva_profile_additions.txt create mode 100644 scripts/database/backup_mysql_dbs.sh create mode 100644 scripts/database/call_movie_seeker.sh create mode 100644 scripts/database/call_movie_stripper.sh create mode 100644 scripts/database/call_show_stripper.sh create mode 100644 scripts/database/movie_seeker.sh create mode 100644 scripts/database/movie_stripper.sh create mode 100644 scripts/database/show_stripper.sh create mode 100644 scripts/demo/OS_crusher.bat create mode 100644 scripts/demo/OS_crusher.sh create mode 100644 scripts/demo/wma2mp3.sh create mode 100644 scripts/devices/zaurus_usb.sh create mode 100644 scripts/email/dump_email_headers.sh create mode 100644 scripts/email/move_spams_and_check.sh create mode 100644 scripts/email/scan_spam.sh create mode 100644 scripts/email/scavenge_emails_from_ldif.sh create mode 100644 scripts/examples/bashrc_grover create mode 100644 scripts/examples/bashrc_root create mode 100644 scripts/examples/bashrc_user create mode 100644 scripts/examples/bashrc_user_windoz create mode 100644 scripts/examples/example_registry_ops.sh create mode 100644 scripts/files/change_endings.pl create mode 100644 scripts/files/change_prefix.sh create mode 100644 scripts/files/count_files_in_subdirs.sh create mode 100644 scripts/files/dir_zero_entries.sh create mode 100644 scripts/files/dos_perm.sh create mode 100644 scripts/files/easy_perm.sh create mode 100644 scripts/files/exe_perm.sh create mode 100644 scripts/files/filedump.pl create mode 100644 scripts/files/filename_helper.pl create mode 100644 scripts/files/find_bad_protection.sh create mode 100644 scripts/files/find_non_owned.sh create mode 100644 scripts/files/fix_game_perms.sh create mode 100644 scripts/files/harsh_perm.sh create mode 100644 scripts/files/list_dupes.sh create mode 100644 scripts/files/normal_perm.sh create mode 100644 scripts/files/order_file_dates.sh create mode 100644 scripts/files/phrase_replacer.py create mode 100644 scripts/files/prefix_renamer.sh create mode 100644 scripts/files/recursive_renlower.sh create mode 100644 scripts/files/recursive_whack_dupes.sh create mode 100644 scripts/files/remove_bracket_id.sh create mode 100644 scripts/files/renlower.pl create mode 100644 scripts/files/safedel.pl create mode 100644 scripts/files/show_directory_listing.sh create mode 100644 scripts/files/show_links.sh create mode 100644 scripts/files/strip_html_code.sh create mode 100644 scripts/files/summing_dir.pl create mode 100644 scripts/files/whack_dupes.sh create mode 100644 scripts/files/whackem.pl create mode 100644 scripts/files/zap_the_dir.pl create mode 100644 scripts/files/zapdirs.pl create mode 100644 scripts/generator/bootstrap_build.sh create mode 100644 scripts/generator/build_variables.sh create mode 100644 scripts/generator/next_version.sh create mode 100644 scripts/generator/vis_stu_vars.sh create mode 100644 scripts/generator/whack_build.sh create mode 100644 scripts/jenkins/empty_parsing_rules.txt create mode 100644 scripts/jenkins/hoople_unit_parsing_rules.txt create mode 100644 scripts/legacy/template.pl create mode 100644 scripts/makefile create mode 100644 scripts/multimedia/pic_importer.sh create mode 100644 scripts/multimedia/play_random.sh create mode 100644 scripts/multimedia/randomly_play.sh create mode 100644 scripts/multimedia/sound_play.sh create mode 100644 scripts/notes/info_overload_report.sh create mode 100644 scripts/opensim/backup_opensim.sh create mode 100644 scripts/opensim/backup_osgrid.sh create mode 100644 scripts/opensim/find_opensim_ports.sh create mode 100644 scripts/opensim/indent_lsl.sh create mode 100644 scripts/opensim/list_sims.sh create mode 100644 scripts/opensim/maybe_restart_opensim.sh create mode 100644 scripts/opensim/opensim_utils.sh create mode 100644 scripts/opensim/run_opensim.sh create mode 100644 scripts/opensim/run_osgrid.sh create mode 100644 scripts/opensim/scan_opensim_log.sh create mode 100644 scripts/opensim/stop_opensim.sh create mode 100644 scripts/opensim/stop_osgrid.sh create mode 100644 scripts/opensim/zap_opensim_main.sh create mode 100644 scripts/processes/pskill.sh create mode 100644 scripts/processes/runner.pl create mode 100644 scripts/rev_control/checkin.sh create mode 100644 scripts/rev_control/cvs_fix.pl create mode 100644 scripts/rev_control/cvs_importer.sh create mode 100644 scripts/rev_control/fix_svn_binaries.sh create mode 100644 scripts/rev_control/getme.sh create mode 100644 scripts/rev_control/rev_control.sh create mode 100644 scripts/rev_control/svn_set_binary.sh create mode 100644 scripts/rev_control/update_these.sh create mode 100644 scripts/rip_burn/dvd_image.sh create mode 100644 scripts/schedule/cal.sh create mode 100644 scripts/schedule/generate_reminders.pl create mode 100644 scripts/schedule/start_calendar.sh create mode 100644 scripts/schedule/time_control.sh create mode 100644 scripts/security/check_website.sh create mode 100644 scripts/security/start_tunnels.sh create mode 100644 scripts/security/tell_zooty_our_ip.sh create mode 100644 scripts/security/y2038_check.pl create mode 100644 scripts/system/check_mount.sh create mode 100644 scripts/system/find_dhcp_unassigned.sh create mode 100644 scripts/system/machine-update.pl create mode 100644 scripts/system/osx_cd_mounter.sh create mode 100644 scripts/system/redo_nvidia_driver.sh create mode 100644 scripts/system/write_uptime_report.sh create mode 100644 scripts/text/add_cr.pl create mode 100644 scripts/text/cpdiff.pl create mode 100644 scripts/text/cpdiffnow.pl create mode 100644 scripts/text/csv_compare.sh create mode 100644 scripts/text/csv_to_array.sh create mode 100644 scripts/text/diff_lib.pl create mode 100644 scripts/text/differ.pl create mode 100644 scripts/text/list_to_html.pl create mode 100644 scripts/text/nechung_signature.sh create mode 100644 scripts/text/new_sig.pl create mode 100644 scripts/text/smoove_lists.sh create mode 100644 scripts/text/strip_cr.pl create mode 100644 scripts/text/text_to_url.pl create mode 100644 scripts/tty/findterm.sh create mode 100644 scripts/tty/keep_awake.sh create mode 100644 scripts/tty/keep_awake_process.sh create mode 100644 scripts/tty/label_terminal_with_infos.sh create mode 100644 scripts/tty/lockup.sh create mode 100644 scripts/tty/set_term_title.sh create mode 100644 scripts/tty/sftp.sh create mode 100644 scripts/tty/ssh.sh create mode 100644 scripts/users/find_user.sh create mode 100644 scripts/users/findme.sh create mode 100644 scripts/web/javascript/dtree/dtree.css create mode 100644 scripts/web/javascript/dtree/dtree.js create mode 100644 scripts/web/javascript/gruntose/gruntomote.js create mode 100644 scripts/web/javascript/gruntose/launchers.js create mode 100644 scripts/web/javascript/gruntose/norse_date.js create mode 100644 scripts/web/javascript/gruntose/show_file.js create mode 100644 scripts/web/javascript/jquery/jquery-1.5.1.js create mode 100644 scripts/winders/exploder.sh create mode 100644 scripts/winders/find_64bit_bins.sh create mode 100644 scripts/winders/locate_function_in_lib.sh create mode 100644 scripts/winders/zap_msbuilds.sh create mode 100644 scripts/x_win/get_x_auth.sh create mode 100644 scripts/x_win/make_display.sh create mode 100644 scripts/x_win/webcam_snagger.sh create mode 100644 scripts/x_win/xtfont.sh diff --git a/core/.cproject b/core/.cproject new file mode 100644 index 00000000..13aecb09 --- /dev/null +++ b/core/.cproject @@ -0,0 +1,972 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +make + +all +true +true +true + + +make + +all +true +true +false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +make + +all +true +true +true + + +make + +all +true +true +false + + + + + + + + + diff --git a/core/.project b/core/.project new file mode 100644 index 00000000..6057985b --- /dev/null +++ b/core/.project @@ -0,0 +1,82 @@ + + + hoople_core + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + ?name? + + + + org.eclipse.cdt.make.core.append_environment + true + + + org.eclipse.cdt.make.core.autoBuildTarget + all + + + org.eclipse.cdt.make.core.buildArguments + + + + org.eclipse.cdt.make.core.buildCommand + make + + + org.eclipse.cdt.make.core.buildLocation + ${workspace_loc:/hoople_core/Debug} + + + org.eclipse.cdt.make.core.cleanBuildTarget + clean + + + org.eclipse.cdt.make.core.contents + org.eclipse.cdt.make.core.activeConfigSettings + + + org.eclipse.cdt.make.core.enableAutoBuild + false + + + org.eclipse.cdt.make.core.enableCleanBuild + true + + + org.eclipse.cdt.make.core.enableFullBuild + true + + + org.eclipse.cdt.make.core.fullBuildTarget + all + + + org.eclipse.cdt.make.core.stopOnError + true + + + org.eclipse.cdt.make.core.useDefaultBuildCmd + true + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/core/applications/bookmark_tools/bookmark_tree.cpp b/core/applications/bookmark_tools/bookmark_tree.cpp new file mode 100644 index 00000000..c8fc1f55 --- /dev/null +++ b/core/applications/bookmark_tools/bookmark_tree.cpp @@ -0,0 +1,485 @@ +/*****************************************************************************\ +* * +* Name : bookmark_tree * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "bookmark_tree.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +///#include //temp + +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace nodes; +using namespace structures; +using namespace textual; + +#define DEBUG_MARKS + // uncomment to have more debugging noise, but a reasonable amount. +//#define DEBUG_MARKS_TREE + // uncomment to get crazy noisy debug noise about tree traversal. + +#undef BASE_LOG +#define BASE_LOG(s) program_wide_logger::get().log(s) +#define SHOW_LINE a_sprintf("line %d: ", _line_number) +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), SHOW_LINE + s) +#define DEADLY_LINE (astring(func) + a_sprintf(", line %d: ", _line_number)) + +const int ESTIMATED_ELEMENTS = 100; + // we're planning for about this many links to be efficiently handled. + +const int MAX_LINE_SIZE = 256 * KILOBYTE; + // the largest line we'll process in the links database. + +// used to compare two strings while ignoring case; we use this to find +// our categories in the symbol table. +bool case_insense_compare(const astring &a, const astring &b) +{ return a.iequals(b); } + +//////////////////////////////////////////////////////////////////////////// + +listo_links::listo_links() : amorph(), _next_index(0) {} + +void listo_links::add(link_record *new_rec, bool sort) +{ + // we don't sort blank lines--they just get dropped in the end of + // the section. + if (sort && new_rec->_description.t()) { + for (int i = _next_index; i < elements(); i++) { + const astring &desc_cur = borrow(i)->_description; +//this check doesn't make much sense; it only checks if the description is equal? +// if it were really sorting, wouldn't it need to check if the check is greater than current? + if (desc_cur.iequals(new_rec->_description) +// || shouldn't there be a case for this being greater than the current??? + || !desc_cur) { + insert(i + 1, 1); + put(i + 1, new_rec); + return; + } + } + } + append(new_rec); + if (!sort) + _next_index = elements(); +} + +//////////////////////////////////////////////////////////////////////////// + +class symbol_int : public symbol_table +{ +public: + symbol_int() : symbol_table(10) {} +}; + +//////////////////////////////////////////////////////////////////////////// + +bookmark_tree::bookmark_tree() +: _line_number(0), + _mark_tree(new inner_mark_tree("Root", 0, ESTIMATED_ELEMENTS)), + _link_count(0), + _category_count(0), + _last_parent(_mark_tree), + _last_node(_mark_tree), + _links_seen(new symbol_int), + _category_names(new string_table) +{} + +bookmark_tree::~bookmark_tree() +{ + WHACK(_links_seen); + WHACK(_mark_tree); + WHACK(_category_names); +} + +void bookmark_tree::break_name(const astring &to_break, astring &name, + astring &nick) +{ + nick = astring::empty_string(); + name = to_break; + int indy = name.find('['); + if (negative(indy)) return; + nick = name.substring(indy + 1, name.end()); + while ( (nick[nick.end()] == ' ') || (nick[nick.end()] == ']') ) + nick.zap(nick.end(), nick.end()); + name.zap(indy, name.end()); + name.strip_spaces(); + nick.strip_spaces(); +} + +inner_mark_tree &bookmark_tree::access_root() { return *_mark_tree; } + +bool bookmark_tree::magic_category_comparison(const astring &a, const astring &b) +{ +// FUNCDEF("magic_category_comparison"); +//LOG(astring("compare: a=") + a + " b=" + b); + if (a.iequals(b)) return true; + astring a_name, a_nick; + break_name(a, a_name, a_nick); + astring b_name, b_nick; + break_name(b, b_name, b_nick); + if (a_name.iequals(b_name)) return true; + if (a_nick.t() && a_nick.iequals(b_name)) return true; + if (b_nick.t() && a_name.iequals(b_nick)) return true; + if (a_nick.t() && b_nick.t() && a_nick.iequals(b_nick)) return true; + return false; +} + +const astring &HTTP_HEAD = "http://"; +const astring &FTP_HEAD = "ftp://"; +const astring &WWW_SITE = "www."; +const astring &FTP_SITE = "ftp."; + +bool bookmark_tree::advance(int &index, const astring &check, const astring &finding) +{ + if (check.compare(finding, index, 0, finding.length(), false)) { + index += finding.length(); + return true; + } + return false; +} + +int bookmark_tree::find_prune_point(const astring &to_prune) +{ + int to_return = 0; + advance(to_return, to_prune, HTTP_HEAD); + advance(to_return, to_prune, FTP_HEAD); + advance(to_return, to_prune, WWW_SITE); + advance(to_return, to_prune, FTP_SITE); + return to_return; +} + +astring bookmark_tree::prune_link_down(const astring &to_prune) +{ +//printf("%s\n", (astring("pruned=") + to_prune.substring(find_prune_point(to_prune), to_prune.end())).s()); + return to_prune.substring(find_prune_point(to_prune), to_prune.end()); } + +bool bookmark_tree::excellent_link_comparator(const astring &a, const astring &b) +{ + int prune_a = find_prune_point(a); + int prune_b = find_prune_point(b); + int bigger_len = maximum(a.length() - prune_a, b.length() - prune_b); + bool to_return = a.compare(b, prune_a, prune_b, bigger_len, false); +//if (to_return) printf("%s and %s are equal.", a.s(), b.s()); + return to_return; +} + +inner_mark_tree *bookmark_tree::find_parent(const astring &parent_name) +{ + FUNCDEF("find_parent"); + // first, look for the node above the last parent. + inner_mark_tree *parent = dynamic_cast + (_last_parent->find(parent_name, inner_mark_tree::recurse_upward, + magic_category_comparison)); + +#ifdef DEBUG_MARKS_TREE + if (parent) + LOG(astring("trying upwards find for parent node ") + parent_name); +#endif + + if (!parent) { +#ifdef DEBUG_MARKS_TREE + LOG(astring("upwards find failed seeking on last_parent node ") + + parent_name); +#endif + + // we didn't find the parent above the last category... + parent = dynamic_cast(_last_node->find(parent_name, + inner_mark_tree::recurse_upward, magic_category_comparison)); + } + + if (!parent) { +#ifdef DEBUG_MARKS_TREE + LOG(astring("upwards find failed seeking on last_node ") + parent_name); +#endif + + // last node didn't help either. + parent = dynamic_cast(_mark_tree->find(parent_name, + inner_mark_tree::recurse_downward, magic_category_comparison)); + } + if (!parent) { + // failed to find the parent node, so hook it to the root node. + LOG(astring("failed to find parent node ") + parent_name); + + // create a parent node and use it for this guy. + inner_mark_tree *new_node = new inner_mark_tree(parent_name, + _line_number, ESTIMATED_ELEMENTS); + _mark_tree->attach(new_node); + _mark_tree->sort(); + _category_count++; + + parent = new_node; + } else { +#ifdef DEBUG_MARKS_TREE + LOG(astring("found parent node ") + parent_name); +#endif + } + + return parent; +} + +inner_mark_tree *bookmark_tree::process_category(const string_array &items) +{ + FUNCDEF("process_category"); + const astring &category_name = items[1]; + const astring &parent_name = items[2]; + + if (items.length() > 3) { + // complain about a possibly malformed category. + LOG(astring("category ") + category_name + " under " + parent_name + + " has extra fields!"); + } + +//BASE_LOG("CURRENT:"); +//BASE_LOG(_mark_tree->text_form()); + + // make sure we don't add anything to the tree if this is the root. + if (!parent_name || magic_category_comparison("Root", category_name)) { +#ifdef DEBUG_MARKS_TREE + LOG(astring("skipping parent node for ") + category_name); +#endif + return _mark_tree; + } + + // ensure that the categories aren't competing with other names. + astring main_name, nickname; + break_name(category_name, main_name, nickname); + astring *found1 = _category_names->find(main_name, case_insense_compare); + astring *found2 = _category_names->find(nickname, case_insense_compare); + if (found1 || found2) { + astring catnames; + if (found1) catnames = *found1; // add the first match, if it exists. + if (found2) { + if (!!catnames) catnames += " and "; + catnames += *found2; + } + LOG(astring("category name \"") + category_name + + "\" in conflict with existing: " + catnames); + inner_mark_tree *fake_it = NIL; + +//hmmm: neither of these are right; they need to use a comparator that +// uses our magic comparison function. + + if (found1) { +#ifdef DEBUG_MARKS + LOG(astring("found existing category for main name: ") + main_name); +#endif +// fake_it = (inner_mark_tree *)_mark_tree->find(*found1, +// symbol_tree::recurse_downward); + fake_it = dynamic_cast(_mark_tree->find + (*found1, inner_mark_tree::recurse_downward, + magic_category_comparison)); + } + if (fake_it) { +#ifdef DEBUG_MARKS + LOG(astring("returning existing category for main name: ") + main_name + + " as: " + fake_it->name()); +#endif + return fake_it; + } + if (found2) { +#ifdef DEBUG_MARKS + LOG(astring("found existing category for nickname: ") + nickname); +#endif +/// fake_it = (inner_mark_tree *)_mark_tree->find(*found2, +/// symbol_tree::recurse_downward); + fake_it = dynamic_cast(_mark_tree->find + (*found2, inner_mark_tree::recurse_downward, + magic_category_comparison)); + } + if (fake_it) { +#ifdef DEBUG_MARKS + LOG(astring("returning existing category for nickname: ") + nickname + + " as: " + fake_it->name()); +#endif + return fake_it; + } + LOG("==> failure to find a match for either category!"); + deadly_error(class_name(), func, "collision resolution code failed; " + "please fix category error"); + return NIL; + } + // now that we know these names are unique, we'll add them into the list + // so future categories can't reuse these. + _category_names->add(main_name, main_name); + if (!!nickname) _category_names->add(nickname, nickname); + + inner_mark_tree *parent = find_parent(parent_name); + _last_parent = parent; // set the parent for the next time. + + // see if the category is already present under the parent. + for (int i = 0; i < parent->branches(); i++) { + inner_mark_tree *curr = dynamic_cast(parent->branch(i)); + if (!curr) + non_continuable_error(class_name(), DEADLY_LINE, "missing branch in tree"); +#ifdef DEBUG_MARKS_TREE + LOG(astring("looking at branch ") + curr->name()); +#endif + if (magic_category_comparison(curr->name(), category_name)) { + // it already exists? argh. + LOG(astring("category ") + category_name + " already exists under " + + parent_name + "."); + _last_node = curr; + return curr; + } + } + + inner_mark_tree *new_node = new inner_mark_tree(category_name, + _line_number, ESTIMATED_ELEMENTS); + parent->attach(new_node); + parent->sort(); + _last_node = new_node; + + _category_count++; + +#ifdef DEBUG_MARKS_TREE + LOG(astring("attaching node ") + category_name + " to parent " + + parent->name()); +#endif + return new_node; +} + +void bookmark_tree::process_link(const string_array &items) +{ + FUNCDEF("process_link"); + astring description = items[1]; + astring parent_name = items[2]; + astring url = "UNKNOWN"; + if (items.length() >= 4) url = items[3]; + + // strip any directory slashes that are provided as a suffix. we don't need + // them and they tend to confuse the issue when we look for duplicates. + while (url[url.end()] == '/') { + url.zap(url.end(), url.end()); + } + + // make some noise if they seem to have a badly formed link. + if (items.length() < 4) { + LOG(astring("link ") + description + " under " + parent_name + + " has no URL!"); + } else if (items.length() > 4) { + LOG(astring("link ") + description + " under " + parent_name + + " has extra fields!"); + } + + // find the parent for this link. + inner_mark_tree *parent = find_parent(parent_name); + _last_parent = parent; // set the parent for the next time. + + // see if the link already exists. + int *found = _links_seen->find(url, excellent_link_comparator); + if (found) { + // this is not so great; a duplicate link has been found. + LOG(a_sprintf("Duplicate Link: line %d already has ", *found) + url); + return; + } else { + _links_seen->add(prune_link_down(url), _line_number); + } + + // add the link to the parent. + link_record *new_rec = new link_record(description, url, _line_number); + parent->_links.add(new_rec); + + _link_count++; +} + +void bookmark_tree::process_comment(const astring ¤t_line_in) +{ +/// FUNCDEF("process_comment"); + astring current_line = current_line_in; + + // output the comment as simple text. +//BASE_LOG("comment case"); + if (current_line.contains("http:")) { + astring hold_curr = current_line; + int indy = current_line.find("http:"); + hold_curr.zap(0, indy - 1); + current_line = astring("        " + "" + hold_curr + ""; + } else if (current_line.t()) { + // snap the comment character off of the front. + current_line.zap(0, 0); + } + + link_record *new_rec = new link_record(current_line, + astring::empty_string(), _line_number); + _last_node->_links.add(new_rec, false); +} + +int bookmark_tree::read_csv_file(const astring &input_filename) +{ + FUNCDEF("read_csv_file"); + byte_filer input_file(input_filename, "r"); + if (!input_file.good()) + non_continuable_error(class_name(), DEADLY_LINE, + "the input file could not be opened"); + + string_array items; // parsed in csv line. + astring current_line; // read from input file. + + // read the lines in the file, one at a time. + while (input_file.getline(current_line, MAX_LINE_SIZE) > 0) { + _line_number++; // go to the next line. + // remove the carriage returns first. + while (parser_bits::is_eol(current_line[current_line.end()])) { + current_line.zap(current_line.end(), current_line.end()); + } + current_line.strip_spaces(); + if (!current_line.length()) { +// // blank lines get treated as a special case. they are always added +// // at the end of the list. +// process_comment(current_line); + continue; + } else if (current_line[0] == '#') { + // handle a comment in the database. + process_comment(current_line); + } else { + // csv parse the line, since we don't support much else. + bool parsed = list_parsing::parse_csv_line(current_line, items); + if (!parsed) + non_continuable_error(class_name(), DEADLY_LINE, + astring("the line could not be parsed: ") + current_line); + if (!items.length()) { + LOG("bad formatting on this line."); + continue; + } + if (items[0].iequals("C")) { + inner_mark_tree *node = process_category(items); + if (!node) { + LOG(astring("failed to get a node for ") + items[1]); + } + } else if (items[0].iequals("L")) { + process_link(items); + } else { + non_continuable_error(class_name(), DEADLY_LINE, + astring("unknown format in line: ") + current_line); + } + } + } + return 0; +} + diff --git a/core/applications/bookmark_tools/bookmark_tree.h b/core/applications/bookmark_tools/bookmark_tree.h new file mode 100644 index 00000000..744d9fd3 --- /dev/null +++ b/core/applications/bookmark_tools/bookmark_tree.h @@ -0,0 +1,144 @@ +#ifndef BOOKMARK_TREE_CLASS +#define BOOKMARK_TREE_CLASS + +/*****************************************************************************\ +* * +* Name : bookmark_tree * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Parses a link database in HOOPLE format into tree structure. * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include + +// forward. +class inner_mark_tree; +class link_record; +class listo_links; +class symbol_int; + +//////////////////////////////////////////////////////////////////////////// + +class bookmark_tree +{ +public: + bookmark_tree(); + virtual ~bookmark_tree(); + DEFINE_CLASS_NAME("bookmark_tree"); + + int read_csv_file(const basis::astring &input_filename); + // reads the file contents of "input_filename" into this tree. + + static void break_name(const basis::astring &to_break, basis::astring &name, + basis::astring &nick); + // breaks a category name into the two components, if they exist. + + static bool magic_category_comparison(const basis::astring &a, const basis::astring &b); + // compares the two strings "a" and "b" and returns true if either the + // main name or the nickname matches either. + + static basis::astring prune_link_down(const basis::astring &to_prune); + // reduces a URL to its bare bones. it will strip out the "http://" and "www." and such. + + static bool excellent_link_comparator(const basis::astring &a, const basis::astring &b); + // a string comparator that handles how links are often formed. it uses the link pruner + // to decide whether the links are equal at their root. + + inner_mark_tree *process_category(const structures::string_array &items); + // handles category declarations and adds the new category to our list. + // this tries to do the intelligent thing if the category is already + // found to exist, meaning that the file has a duplicate category + // definitions. + + void process_link(const structures::string_array &items); + + void process_comment(const basis::astring ¤t_line_in); + + inner_mark_tree *find_parent(const basis::astring &parent_name); + // locates the parent called "parent_name" given the context that + // we've saved about the last parent. + + static bool advance(int &index, const basis::astring &check, const basis::astring &finding); + //!< moves the "index" forward if the "finding" string is the head of "check". + + static int find_prune_point(const basis::astring &to_prune); + //!< attempts to locate the real start of the root URL in "to_prune". + + // these provide access to the information held about the tree... + + inner_mark_tree &access_root(); // allows access to the root of the tree. + + int link_count() const { return _link_count; } + + int category_count() const { return _category_count; } + +// public data members... currently this is used outside the class. + int _line_number; // the current line in the database. + +private: + inner_mark_tree *_mark_tree; // our tree of categories. + int _link_count; // number of links. + int _category_count; // number of categories. + inner_mark_tree *_last_parent; // the last parent we saw. + inner_mark_tree *_last_node; // the last node we touched. + symbol_int *_links_seen; // URLs we've seen. + structures::string_table *_category_names; // used to enforce uniqueness of categories. +}; + +//////////////////////////////////////////////////////////////////////////// + +class link_record +{ +public: + basis::astring _description; + basis::astring _url; + int _uid; + + link_record(const basis::astring &description, const basis::astring &url, int uid) + : _description(description), _url(url), _uid(uid) {} +}; + +//////////////////////////////////////////////////////////////////////////// + +class listo_links : public structures::amorph +{ +public: + listo_links(); + + void add(link_record *new_rec, bool sort = true); + +private: + int _next_index; // tracks where we've added unsorted items. +}; + +//////////////////////////////////////////////////////////////////////////// + +class inner_mark_tree : public nodes::symbol_tree +{ +public: + listo_links _links; // the list held at this node. + int _uid; // the unique identifier of this node. + + inner_mark_tree(const basis::astring &node_name, int uid, int max_bits = 2) + : nodes::symbol_tree(node_name, max_bits), _uid(uid) {} + +}; + +//////////////////////////////////////////////////////////////////////////// + +#endif + diff --git a/core/applications/bookmark_tools/bookmark_version.rc b/core/applications/bookmark_tools/bookmark_version.rc new file mode 100644 index 00000000..64c09f6e --- /dev/null +++ b/core/applications/bookmark_tools/bookmark_version.rc @@ -0,0 +1,46 @@ +#ifndef NO_VERSION +#include +#include <__build_version.h> +#include <__build_configuration.h> +#define BI_PLAT_WIN32 + // force 32 bit compile. +1 VERSIONINFO LOADONCALL MOVEABLE +FILEVERSION __build_FILE_VERSION_COMMAS +PRODUCTVERSION __build_PRODUCT_VERSION_COMMAS +FILEFLAGSMASK 0 +FILEFLAGS VS_FFI_FILEFLAGSMASK +#if defined(BI_PLAT_WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + // Language type = U.S. English(0x0409) and Character Set = Windows, Multilingual(0x04b0) + BLOCK "040904b0" // Matches VarFileInfo Translation hex value. + BEGIN + VALUE "CompanyName", __build_company "\000" +#ifndef _DEBUG + VALUE "FileDescription", "Bookmark Utilities\000" +#else + VALUE "FileDescription", "Bookmark Utilities (DEBUG)\000" +#endif + VALUE "FileVersion", __build_FILE_VERSION "\000" + VALUE "ProductVersion", __build_PRODUCT_VERSION "\000" + VALUE "InternalName", "Bookmark Apps\000" + VALUE "LegalCopyright", __build_copyright "\000" + VALUE "LegalTrademarks", __build_legal_info "\000" + VALUE "OriginalFilename", "bookmark.exe\000" + VALUE "ProductName", __build_product_name "\000" + + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04b0 // US English (0x0409) and win32 multilingual (0x04b0) + END +END +#endif diff --git a/core/applications/bookmark_tools/example_crash_file_for_marks.csv b/core/applications/bookmark_tools/example_crash_file_for_marks.csv new file mode 100644 index 00000000..3bbff089 --- /dev/null +++ b/core/applications/bookmark_tools/example_crash_file_for_marks.csv @@ -0,0 +1,23 @@ +"L","Shawn's Occultism and Religion Resources","Eclectic","http://valen.dws.acs.cmu.edu/occult/" +"L","Software, Unix and Music - Paladin Corporation","Research","http://www.paladincorp.com.au/" +"L","southwest indian foundation","unsorted","http://www.southwestindia.com" +"L","Sparptoe.com CD's","Music Archives","http://www.sharptoe.com/toe/cds.htm" +"L","Starbase - Formerly Premia Corporation","Tools","http://www.premia.com/" +"L","superpages.com's BigBook","People","http://bigbook.com/" +"L","Technische Universität Darmstadt","schools","http://www.th-darmstadt.de/" +"L","The Art Bell Web Site","Radio","http://www.artbell.com/" +"L","The iBrator.","Cyber","http://www.briarskin.com/humor/ibrator/" +"L","The Louvre","museums","http://louvre.edu" +"L","The Morrígan's WebAerie","Celtic","http://morrigan.alabanza.com/" +"L","The Net Censorship Dilemma: Liberty or Tyranny","Legalizing Freedom","http://rene.efa.org.au/liberty/" +"L","TombTown home page","web","http://www.tombtown.com/" +"L","Tom Valesky's Old Old Home On the Wild, Wild Worldwide Web","Thomas Valesky","http://www.site.gmu.edu/tvalesky/" +"L","Tuning Apache for Performance","apache","http://php.weblogs.com/tuning_apache_unix" +"L","VNC - Virtual Network Computing from ATT Laboratories Cambridge","ent_man","http://www.uk.research.att.com/vnc/" +"L","Welcome to Caligari Creator of trueSpace","Tools","http://www.caligari.com/" +"L","Welcome to QGPC Home Page","Chemicals","http://www.qgpc.com.qa/" +"L","Welcome to the Inquirer","papers","http://www.theinquirer.net/" +"L","Welcome To The Sequoia Bison Society!","Herd","http://www.sequoiabison.org/" +"L","Welcome to Virtual Pooh Sticks","Automata","http://pooh.muscat.co.uk/pooh-sticks/" +"L","Welcome to Webcorp Multimedia!","Sounds","http://www.webcorp.com/sounds/" +"L","Welcome to web.life.sucks","Nauseating","http://www.alt-life-sucks.org/" diff --git a/core/applications/bookmark_tools/js_marks_maker.cpp b/core/applications/bookmark_tools/js_marks_maker.cpp new file mode 100644 index 00000000..d32f50f4 --- /dev/null +++ b/core/applications/bookmark_tools/js_marks_maker.cpp @@ -0,0 +1,347 @@ +/*****************************************************************************\ +* * +* Name : marks_maker_javascript * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Turns a link database in HOOPLE format into a web page, when given a * +* suitable template file. The template file must have the phrase: * +* $INSERT_LINKS_HERE * +* at the point where the generated links are supposed to be stored. * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "bookmark_tree.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace nodes; +using namespace structures; +using namespace textual; +using namespace timely; + +//#define DEBUG_MARKS + // uncomment to have more debugging noise. + +#undef BASE_LOG +#define BASE_LOG(s) program_wide_logger::get().log(s, ALWAYS_PRINT) +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), \ + a_sprintf("line %d: ", _categories._line_number) + s, ALWAYS_PRINT) + +const int MAX_FILE_SIZE = 4 * MEGABYTE; + // the largest file we'll read. + +//////////////////////////////////////////////////////////////////////////// + +class marks_maker_javascript : public application_shell +{ +public: + marks_maker_javascript() : application_shell(), _need_closure(false), + _loader_count(0), _link_spool(0), _functions_pending(0) {} + DEFINE_CLASS_NAME("marks_maker_javascript"); + virtual int execute(); + int print_instructions(const filename &program_name); + + int write_marks_page(const astring &output_filename, + const astring &template_filename); + // given a tree of links, this writes out a web page to "output_filename" + // using a template file "template_filename". + +private: + bookmark_tree _categories; // our tree of categories. + bool _need_closure; // true if our
needs a closure. + int _loader_count; // count of the loader functions. + int _link_spool; // count of which link we're writing. + stack _functions_pending; // used for javascript node functions. + +//this needs to gather any strings that would have gone into functions. +//instead, they need to be written into the current node's string. +//when a new function def would be seen, then we need to push a new node +//for accumulating the text. + + // these handle outputting text for categories and links. + void write_category(inner_mark_tree *node, astring &output); + void write_link(inner_mark_tree *node, const link_record &linko, + astring &output); +}; + +//////////////////////////////////////////////////////////////////////////// + +int marks_maker_javascript::print_instructions(const filename &program_name) +{ + a_sprintf to_show("%s:\n\ +This program needs three filenames as command line parameters. The -i flag\n\ +is used to specify the input filename, the -t flag specifies a template web\n\ +page which is used as the wrapper around the links, and the -o flag specifies\n\ +the web page to be created. The input file is expected to be in the HOOPLE\n\ +link database format. The output file will be created from the template file\n\ +by finding the phrase $INSERT_LINKS_HERE in it and replacing that with html\n\ +formatted link and categories from the input file. Another tag of $TODAYS_DATE\n\ +will be replaced with the date when the output file is regenerated.\n\ +The HOOPLE link format is documented here:\n\ + http://hoople.org/guides/link_database/format_manifesto.txt\n\ +", program_name.basename().raw().s(), program_name.basename().raw().s()); + program_wide_logger::get().log(to_show, ALWAYS_PRINT); + return 12; +} + +void marks_maker_javascript::write_category(inner_mark_tree *node, astring &output) +{ +// FUNCDEF("write_category"); + // output a javascript line for the category. + + int node_num = node->_uid; + inner_mark_tree *parent = dynamic_cast(node->parent()); + int parent_node = parent? parent->_uid : -1; + // the parent node for root is a negative one. + astring chewed_name = node->name(); + for (int i = chewed_name.end(); i >= 0; i--) { + // escape any raw single quotes that we see. + if (chewed_name[i] == '\'') { + chewed_name.zap(i, i); + chewed_name.insert(i, "\\'"); + } + } + output += a_sprintf(" b.add(%d, %d, '%s');\n", node_num, parent_node, + chewed_name.s()); +} + +void marks_maker_javascript::write_link(inner_mark_tree *node, const link_record &linko, + astring &output) +{ +// FUNCDEF("write_link"); + // write a javascript link definition. + int parent_node = node->_uid; + astring chewed_name = linko._description; + for (int i = chewed_name.end(); i >= 0; i--) { + // escape any raw single quotes that we see. + if (chewed_name[i] == '\'') { + chewed_name.zap(i, i); + chewed_name.insert(i, "\\'"); + } + } + + if (!linko._url) { + // this just appears to be a comment line. + if (!linko._description) return; // it's a nothing line. + +/* +//hmmm: probably not what we want. +//hmmm: why not, again? + output += linko._description; + output += "
"; + output += parser_bits::platform_eol_to_chars(); +*/ + return; + } + + // generate a function header if the number of links is a multiple of 100. + if (! (_link_spool % 100) ) { + if (_link_spool) { + // close out the previous function and set a timeout. + output += " setTimeout('run_tree_loaders()', 0);\n"; + output += "}\n"; + } + + output += a_sprintf("function tree_loader_%d() {\n", _loader_count++); + } + _link_spool++; + + output += a_sprintf(" b.add(%d, %d, '%s', '%s');\n", + linko._uid, parent_node, chewed_name.s(), linko._url.s()); +} + +int marks_maker_javascript::execute() +{ +// FUNCDEF("execute"); + SETUP_COMBO_LOGGER; + + command_line cmds(_global_argc, _global_argv); // process the command line parameters. + astring input_filename; // we'll store our link database name here. + astring output_filename; // where the web page we're creating goes. + astring template_filename; // the wrapper html code that we'll stuff. + if (!cmds.get_value('i', input_filename, false)) + return print_instructions(cmds.program_name()); + if (!cmds.get_value('o', output_filename, false)) + return print_instructions(cmds.program_name()); + if (!cmds.get_value('t', template_filename, false)) + return print_instructions(cmds.program_name()); + + BASE_LOG(astring("input file: ") + input_filename); + BASE_LOG(astring("output file: ") + output_filename); + BASE_LOG(astring("template file: ") + template_filename); + + int ret = _categories.read_csv_file(input_filename); + if (ret) return ret; + + ret = write_marks_page(output_filename, template_filename); + if (ret) return ret; + + return 0; +} + +int marks_maker_javascript::write_marks_page(const astring &output_filename, + const astring &template_filename) +{ + FUNCDEF("write_marks_page"); + astring long_string; + // this is our accumulator of links. it is the semi-final result that will + // be injected into the template file. + + // add our target layer so that we can write to a useful place. + long_string += "
Marks Target
\n"; + + // add the tree style and creation of the tree object into the text. + long_string += "\n
\n"; + long_string += "\n"; + long_string += "

open all | " + "close all

\n"; + long_string += "
\n"; + + byte_filer template_file(template_filename, "r"); + astring full_template; + if (!template_file.good()) + non_continuable_error(class_name(), func, "the template file could not be opened"); + template_file.read(full_template, MAX_FILE_SIZE); + template_file.close(); + + // javascript output needs some extra junk added to the header section. + int indy = full_template.ifind(""); + if (negative(indy)) + non_continuable_error(class_name(), func, "the template file is missing " + "a declaration"); +//hmmm: the path here must be configurable! + full_template.insert(indy + 8, "\n\n" + "\n" + "\n"); + + // replace the tag with the long string we created. + bool found_it = full_template.replace("$INSERT_LINKS_HERE", long_string); + if (!found_it) + non_continuable_error(class_name(), func, "the template file is missing " + "the insertion point"); + full_template.replace("$TODAYS_DATE", time_stamp::notarize(true)); + + filename outname(output_filename); + if (outname.exists()) { + non_continuable_error(class_name(), func, astring("the output file ") + + output_filename + " already exists. It would be over-written if " + "we continued."); + } + + byte_filer output_file(output_filename, "w"); + if (!output_file.good()) + non_continuable_error(class_name(), func, "the output file could not be opened"); + // write the newly generated web page out now. + output_file.write(full_template); + output_file.close(); + +// show the tree. +// BASE_LOG(""); +// BASE_LOG(_categories.access_root().text_form()); + + BASE_LOG(a_sprintf("wrote %d links in %d categories.", + _categories.link_count(), _categories.category_count())); + BASE_LOG(astring("")); + + return 0; +} + +//////////////////////////////////////////////////////////////////////////// + +HOOPLE_MAIN(marks_maker_javascript, ) + diff --git a/core/applications/bookmark_tools/link_parser.cpp b/core/applications/bookmark_tools/link_parser.cpp new file mode 100644 index 00000000..1dd0f9ed --- /dev/null +++ b/core/applications/bookmark_tools/link_parser.cpp @@ -0,0 +1,496 @@ +/*****************************************************************************\ +* * +* Name : link_parser * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Processes html files and finds the links. A database in the HOOPLE * +* link format is created from the links found. * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +// Notes: +// +// the standard link structure in html is similar to this: +// Link Name and Launching Point +// +// the standard we adopt for section titles is that it must be a heading +// marker. that formatting looks like this, for example: +//

The Section Title:

+ +#include "bookmark_tree.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; + +#undef BASE_LOG +#define BASE_LOG(s) program_wide_logger::get().log(s, ALWAYS_PRINT) +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +//#define DEBUG_LINK_PARSER + // uncomment for noisier run to seek problems. + +//////////////////////////////////////////////////////////////////////////// + +const int MAX_FILE_SIZE = 4 * MEGABYTE; + // this is the largest html file size we will process. + +//////////////////////////////////////////////////////////////////////////// + +// a macro that increments the position in the string and restarts the loop. +#define INCREM_N_GO { curr_index++; continue; } + +// puts the current character on the intermediate string. +#define ADD_INTERMEDIATE \ + intermediate_text += full_contents[curr_index] + +// returns a character in lower-case, if 'a' is in upper case. +char normalize_char(char a) +{ + if ( (a >= 'A') && (a <= 'Z') ) return a + 'a' - 'A'; + return a; +} + +// returns true if the two characters are the same, ignoring upper/lower case. +bool caseless_equals(char a, char b) { return normalize_char(a) == normalize_char(b); } + +// a macro that skips all characters until the specified one is seen. +#define JUMP_TO_CHAR(to_find, save_them) { \ + while ( (curr_index < full_contents.length()) \ + && !caseless_equals(to_find, full_contents[curr_index]) ) { \ + if (save_them) ADD_INTERMEDIATE; \ + curr_index++; \ + } \ +} + +// increments the state, the current character and restarts the loop. +#define NEXT_STATE_INCREM { \ + state = parsing_states(state+1); /* move forward in states. */ \ + curr_index++; \ + continue; \ +} + +// cleans out the disallowed characters in the string provided. +#define CLEAN_UP_NAUGHTY(s) { \ + while (s.replace("\n", " ")) {} \ + while (s.replace("\r", "")) {} \ + s.strip_spaces(); \ +} + +//was before the strip_spaces code above. +/* + int indy = s.find("--"); \ + while (non_negative(indy)) { \ + s[indy] = ' '; / * replace the first dash with a space. * / \ + for (int i = indy + 1; i < s.length(); i++) { \ + if (s[i] != '-') break; \ + s.zap(i, i); \ + i--; \ + } \ + indy = s.find("--"); \ + } \ + while (s.replace(" ", " ")) {} \ +*/ + +// cleans up underscores in areas that are supposed to be english. +#define MAKE_MORE_ENGLISH(s) \ + s.replace_all('_', ' ') + +void strain_out_html_codes(astring &to_edit) +{ + for (int i = 0; i < to_edit.length(); i++) { + if (to_edit[i] != '<') continue; + // found a left bracket. + int indy = to_edit.find('>', i); + if (negative(indy)) return; // bail out, unexpected unmatched bracket. + to_edit.zap(i, indy); + i--; // skip back to reconsider current place. + } +} + +// writes out the currently accumulated link info. +#define WRITE_LINK { \ + /* clean naughty characters out of the names. */ \ + CLEAN_UP_NAUGHTY(url_string); \ + CLEAN_UP_NAUGHTY(name_string); \ + if (url_string.ends(name_string)) { \ + /* handle the name being boring. replace with the intermediate text. */ \ + MAKE_MORE_ENGLISH(intermediate_text); \ + strain_out_html_codes(intermediate_text); \ + CLEAN_UP_NAUGHTY(intermediate_text); \ + if (intermediate_text.length()) \ + name_string = intermediate_text; \ + } \ + /* output a link in the HOOPLE format. */ \ + astring to_write = "\"L\",\""; \ + to_write += translate_web_chars(name_string); \ + to_write += "\",\""; \ + to_write += abbreviate_category(last_heading); \ + to_write += "\",\""; \ + to_write += translate_web_chars(url_string); \ + to_write += "\"\n"; \ + output_file.write(to_write); \ + _link_count++; \ +} + +// writes out the current section in the HOOPLE format. +// currently the parent category is set to Root. +#define WRITE_SECTION { \ + CLEAN_UP_NAUGHTY(last_heading); /* clean the name. */ \ + /* output a category definition. */ \ + astring to_write = "\"C\",\""; \ + to_write += last_heading; \ + to_write += "\",\""; \ + to_write += abbreviate_category(last_parents.top()); \ + to_write += "\"\n"; \ + output_file.write(to_write); \ + _category_count++; \ +} + +// clears our accumulator strings. +#define RESET_STRINGS { \ + url_string = astring::empty_string(); \ + name_string = astring::empty_string(); \ + intermediate_text = astring::empty_string(); \ +} + +//////////////////////////////////////////////////////////////////////////// + +class link_parser : public application_shell +{ +public: + link_parser(); + DEFINE_CLASS_NAME("link_parser"); + virtual int execute(); + int print_instructions(const filename &program_name); + +private: + int _link_count; // number of links. + int _category_count; // number of categories. + + astring url_string; // the URL we've parsed. + astring name_string; // the name that we've parsed for the URL. + astring last_heading; // the last name that was set for a section. + stack last_parents; // the history of the parent names. + astring intermediate_text; // strings we saw before a link. + + astring heading_num; + // this string form of a number tracks what kind of heading was started. + + astring abbreviate_category(const astring &simplify); + // returns the inner category nickname if the category has one. + + astring translate_web_chars(const astring &vervoom); + // translates a few web chars that are safe for csv back into their non-encoded form. +}; + +//////////////////////////////////////////////////////////////////////////// + +link_parser::link_parser() +: application_shell(), + _link_count(0), + _category_count(0), + last_heading("Root"), + last_parents() +{ + last_parents.push(last_heading); // make sure we have at least one level. +} + +int link_parser::print_instructions(const filename &program_name) +{ + a_sprintf to_show("%s:\n\ +This program needs two filenames as command line parameters. The -i flag\n\ +is used to specify the input filename and the -o flag specifies the output\n\ +file to be created. The input file is expected to be an html file\n\ +containing links to assorted web sites. The links are gathered, along with\n\ +descriptive text that happens to be near them, to create a link database in\n\ +the HOOPLE link format and write it to the output file. HOOPLE link format\n\ +is basically a CSV file that defines the columns 1-4 for describing either\n\ +link categories (which support hierarchies) or actual links (i.e., URLs of\n\ +interest). The links are written to a CSV file in the standard HOOPLE link\n\ +The HOOPLE link format is documented here:\n\ + http://hoople.org/guides/link_database/format_manifesto.txt\n\ +", program_name.basename().raw().s(), program_name.basename().raw().s()); + program_wide_logger::get().log(to_show, ALWAYS_PRINT); + return 12; +} + +astring link_parser::abbreviate_category(const astring &simplify) +{ + astring to_return; + astring name_portion; + bookmark_tree::break_name(simplify, name_portion, to_return); + if (!to_return) return name_portion; + return to_return; +} + +astring link_parser::translate_web_chars(const astring &vervoom) +{ + astring to_return = vervoom; + to_return.replace_all("&", "&"); + to_return.replace_all("%7E", "~"); + return to_return; +} + +int link_parser::execute() +{ + FUNCDEF("main"); + command_line cmds(_global_argc, _global_argv); // process the command line parameters. + astring input_filename; // we'll store our bookmarks file's name here. + astring output_filename; // where the processed marks go. + if (!cmds.get_value('i', input_filename, false)) + return print_instructions(cmds.program_name()); + if (!cmds.get_value('o', output_filename, false)) + return print_instructions(cmds.program_name()); + + BASE_LOG(astring("input file: ") + input_filename); + BASE_LOG(astring("output file: ") + output_filename); + + astring full_contents; + byte_filer input_file(input_filename, "r"); + if (!input_file.good()) + non_continuable_error(class_name(), func, "the input file could not be opened"); + input_file.read(full_contents, MAX_FILE_SIZE); + input_file.close(); + + filename outname(output_filename); + if (outname.exists()) { + non_continuable_error(class_name(), func, astring("the output file ") + + output_filename + " already exists. It would be over-written if " + "we continued."); + } + + byte_filer output_file(output_filename, "w"); + if (!output_file.good()) + non_continuable_error(class_name(), func, "the output file could not be opened"); + + enum parsing_states { + // the states below are order dependent; do not change the ordering! + SEEKING_LINK_START, // looking for the beginning of an html link. + SEEKING_HREF, // finding the href portion of the link. + GETTING_URL, // chowing on the URL portion of the link. + SEEKING_NAME, // finding the closing bracket of the . + GETTING_NAME, // chowing down on characters in the link's name. + SEEKING_CLOSURE, // looking for the to end the link. + // there is a discontinuity after SEEKING_CLOSURE, but then the following + // states are also order dependent. + SAW_TITLE_START, // the beginning of a section heading was seen. + GETTING_TITLE, // grabbing characters in the title. + // new text processing states. + SAW_NESTING_INCREASE, // a new nesting level has been achieved. + SAW_NESTING_DECREASE, // we exited from a former nesting level. + }; + + int curr_index = 0; + parsing_states state = SEEKING_LINK_START; + while (curr_index < full_contents.length()) { + switch (state) { + case SEEKING_LINK_START: + // if we don't see a less-than, then it's not the start of html code, + // so we'll ignore it for now. + if (full_contents[curr_index] != '<') { + ADD_INTERMEDIATE; + INCREM_N_GO; + } + // found a left angle bracket, so now we need to decided where to go next for parsing + // the html coming up. + curr_index++; + // see if this is a heading. if so, we can snag the heading name. + if (caseless_equals('h', full_contents[curr_index])) { +#ifdef DEBUG_LINK_PARSER + LOG("into the '= '0') && (next <= '9') ) { + // we found our proper character for starting a heading. we need + // to jump into that state now. we'll leave the cursor at the + // beginning of the number. + state = SAW_TITLE_START; + INCREM_N_GO; + } + } + // check if they're telling us a new indentation level of the type we care about. + if (caseless_equals('d', full_contents[curr_index])) { +#ifdef DEBUG_LINK_PARSER + LOG("into the ' tag. + char next = full_contents[curr_index + 1]; + if (caseless_equals(next, 'l')) { +#ifdef DEBUG_LINK_PARSER + LOG("into the ' tag. + if ( caseless_equals(full_contents[curr_index + 1], 'd') + && caseless_equals(full_contents[curr_index + 2], 'l') ) { +#ifdef DEBUG_LINK_PARSER + LOG("into the '', true); + continue; + } +#ifdef DEBUG_LINK_PARSER + LOG("into the final case, the '', true); + continue; + } + // this looks like an address so find the start of the href. + NEXT_STATE_INCREM; + break; + case SAW_NESTING_INCREASE: + last_parents.push(last_heading); +#ifdef DEBUG_LINK_PARSER + LOG(a_sprintf("nesting inwards, new depth %d", last_parents.size())); +#endif + JUMP_TO_CHAR('>', false); + state = SEEKING_LINK_START; + break; + case SAW_NESTING_DECREASE: + last_parents.pop(); +#ifdef DEBUG_LINK_PARSER + LOG(a_sprintf("nesting outwards, new depth %d", last_parents.size())); +#endif + JUMP_TO_CHAR('>', false); + state = SEEKING_LINK_START; + break; + case SEEKING_HREF: + JUMP_TO_CHAR('h', false); // find the next 'h' for "href". + curr_index++; + if (!caseless_equals('r', full_contents[curr_index])) continue; + curr_index++; + if (!caseless_equals('e', full_contents[curr_index])) continue; + curr_index++; + if (!caseless_equals('f', full_contents[curr_index])) continue; + curr_index++; + if (full_contents[curr_index] != '=') continue; + curr_index++; + if (full_contents[curr_index] != '"') continue; + // whew, got through the word href and the assignment. the rest + // should all be part of the link. + NEXT_STATE_INCREM; + break; + case GETTING_URL: + // as long as we don't see the closure of the quoted string for the + // href, then we can keep accumulating characters from it. + if (full_contents[curr_index] == '"') NEXT_STATE_INCREM; + url_string += full_contents[curr_index]; + INCREM_N_GO; // keep chewing on it in this same state. + break; + case SEEKING_NAME: + JUMP_TO_CHAR('>', false); // find closing bracket. + NEXT_STATE_INCREM; // now start grabbing the name characters. + break; + case GETTING_NAME: + // we have to stop grabbing name characters when we spy a new code + // being started. + if (full_contents[curr_index] == '<') { + // if we see a closing command, then we assume it's the one we want. + if (full_contents[curr_index + 1] == '/') + NEXT_STATE_INCREM; + // if we see html inside the name, we just throw it out. + JUMP_TO_CHAR('>', false); + curr_index++; + continue; + } + name_string += full_contents[curr_index]; + INCREM_N_GO; // keep chewing on it in this same state. + break; + case SEEKING_CLOSURE: + JUMP_TO_CHAR('>', false); // find the closure of the html code. + // write the link out now. + WRITE_LINK; + // clean out our accumulated strings. + RESET_STRINGS; + state = SEEKING_LINK_START; + INCREM_N_GO; + break; + case SAW_TITLE_START: + heading_num = full_contents.substring(curr_index, curr_index); + JUMP_TO_CHAR('>', false); + NEXT_STATE_INCREM; // start eating the name. + break; + case GETTING_TITLE: { + int indy = full_contents.find('<', curr_index); + if (negative(indy)) { + state = SEEKING_LINK_START; // too weird, go back to start. + continue; + } + // push the last title if it differs from the top of the stack. + last_heading = full_contents.substring(curr_index, indy - 1); + WRITE_SECTION; + JUMP_TO_CHAR('<', false); // now find the start of the header closure. + JUMP_TO_CHAR('>', false); // now find the end of the header closure. + RESET_STRINGS; + state = SEEKING_LINK_START; // successfully found section name. + break; + } + default: + non_continuable_error(class_name(), func, "entered erroneous state!"); + } + } + + if (url_string.t()) WRITE_LINK; + + output_file.close(); + + BASE_LOG(a_sprintf("wrote %d links in %d categories.", _link_count, + _category_count)); + + return 0; +} + +//////////////////////////////////////////////////////////////////////////// + +HOOPLE_MAIN(link_parser, ) + diff --git a/core/applications/bookmark_tools/makefile b/core/applications/bookmark_tools/makefile new file mode 100644 index 00000000..655c9373 --- /dev/null +++ b/core/applications/bookmark_tools/makefile @@ -0,0 +1,14 @@ +CONSOLE_MODE = true + +include cpp/variables.def + +PROJECT = bookmark_tools +TYPE = application +SOURCE = bookmark_tree.cpp bookmark_version.rc +LOCAL_LIBS_USED += application configuration filesystem loggers mathematics nodes \ + processes textual timely structures basis +TARGETS = js_marks_maker.exe link_parser.exe marks_checker.exe marks_maker.exe marks_sorter.exe +USE_CURL = t + +include cpp/rules.def + diff --git a/core/applications/bookmark_tools/marks_checker.cpp b/core/applications/bookmark_tools/marks_checker.cpp new file mode 100644 index 00000000..1e2c6f07 --- /dev/null +++ b/core/applications/bookmark_tools/marks_checker.cpp @@ -0,0 +1,453 @@ +/*****************************************************************************\ +* * +* Name : marks_checker * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Checks on the existence of the links listed in a HOOPLE format link * +* database and reports the bad ones. * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "bookmark_tree.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace algorithms; +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace nodes; +using namespace mathematics; +using namespace processes; +using namespace structures; +using namespace textual; +using namespace timely; + +//#define DEBUG_MARKS + // uncomment to have more debugging noise. + +#undef BASE_LOG +#define BASE_LOG(s) program_wide_logger::get().log(astring(s), ALWAYS_PRINT) +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), \ + a_sprintf("line %d: ", _categories._line_number) + s) + +const int PAUSEY_SNOOZE = 200; + // how long we sleep if there are too many threads running already. + +const int MAXIMUM_THREADS = 14; + // we allow this many simultaneous web checks at a time. + +const int MAXIMUM_READ = 1008; + // we only download this much of the link. this avoids huge downloads of + // very large sites. + +const int MAXIMUM_ATTEMPTS = 2; + // we'll retry the check if we get an actual error instead of an http error + // code. when a name can't be found in the DNS, it sometimes comes back + // shortly after it was checked. if we see we can't reach the domain after + // this many tries, then we give up on the address. + +const int TIME_PER_REQUEST_IN_SEC = 60 * 6; + // limit our requests to this long of a period. then we will not be + // stalled forever by uncooperative websites. + +const char *FAKE_AGENT_STRING = "FredWeb/7.0 (X11; U; Linux i686; " + "en-US; rv:1.8.19) Flecko/20081031"; + // we use this as our agent type, since some sites won't treat us fairly + // if they think we're robots when we're checking their site health. +//still true? + // for example (ahem!), the usa today websites. + +//////////////////////////////////////////////////////////////////////////// + +class safe_int_array +{ +public: + safe_int_array() : _lock(), _list(0) {} + + void add(int to_add) { +///BASE_LOG(a_sprintf("adding %d to list", to_add)); + auto_synchronizer l(_lock); + _list += to_add; + } + + int length() { + auto_synchronizer l(_lock); + return _list.length(); + } + + basis::int_array make_copy() { + auto_synchronizer l(_lock); + return _list; + } + +private: + basis::mutex _lock; + basis::int_array _list; +}; + +//////////////////////////////////////////////////////////////////////////// + +class marks_checker : public application_shell +{ +public: + marks_checker() + : application_shell(), _check_redirection(false), + _max_threads(MAXIMUM_THREADS), _null_file(filename::null_device(), "w") + {} + + DEFINE_CLASS_NAME("marks_checker"); + virtual int execute(); + int print_instructions(const filename &program_name); + + int test_all_links(); + // goes through the tree of links and tests them all. + + int check_link(const astring &url, astring &error_msg); + // synchronously checks the "url" for health. the return value is zero + // on success or an HTTP error code on failure. + + void write_new_files(); + // writes out the two new files given the info accumulated so far. + +private: + bookmark_tree _categories; // our tree of categories. + safe_int_array _bad_lines; // lines with bad contents. + thread_cabinet _checkers; // threads checking on links. + astring _input_filename; // we'll store our link database name here. + astring _output_filename; // where the list of good links is stored. + astring _bad_link_filename; // garbage dump of bad links. + bool _check_redirection; // true if redirection is disallowed. + int _max_threads; // the most threads we'll allow at once. + byte_filer _null_file; // we'll use this for trashing output data. + + static void handle_OS_signal(int sig_id); + // handles break signals from the user. +}; + +//////////////////////////////////////////////////////////////////////////// + +class checking_thread : public ethread +{ +public: + checking_thread(const link_record &link_info, safe_int_array &bad_lines, + marks_checker &checker) + : ethread(), _bad_lines(bad_lines), _checker(checker), _info(link_info) {} + + void perform_activity(void *formal(ptr)) { + astring message; + int ret = _checker.check_link(_info._url, message); + if (ret != 0) { + astring complaint = a_sprintf("Bad Link at line %d:", _info._uid) + += parser_bits::platform_eol_to_chars(); + const astring spacer(' ', 4); + complaint += spacer + _info._url += parser_bits::platform_eol_to_chars(); + complaint += spacer + _info._description += parser_bits::platform_eol_to_chars(); + complaint += spacer + "error: " += message; + BASE_LOG(complaint); +if ( (_info._uid> 100000) || (_info._uid < 0) ) { +BASE_LOG(a_sprintf("somehow got bogus line number! %d", _info._uid)); +return; +} + _bad_lines.add(_info._uid); // list ours as bad. + } + } + +private: + safe_int_array &_bad_lines; + marks_checker &_checker; + link_record _info; +}; + +//////////////////////////////////////////////////////////////////////////// + +int marks_checker::print_instructions(const filename &program_name) +{ + a_sprintf to_show("%s:\n\ +This program needs three filenames as command line parameters. The -i flag\n\ +is used to specify the input filename. The -o flag specifies the file where\n\ +where the good links will be written. The -b flag specifies the file where\n\ +the bad links are written. The optional flag --no-redirs can be used to\n\ +disallow web-site redirection, which will catch when the site has changed\n\ +its location. Note that redirection is not necessarily an error, but it\n\ +instead may just be a link that needs its URL modified. It is recommended\n\ +that you omit this flag in early runs, in order to only locate definitely\n\ +dead links. Then later checking runs can find any sites that were redirected\n\ +or being routed to a dead link page which doesn't provide an error code.\n\ +The optional flag --threads with a parameter will set the maximum number of\n\ +threads that will simultaneously check on links.\n\ +The input file is expected to be in the HOOPLE link database format.\n\ +The HOOPLE link format is documented here:\n\ + http://hoople.org/guides/link_database/format_manifesto.txt\n\ +", program_name.basename().raw().s(), program_name.basename().raw().s()); + program_wide_logger::get().log(to_show, ALWAYS_PRINT); + return 12; +} + +// this function just eats any data it's handed. +size_t data_sink(void *formal(ptr), size_t size, size_t number, void *formal(stream)) +{ return size * number; } + +int marks_checker::check_link(const astring &url, astring &error_msg) +{ + int to_return = -1; + + CURL *cur = curl_easy_init(); + + curl_easy_setopt(cur, CURLOPT_URL, url.s()); // set the URL itself. + + curl_easy_setopt(cur, CURLOPT_SSL_VERIFYPEER, 0); + // don't verify SSL certificates. + curl_easy_setopt(cur, CURLOPT_MAXFILESIZE, MAXIMUM_READ); + // limit the download size; causes size errors, which we elide to success. + curl_easy_setopt(cur, CURLOPT_NOSIGNAL, 1); + // don't use signals since it interferes with sleep. + curl_easy_setopt(cur, CURLOPT_TIMEOUT, TIME_PER_REQUEST_IN_SEC); + // limit time allowed per operation. + curl_easy_setopt(cur, CURLOPT_AUTOREFERER, true); + // automatically fill in the referer field when redirected. + + curl_easy_setopt(cur, CURLOPT_WRITEDATA, _null_file.file_handle()); + // set the file handle where we want our downloaded data to go. since + // we're just checking the links, this goes right to the trash. + curl_easy_setopt(cur, CURLOPT_WRITEFUNCTION, data_sink); + // set the function which will be given all the downloaded data. + + curl_easy_setopt(cur, CURLOPT_USERAGENT, FAKE_AGENT_STRING); + // fake being a browser here since otherwise we get no respect. + + curl_easy_setopt(cur, CURLOPT_FTPLISTONLY, 1); + // get only a simple list of files, which allows us to hit ftp sites + // properly. if the normal curl mode is used, we get nothing. + + if (_check_redirection) { + // attempting to quash redirects as being valid. + curl_easy_setopt(cur, CURLOPT_FOLLOWLOCATION, 1); // follow redirects. + curl_easy_setopt(cur, CURLOPT_MAXREDIRS, 0); // allow zero redirects. + } + + int tries = 0; + while (tries++ < MAXIMUM_ATTEMPTS) { + + // we do the error message again every time, since it gets shrunk after + // the web page check and is no longer available where it was in memory. + error_msg = astring(' ', CURL_ERROR_SIZE + 5); + curl_easy_setopt(cur, CURLOPT_ERRORBUFFER, error_msg.s()); + + // set the error message buffer so we know what happened. + + // try to lookup the web page we've been given. + to_return = curl_easy_perform(cur); + + error_msg.shrink(); // just use the message without extra spaces. + + // we turn file size errors into non-errors, since we have set a very + // low file size in order to avoid downloading too much. we really just + // want to check links, not download their contents. + if (to_return == CURLE_FILESIZE_EXCEEDED) to_return = 0; + + if (!to_return) { + // supposedly this is a success, but let's check the result code. + long result; + curl_easy_getinfo(cur, CURLINFO_RESPONSE_CODE, &result); + if (result >= 400) { + error_msg = a_sprintf("received http failure code %d", result); + to_return = -1; + } + break; // this was a successful result, a zero outcome from perform. + } + + time_control::sleep_ms(10 * SECOND_ms); // give it a few more seconds... + } + + curl_easy_cleanup(cur); + + return to_return; +} + +int marks_checker::test_all_links() +{ + FUNCDEF("test_all_links"); + // traverse the tree in prefix order. + tree::iterator itty = _categories.access_root().start(tree::prefix); + tree *curr = NIL; + while ( (curr = itty.next()) ) { + inner_mark_tree *nod = dynamic_cast(curr); + if (!nod) + non_continuable_error(static_class_name(), func, "failed to cast a tree node"); + // iterate on all the links at this node to check them. + for (int i = 0; i < nod->_links.elements(); i++) { + link_record *lin = nod->_links.borrow(i); + if (!lin->_url) continue; // not a link. + + while (_checkers.threads() > _max_threads) { + time_control::sleep_ms(PAUSEY_SNOOZE); + _checkers.clean_debris(); + } + + checking_thread *new_thread = new checking_thread(*lin, _bad_lines, + *this); + unique_int id = _checkers.add_thread(new_thread, true, NIL); + } + } + +BASE_LOG("... finished iterating on tree."); + + // now wait until all the threads are finished. + while (_checkers.threads()) { + time_control::sleep_ms(PAUSEY_SNOOZE); + _checkers.clean_debris(); + } + +BASE_LOG("... finished waiting for all threads."); + + return 0; +} + +void marks_checker::write_new_files() +{ + byte_filer input_file(_input_filename, "r"); + byte_filer output_file(_output_filename, "w"); + byte_filer badness_file(_bad_link_filename, "w"); + + basis::int_array badness = _bad_lines.make_copy(); + shell_sort(badness.access(), badness.length()); + + BASE_LOG("bad links are on lines:"); + astring bad_list; + for (int i = 0; i < badness.length(); i++) { + bad_list += a_sprintf("%d, ", badness[i]); + } + BASE_LOG(bad_list); + + astring buffer; + int curr_line = 0; + while (!input_file.eof()) { + curr_line++; + while (badness.length() && (badness[0] < curr_line) ) { + BASE_LOG(a_sprintf("whacking too low line number: %d", badness[0])); + badness.zap(0, 0); + } + input_file.getline(buffer, 2048); +//make that a constant. + if (badness.length() && (badness[0] == curr_line)) { + // we seem to have found a bad line. + badness_file.write(buffer); + badness.zap(0, 0); // remove the current line number. + } else { + // this is a healthy line. + output_file.write(buffer); + } + + } + input_file.close(); + output_file.close(); + badness_file.close(); +} + +marks_checker *main_program = NIL; + +void marks_checker::handle_OS_signal(int formal(sig_id)) +{ + signal(SIGINT, SIG_IGN); // turn off that signal for now. + BASE_LOG("caught break signal... now writing files."); + if (main_program) main_program->write_new_files(); + BASE_LOG("exiting after handling break."); + main_program = NIL; + exit(0); +} + +int marks_checker::execute() +{ + FUNCDEF("execute"); + SETUP_COMBO_LOGGER; + + main_program = this; // used by our signal handler. + + command_line cmds(_global_argc, _global_argv); // process the command line parameters. + if (!cmds.get_value('i', _input_filename, false)) + return print_instructions(cmds.program_name()); + if (!cmds.get_value('o', _output_filename, false)) + return print_instructions(cmds.program_name()); + if (!cmds.get_value('b', _bad_link_filename, false)) + return print_instructions(cmds.program_name()); + + astring temp; + + // optional flag for checking website redirection. + if (cmds.get_value("no-redirs", temp, false)) { + BASE_LOG("Enabling redirection checking: redirected web sites are reported as bad."); + _check_redirection = true; + } + // optional flag for number of threads. + astring threads; + if (cmds.get_value("threads", threads, false)) { + _max_threads = threads.convert(0); + BASE_LOG(a_sprintf("Maximum threads allowed=%d", _max_threads)); + } + + BASE_LOG(astring("input file: ") + _input_filename); + BASE_LOG(astring("output file: ") + _output_filename); + BASE_LOG(astring("bad link file: ") + _bad_link_filename); + +//hmmm: check if output file already exists. +//hmmm: check if bad file already exists. + +LOG("before reading input..."); + + int ret = _categories.read_csv_file(_input_filename); + if (ret) return ret; // failure during read means we can't do much. + +LOG("after reading input..."); + + signal(SIGINT, handle_OS_signal); + // hook the break signal so we can still do part of the job if they + // interrupt us. + + curl_global_init(CURL_GLOBAL_ALL); // crank up the cURL engine. + + ret = test_all_links(); + + write_new_files(); + main_program = NIL; + + curl_global_cleanup(); // shut down cURL engine again. + + return 0; +} + +//////////////////////////////////////////////////////////////////////////// + +HOOPLE_MAIN(marks_checker, ) + diff --git a/core/applications/bookmark_tools/marks_maker.cpp b/core/applications/bookmark_tools/marks_maker.cpp new file mode 100644 index 00000000..f3edbc01 --- /dev/null +++ b/core/applications/bookmark_tools/marks_maker.cpp @@ -0,0 +1,416 @@ +////////////// +// Name : marks_maker +// Author : Chris Koeritz +////////////// +// Copyright (c) 2005-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +#include "bookmark_tree.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace nodes; +using namespace structures; +using namespace textual; +using namespace timely; + +#define DEBUG_MARKS + // uncomment to have more debugging noise. + +#undef BASE_LOG +#define BASE_LOG(s) program_wide_logger::get().log(s, ALWAYS_PRINT) +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +const int MAX_FILE_SIZE = 4 * MEGABYTE; + // the largest file we'll read. + +const int SPACING_CHUNK = 4; + // number of spaces per indentation level. + +const int MAX_URL_DISPLAYED = 58; +const int MAX_DESCRIP_DISPLAYED = 72; + +//////////////////////////////////////////////////////////////////////////// + +class marks_maker : public application_shell +{ +public: + marks_maker(); + + enum output_style { + ST_HUMAN_READABLE, + ST_MOZILLA_MARKS, +// ST_JAVASCRIPT_BASED... separate implementation currently. + }; + + int write_marks_page(const astring &output_filename, + const astring &template_filename, output_style way); + // given a tree of links, this writes out a web page to "output_filename" + // using a template file "template_filename". + + DEFINE_CLASS_NAME("marks_maker"); + int print_instructions(const filename &program_name); + virtual int execute(); + +private: + bookmark_tree c_categories; // our tree of categories. + int c_current_depth; // current indentation depth in list. + output_style c_style; // style of marks to write, set after construction. + + void increase_nesting(astring &output); + // adds a new level of nesting to the text. + + void decrease_nesting(astring &output); + // drops out of a level of nesting in the text. + + astring recurse_on_node(inner_mark_tree *nod); + // the main recursive method that spiders down the tree. it is important that it builds + // the string through composition rather than being given a string reference, since it + // expands all sub-trees as it goes. + + void inject_javascript_function(astring &output); + // replaces a special phrase in the template file with our javascript-based link opener. + + void write_category_start(const astring &name, int node_depth, astring &output); + // outputs the text for categories and adjusts the indentation level. + + void write_category_end(int depth, astring &output); + // closes a category appropriately for the nesting depth. + + void write_link(inner_mark_tree *node, const link_record &linko, + astring &output, int depth); + // outputs the text for web links. +}; + +//////////////////////////////////////////////////////////////////////////// + +marks_maker::marks_maker() +: application_shell(), + c_current_depth(0), + c_style(ST_HUMAN_READABLE) +{} + +int marks_maker::print_instructions(const filename &program_name) +{ + a_sprintf to_show("%s:\n\ +This program needs three filenames as command line parameters. The -i flag\n\ +is used to specify the input filename, the -t flag specifies a template web\n\ +page which is used as the wrapper around the links, and the -o flag specifies\n\ +the web page to be created. The input file is expected to be in the HOOPLE\n\ +link database format. The output file will be created from the template file\n\ +by finding the phrase $INSERT_LINKS_HERE in it and replacing that with html\n\ +formatted link and categories from the input file. Another tag of $TODAYS_DATE\n\ +will be replaced with the date when the output file is regenerated. A final\n\ +tag of $INSERT_JAVASCRIPT_HERE is replaced with a link opening function.\n\ +Note that an optional -s flag can specify a value of \"human\" readable\n\ +or \"mozilla\" bookmarks style to specify the style of the output file\n\ +generated.\n\ +The HOOPLE link format is documented here:\n\ + http://hoople.org/guides/link_database/format_manifesto.txt\n\ +", program_name.basename().raw().s(), program_name.basename().raw().s()); + program_wide_logger::get().log(to_show, ALWAYS_PRINT); + return 12; +} + +void marks_maker::increase_nesting(astring &output) +{ + FUNCDEF("increase_nesting"); + int spaces = SPACING_CHUNK * c_current_depth; + c_current_depth++; +#ifdef DEBUG_MARKS + LOG(a_sprintf("++increased depth to %d...", c_current_depth)); +#endif + output += string_manipulation::indentation(spaces); + output += "

"; + output += parser_bits::platform_eol_to_chars(); +} + +void marks_maker::decrease_nesting(astring &output) +{ + FUNCDEF("decrease_nesting"); + c_current_depth--; +#ifdef DEBUG_MARKS + LOG(a_sprintf("--decreased depth to %d...", c_current_depth)); +#endif + int spaces = SPACING_CHUNK * c_current_depth; + output += string_manipulation::indentation(spaces); + output += "

"; + output += parser_bits::platform_eol_to_chars(); +} + +void marks_maker::write_category_start(const astring &name, int node_depth, astring &output) +{ + FUNCDEF("write_category_start"); + + // calculate proper heading number. + int heading_num = node_depth + 1; + astring heading = a_sprintf("%d", heading_num); + // force a weird requirement for mozilla bookmarks, all headings must be set at 3. + if (c_style == ST_MOZILLA_MARKS) heading = "3"; + +#ifdef DEBUG_MARKS + LOG(astring("header [") + name + "] level " + a_sprintf("%d", node_depth)); +#endif + + // output our heading. + output += string_manipulation::indentation(c_current_depth * SPACING_CHUNK); + output += "

MAX_URL_DISPLAYED) { + chomped_url.zap(MAX_URL_DISPLAYED / 2, + chomped_url.length() - MAX_URL_DISPLAYED / 2 - 1); + chomped_url.insert(MAX_URL_DISPLAYED / 2, "..."); + } + } + + astring description = linko._description; + if (c_style != ST_MOZILLA_MARKS) { + // this is chopping the tail off, which seems reasonable for a very long description. + if (description.length() > MAX_DESCRIP_DISPLAYED) { + description.zap(MAX_DESCRIP_DISPLAYED - 1, description.end()); + description += "..."; + } + } + + // new output format, totally clean and simple. description is there + // in readable manner, and it's also a link. plus, this takes up a fraction + // of the space the old way used. + astring indentulus = string_manipulation::indentation(c_current_depth * SPACING_CHUNK); + output += indentulus; + output += "
  • "; + output += ""; + output += description; + output += ""; + + if (c_style != ST_MOZILLA_MARKS) { + output += "   "; + output += ""; + output += "[launch]"; + output += ""; + } + + output += "
  • "; + output += "
    "; + output += parser_bits::platform_eol_to_chars(); +} + +astring marks_maker::recurse_on_node(inner_mark_tree *nod) +{ + FUNCDEF("recurse_on_node"); + astring to_return; + + // print out the category on this node. + write_category_start(nod->name(), nod->depth(), to_return); + + // print the link for all of the ones stored at this node. + for (int i = 0; i < nod->_links.elements(); i++) { + link_record *lin = nod->_links.borrow(i); + write_link(nod, *lin, to_return, nod->depth()); + } + + // zoom down into sub-categories. + for (int i = 0; i < nod->branches(); i++) { + to_return += recurse_on_node((inner_mark_tree *)nod->branch(i)); + } + + // finish this category. + write_category_end(nod->depth(), to_return); + + return to_return; +} + +void marks_maker::inject_javascript_function(astring &output) +{ + FUNCDEF("inject_javascript_function"); + astring scrip = "\n\ +\n\ +\n"; + + bool found_it = output.replace("$INSERT_JAVASCRIPT_HERE", scrip); + if (!found_it) + non_continuable_error(class_name(), func, "the template file is missing " + "the insertion point for '$INSERT_JAVASCRIPT_HERE'"); +} + +int marks_maker::write_marks_page(const astring &output_filename, + const astring &template_filename, output_style style) +{ + FUNCDEF("write_marks_page"); + c_style = style; // set the overall output style here. + astring long_string; + // this is our accumulator of links. it is the semi-final result that will + // be injected into the template file. + + // generate the meaty portion of the bookmarks. + increase_nesting(long_string); + inner_mark_tree *top = (inner_mark_tree *)&c_categories.access_root(); + long_string += recurse_on_node(top); + decrease_nesting(long_string); + + byte_filer template_file(template_filename, "r"); + astring full_template; + if (!template_file.good()) + non_continuable_error(class_name(), func, "the template file could not be opened"); + template_file.read(full_template, MAX_FILE_SIZE); + template_file.close(); + + // spice up the boring template with a nice link opening function. + inject_javascript_function(full_template); + + // replace the tag with the long string we created. + bool found_it = full_template.replace("$INSERT_LINKS_HERE", long_string); + if (!found_it) + non_continuable_error(class_name(), func, "the template file is missing " + "the insertion point for '$INSERT_LINKS_HERE'"); + + full_template.replace("$TODAYS_DATE", time_stamp::notarize(true)); + + filename outname(output_filename); + byte_filer output_file(output_filename, "w"); + if (!output_file.good()) + non_continuable_error(class_name(), func, "the output file could not be opened"); + // write the newly generated web page out now. + output_file.write(full_template); + output_file.close(); + +#ifdef DEBUG_MARKS + // show the tree. + BASE_LOG(astring()); + BASE_LOG(astring("the tree, sir...")); + BASE_LOG(astring()); + BASE_LOG(c_categories.access_root().text_form()); +#endif + + BASE_LOG(a_sprintf("wrote %d links in %d categories.", + c_categories.link_count(), c_categories.category_count())); + BASE_LOG(astring("")); + + return 0; +} + +int marks_maker::execute() +{ + FUNCDEF("execute"); + SETUP_COMBO_LOGGER; + + command_line cmds(_global_argc, _global_argv); // process the command line parameters. + astring input_filename; // we'll store our link database name here. + astring output_filename; // where the web page we're creating goes. + astring template_filename; // the wrapper html code that we'll stuff. + astring style_used; // type of output file style to create. + if (!cmds.get_value('i', input_filename, false)) + return print_instructions(cmds.program_name()); + if (!cmds.get_value('o', output_filename, false)) + return print_instructions(cmds.program_name()); + if (!cmds.get_value('t', template_filename, false)) + return print_instructions(cmds.program_name()); + cmds.get_value('s', style_used, false); + if (!style_used) style_used = "human"; + + BASE_LOG(astring("input file: ") + input_filename); + BASE_LOG(astring("output file: ") + output_filename); + BASE_LOG(astring("template file: ") + template_filename); + BASE_LOG(astring("style: ") + style_used); + + filename outname(output_filename); + if (outname.exists()) { + non_continuable_error(class_name(), func, astring("the output file ") + + output_filename + " already exists. It would be over-written if " + "we continued."); + } + + output_style styley = ST_HUMAN_READABLE; + if (style_used == astring("mozilla")) styley = ST_MOZILLA_MARKS; + + int ret = c_categories.read_csv_file(input_filename); + if (ret) return ret; + + ret = write_marks_page(output_filename, template_filename, styley); + if (ret) return ret; + + return 0; +} + +//////////////////////////////////////////////////////////////////////////// + +HOOPLE_MAIN(marks_maker, ) + diff --git a/core/applications/bookmark_tools/marks_sorter.cpp b/core/applications/bookmark_tools/marks_sorter.cpp new file mode 100644 index 00000000..b793d373 --- /dev/null +++ b/core/applications/bookmark_tools/marks_sorter.cpp @@ -0,0 +1,232 @@ +/*****************************************************************************\ +* * +* Name : marks_sorter * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Processes a link database in HOOPLE format and generates a new database * +* that is sorted and always uses category nicknames where defined. * +* * +******************************************************************************* +* Copyright (c) 2006-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "bookmark_tree.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace nodes; +using namespace structures; +using namespace textual; + +//#define DEBUG_MARKS + // uncomment to have more debugging noise. + +#undef BASE_LOG +#define BASE_LOG(s) program_wide_logger::get().log(s, ALWAYS_PRINT) +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), \ + a_sprintf("line %d: ", _categories._line_number) + s) + +const int MAX_FILE_SIZE = 4 * MEGABYTE; + // the largest file we'll read. + +//////////////////////////////////////////////////////////////////////////// + +class marks_sorter : public application_shell +{ +public: + marks_sorter() + : application_shell(), _loader_count(0), _link_spool(0) {} + DEFINE_CLASS_NAME("marks_sorter"); + virtual int execute(); + int print_instructions(const filename &program_name); + + int write_new_marks(const astring &output_filename); + // given a tree of links, this writes out a new sorted file to the + // "output_filename". + +private: + bookmark_tree _categories; // our tree of categories. + int _loader_count; // count of the loader functions. + int _link_spool; // count of which link we're writing. +}; + +//////////////////////////////////////////////////////////////////////////// + +int marks_sorter::print_instructions(const filename &program_name) +{ + a_sprintf to_show("%s:\n\ +This program needs two filenames as command-line parameters. The -i flag\n\ +is used to specify the input filename, which is expected to be in the HOOPLE\n\ +link database format. The -o flag specifies the new bookmarks file to be\n\ +created, which will also be in the HOOPLE link format.\n\ +The HOOPLE link format is documented here:\n\ + http://hoople.org/guides/link_database/format_manifesto.txt\n\ +", program_name.basename().raw().s(), program_name.basename().raw().s()); + program_wide_logger::get().log(to_show, ALWAYS_PRINT); + return 12; +} + +int marks_sorter::execute() +{ + FUNCDEF("execute"); + SETUP_COMBO_LOGGER; + + command_line cmds(_global_argc, _global_argv); // process the command line parameters. + astring input_filename; // we'll store our link database name here. + astring output_filename; // where the web page we're creating goes. + if (!cmds.get_value('i', input_filename, false)) + return print_instructions(cmds.program_name()); + if (!cmds.get_value('o', output_filename, false)) + return print_instructions(cmds.program_name()); + + BASE_LOG(astring("input file: ") + input_filename); + BASE_LOG(astring("output file: ") + output_filename); + + filename outname(output_filename); + if (outname.exists()) { + non_continuable_error(class_name(), func, astring("the output file ") + + output_filename + " already exists. It would be over-written if " + "we continued."); + } + + int ret = _categories.read_csv_file(input_filename); + if (ret) return ret; + + ret = write_new_marks(output_filename); + if (ret) return ret; + + return 0; +} + +int marks_sorter::write_new_marks(const astring &output_filename) +{ + FUNCDEF("write_new_marks"); + // open the output file for streaming out the new marks file. + filename outname(output_filename); + byte_filer output_file(output_filename, "w"); + if (!output_file.good()) + non_continuable_error(class_name(), func, "the output file could not be opened"); + + bool just_had_return = false; // did we just see a carriage return? + bool first_line = true; // is this the first line to be emitted? + + // traverse the tree in prefix order. + tree::iterator itty = _categories.access_root().start(tree::prefix); + tree *curr = NIL; // the current node. + + while ( (curr = itty.next()) ) { + inner_mark_tree *nod = (inner_mark_tree *)curr; + // set up a category printout for this node. + string_array cat_list; + cat_list += "C"; + cat_list += nod->name(); + inner_mark_tree *pare = (inner_mark_tree *)nod->parent(); + if (pare) { + astring name_split, nick_split; + _categories.break_name(pare->name(), name_split, nick_split); + if (!nick_split) cat_list += name_split; + else cat_list += nick_split; + } else { + cat_list += ""; + } + + // create a text line to send to the output file. + astring tmp; + list_parsing::create_csv_line(cat_list, tmp); + tmp += "\n"; + if (!just_had_return && !first_line) { + // generate a blank line before the category name. + output_file.write(parser_bits::platform_eol_to_chars()); + } + + // reset the flags after we've checked them. + just_had_return = false; + first_line = false; + + output_file.write(tmp); + // write the actual category definition. + + // print the links for all of the ones stored at this node. + for (int i = 0; i < nod->_links.elements(); i++) { + link_record *lin = nod->_links.borrow(i); + if (!lin->_url) { + // just a comment. + astring descrip = lin->_description; + if (descrip.contains("http:")) { + // we'll clean the html formatting out that we added earlier. + int indy = descrip.find('"'); + if (non_negative(indy)) { + descrip.zap(0, indy); + indy = descrip.find('"'); + if (non_negative(indy)) descrip.zap(indy, descrip.end()); + } + descrip = astring(" ") + descrip; + // add a little spacing. + } + if (descrip.t()) { + output_file.write(astring("#") + descrip + "\n"); + just_had_return = false; + } else { + // this line's totally blank, so we'll generate a blank line. + // we don't want to put in more than one blank though, so we check + // whether we did this recently. + if (!just_had_return) { + output_file.write(parser_bits::platform_eol_to_chars()); + just_had_return = true; // set our flag for a carriage return. + } + } + } else { + // should be a real link. + string_array lnks; + lnks += "L"; + lnks += lin->_description; + // use just the nickname for the parent, if there is a nick. + astring name_split; + astring nick_split; + _categories.break_name(nod->name(), name_split, nick_split); + if (!nick_split) lnks += nod->name(); + else lnks += nick_split; + lnks += lin->_url; + list_parsing::create_csv_line(lnks, tmp); + tmp += "\n"; + output_file.write(tmp); + just_had_return = false; + } + } + } + + output_file.close(); + + BASE_LOG(a_sprintf("wrote %d links in %d categories.", + _categories.link_count(), _categories.category_count())); + BASE_LOG(astring()); + + return 0; +} + +//////////////////////////////////////////////////////////////////////////// + +HOOPLE_MAIN(marks_sorter, ) + diff --git a/core/applications/bookmark_tools/version.ini b/core/applications/bookmark_tools/version.ini new file mode 100644 index 00000000..ef1e3c14 --- /dev/null +++ b/core/applications/bookmark_tools/version.ini @@ -0,0 +1,6 @@ +[version] +description = Bookmark Utilities +root = bookmark +name = Bookmark Apps +extension = exe + diff --git a/core/applications/bundler/bundle_creator.cpp b/core/applications/bundler/bundle_creator.cpp new file mode 100644 index 00000000..5a6722a9 --- /dev/null +++ b/core/applications/bundler/bundle_creator.cpp @@ -0,0 +1,1007 @@ + +//hmmm: anything related to _stub_size should be kept, but that is where +// we need a redundant search mechanism that can't be fooled so easily +// by modifying exe; make a pattern that will be found and is the first +// place to start looking for manifest. + +/*****************************************************************************\ +* * +* Name : bundle_creator * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2006-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "common_bundle.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef __WIN32__ + #include +#endif + +using namespace application; +using namespace basis; +using namespace configuration; +using namespace filesystem; +using namespace loggers; +using namespace filesystem; +using namespace processes; +using namespace structures; +using namespace textual; +using namespace timely; + +const int CHUNKING_SIZE = 256 * KILOBYTE; + // we'll read this big a chunk from a source file at a time. + +const astring SUBVERSION_FOLDER = ".svn"; + // we don't want to include this in a bundle. + +#define BASE_LOG(to_print) program_wide_logger::get().log(to_print, ALWAYS_PRINT) +#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print) + +//#define DEBUG_BUNDLER + // uncomment for noisy debugging version. + +// returns the "retval" and mentions that this is a failure at "where". +#define FAIL_RETURN(retval, where) { \ + LOG(astring("failure in ") + where + a_sprintf(", exit=%d", retval)); \ + return retval; \ +} + +//////////////////////////////////////////////////////////////////////////// + +bool true_value(const astring &value) +{ return (!value.equal_to("0")) && (!value.equal_to("false")); } + +//////////////////////////////////////////////////////////////////////////// + +// this structure overrides the manifest_chunk by providing a source string. + +struct bundled_chunk : manifest_chunk +{ + astring _source; //!< where the file comes from on the source system. + virtual ~bundled_chunk() {} +}; + +//////////////////////////////////////////////////////////////////////////// + +// main bundler class. + +class bundle_creator : public application_shell +{ +public: + bundle_creator() + : application_shell(), + _app_name(filename(_global_argv[0]).basename()), + _bundle(NIL), _stub_size(0), _keyword() {} + + virtual ~bundle_creator() { + WHACK(_bundle); + } + + DEFINE_CLASS_NAME("bundle_creator"); + virtual int execute(); + int print_instructions(); + + astring determine_stub_file_and_validate(); + //!< returns the stub file location if it could be successfully located. + + int open_output_file(); + //!< prepares the output file to be written into. + /*!< non-zero return indicates an error. */ + + int read_manifest(); + //!< reads manifest definition specifying files in the bundle. + /*!< creates the list of bundle pieces. */ + + int write_stub_and_toc(); + //!< stuffs the unpacker stub into output file and table of contents. + + int bundle_sources(); + //!< reads all of the input files and dumps them into the bundle. + + int finalize_file(); + //!< puts finishing touches on the output file and closes it. + + int write_offset(); + //!< writes the offset position into the output file. + /*!< this happens at the specially marked location (muftiloc). */ + + int patch_recursive_target(const astring &source, const astring &target, + int manifest_index); + //!< processes the recursive target specified in "curr". + /*!< the manifest_index tells the function where the external caller + is currently working on the manifest. new items will appear just after + that index. */ + + int recurse_into_dir(const astring &source, const astring &target, + int manifest_index); + //!< adds all files from "source" to our list, recurses on dirs. + + int patch_wildcard_target(const astring &source, const astring &target, + int manifest_index); + //!< processes the wildcard bearing target specified in "curr". + /*!< any new source items will get dropped on the end of the manifest. */ + + int add_files_here(directory &dirndl, const astring &source, + const astring &target, int manifest_index); + //!< takes all the files found in "source" and adds them to manifest. + + bool get_file_size(const astring &file, un_int &size, byte_array ×tamp); + //!< returns the file "size" and "timestamp" found for "file". + +private: + astring _app_name; //!< application name for this program. + astring _output_file; //!< what bundle file to create. + astring _manifest_file; //!< the manifest of what's included in bundle. + array _manifest_list; //!< the parsed list of contents. + byte_filer *_bundle; //!< points at the bundled output file. + int _stub_size; //!< where the TOC will be located. + astring _keyword; // set if we were given a keyword on cmd line. +}; + +//////////////////////////////////////////////////////////////////////////// + +int bundle_creator::print_instructions() +{ + BASE_LOG(a_sprintf("\ +%s: This program needs two parameters on the command line.\n\ +The -o flag must point at the bundled output file to create. The -m flag\n\ +must point at a valid manifest file that defines what will be packed into\n\ +the output file. See the example manifest in the bundler example\n\ +(in setup_src/bundle_example) for more information on the required file\n\ +format.\n\ +", _app_name.s())); + return 4; +} + +int bundle_creator::execute() +{ + FUNCDEF("execute"); + + BASE_LOG(astring("starting file bundling at ") + time_stamp::notarize(false)); + + command_line cmds(_global_argc, _global_argv); + astring temp; + if (cmds.get_value('?', temp)) return print_instructions(); + if (cmds.get_value("?", temp)) return print_instructions(); + if (!cmds.get_value('o', _output_file)) return print_instructions(); + if (!cmds.get_value('m', _manifest_file)) return print_instructions(); + + if (filename(_output_file).exists()) { + BASE_LOG(a_sprintf("\ +%s: The output file already exists. Please move it out of\n\ +the way; this program will not overwrite existing files.\n", +_app_name.s())); + return 3; + } + + if (!filename(_manifest_file).exists()) { + BASE_LOG(a_sprintf("\ +%s: The manifest file does not exist. This program cannot do anything\n\ +without a valid packing manifest.\n", _app_name.s())); + return 2; + } + + // test this early on so we don't waste time uselessly. + astring stub_file_okay = determine_stub_file_and_validate(); + if (!stub_file_okay) { + BASE_LOG(a_sprintf("\ +%s: The unpacking stub file does not exist (check binaries folder).\n\ +Abandoning bundling process.\n", _app_name.s())); + return 4; + } + + // make sure we snag any keyword that was passed on the command line. + cmds.get_value("keyword", _keyword); + + // first step is to provide some built-in variables that can be used to + // make the manifests less platform specific. this doesn't really help + // if you bundle it on linux and try to run it on windows. but either + // platform's resources can easily be made into a bundle with the same + // packing manifest. +#ifndef __WIN32__ + environment::set("EXE_END", ""); // executable file ending. + environment::set("DLL_START", "lib"); // dll file prefix. + environment::set("DLL_END", ".so"); // dll file ending. +#else + environment::set("EXE_END", ".exe"); + environment::set("DLL_START", ""); + environment::set("DLL_END", ".dll"); +#endif + + int ret = 0; + if ( (ret = read_manifest()) ) FAIL_RETURN(ret, "reading manifest"); + // read manifest to build list of what's what. + if ( (ret = open_output_file()) ) FAIL_RETURN(ret, "opening output file"); + // open up our output file for the bundled chunks. + if ( (ret = write_stub_and_toc()) ) FAIL_RETURN(ret, "writing stub and TOC"); + // writes the stub unpacker application and the table of contents to the + // output file. + if ( (ret = bundle_sources()) ) FAIL_RETURN(ret, "bundling source files"); + // stuff all the source files into the output bundle. + if ( (ret = finalize_file()) ) FAIL_RETURN(ret, "finalizing file"); + // finishes with the file and closes it up. + if ( (ret = write_offset()) ) FAIL_RETURN(ret, "writing offset"); + // stores the offset of the TOC into the output file in a special location + // that is delineated by a known keyword (muftiloc) and which should only + // exist in the file in one location. + + return 0; +} + +int bundle_creator::open_output_file() +{ + FUNCDEF("open_output_file"); + _bundle = new byte_filer(_output_file, "wb"); + if (!_bundle->good()) { + LOG(astring("failed to open the output file: ") + _output_file); + return 65; + } + return 0; +} + +bool bundle_creator::get_file_size(const astring &infile, un_int &size, + byte_array &time_stamp) +{ + FUNCDEF("get_file_size"); + time_stamp.reset(); + // access the source file to get its size. + byte_filer source_file(infile, "rb"); + if (!source_file.good()) { + LOG(astring("could not access the file for size check: ") + infile); + return false; + } + size = int(source_file.length()); + file_time tim(infile); + tim.pack(time_stamp); + return true; +} + +int bundle_creator::add_files_here(directory &dirndl, const astring &source, + const astring &target, int manifest_index) +{ + FUNCDEF("add_files_here"); + for (int i = 0; i < dirndl.files().length(); i++) { + astring curry = dirndl.files()[i]; + // skip .svn folders and contents. + if (curry.contains(SUBVERSION_FOLDER)) continue; +//hmmm: this could be a much nicer generalized file exclusion list. + +//LOG(astring("file is: ") + curry); + bundled_chunk new_guy; + new_guy._source = source + "/" + curry; // the original full path to it. + new_guy._payload = target + "/" + curry; + new_guy._keywords = _manifest_list[manifest_index]._keywords; + // copy the flags from the parent, so we don't forget options. + new_guy._flags = _manifest_list[manifest_index]._flags; + // remove some flags that make no sense for the new guy. + new_guy._flags &= ~RECURSIVE_SRC; + +//LOG(a_sprintf("adding: source=%s targ=%s", new_guy._source.s(), new_guy._payload.s())); + bool okaysize = get_file_size(new_guy._source, new_guy._size, new_guy.c_filetime); + if (!okaysize || (new_guy._size < 0) ) { + LOG(astring("failed to get file size for ") + new_guy._source); + return 75; + } + + _manifest_list.insert(manifest_index + 1, 1); + _manifest_list[manifest_index + 1] = new_guy; + } + return 0; +} + +int bundle_creator::recurse_into_dir(const astring &source, + const astring &target, int manifest_index) +{ +// FUNCDEF("recurse_into_dir"); +//LOG(astring("src=") + source + " dest=" + target); + + // we won't include the subversion folder. + if (source.contains(SUBVERSION_FOLDER)) return 0; + + string_array dirs; // culled from the directory listing. + { + // don't pay for the directory object on the recursive invocation stack; + // just have what we need on the stack (the directory list). + directory dirndl(source); +//check dir for goodness! + int ret = add_files_here(dirndl, source, target, manifest_index); + // add in just the files that were found. + if (ret != 0) { + // this is a failure, but the function complains about it already. + return 75; + } + dirs = dirndl.directories(); + } + +//LOG("now scanning directories..."); + + // now scan across the directories we found. + for (int i = 0; i < dirs.length(); i++) { + astring s = dirs[i]; +//LOG(astring("curr dir is ") + s); + int ret = recurse_into_dir(source + "/" + s, target + "/" + + s, manifest_index); + if (ret != 0) return ret; // bail out. + } + + return 0; +} + +int bundle_creator::patch_recursive_target(const astring &source, + const astring &target, int manifest_index) +{ +// FUNCDEF("patch_recursive_target"); +//LOG(astring("patch recurs src=") + source + " targ=" + target); + return recurse_into_dir(source, target, manifest_index); +} + +int bundle_creator::patch_wildcard_target(const astring &source, + const astring &target, int manifest_index) +{ +// FUNCDEF("patch_wildcard_target"); + // find the last slash. the rest is our wildcard component. + int src_end = source.end(); + int slash_indy = source.find('/', src_end, true); + astring real_source = source.substring(0, slash_indy - 1); + astring wild_pat = source.substring(slash_indy + 1, src_end); +//BASE_LOG(astring("got src=") + real_source + " wildpat=" + wild_pat); + + directory dirndl(real_source, wild_pat.s()); +//check dir for goodness! + int ret = add_files_here(dirndl, real_source, target, manifest_index); + if (ret != 0) { + // this is a failure, but the function complains about it already. + return 75; + } + + return 0; +} + +int bundle_creator::read_manifest() +{ + FUNCDEF("read_manifest"); + ini_configurator ini(_manifest_file, configurator::RETURN_ONLY); + string_table toc; + bool worked = ini.get_section("toc", toc); + if (!worked) { + LOG(astring("failed to read TOC section in manifest:\n") + _manifest_file + + "\ndoes that file exist?"); + return 65; + } + +//hmmm: make a class member. + file_logger noisy_logfile(application_configuration::make_logfile_name + ("bundle_creator_activity.log")); + noisy_logfile.log(astring('-', 76)); + noisy_logfile.log(astring("Bundling starts at ") + time_stamp::notarize(false)); + + // add enough items in the list for our number of sections. + _manifest_list.insert(0, toc.symbols()); + astring value; // temporary string used below. + int final_return = 0; // if non-zero, an error occurred. + +#define BAIL(retval) \ + final_return = retval; \ + toc.zap_index(i); \ + _manifest_list.zap(i, i); \ + i--; \ + continue + + for (int i = 0; i < toc.symbols(); i++) { + // read all the info in this section and store it into our list. + astring section_name = toc.name(i); + section_name.strip_spaces(astring::FROM_FRONT); + if (section_name[0] == '#') { +//hmmm: this looks a bit familiar from bail macro above. abstract out? + toc.zap_index(i); + _manifest_list.zap(i, i); + i--; + continue; // skip comments. + } + + // check for any keywords on the section. these are still needed for + // variables, which otherwise would skip the rest of the field checks. + if (ini.get(section_name, "keyword", value)) { +///LOG(astring("into keyword processing--value held is ") + value); + string_array keys; + bool worked = list_parsing::parse_csv_line(value, keys); + if (!worked) { + LOG(astring("failed to parse keywords for section ") + + section_name + " in " + _manifest_file); + BAIL(82); + } +///LOG(astring("parsed list is ") + keys.text_form()); + _manifest_list[i]._keywords = keys; + astring dumped; + list_parsing::create_csv_line(_manifest_list[i]._keywords, dumped); + noisy_logfile.log(section_name + " keywords: " + dumped); + } + + if (ini.get(section_name, "variable", value)) { + // this is a variable assignment. it is the only thing we care about + // for this section, so the rest is ignored. + variable_tokenizer zohre; + zohre.parse(value); + if (zohre.symbols() < 1) { + LOG(astring("failed to parse a variable statement from ") + value); + BAIL(37); + } + _manifest_list[i]._flags = SET_VARIABLE; // not orred, just this. + // set the two parts of our variable. + _manifest_list[i]._payload = zohre.table().name(0); + _manifest_list[i]._parms = zohre.table()[0]; + BASE_LOG(astring("will set ") + _manifest_list[i]._payload + " = " + + _manifest_list[i]._parms); + astring new_value = parser_bits::substitute_env_vars(_manifest_list[i]._parms); + environment::set(_manifest_list[i]._payload, new_value); + +#ifdef DEBUG_BUNDLER + BASE_LOG(astring("** variable ") + _manifest_list[i]._payload + " should have value=" + new_value); + BASE_LOG(astring("** variable ") + _manifest_list[i]._payload + " now does have value=" + environment::get(_manifest_list[i]._payload)); +#endif + + continue; + } else if (ini.get(section_name, "assert_defined", value)) { + // they are just asking for a variable test, to see if a variable + // that the installer needs is actually defined at unpacking time. + _manifest_list[i]._payload = value; + _manifest_list[i]._flags = TEST_VARIABLE_DEFINED; + BASE_LOG(astring("will test ") + _manifest_list[i]._payload + " is " + + "defined at unpacking time."); + continue; + } + + if (!ini.get(section_name, "source", _manifest_list[i]._source)) { + // check whether they told us not to pack and it's executable. + bool okay_to_omit_source = false; + astring value2; + if (ini.get(section_name, "no_pack", value) + && ini.get(section_name, "exec_target", value2) ) { + if (true_value(value) && true_value(value2)) { + // this type of section doesn't need source declared. + okay_to_omit_source = true; + } + } + if (!okay_to_omit_source) { + LOG(astring("failed to read the source entry for section ") + + section_name + " in " + _manifest_file); + BAIL(67); + } + } + // fix meshugener backslashes so we can count on the slash direction. + _manifest_list[i]._source.replace_all('\\', '/'); + + if (!ini.get(section_name, "target", _manifest_list[i]._payload)) { + // check whether they told us not to pack and it's executable. + bool okay_to_omit_target = false; + astring value2; + if (ini.get(section_name, "no_pack", value) + && ini.get(section_name, "exec_source", value2) ) { + if (true_value(value) && true_value(value2)) { + // this type of section doesn't need target declared. + okay_to_omit_target = true; + } + } + if (!okay_to_omit_target) { + LOG(astring("failed to read the target entry for section ") + + section_name + " in " + _manifest_file); + BAIL(68); + } + } + // fix backslashes in target also. + _manifest_list[i]._payload.replace_all('\\', '/'); + + // capture any parameters they have specified for exec or other options. + if (ini.get(section_name, "parms", value)) { + _manifest_list[i]._parms = value; +#ifdef DEBUG_BUNDLER + BASE_LOG(astring("got parms for ") + section_name + " as: " + value); +#endif + if (value[0] != '"') { + // repair the string if we're running on windows. + _manifest_list[i]._parms = astring("\"") + value + "\""; + } + noisy_logfile.log(section_name + " parms: " + _manifest_list[i]._parms); + } + + // check for the ignore errors flag. + if (ini.get(section_name, "error_okay", value)) { + if (true_value(value)) + _manifest_list[i]._flags |= IGNORE_ERRORS; + } + + // see if they are saying not to overwrite the target file. + if (ini.get(section_name, "no_replace", value)) { + if (true_value(value)) + _manifest_list[i]._flags |= NO_OVERWRITE; + } + + // test whether they are saying not to complain about a failure with + // our normal pop-up dialog (on winders). + if (ini.get(section_name, "quiet", value)) { + if (true_value(value)) + _manifest_list[i]._flags |= QUIET_FAILURE; + } + + // did they want a backup of the original to be made, instead of + // just overwriting the file? + if (ini.get(section_name, "make_backup", value)) { + if (true_value(value)) + _manifest_list[i]._flags |= MAKE_BACKUP_FILE; + } + + // look for our recursion flag. + if (ini.get(section_name, "recurse", value)) { + if (true_value(value)) + _manifest_list[i]._flags |= RECURSIVE_SRC; + } else { + // the options here are only appropriate when the target is NOT set to + // be recursive. + + if (ini.get(section_name, "no_pack", value)) { + // allow either side to not be required if this is an executable. + if (true_value(value)) + _manifest_list[i]._flags |= OMIT_PACKING; + } + + // check if they have specified a source side executable. + if (ini.get(section_name, "exec_source", value)) { + if (true_value(value)) { + _manifest_list[i]._flags |= SOURCE_EXECUTE; + } + } else { + // check if they have specified a target side executable. this is + // mutually exclusive with a source side exec. + if (ini.get(section_name, "exec_target", value)) { + if (true_value(value)) + _manifest_list[i]._flags |= TARGET_EXECUTE; + } + } + } + + // replace environment variables in the source now... + _manifest_list[i]._source = parser_bits::substitute_env_vars + (_manifest_list[i]._source, false); + + // look for wildcards in the source. + int indy = _manifest_list[i]._source.find("*"); + + // see if they specified a keyword on the command line and if this matches. + // if not we need to abandon this item. + if (!!_keyword && !_manifest_list[i]._keywords.member(_keyword)) { + // their keyword choice didn't match what we were told to use. + noisy_logfile.log(astring("skipping ") + _manifest_list[i]._payload + + " file check; doesn't match keyword \"" + _keyword + "\""); + continue; + } + + // we only access the source file here if it's finalized. we can't do + // this if the target is supposed to be recursive or if it's got a wildcard + // pattern in it. + if (!(_manifest_list[i]._flags & RECURSIVE_SRC) && negative(indy) + && !(_manifest_list[i]._flags & OMIT_PACKING) ) { + // access the source file to get its size. + byte_filer source_file(_manifest_list[i]._source, "rb"); + if (!source_file.good()) { + LOG(astring("could not access the source file for bundling: ") + + _manifest_list[i]._source); + BAIL(69); + } + bool okaysize = get_file_size(_manifest_list[i]._source, + _manifest_list[i]._size, _manifest_list[i].c_filetime); + if (!okaysize || (_manifest_list[i]._size < 0) ) { + // this is a failure, but the function complains about it already. + BAIL(75); + } + } + } + + // patch the manifest list for wildcards and recursive sources. + for (int i = 0; i < _manifest_list.length(); i++) { + bundled_chunk curr = _manifest_list[i]; + + if (!!_keyword && !curr._keywords.member(_keyword)) { + // this item's keyword doesn't match the one we were given, so skip it. + noisy_logfile.log(astring("zapping entry for ") + curr._payload + + "; doesn't match keyword \"" + _keyword + "\""); + _manifest_list.zap(i, i); + i--; // skip back since we eliminated an index. + continue; + } + + if (curr._flags & SET_VARIABLE) { + // we're done working on this. + continue; + } else if (curr._flags & TEST_VARIABLE_DEFINED) { + // this also requires no further effort. + continue; + } else if (curr._flags & RECURSIVE_SRC) { + // handle a recursive style target. + int star_indy = curr._source.find("*"); + if (non_negative(star_indy)) { + // this is currently illegal. we don't allow recursion + wildcards. + LOG(astring("illegal combination of recursion and wildcard: ") + + curr._source); + BAIL(70); + } + // handle the recursive guy. + int ret = patch_recursive_target(curr._source, curr._payload, i); + if (ret != 0) { + LOG(astring("failed during packing of recursive source: ") + + curr._source); + BAIL(72); + } + // take this item out of the picture, since all contents got included. + _manifest_list.zap(i, i); + i--; // skip back since we eliminated an index. + continue; + } else if (curr._flags & SOURCE_EXECUTE) { + // we have massaged the current manifest chunk as much as we can, so now + // we will execute the source item if that was specified. + BASE_LOG(astring("launching ") + curr._source); + if (!!curr._parms) { + curr._parms = parser_bits::substitute_env_vars(curr._parms, false); + BASE_LOG(astring("\tparameters ") + curr._parms); + } + BASE_LOG(astring('-', 76)); + basis::un_int kid; + basis::un_int retval = launch_process::run(curr._source, curr._parms, + launch_process::AWAIT_APP_EXIT, kid); + if (retval != 0) { + LOG(astring("failed to launch process, source=") + curr._source + + ", with parms " + curr._parms); + if (! (curr._flags & IGNORE_ERRORS) ) { + BAIL(92); + } + } + BASE_LOG(astring('-', 76)); + if (curr._flags & OMIT_PACKING) { + // this one shouldn't be included in the package. + _manifest_list.zap(i, i); + i--; // skip back since we eliminated an index. + } + continue; + } else { + // check for a wildcard. + int star_indy = curr._source.find("*"); + if (negative(star_indy)) continue; // simple targets are boring. + // this does have a wildcard in it. let's make sure it's in the right + // place for a wildcard in our scheme. + int slash_indy = curr._source.find('/', curr._source.end(), true); + if (star_indy < slash_indy) { + BASE_LOG(astring("illegal wildcard placement in ") + curr._source); + BASE_LOG(astring(" (the wildcard must be in the last component of the path)")); + BAIL(71); + } + // handle the wildcarded source. + int ret = patch_wildcard_target(curr._source, curr._payload, i); + if (ret != 0) { + LOG(astring("failed during packing of wildcarded source: ") + + curr._source); + BAIL(73); + } + _manifest_list.zap(i, i); + i--; // skip back since we eliminated an index. + continue; + } + } + +#ifdef DEBUG_BUNDLER + if (!final_return) { + // we had a successful run so we can print this stuff out. + LOG("read the following info from manifest:"); + for (int i = 0; i < _manifest_list.length(); i++) { + bundled_chunk &curr = _manifest_list[i]; + BASE_LOG(a_sprintf("(%d) size %d, %s => %s", i, curr._size, + curr._source.s(), curr._payload.s())); + } + } +#endif + + return final_return; +} + +astring bundle_creator::determine_stub_file_and_validate() +{ + FUNCDEF("determine_stub_file_and_validate"); + // define our location to find the unpacking stub program. +//hmmm: make this a command line parameter. +#ifdef __UNIX__ + astring stub_filename("unpacker_stub"); +#endif +#ifdef __WIN32__ + astring stub_filename("unpacker_stub.exe"); +#endif + astring repo_dir = "$PRODUCTION_DIR"; + astring stub_file = parser_bits::substitute_env_vars + (repo_dir + "/binaries/" + stub_filename, false); + if (!filename(stub_file).exists()) { + // we needed to find that to build the bundle. + LOG(astring("could not find unpacking stub file at: ") + stub_file); + return astring::empty_string(); + } + return stub_file; +} + +int bundle_creator::write_stub_and_toc() +{ + FUNCDEF("write_stub_and_toc"); + + astring stub_file = determine_stub_file_and_validate(); + if (!stub_file) return 1; + + // make sure the stub is accessible. + byte_filer stubby(stub_file, "rb"); + if (!stubby.good()) { + FAIL_RETURN(80, astring("could not read the unpacking stub at: ") + stub_file); + } + _stub_size = int(stubby.length()); // get the stub size for later reference. + byte_array whole_stub; + stubby.read(whole_stub, _stub_size + 100); + stubby.close(); + _bundle->write(whole_stub); + + byte_array packed_toc_len; + structures::obscure_attach(packed_toc_len, _manifest_list.length()); + int ret = _bundle->write(packed_toc_len); + if (ret < 0) { + LOG(astring("could not write the TOC length to the bundle: ") + + _output_file); + return 81; + } + + // dump out the manifest list in our defined format. + for (int i = 0; i < _manifest_list.length(); i++) { + bundled_chunk &curr = _manifest_list[i]; +//LOG(a_sprintf("flag %d is %d", i, curr._flags)); + byte_array chunk; + curr.pack(chunk); + if (_bundle->write(chunk) <= 0) { + LOG(a_sprintf("could not write item #%d [%s] to the bundle: ", i, + curr._source.s()) + + _output_file); + return 88; + } + } + + return 0; +} + +int bundle_creator::bundle_sources() +{ + FUNCDEF("bundle_sources"); + // go through all the source files and append them to the bundled output. + file_logger noisy_logfile(application_configuration::make_logfile_name + ("bundle_creator_activity.log")); + for (int i = 0; i < _manifest_list.length(); i++) { + bundled_chunk &curr = _manifest_list[i]; + + if (curr._flags & SET_VARIABLE) { + // all we need to do is keep this in the manifest. + noisy_logfile.log(astring("bundling: variable setting ") + curr._payload + + "=" + curr._parms); + continue; + } else if (curr._flags & TEST_VARIABLE_DEFINED) { + // just remember to test this when running the unpack. + noisy_logfile.log(astring("bundling: test variable ") + curr._payload + + " is defined."); + continue; + } else if (curr._flags & OMIT_PACKING) { + // this one shouldn't be included in the package. + continue; + } + + noisy_logfile.log(astring("bundling: ") + curr._source); + byte_filer source(curr._source, "rb"); + if (!source.good()) { + LOG(a_sprintf("could not read item #%d for the bundle: \"", i) + + curr._source + "\""); + return 98; + } + + byte_array compressed(256 * KILOBYTE); // expand the buffer to start with. + byte_array temp; // temporary read buffer. + + // chew on the file a chunk at a time. this allows us to easily handle + // arbitrarily large files rather than reading their entirety into memory. + int total_written = 0; + do { + int ret = source.read(temp, CHUNKING_SIZE); + if (ret < 0) { + LOG(a_sprintf("failed while reading item #%d: ", i) + curr._source); + return 99; + } + total_written += ret; // add in what we expect to write. + // skip compressing if there's no data. + uLongf destlen = 0; + bool null_chunk = false; + if (ret == 0) { + compressed.reset(); + null_chunk = true; + } else { + compressed.reset(int(0.1 * ret) + ret + KILOBYTE); + // provide some extra space as per zlib instructions. we're giving it + // way more than they request. + destlen = compressed.length(); + // pack the chunks first so we can know sizes needed. + int comp_ret = compress(compressed.access(), &destlen, temp.observe(), + temp.length()); + if (comp_ret != Z_OK) { + LOG(a_sprintf("failed while compressing item #%d: ", i) + + curr._source); + return 99; + } + compressed.zap(destlen, compressed.length() - 1); + } + byte_array just_sizes; + structures::obscure_attach(just_sizes, temp.length()); + // add in the real size. + structures::obscure_attach(just_sizes, int(destlen)); + // add in the packed size. + ret = _bundle->write(just_sizes); + if (ret <= 0) { + LOG(a_sprintf("failed while writing sizes for item #%d: ", i) + + curr._source); + return 93; + } + if (!null_chunk) { + ret = _bundle->write(compressed); + if (ret <= 0) { + LOG(a_sprintf("failed while writing item #%d: ", i) + curr._source); + return 93; + } else if (ret != compressed.length()) { + LOG(a_sprintf("wrote different size for item #%d (tried %d, " + "wrote %d): ", i, compressed.length(), ret) + curr._source); + return 93; + } + } + } while (!source.eof()); +//hmmm: very common code to above size writing. + byte_array just_sizes; + structures::obscure_attach(just_sizes, -1); + structures::obscure_attach(just_sizes, -1); + int ret = _bundle->write(just_sizes); + if (ret <= 0) { + LOG(a_sprintf("failed while writing sentinel of item #%d: ", i) + + curr._source); + return 96; + } + source.close(); + if (total_written != curr._size) { + LOG(a_sprintf("size (%d) disagrees with initial size (%d) for " + "item #%d: ", total_written, curr._size, i) + curr._source); + } + } + noisy_logfile.log(astring("Bundling run ends at ") + time_stamp::notarize(false)); + noisy_logfile.log(astring('-', 76)); + + return 0; +} + +int bundle_creator::finalize_file() +{ + _bundle->close(); + return 0; +} + +int bundle_creator::write_offset() +{ +// FUNCDEF("write_offset"); + byte_filer bun(_output_file, "r+b"); // open the file for updating. + + astring magic_string("muftiloc"); // our sentinel string. + astring temp_string; // data from the file. + + while (!bun.eof()) { + // find the telltale text in the file. + bool found_it = false; // we'll set this to true if we see the string. + int location = 0; // where the sentinel's end is. + for (int i = 0; i < magic_string.length(); i++) { + int ret = bun.read(temp_string, 1); + if (ret <= 0) break; + if (temp_string[0] != magic_string[i]) break; // no match. + if (i == magic_string.end()) { + // we found a match to our string! + found_it = true; + location = int(bun.tell()); +//LOG(a_sprintf("found the sentinel in the file! posn=%d", location)); + } + } + if (!found_it) continue; // keep reading. + bun.seek(location); + byte_array packed_offset; + structures::obscure_attach(packed_offset, _stub_size); +//LOG(astring("pattern of len is:\n") + byte_format::text_dump(packed_offset)); + // write the offset into the current position, which should be just after + // the sentinel's location. + bun.write(packed_offset); +//LOG(a_sprintf("wrote manifest offset before posn=%d", bun.tell())); + break; // done with looking for that pattern. + } + bun.close(); // completely finished now. + + chmod(_output_file.s(), 0766); + // make sure it's an executable file when we're done with it. + + BASE_LOG(astring("done file bundling at ") + time_stamp::notarize(false)); + + return 0; +} + +//////////////////////////////////////////////////////////////////////////// + +HOOPLE_MAIN(bundle_creator, ) + +#ifdef __BUILD_STATIC_APPLICATION__ + // static dependencies found by buildor_gen_deps.sh: + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif // __BUILD_STATIC_APPLICATION__ + diff --git a/core/applications/bundler/bundler_version.rc b/core/applications/bundler/bundler_version.rc new file mode 100644 index 00000000..998ee3e2 --- /dev/null +++ b/core/applications/bundler/bundler_version.rc @@ -0,0 +1,46 @@ +#ifndef NO_VERSION +#include +#include <__build_version.h> +#include <__build_configuration.h> +#define BI_PLAT_WIN32 + // force 32 bit compile. +1 VERSIONINFO LOADONCALL MOVEABLE +FILEVERSION __build_FILE_VERSION_COMMAS +PRODUCTVERSION __build_PRODUCT_VERSION_COMMAS +FILEFLAGSMASK 0 +FILEFLAGS VS_FFI_FILEFLAGSMASK +#if defined(BI_PLAT_WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + // Language type = U.S. English(0x0409) and Character Set = Windows, Multilingual(0x04b0) + BLOCK "040904b0" // Matches VarFileInfo Translation hex value. + BEGIN + VALUE "CompanyName", __build_company "\000" +#ifndef _DEBUG + VALUE "FileDescription", "Application Bundler\000" +#else + VALUE "FileDescription", "Application Bundler (DEBUG)\000" +#endif + VALUE "FileVersion", __build_FILE_VERSION "\000" + VALUE "ProductVersion", __build_PRODUCT_VERSION "\000" + VALUE "InternalName", "Bundler\000" + VALUE "LegalCopyright", __build_copyright "\000" + VALUE "LegalTrademarks", __build_legal_info "\000" + VALUE "OriginalFilename", "bundler.exe\000" + VALUE "ProductName", __build_product_name "\000" + + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04b0 // US English (0x0409) and win32 multilingual (0x04b0) + END +END +#endif diff --git a/core/applications/bundler/common_bundle.cpp b/core/applications/bundler/common_bundle.cpp new file mode 100644 index 00000000..6d170b59 --- /dev/null +++ b/core/applications/bundler/common_bundle.cpp @@ -0,0 +1,139 @@ +/*****************************************************************************\ +* * +* Name : common bundler definitions * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2007-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "common_bundle.h" + +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace filesystem; +using namespace structures; + +manifest_chunk::~manifest_chunk() +{} + +int manifest_chunk::packed_filetime_size() +{ + static file_time hidden_comparison_object; + return hidden_comparison_object.packed_size(); +} + +void manifest_chunk::pack(byte_array &target) const +{ + structures::obscure_attach(target, _size); + _payload.pack(target); + structures::attach(target, _flags); + _parms.pack(target); + _keywords.pack(target); + target += c_filetime; +} + +bool manifest_chunk::unpack(byte_array &source) +{ + if (!structures::obscure_detach(source, _size)) return false; + if (!_payload.unpack(source)) return false; + if (!structures::detach(source, _flags)) return false; + if (!_parms.unpack(source)) return false; + if (!_keywords.unpack(source)) return false; + if (source.length() < 8) return false; + c_filetime = source.subarray(0, 7); + source.zap(0, 7); + return true; +} + +bool manifest_chunk::read_an_int(byte_filer &bundle, un_int &found) +{ +// FUNCDEF("read_an_int"); + byte_array temp; + if (bundle.read(temp, sizeof(int)) != sizeof(int)) return false; + if (!structures::detach(temp, found)) return false; + return true; +} + +bool manifest_chunk::read_an_obscured_int(byte_filer &bundle, un_int &found) +{ +// FUNCDEF("read_an_obscured_int"); + byte_array temp; + if (bundle.read(temp, 2 * sizeof(int)) != 2 * sizeof(int)) return false; + if (!structures::obscure_detach(temp, found)) return false; + return true; +} + +bool manifest_chunk::read_a_filetime(byte_filer &bundle, byte_array &found) +{ +// FUNCDEF("read_a_filetime"); + byte_array temp; + // the trick below only works because we know we have a constant sized packed version + // for the file time. + if (bundle.read(temp, packed_filetime_size()) != packed_filetime_size()) return false; + found = temp; + return true; +} + +astring manifest_chunk::read_a_string(byte_filer &bundle) +{ +// FUNCDEF("read_a_string"); + astring found; + byte_array temp; + // read in the zero-terminated character string. + while (!bundle.eof()) { + // read a single byte out of the file. + if (bundle.read(temp, 1) <= 0) + break; + if (temp[0]) { + // add the byte to the string we're accumulating. + found += temp[0]; + } else { + // this string is done now. + break; + } + } + return found; +} + +bool manifest_chunk::read_manifest(byte_filer &bundle, manifest_chunk &curr) +{ + curr._size = 0; + bool worked = read_an_obscured_int(bundle, curr._size); + if (!worked) + return false; + byte_array temp; + curr._payload = read_a_string(bundle); + if (!curr._payload) return false; + worked = read_an_int(bundle, curr._flags); + if (!worked) + return false; + curr._parms = read_a_string(bundle); + // it's valid for the _parms to be empty. +//if (curr._parms.length()) { printf("parms len=%d are: \"%s\"\n", curr._parms.length(), curr._parms.s()); } + // now get the keywords list, if it exists. + un_int key_elems = 0; // number of keywords. + worked = read_an_obscured_int(bundle, key_elems); // get number of elements. + if (!worked) + return false; + curr._keywords.reset(); + for (int i = 0; i < (int)key_elems; i++) { + astring found = read_a_string(bundle); + if (!found) return false; // not allowed an empty keyword. + curr._keywords += found; + } + worked = read_a_filetime(bundle, curr.c_filetime); + return worked; +} + diff --git a/core/applications/bundler/common_bundle.h b/core/applications/bundler/common_bundle.h new file mode 100644 index 00000000..9e9a57e3 --- /dev/null +++ b/core/applications/bundler/common_bundle.h @@ -0,0 +1,113 @@ +#ifndef COMMON_BUNDLER_DEFS +#define COMMON_BUNDLER_DEFS + +/*****************************************************************************\ +* * +* Name : common bundler definitions * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2007-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! Contains some definitions used by both the bundle creator and unpacker. +/*! + Note that this is a heavyweight header and should not be pulled into + other headers. +*/ + +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////// + +//! flags that control special attributes of the packed files. +enum special_bundling_flags { + SOURCE_EXECUTE = 0x2, //!< the file should be executed before bundling. + TARGET_EXECUTE = 0x4, //!< the file should be executed on unbundling. + RECURSIVE_SRC = 0x8, //!< source is a recursive folder. + OMIT_PACKING = 0x10, //!< for a source side exe, do not pack the file. + SET_VARIABLE = 0x20, //!< this item just has a variable assignment. + IGNORE_ERRORS = 0x40, //!< if set, errors in an item will not stop program. + NO_OVERWRITE = 0x80, //!< target file will not be overwritten if exists. + QUIET_FAILURE = 0x100, //!< when errors happen, no popup message happens. + MAKE_BACKUP_FILE = 0x200, //!< save a copy if original file already exists. + TEST_VARIABLE_DEFINED = 0x400 //!< check for required variable's presence. +}; + +//////////////////////////////////////////////////////////////////////////// + +//! we will read the manifest pieces out of our own exe image. +/*! + the manifest chunks provide us with enough information to unpack the + data chunks that come afterward. +*/ + +struct manifest_chunk : public basis::text_formable +{ + basis::un_int _size; //!< the size of the packed file. + basis::astring _payload; //!< guts of the chunk, such as location for file on target or a variable definition. + basis::un_int _flags; //!< uses the special_bundling_flags. + basis::astring _parms; //!< the parameters to pass on the command line. + structures::string_set _keywords; //!< keywords applicable to this item. + basis::byte_array c_filetime; //!< more than enough room for unix file time. + + // note: when flags has SET_VARIABLE, the _payload is the variable + // name to be set and the _parms is the value to use. + + static int packed_filetime_size(); + + //! the chunk is the unit found in the packing manifest in the bundle. + manifest_chunk(int size, const basis::astring &target, int flags, + const basis::astring &parms, const structures::string_set &keywords) + : _size(size), _payload(target), _flags(flags), _parms(parms), + _keywords(keywords), c_filetime(packed_filetime_size()) { + for (int i = 0; i < packed_filetime_size(); i++) c_filetime[i] = 0; + } + + manifest_chunk() : _size(0), _flags(0), c_filetime(packed_filetime_size()) { + //!< default constructor. + for (int i = 0; i < packed_filetime_size(); i++) c_filetime[i] = 0; + } + + virtual ~manifest_chunk(); + + virtual void text_form(basis::base_string &state_fill) const { + state_fill.assign(basis::astring(class_name()) + + basis::a_sprintf(": size=%d payload=%s flags=%x parms=%s", + _size, _payload.s(), _flags, _parms.s())); + } + + DEFINE_CLASS_NAME("manifest_chunk"); + + void pack(basis::byte_array &target) const; //!< streams out into the "target". + bool unpack(basis::byte_array &source); //!< streams in from the "source". + + static bool read_manifest(filesystem::byte_filer &bundle, manifest_chunk &to_fill); + //!< reads a chunk out of the "bundle" and stores it in "to_fill". + /*!< false is returned if the read failed. */ + + static basis::astring read_a_string(filesystem::byte_filer &bundle); + //!< reads a string from the "bundle" file, one byte at a time. + + static bool read_an_int(filesystem::byte_filer &bundle, basis::un_int &found); + //!< reads an integer (4 bytes) from the file into "found". + + static bool read_an_obscured_int(filesystem::byte_filer &bundle, basis::un_int &found); + //!< reads in our obscured packing format for an int, which takes 8 bytes. + + static bool read_a_filetime(filesystem::byte_filer &bundle, basis::byte_array &found); + //!< retrieves packed_filetime_size() byte timestamp from the "bundle". +}; + +//////////////////////////////////////////////////////////////////////////// + +#endif + diff --git a/core/applications/bundler/makefile b/core/applications/bundler/makefile new file mode 100644 index 00000000..1137f980 --- /dev/null +++ b/core/applications/bundler/makefile @@ -0,0 +1,27 @@ +CONSOLE_MODE = true + +include cpp/variables.def + +PROJECT = app_bundle +TYPE = application +SOURCE = common_bundle.cpp +ifeq "$(OMIT_VERSIONS)" "" + SOURCE += bundler_version.rc +endif +DEFINITIONS += __BUILD_STATIC_APPLICATION__=t +TARGETS = bundle_creator.exe +LAST_TARGETS += make_stub +ifeq "$(OP_SYSTEM)" "WIN32" + LOCAL_HEADERS += $(THIRD_PARTY_DIR)/zlib/include + LOCAL_LIBRARIES += $(THIRD_PARTY_DIR)/zlib/lib + LIBS_USED += zlib.lib +endif +ifeq "$(OP_SYSTEM)" "UNIX" + LIBS_USED += z +endif + +include cpp/rules.def + +make_stub: + $(MAKE) -f makefile.stub + diff --git a/core/applications/bundler/makefile.stub b/core/applications/bundler/makefile.stub new file mode 100644 index 00000000..4ab4683e --- /dev/null +++ b/core/applications/bundler/makefile.stub @@ -0,0 +1,24 @@ +CONSOLE_MODE = true + +include cpp/variables.def + +PROJECT = app_bundle_stub +TYPE = application +SOURCE = common_bundle.cpp +ifeq "$(OMIT_VERSIONS)" "" + SOURCE += bundler_version.rc +endif +DEFINITIONS += __BUILD_STATIC_APPLICATION__=t +TARGETS = unpacker_stub.exe +ifeq "$(OP_SYSTEM)" "WIN32" + LIBS_USED += libcmt.lib shlwapi.lib zlib.lib + LOAD_FLAG_PREFIX += -nodefaultlib:msvcrt.lib + COMPILER_FLAGS += -MT + LOCAL_HEADERS += $(THIRD_PARTY_DIR)/zlib/include + LOCAL_LIBRARIES += $(THIRD_PARTY_DIR)/zlib/lib +else + LIBS_USED += z +endif + +include cpp/rules.def + diff --git a/core/applications/bundler/manifest_format.txt b/core/applications/bundler/manifest_format.txt new file mode 100644 index 00000000..20e38a93 --- /dev/null +++ b/core/applications/bundler/manifest_format.txt @@ -0,0 +1,23 @@ + +the unpacking manifest is a structure defined in terms of bytes. +the exe's manifest offset is set to point to the beginning of this structure. + +bytes content +----- ------- +0 => 3 number of chunks in the TOC +4 => 4+N-1 first manifest item, with length N +4+N => 4+N+M-1 second item, with length M +4+N+M =>...etc. + +each bundle chunk has a structure: + +bytes content +----- ------- +0 => 3 size of the data component of the chunk +4 => 4+S-1 the file system target location for this chunk, as a zero + terminated string (of length S). this string comes from the + target defined in the packing manifest. + +then the data starts after the end of the TOC; each chunk occupies the space +declared for it in the manifest. + diff --git a/core/applications/bundler/unpacker_stub.cpp b/core/applications/bundler/unpacker_stub.cpp new file mode 100644 index 00000000..a515d8cc --- /dev/null +++ b/core/applications/bundler/unpacker_stub.cpp @@ -0,0 +1,660 @@ +/*****************************************************************************\ +* * +* Name : unpacker stub program * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2006-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "common_bundle.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef __UNIX__ + #include +#endif +#ifdef __WIN32__ + #include + #include + #include + #include +#endif + +using namespace application; +using namespace basis; +using namespace configuration; +using namespace filesystem; +using namespace loggers; +using namespace processes; +using namespace structures; +using namespace textual; + +const int CHUNKING_SIZE = 64 * KILOBYTE; + // we'll read this big a chunk from a source file at a time. + +const astring TARGET_WORD = "TARGET"; +const astring LOGDIR_WORD = "LOGDIR"; + +#define BASE_LOG(to_print) program_wide_logger::get().log(to_print, ALWAYS_PRINT) +#define LOG(to_print) STAMPED_EMERGENCY_LOG(program_wide_logger::get(), to_print) + +//#define DEBUG_STUB + // uncomment for noisier version. + +const char *ERROR_TITLE = "An Error Caused Incomplete Installation"; + // used in error messages as the title. + +//////////////////////////////////////////////////////////////////////////// + +class unpacker_stub : public application_shell +{ +public: + unpacker_stub() : application_shell(), _app_name(filename(_global_argv[0]).basename()) {} + DEFINE_CLASS_NAME("unpacker_stub"); + + int print_instructions(); + + virtual int execute(); + +private: + astring _app_name; + array _manifest; //!< the list of chunks to unpack. + string_table _variables; //!< our list of variable overrides. +}; + +//////////////////////////////////////////////////////////////////////////// + +void show_message(const astring &msg, const astring &title) +{ +#ifndef __WIN32__ + BASE_LOG(title); + BASE_LOG(astring('-', title.length())); + BASE_LOG(msg); +#else + MessageBox(0, to_unicode_temp(msg), to_unicode_temp(title), + MB_OK | MB_ICONINFORMATION); +#endif +} + +int unpacker_stub::print_instructions() +{ + a_sprintf msg("\ + %s: This program unpacks its contents into the locations\n\ + specified at packing time. The --target flag can be used to specify a\n\ +different %s directory for the installation (for components that use\n\ +the default %s variable to specify their install folder).\n\ + One can also pass a --keyword flag to specify a keyword; the files in the\n\ +bundle that are marked with that keyword will be installed, but files that\n\ +are missing the keyword will not be.\n\ + Further, variables can be overridden on the command line in the\n\ +form: X=Y.\n\n\ +The line below uses all these parameters as an example:\n\n\ + %s --target c:\\Program Files\\gubernator --keyword dlls_only SILENT=true\n\ +\n\ +Additional Notes:\n\ +\n\ + One helpful variable is \"%s\". This is where the unpacking log file\n\ +will be written to. The default is \"~/logs\" (or \"$TMP/logs\" on win32)\n\ +until overridden.\n\ +\n", _app_name.s(), TARGET_WORD.s(), TARGET_WORD.s(), _app_name.s(), LOGDIR_WORD.s()); + show_message(msg, "Unpacking Instructions"); + return 12; +} + +// creates a unique backup file name, if it can. +// we assume that this file already exists, but we want to check for +// our backup file naming scheme in case we already backed this up +// some previous time. +astring find_unique_backup_name(astring original_file) +{ +const int MAXIMUM_BACKUPS = 200; + + for (int i = 0; i < MAXIMUM_BACKUPS; i++) { + filename target_file = original_file + a_sprintf(".%04d", i); + if (target_file.exists()) { +//BASE_LOG(astring("bkup already here: ") + target_file); + continue; + } + // this file is okay to use. + return target_file; + } + return ""; // nothing found. +} + + +// the string embedded into the array is not mentioned anywhere else in this +// program, which should allow the packer to find it and fix the manifest +// size. the keyword is: "muftiloc", and the first bytes that can be +// overwritten are its beginning offset plus its length of 8 chars. there +// is room for 8 bytes after the tag, but currently the first 4 are used as +// a 32 bit offset. +abyte MANIFEST_OFFSET_ARRAY[] + = { 'm', 'u', 'f', 't', 'i', 'l', 'o', 'c', 0, 0, 0, 0, 0, 0, 0, 0 }; + +int unpacker_stub::execute() +{ +#ifdef ADMIN_CHECK + #ifdef __WIN32__ + if (IsUserAnAdmin()) { + ::MessageBox(0, to_unicode_temp("IS admin in bundler"), to_unicode_temp("bundler"), MB_OK); + } else { + ::MessageBox(0, to_unicode_temp("NOT admin in bundler"), to_unicode_temp("bundler"), MB_OK); + } + #endif +#endif + + command_line cmds(_global_argc, _global_argv); + + int indy = 0; + if (cmds.find('?', indy)) return print_instructions(); + if (cmds.find("?", indy)) return print_instructions(); + + // make sure we provide the same services as the bundle creator for the + // default set of variables. +#ifndef __WIN32__ + environment::set("EXE_END", ""); // executable file ending. + environment::set("DLL_START", "lib"); // dll file prefix. + environment::set("DLL_END", ".so"); // dll file ending. +#else + environment::set("EXE_END", ".exe"); + environment::set("DLL_START", ""); + environment::set("DLL_END", ".dll"); +#endif + + // set TARGET directory if passed on the command line, + bool provided_target = false; // true if the command line specified target. + astring target; + cmds.get_value("target", target); + if (!target) { +/* no, this is wrong headed. make them supply a target if there is + * no default target provided in the bundle. + // a bogus default is provided if they don't specify the destination. + target = environment::get("TMP") + "/unbundled"; +*/ + provided_target = false; + } else { +//LOG(astring("target is now ") + target); + environment::set(TARGET_WORD, target); + provided_target = true; + } + + { + astring logdir = environment::get(LOGDIR_WORD); +#ifdef __WIN32__ + if (!logdir) { + logdir = environment::get("TMP") + "/logs"; + environment::set(LOGDIR_WORD, logdir); + } +#else + if (!logdir) { + astring homedir = environment::get("HOME"); + logdir = homedir + "/logs"; + environment::set(LOGDIR_WORD, logdir); + } +#endif + } + + astring keyword; // set if we were given a keyword on cmd line. + cmds.get_value("keyword", keyword); + + astring vars_set; // we will document the variables we saw and show later. + + for (int x = 0; x < cmds.entries(); x++) { + command_parameter curr = cmds.get(x); + if (curr.type() != command_parameter::VALUE) continue; // skip it. + if (curr.text().find('=', 0) < 0) continue; // no equals character. + variable_tokenizer t; + t.parse(curr.text()); + if (!t.symbols()) continue; // didn't parse right. + astring var = t.table().name(0); + astring value = t.table()[0]; + vars_set += astring("variable set: ") + var + "=" + value + + parser_bits::platform_eol_to_chars(); + if (var == TARGET_WORD) { + provided_target = true; + } +//hmmm: handle LOGDIR passed as variable this way also! + _variables.add(var, value); + environment::set(var, value); + } + + // get the most up to date version of the variable now. + astring logdir = environment::get(LOGDIR_WORD); + + astring appname = filename(application_configuration::application_name()).rootname(); + + astring logname = logdir + "/" + appname + ".log"; +// log_base *old_log = set_PW_logger_for_combo(logname); + standard_log_base *old_log = program_wide_logger::set(new combo_logger(logname)); + WHACK(old_log); + + BASE_LOG(astring('#', 76)); + BASE_LOG(appname + " command-line parameters:"); + BASE_LOG(cmds.text_form()); + BASE_LOG(astring('#', 76)); + + BASE_LOG(vars_set); + +#ifdef __WIN32__ + // create a window so that installshield won't barf. this is only needed + // on windows when using this as a prerequisite for installshield. + window_handle f_window = create_simplistic_window("temp_stubby_class", + "stubby window title"); +#endif + + // read position for manifest offset out of our array. + byte_array temp_packed(2 * sizeof(int), MANIFEST_OFFSET_ARRAY + 8); + un_int manifest_offset; + if (!structures::obscure_detach(temp_packed, manifest_offset)) { + show_message(astring("could not read manifest offset in: ") + _global_argv[0], + ERROR_TITLE); + return 24; + } + + filename this_exe(_global_argv[0]); + if (!this_exe.exists()) { + show_message(astring("could not access this exe image: ") + this_exe.raw(), + ERROR_TITLE); + return 23; + } + + // start reading the manifest... + byte_filer our_exe(this_exe, "rb"); + our_exe.seek(manifest_offset); // go to where the manifest starts. + + // get number of chunks in manifest as the first bytes. + if (our_exe.read(temp_packed, 2 * sizeof(int)) <= 0) { + show_message(astring("could not read the manifest length in: ") + + this_exe.raw(), ERROR_TITLE); + return 26; + } + un_int item_count; + structures::obscure_detach(temp_packed, item_count); +//check result of detach! + _manifest.insert(0, item_count); // add enough spaces for our item list. + + // read each item definition out of the manifest now. + for (int i = 0; i < (int)item_count; i++) { + manifest_chunk &curr = _manifest[i]; + bool worked = manifest_chunk::read_manifest(our_exe, curr); + +#ifdef DEBUG_STUB + astring tmpork; + curr.text_form(tmpork); + LOG(a_sprintf("item %d: ", i) + tmpork); +#endif + + if (!worked) { + show_message(a_sprintf("could not read chunk for item #%d [%s]: ", i, + curr._payload.s()) + + this_exe.raw(), ERROR_TITLE); + return 86; + } + } + +#ifdef DEBUG_STUB + LOG("read the following info from manifest:"); + astring temp; + for (int i = 0; i < _manifest.length(); i++) { + manifest_chunk &curr = _manifest[i]; + temp += a_sprintf("(%d) size %d, %s\n", i, curr._size, + curr._payload.s()); + } + critical_events::alert_message(temp, "manifest contents"); +#endif + + // now we should be just after the last byte of the manifest, at the + // first piece of data. we should read each chunk of data out and store + // it where it's supposed to go. + for (int festdex = 0; festdex < _manifest.length(); festdex++) { + manifest_chunk &curr = _manifest[festdex]; + int size_left = curr._size; + + // patch in our environment variables. + curr._payload = parser_bits::substitute_env_vars(curr._payload, false); + curr._parms = parser_bits::substitute_env_vars(curr._parms, false); +#ifdef DEBUG_STUB + BASE_LOG(astring("processing ") + curr._payload + + a_sprintf(", size=%d, flags=%d", curr._size, curr._flags)); + if (!!curr._parms) + BASE_LOG(astring(" parms: ") + curr._parms); +#endif + + // see if they specified a keyword on the command line. + bool keyword_good = true; + if (!!keyword && !curr._keywords.member(keyword)) { + // their keyword choice didn't match what we were pickled with. + keyword_good = false; +//BASE_LOG(astring("skipping ") + curr._payload + " for wrong keyword " + keyword); + } + + if (curr._flags & SET_VARIABLE) { + if (keyword_good) { + // this is utterly different from a real target. we just set the + // variable and move on. + if (provided_target && (curr._payload == TARGET_WORD) ) { + BASE_LOG(astring("skipping ") + curr._payload + "=" + curr._parms + + ": was provided explicitly as " + target); + } else if (_variables.find(curr._payload)) { + BASE_LOG(astring("skipping ") + curr._payload + "=" + curr._parms + + ": was provided on command line."); + } else { + BASE_LOG(astring("setting ") + curr._payload + "=" + curr._parms); + environment::set(curr._payload, curr._parms); + + // special code for changing logging directory midstream. + if (curr._payload == LOGDIR_WORD) { + astring logdir = curr._parms; + astring appname = filename(application_configuration::application_name()).rootname(); + astring logname = logdir + "/" + appname + ".log"; + standard_log_base *old_log = program_wide_logger::set(new combo_logger(logname)); +/// log_base *old_log = set_PW_logger_for_combo(logname); + WHACK(old_log); + } + if (curr._payload == TARGET_WORD) { + // well we've now seen this defined. + provided_target = true; + } + } + } + continue; + } else if (curr._flags & TEST_VARIABLE_DEFINED) { + if (keyword_good) { + astring var_value = environment::get(curr._payload); + if (var_value.empty()) { + BASE_LOG(astring("assertion failed: ") + curr._payload + " is not defined!"); + show_message(a_sprintf("failed test for variable %s: it is " + "*not* defined, for item #%d.", curr._payload.s(), festdex), + ERROR_TITLE); + return 98; + } + BASE_LOG(astring("assertion succeeded: ") + curr._payload + " defined as " + var_value); + } + continue; + } + + if (! (curr._flags & OMIT_PACKING) ) { + // this one has a payload, so install it now if appropriate. + if (!provided_target) { +//error! + BASE_LOG(astring("No TARGET has been specified; please provide one on the command line.") + parser_bits::platform_eol_to_chars()); + return print_instructions(); + } + + // make sure that the directories needed are present for the outputs. + filename target_dir = filename(curr._payload).dirname(); + + if (keyword_good && !target_dir.exists() + && !directory::recursive_create(target_dir)) { + LOG(a_sprintf("failed to create directory %s for item #%d: ", + target_dir.raw().s(), festdex) + curr._payload); + } + + // test whether they wanted to allow overwriting. + if (curr._flags & NO_OVERWRITE) { + filename target_file(curr._payload); + if (target_file.exists()) { + BASE_LOG(astring("not overwriting existing ") + curr._payload); + keyword_good = false; + } + } + + // see if this is supposed to be backed up before installation. + if (curr._flags & MAKE_BACKUP_FILE) { + filename target_file(curr._payload); + if (target_file.exists()) { + astring new_file_name = find_unique_backup_name(curr._payload); + if (!new_file_name) { + BASE_LOG(astring("failed to calculate new filename for ") + curr._payload); + keyword_good = false; // cancel the overwrite, couldn't backup. + } else { + // make a backup of the file by moving the old file name to the + // new file name, which should be unique, and then the current + // name is all clear to be written as a new file. +// BASE_LOG(astring("backing up ") + curr._payload + " --> " + new_file_name); + int retval = rename(curr._payload.s(), new_file_name.s()); + if (retval) { + BASE_LOG(astring("failed to rename ") + curr._payload + " as " + new_file_name); + keyword_good = false; // cancel the overwrite, couldn't backup. + } + } + } + } + + byte_filer *targo = NIL; + if (keyword_good) targo = new byte_filer(curr._payload, "wb"); + byte_array uncompressed(256 * KILOBYTE); // fluff it out to begin with. + byte_array temp(256 * KILOBYTE); + + bool first_read = true; + // true if there haven't been any reads of the file before now. + bool too_tiny_complaint_already = false; + // becomes true if we complain about the file's size being larger than + // expected. this allows us to only complain once about each file. + + // read a chunk at a time out of our exe image and store it into the + // target file. + while (first_read || !our_exe.eof()) { + first_read = false; + un_int real_size = 0, packed_size = 0; + // read in the real size from the file. + bool worked = manifest_chunk::read_an_obscured_int(our_exe, real_size); + if (!worked) { + show_message(a_sprintf("failed while reading real size " + "for item #%d: ", festdex) + curr._payload, ERROR_TITLE); + return 99; + } + // read in the packed size now. + worked = manifest_chunk::read_an_obscured_int(our_exe, packed_size); + if (!worked) { + show_message(a_sprintf("failed while reading packed size " + "for item #%d: ", festdex) + curr._payload, ERROR_TITLE); + return 99; + } + + // make sure we don't eat the whole package--did the file end? + if ( (real_size == -1) && (packed_size == -1) ) { + // we've hit our sentinel; we've already unpacked all of this file. + break; + } + +#ifdef DEBUG_STUB + BASE_LOG(a_sprintf("chunk packed_size=%d, real_size=%d", packed_size, + real_size)); +#endif + + // now we know how big our next chunk is, so we can try reading it. + if (packed_size) { + int ret = our_exe.read(temp, packed_size); + if (ret <= 0) { + show_message(a_sprintf("failed while reading item #%d: ", festdex) + + curr._payload, ERROR_TITLE); + return 99; + } else if (ret != packed_size) { + show_message(a_sprintf("bad trouble ahead, item #%d had different " + " size on read (expected %d, got %d): ", festdex, packed_size, + ret) + curr._payload, ERROR_TITLE); + } + + uncompressed.reset(real_size + KILOBYTE); // add some for paranoia. + uLongf destlen = uncompressed.length(); + int uncomp_ret = uncompress(uncompressed.access(), &destlen, + temp.observe(), packed_size); + if (uncomp_ret != Z_OK) { + show_message(a_sprintf("failed while uncompressing item #%d: ", + festdex) + curr._payload, ERROR_TITLE); + return 99; + } + + if (int(destlen) != real_size) { + LOG(a_sprintf("got a different unpacked size for item #%d: ", + festdex) + curr._payload); + } + + // update the remaining size for this data chunk. + size_left -= real_size; + if (size_left < 0) { + if (!too_tiny_complaint_already) { + LOG(a_sprintf("item #%d was larger than expected (non-fatal): ", + festdex) + curr._payload); + too_tiny_complaint_already = true; + } + } + // toss the extra bytes out. + uncompressed.zap(real_size, uncompressed.length() - 1); + + if (targo) { + // stuff the data we read into the target file. + ret = targo->write(uncompressed); + if (ret != uncompressed.length()) { + show_message(a_sprintf("failed while writing item #%d: ", festdex) + + curr._payload, ERROR_TITLE); + return 93; + } + } + } + } + if (targo) targo->close(); + WHACK(targo); + // the file's written, but now we slap it's old time on it too. + file_time t; + if (!t.unpack(curr.c_filetime)) { + show_message(astring("failed to interpret timestamp for ") + + curr._payload, ERROR_TITLE); + return 97; + } + // put the timestamp on the file. + t.set_time(curr._payload); +// utimbuf held_time; +// held_time.actime = t.raw(); +// held_time.modtime = t.raw(); +// // put the timestamp on the file. +// utime(curr._payload.s(), &held_time); + } + + // now that we're pretty sure the file exists, we can run it if needed. + if ( (curr._flags & TARGET_EXECUTE) && keyword_good) { + // change the mode on the target file so we can execute it. + chmod(curr._payload.s(), 0766); + astring prev_dir = application_configuration::current_directory(); + + BASE_LOG(astring("launching ") + curr._payload); + if (!!curr._parms) + BASE_LOG(astring(" with parameters: ") + curr._parms); + BASE_LOG(astring('-', 76)); + + basis::un_int kid; + basis::un_int retval = launch_process::run(curr._payload, curr._parms, + launch_process::AWAIT_APP_EXIT | launch_process::HIDE_APP_WINDOW, kid); + if (retval != 0) { + if (! (curr._flags & IGNORE_ERRORS) ) { + if (curr._flags & QUIET_FAILURE) { + // no message box for this, but still log it. + LOG(astring("failed to launch process, targ=") + + curr._payload + " with parms " + curr._parms + + a_sprintf(" error=%d", retval)); + } else { + show_message(astring("failed to launch process, targ=") + + curr._payload + " with parms " + curr._parms + + a_sprintf(" error=%d", retval), ERROR_TITLE); + } + return retval; // pass along same exit value we were told. + } else { + LOG(astring("ignoring failure to launch process, targ=") + + curr._payload + " with parms " + curr._parms + + a_sprintf(" error=%d", retval)); + } + } + + chdir(prev_dir.s()); // reset directory pointer, just in case. + + BASE_LOG(astring('-', 76)); + } + + } + +#ifdef __WIN32__ + whack_simplistic_window(f_window); +#endif + + return 0; +} + +//////////////////////////////////////////////////////////////////////////// + +HOOPLE_MAIN(unpacker_stub, ) + +#ifdef __BUILD_STATIC_APPLICATION__ + // static dependencies found by buildor_gen_deps.sh: + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif // __BUILD_STATIC_APPLICATION__ + diff --git a/core/applications/bundler/version.ini b/core/applications/bundler/version.ini new file mode 100644 index 00000000..18196156 --- /dev/null +++ b/core/applications/bundler/version.ini @@ -0,0 +1,6 @@ +[version] +description = Application Bundler +root = bundler +name = Bundler +extension = exe + diff --git a/core/applications/example_application/example_application.cpp b/core/applications/example_application/example_application.cpp new file mode 100644 index 00000000..767b4530 --- /dev/null +++ b/core/applications/example_application/example_application.cpp @@ -0,0 +1,130 @@ +////////////// +// Name : Simple Application Example +// Author : Chris Koeritz +////////////// +// Copyright (c) 2006-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +//! An example of a bare-bones hoople application. +/*! + This application provides a very simple example for how we create programs + based on the HOOPLE code. +*/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace loggers; +//using namespace processes; +using namespace structures; +using namespace unit_test; + +const int CHECKING_INTERVAL = 4 * SECOND_ms; + // this many milliseconds elapses between checks on shutdown conditions. + +#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + // define a macro that will send diagnostic output to the app's logger. + +////////////// + +class application_example : virtual public unit_base, virtual public application_shell +{ +public: + application_example(); + ~application_example(); + + DEFINE_CLASS_NAME("application_example"); + + bool already_running(); + //!< true if this program is already running. + + virtual void handle_timer(); + //!< called by timer events from anchor window. + + virtual void handle_startup(); + //!< begins our service's activity. + + virtual void handle_shutdown(); + //!< catches the graceful shutdown so our objects get closed normally. + + virtual int execute(); + //!< the root of the program's activity. + + void print_instructions(); + //!< describes the available command line options for the ULS. + +private: + singleton_application _app_lock; //!< our inter-application synchronizer. +}; + +////////////// + +application_example::application_example() +: application_shell(), + _app_lock(static_class_name()) +{} + +application_example::~application_example() +{} + +void application_example::handle_startup() +{ + FUNCDEF("handle_startup"); + LOG("starting up now."); +} + +void application_example::handle_shutdown() +{ + FUNCDEF("handle_shutdown"); + LOG("shutting down now."); +} + +void application_example::handle_timer() +{ + FUNCDEF("handle_timer"); + LOG("timer blip."); +} + +bool application_example::already_running() +{ return _app_lock.already_running(); } + +int application_example::execute() +{ + FUNCDEF("execute"); + command_line cmds(_global_argc, _global_argv); + +//hmmm: test for command line options that are supported. + + // make sure this app is not running already. + if (already_running()) { + return 0; + } + +//need anchor window online for this. +// anchor_window::launch(*this, GET_INSTANCE_HANDLE(), class_name(), CHECKING_INTERVAL); + + ASSERT_EQUAL(astring(class_name()), astring("application_example"), + "simple application name check"); + + return final_report(); +} + +////////////// + +HOOPLE_MAIN(application_example, ) + diff --git a/core/applications/example_application/makefile b/core/applications/example_application/makefile new file mode 100644 index 00000000..04094dbc --- /dev/null +++ b/core/applications/example_application/makefile @@ -0,0 +1,13 @@ +CONSOLE_MODE = t + +include cpp/variables.def + +PROJECT = example_application +TYPE = application +SOURCE = +TARGETS = example_application.exe +LOCAL_LIBS_USED = application unit_test loggers configuration processes filesystem textual \ + structures timely basis + +include cpp/rules.def + diff --git a/core/applications/makefile b/core/applications/makefile new file mode 100644 index 00000000..582277c0 --- /dev/null +++ b/core/applications/makefile @@ -0,0 +1,7 @@ +include variables.def + +PROJECT = core_applications +BUILD_BEFORE = bookmark_tools bundler example_application nechung utilities + +include rules.def + diff --git a/core/applications/nechung/cgi_nechung.cpp b/core/applications/nechung/cgi_nechung.cpp new file mode 100644 index 00000000..a7478245 --- /dev/null +++ b/core/applications/nechung/cgi_nechung.cpp @@ -0,0 +1,155 @@ +/*****************************************************************************\ +* * +* Name : CGI nechung * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1997-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! @file cgi_nechung.cpp Spits out a CGI appropriate chunk of text with a fortune in it. + +#include "nechung_oracle.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +//using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; + +#undef LOG +#define LOG(s) program_wide_logger::get().log(s, 0) + +////HOOPLE_STARTUP_CODE; +//hmmm: need this back when things are ready! + +#define DEFAULT_FORTUNE_FILE "fortunes.dat" + +int main(int argc, char *argv[]) +{ + SETUP_CONSOLE_LOGGER; + + astring name; + astring index; + if (argc > 1) { + // use the first command line argument. + name = argv[1]; + } else { + // if nothing on the command line, then use our defaults. + name = environment::get("NECHUNG"); + // first try the environment variable. + if (!name) name = DEFAULT_FORTUNE_FILE; + // next, use the hardwired default. + } + + if (name.length() < 5) { + LOG(astring("nechung:: file name is too short (") + name + ")."); + return 1; + } + filename index_file_name(name); + astring extension(index_file_name.extension()); + int end = index_file_name.raw().end(); +#ifdef DEBUG_NECHUNG + LOG(astring("fortune filename is ") + name); + LOG(astring("extension is ") + extension); +#endif + astring tmp = index_file_name; + tmp.zap( (end + 1) - extension.length(), end); + tmp += "idx"; + astring base_part = filename(tmp).basename(); + index_file_name = environment::get("TMP") + "/" + base_part; +#ifdef DEBUG_NECHUNG + LOG(astring("index file is ") + index_file_name); +#endif + index = index_file_name; + + nechung_oracle some_fortunes(name, index); + // send the header for html text. + printf("content-type: text/html\n\n"); + // send the preliminary gunk. + printf("\n"); +//old text color #33ccff + printf("\n"); + printf("\n"); + + astring to_show = some_fortunes.pick_random(); + int line_posn = 0; + for (int i = 0; i < to_show.length(); i++) { + if (to_show[i] == ' ') { + // spaces get translated to one non-breaking space. + printf(" "); + line_posn++; + } else if (to_show[i] == '\t') { + // tabs get translated to positioning at tab stops based on eight. + int to_add = 8 - line_posn % 8; + for (int j = 0; j < to_add; j++) printf(" "); + line_posn += to_add; + } else if (to_show[i] == '\r') + continue; + else if (to_show[i] == '\n') { + printf("
    %c", to_show[i]); + line_posn = 0; + } else { + printf("%c", to_show[i]); + line_posn++; + } + } + printf("\n"); + printf("
    \n"); + printf("
    \n"); + printf("\n"); + return 0; +} + +#ifdef __BUILD_STATIC_APPLICATION__ + // static dependencies found by buildor_gen_deps.sh: + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif // __BUILD_STATIC_APPLICATION__ + diff --git a/core/applications/nechung/example.txt b/core/applications/nechung/example.txt new file mode 100644 index 00000000..4a08ef93 --- /dev/null +++ b/core/applications/nechung/example.txt @@ -0,0 +1,76 @@ +/*****************************************************************************\ +* * +* Name : nechung database format sample * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Describes the database format. The "fields" can be any kind of text, * +* including special characters. The one reserved term is a line containing * +* a single tilde character ('~') followed by the appropriate line ending. * +* That marks the beginning of the next record. Nechung will automatically * +* index the file based on these separators whenever the database changes. * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ +~ +Om Muni Muni Maha Muni Yea Swaha +~ +Gun dro nga tang yul nge nga +Gewa ju jik dza nyon druk +Nye nyon nyi shu shen gyur shi +Sem chung nga jik di dak o. +~ +DEVIL +EVIL +GOOD +GOD +~ +Nam Myoho Renge Kyo +~ +Om Mani Padme Hum +~ +Om Shanthi Om +~ +Om A Ra Ba Tsa Na Di +~ +The man in whom Tao +Acts without impediment +Harms no other being +By his actions +Yet he does not know himself +To be "kind," to be "gentle" +~ +When an archer is shooting for nothing +He has all his skill. +If he shoots for a brass buckle +He is already nervous. +If he shoots for a prize of gold +He goes blind +Or sees two targets-- +He is out of his mind! + +His skill has not changed. But the prize +Divides him. He cares. +He thinks more of winning +Than of shooting-- +And the need to win +Drains him of power. +~ +By giving, resources; by ethics, bliss +~ +what you don't see is what you get when you don't look +~ +this bell's knelling is never quelled, + while service is rendered, +tin staccato splattered over cupric strands, + spraying crazed meaning to distant lands. +what is it? +~ +To conquer oneself is a greater task than conquering others diff --git a/core/applications/nechung/makefile b/core/applications/nechung/makefile new file mode 100644 index 00000000..484bf88e --- /dev/null +++ b/core/applications/nechung/makefile @@ -0,0 +1,13 @@ +CONSOLE_MODE = t + +include cpp/variables.def + +PROJECT = nechung +TYPE = application +SOURCE = nechung_oracle.cpp nechung_version.rc +DEFINITIONS += __BUILD_STATIC_APPLICATION__ +UNDEFINITIONS += ENABLE_MEMORY_HOOK ENABLE_CALLSTACK_TRACKING +TARGETS = nechung.exe cgi_nechung.exe + +include cpp/rules.def + diff --git a/core/applications/nechung/nechung.cpp b/core/applications/nechung/nechung.cpp new file mode 100644 index 00000000..5c93f7c6 --- /dev/null +++ b/core/applications/nechung/nechung.cpp @@ -0,0 +1,119 @@ +/*****************************************************************************\ +* * +* Name : nechung console application * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*****************************************************************************/ + +//! @file nechung.cpp The application base for the Nechung Oracle Program (NOP). + +#include "nechung_oracle.h" + +#include +#include +#include +#include +#include +#include + +//using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; + +///HOOPLE_STARTUP_CODE; +//hmmm: missing. + +#undef LOG +#define LOG(s) program_wide_logger::get().log((s), 0) +//hmmm: need ALWAYS_PRINT back in there! + +#define DEFAULT_FORTUNE_FILE "fortunes.dat" + +int main(int argc, char *argv[]) +{ + SETUP_CONSOLE_LOGGER; + + astring name; + astring index; + if (argc > 1) { + // use the first command line argument. + name = argv[1]; + } else { + // if nothing on the command line, then use our defaults. + name = environment::get("NECHUNG"); + // first try the environment variable. + if (!name) name = DEFAULT_FORTUNE_FILE; + // next, use the hardwired default. + } + + if (name.length() < 5) { + LOG(astring("nechung:: file name is too short (") + name + ")."); + return 1; + } + filename index_file_name(name); + astring extension(index_file_name.extension()); + int end = index_file_name.raw().end(); +#ifdef DEBUG_NECHUNG + LOG(astring("fortune filename is ") + name); + LOG(astring("extension is ") + extension); +#endif + astring tmp = index_file_name; + tmp.zap( (end + 1) - extension.length(), end); + tmp += "idx"; + astring base_part = filename(tmp).basename(); + index_file_name = environment::get("TMP") + "/" + base_part; +#ifdef DEBUG_NECHUNG + LOG(astring("index file is ") + index_file_name); +#endif + index = index_file_name; + + nechung_oracle some_fortunes(name, index); + some_fortunes.display_random(); + return 0; +} + +#ifdef __BUILD_STATIC_APPLICATION__ + // static dependencies found by buildor_gen_deps.sh: + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif // __BUILD_STATIC_APPLICATION__ + diff --git a/core/applications/nechung/nechung_oracle.cpp b/core/applications/nechung/nechung_oracle.cpp new file mode 100644 index 00000000..fe34e1a9 --- /dev/null +++ b/core/applications/nechung/nechung_oracle.cpp @@ -0,0 +1,259 @@ +/*****************************************************************************\ +* * +* Name : nechung_oracle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*****************************************************************************/ + +#include "nechung_oracle.h" + +#include +#include +#include +#include +#include + +#include +#include + +//using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; + +#undef LOG +#define LOG(s) program_wide_logger::get().log(s, 0) +///hmmm: fix filter value to be ALWAYS_PRINT! + +const int MAX_LINE_LENGTH = 2048; + +nechung_oracle::nechung_oracle(const astring &nechung_filename, + const astring &index_filename) +: c_randomizer(), + c_filename_held(nechung_filename), + c_index_held(index_filename), + c_number_of_fortunes(0) +{ parse_file(); } + +nechung_oracle::~nechung_oracle() {} + +void nechung_oracle::parse_file() +{ + FUNCDEF("parse_file"); + // below is code for comparing dates on the fortune file and the index file. + byte_filer fortune_file(c_filename_held.s(), "rb"); +#ifdef DEBUG_NECHUNG + LOG(astring("filename=") + c_filename_held + " idx file=" + c_index_held); +#endif + if (!fortune_file.good()) + non_continuable_error(class_name(), func, "Cannot open fortune file."); + + byte_array buffer(MAX_LINE_LENGTH + 1); + // used throughout parsing for line storage. + + byte_filer index_file(c_index_held.observe(), "r"); + if (index_file.good()) { +#ifdef DEBUG_NECHUNG + LOG("index file exists"); +#endif + file_time index_time((FILE *)index_file.file_handle()); + file_time fortune_time((FILE *)fortune_file.file_handle()); + if (index_time >= fortune_time) { + // need to read in the list of indices + index_file.getline(buffer, MAX_LINE_LENGTH); + sscanf((char *)buffer.access(), "%d", &c_number_of_fortunes); +#ifdef DEBUG_NECHUNG + LOG(astring(astring::SPRINTF, "%d entries in index", + c_number_of_fortunes)); +#endif + return; + } + } + index_file.close(); + + // below is code for creating the list. + enum fortune_states { + chowing_separators, // looking for the breaks between fortunes. + adding_fortunes, // saw the separator so get ready for a new fortune. + chowing_fortunes, // currently in a fortune accumulating lines. + done_parsing // finished parsing the fortune file. + }; + + c_number_of_fortunes = 0; + fortune_states state = chowing_separators; + + int posn; + int_array fortune_posns; // our list of fortunes. + while (state != done_parsing) { +#ifdef DEBUG_NECHUNG + LOG(astring(astring::SPRINTF, "#%d", c_number_of_fortunes)); +#endif + if (fortune_file.eof()) { + // exit from the loop now... + state = done_parsing; + continue; + } + switch (state) { + case chowing_separators: { +#ifdef DEBUG_NECHUNG + LOG("chowseps, "); +#endif + posn = int(fortune_file.tell()); + if (posn < 0) + non_continuable_error(class_name(), func, "Cannot get file position."); + fortune_file.getline(buffer, MAX_LINE_LENGTH); +#ifdef DEBUG_NECHUNG + LOG(astring("got a line: ") + buffer); +#endif + if (buffer[0] != NECHUNG_SEPARATION_CHARACTER) state = adding_fortunes; + else { + // special casing is for when we see a separator on the line + // by itself versus when it is the beginning of a line. if the + // beginning of a line, we currently take that to mean the rest + // of the line is the fortune. + if (strlen((char *)buffer.access()) == 2) posn += 2; + else posn++; + state = adding_fortunes; + } + break; + } + case adding_fortunes: { +#ifdef DEBUG_NECHUNG + LOG("add forts, "); +#endif + fortune_posns += posn; + c_number_of_fortunes++; + state = chowing_fortunes; + break; + } + case chowing_fortunes: { +#ifdef DEBUG_NECHUNG + LOG("chow forts, "); +#endif + posn = int(fortune_file.tell()); + if (posn < 0) + non_continuable_error(class_name(), func, "Cannot get file size."); + fortune_file.getline(buffer, MAX_LINE_LENGTH); +#ifdef DEBUG_NECHUNG + LOG(astring(astring::SPRINTF, "got a line: %s", buffer.access())); + LOG(astring(astring::SPRINTF, "len is %d", strlen((char *)buffer.access()))); +#endif + if ( (buffer[0] == NECHUNG_SEPARATION_CHARACTER) + && (strlen((char *)buffer.access()) == 2) ) + state = chowing_separators; + else if (buffer[0] == NECHUNG_SEPARATION_CHARACTER) { + posn++; + state = adding_fortunes; + } + break; + } + case done_parsing: { + non_continuable_error(class_name(), func, "Illegal state reached."); + } + } + } + fortune_file.close(); + + // make a new index file. + index_file.open(c_index_held.observe(), "w"); + if (!index_file.good()) + non_continuable_error(class_name(), func, astring("Cannot open index file: ") + c_index_held); + astring to_write(astring::SPRINTF, "%d\n", c_number_of_fortunes); + index_file.write((abyte *)to_write.s(), to_write.length()); + for (int j = 0; j < c_number_of_fortunes; j++) { + to_write.sprintf("%d\n", fortune_posns[j]); + index_file.write((abyte *)to_write.s(), to_write.length()); + } + index_file.close(); +} + +astring nechung_oracle::pick_random() +{ + FUNCDEF("pick_random"); +#ifdef DEBUG_NECHUNG + LOG(astring("got to ") + func); +#endif + + byte_filer fortune_file(c_filename_held.s(), "rb"); + +///printf("num forts = %d\n", c_number_of_fortunes ); + + if (!fortune_file.good()) + non_continuable_error(class_name(), func, "Cannot open data file."); + int to_display = c_randomizer.inclusive(0, c_number_of_fortunes - 1); + +///printf("rand chose= %d\n", to_display); + +///// +///hmmm: this bit could be more efficient by just jumping to the Nth line +/// instead of reading through up to the Nth line. +///// + byte_filer index_file(c_index_held.observe(), "r"); + int chosen_posn = 0; // which position to read the chosen line at. + if (index_file.good()) { + astring accumulated_text; + byte_array buffer(MAX_LINE_LENGTH + 1); + for (int i = 0; i <= to_display; i++) { +#ifdef DEBUG_NECHUNG + accumulated_text += astring(astring::SPRINTF, "#%d: ", i); +#endif + index_file.getline(buffer, MAX_LINE_LENGTH); + sscanf((char *)buffer.access(), "%d", &chosen_posn); +#ifdef DEBUG_NECHUNG + accumulated_text += astring(astring::SPRINTF, "%d, ", chosen_posn); + if ((i + 1) % 5 == 0) accumulated_text += "\n"; +#endif + } +#ifdef DEBUG_NECHUNG + LOG(accumulated_text); +#endif + + } else { + non_continuable_error(class_name(), func, \ + astring("Could not open the index file \"") + c_index_held + "\""); + } + index_file.close(); +#ifdef DEBUG_NECHUNG + LOG(astring(astring::SPRINTF, "about to seek @ num %d and " + "index %d", to_display, chosen_posn)); +#endif + if (!fortune_file.seek(chosen_posn, byte_filer::FROM_START)) + non_continuable_error(class_name(), func, "Cannot seek to indexed position."); +#ifdef DEBUG_NECHUNG + LOG("after seek"); +#endif + + astring to_return; + byte_array temp(MAX_LINE_LENGTH + 1); + while (!fortune_file.eof()) { + int chars_read = fortune_file.getline(temp, MAX_LINE_LENGTH); + if (!chars_read) { + if (!fortune_file.eof()) { + non_continuable_error(class_name(), func, "Error while reading fortune."); + } else break; + } + if (temp[0] == NECHUNG_SEPARATION_CHARACTER) break; + else to_return += astring((char *)temp.access()); + } + return to_return; +} + +//hmmm: stolen from parser bits. reconnect when available. +bool is_eol(char to_check) +{ return (to_check == '\n') || (to_check == '\r'); } + +void nechung_oracle::display_random() +{ + astring to_show = pick_random(); + while (is_eol(to_show[to_show.end()])) + to_show.zap(to_show.end(), to_show.end()); + LOG(to_show); +} + diff --git a/core/applications/nechung/nechung_oracle.h b/core/applications/nechung/nechung_oracle.h new file mode 100644 index 00000000..31381b22 --- /dev/null +++ b/core/applications/nechung/nechung_oracle.h @@ -0,0 +1,87 @@ +#ifndef NECHUNG_CLASS +#define NECHUNG_CLASS + +/*****************************************************************************\ +* * +* Name : nechung_oracle * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* This is the root nechung functionality. It provides a means of * +* randomly selecting an item out of a specially formatted file. If no index * +* file has previously been built for the file, then one is created. The * +* index file makes choosing a fortune randomly very quick; only a seek on * +* the much smaller index is needed in order to find the position of the * +* fortune to be shown. * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +// Nechung works with a particular form of data file and will extract a random +// and hopefully auspicious message out of that file. An example of the file +// format is: +// msg1 +// ~ +// msg2 +// ~ +// (... more messages and tildes...) +// The tilde is the separation character mentioned below. + +#include +#include + +//#define DEBUG_NECHUNG + // uncomment this to get the debugging version. it is used by several files + // that are part of nechung. + +const char NECHUNG_SEPARATION_CHARACTER = '~'; + // this character separates the entries in the fortunes database. + +class nechung_oracle +{ +public: + nechung_oracle(const basis::astring &data_filename, const basis::astring &index_filename); + // the constructor needs the name of a nechung format data file in + // "data_filename" and the name of the index file to be used for that data + // file in "index_filename". + + virtual ~nechung_oracle(); + + DEFINE_CLASS_NAME("nechung_oracle"); + + basis::astring pick_random(); + // returns a randomly chosen fortune. + + void display_random(); + // selects an oracular pronouncement from the file and then shows it on + // standard output. + +private: + mathematics::chaos c_randomizer; // the random number generator we use. + basis::astring c_filename_held; // the data file's name. + basis::astring c_index_held; // the index file's name. + int c_number_of_fortunes; // how many fortunes exist in the file. + + void parse_file(); + // given the data file and index file, this will ensure that the index + // file is made up to date. it creates, if necessary, the file that + // contains the positions of fortunes in the data file. this is what + // we'll use to find the start of each fortune. if the file already + // exists, then it will just retrieve the number of fortunes from the index + // file. after this method, the pick_random() and display_random() methods + // are available. + + // disallowed. + nechung_oracle(const nechung_oracle &); + nechung_oracle &operator =(const nechung_oracle &); +}; + +#endif + diff --git a/core/applications/nechung/nechung_version.rc b/core/applications/nechung/nechung_version.rc new file mode 100644 index 00000000..936fc60a --- /dev/null +++ b/core/applications/nechung/nechung_version.rc @@ -0,0 +1,46 @@ +#ifndef NO_VERSION +#include +#include <__build_version.h> +#include <__build_configuration.h> +#define BI_PLAT_WIN32 + // force 32 bit compile. +1 VERSIONINFO LOADONCALL MOVEABLE +FILEVERSION __build_FILE_VERSION_COMMAS +PRODUCTVERSION __build_PRODUCT_VERSION_COMMAS +FILEFLAGSMASK 0 +FILEFLAGS VS_FFI_FILEFLAGSMASK +#if defined(BI_PLAT_WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + // Language type = U.S. English(0x0409) and Character Set = Windows, Multilingual(0x04b0) + BLOCK "040904b0" // Matches VarFileInfo Translation hex value. + BEGIN + VALUE "CompanyName", __build_company "\000" +#ifndef _DEBUG + VALUE "FileDescription", "Nechung Oracle Program\000" +#else + VALUE "FileDescription", "Nechung Oracle Program (DEBUG)\000" +#endif + VALUE "FileVersion", __build_FILE_VERSION "\000" + VALUE "ProductVersion", __build_PRODUCT_VERSION "\000" + VALUE "InternalName", "Nechung\000" + VALUE "LegalCopyright", __build_copyright "\000" + VALUE "LegalTrademarks", __build_legal_info "\000" + VALUE "OriginalFilename", "nechung.exe\000" + VALUE "ProductName", __build_product_name "\000" + + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04b0 // US English (0x0409) and win32 multilingual (0x04b0) + END +END +#endif diff --git a/core/applications/nechung/readme b/core/applications/nechung/readme new file mode 100644 index 00000000..ed2e1822 --- /dev/null +++ b/core/applications/nechung/readme @@ -0,0 +1,7 @@ + +The Nechung oracle is a person manifesting the spirit of Dorje Drakden who is +the protector divinity of the Dalai Lama. + +This program cannot attempt to fulfil the same role as the Nechung oracle, but +hopefully it will give you good advice relating to your needs and your life. + diff --git a/core/applications/nechung/test_nechung.sh b/core/applications/nechung/test_nechung.sh new file mode 100644 index 00000000..3acabd95 --- /dev/null +++ b/core/applications/nechung/test_nechung.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# prints out the time it takes to run the nechung oracle a thousand times. +# the number of seconds in the result should be equivalent to the number of +# milliseconds that nechung takes to run and produce a fortune, on average. + +export i=0 + +mdate +while [ $i -le 1000 ]; do +# echo $i + nechung >/dev/null + let i++ +done +mdate diff --git a/core/applications/nechung/version.ini b/core/applications/nechung/version.ini new file mode 100644 index 00000000..7b4eb044 --- /dev/null +++ b/core/applications/nechung/version.ini @@ -0,0 +1,6 @@ +[version] +description=Nechung Oracle Program +root=nechung +name=Nechung +extension=exe + diff --git a/core/applications/utilities/await_app_exit.cpp b/core/applications/utilities/await_app_exit.cpp new file mode 100644 index 00000000..ec604ae4 --- /dev/null +++ b/core/applications/utilities/await_app_exit.cpp @@ -0,0 +1,157 @@ +/* +* Name : await_app_exit +* Author : Chris Koeritz +* Purpose: * +* This program waits for a particular application to exit before this app * +* itself exits. This allows a pause while another possibly slow process is * +* leaving. * +* Copyright (c) 2003-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace loggers; +using namespace filesystem; +using namespace processes; +using namespace structures; +using namespace textual; +using namespace timely; + +#undef BASE_LOG +#define BASE_LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), astring(to_print)) +#undef LOG +#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), astring(to_print)) + +class await_app_exit : public application_shell +{ +public: + await_app_exit() : application_shell() {} + DEFINE_CLASS_NAME("await_app_exit"); + int execute(); +}; + +int await_app_exit::execute() +{ + FUNCDEF("execute"); + SETUP_COMBO_LOGGER; + if (_global_argc < 3) { + BASE_LOG("This program needs two parameters on the command line. The first is an"); + BASE_LOG("application name (e.g. 'blofeld.exe' is a valid example--no path should be"); + BASE_LOG("included but the .exe suffix must be included) to seek out in the process"); + BASE_LOG("list and the second parameter is the time to wait for it to exit (in seconds)."); + BASE_LOG("This program will not exit until the specified application is no longer"); + BASE_LOG("running or the timeout elapses. If the timeout elapses, then a failure exit"); + BASE_LOG("will occur from this program so that it is known that the target application"); + BASE_LOG("never exited."); + return 2; + } + + astring app_name = _global_argv[1]; // get the app's name. + astring duration = _global_argv[2]; // get the time to wait. + int timeout = duration.convert(0) * 1000; + if (timeout < 0) { + LOG(astring("The timeout specified is invalid: ") + duration); + return 3; + } + + // now see if that app is even running. + process_control querier; + process_entry_array processes; + querier.query_processes(processes); + int_set pids; + time_stamp when_to_leave(timeout); // when we should stop checking. + + // wait for the app to go away. + while (querier.find_process_in_list(processes, app_name, pids)) { + // the program of interest is still running. + time_control::sleep_ms(100); + querier.query_processes(processes); + if (time_stamp() > when_to_leave) { + LOG(astring("The timeout elapsed and ") + app_name + " is still running."); + return 4; + } + } + LOG(astring("The ") + app_name + " process has exited."); + return 0; +} + +HOOPLE_MAIN(await_app_exit, ) + +#ifdef __BUILD_STATIC_APPLICATION__ + // static dependencies found by buildor_gen_deps.sh: + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif // __BUILD_STATIC_APPLICATION__ + diff --git a/core/applications/utilities/bytedump.cpp b/core/applications/utilities/bytedump.cpp new file mode 100644 index 00000000..a30d16d1 --- /dev/null +++ b/core/applications/utilities/bytedump.cpp @@ -0,0 +1,108 @@ +/*****************************************************************************\ +* * +* Name : dump_bytes * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Prints the files specified out in terms of their hex bytes. * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace loggers; +using namespace filesystem; +using namespace structures; +using namespace textual; + +///HOOPLE_STARTUP_CODE; + +const int MAXIMUM_BYTEDUMP_BUFFER_SIZE = 32768; + // this is the size of the chunk we read from the files at a time. it is + // important to make this a multiple of 16, since that's the size of the line + // we use in the byte dumping. + +#define console program_wide_logger::get() + +int print_instructions_and_exit(char *program_name) +{ + console.log(astring(astring::SPRINTF, "\n\ +Usage:\n\t%s filename [filename]\n\n\ +Prints out (on standard output) a abyte dump of the files specified.\n\n", + program_name), ALWAYS_PRINT); + return 12; +} + +int main(int argc, char *argv[]) +{ + SETUP_CONSOLE_LOGGER; + + if (argc <= 1) return print_instructions_and_exit(argv[0]); + else { + int current_parameter = 0; +/* + if (argv[1][0] == '-') { + if (argv[1][1] == 't') { + current_parameter++; + open_file_as_text = true; + } else if (argv[1][1] == 'b') { + current_parameter++; + open_file_as_text = false; + } else print_instructions_and_exit(argv[0]); + } +*/ + bool past_first_file = false; + while (++current_parameter < argc) { + if (past_first_file) { + // we're into the second file so start using some white space. + console.log(astring::empty_string(), ALWAYS_PRINT); + console.log(astring::empty_string(), ALWAYS_PRINT); + } + past_first_file = true; // set condition for next time. + astring name = argv[current_parameter]; + byte_filer current(name, "rb"); + if (!current.good()) { + console.log(astring("Cannot find the file named \"") + name + astring("\"."), + ALWAYS_PRINT); + continue; + } + abyte buff[MAXIMUM_BYTEDUMP_BUFFER_SIZE + 10]; + // buffer plus some extra room. + bool printed_header = false; + int current_label = 0; + while (true) { + int bytes_read = current.read(buff, MAXIMUM_BYTEDUMP_BUFFER_SIZE); + if (bytes_read <= 0) break; // no contents. +//console.log(astring(astring::SPRINTF, "read %d bytes", bytes_read)); + if (!printed_header) { + console.log(name + ":", ALWAYS_PRINT); + console.log(astring::empty_string(), ALWAYS_PRINT); // blank line. + printed_header = true; + } + astring to_log = byte_formatter::text_dump(buff, bytes_read, + current_label); + if (to_log[to_log.end()] == '\n') + to_log.zap(to_log.end(), to_log.end()); + console.log(to_log, ALWAYS_PRINT); + current_label += bytes_read; + } + } + } + return 0; +} + diff --git a/core/applications/utilities/checker.cpp b/core/applications/utilities/checker.cpp new file mode 100644 index 00000000..c56cec3f --- /dev/null +++ b/core/applications/utilities/checker.cpp @@ -0,0 +1,179 @@ +/*****************************************************************************\ +* * +* Name : checker * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Generates checksums for a set of files. * +* * +******************************************************************************* +* Copyright (c) 1990-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace basis; +using namespace structures; +using namespace timely; + +const int buffer_size = 4096; + +//HOOPLE_STARTUP_CODE; + +//#define DEBUG_CHECKER + // uncomment for noisy version. + +void print_instructions_and_exit(char *program_name) +{ + printf("\n\ +Usage:\n\t%s [-t] filename [filename]\n\n\ +This program generates a checksum for each file that is entered on the\n\ +command line. The checksum is (hopefully) an architecture independent\n\ +number that is a very compressed representation of the file gestalt.\n\ +If one compares two copies of a file, then the checksums should be identical.\n\ +This is a useful test of whether a file copy or a program download is\n\ +successful in making an identical version of the file. In particular, if the\n\ +file is made slightly bigger or smaller, or if an item in the file is changed,\n\ +then the checksums of the two versions should be different numbers.\n\n\ +The -b flag is used if the files are to be compared as binary files, and this\n\ +is also the default. The -t flag is used if the files are to be compared as\n\ +text files.\n", + program_name); + exit(1); +} + +#define HIGHEST_CHECK 32714 + +// do_checksum: takes the specified file name and generates a checksum for it. +// if the file is inaccessible or, at any point, reading it returns an +// error message, then a negative value is returned. +int do_checksum(char *file_name, int open_as_a_text_file) +{ + char file_open_mode[10]; + if (open_as_a_text_file) strcpy(file_open_mode, "rt"); + else strcpy(file_open_mode, "rb"); + FILE *opened_file = fopen(file_name, file_open_mode); +#ifdef DEBUG_CHECKER + LOG(astring("opened ") + file_name); +#endif + if (!opened_file) return common::NOT_FOUND; + int characters_read = 0; + int current_checksum_value = 0; + char buffer_chunk[buffer_size]; + while (!feof(opened_file)) { + characters_read = int(fread(buffer_chunk, sizeof(char), buffer_size, + opened_file)); + // if result is 0 or negative, stop messing with the file. +#ifdef DEBUG_CHECKER + LOG(a_sprintf("char read = %d", characters_read)); +#endif + if (characters_read <= 0) { + if (characters_read < 0) current_checksum_value = -1; + else if (current_checksum_value == 0) current_checksum_value = -1; + break; + } + current_checksum_value = (current_checksum_value + + checksums::bizarre_checksum((abyte *)buffer_chunk, characters_read)) + % HIGHEST_CHECK; +#ifdef DEBUG_CHECKER + LOG(a_sprintf("current checksum=%d", current_checksum_value)); +#endif + } + fclose(opened_file); + return int(current_checksum_value); +} + +// do_fletcher_checksum: takes the specified file name and generates a fletcher +// checksum for it. if the file is inaccessible or, at any point, +// reading it returns an error message, then a negative value is returned. +int do_fletcher_checksum(char *file_name, int open_as_a_text_file) +{ + char file_open_mode[10]; + if (open_as_a_text_file) strcpy(file_open_mode, "rt"); + else strcpy(file_open_mode, "rb"); + FILE *opened_file = fopen(file_name, file_open_mode); +#ifdef DEBUG_CHECKER + LOG(astring("opened ") + file_name); +#endif + if (!opened_file) return common::NOT_FOUND; + int characters_read = 0; + int current_checksum_value = 0; + char buffer_chunk[buffer_size]; + while (!feof(opened_file)) { + characters_read = int(fread(buffer_chunk, sizeof(char), buffer_size, + opened_file)); + // if result is 0 or negative, stop messing with the file. +#ifdef DEBUG_CHECKER + LOG(a_sprintf("char read = %d", characters_read)); +#endif + if (characters_read <= 0) { + if (characters_read < 0) current_checksum_value = -1; + else if (current_checksum_value == 0) current_checksum_value = -1; + break; + } + current_checksum_value = checksums::rolling_fletcher_checksum + ((uint16)current_checksum_value, (abyte *)buffer_chunk, + characters_read); +#ifdef DEBUG_CHECKER + LOG(a_sprintf("current checksum=%d", current_checksum_value)); +#endif + } + fclose(opened_file); + return current_checksum_value; +} + +int main(int argc, char *argv[]) +{ + char name[200]; + + // if the file is to be read as a text file, then this is true. + int open_file_as_text = false; + + if (argc <= 1) print_instructions_and_exit(argv[0]); + else { + int current_parameter = 0; + if (argv[1][0] == '-') { + if (argv[1][1] == 't') { + current_parameter++; + open_file_as_text = true; + } else if (argv[1][1] == 'b') { + current_parameter++; + open_file_as_text = false; + } else print_instructions_and_exit(argv[0]); + } + bool printed_header = false; + while (++current_parameter < argc) { + if (!printed_header) { + printed_header = true; + printf("%s\n", (astring("[ checker running at ") + time_stamp::notarize(true) + "]").s()); + printf("bizarro fletcher filename\n"); + printf("======= ======== ========\n"); + } + strcpy(name, argv[current_parameter]); + int checksum_of_file = do_checksum(name, open_file_as_text); + int fletcher_chksum = do_fletcher_checksum(name, open_file_as_text); + if (checksum_of_file >= 0) { + printf("%s", a_sprintf(" %05d 0x%04x %s\n", checksum_of_file, + fletcher_chksum, name).s()); + } else { + printf("%s", a_sprintf("%s is inaccessible.\n", name).s()); + } + } + } + return 0; +} + diff --git a/core/applications/utilities/ini_edit.cpp b/core/applications/utilities/ini_edit.cpp new file mode 100644 index 00000000..a56bf6a9 --- /dev/null +++ b/core/applications/utilities/ini_edit.cpp @@ -0,0 +1,126 @@ +/*****************************************************************************\ +* * +* Name : ini_edit * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Provides command line ini editing capabilities. These include both * +* reading of ini files and writing new entries to them. * +* * +******************************************************************************* +* Copyright (c) 2006-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace application; +using namespace basis; +using namespace configuration; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; + +#undef LOG +#define LOG(to_print) program_wide_logger::get().log(to_print, ALWAYS_PRINT) + +class ini_editor : public application_shell +{ +public: + ini_editor() + : application_shell(), + _app_name(filename(application::_global_argv[0]).basename()) {} + DEFINE_CLASS_NAME("ini_editor"); + virtual int execute(); + int print_instructions(); + +private: + astring _app_name; +}; + +int ini_editor::print_instructions() +{ + LOG(a_sprintf("\ +%s: This program needs five parameters to process an ini file.\n\ +There are two major operations, read and write. The type of operation\n\ +should be the first parameter. The other parameters are similar for both\n\ +operations, except for the last parameter. These are as follows:\n\ +Reading:\n\ +\tread inifile section entry defaultvalue\n\ + This reads the \"inifile\" specified and looks for the \"section\" and\n\ +\"entry\" name in the file. It will either return (via standard output)\n\ +the value found there or it will return the \"defaultvalue\". No error\n\ +will be raised if the entry is missing, but the default signals that no\n\ +value was defined.\n\ + Additionally, if the entry name is the special value \"whole_section\",\n\ +then the entire section will be read and returned as a CSV list. If the\n\ +section is empty, then the default string is returned instead.\n\ +Writing:\n\ +\twrite inifile section entry newvalue\n\ + This writes a new item with contents \"newvalue\" into the \"inifile\"\n\ +in the \"section\" at the \"entry\" specified. This should always succeed\n\ +unless the ini file is not writable (in which case an error should be\n\ +returned). Nothing is send to standard output for a write operation.\n\ +", _app_name.s())); + return 23; +} + +int ini_editor::execute() +{ + SETUP_CONSOLE_LOGGER; + + if (application::_global_argc < 6) return print_instructions(); + + astring operation = application::_global_argv[1]; + bool read_op = true; + if ( (operation[0] == 'w') || (operation[0] == 'W') ) read_op = false; + astring ini_file = application::_global_argv[2]; + astring section = application::_global_argv[3]; + astring entry = application::_global_argv[4]; + astring value = application::_global_argv[5]; + ini_configurator ini(ini_file, ini_configurator::RETURN_ONLY); + if (read_op) { + // read the entry from the ini file. + astring found; + if (entry.equal_to("whole_section")) { + // they want the whole section back at them. + string_table found; + bool worked = ini.get_section(section, found); + if (!worked) program_wide_logger::get().log(value, ALWAYS_PRINT); // default. + else { + // generate answer as csv. + astring temp; + list_parsing::create_csv_line(found, temp); + program_wide_logger::get().log(temp, ALWAYS_PRINT); // real value read. + } + } else { + bool worked = ini.get(section, entry, found); +/// program_wide_logger::get().eol(log_base::NO_ENDING); + if (!worked) program_wide_logger::get().log(value, ALWAYS_PRINT); // default. + else program_wide_logger::get().log(found, ALWAYS_PRINT); // real value read. + } + } else { + // write the entry to the ini file. + bool worked = ini.put(section, entry, value); + if (!worked) exit(28); // failure to write the entry. + } + + return 0; +} + +HOOPLE_MAIN(ini_editor, ) + diff --git a/core/applications/utilities/makefile b/core/applications/utilities/makefile new file mode 100644 index 00000000..2ba9d0eb --- /dev/null +++ b/core/applications/utilities/makefile @@ -0,0 +1,19 @@ +CONSOLE_MODE = true + +include cpp/variables.def + +PROJECT = utilities +TYPE = application +ifeq "$(OMIT_VERSIONS)" "" + SOURCE += util_version.rc +endif +LOCAL_LIBS_USED = application loggers mathematics processes textual timely configuration filesystem structures basis +TARGETS = await_app_exit.exe bytedump.exe checker.exe ini_edit.exe mdate.exe splitter.exe +#check_address.exe check_eol.exe create_guid.exe cstrip.exe \ + empty.exe find_non_ascii.exe find_text.exe insert_before.exe \ + lexcaser.exe machine_name.exe merge_inis.exe net_calc.exe pi.exe \ + replace_text.exe show_args.exe show_versions.exe to_lower.exe uucat.exe +#VCPP_USE_SOCK=t + +include cpp/rules.def + diff --git a/core/applications/utilities/mdate.cpp b/core/applications/utilities/mdate.cpp new file mode 100644 index 00000000..ffb1a662 --- /dev/null +++ b/core/applications/utilities/mdate.cpp @@ -0,0 +1,45 @@ +/*****************************************************************************\ +* * +* Name : mdate * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Provides a more informative date command, providing milliseconds also. * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include + +#include + +using namespace application; +using namespace basis; +using namespace loggers; +using namespace structures; +using namespace timely; + +class mdate_app : public application_shell +{ +public: + virtual int execute() { + SETUP_CONSOLE_LOGGER; + program_wide_logger::get().log(time_stamp::notarize(false), ALWAYS_PRINT); + return 0; + } + DEFINE_CLASS_NAME("mdate_app"); +}; + +HOOPLE_MAIN(mdate_app, ) + diff --git a/core/applications/utilities/splitter.cpp b/core/applications/utilities/splitter.cpp new file mode 100644 index 00000000..0de8347a --- /dev/null +++ b/core/applications/utilities/splitter.cpp @@ -0,0 +1,141 @@ +/*****************************************************************************\ +* * +* Name : splitter * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Takes text as input and splits the lines so that they will fit on a * +* standard 80 column terminal. * +* * +******************************************************************************* +* Copyright (c) 1993-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; + +const int MAX_BUFFER = 1024; + +class splitter_app : public application_shell +{ +public: + splitter_app() : application_shell() {} + + DEFINE_CLASS_NAME("splitter_app"); + + virtual int execute(); + + int print_instructions(); + +private: +}; + +////////////// + +int splitter_app::print_instructions() +{ + astring name = filename(_global_argv[0]).basename().raw(); + log(a_sprintf("%s usage:", name.s())); + log(astring::empty_string()); + log(a_sprintf("\ +This program splits long lines in input files into a more reasonable size.\n\ +Any filenames on the command line are split and sent to standard output.\n\ +The following options change how the splitting is performed:\n\ + --help or -?\tShow this help information.\n\ + --mincol N\tMinimum column to use for output.\n\ + --maxcol N\tMinimum column to use for output.\n\ +")); + return -3; +} + +int splitter_app::execute() +{ + command_line cmds(_global_argc, _global_argv); // parse the command line up. + + // retrieve any specific flags first. + astring temp; + int min_col = 0; + if (cmds.get_value("mincol", temp)) + min_col = temp.convert(min_col); + int max_col = 77; + if (cmds.get_value("maxcol", temp)) + max_col = temp.convert(max_col); + // look for help command. + int junk_index = 0; + if (cmds.find("help", junk_index, false) + || cmds.find('h', junk_index, false) + || cmds.find("?", junk_index, false) + || cmds.find('?', junk_index, false) ) { + print_instructions(); + return 0; + } + + // gather extra input files. + string_set input_files; + for (int i = 0; i < cmds.entries(); i++) { + const command_parameter &curr = cmds.get(i); + if (curr.type() == command_parameter::VALUE) { +//log(astring("adding input file:") + curr.text()); + input_files += curr.text(); + } + } + + astring accumulator; + for (int q = 0; q < input_files.length(); q++) { + byte_filer current(input_files[q], "r"); + if (!current.good()) continue; + while (!current.eof()) { + astring line_read; + int num_chars = current.getline(line_read, MAX_BUFFER); + if (!num_chars) continue; +//printf("line len=%d, cont=%s\n", line_read.length(), line_read.s()); + accumulator += line_read; +//// accumulator += '\n'; + } + } + + // now get from standard input if there weren't any files specified. + if (!input_files.length()) { + char input_line[MAX_BUFFER + 2]; + while (!feof(stdin)) { + char *got = fgets(input_line, MAX_BUFFER, stdin); + if (!got) break; +//printf("line=%s\n", got); + accumulator += got; +//// accumulator += '\n'; + } + } +//printf("splitting accum with %d chars...\n", accumulator.length()); + astring chewed; + string_manipulation::split_lines(accumulator, chewed, min_col, max_col); +//printf("chewed string now has %d chars...\n", chewed.length()); + printf("%s", chewed.s()); + return 0; +} + +////////////// + +HOOPLE_MAIN(splitter_app, ) + diff --git a/core/applications/utilities/splitter_test_1.txt b/core/applications/utilities/splitter_test_1.txt new file mode 100644 index 00000000..f1a009ef --- /dev/null +++ b/core/applications/utilities/splitter_test_1.txt @@ -0,0 +1,33 @@ + +FROOP Corporation + +Software Design Description (SDD) + +Document: Outcome and Diagnostic Standardization Proposal + +Project: Snoopter 5.1 + +Author: Chris Koeritz + +Created: 11/17/2004 + +Revision: 2 + +The Problem + + 1. The Snoopter product incorporates hundreds of C++ classes. Many of these define a set of outcomes that represent the exit status of a function invocation. This is a useful feature, rather than just returning a boolean result, but it has spread like wildfire and the set of outcomes today is very large. One part of the problem is that it is not clear to our customers what the outcomes even are, much less what they mean. + + 2. This impacts the logging of diagnostic information too. Sometimes the bare numerical values of outcomes are logged, whereas other times the outcome name is logged. While logging the names of outcomes is the preferred method, even then the set of names and what they mean is not necessarily clear. Another part of the problem is thus that diagnostic entries using outcomes are more difficult to decode than necessary. + + 3. Beyond the potential confusion that can arise from logging outcomes, some of our diagnostic entries are unclear or incomplete. The entries describe a problem, but sometimes they don't describe the object that the problem pertains to or the don't provide enough information to really understand what the problem is. Since each programmer writes their own log entries according to their own predilections, the diagnostic traces from Snoopter can be quirky and differ substantially from program to program. Some of this is unavoidable, but we need to provide better and more complete logging. + + 4. + +The Solution + + 1. One aspect of this project is to define a very low-level set of outcomes that cover most common function results. These will be used wherever possible as the outcomes returned by our C++ classes. There may still need to be a larger set of outcomes for certain classes, since their behavior may not make sense to describe at a very low-level. To support these extensions to the low-level set of outcomes, there needs to be a set of tools for adding new outcomes programmatically and in a way that is stable (i.e., the set does not change between releases unless intentionally modified) and verifiable (i.e., ensuring that no two outcomes share the same numeric value unless they are the same outcome). + + 2. It is important for the customer's sanity (and our own) that we have a way to produce a list of all outcomes in the program, their numerical values, and some description of what that outcome means. Part of the standardization project is to produce a tool capable of listing all of the Snoopter outcomes in just this way. + + 3. This project will create a set of guidelines for how to log the right amount of information when describing a situation in a log file. This will not be something that can be absolute and unvarying for everyone, but having a set of clear techniques for creating good log entries will be valuable. + diff --git a/core/applications/utilities/splitter_test_2.txt b/core/applications/utilities/splitter_test_2.txt new file mode 100644 index 00000000..a20779ce --- /dev/null +++ b/core/applications/utilities/splitter_test_2.txt @@ -0,0 +1,8 @@ + +this file used to be done wrong; the snow lion tags would be at the end of the line at the bottom +but they should have been auto-ejected to the next line. if it looks kind of right now, then the problem is gone. + + +Karma has four main characteristics. The first is its increasing effect: goodness heralds further goodness and evil heralds further evil. Secondly, karma is definite: in the long run, goodness always produces joy and negativity always produces suffering. Thirdly, one never experiences a joy or sorrow that does not have an according karmic cause. And lastly, the karmic seeds that are placed on the mind at the time of an action will never lose their potency even in a hundred million lifetimes, but will lie dormant within the mind until one day the conditions that activate them appear. + +-- His Holiness the Dalai Lama from The Path to Enlightenment, published by Snow Lion Publications diff --git a/core/applications/utilities/util_version.rc b/core/applications/utilities/util_version.rc new file mode 100644 index 00000000..73278de9 --- /dev/null +++ b/core/applications/utilities/util_version.rc @@ -0,0 +1,46 @@ +#ifndef NO_VERSION +#include +#include <__build_version.h> +#include <__build_configuration.h> +#define BI_PLAT_WIN32 + // force 32 bit compile. +1 VERSIONINFO LOADONCALL MOVEABLE +FILEVERSION __build_FILE_VERSION_COMMAS +PRODUCTVERSION __build_PRODUCT_VERSION_COMMAS +FILEFLAGSMASK 0 +FILEFLAGS VS_FFI_FILEFLAGSMASK +#if defined(BI_PLAT_WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + // Language type = U.S. English(0x0409) and Character Set = Windows, Multilingual(0x04b0) + BLOCK "040904b0" // Matches VarFileInfo Translation hex value. + BEGIN + VALUE "CompanyName", __build_company "\000" +#ifndef _DEBUG + VALUE "FileDescription", "Small Utility Programs\000" +#else + VALUE "FileDescription", "Small Utility Programs (DEBUG)\000" +#endif + VALUE "FileVersion", __build_FILE_VERSION "\000" + VALUE "ProductVersion", __build_PRODUCT_VERSION "\000" + VALUE "InternalName", "Utility App\000" + VALUE "LegalCopyright", __build_copyright "\000" + VALUE "LegalTrademarks", __build_legal_info "\000" + VALUE "OriginalFilename", "util.exe\000" + VALUE "ProductName", __build_product_name "\000" + + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04b0 // US English (0x0409) and win32 multilingual (0x04b0) + END +END +#endif diff --git a/core/applications/utilities/version.ini b/core/applications/utilities/version.ini new file mode 100644 index 00000000..f1a352ea --- /dev/null +++ b/core/applications/utilities/version.ini @@ -0,0 +1,6 @@ +[version] +description = Small Utility Programs +root = util +name = Utility App +extension = exe + diff --git a/core/library/__build_configuration.h b/core/library/__build_configuration.h new file mode 100644 index 00000000..fed2488b --- /dev/null +++ b/core/library/__build_configuration.h @@ -0,0 +1,54 @@ +#ifndef BUILD_SYSTEM_CONFIGURATION +#define BUILD_SYSTEM_CONFIGURATION + + // This file provides all of the code flags which were used when this build + // was generated. Some of the items in the build configuration have been + // stripped out because they are not used. + + #ifndef __build_company + #define __build_company "Feisty Meow Concerns" + #endif + #ifndef __build_copyright + #define __build_copyright "(C) 1987-2010 Chris Koeritz & Others" + #endif + #ifndef __build_legal_info + #define __build_legal_info "This software is free to copy under the terms of the GNU GPL (or the GNU Library License if you prefer). See http://www.gnu.org/licenses/gpl.html for more info. We suggest peaceful purposes that help all sentient beings." + #endif + #ifndef __build_product_name + #define __build_product_name "hoople" + #endif + #ifndef __build_web_site + #define __build_web_site "http://hoople.org" + #endif + #ifndef __build_LAX_DEPENDENCIES + #define __build_LAX_DEPENDENCIES "t" + #endif + #ifndef CATCH_ERRORS + #define CATCH_ERRORS "t" + #endif + #ifndef ERRORS_ARE_FATAL + #define ERRORS_ARE_FATAL "t" + #endif + +/* +These settings were not used: + [version] = + DEBUG = t + OPTIMIZE = t + ifneq "$(DEBUG)" "" = + OPTIMIZE = + endif = + ifeq "$(BOOT_STRAPPING)" "" = + endif = + ifeq "$(OP_SYSTEM)" "WIN32" = + else = + UNICODE = t + endif = + ifeq "$(BOOT_STRAPPING)" "" = + endif = + ifeq "$(BOOT_STRAPPING)" "" = + endif = +*/ + +#endif /* outer guard */ + diff --git a/core/library/__build_version.h b/core/library/__build_version.h new file mode 100644 index 00000000..bbd6b673 --- /dev/null +++ b/core/library/__build_version.h @@ -0,0 +1,29 @@ +#ifndef BUILD_VERSION_CONFIGURATION +#define BUILD_VERSION_CONFIGURATION + + // This file provides the version macros for this particular build. + + #ifndef __build_major + #define __build_major "2" + #endif + #ifndef __build_minor + #define __build_minor "108" + #endif + #ifndef __build_revision + #define __build_revision "86" + #endif + #ifndef __build_build + #define __build_build "0" + #endif + + // calculated macros are dropped in here. + + #define __build_SYSTEM_VERSION "2.108.86.0" + + #define __build_FILE_VERSION_COMMAS 2, 108, 86, 0 + #define __build_FILE_VERSION "2.108.86.0" + #define __build_PRODUCT_VERSION_COMMAS 2, 108, 0, 0 + #define __build_PRODUCT_VERSION "2.108" + +#endif /* outer guard */ + diff --git a/core/library/algorithms/makefile b/core/library/algorithms/makefile new file mode 100644 index 00000000..8cf4b298 --- /dev/null +++ b/core/library/algorithms/makefile @@ -0,0 +1,9 @@ +include cpp/variables.def + +PROJECT = algorithms +TYPE = library +SOURCE = placeholder.cpp +TARGETS = algorithms.lib + +include cpp/rules.def + diff --git a/core/library/algorithms/placeholder.cpp b/core/library/algorithms/placeholder.cpp new file mode 100644 index 00000000..4878eca4 --- /dev/null +++ b/core/library/algorithms/placeholder.cpp @@ -0,0 +1,4 @@ + + +// sole purpose of this is to make the lib be generated, +// even though we currently do not have any code that lives inside it. diff --git a/core/library/algorithms/shell_sort.h b/core/library/algorithms/shell_sort.h new file mode 100644 index 00000000..7e9b2539 --- /dev/null +++ b/core/library/algorithms/shell_sort.h @@ -0,0 +1,65 @@ +#ifndef SHELL_SORT_CLASS +#define SHELL_SORT_CLASS + +////////////// +// Name : shell_sort +// Author : Chris Koeritz +////////////// +// Copyright (c) 1991-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +namespace algorithms { + +//! Orders an array in O(n log n) time. +/*! + This algorithm is based on Kernighan and Ritchie's "The C Programming Language". +*/ + +template +void shell_sort(type v[], int n, bool reverse = false) + //!< this function sorts a C array of the "type" with "n" elements. + /*!< the "type" of object must support comparison operators. if "reverse" + is true, then the list is sorted highest to lowest. */ +{ + type temp; + int gap, i, j; + // the gap sizes decrease quadratically(?). they partition the array of + // items that need to be sorted into first two groups, then four, then + // eight, .... + // within each gap's worth of the array, the next loop takes effect... + for (gap = n / 2; gap > 0; gap /= 2) { + // the i indexed loop is the base for where the comparisons are made in + // the j indexed loop. it makes sure that each item past the edge of + // the gap sized partition gets considered. + for (i = gap; i < n; i++) { + // the j indexed loop looks at the values in our current gap and ensures + // that they are in sorted order. + if (!reverse) { + // normal ordering. + for (j = i - gap; j >= 0 && v[j] > v[j + gap]; j = j - gap) { + // swap the elements that are disordered. + temp = v[j]; v[j] = v[j + gap]; v[j + gap] = temp; + } + } else { + // reversed ordering. + for (j = i - gap; j >= 0 && v[j] < v[j + gap]; j = j - gap) { + // swap the elements that are disordered. + temp = v[j]; v[j] = v[j + gap]; v[j + gap] = temp; + } + } + } + } +} + +} // namespace. + +#endif // outer guard. + diff --git a/core/library/application/application_shell.cpp b/core/library/application/application_shell.cpp new file mode 100644 index 00000000..a894bfac --- /dev/null +++ b/core/library/application/application_shell.cpp @@ -0,0 +1,90 @@ +/*****************************************************************************\ +* * +* Name : application_shell * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "application_shell.h" + +#include +#include +#include +#include +#include +#include + +#include +#ifdef __UNIX__ + #include + #include +#endif + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +using namespace basis; +using namespace configuration; +using namespace loggers; +using namespace mathematics; +using namespace textual; +using namespace timely; + +namespace application { + +const int MAXIMUM_COMMAND_LINE = 32 * KILOBYTE; + // maximum command line that we'll deal with here. + +application_shell *not_so_hidden_pointer = NULL; // fodder for the single instance function. + +application_shell *application_shell::single_instance() { return not_so_hidden_pointer; } + +application_shell::application_shell() +: c_rando(), + c_exit_value(120) // if this is never changed from the default, that in itself is an error. +{ + // we create a combo logger for program-wide usage. + SETUP_COMBO_LOGGER; + not_so_hidden_pointer = this; // setup the single instance method. +} + +application_shell::~application_shell() +{ + if (not_so_hidden_pointer == this) not_so_hidden_pointer = NULL; // only scorch it when it's us. +} + +outcome application_shell::log(const base_string &to_print, int filter) +{ + outcome to_return = common::OKAY; + if (program_wide_logger::get().member(filter)) { + astring temp_log(to_print); + if (temp_log.length()) + temp_log.insert(0, time_stamp::notarize(true)); + to_return = program_wide_logger::get().log(temp_log, filter); + } + return to_return; +} + +int application_shell::execute_application() +{ + try { + c_exit_value = execute(); + } catch (const char *message) { + printf("caught exception:\n%s\n", message); + } catch (astring message) { + printf("caught exception:\n%s\n", message.s()); + } catch (...) { + printf("caught exception: unknown type!\n"); + } + return c_exit_value; +} + +} //namespace. + diff --git a/core/library/application/application_shell.h b/core/library/application/application_shell.h new file mode 100644 index 00000000..950eb919 --- /dev/null +++ b/core/library/application/application_shell.h @@ -0,0 +1,101 @@ +#ifndef APPLICATION_SHELL_CLASS +#define APPLICATION_SHELL_CLASS + +/*****************************************************************************\ +* * +* Name : application_shell * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "base_application.h" + +#include +#include + +namespace application { + +//! The application_shell is a base object for console programs. +/*! + It is generally used in that context (console mode), but can be employed as the root of windowed + programs also. The application_shell provides a few features, such as logging functionality, + that make it a bit easier than starting up a program from scratch every time. +*/ + +class application_shell : public base_application +{ +public: + application_shell(); + //!< constructs an application_shell to serve as the root of the program. + + virtual ~application_shell(); + + DEFINE_CLASS_NAME("application_shell") + + static application_shell *single_instance(); + //!< in a program with a single application_shell extant, this gives out the instance. + /*!< if there are more than one application_shells floating around a program, then this + will only give out the most recently registered. note that this pointer is not the + slightest bit thread safe if it's changing after the application shell has been constructed, + and that it cannot be relied upon until that's happened either. be careful. do not use + it in any function that might be invoked during program shutdown. */ + + virtual int execute_application(); + //!< runs the base class's execute() method and catches any exceptions due to it. + /*!< you can override this method, but generally should never need to. the derived class's + method should catch exceptions and deal with them meaningfully also. */ + + int exit_value() const { return c_exit_value; } + //!< once the application has finished executing, this will contain the exit value. + + const mathematics::chaos &randomizer() const { return c_rando; } + //!< provides access to the random number generator owned by this app. + +// static basis::astring application_name(); + //!< returns the full name of the current application. + +// static basis::u_int process_id(); + //!< returns the process id for this task, if that's relevant on the OS. + +// static basis::astring current_directory(); + //!< returns the current directory as reported by the operating system. + + virtual basis::outcome log(const basis::base_string &to_print, int filter = basis::ALWAYS_PRINT); + //!< as above, logs a line "to_print" but only if the "filter" is enabled. + /*!< the second version uses the filter value to assess whether to print + the string or not. the string will not print if that filter is not + enabled for the program wide logger. */ + +#ifdef __UNIX__ +// static basis::astring get_cmdline_from_proc(); + //!< retrieves the command line from the /proc hierarchy on linux. +// static basis::astring query_for_process_info(); + //!< seeks out process info for a particular process. +#endif + +protected: + virtual int execute() = 0; + //!< forwards base_application responsibility upwards to derived objects. + /*!< this should not be invoked by anyone in general; it is automatically called from + the constructor of this class. */ + +private: + mathematics::chaos c_rando; //!< random number generator. + int c_exit_value; //!< how did things end up for the app? + + // not applicable. + application_shell(const application_shell &); + application_shell &operator =(const application_shell &); +}; + +} //namespace. + +#endif // outer guard. + diff --git a/core/library/application/base_application.h b/core/library/application/base_application.h new file mode 100644 index 00000000..23a658a2 --- /dev/null +++ b/core/library/application/base_application.h @@ -0,0 +1,67 @@ +#ifndef BASE_APPLICATION_CLASS +#define BASE_APPLICATION_CLASS + +////////////// +// Name : base_application +// Author : Chris Koeritz +////////////// +// Copyright (c) 2000-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +#include +#include + +namespace application { + +//! Provides a base object for the root application portion of a program. +/*! + This mainly defines an entry point into the application's real functionality. + Derived versions of the base_application can layer in more functionality as + appropriate for different types of applications. +*/ + +class base_application : public virtual basis::nameable +{ +public: + virtual const char *class_name() const = 0; // must be provided by implementor. + + virtual int execute() = 0; + //!< performs the main activity of this particular application object. + /*!< the method must be overridden by the derived object. a return value + for the program as a whole should be returned. */ +}; + +////////////// + +#if 0 + +//! This is an example usage of the base_application class... +class example_application : public base_application +{ +public: + example_application() : base_application() {} + DEFINE_CLASS_NAME("example_application"); + int execute() { /* do stuff and return final exit value. */ } +}; + +//! This is a sample main application for a console mode program... +int __example__main(int argc, char *argv[]) +{ + example_application root_program; + return root_program.execute(); +} + +#endif // example guard. + +} //namespace. + +#endif // outer guard. + diff --git a/core/library/application/build_configuration.h b/core/library/application/build_configuration.h new file mode 100644 index 00000000..1ad87b6c --- /dev/null +++ b/core/library/application/build_configuration.h @@ -0,0 +1,59 @@ +#ifndef BUILD_CONFIGURATION_GROUP +#define BUILD_CONFIGURATION_GROUP + +////////////// +// Name : build configuration +// Author : Chris Koeritz +////////////// +// Copyright (c) 1995-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +/*! @file build_configuration.h + @brief Contains definitions that control how libraries and programs are built. + + These definitions can be used for declaring classes and functions that need + to be called from a DLL or that need to be compiled into a DLL. The import + style is used to declare that the item is being pulled out of a DLL. The + export style is used to declare that the item is being included in a DLL for + use by other functions. +*/ + +#include + +// Here is an example of a dynamic library header to be used by a particular library +// (presumably the gurpta library): +#if 0 + #ifndef GURPTA_DYNAMIC_LIBRARY_HEADER + #define GURPTA_DYNAMIC_LIBRARY_HEADER + // define BUILD_GURPTA when you are creating the dynamic library and + // define DYNAMIC_HOOPLE when you are importing code from the dynamic library. + #include + #if defined(BUILD_GURPTA) + #define GURPTA_LIBRARY HOOPLE_DYNAMIC_EXPORT + #elif defined(DYNAMIC_HOOPLE) + #define GURPTA_LIBRARY HOOPLE_DYNAMIC_IMPORT + #else + #define GURPTA_LIBRARY + #endif + #endif // outer guard. +#endif + +#ifdef __WIN32__ + #define HOOPLE_DYNAMIC_EXPORT __declspec(dllexport) + #define HOOPLE_DYNAMIC_IMPORT __declspec(dllimport) +#else + // no known requirements for these tags, so set them to nothing. + #define HOOPLE_DYNAMIC_EXPORT + #define HOOPLE_DYNAMIC_IMPORT +#endif + +#endif // outer guard. + diff --git a/core/library/application/callstack_tracker.cpp b/core/library/application/callstack_tracker.cpp new file mode 100644 index 00000000..c9f43bab --- /dev/null +++ b/core/library/application/callstack_tracker.cpp @@ -0,0 +1,272 @@ + + + +/*****************************************************************************\ +* * +* Name : callstack_tracker * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2007-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#ifdef ENABLE_CALLSTACK_TRACKING + +// note: this object cannot be constructed when the memory_checker is still +// tracking memory leaks. it must be disabled so that this object can +// construct without being tracked, which causes an infinite loop. the code +// in the basis extern support takes care of that for us. + +#include "callstack_tracker.h" + +#include +#include + +////#undef new +//is this right way to clean that out. + +const int MAX_STACK_DEPTH = 2000; + // beyond that many stack frames, we will simply refuse to add any more. + +const int MAX_TEXT_FIELD = 1024; + // the most space we allow the class, function, and file to take up. + +const char *emptiness_note = "Empty Stack\n"; + //!< what we show when the stack is empty. + +////////////// + +class callstack_records +{ +public: + frame_tracking_instance _records[MAX_STACK_DEPTH + 2]; // fudging room. +}; + +////////////// + +// our current depth gives us our position in the array. we define our +// stack as starting at element zero, which is a null stack entry and +// corresponds to a depth of zero also. then, when the stack depth is one, +// we actually have an element in place and it resides at index 1. this +// scheme allows us to have an update to the line number just do nothing when +// there is no current stack. + +callstack_tracker::callstack_tracker() +: _bt(new callstack_records), + _depth(0), + _frames_in(0), + _frames_out(0), + _highest(0), + _unusable(false) +{ +//printf("callstack ctor\n"); +} + +callstack_tracker::~callstack_tracker() +{ +//printf("!!!!!!!!!!!!!!!!!! callstack dtor in\n"); + _unusable = true; + WHACK(_bt); +//printf("!!!!!!!!!!!!!!!!!! callstack dtor out\n"); +} + +bool callstack_tracker::push_frame(const char *class_name, const char *func, + const char *file, int line) +{ +//printf("callstack pushframe depth=%d in\n", _depth); + if (_unusable) return false; + if (_depth >= MAX_STACK_DEPTH) { + // too many frames already. + printf("callstack_tracker::push_frame: past limit at class=%s func=%s " + "file=%s line=%d\n", class_name, func, file, line); + return false; + } + _depth++; + if (_depth > _highest) _highest = _depth; + _frames_in += 1; + _bt->_records[_depth].assign(class_name, func, file, line); +//printf("callstack pushframe depth=%d out\n", _depth); + return true; +} + +bool callstack_tracker::pop_frame() +{ +//printf("callstack popframe depth=%d in\n", _depth); + if (_unusable) return false; + if (_depth <= 0) { + // how inappropriate of them; we have no frames. + _depth = 0; // we don't lose anything useful by forcing it to be zero. + printf("callstack_tracker::pop_frame stack underflow!\n"); + return false; + } + _bt->_records[_depth].clean(); + _depth--; + _frames_out += 1; +//printf("callstack popframe depth=%d out\n", _depth); + return true; +} + +bool callstack_tracker::update_line(int line) +{ + if (_unusable) return false; + if (!_depth) return false; // not as serious, but pretty weird. + _bt->_records[_depth]._line = line; + return true; +} + +char *callstack_tracker::full_trace() const +{ + if (_unusable) return strdup(""); +//printf("fulltrace in\n"); + char *to_return = (char *)malloc(full_trace_size()); + to_return[0] = '\0'; + if (!_depth) { + strcat(to_return, emptiness_note); + return to_return; + } + const int initial_len = MAX_TEXT_FIELD + 8; + char temp[initial_len]; + int allowed_len = initial_len; + // space provided for one text line. + // start at top most active frame and go down towards bottom most. + for (int i = _depth; i >= 1; i--) { + strcat(to_return, "\t"); // we left space for this and \n at end. + temp[0] = '\0'; + int len_class = strlen(_bt->_records[i]._class); + int len_func = strlen(_bt->_records[i]._func); + if (allowed_len > len_class + len_func + 6) { + allowed_len -= len_class + len_func + 6; + sprintf(temp, "\"%s::%s\", ", _bt->_records[i]._class, + _bt->_records[i]._func); + strcat(to_return, temp); + } + + temp[0] = '\0'; + int len_file = strlen(_bt->_records[i]._file); + if (allowed_len > len_file + 4) { + allowed_len -= len_file + 4; + sprintf(temp, "\"%s\", ", _bt->_records[i]._file); + strcat(to_return, temp); + } + + temp[0] = '\0'; + sprintf(temp, "\"line=%d\"", _bt->_records[i]._line); + int len_line = strlen(temp); + if (allowed_len > len_line) { + allowed_len -= len_line; + strcat(to_return, temp); + } + + strcat(to_return, "\n"); // we left space for this already. + } + +//printf("fulltrace out\n"); + return to_return; +} + +int callstack_tracker::full_trace_size() const +{ + if (_unusable) return 0; + if (!_depth) return strlen(emptiness_note) + 14; // liberal allocation. + int to_return = 28; // another hollywood style excess. + for (int i = _depth; i >= 1; i--) { + int this_line = 0; // add up parts for just this item. + + // all of these additions are completely dependent on how it's done above. + + int len_class = strlen(_bt->_records[i]._class); + int len_func = strlen(_bt->_records[i]._func); + this_line += len_class + len_func + 6; + + int len_file = strlen(_bt->_records[i]._file); + this_line += len_file + 4; + + this_line += 32; // extra space for line number and such. + + // limit it like we did above; we will use the lesser size value. + if (this_line < MAX_TEXT_FIELD + 8) to_return += this_line; + else to_return += MAX_TEXT_FIELD + 8; + } + return to_return; +} + +////////////// + +frame_tracking_instance::frame_tracking_instance(const char *class_name, + const char *func, const char *file, int line, bool add_frame) +: _frame_involved(add_frame), + _class(class_name? strdup(class_name) : NIL), + _func(func? strdup(func) : NIL), + _file(file? strdup(file) : NIL), + _line(line) +{ + if (_frame_involved) { +//printf("frametrackinst ctor in class=%s func=%s\n", class_name, func); + program_wide_stack_trace().push_frame(class_name, func, file, line); +//printf("frametrackinst ctor out\n"); + } +} + +frame_tracking_instance::frame_tracking_instance + (const frame_tracking_instance &to_copy) +: _frame_involved(false), // copies don't get a right to this. + _class(to_copy._class? strdup(to_copy._class) : NIL), + _func(to_copy._func? strdup(to_copy._func) : NIL), + _file(to_copy._file? strdup(to_copy._file) : NIL), + _line(to_copy._line) +{ +} + +frame_tracking_instance::~frame_tracking_instance() { clean(); } + +void frame_tracking_instance::clean() +{ + if (_frame_involved) { +//printf("frametrackinst clean\n"); + program_wide_stack_trace().pop_frame(); + } + _frame_involved = false; + free(_class); _class = NIL; + free(_func); _func = NIL; + free(_file); _file = NIL; + _line = 0; +} + +frame_tracking_instance &frame_tracking_instance::operator = + (const frame_tracking_instance &to_copy) +{ +//printf("frametrackinst tor = in\n"); + if (this == &to_copy) return *this; + assign(to_copy._class, to_copy._func, to_copy._file, to_copy._line); +//printf("frametrackinst tor = out\n"); + return *this; +} + +void frame_tracking_instance::assign(const char *class_name, const char *func, + const char *file, int line) +{ + clean(); + _frame_involved = false; // copies don't get a right to this. + _class = class_name? strdup(class_name) : NIL; + _func = func? strdup(func) : NIL; + _file = file? strdup(file) : NIL; + _line = line; +} + +void update_current_stack_frame_line_number(int line) +{ +//printf("frametrackinst updatelinenum in\n"); + program_wide_stack_trace().update_line(line); +//printf("frametrackinst updatelinenum out\n"); +} + +#endif // ENABLE_CALLSTACK_TRACKING + + + + diff --git a/core/library/application/callstack_tracker.h b/core/library/application/callstack_tracker.h new file mode 100644 index 00000000..bb75adaa --- /dev/null +++ b/core/library/application/callstack_tracker.h @@ -0,0 +1,156 @@ +#ifndef CALLSTACK_TRACKER_CLASS +#define CALLSTACK_TRACKER_CLASS + +/*****************************************************************************\ +* * +* Name : callstack_tracker * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2007-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "definitions.h" + +#ifdef ENABLE_CALLSTACK_TRACKING + +#include "build_configuration.h" +#include "root_object.h" + +namespace application { + +// forward. +class callstack_records; +class callstack_tracker; + +////////////// + +callstack_tracker BASIS_EXTERN &program_wide_stack_trace(); + //!< a global object that can be used to track the runtime callstack. + +////////////// + +//! This object can provide a backtrace at runtime of the invoking methods. +/*! + The callstack tracking is hooked in through the FUNCDEF macros used to + set function names for logging. Thus it will only be visible if those + macros are used fairly carefully or if people invoke the stack frame addition + method themselves. +*/ + +class callstack_tracker +{ +public: + callstack_tracker(); + virtual ~callstack_tracker(); + + DEFINE_CLASS_NAME("callstack_tracker"); + + bool push_frame(const char *class_name, const char *func, const char *file, + int line); + //!< adds a new stack from for the "class_name" in "function" at the "line". + /*!< this function should be invoked when entering a new stack frame. the + "file" can be gotten from the __FILE__ macro and the "line" number can come + from __LINE__, but the "class_name" and "func" must be tracked some other + way. we recommend the FUNCDEF macro. this function might return false if + there is no longer any room for tracking more frames; that is a serious + issue that might indicate a runaway recursion or infinite loop. */ + + bool pop_frame(); + //!< removes the last callstack frame off from our tracking. + + bool update_line(int line); + //!< sets the line number within the current stack frame. + /*!< the current frame can reside across several line numbers, so this + allows the code to be more specific about the location of an invocation. */ + + char *full_trace() const; + //!< provides the current stack trace in a newly malloc'd string. + /*!< the user *must* free() the string returned. */ + + int full_trace_size() const; + //!< this returns the number of bytes needed for the above full_trace(). + + int depth() const { return _depth; } + //!< the current number of frames we know of. + + double frames_in() const { return _frames_in; } + //!< reports the number of call stack frames that were added, total. + + double frames_out() const { return _frames_out; } + //!< reports the number of call stack frames that were removed, total. + + double highest() const { return _highest; } + //!< reports the maximum stack depth seen during the runtime so far. + +private: + callstack_records *_bt; //!< the backtrace records for current program. + int _depth; //!< the current number of frames we know of. + double _frames_in; //!< number of frame additions. + double _frames_out; //!< number of frame removals. + double _highest; //!< the most number of frames in play at once. + bool _unusable; //!< object has already been destroyed. +}; + +////////////// + +//! a small object that represents a stack trace in progress. +/*! the object will automatically be destroyed when the containing scope +exits. this enables a users of the stack tracker to simply label their +function name and get the frame added. if they want finer grained tracking, +they should update the line number periodically through their function, +especially when memory is about to be allocated or where something might go +wrong. */ + +class frame_tracking_instance +{ +public: + // these are not encapsulated, but be careful with the contents. + bool _frame_involved; //!< has this object been added to the tracker? + char *_class, *_func, *_file; //!< newly allocated copies. + int _line; + + frame_tracking_instance(const char *class_name = "", const char *func = "", + const char *file = "", int line = 0, bool add_frame = false); + //!< as an automatic variable, this can hang onto frame information. + /*!< if "add_frame" is true, then this actually adds the stack frame in + question to the tracker. thus if you use this class at the top of your + function, such as via the FUNCDEF macro, then you can forget about having + to pop the frame later. */ + + frame_tracking_instance(const frame_tracking_instance &to_copy); + + ~frame_tracking_instance(); + //!< releases the information *and* this stack frame in the tracker. + + frame_tracking_instance &operator =(const frame_tracking_instance &to_copy); + + void assign(const char *class_name, const char *func, const char *file, + int line); + //!< similar to assignment operator but doesn't require an object. + + void clean(); + //!< throws out our accumulated memory and pops frame if applicable. +}; + +void update_current_stack_frame_line_number(int line); + //!< sets the line number for the current frame in the global stack trace. + +#else // ENABLE_CALLSTACK_TRACKING + // bogus replacements for most commonly used callstack tracking support. + #define frame_tracking_instance + #define __trail_of_function(p1, p2, p3, p4, p5) if (func) {} + // the above actually trades on the name of the object we'd normally + // define. it must match the object name in the FUNCDEF macro. + #define update_current_stack_frame_line_number(line) +#endif // ENABLE_CALLSTACK_TRACKING + +} //namespace. + +#endif // outer guard. + diff --git a/core/library/application/command_line.cpp b/core/library/application/command_line.cpp new file mode 100644 index 00000000..19f61810 --- /dev/null +++ b/core/library/application/command_line.cpp @@ -0,0 +1,486 @@ +/*****************************************************************************\ +* * +* Name : command_line * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "command_line.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +using namespace basis; +using namespace configuration; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; + +namespace application { + +DEFINE_ARGC_AND_ARGV; + +command_parameter::command_parameter(parameter_types type) +: _type(type), _text(new astring) {} + +command_parameter::command_parameter(parameter_types type, const astring &text) +: _type(type), _text(new astring(text)) {} + +command_parameter::command_parameter(const command_parameter &to_copy) +: _type(VALUE), _text(new astring) +{ *this = to_copy; } + +command_parameter::~command_parameter() { WHACK(_text); } + +const astring &command_parameter::text() const { return *_text; } + +void command_parameter::text(const astring &new_text) { *_text = new_text; } + +command_parameter &command_parameter::operator = + (const command_parameter &to_copy) +{ + if (this == &to_copy) return *this; + _type = to_copy._type; + *_text = *to_copy._text; + return *this; +} + +////////////// + +// option_prefixes: the list of valid prefixes for options on a command line. +// these are the characters that precede command line arguments. For Unix, +// the default is a dash (-), while for DOS most programs use forward-slash +// (/). Adding more characters is trivial; just add a character to the list +// before the sentinel of '\0'. +#if defined(_MSC_VER) || defined(__MINGW32__) + static char option_prefixes[] = { '-', '/', '\0' }; +#elif defined(__UNIX__) + static char option_prefixes[] = { '-', '\0' }; +#else + #error "I don't know what kind of operating system this is." +#endif + +bool it_is_a_prefix_char(char to_test) +{ + for (int i = 0; option_prefixes[i]; i++) + if (to_test == option_prefixes[i]) return true; + return false; +} + +////////////// + +class internal_cmd_line_array_of_parms : public array {}; + +////////////// + +SAFE_STATIC_CONST(command_parameter, command_line::cmdline_blank_parm, ) + // our default return for erroneous indices. + +command_line::command_line(int argc, char *argv[]) +: _implementation(new internal_cmd_line_array_of_parms), + _program_name(new filename(directory::absolute_path(argv[0]))) +{ + argv++; // skip command name in argv. + + // loop over the rest of the fields and examine them. + string_array string_list; // accumulated below. + while (--argc > 0) { + astring to_store = argv[0]; // retrieve the current string. + string_list += to_store; // put the string in our list. + argv++; // next string. + } + parse_string_array(string_list); +} + +command_line::command_line(const astring &full_line) +: _implementation(new internal_cmd_line_array_of_parms), + _program_name(new filename) +{ + astring accumulator; + string_array string_list; + bool in_quote = false; +//hmmm: this is not quote right yet. +// use the separate command line method, but get it to run iteratively +// so we can keep pulling them apart? maybe it already does! +// separate is better because it handles escaped quotes. + for (int i = 0; i < full_line.length(); i++) { + char to_examine = full_line.get(i); + if (to_examine == '"') { + // it's a quote character, so maybe we can start eating spaces. + if (!in_quote) { + in_quote = true; + continue; // eat the quote character but change modes. + } + // nope, we're closing a quote. we assume that the quotes are + // around the whole argument. that's the best win32 can do at least. + in_quote = false; + to_examine = ' '; // trick parser into logging the accumulated string. + // intentional fall-through to space case. + } + + if (parser_bits::white_space(to_examine)) { + // if this is a white space, then we start a new string. + if (!in_quote && accumulator.t()) { + // only grab the accumulator if there are some contents. + string_list += accumulator; + accumulator = ""; + } else if (in_quote) { + // we're stuffing the spaces into the string since we're quoted. + accumulator += to_examine; + } + } else { + // not white space, so save it in the accumulator. + accumulator += to_examine; + } + } + if (accumulator.t()) string_list += accumulator; + // that partial string wasn't snarfed during the loop. + // grab the program name off the list so the parsing occurs as expected. + *_program_name = directory::absolute_path(string_list[0]); + string_list.zap(0, 0); + parse_string_array(string_list); +} + +command_line::~command_line() +{ + WHACK(_program_name); + WHACK(_implementation); +} + +int command_line::entries() const { return _implementation->length(); } + +filename command_line::program_name() const { return *_program_name; } + +const command_parameter &command_line::get(int field) const +{ + bounds_return(field, 0, entries() - 1, cmdline_blank_parm()); + return _implementation->get(field); +} + +void command_line::separate_command_line(const astring &cmd_line, + astring &app, astring &parms) +{ + char to_find = ' '; // the command separator. + if (cmd_line[0] == '\"') to_find = '\"'; + // if the first character is a quote, then we are seeing a quoted phrase + // and need to look for its completing quote. otherwise, we'll just look + // for the next space. + + int seek_posn = 1; // skip the first character. we have accounted for it. + // skim down the string, looking for the ending of the first phrase. + while (seek_posn < cmd_line.length()) { + // look for our parameter separator. this will signify the end of the + // first phrase / chunk. if we don't find it, then it should just mean + // there was only one item on the command line. + int indy = cmd_line.find(to_find, seek_posn); + if (negative(indy)) { + // yep, there wasn't a matching separator, so we think this is just + // one chunk--the app name. + app = cmd_line; + break; + } else { + // now that we know where our separator is, we need to find the right + // two parts (app and parms) based on the separator character in use. + if (to_find == '\"') { + // we are looking for a quote character to complete the app name. + if (cmd_line[indy - 1] == '\\') { + // we have a backslash escaping this quote! keep seeking. + seek_posn = indy + 1; + continue; + } + app = cmd_line.substring(0, indy); + parms = cmd_line.substring(indy + 2, cmd_line.end()); + // skip the quote and the obligatory space character after it. + break; + } else { + // simple space handling here; no escapes to worry about. + app = cmd_line.substring(0, indy - 1); + parms = cmd_line.substring(indy + 1, cmd_line.end()); + break; + } + } + } +} + +bool command_line::zap(int field) +{ + bounds_return(field, 0, entries() - 1, false); + _implementation->zap(field, field); + return true; +} + +// makes a complaint about a failure and sets the hidden commands to have a +// bogus entry so they aren't queried again. +#define COMPLAIN_CMDS(s) \ + listo_cmds += "unknown"; \ + COMPLAIN(s) + +string_array command_line::get_command_line() +{ +// FUNCDEF("get_command_line"); + string_array listo_cmds; + // the temporary string below can be given a flat formatting of the commands + // and it will be popped out into a list of arguments. + astring temporary; +#ifdef __UNIX__ + if (!_global_argc || !_global_argv) { + // our global parameters have not been set, so we must calculate them. + temporary = application_configuration::get_cmdline_from_proc(); + } else { + // we have easy access to command line arguments supposedly, so use them. + for (int i = 0; i < _global_argc; i++) { + // add a string entry for each argument. + listo_cmds += _global_argv[i]; + } + // we don't need a long string to be parsed; the list is ready. + return listo_cmds; + } +#elif defined(__WIN32__) + // we have easy access to the original list of commands. + for (int i = 0; i < _global_argc; i++) { + // add a string entry for each argument. + listo_cmds += _global_argv[i]; + } + return listo_cmds; +#else + COMPLAIN_CMDS("this OS doesn't support getting the command line."); + return listo_cmds; +#endif + + // now that we have our best guess at a flat representation of the command + // line arguments, we'll chop it up. + +//hmmm: this algorithm doesn't support spaces in filenames currently. +//hmmm: for windows, we can parse the quotes that should be around cmd name. +//hmmm: but for unix, the ps command doesn't support spaces either. how to +// get around that to support programs with spaces in the name? + int posn = 0; + int last_posn = -1; + while (posn < temporary.length()) { + posn = temporary.find(' ', posn); + if (non_negative(posn)) { + // found another space to turn into a portion of the command line. + listo_cmds += temporary.substring(last_posn + 1, posn - 1); + // grab the piece of string between the point just beyond where we + // last saw a space and the position just before the space. + last_posn = posn; // save the last space position. + posn++; // push the pointer past the space. + } else { + // no more spaces in the string. grab what we can from the last bit + // of the string that we see. + if (last_posn < temporary.length() - 1) { + // there's something worthwhile grabbing after the last place we + // saw a space. + listo_cmds += temporary.substring(last_posn + 1, + temporary.length() - 1); + } + break; // we're done finding spaces. + } + } + + return listo_cmds; +} + +astring command_line::text_form() const +{ + astring to_return; + const astring EOL = parser_bits::platform_eol_to_chars(); + for (int i = 0; i < entries(); i++) { + const command_parameter &curr = get(i); + to_return += a_sprintf("%d: ", i + 1); + switch (curr.type()) { + case command_parameter::CHAR_FLAG: + to_return += astring(" ") + curr.text() + EOL; + break; + case command_parameter::STRING_FLAG: + to_return += astring(" ") + curr.text() + EOL; + break; + case command_parameter::VALUE: // pass through to default. + default: + to_return += astring(" ") + curr.text() + EOL; + break; + } + } + return to_return; +} + +bool command_line::find(char option_character, int &index, + bool case_sense) const +{ + astring opt(option_character, 1); // convert to a string once here. + if (!case_sense) opt.to_lower(); // no case-sensitivity. + for (int i = index; i < entries(); i++) { +//hmmm: optimize this too. + if (get(i).type() == command_parameter::CHAR_FLAG) { + bool success = (!case_sense && get(i).text().iequals(opt)) + || (case_sense && (get(i).text() == opt)); + if (success) { + // the type is appropriate and the value is correct as well... + index = i; + return true; + } + } + } + return false; +} + +bool command_line::find(const astring &option_string, int &index, + bool case_sense) const +{ + FUNCDEF("find"); +if (option_string.length() && (option_string[0] == '-') ) +LOG(astring("found option string with dash! string is: ") + option_string); + + for (int i = index; i < entries(); i++) { + if (get(i).type() == command_parameter::STRING_FLAG) { + bool success = (!case_sense && get(i).text().iequals(option_string)) + || (case_sense && (get(i).text() == option_string)); + if (success) { + // the type is appropriate and the value is correct as well... + index = i; + return true; + } + } + } + return false; +} + +bool command_line::get_value(char option_character, astring &value, + bool case_sense) const +{ + value = ""; + int posn = 0; // where we find the flag. + if (!find(option_character, posn, case_sense)) return false; + + // get the value after the flag, if there is such. + posn++; // this is where we think our flag's value lives. + if (posn >= entries()) return false; + + // there's still an entry after where we found our flag; grab it. + command_parameter cp = get(posn); + if (cp.type() != command_parameter::VALUE) return false; + + // finally; we've found an appropriate text value. + value = cp.text(); + return true; +} + +bool command_line::get_value(const astring &option_string, astring &value, + bool case_sense) const +{ + FUNCDEF("get_value"); +if (option_string.length() && (option_string[0] == '-') ) +LOG(astring("found option string with dash! string is: ") + option_string); + + value = ""; + int posn = 0; // where we find the flag. + if (!find(option_string, posn, case_sense)) return false; + + // get the value after the flag, if there is such. + posn++; // this is where we think our flag's value lives. + if (posn >= entries()) return false; + + // there's still an entry after where we found our flag; grab it. + command_parameter cp = get(posn); + if (cp.type() != command_parameter::VALUE) return false; + + // finally; we've found an appropriate text value. + value = cp.text(); + return true; +} + +void command_line::parse_string_array(const string_array &to_parse) +{ + bool still_looking_for_flags = true; // goes to false when only values left. + // loop over the fields and examine them. + for (int i = 0; i < to_parse.length(); i++) { + // retrieve a character from the current string. + int index = 0; + char c = to_parse[i].get(index++); + // we check whether it's a prefix character, and if so, what kind. + if (still_looking_for_flags && it_is_a_prefix_char(c)) { + // at least one prefix is there, so treat this as a flag. + bool gnu_type_of_flag = false; + if (it_is_a_prefix_char(to_parse[i].get(index))) { + // there's a special GNU double flag beginner. + index++; // skip that extra one. + if ( (index >= to_parse[i].length()) + || parser_bits::white_space(to_parse[i].get(index))) { + // special case of '--' (or '//' i suppose) with white space or + // nothing else afterwards; indicates that the rest of the items + // should just be values, not flags. + still_looking_for_flags = false; + continue; // we ate that item. + } + gnu_type_of_flag = true; + } + // everything after the prefixes is considered part of the flag; they're + // either individual flag characters (on a single prefix) or they're the + // full name for the flag (gnu style). + c = 1; // reset to a true bool value. + astring gnu_accumulator; // if processing a gnu flag, it arrives here. + while (c) { + if (!gnu_type_of_flag) { + // add as many flag parameters as possible. + c = to_parse[i].get(index++); + // c will be zero once we hit the end of the string. + if (c) { + command_parameter to_add(command_parameter::CHAR_FLAG, astring(c, 1)); + *_implementation += to_add; + } + } else { + // the gnu flag name is added to here. + c = to_parse[i].get(index++); // zero at end of string. + if (c) + gnu_accumulator += c; // one more character. + } + } + if (gnu_accumulator.t()) { + // we've accumulated a gnu flag, so store it. + command_parameter to_add(command_parameter::STRING_FLAG, + gnu_accumulator); + *_implementation += to_add; + } + } else { + // add a value type of command_parameter. + astring found = to_parse[i]; + command_parameter to_add(command_parameter::VALUE, found); + *_implementation += to_add; + } + } +} + +astring command_line::gather(int &index) const +{ + astring to_return; + for (int i = index; i < entries(); i++) { + if (get(i).type() == command_parameter::CHAR_FLAG) { + index = i; + return to_return; + } else to_return += get(i).text(); + } + index = entries() - 1; + return to_return; +} + +} //namespace. + diff --git a/core/library/application/command_line.h b/core/library/application/command_line.h new file mode 100644 index 00000000..4addb392 --- /dev/null +++ b/core/library/application/command_line.h @@ -0,0 +1,216 @@ +#ifndef COMMAND_LINE_CLASS +#define COMMAND_LINE_CLASS + +/*****************************************************************************\ +* * +* Name : command_line * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +namespace application { + +//! This class parses the command line passed to the main() function. +/*! + The main function might be called WinMain or be implemented by CWinApp in MS-Windows, but all + of these types of applications will still have flags passed to them. This class constructs a + list of parameters from the command line provided by the OS. A parameter is either a command + flag or a value string. Flag characters take the form of "-l" or "-Q" or "-als" (which has + three flags). Value strings are other strings found on the command line that do not start with + the separator character (usually '-'). Flag characters are all broken up into separate entries. + A special kind of flag uses a double separator to allow multiple character flag names (e.g., + the string "--follow" where the flag name is "follow"), as in GNU software. + This class knows about the convention of a paramter of just '--' being used to + indicate that the rest of the line is all normal parameters and has no intentional flags + in them. This allows passing of character strings that would otherwise be misinterpreted + as flags rather than literal input. + +(not implemented) + A special feature is provided to allow parsing of flag characters followed + directly by the value (as in "-fbroiler.h", where the flag character is 'f' + and the value is "broiler.h". +(not implemented) +*/ + +// forward declarations. +class internal_cmd_line_array_of_parms; + +class command_parameter : public virtual basis::root_object +{ +public: + enum parameter_types { VALUE, CHAR_FLAG, STRING_FLAG, BOGUS_ITEM }; + + command_parameter(parameter_types type = BOGUS_ITEM); + //!< default constructor initializes to mostly blank state. + + command_parameter(parameter_types type, const basis::astring &text); + //!< constructs a parameter of "type" where the value is "text". + /*!< if the "type" is CHAR_FLAG, then this should be a string of + one character. for STRING_FLAG, the length is arbitrary. */ + + command_parameter(const command_parameter &to_copy); + + ~command_parameter(); + + DEFINE_CLASS_NAME("command_parameter"); + + command_parameter &operator =(const command_parameter &to_copy); + + parameter_types type() const { return _type; } + //!< observes the type of the parameter. + void type(parameter_types new_type) { _type = new_type; } + //!< modifies the type of the parameter. + + const basis::astring &text() const; + //!< observes the string contents. + void text(const basis::astring &new_text); + //!< modifies the string contents. + +private: + parameter_types _type; + basis::astring *_text; +}; + +////////////// + +class command_line +{ +public: + command_line(int argc, char *argv[]); + //!< takes command line parameters in the form of "argc" and "argv". + /*!< this is suitable for most C++ main programs. the first "argv" + string (element zero) is ignored because it is assumed that it is the program name. + these become available in the global variables _global_argc and _global_argv. */ + + command_line(const basis::astring &to_parse); + //!< takes a string form of the command line. + /*!< this is the form rendered by GetCommandLine() in Win32. on certain + win32 platforms, this may not return a full path for the program_name() + method. this uses the separate_command_line() method to pick out the + relevant pieces and supports embedded, escaped quotes. */ + + virtual ~command_line(); + + DEFINE_CLASS_NAME("command_line"); + + filesystem::filename program_name() const; + //!< Returns the program name found in the command line. + + static void separate_command_line(const basis::astring &cmd_line, basis::astring &app, + basis::astring &parms); + //!< breaks apart a command line in "cmd_line" into "app" and "parms". + /*!< when given a full command line, where the application to run is the + first chunk and its parameters (if any) are subsequent chunks, this will + store the application name in "app" and the rest of the parameters in + "parms". this expects any paths in the "cmd_line" that contain spaces + to be surrounded by quotes. if there are any quote characters that are + escaped, they are considered to be embedded in the parameter string; they + will not be considered as matching any pending closing quotes. */ + + int entries() const; + //!< Returns the number of fields found on the command line. + /*!< This does not include the program name found; that's only + accessible through the program_name() method. */ + + const command_parameter &get(int field) const; + //!< Returns the parameter at the "field" specified. + /*!< The "field" ranges from zero through "entries() - 1" inclusive. if + an invalid index is used, then the type will be BOGUS_ITEM. */ + + bool zap(int field); + //!< eats the entry at position "field". + /*!< this is useful for cleaning out entries that have already been dealt + with. */ + + // note: in the following, if "case_sense" is true, then the searches are + // case-sensitive. otherwise, case of the flags is not a concern. + // the returned values always retain the original case. + + bool find(char option_character, int &index, bool case_sense = true) const; + //!< Returns true if the "option_character" is found in the parameters. + /*!< The search starts at the "index" specified, and if the item is found, + its location is returned in "index" and the function returns true. + Otherwise false is returned and the "index" is not modified. */ + bool find(const basis::astring &option_string, int &index, + bool case_sense = true) const; + //!< Returns true if the "option_string" is found in the parameters. + + bool get_value(char option_character, basis::astring &value, + bool case_sense = true) const; + //!< retrieves the "value" found for the option flag specified. + /*!< this is useful for command lines with standard spacing. for example, + if the command line is "-Q query.bop --Linkage plorgs.txt", then this + function would return "query.bop" for a search on 'Q' and the find() + method below would return "plorgs.txt" for the string flag search on + "Linkage". */ + bool get_value(const basis::astring &option_string, basis::astring &value, + bool case_sense = true) const; + //!< retrieves the "value" found for the "option_string" specified. + +//is this useful? it's kind of like what we need for special flags (like +// -fgob.h, where gob.h is a value parameter) but needs to terminate +//differently for that to work. + basis::astring gather(int &index) const; + //!< coalesces parameters together until the next option flag. + /*!< Returns a string constructed from the concatenation of the strings + for the parameters at all indices in the list starting at "index" until + an option character is found. Note that this means an empty string + will be returned if the parameter at "index" has an option character, + or if "index" is greater than or equal to "elements()". + After gather, "index" is set to the last location included in the + string. "index" is set to the last index in the list if "index" was + past the end to begin with or if strings are gathered up to the last + index. otherwise, "index" is unchanged if nothing was gathered. */ + + basis::astring text_form() const; + //!< returns a string with all the information we have for the command line. + + static structures::string_array get_command_line(); + //!< returns the command line passed to the program as a list of strings. + /*!< the string at index zero is the program name. this is just a useful + helper function and is not normally needed by users of the command_line + object. */ + +private: + internal_cmd_line_array_of_parms *_implementation; //!< held parameters. + filesystem::filename *_program_name; //!< the name of this program. + + void parse_string_array(const structures::string_array &to_parse); + //!< pulls all the strings in "to_parse" into the command_parameter list. + + // forbidden: + command_line(const command_line &to_copy); + command_line &operator =(const command_line &to_copy); + + static const command_parameter &cmdline_blank_parm(); +}; + +////////////// + +// this declares a program-wide command-line argument storage area. + +extern int _global_argc; +extern char **_global_argv; +//! this macro allocates space for the command-line storage areas. +#define DEFINE_ARGC_AND_ARGV int _global_argc = 0; char **_global_argv = NIL + +//! this macro assigns our command-line parameters for this program. +#define SET_ARGC_ARGV(argc, argv) { \ + application::_global_argc = argc; \ + application::_global_argv = argv; \ +} + +} //namespace. + +#endif + diff --git a/core/library/application/dll_root.cpp b/core/library/application/dll_root.cpp new file mode 100644 index 00000000..7eab74b3 --- /dev/null +++ b/core/library/application/dll_root.cpp @@ -0,0 +1,136 @@ +/*****************************************************************************\ +* * +* Name : DLL Main Root Support * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1995-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +// Thanks to Andy Tan for some research into MFC extension dlls. + +#include +#include +#include + +//HOOPLE_STARTUP_CODE_DLL; + // initialize objects needed by the hoople libs. + +#ifdef _AFXDLL + +#ifdef DEBUG + #define TRACE_PRINTER(s) TRACE_PRINT(s) +#else + #define TRACE_PRINTER(s) +#endif + +// base for AFX dlls. + +#include // MFC extensions. +#include // Extension DLL declarations; include only once! + +bool check_DLL_versions(); + // supplied by the library's version of dllmain.cpp. + +static AFX_EXTENSION_MODULE SomeDLL = { NULL, NULL }; + +#ifndef DLL_NAME + #define DLL_NAME "unnamed DLL" +#endif + +extern "C" int APIENTRY +DllMain(application_instance instance, DWORD reason, LPVOID reserved) +{ + SET_INSTANCE_HANDLE(instance); + // Remove this if you use lpReserved. + UNREFERENCED_PARAMETER(reserved); + + char *dll_name = DLL_NAME; + // mainly for debugging purposes. having the value for DLL_NAME actually + // stored should allow us to know which dll is being debugged. + + static CDynLinkLibrary *dll_link = NIL; + + static int dll_entry_count = 0; + + switch (reason) { + case DLL_PROCESS_ATTACH: { + char *message = DLL_NAME " Initializing!\n"; + TRACE_PRINTER(message); + + if (!check_DLL_versions()) return 0; + + // Extension DLL one-time initialization + if (!AfxInitExtensionModule(SomeDLL, instance)) return 0; + + // Insert this DLL into the resource chain. + dll_link = new CDynLinkLibrary(SomeDLL); + + // NOTE: If this Extension DLL is being implicitly linked to by an MFC + // Regular DLL (such as an ActiveX Control) instead of an MFC + // application, then you will want to remove this line from DllMain and + // put it in a separate function exported from this Extension DLL. The + // Regular DLL that uses this Extension DLL should then explicitly call + // that function to initialize this Extension DLL. Otherwise, the + // CDynLinkLibrary object will not be attached to the Regular DLL's + // resource chain, and serious problems will result. + ++dll_entry_count; + break; + } + case DLL_PROCESS_DETACH: { + --dll_entry_count; + char *message = DLL_NAME " Terminating!\n"; + TRACE_PRINTER(message); + // clean up our other stuff. + WHACK(dll_link); + // Terminate the library before destructors are called. + AfxTermExtensionModule(SomeDLL); + break; + } + case DLL_THREAD_ATTACH: + ++dll_entry_count; + break; + case DLL_THREAD_DETACH: + --dll_entry_count; + break; + default: +// do nothing. + break; + } + + return 1; +} + +#elif defined(__WIN32__) + +// regular dll base. + +#include // base windows stuff. + +bool check_DLL_versions(); + // supplied by the library's version of dllmain.cpp. + +BOOL APIENTRY DllMain(HANDLE module, DWORD ul_reason_for_call, LPVOID reserved) +{ + SET_INSTANCE_HANDLE((application_instance)module); + switch (ul_reason_for_call) { + case DLL_PROCESS_ATTACH: + if (!check_DLL_versions()) return 0; + break; + + // these are currently not processed. + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return true; +} + +#endif + diff --git a/core/library/application/hoople_main.h b/core/library/application/hoople_main.h new file mode 100644 index 00000000..aac5a74e --- /dev/null +++ b/core/library/application/hoople_main.h @@ -0,0 +1,122 @@ +#ifndef HOOPLE_MAIN_GROUP +#define HOOPLE_MAIN_GROUP + +/*****************************************************************************\ +* * +* Name : HOOPLE_MAIN group +* Author : Chris Koeritz +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! @file "hoople_main.h" Provides macros that implement the 'main' program of an application. + +#include "application_shell.h" +#include "command_line.h" +#include "windoze_helper.h" + +#include +#include +#include +#include +#include + +namespace application { + +// The following versions of main programs are provided for different operating +// systems and compilation environments. These support the requirements of the +// HOOPLE startup code and program-wide features, assuming an object derived +// from base_application is available. +// The object derived from base_application must be provided in "obj_name". +// The "obj_args" are any arguments that need to be passed to the object's +// constructor; this list can be empty. +// Note that the default logger used for unix and console mode win32 programs +// is the console logger; that can be changed in the startup code for the +// "obj_name". + +#define HOOPLE_STARTUP_CODE \ + DEFINE_INSTANCE_HANDLE; + +#ifdef __WXWIDGETS__ + //! main program for applications using WxWidgets library. + #define HOOPLE_MAIN(obj_name, obj_args) \ + HOOPLE_STARTUP_CODE; \ + int main(int argc, char *argv[]) { \ + SET_ARGC_ARGV(argc, argv); \ + SETUP_COMBO_LOGGER; \ + obj_name to_run_obj obj_args; \ + return to_run_obj.execute_application(); \ + } + +////////////// + +#elif defined(__UNIX__) + //! options that should work for most unix and linux apps. + #define HOOPLE_MAIN(obj_name, obj_args) \ + HOOPLE_STARTUP_CODE; \ + int main(int argc, char *argv[]) { \ + SET_ARGC_ARGV(argc, argv); \ + SETUP_COMBO_LOGGER; \ + obj_name to_run_obj obj_args; \ + return to_run_obj.execute_application(); \ + } + +////////////// + +#elif defined(__WIN32__) + // for win32 we need to support four different environments--console mode, + // borland compilation, MFC programs and regular windows programs. + #ifdef _CONSOLE + //! console mode programs can easily write to a command shell. + #define HOOPLE_MAIN(obj_name, obj_args) \ + HOOPLE_STARTUP_CODE; \ + int main(int argc, char *argv[]) { \ + SETUP_COMBO_LOGGER; \ + SET_ARGC_ARGV(argc, argv); \ + obj_name to_run_obj obj_args; \ + return to_run_obj.execute_application(); \ + } + #elif defined(_AFXDLL) + //! MFC applications generally use a tiny shell which hooks up logging. + #define HOOPLE_MAIN(obj_name, obj_args) \ + HOOPLE_STARTUP_CODE; \ + SET_ARGC_ARGV(__argc, __argv); \ + tiny_application theApp; + #elif defined(__WIN32__) + //! standard win32 applications have no console, so we just log to a file. + #define HOOPLE_MAIN(obj_name, obj_args) \ + HOOPLE_STARTUP_CODE; \ + int WINAPI WinMain(application_instance instance, \ + application_instance prev_instance, LPSTR lpCmdLine, \ + int nCmdShow) { \ + SET_ARGC_ARGV(__argc, __argv); \ + SET_INSTANCE_HANDLE(instance); \ + SETUP_FILE_LOGGER; \ + obj_name to_run_obj obj_args; \ + return to_run_obj.execute_application(); \ + } + #endif + +////////////// + +#else // not __UNIX__ or __WIN32__ + //! just guessing this might work; otherwise we have no idea. + #define HOOPLE_MAIN(obj_name, obj_args) \ + HOOPLE_STARTUP_CODE; \ + int main(int argc, char *argv[]) { \ + SETUP_CONSOLE_LOGGER; \ + obj_name to_run_obj obj_args; \ + return to_run_obj.execute_application(); \ + } +#endif + +} //namespace. + +#endif // outer guard. + diff --git a/core/library/application/hoople_service.cpp b/core/library/application/hoople_service.cpp new file mode 100644 index 00000000..969a2c27 --- /dev/null +++ b/core/library/application/hoople_service.cpp @@ -0,0 +1,224 @@ +////////////// +// Name : hoople_service +// Author : Chris Koeritz +////////////// +// Copyright (c) 2000-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +#include "hoople_service.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace processes; +using namespace structures; +using namespace timely; + +//#define DEBUG_HOOPLE_SERVICE + // uncomment for noisy version. + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +namespace application { + +////////////// + +SAFE_STATIC(astring, hoople_service::_app_name, ) + +bool &hoople_service::_defunct() { static bool _defu = false; return _defu; } + +bool &hoople_service::_saw_interrupt() +{ static bool _saw = false; return _saw; } + +int &hoople_service::_timer_period() { static int _tim = 0; return _tim; } + +////////////// + +static hoople_service *_global_hoople_service = NIL; + // this static object is set by the setup() method. it should only come + // into existence once during a program's lifetime. + +hoople_service::hoople_service() +{ +} + +hoople_service::~hoople_service() +{ + make_defunct(); + if (_global_hoople_service) { + program_wide_timer().zap_timer(_global_hoople_service); + } +} + +void hoople_service::handle_startup() { /* nothing for base class. */ } + +void hoople_service::handle_shutdown() { /* nothing for base class. */ } + +void hoople_service::handle_timer() { /* nothing for base class. */ } + +void hoople_service::handle_timer_callback() +{ + // don't invoke the user's timer unless the object is still alive. + if (!is_defunct()) handle_timer(); +} + +void hoople_service::close_this_program() +{ + make_defunct(); +} + +bool hoople_service::close_application(const astring &app_name) +{ + FUNCDEF("close_application"); + process_entry_array procs; + process_control querier; + + // lookup process ids of apps. + bool got_list = querier.query_processes(procs); + if (!got_list) { + LOG(astring("couldn't get process list.")); + return false; + } + int_set pids; + got_list = querier.find_process_in_list(procs, app_name, pids); + if (!got_list) { + LOG(astring("couldn't find process in the list of active ones.")); + return true; + } + + // zap all of them using our signal. + for (int i = 0; i < pids.length(); i++) { +//would linux be better served with sigterm also? +#ifdef __UNIX__ + kill(pids[i], SIGHUP); +#endif +#ifdef __WIN32__ +//lame--goes to whole program. + raise(SIGTERM); +#endif +//hmmm: check results... + } + + return true; +} + +void hoople_service::handle_OS_signal(int formal(sig_id)) +{ + _saw_interrupt() = true; // save the status. + if (_global_hoople_service) { + _global_hoople_service->close_this_program(); + } +} + +void hoople_service::make_defunct() +{ + _defunct() = true; +} + +bool hoople_service::setup(const astring &app_name, int timer_period) +{ +//hmmm: make sure not already initted. + + // simple initializations first... + _timer_period() = timer_period; + _app_name() = app_name; + + _global_hoople_service = this; + + // setup signal handler for HUP signal. this is the one used to tell us + // to leave. +#ifdef __UNIX__ + signal(SIGHUP, handle_OS_signal); +#endif + + // setup a handler for interrupt (e.g. ctrl-C) also. + signal(SIGINT, handle_OS_signal); +#ifdef __WIN32__ + signal(SIGBREAK, handle_OS_signal); +#endif + + return true; +} + +bool hoople_service::launch_console(hoople_service &alert, + const astring &app_name, int timer_period) +{ +#ifdef DEBUG_HOOPLE_SERVICE + FUNCDEF("launch_console"); +#endif + if (!alert.setup(app_name, timer_period)) return false; + + alert.handle_startup(); // tell the program it has started up. + + // start a timer if they requested one. + if (_timer_period()) { + program_wide_timer().set_timer(_timer_period(), &alert); + } + +#ifdef DEBUG_HOOPLE_SERVICE + time_stamp next_report(10 * SECOND_ms); +#endif + + while (!alert.is_defunct()) { +#ifdef DEBUG_HOOPLE_SERVICE + if (time_stamp() >= next_report) { + printf("%s: shout out from my main thread yo.\n", _global_argv[0]); + next_report.reset(10 * SECOND_ms); + } +#endif + time_control::sleep_ms(42); + } + alert.handle_shutdown(); + return true; +} + +/* +#ifdef __WIN32__ +bool hoople_service::launch_event_loop(hoople_service &alert, + const astring &app_name, int timer_period) +{ + if (!alert.setup(app_name, timer_period)) return false; + alert.handle_startup(); + + if (timer_period) + program_wide_timer().set_timer(timer_period, this); + + MSG msg; + msg.hwnd = 0; msg.message = 0; msg.wParam = 0; msg.lParam = 0; + while (!alert.is_defunct() && (GetMessage(&msg, NIL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + alert.handle_shutdown(); + + return true; +} +#endif +*/ + +} //namespace. + diff --git a/core/library/application/hoople_service.h b/core/library/application/hoople_service.h new file mode 100644 index 00000000..8f02ff19 --- /dev/null +++ b/core/library/application/hoople_service.h @@ -0,0 +1,137 @@ +#ifndef HOOPLE_SERVICE_CLASS +#define HOOPLE_SERVICE_CLASS + +////////////// +// Name : hoople_service +// Author : Chris Koeritz +////////////// +// Copyright (c) 2000-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +#include +#include +#include + +namespace application { + +//! A platform-independent way to alert a program that it should shut down immediately. +/*! + This can provide a service management feature for graceful shutdown of an + application, allowing it a chance to close its objects cleanly, rather than + just being whacked in the middle of whatever it's doing. + Only one of these objects should be instantiated per program, but the + static methods can be used from anywhere in the program. +*/ + +class hoople_service +: public virtual basis::root_object, public timely::timeable +{ +public: + hoople_service(); + //!< constructor does very little; setup() is what begins operation. + + virtual ~hoople_service(); + + DEFINE_CLASS_NAME("hoople_service"); + + bool setup(const basis::astring &app_name, int timer_period = 0); + //!< constructs a hoople_service for the "app_name" specified. + /*!< this can be any string, although it might be processed for certain + operating systems. also, for close_this_program() to work properly, it + must be the application's basename. the "timer_period" specifies how + frequently to invoke the handle_timer() method during runtime. if it's + zero, then no timer will be used. */ + + static bool is_defunct() { return _defunct(); } + //!< returns true if the object has been marked as defunct. + /*!< this means that it is either shutting down or soon will be. */ + + static void make_defunct(); + //!< used by the derived class to mark that this object is about to exit. + /*!< note that this can be used anywhere in the program to initiate an + exit of the program. */ + + bool saw_interrupt() { return _saw_interrupt(); } + //!< reports whether the process saw an interrupt from the user. + + // these virtual methods can be overridden by applications derived from the + // hoople_service. they support a graceful shutdown process by which + // applications can be alerted that they must shutdown, allowing them to take + // care of releasing resources beforehand. + + virtual void handle_startup(); + //!< this function is called once the program has begun operation. + + virtual void handle_shutdown(); + //!< called during the program's shutdown process. + /*!< this is invoked just prior to the destruction of this class which is + also just before the shutdown of the program overall. in this method, + the derived object must ensure that any threads the program started get + stopped, that any opened files get closed, and that any other resources + are released. this is the application's last chance to clean up. */ + + virtual void handle_timer(); + //!< called periodically if a timer period was specified. + + // static methods that can be used by the program for starting up or for + // graceful shutdown. + +//why? + static bool launch_console(hoople_service &alert, const basis::astring &app_name, + int timer_period = 0); + //!< this is used to begin execution of a console mode application. + /*!< this method does not do anything except sit while the extant threads + are in play. it will not return until the program must exit, as caused + by close_this_program() or close_application(). */ + +#if 0 //not implemented. +#ifdef __WIN32__ + static bool launch_event_loop(hoople_service &alert, + const basis::astring &app_name, int timer_period = 0); + //!< launches by starting up a windowing event loop. + /*!< this is appropriate for programs that are windowed and must + continually process window events. */ +#endif +#endif + + static void close_this_program(); + //!< causes this particular application to begin shutting down. + /*!< this is a static method available for programs that support the + hoople_service's graceful shutdown process. it causes the application + to begin the shutdown. */ + + static bool close_application(const basis::astring &app_name); + //!< attempts to close the application named "app_name". + /*!< this can only be done if this program possesses sufficient rights to + zap that program. */ + + // internal methods not to be used by outside objects. + + static void handle_OS_signal(int sig_id); + //!< processes the signal from the OS when its time to shut down. + +private: + static bool &_saw_interrupt(); //!< did we see a break from the user? + static basis::astring &_app_name(); //!< the of this application. + static bool &_defunct(); //!< is the program shutting down? + static int &_timer_period(); //!< rate at which timer goes off. + + virtual void handle_timer_callback(); //!< invoked by the timer driver. + + // not appropriate. + hoople_service(const hoople_service &); + hoople_service &operator =(const hoople_service &); +}; + +} //namespace. + +#endif // outer guard. + diff --git a/core/library/application/launch_manager.cpp b/core/library/application/launch_manager.cpp new file mode 100644 index 00000000..7e07c1e6 --- /dev/null +++ b/core/library/application/launch_manager.cpp @@ -0,0 +1,804 @@ +/*****************************************************************************\ +* * +* Name : launch_manager +* Author : Chris Koeritz +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "hoople_service.h" +#include "launch_manager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace configuration; +using namespace filesystem; +using namespace loggers; +using namespace processes; +using namespace structures; +using namespace textual; +using namespace timely; + +namespace application { + +#define DEBUG_PROCESS_MANAGER + // uncomment for verbose diagnostics. + +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +const int CHECK_INTERVAL = 4 * SECOND_ms; + // this is how frequently the checking thread executes to ensure that + // processes are gone when they should be. + +const int GRACEFUL_SLACK = 90 * SECOND_ms; + // the length of time before a graceful shutdown devolves into a forced + // shutdown. + +const int MAXIMUM_INITIAL_APP_WAIT = 4 * SECOND_ms; + // this is the longest we bother to wait for a process we just started. + // if it hasn't begun by then, we decide it will never do so. + +const int STARTUP_APPS_DELAY_PERIOD = 2 * SECOND_ms; + // we delay for this long before the initial apps are started. + +const int MAXIMUM_REQUEST_PAUSE = 42 * SECOND_ms; + // the longest we will ever wait for a response to be generated based on + // our last request. + +// these are concurrency control macros for the lists managed here. +#define LOCK_CONFIG auto_synchronizer l(*_config_lock) +#define LOCK_ZOMBIES auto_synchronizer l(*_zombie_lock) +#define LOCK_KIDS auto_synchronizer l(*_scamp_lock) + +// error messages. +#ifdef DEBUG_PROCESS_MANAGER + #define COMPLAIN_APPLICATION \ + LOG(astring("the application called ") + app_name + " could not be found.") + #define COMPLAIN_PRODUCT \ + LOG(astring("the section for ") + product + " could not be found.") +#else + #define COMPLAIN_APPLICATION {} + #define COMPLAIN_PRODUCT {} +#endif + +////////////// + +class launch_manager_thread : public ethread +{ +public: + launch_manager_thread(launch_manager &parent) + : ethread(CHECK_INTERVAL, ethread::SLACK_INTERVAL), + _parent(parent) {} + virtual ~launch_manager_thread() {} + + virtual void perform_activity(void *) + { _parent.push_timed_activities(_processes); } + +private: + launch_manager &_parent; // the owner of the object. + process_entry_array _processes; // will be filled as needed. +}; + +////////////// + +class graceful_record +{ +public: + astring _product; // the product name the app is listed under. + astring _app_name; // the application's name. + time_stamp _started; // when the graceful shutdown started. + int _pid; // the particular process id for this app. + int _level; // the shutdown ordering specifier. + + graceful_record(int pid = 0, const astring &product = "", + const astring &app_name = "", int level = 0) + : _product(product), _app_name(app_name), _pid(pid), _level(level) {} +}; + +class graceful_array : public array {}; + +////////////// + +launch_manager::launch_manager(configured_applications &config) +: _configs(config), + _started_initial_apps(false), + _checker(new launch_manager_thread(*this)), + _config_lock(new mutex), + _going_down(new graceful_array), + _zombie_lock(new mutex), + _our_kids(new graceful_array), + _scamp_lock(new mutex), + _stop_launching(false), + _startup_time(new time_stamp(STARTUP_APPS_DELAY_PERIOD)), + _procs(new process_control), + _gag_exclusions(new string_set), + _tracking_exclusions(new string_set) +{ +// FUNCDEF("constructor"); + + // start the application checking thread. + _checker->start(NIL); + + _checker->reschedule(200); // make it start pretty quickly. +} + +launch_manager::~launch_manager() +{ + FUNCDEF("destructor"); + stop_everything(); + + WHACK(_checker); + WHACK(_going_down); + WHACK(_our_kids); + WHACK(_scamp_lock); + WHACK(_zombie_lock); + WHACK(_config_lock); + WHACK(_startup_time); + WHACK(_procs); + WHACK(_gag_exclusions); + WHACK(_tracking_exclusions); + LOG("launch_manager is now stopped."); +} + +void launch_manager::add_gag_exclusion(const astring &exclusion) +{ *_gag_exclusions += exclusion; } + +void launch_manager::add_tracking_exclusion(const astring &exclusion) +{ *_tracking_exclusions += exclusion; } + +const char *launch_manager::outcome_name(const outcome &to_name) +{ + switch (to_name.value()) { + case FILE_NOT_FOUND: return "FILE_NOT_FOUND"; + case NO_ANCHOR: return "NO_ANCHOR"; + case NO_PRODUCT: return "NO_PRODUCT"; + case NO_APPLICATION: return "NO_APPLICATION"; + case BAD_PROGRAM: return "BAD_PROGRAM"; + case LAUNCH_FAILED: return "LAUNCH_FAILED"; + case NOT_RUNNING: return "NOT_RUNNING"; + case FROZEN: return "FROZEN"; + default: return common::outcome_name(to_name); + } +} + +void launch_manager::stop_everything() +{ + _stop_launching = true; // at least deny any connected clients. + stop_all_kids(); // shut down all programs that we started. + _checker->stop(); // stop our thread. +} + +void launch_manager::stop_all_kids() +{ + FUNCDEF("stop_all_kids"); + _stop_launching = true; // set this for good measure to keep clients out. + LOG("zapping any active sub-processes prior to exit."); + + // now we wait for the process closures to take effect. we are relying on + // the graceful shutdown devolving to a process zap and the timing is + // rooted around that assumption. + + for (int lev = 100; lev >= 0; lev--) { + // loop from our highest level to our lowest for the shutdown. +#ifdef DEBUG_PROCESS_MANAGER + LOG(a_sprintf("level %d", lev)); +#endif + bool zapped_any = false; + { + // this shuts down all the child processes we've started at this level. + LOCK_KIDS; // lock within this scope. + for (int i = _our_kids->length() - 1; i >= 0; i--) { + // now check each record and see if it's at the appropriate level. + graceful_record &grace = (*_our_kids)[i]; + if (lev == grace._level) { + // start a graceful shutdown. + zap_process(grace._product, grace._app_name, true); + // remove it from our list. + _our_kids->zap(i, i); + zapped_any = true; // set our flag. + } + } + } + int num_dying = 1; // go into the loop once at least. + +#ifdef DEBUG_PROCESS_MANAGER + time_stamp next_print(4 * SECOND_ms); +#endif + + while (num_dying) { +#ifdef DEBUG_PROCESS_MANAGER + if (time_stamp() >= next_print) { + LOG("waiting..."); + next_print.reset(4 * SECOND_ms); + } +#endif + + // while there are any pending process zaps, we will wait here. this + // will hose us but good if the processes aren't eventually cleared up, + // but that shouldn't happen. + + { + LOCK_ZOMBIES; + num_dying = _going_down->length(); + } + + if (!num_dying) break; // jump out of loop. + + _checker->reschedule(0); // make thread check as soon as possible. + + time_control::sleep_ms(40); + } +#ifdef DEBUG_PROCESS_MANAGER + LOG("done waiting..."); +#endif + } +} + +void launch_manager::launch_startup_apps() +{ + FUNCDEF("launch_startup_apps"); + + // read the startup section. + string_table startup_info; + { + LOCK_CONFIG; + if (!_configs.find_section(_configs.STARTUP_SECTION(), startup_info)) { + // if there's no startup section, we do nothing right now. + LOG("the startup section was not found!"); + return; + } + } +#ifdef DEBUG_PROCESS_MANAGER + LOG(astring("table has: ") + startup_info.text_form()); +#endif + + for (int i = 0; i < startup_info.symbols(); i++) { + astring app = startup_info.name(i); + if (app.equal_to(configured_applications::STARTUP_APP_NAME())) continue; + // skip bogus name that keeps the section present. + astring info = startup_info[i]; + LOG(astring("launching application ") + app + "..."); + // parse the items that are in the entry for this program. + astring product, parms; + bool one_shot; + if (!configured_applications::parse_startup_entry(info, product, parms, + one_shot)) { + LOG("the startup entry was not malformed; we could not parse it!"); + continue; + } + + LOG(app + a_sprintf(" is %ssingle shot.", one_shot? "" : "not ")); + + // now try to send the program off on its way. + launch_now(product, app, parms); + + if (one_shot) { + // it's only supposed to be started once, so now toss out the entry. + remove_from_startup(product, app); + } + } +} + +outcome launch_manager::launch_now(const astring &product, + const astring &app_name, const astring ¶meters) +{ + FUNCDEF("launch_now"); + LOG(astring("product \"") + product + "\", application \"" + app_name + + (parameters.length() ? astring("\"") + : astring("\", parms=") + parameters)); + + if (_stop_launching) { + // if the application is one of the exceptions to the gag rule, then + // we will still launch it. otherwise, we'll ignore this request. + if (_gag_exclusions->member(app_name)) { + // this is one of the apps that can still be launched when gagged. + } else { + LOG(astring("application \"") + app_name + "\" cannot be launched;" + + parser_bits::platform_eol_to_chars() + "launching services have been " + "shut down."); + return LAUNCH_FAILED; + } + } + + astring entry_found; + int level; + { + LOCK_CONFIG; + + // get the specific entry for the program they want. + entry_found = _configs.find_program(product, app_name, level); + if (!entry_found) { + if (!_configs.product_exists(product)) { + // return more specific error for missing product. + COMPLAIN_PRODUCT; + return NO_PRODUCT; + } + COMPLAIN_APPLICATION; + return NO_APPLICATION; + } + } + + filename existence_check(entry_found); + if (!existence_check.exists()) { + LOG(astring("file or path wasn't found for ") + entry_found + "."); + return FILE_NOT_FOUND; + } + + basis::un_int kid = 0; + int ret = launch_process::run(entry_found, parameters, + launch_process::RETURN_IMMEDIATELY | launch_process::HIDE_APP_WINDOW, kid); + if (!ret) { + // hey, it worked! so now make sure we track its lifetime... + + if (_tracking_exclusions->member(app_name)) { + // this is one of the apps that we don't track. if it's still + // running when we're shutting down, it's not our problem. + return OKAY; + } + + if (kid) { + // we were told the specific id for the process we started. + LOCK_KIDS; + LOG(a_sprintf("adding given process id %d for app %s at level %d.", + kid, app_name.s(), level)); + graceful_record to_add(kid, product, app_name, level); + *_our_kids += to_add; + return OKAY; + } +#ifdef DEBUG_PROCESS_MANAGER + LOG("was not told child process id!!!"); +#endif + // we weren't told the process id; let's see if we can search for it. + int_set pids; + time_stamp give_it_up(MAXIMUM_INITIAL_APP_WAIT); + while (give_it_up > time_stamp()) { + // find the process id for the program we just started. + if (find_process(app_name, pids)) break; + time_control::sleep_ms(10); // pause to see if we can find it yet. + } + + if (time_stamp() >= give_it_up) { + // we could not launch it for some reason, or our querier has failed. + // however, as far as we know, it launched successfully. if the id is + // missing, then that's not really our fault. we will just not be able + // to track the program, possibly because it's already gone. + LOG(astring("no process found for product \"") + product + + "\", application \"" + app_name + "\"; not adding " + "tracking record."); + return OKAY; + } + + LOCK_KIDS; + + // add all the processes we found under that name. + for (int i = 0; i < pids.elements(); i++) { + LOG(a_sprintf("adding process id %d for app %s at level %d.", + pids[i], app_name.s(), level)); + graceful_record to_add(pids[i], product, app_name, level); + *_our_kids += to_add; + } + return OKAY; + } + + // if we reached here, things are not good. we must not have been able to + // start the process. + +#ifdef __WIN32__ + if (ret == NO_ERROR) return OKAY; // how would that happen? + else if ( (ret == ERROR_FILE_NOT_FOUND) || (ret == ERROR_PATH_NOT_FOUND) ) { + LOG(astring("file or path wasn't found for ") + app_name + "."); + return FILE_NOT_FOUND; + } else if (ret == ERROR_BAD_FORMAT) { + LOG(astring(app_name) + " was not in EXE format."); + return BAD_PROGRAM; + } else { + LOG(astring("there was an unknown error while trying to run ") + + app_name + "; the error is:" + parser_bits::platform_eol_to_chars() + + critical_events::system_error_text(ret)); + return LAUNCH_FAILED; + } +#else + LOG(astring("error ") + critical_events::system_error_text(ret) + + " occurred attempting to run: " + app_name + " " + parameters); + return FILE_NOT_FOUND; +#endif +} + +outcome launch_manager::launch_at_startup(const astring &product, + const astring &app_name, const astring ¶meters, int one_shot) +{ + FUNCDEF("launch_at_startup"); + LOCK_CONFIG; // this whole function modifies the config file. + +#ifdef DEBUG_PROCESS_MANAGER + LOG(astring("product \"") + product + "\", application \"" + app_name + + (one_shot? astring("\", OneShot") : astring("\", MultiUse"))); +#endif + + // get the specific entry for the program they want. + int level; + astring entry_found = _configs.find_program(product, app_name, level); + if (!entry_found) { + if (!_configs.product_exists(product)) { + // return more specific error for missing product. + COMPLAIN_PRODUCT; + return NO_PRODUCT; + } + COMPLAIN_APPLICATION; + return NO_APPLICATION; + } + + if (!_configs.add_startup_entry(product, app_name, parameters, one_shot)) { + // most likely problem is that it was already there. + return EXISTING; + } + + return OKAY; +} + +outcome launch_manager::remove_from_startup(const astring &product, + const astring &app_name) +{ + FUNCDEF("remove_from_startup"); + LOCK_CONFIG; // this whole function modifies the config file. + +#ifdef DEBUG_PROCESS_MANAGER + LOG(astring("product \"") + product + "\", application \"" + app_name + "\""); +#endif + + // get the specific entry for the program they want. + int level; + astring entry_found = _configs.find_program(product, app_name, level); + if (!entry_found) { + if (!_configs.product_exists(product)) { + // return more specific error for missing product. + COMPLAIN_PRODUCT; + return NO_PRODUCT; + } + COMPLAIN_APPLICATION; + return NO_APPLICATION; + } +//hmmm: is product required for this for real? + + if (!_configs.remove_startup_entry(product, app_name)) { + // the entry couldn't be removed, probably doesn't exist. + return NO_APPLICATION; + } + + return OKAY; +} + +outcome launch_manager::query_application(const astring &product, + const astring &app_name) +{ + FUNCDEF("query_application"); +#ifdef DEBUG_PROCESS_MANAGER + LOG(astring("product \"") + product + "\", application \"" + app_name + "\""); +#endif + + { + LOCK_CONFIG; + // get the specific entry for the program they want. + int level; + astring entry_found = _configs.find_program(product, app_name, level); + if (!entry_found) { + if (!_configs.product_exists(product)) { + // return more specific error for missing product. + COMPLAIN_PRODUCT; + return NO_PRODUCT; + } + COMPLAIN_APPLICATION; + return NO_APPLICATION; + } + } + + // now seek that program name in the current list of processes. + astring program_name(app_name); + program_name.to_lower(); + + int_set pids; + if (!find_process(app_name, pids)) + return NOT_RUNNING; + return OKAY; +} + +outcome launch_manager::start_graceful_close(const astring &product, + const astring &app_name) +{ + FUNCDEF("start_graceful_close"); +//hmmm: record this app as one we need to watch. + + { + // find that program name to make sure it's not already shutting down. + LOCK_ZOMBIES; + + for (int i = _going_down->length() - 1; i >= 0; i--) { + graceful_record &grace = (*_going_down)[i]; + if (grace._app_name.iequals(app_name)) { + return OKAY; + } + } + } + + if (!hoople_service::close_application(app_name)) + return NO_ANCHOR; + + int_set pids; + if (!find_process(app_name, pids)) { + LOG(astring("Failed to find process id for [") + app_name + + astring("], assuming it has already exited.")); + return OKAY; + } + + { + // add all the process ids, just in case there were multiple instances + // of the application somehow. + LOCK_ZOMBIES; + for (int i = 0; i < pids.elements(); i++) { + graceful_record to_add(pids[i], product, app_name); + *_going_down += to_add; + } + } + + return OKAY; +} + +bool launch_manager::get_processes(process_entry_array &processes) +{ + FUNCDEF("get_processes"); + if (!_procs->query_processes(processes)) { + LOG("failed to query processes!"); + return false; + } + return true; +} + +bool launch_manager::find_process(const astring &app_name_in, int_set &pids) +{ +// FUNCDEF("find_process"); + pids.clear(); + process_entry_array processes; + if (!get_processes(processes)) return false; + return process_control::find_process_in_list(processes, app_name_in, pids); +} + +outcome launch_manager::zap_process(const astring &product, + const astring &app_name_key, bool graceful) +{ + FUNCDEF("zap_process"); + +#ifdef DEBUG_PROCESS_MANAGER + LOG(astring("product \"") + product + "\", application \"" + app_name_key + + (graceful? "\", Graceful Close" : "\", Brutal Close")); +#endif + + if (_tracking_exclusions->member(app_name_key)) { + // the non-tracked applications are never reported as running since they're + // not allowed to be zapped anyhow. + return NOT_RUNNING; + } + + // use the real program name from here onward. + astring app_name; + { + LOCK_CONFIG; + + // get the specific entry for the program they want. + int level; + app_name = _configs.find_program(product, app_name_key, level); + if (!app_name) { + if (!_configs.product_exists(product)) { + // return more specific error for missing product. + COMPLAIN_PRODUCT; + return NO_PRODUCT; + } + COMPLAIN_APPLICATION; + return NO_APPLICATION; + } + // truncate the directory and just use the base. + app_name = filename(app_name).basename(); + } + + // if they want a graceful close, start by trying that. if it appears to + // have succeeded, then we exit and let the thread take care of ensuring + // the app goes down. + outcome to_return = NOT_RUNNING; + if (graceful) + to_return = start_graceful_close(product, app_name); + if (to_return == OKAY) + return OKAY; // maybe finished the close. + + // they want a harsh close of this application or the graceful close failed. + astring program_name(app_name); + int_set pids; + if (!find_process(program_name, pids)) { +#ifdef DEBUG_PROCESS_MANAGER + LOG(program_name + " process was not running.") +#endif + return NOT_RUNNING; + } + + // search for the application in the process list. + bool failed = false; + for (int i = 0; i < pids.elements(); i++) { + bool ret = _procs->zap_process(pids[i]); + if (ret) { + LOG(astring(astring::SPRINTF, "Killed process %d [", + pids[i]) + program_name + astring("]")); + } else { + LOG(astring(astring::SPRINTF, "Failed to zap process %d [", + pids[i]) + program_name + astring("]")); + } + if (!ret) failed = true; + } +// kind of a bizarre return, but whatever. it really should be some +// failure result since the zap failed. + return failed? ACCESS_DENIED : OKAY; +} + +outcome launch_manager::shut_down_launching_services(const astring &secret_word) +{ + FUNCDEF("shut_down_launching_services"); + LOG("checking secret word..."); +//hmmm: changing the secret word here. + if (secret_word.equal_to("MarblesAreRoundishYo")) { + LOG("it's correct; ending launch capabilities."); + } else { + LOG("the secret word is wrong. continuing normal operation."); + return BAD_PROGRAM; + } + _stop_launching = true; + return OKAY; +} + +outcome launch_manager::reenable_launching_services(const astring &secret_word) +{ + FUNCDEF("reenable_launching_services"); + LOG("checking secret word..."); + if (secret_word.equal_to("MarblesAreRoundishYo")) { + LOG("it's correct; resuming launch capabilities."); + } else { + LOG("the secret word is wrong. continuing with prior mode."); + return BAD_PROGRAM; + } + _stop_launching = false; + return OKAY; +} + +#define GET_PROCESSES \ + if (!retrieved_processes) { \ + retrieved_processes = true; \ + if (!get_processes(processes)) { \ + LOG("failed to retrieve process list from OS!"); \ + return; /* badness. */ \ + } \ + } + +void launch_manager::push_timed_activities(process_entry_array &processes) +{ + FUNCDEF("push_timed_activities"); + + // make sure we started the applications that were slated for execution at + // system startup time. we wait on this until the first thread activation + // to give other processes some breathing room right at startup time. + // also, it then doesn't block the main service thread. + if (!_started_initial_apps && (*_startup_time <= time_stamp()) ) { + // launch all the apps that are listed for system startup. + LOG("starting up the tasks registered for system initiation."); + launch_startup_apps(); + _started_initial_apps = true; + } + + if (!_started_initial_apps) { + _checker->reschedule(200); + // keep hitting this function until we know we can relax since the + // startup apps have been sent off. + } + + bool retrieved_processes = false; + + { + // loop over the death records we've got and check on the soon to be gone. + LOCK_ZOMBIES; + + for (int i = _going_down->length() - 1; i >= 0; i--) { + graceful_record &grace = (*_going_down)[i]; + + GET_PROCESSES; // load them if they hadn't been. + + int_set pids; + if (!process_control::find_process_in_list(processes, grace._app_name, + pids)) { + // the app can't be found as running, so whack the record for it. +#ifdef DEBUG_PROCESS_MANAGER + LOG(astring("cannot find app ") + grace._app_name + + " as running still; removing its dying record"); +#endif + _going_down->zap(i, i); + continue; + } + if (!pids.member(grace._pid)) { + // that particular instance exited on its own, so whack the record. +#ifdef DEBUG_PROCESS_MANAGER + LOG(astring("app ") + grace._app_name + + " exited on its own; removing its dying record"); +#endif + _going_down->zap(i, i); + continue; + } + + if (grace._started <= time_stamp(-GRACEFUL_SLACK)) { + // time to force it. + LOG(astring("Application ") + grace._app_name + " is unresponsive to " + "the graceful shutdown request. Now zapping it instead."); + if (!_procs->zap_process(grace._pid)) + LOG("Devolved graceful shutdown failed as zap_process also."); + _going_down->zap(i, i); + continue; + } + + // it's not time to dump this one yet, so keep looking at others. + } + } + + { + // now loop over the list of our active kids and make sure that they are + // all still running. if they aren't, then toss the record out. + LOCK_KIDS; + + for (int i = _our_kids->length() - 1; i >= 0; i--) { + graceful_record &grace = (*_our_kids)[i]; + + GET_PROCESSES; // load them if they hadn't been. + + int_set pids; + if (!process_control::find_process_in_list(processes, grace._app_name, + pids)) { + // the app can't be found as running, so whack the record for it. +#ifdef DEBUG_PROCESS_MANAGER + LOG(astring("cannot find kid ") + grace._app_name + + " as still running; removing its normal record"); +#endif + _our_kids->zap(i, i); + continue; + } + if (!pids.member(grace._pid)) { + // that particular instance exited on its own, so whack the record. +#ifdef DEBUG_PROCESS_MANAGER + LOG(astring("kid ") + grace._app_name + + " exited on its own; removing its normal record"); +#endif + _our_kids->zap(i, i); + continue; + } + + // this kid is still going, so keep its record. + } + } +} + +} //namespace. + diff --git a/core/library/application/launch_manager.h b/core/library/application/launch_manager.h new file mode 100644 index 00000000..33e4aef8 --- /dev/null +++ b/core/library/application/launch_manager.h @@ -0,0 +1,186 @@ + +// current bad issues: +// +// this class does not really provide a notion of the process name AND process id as being +// a pair one can operate on. mostly it assumes a bunch of singletons or that it's okay +// to whack all of them. +// that could be fixed by making the procname+procid pair into a unit in all functions, +// and make kill_all be a specialization of that. + + +#ifndef LAUNCH_MANAGER_CLASS +#define LAUNCH_MANAGER_CLASS + +/*****************************************************************************\ +* * +* Name : launch_manager +* Author : Chris Koeritz +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace application { + +class graceful_array; +class launch_manager_thread; + +//! Provides methods for starting, stopping and checking on processes. +/*! + This includes support for graceful shutdowns and background handling of + exiting processes. +*/ + +class launch_manager : public virtual basis::root_object +{ +public: + launch_manager(processes::configured_applications &config); + //!< the launch_manager needs a configuration set to work with. + + virtual ~launch_manager(); + + DEFINE_CLASS_NAME("launch_manager"); + + enum outcomes { + OKAY = basis::common::OKAY, + EXISTING = basis::common::EXISTING, + //!< the entry already exists and overwriting is disallowed. + ACCESS_DENIED = basis::common::ACCESS_DENIED, + //!< the requested operation was not permitted. + + DEFINE_API_OUTCOME(FILE_NOT_FOUND, -53, "The file specified for the " + "application doesn't exist, as far as we can tell"), + DEFINE_API_OUTCOME(NO_PRODUCT, -54, "The product specified does not exist"), + DEFINE_API_OUTCOME(NO_APPLICATION, -55, "The application is not listed for " + "the product"), + DEFINE_API_OUTCOME(NOT_RUNNING, -56, "The program is not currently active, " + "according to the OS process list"), + DEFINE_API_OUTCOME(BAD_PROGRAM, -57, "The file existed but doesn't appear " + "to be a valid program image"), + DEFINE_API_OUTCOME(NO_ANCHOR, -58, "This occurs when the graceful shutdown " + "process cannot find the special anchor window that implements the " + "client side of a graceful shutdown"), + DEFINE_API_OUTCOME(LAUNCH_FAILED, -59, "The program existed and seemed " + "valid but its launch failed for some reason"), + DEFINE_API_OUTCOME(FROZEN, -60, "The application is broken somehow; the " + "system reports it as non-responsive"), + // note that these values are reversed, since the ordering is negative. + FIRST_OUTCOME = FROZEN, // hold onto start of range. + LAST_OUTCOME = FILE_NOT_FOUND // hold onto end of range. + }; + + static const char *outcome_name(const basis::outcome &to_name); + //!< returns the text associated with "to_name". + + basis::outcome launch_now(const basis::astring &product, const basis::astring &app_name, + const basis::astring ¶meters); + //!< starts the application "app_name" now. + /*!< causes the program with "app_name" that's listed for the "product" to + be started with the "parameters". this can fail if the application + isn't listed or if the program can't be found or if the process can't + be created. */ + + basis::outcome launch_at_startup(const basis::astring &product, const basis::astring &app_name, + const basis::astring ¶meters, int one_shot); + //!< records an entry for the "app_name" to be launched at startup. + /*!< this does not launch the application now. if "one_shot" is true, the + application will only be started once and then removed from the list. */ + + basis::outcome remove_from_startup(const basis::astring &product, const basis::astring &app_name); + //!< takes the "app_name" out of the startup list. + + basis::outcome query_application(const basis::astring &product, const basis::astring &app_name); + //!< retrieves the current state of the program with "app_name". + + basis::outcome zap_process(const basis::astring &product, const basis::astring &app_name, + bool graceful); + //!< zaps the process named "app_name". + /*!< if "graceful" is true, then the clean shutdown process is attempted. + otherwise the application is harshly terminated. */ + + void add_gag_exclusion(const basis::astring &exclusion); + //!< add an application that isn't subject to gagging. + /*!< if the launch_manager is gagged, the excluded applications can + still be started. */ + void add_tracking_exclusion(const basis::astring &exclusion); + //!< apps that aren't tracked when running. + /*!< if a zap is attempted on one of these applications, then + the process is not shut down. */ + + basis::outcome shut_down_launching_services(const basis::astring &secret_word); + //!< closes down the ability of clients to launch applications. + /*!< the checking and shut down functions continue to operate. this is + intended for use prior to a restart of the application controller, in + order to ensure that no remote clients can start new servers on this + machine. */ + + basis::outcome reenable_launching_services(const basis::astring &secret_word); + //!< undoes the gagging that the above "shut_down" function does. + /*!< this allows the launch_manager to continue operating normally. */ + + bool services_disabled() const { return _stop_launching; } + //!< returns true if the capability to launch new processes is revoked. + + void push_timed_activities(processes::process_entry_array &processes); + //!< keeps any periodic activities going. + /*!< this includes such tasks as zapping processes that have gone beyond + their time limit for graceful shutdown. */ + + void stop_everything(); + //!< closes down the operation of this object. + +private: + processes::configured_applications &_configs; //!< manages the entries for companies. + bool _started_initial_apps; //!< true if we launched the boot apps. + launch_manager_thread *_checker; //!< keeps periodic activities going. + basis::mutex *_config_lock; //!< the synchronizer for our configuration entries. + graceful_array *_going_down; //!< record of graceful shutdowns in progress. + basis::mutex *_zombie_lock; //!< the synchronizer for the dying processes. + graceful_array *_our_kids; //!< the processes we've started. + basis::mutex *_scamp_lock; //!< the synchronizer for the list of children. + bool _stop_launching; //!< true if no launches should be allowed any more. + timely::time_stamp *_startup_time; + //!< the time we feel it's safe to launch the startup apps. + /*!< we delay this some so that the launch_manager doesn't immediately + soak up too much CPU. */ + processes::process_control *_procs; //!< gives us access to the process list. + structures::string_set *_gag_exclusions; //!< apps that aren't subject to gag law. + structures::string_set *_tracking_exclusions; //!< apps that aren't tracked when running. + + bool get_processes(processes::process_entry_array &processes); + //!< grabs the list of "processes" or returns false. + + bool find_process(const basis::astring &app_name, structures::int_set &pids); + //!< locates any instances of "app_name" and returns process ids in "pids". + + basis::outcome start_graceful_close(const basis::astring &product, const basis::astring &app_name); + //!< attempts to close the "app_name". + /*!< if the application doesn't shut down within the time limit, it is + eventually zapped harshly. */ + + void launch_startup_apps(); + //!< iterates over the list of startup apps and creates a process for each. + + void stop_all_kids(); + //!< closes all dependent processes when its time to stop the service. +}; + +} //namespace. + +#endif + diff --git a/core/library/application/makefile b/core/library/application/makefile new file mode 100644 index 00000000..758240c5 --- /dev/null +++ b/core/library/application/makefile @@ -0,0 +1,12 @@ +include cpp/variables.def + +PROJECT = application +TYPE = library +SOURCE = application_shell.cpp callstack_tracker.cpp command_line.cpp dll_root.cpp \ + hoople_service.cpp launch_manager.cpp memory_checker.cpp redirecter.cpp shared_memory.cpp \ + singleton_application.cpp windoze_helper.cpp +TARGETS = application.lib +LOCAL_LIBS_USED = basis + +include cpp/rules.def + diff --git a/core/library/application/memory_checker.cpp b/core/library/application/memory_checker.cpp new file mode 100644 index 00000000..2426c785 --- /dev/null +++ b/core/library/application/memory_checker.cpp @@ -0,0 +1,414 @@ + + + +/*****************************************************************************\ +* * +* Name : memory_checker * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +// note: parts of this have been around since at least 1998, but this code was +// newly revised for memory checking in february of 2007. --cak + +#ifdef ENABLE_MEMORY_HOOK + +#include "definitions.h" +#include "log_base.h" +#include "memory_checker.h" +#include "mutex.h" +#include "utility.h" + +#include +#include +#include + +const int MAXIMUM_HASH_SLOTS = 256 * KILOBYTE; + // that's a whole lot of slots. this number is basically multiplied by + // the sizeof(memory_bin) to get full memory footprint. + +const int SINGLE_LINE_SIZE_ESTIMATE = 200; + // we are guessing that the average line of memory printout will take + // this many characters. that includes the size, the pointer value, + // and the location and line number. + +const int RESERVED_AREA = 1000; + // we reserve this much space in the string generated for the memory bin + // dumps. it will be used for adding more information to the string. + +#define CLEAR_ALLOCATED_MEMORY + // uncomment this to ensure that new memory gets its contents set to zero. + // neither new nor malloc does this, but it can help finding bugs from + // people re-using deallocated memory. + +#define MEMORY_CHECKER_STATISTICS + // uncomment to enable code that analyzes how many allocations were new and + // so forth. this will make the object run a bit slower. + +//#define DEBUG_MEMORY_CHECKER + // uncomment for super noisy version. + +////////////// + +// define the replacement new and delete operators. + +#include +void *operator new(size_t size, char *file, int line) throw (std::bad_alloc) +{ return program_wide_memories().provide_memory(size, file, line); } +#include + +void operator delete(void *ptr) throw () +{ program_wide_memories().release_memory(ptr); } + +////////////// + +// memlink is one link in a chain of memories. it's singly linked, so our +// algorithms have to explicitly remember the parent. + +class memlink +{ +public: + void *_chunk; //!< the chunk of memory held (not real address). + /*!< NOTE: we store the chunk as it looks to the outside world, rather + than using its real address. this eliminates a bit of ambiguity in the + code. */ + memlink *_next; //!< the next memory wrapper in the list. + int _size; //!< the size of the chunk delivered. + char *_where; //!< the name of the place that created it. + int _line; //!< the line number where the item was allocated. +#ifdef ENABLE_CALLSTACK_TRACKING + char *_stack; //!< records the stack seen at time of allocation. +#endif + + void construct(void *ptr, int size, char *where, int line) { + _next = NIL; + _chunk = ptr; + _size = size; + _where = strdup(where); // uses malloc, not new, so we're safe. + if (strlen(_where) > SINGLE_LINE_SIZE_ESTIMATE - 40) { + // if we will not have room for the full line, we crop it. + _where[SINGLE_LINE_SIZE_ESTIMATE - 40] = '\0'; + } + _line = line; +#ifdef ENABLE_CALLSTACK_TRACKING + _stack = program_wide_stack_trace().full_trace(); +///printf("stack here:\n%s", _stack); +#endif + } + + void destruct() { + free(_chunk); _chunk = NIL; + free(_where); _where = NIL; + _next = NIL; + _size = 0; + _line = 0; +#ifdef ENABLE_CALLSTACK_TRACKING + free(_stack); _stack = NIL; +#endif + } +}; + +////////////// + +//pretty lame here so far. +#ifdef MEMORY_CHECKER_STATISTICS + // simple stats: the methods below will tweak these numbers if memory_checker + // statistics are enabled. ints won't do here, due to the number of + // operations in a long-running program easily overflowing that size. + + // this bank of statistics counts the number of times memory was treated + // a certain way. + double _stat_new_allocations = 0; // this many new allocations occurred. + double _stat_freed_allocations = 0; // number of freed blocks. + // next bank of stats are the sizes of the memory that were stowed, etc. + double _stat_new_allocations_size = 0; // this many bytes got allocated. + double _stat_freed_allocations_size = 0; // this many bytes were freed. +#endif + +////////////// + +//! the memory bin holds a list of chunks of memory in memlink objects. + +class memory_bin +{ +public: + void construct() { + _head = NIL; + _count = 0; + _lock = (mutex_base *)malloc(sizeof(mutex_base)); + _lock->construct(); + } + void destruct() { + _lock->destruct(); + free(_lock); + } + + int count() const { return _count; } + + int record_memory(void *ptr, int size, char *where, int line) { + memlink *new_guy = (memlink *)malloc(sizeof(memlink)); + new_guy->construct(ptr, size, where, line); + _lock->lock(); + // this code has the effect of putting more recent allocations first. + // if they happen to get cleaned up right away, that's nice and fast. + new_guy->_next = _head; + _head = new_guy; + _count++; + _lock->unlock(); + return common::OKAY; // seems to have worked fine. + } + + int release_memory(void *to_release) { + _lock->lock(); + // search the bin to locate the item specified. + memlink *current = _head; // current will scoot through the list. + memlink *previous = NIL; // previous remembers the parent node, if any. + while (current) { + if (current->_chunk == to_release) { +#ifdef MEMORY_CHECKER_STATISTICS + // record that it went away. + _stat_freed_allocations += 1.0; + _stat_freed_allocations_size += current->_size; +#endif +#ifdef DEBUG_MEMORY_CHECKER + printf("found %p listed, removing for %s[%d]\n", to_release, + current->_where, current->_line); +#endif + // unlink this one and clean up; they don't want it now. + if (!previous) { + // this is the head we're modifying. + _head = current->_next; + } else { + // not the head, so there was a valid previous element. + previous->_next = current->_next; + } + // now trash that goner's house. + current->destruct(); + free(current); + _count--; + _lock->unlock(); + return common::OKAY; + } + // the current node isn't it; jump to next node. + previous = current; + current = current->_next; + } +#ifdef DEBUG_MEMORY_CHECKER + printf("failed to find %p listed.\n", to_release); +#endif + _lock->unlock(); + return common::NOT_FOUND; + } + + void dump_list(char *add_to, int &curr_size, int max_size) { + int size_alloc = 2 * SINGLE_LINE_SIZE_ESTIMATE; // room for one line. + char *temp_str = (char *)malloc(size_alloc); + memlink *current = _head; // current will scoot through the list. + while (current) { + temp_str[0] = '\0'; + sprintf(temp_str, "\n\"%s[%d]\", \"size %d\", \"addr %p\"\n", + current->_where, current->_line, current->_size, current->_chunk); + int len_add = strlen(temp_str); + if (curr_size + len_add < max_size) { + strcat(add_to, temp_str); + curr_size += len_add; + } +#ifdef ENABLE_CALLSTACK_TRACKING + len_add = strlen(current->_stack); + if (curr_size + len_add < max_size) { + strcat(add_to, current->_stack); + curr_size += len_add; + } +#endif + current = current->_next; + } + free(temp_str); + } + +private: + memlink *_head; // our first, if any, item. + mutex_base *_lock; // protects our bin from concurrent access. + int _count; // current count of items held. +}; + +////////////// + +class allocation_memories +{ +public: + void construct(int num_slots) { + _num_slots = num_slots; + _bins = (memory_bin *)malloc(num_slots * sizeof(memory_bin)); + for (int i = 0; i < num_slots; i++) + _bins[i].construct(); + } + + void destruct() { + // destroy each bin in our list. + for (int i = 0; i < _num_slots; i++) { + _bins[i].destruct(); + } + free(_bins); + _bins = NIL; + } + + int compute_slot(void *ptr) { + return utility::hash_bytes(&ptr, sizeof(void *)) % _num_slots; + } + + void *provide_memory(int size_needed, char *file, int line) { + void *new_allocation = malloc(size_needed); + // slice and dice pointer to get appropriate hash bin. + int slot = compute_slot(new_allocation); +#ifdef DEBUG_MEMORY_CHECKER + printf("using slot %d for %p\n", slot, new_allocation); +#endif + _bins[slot].record_memory(new_allocation, size_needed, file, line); +#ifdef MEMORY_CHECKER_STATISTICS + _stat_new_allocations += 1.0; + _stat_new_allocations_size += size_needed; +#endif + return new_allocation; + } + + int release_memory(void *to_drop) { + int slot = compute_slot(to_drop); // slice and dice to get bin number. +#ifdef DEBUG_MEMORY_CHECKER + printf("removing mem %p from slot %d.\n", to_drop, slot); +#endif + return _bins[slot].release_memory(to_drop); + } + + //! this returns a newly created string with all current contents listed. + /*! this returns a simple char * pointer that was created with malloc. + the invoker *must* free the returned pointer. we use malloc/free here to + avoid creating a wacky report that contains a lot of new allocations for + the report itself. that seems like it could become a problem. */ + char *report_allocations() { + // count how many allocations we have overall. + int full_count = 0; + for (int i = 0; i < _num_slots; i++) { +///if (_bins[i].count()) printf("%d adding count %d\n", i, _bins[i].count()); + full_count += _bins[i].count(); + } +///printf("full count is %d\n", full_count); + // calculate a guess for how much space we need to show all of those. + int alloc_size = full_count * SINGLE_LINE_SIZE_ESTIMATE + RESERVED_AREA; + char *to_return = (char *)malloc(alloc_size); + to_return[0] = '\0'; + if (full_count) { + strcat(to_return, "===================\n"); + strcat(to_return, "Unfreed Allocations\n"); + strcat(to_return, "===================\n"); + } + int curr_size = strlen(to_return); // how much in use so far. + for (int i = 0; i < _num_slots; i++) { + _bins[i].dump_list(to_return, curr_size, alloc_size - RESERVED_AREA); + } + return to_return; + } + + // this is fairly resource intensive, so don't dump the state out that often. + char *text_form(bool show_outstanding) { + char *to_return = NIL; + if (show_outstanding) { + to_return = report_allocations(); + } else { + to_return = (char *)malloc(RESERVED_AREA); + to_return[0] = '\0'; + } +#ifdef MEMORY_CHECKER_STATISTICS + char *temp_str = (char *)malloc(4 * SINGLE_LINE_SIZE_ESTIMATE); + + sprintf(temp_str, "=================\n"); + strcat(to_return, temp_str); + sprintf(temp_str, "Memory Statistics\n"); + strcat(to_return, temp_str); + sprintf(temp_str, "=================\n"); + strcat(to_return, temp_str); + sprintf(temp_str, "Measurements taken across entire program runtime:\n"); + strcat(to_return, temp_str); + sprintf(temp_str, " %.0f new allocations.\n", _stat_new_allocations); + strcat(to_return, temp_str); + sprintf(temp_str, " %.4f new Mbytes.\n", + _stat_new_allocations_size / MEGABYTE); + strcat(to_return, temp_str); + sprintf(temp_str, " %.0f freed deallocations.\n", + _stat_freed_allocations); + strcat(to_return, temp_str); + sprintf(temp_str, " %.4f freed Mbytes.\n", + _stat_freed_allocations_size / MEGABYTE); + strcat(to_return, temp_str); + + free(temp_str); +#endif + return to_return; + } + +private: + memory_bin *_bins; //!< each bin manages a list of pointers, found by hash. + int _num_slots; //!< the number of hash slots we have. +}; + +////////////// + +void memory_checker::construct() +{ + _mems = (allocation_memories *)malloc(sizeof(allocation_memories)); + _mems->construct(MAXIMUM_HASH_SLOTS); + _unusable = false; + _enabled = true; +} + +void memory_checker::destruct() +{ + if (_unusable) return; // already gone. +if (!_mems) printf("memory_checker::destruct being invoked twice!\n"); + + // show some stats about memory allocation. + char *mem_state = text_form(true); + printf("%s", mem_state); +///uhhh free(mem_state); +//the above free seems to totally die if we allow it to happen. + + _unusable = true; + + _mems->destruct(); + free(_mems); + _mems = NIL; +} + +void *memory_checker::provide_memory(size_t size, char *file, int line) +{ + if (_unusable || !_enabled) return malloc(size); + return _mems->provide_memory(size, file, line); +} + +int memory_checker::release_memory(void *ptr) +{ + if (_unusable || !_enabled) { + free(ptr); + return common::OKAY; + } + return _mems->release_memory(ptr); +} + +char *memory_checker::text_form(bool show_outstanding) +{ + if (_unusable) return strdup("already destroyed memory_checker!\n"); + return _mems->text_form(show_outstanding); +} + +////////////// + +#endif // enable memory hook + + + diff --git a/core/library/application/memory_checker.h b/core/library/application/memory_checker.h new file mode 100644 index 00000000..d899abd4 --- /dev/null +++ b/core/library/application/memory_checker.h @@ -0,0 +1,99 @@ +#ifndef MEMORY_CHECKER_CLASS +#define MEMORY_CHECKER_CLASS + +/*****************************************************************************\ +* * +* Name : memory_checker * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "definitions.h" + +#ifdef ENABLE_MEMORY_HOOK + +#include "build_configuration.h" +uhh + +// forward. +class allocation_memories; +class memory_checker; + +////////////// + +memory_checker BASIS_EXTERN &program_wide_memories(); + //!< a global version of the memory checker to access memory tracking. + /*!< this accesses the singleton object that tracks all memory allocations + occuring in the program via calls to new and delete. it should be used + rather than creating a memory_checker anywhere else. */ + +////////////// + +//! Debugging assistance tool that checks for memory leaks. +/*! + Provides allocation checking for heap memory for C++. This is used as a + replacement for the standard new and delete operations. No object should + really need to deal with this class directly; it is hooked in automatically + unless the ENABLE_MEMORY_HOOK macro is defined. Generally, release builds + should not have this enabled, since it will slow things down tremendously. + NOTE: this object can absolutely not be hooked into callstack tracker, since + this object implements all c++ memory, including the tracker's. This object + will use the backtrace information if it's available. +*/ + +class memory_checker +{ +public: + void construct(); //!< faux constructor for mallocing. + + void destruct(); //!< faux destructor shuts down object. + + //! turn off memory checking. + void disable() { _enabled = false; } + //! turn memory checking back on. + void enable() { _enabled = true; } + //! reports on whether the memory checker is currently in service or not. + bool enabled() const { return _enabled; } + + void *provide_memory(size_t size, char *file, int line); + //!< returns a chunk of memory with the "size" specified. + /*!< this is the replacement method for the new operator. we will be + calling this instead of the compiler provided new. the "file" string + should be a record of the location where this is invoked, such as is + provided by the __FILE__ macro. the "line" should be set to the line + number within the "file", if applicable (use __LINE__). */ + + int release_memory(void *ptr); + //!< drops our record for the memory at "ptr". + /*!< this is the only way to remove an entry from our listings so that + it will not be reported as a leak. we do not currently gather any info + about where the release is invoked. if there was a problem, the returned + outcome will not be OKAY. */ + + char *text_form(bool show_outstanding); + //!< returns a newly allocated string with the stats for this object. + /*!< if "show_outstanding" is true, then all outstanding allocations are + displayed in the string also. the invoker *must* free the returned + pointer. */ + +private: + allocation_memories *_mems; //!< internal object tracks all allocations. + bool _unusable; //!< true after destruct is called. + bool _enabled; //!< true if the object is okay to use. +}; + +#else // enable memory hook. + // this section disables the memory checker entirely. + #define program_wide_memories() + // define a do nothing macro for the global memory tracker. +#endif // enable memory hook. + +#endif // outer guard. + diff --git a/core/library/application/redirecter.cpp b/core/library/application/redirecter.cpp new file mode 100644 index 00000000..78091446 --- /dev/null +++ b/core/library/application/redirecter.cpp @@ -0,0 +1,506 @@ +/*****************************************************************************\ +* * +* Name : stdio_redirecter * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "redirecter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef __UNIX__ + #include + #include +#endif + +using namespace application; +using namespace basis; +using namespace configuration; +using namespace loggers; +using namespace processes; +using namespace textual; + +namespace application { + +const int IO_PAUSE_PERIOD = 50; // sleep for this long between read attempts. + +const int BUFFER_SIZE = 4096; // maximum we will read at once. + +#undef LOG +#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print) + +const char *REDIRECTER_INI = "redirecter.ini"; + // used to report process ids, since there are users that need this + // info currently. + +const char *PROCESS_SECTION = "process_id"; + // the section in the ini file where we store our process ids. +//hmmm: above should be removed and pushed into stdio wrapper. + +////////////// + +class reader_thread : public ethread +{ +public: + reader_thread(stdio_redirecter &parent, bool is_stdout) + : ethread(), _is_stdout(is_stdout), _parent(parent) { + } + + virtual ~reader_thread() { + } + + virtual void perform_activity(void *formal(ptr)) { + while (!should_stop()) { + _parent.std_thread_action(_is_stdout); + } + } + +private: + bool _is_stdout; // if true, then stdout, if false, then stderr. + stdio_redirecter &_parent; +}; + +////////////// + +stdio_redirecter::stdio_redirecter() +: +#ifdef __WIN32__ + _child_in(NIL), _child_out(NIL), _child_err(NIL), + _parent_in(NIL), _parent_out(NIL), _parent_err(NIL), + _app_handle(NIL), +#endif + _command(new astring), + _parms(new astring), + _persistent_result(OKAY), + _stdout_reader(new reader_thread(*this, true)), + _stderr_reader(new reader_thread(*this, false)), + _stdout_queue(new byte_array), + _stderr_queue(new byte_array), + _lock(new mutex), + _process_id(0), + _exit_value(0) +{ +} + +stdio_redirecter::stdio_redirecter(const astring &command, + const astring ¶meters) +: +#ifdef __WIN32__ + _child_in(NIL), _child_out(NIL), _child_err(NIL), + _parent_in(NIL), _parent_out(NIL), _parent_err(NIL), + _app_handle(NIL), +#endif + _command(new astring(command)), + _parms(new astring(parameters)), + _persistent_result(OKAY), + _stdout_reader(new reader_thread(*this, true)), + _stderr_reader(new reader_thread(*this, false)), + _stdout_queue(new byte_array), + _stderr_queue(new byte_array), + _lock(new mutex), + _process_id(0), + _exit_value(0) +{ + outcome ret = create_pipes(); + if (ret != OKAY) { _persistent_result = ret; return; } + ret = launch_program(_process_id); + if (ret != OKAY) { _persistent_result = ret; return; } +} + +stdio_redirecter::~stdio_redirecter() +{ + zap_program(); + _process_id = 0; + + WHACK(_stdout_queue); + WHACK(_stderr_queue); + WHACK(_stdout_reader); + WHACK(_stderr_reader); + WHACK(_command); + WHACK(_parms); + WHACK(_lock); +} + +outcome stdio_redirecter::reset(const astring &command, + const astring ¶meters) +{ + zap_program(); + _process_id = 0; + + *_command = command; + *_parms = parameters; + _persistent_result = OKAY; + + outcome ret = create_pipes(); + if (ret != OKAY) { _persistent_result = ret; return ret; } + ret = launch_program(_process_id); + if (ret != OKAY) { _persistent_result = ret; return ret; } + return ret; +} + +outcome stdio_redirecter::create_pipes() +{ + FUNCDEF("create_pipes"); +#ifdef __UNIX__ + // the input and output here are from the perspective of the parent + // process and not the launched program. + if (pipe(_input_fds)) { + LOG("failure to open an unnamed pipe for input."); + return ACCESS_DENIED; + } + if (pipe(_output_fds)) { + LOG("failure to open an unnamed pipe for output."); + return ACCESS_DENIED; + } + if (pipe(_stderr_fds)) { + LOG("failure to open an unnamed pipe for stderr."); + return ACCESS_DENIED; + } +#elif defined (__WIN32__) + // set up the security attributes structure that governs how the child + // process is created. + SECURITY_ATTRIBUTES sa; + ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); + sa.nLength= sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NIL; + sa.bInheritHandle = true; + + HANDLE in_temp = NIL, out_temp = NIL, err_temp = NIL; + + // create pipes that we will hook up to the child process. these are + // currently inheritable based on the security attributes. + if (!CreatePipe(&_child_in, &in_temp, &sa, 0)) return ACCESS_DENIED; + if (!CreatePipe(&out_temp, &_child_out, &sa, 0)) return ACCESS_DENIED; + if (!CreatePipe(&err_temp, &_child_err, &sa, 0)) return ACCESS_DENIED; + + HANDLE process_handle = GetCurrentProcess(); + // retrieve process handle for use in system calls below. since it's + // a pseudo handle, we don't need to close it. + + // create new handles for the parent process (connected to this object) to + // use. the false indicates that the child should not inherit the properties + // on these because otherwise it cannot close them. + if (!DuplicateHandle(process_handle, in_temp, process_handle, &_parent_in, + 0, false, DUPLICATE_SAME_ACCESS)) return ACCESS_DENIED; + if (!DuplicateHandle(process_handle, out_temp, process_handle, &_parent_out, + 0, false, DUPLICATE_SAME_ACCESS)) return ACCESS_DENIED; + if (!DuplicateHandle(process_handle, err_temp, process_handle, &_parent_err, + 0, false, DUPLICATE_SAME_ACCESS)) return ACCESS_DENIED; + + // close out the handles that we're done with and don't want the child to + // inherit. + CloseHandle(in_temp); + CloseHandle(out_temp); + CloseHandle(err_temp); +#endif + + return OKAY; +} + +outcome stdio_redirecter::launch_program(int &new_process_id) +{ +// FUNCDEF("launch_program"); + new_process_id = 0; +#ifdef __UNIX__ + int fork_ret = fork(); + if (fork_ret == 0) { + // this is the child. + close(_output_fds[1]); // close our *input* pipe's output fd. + dup2(_output_fds[0], 0); // close our stdin and replace with input pipe. + close(_input_fds[0]); // close our *output* pipe's input fd. + dup2(_input_fds[1], 1); // close our stdout and replace with output pipe. + close(_stderr_fds[0]); // close stderr input fd. + dup2(_stderr_fds[1], 2); // close our stderr and pipe it to parent. + // now we want to launch the program for real. + processes::char_star_array parms = launch_process::break_line(*_command, *_parms); + execv(_command->s(), parms.observe()); + // oops. failed to exec if we got to here. + exit(1); + } else { + // this is the parent. + _process_id = fork_ret; // save the child's process id. + new_process_id = _process_id; // set the returned id. + close(_output_fds[0]); // close our *output* pipe's input fd. + close(_input_fds[1]); // close our *input* pipe's output fd. + close(_stderr_fds[1]); // close the child's stderr output side. + // now we should have a set of pipes that talk to the child. + } +#elif defined (__WIN32__) + // set up the startup info struct. + STARTUPINFO si; + ZeroMemory(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.hStdInput = _child_in; + si.hStdOutput = _child_out; + si.hStdError = _child_err; + si.wShowWindow = SW_HIDE; // we'll hide the console window. + + // setup the security attributes for the new process. + SECURITY_DESCRIPTOR *sec_desc = (SECURITY_DESCRIPTOR *)GlobalAlloc + (GPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); + InitializeSecurityDescriptor(sec_desc, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(sec_desc, -1, 0, 0); + LPSECURITY_ATTRIBUTES sec_attr = (LPSECURITY_ATTRIBUTES)GlobalAlloc(GPTR, + sizeof(SECURITY_ATTRIBUTES)); + sec_attr->nLength = sizeof(SECURITY_ATTRIBUTES); + sec_attr->lpSecurityDescriptor = sec_desc; + sec_attr->bInheritHandle = true; + + astring cmd = *_command; + if (cmd[0] != '"') + cmd.insert(0, "\""); + if (cmd[cmd.end()] != '"') + cmd += "\""; + cmd += " "; + cmd += *_parms; + + // fork off the process. + PROCESS_INFORMATION pi; + BOOL success = CreateProcess(NIL, to_unicode_temp(cmd), sec_attr, NIL, + true, CREATE_NEW_CONSOLE, NIL, NIL, &si, &pi); + + // cleanup junk we allocated. + if (sec_attr != NIL) GlobalFree(sec_attr); + if (sec_desc != NIL) GlobalFree(sec_desc); + + if (success) { + // toss out the thread handle since we don't use it. + CloseHandle(pi.hThread); + // track the important handle, for our application. + _app_handle = pi.hProcess; +//hmmm: boot this stuff out into the stdio_wrapper class, which is the only +// thing that should do this. + ini_configurator ini(REDIRECTER_INI, ini_configurator::RETURN_ONLY, + ini_configurator::APPLICATION_DIRECTORY); + ini.store(PROCESS_SECTION, a_sprintf("%d", application_configuration::process_id()), + a_sprintf("%d", pi.dwProcessId)); + _process_id = pi.dwProcessId; + new_process_id = _process_id; + } else { + return NOT_FOUND; + } +#endif + + _stdout_reader->start(NIL); + _stderr_reader->start(NIL); + + return OKAY; +} + +bool stdio_redirecter::running() +{ + if (!_process_id) return false; // nothing to check. + if (_exit_value != 0) return false; // gone by now. +#ifdef __UNIX__ + int status; + pid_t pid = waitpid(_process_id, &status, WNOHANG); + if (!pid) return true; // still going. + if (!_exit_value) { +//hmmm: is that all we need from it? unprocessed exit value? + _exit_value = status; + } + if (WIFEXITED(status)) { + // the child exited on its own. + _process_id = 0; + return false; + } else if (WIFSIGNALED(status)) { + // the child was zapped by a signal. + _process_id = 0; + return false; + } + return true; +#elif defined (__WIN32__) + DWORD exit_value = 0; + // see if there's an exit code yet. if this fails with false, then the + // process is maybe long gone or something? + BOOL ret = GetExitCodeProcess(_app_handle, &exit_value); + if (ret) { + // store it if we had no previous version. + if (exit_value != STILL_ACTIVE) { + _exit_value = exit_value; + return false; + } + return true; + } else { + // this one seems to still be going. + return true; + } +#endif +} + +void stdio_redirecter::close_input() +{ +#ifdef __UNIX__ + close(_output_fds[1]); // shut down input to the child program. +#elif defined(__WIN32__) + if (_child_in) { CloseHandle(_child_in); _child_in = NIL; } + if (_parent_in) { CloseHandle(_parent_in); _parent_in = NIL; } +#endif +} + +void stdio_redirecter::zap_program() +{ + FUNCDEF("zap_program"); + _stdout_reader->cancel(); + _stderr_reader->cancel(); + +#ifdef __UNIX__ + close_input(); + close(_stderr_fds[0]); + close(_input_fds[0]); + + if (_process_id) { + kill(_process_id, 9); // end the program without any doubt. + } + _process_id = 0; +#elif defined(__WIN32__) + if (_app_handle) { + // none of the handle closing works if the app is still running. + // microsoft hasn't really got a clue, if you cannot close a file handle + // when you want to, but that's apparently what's happening. + TerminateProcess(_app_handle, 1); + } + + close_input(); + + if (_child_out) { CloseHandle(_child_out); _child_out = NIL; } + if (_parent_out) { CloseHandle(_parent_out); _parent_out = NIL; } + + if (_child_err) { CloseHandle(_child_err); _child_err = NIL; } + if (_parent_err) { CloseHandle(_parent_err); _parent_err = NIL; } + + // shut down the child process if it's still there. + if (_app_handle) { + DWORD ret; + +//hmmm: also should only be in the stdio wrapper program. +//hmmm: remove this in favor of the stdio wrapper or whomever tracking their +// own process id. + ini_configurator ini(REDIRECTER_INI, ini_configurator::RETURN_ONLY, + ini_configurator::APPLICATION_DIRECTORY); + ini.delete_entry(PROCESS_SECTION, a_sprintf("%d", application_configuration::process_id())); + + GetExitCodeProcess(_app_handle, &ret); + if (ret == STILL_ACTIVE) { + // it's still bumbling along; let's drop it. + TerminateProcess(_app_handle, 1); + if (WaitForSingleObject(_app_handle, 1000) == WAIT_TIMEOUT) { +///problem! + LOG("hmmm, we timed out waiting for the process to exit."); + } + } + CloseHandle(_app_handle); + _app_handle = NIL; + } +#endif + + _stdout_reader->stop(); + _stderr_reader->stop(); +} + +outcome stdio_redirecter::read(byte_array &received) +{ + received.reset(); + if (_persistent_result != OKAY) return _persistent_result; + auto_synchronizer l(*_lock); + if (!_stdout_queue->length()) return NONE_READY; +//hmmm: signal eof too! + received = *_stdout_queue; + _stdout_queue->reset(); + return common::OKAY; +} + +outcome stdio_redirecter::write(const astring &to_write, int &written) +{ + byte_array real_write(to_write.length(), (abyte *)to_write.observe()); + return write(real_write, written); +} + +outcome stdio_redirecter::write(const byte_array &to_write, int &written) +{ +// FUNCDEF("write"); + written = 0; + if (_persistent_result != OKAY) return _persistent_result; +#ifdef __UNIX__ + int writ = ::write(_output_fds[1], to_write.observe(), to_write.length()); + if (writ < 0) return ACCESS_DENIED; + written = writ; + return OKAY; +#elif defined(__WIN32__) + DWORD writ = 0; + BOOL ret = WriteFile(_parent_in, to_write.observe(), to_write.length(), + &writ, NIL); + written = writ; + if (ret) return OKAY; + else return ACCESS_DENIED; +#endif +} + +outcome stdio_redirecter::read_stderr(byte_array &received) +{ + received.reset(); + if (_persistent_result != OKAY) return _persistent_result; + auto_synchronizer l(*_lock); + if (!_stderr_queue->length()) return NONE_READY; +//signal eof too! + received = *_stderr_queue; + _stderr_queue->reset(); + return common::OKAY; +} + +void stdio_redirecter::std_thread_action(bool is_stdout) +{ +// FUNCDEF("std_thread_action"); + byte_array buff(BUFFER_SIZE + 1); +#ifdef __UNIX__ + bool ret = false; + int fd = _input_fds[0]; + if (!is_stdout) fd = _stderr_fds[0]; + if (!fd) return; // nothing to read from. + int bytes_read = ::read(fd, buff.access(), BUFFER_SIZE); + if (!bytes_read) { +//indicates end of file; set flags! + } else if (bytes_read > 0) { + ret = true; // there's new data in our buffer. + } +#elif defined(__WIN32__) + HANDLE where = _parent_out; + if (!is_stdout) where = _parent_err; + if (!where) return; // nothing to read from. + // read some data from the file. the function will return when a write + // operation completes or we get that much data. + DWORD bytes_read = 0; + BOOL ret = ReadFile(where, buff.access(), BUFFER_SIZE, &bytes_read, NIL); +//hmmm: if (ret && !bytes_read) {///set eof!!! } +#endif + if (ret && bytes_read) { + auto_synchronizer l(*_lock); + byte_array *queue = _stdout_queue; + if (!is_stdout) queue = _stderr_queue; + *queue += buff.subarray(0, bytes_read - 1); + } +} + +} //namespace. + + diff --git a/core/library/application/redirecter.h b/core/library/application/redirecter.h new file mode 100644 index 00000000..3f52f263 --- /dev/null +++ b/core/library/application/redirecter.h @@ -0,0 +1,138 @@ +#ifndef STDIO_REDIRECTER_CLASS +#define STDIO_REDIRECTER_CLASS + +/*****************************************************************************\ +* * +* Name : stdio_redirecter * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace application { + +//! Redirects I/O for a newly launched application. + +class stdio_redirecter : public virtual basis::root_object +{ +public: + stdio_redirecter(const basis::astring &command, const basis::astring ¶meters); + //!< controls the I/O for a program started with "command" and "parameters". + /*!< this creates an io redirecter that will trap the inputs and outputs + of a program called "command" that is launched with the "parameters". + the program can be communicated with via the read and write methods + below. */ + + stdio_redirecter(); + //!< creates a blank redirecter which should be started up with reset(). + + ~stdio_redirecter(); + //!< shuts down the program that was launched, if it's still running. + + basis::outcome reset(const basis::astring &command, const basis::astring ¶meters); + //!< shuts down the active program and starts with new parameters. + + DEFINE_CLASS_NAME("stdio_redirecter"); + + enum outcomes { + OKAY = basis::common::OKAY, + NOT_FOUND = basis::common::NOT_FOUND, //!< the file to launch was not found. + NONE_READY = basis::common::NONE_READY, //!< there was no data available. + PARTIAL = basis::common::PARTIAL, //!< only some of the bytes could be stored. + ACCESS_DENIED = basis::common::ACCESS_DENIED //!< the OS told us we could not. + }; + + basis::outcome health() const { return _persistent_result; } + //!< if the object constructed successfully, this returns OKAY. + /*!< otherwise an error occurred during the pipe creation or process fork. + */ + + bool running(); + //!< returns true if the application is still running. + /*!< if it exited on its own, then this will return false. */ + + int exit_value() const { return _exit_value; } + //!< returns the exit value from the app after it was launched. + /*!< this is only valid if the launch succeeded, the app ran, and now the + running() method is returning false because the application exited. */ + + int process_id() const { return _process_id; } + //!< the process id of the launched application. + /*!< this is only valid if the app is still running. */ + + void zap_program(); + //!< attempts to force a shutdown of the launched application. + /*!< this also closes out all of our pipes and handles. */ + + basis::outcome read(basis::byte_array &received); + //!< attempts to read bytes from the program's standard output. + /*!< if any bytes were found, OKAY is returned and the bytes are stored + in "received". */ + + basis::outcome write(const basis::byte_array &to_write, int &written); + //!< writes the bytes in "to_write" into the program's standard input. + /*!< OKAY is returned if all were successfully written. the number of + bytes that were actually sent to the program is put in "written". if only + a portion of the bytes could be written, then PARTIAL is returned. */ + + basis::outcome write(const basis::astring &to_write, int &written); + //!< sends a string "to_write" to the launched program's standard input. + + basis::outcome read_stderr(basis::byte_array &received); + //!< reads from the program's standard error stream similarly to read(). + + void close_input(); + //!< shuts down the standard input to the program. + /*!< this simulates when the program receives an end of file on its + main input. */ + + // internal use only... + + void std_thread_action(bool is_stdout); + //!< invoked by our threads when data becomes available. + /*!< if "is_stdout" is true, then that's the pipe we get data from. + otherwise we will try to get data from stderr. */ + +private: +#ifdef __UNIX__ + int _output_fds[2]; //!< file descriptors for parent's output. + int _input_fds[2]; //!< file descriptors for parent's input. + int _stderr_fds[2]; //!< file descriptors for standard error from child. +#endif +#ifdef __WIN32__ + void *_child_in, *_child_out, *_child_err; //!< child process pipes replace io. + void *_parent_in, *_parent_out, *_parent_err; //!< our access to the pipes. + void *_app_handle; //!< refers to the launched application. +#endif + basis::astring *_command; //!< the application that we'll start and switch I/O on. + basis::astring *_parms; //!< the parameters for the app we will launch. + basis::outcome _persistent_result; //!< this records failures during construction. + processes::ethread *_stdout_reader; //!< gets data from process's stdout. + processes::ethread *_stderr_reader; //!< gets data from process's stderr. + basis::byte_array *_stdout_queue; //!< holds data acquired from stdout. + basis::byte_array *_stderr_queue; //!< holds data acquired from stderr. + basis::mutex *_lock; //!< protects our queues. + int _process_id; //!< pid for the launched program. + int _exit_value; //!< stored once when we notice app is gone. + + basis::outcome create_pipes(); + //!< creates the three pipes that the child will be given as stdin, stdout and stderr. + + basis::outcome launch_program(int &new_process_id); + //!< launches the application using the previously established pipes. +}; + +} //namespace. + +#endif + diff --git a/core/library/application/shared_memory.cpp b/core/library/application/shared_memory.cpp new file mode 100644 index 00000000..4dab6191 --- /dev/null +++ b/core/library/application/shared_memory.cpp @@ -0,0 +1,216 @@ +/*****************************************************************************\ +* * +* Name : shared_memory * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "shared_memory.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef __UNIX__ + #include + #include + #include + #include + #include + #include +#endif + +using namespace basis; +using namespace configuration; +using namespace filesystem; +using namespace loggers; +using namespace processes; +using namespace structures; + +namespace application { + +shared_memory::shared_memory(int size, const char *identity) +: _locking(new rendezvous(identity)), + _the_memory(NIL), + _valid(false), + _identity(new astring(identity)), + _size(size) +{ +// FUNCDEF("constructor"); + bool first_use = false; // assume already existing until told otherwise. + _locking->lock(); // force all others to wait on our finishing creation. +#ifdef __UNIX__ + int flag = O_RDWR | O_CREAT | O_EXCL; + // try to create the object if it doesn't exist yet, but fail if it does. + int mode = 0700; // rwx------ for just us. + _the_memory = shm_open(special_filename(identity).s(), flag, mode); + // create the shared memory object but fail if it already exists. + if (negative(_the_memory)) { + // failed to create the shared segment. try without forcing exclusivity. + flag = O_RDWR | O_CREAT; + _the_memory = shm_open(special_filename(identity).s(), flag, mode); + basis::un_int err = critical_events::system_error(); // get last error. + if (negative(_the_memory)) { + // definitely a failure now... + printf("error allocating shared segment for %s, was told %s.\n", + special_filename(identity).s(), critical_events::system_error_text(err).s()); + _the_memory = 0; + _locking->unlock(); // release lock before return. + return; + } + // getting to here means the memory was already allocated. so we're fine. + } else { + // the shared memory segment was just created this time. + int ret = ftruncate(_the_memory, size); + basis::un_int err = critical_events::system_error(); // get last error. + if (ret) { + printf("error truncating shared segment for %s, was told %s.", + special_filename(identity).s(), critical_events::system_error_text(err).s()); + } + first_use = true; + } + _valid = true; +#elif defined(__WIN32__) + _the_memory = ::CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE, + 0, size, to_unicode_temp(identity)); + basis::un_int err = critical_events::system_error(); // get last error. + first_use = (err != ERROR_ALREADY_EXISTS); + if (!_the_memory) { + _locking->unlock(); + return; // not healthy. + } + _valid = true; +#else +//this is junk; simulates shared memory poorly. + #pragma message("simulating shared memory since unknown for this platform.") + if (!_bogus_shared_space().length()) { + _bogus_shared_space().reset(size); + first_use = true; + } + _the_memory = _bogus_shared_space().access(); + _valid = true; +#endif + if (first_use) { + // initialize the new memory to all zeros. + abyte *contents = locked_grab_memory(); + if (!contents) { + _valid = false; + _locking->unlock(); + return; + } + memset(contents, 0, size); + locked_release_memory(contents); // release the memory for now. + } + _locking->unlock(); +} + +shared_memory::~shared_memory() +{ +#ifdef __UNIX__ + if (_the_memory) { + close(int(_the_memory)); + shm_unlink(special_filename(identity()).s()); + } +#elif defined(__WIN32__) + ::CloseHandle(_the_memory); +#else + //hmmm: fix. + _the_memory = NIL; +#endif + WHACK(_locking); + WHACK(_identity); + _valid = false; +} + +const astring &shared_memory::identity() const { return *_identity; } + +astring shared_memory::special_filename(const astring &identity) +{ + astring shared_file = identity; + filename::detooth_filename(shared_file); + shared_file = astring("/tmp_") + "sharmem_" + shared_file; + return shared_file; +} + +astring shared_memory::unique_shared_mem_identifier(int sequencer) +{ + astring to_return("SMID-"); + to_return += a_sprintf("%d-%d-", application_configuration::process_id(), sequencer); + to_return += application_configuration::application_name(); + // replace file delimiters in the name with a safer character. + filename::detooth_filename(to_return); + return to_return; +} + +bool shared_memory::first_usage(abyte *contents, int max_compare) +{ + for (int i = 0; i < max_compare; i++) + if (contents[i] != 0) return false; + return true; +} + +abyte *shared_memory::lock() +{ + _locking->lock(); + return locked_grab_memory(); +} + +void shared_memory::unlock(abyte * &to_unlock) +{ + locked_release_memory(to_unlock); + _locking->unlock(); +} + +abyte *shared_memory::locked_grab_memory() +{ + FUNCDEF("locked_grab_memory") + abyte *to_return = NIL; + if (!_the_memory) return to_return; +#ifdef __UNIX__ + to_return = (abyte *)mmap(NIL, _size, PROT_READ | PROT_WRITE, + MAP_SHARED, int(_the_memory), 0); +#elif defined(__WIN32__) + to_return = (abyte *)::MapViewOfFile((HANDLE)_the_memory, FILE_MAP_ALL_ACCESS, + 0, 0, 0); +#else + to_return = (abyte *)_the_memory; +#endif + if (!to_return) { +// FUNCTION(func); +//not working yet. callstack tracker or whatever is hosed up. + throw(astring(astring(class_name()) + "::" + func + ": no data was accessible in shared space.")); + } + return to_return; +} + +void shared_memory::locked_release_memory(abyte * &to_unlock) +{ + if (!_the_memory || !to_unlock) return; +#ifdef __UNIX__ + munmap(to_unlock, _size); +#elif defined(__WIN32__) + ::UnmapViewOfFile(to_unlock); +#else +//uhh. +#endif +} + +} //namespace. + diff --git a/core/library/application/shared_memory.h b/core/library/application/shared_memory.h new file mode 100644 index 00000000..47c8a61c --- /dev/null +++ b/core/library/application/shared_memory.h @@ -0,0 +1,110 @@ +#ifndef SHARED_MEMORY_CLASS +#define SHARED_MEMORY_CLASS + +/*****************************************************************************\ +* * +* Name : shared_memory * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace application { + +//! Implements storage for memory that can be shared between threads. +/*! + Provides a means to create shared memory chunks and access them from + anywhere in a program or from cooperating programs. +*/ + +class shared_memory : public virtual basis::root_object +{ +public: + shared_memory(int size, const char *identity); + //!< a shared chunk of the "size" specified will be created. + /*!< it is named by the "identity" string. that "identity" uniquely + points to one shared chunk on this machine. note that if this object is + the first to create the chunk of memory, then all of the contents are + initialized to zero. this can be used to determine if the chunk needs + higher level, application-specific initialization. */ + + virtual ~shared_memory(); + //!< cleans up the shared bit of memory as far as we're concerned. + /*!< if some other instance still has it opened, then it isn't + destroyed for real yet. */ + + DEFINE_CLASS_NAME("shared_memory"); + + bool valid() const { return _valid; } + //!< this must be true for the shared_memory to be usable. + /*!< if it's false, then the memory chunk was never created. */ + + int size() const { return _size; } + //!< returns the size of the shared chunk of memory. + + const basis::astring &identity() const; + //!< provides a peek at the name that this chunk was constructed with. + + bool first_usage(basis::abyte *locked_memory, int max_compare); + //!< returns true if the "locked_memory" was just created. + /*!< that is assuming that a user of the shared memory will set the first + "max_compare" bytes to something other than all zeros. this is really + just a test of whether bytes zero through bytes "max_compare" - 1 are + currently zero, causing a return of true. seeing anything besides zero + causes a false return. */ + + basis::abyte *lock(); + //!< locks the shared memory and returns a pointer to the storage. + /*!< the synchronization supported is only within this program; this type + of shared memory is not intended for access from multiple processes, + just for access from multiple threads in the same app. */ + + void unlock(basis::abyte * &to_unlock); + //!< returns control of the shared memory so others can access it. + /*!< calls to lock() must be paired up with calls to unlock(). */ + + static basis::astring unique_shared_mem_identifier(int sequencer); + //!< returns a unique identifier for a shared memory chunk. + /*!< the id returned is unique on this host for this particular process + and application, given a "sequencer" number that is up to the application + to keep track of uniquely. the values from -100 through -1 are reserved + for hoople library internals. */ + +private: + processes::rendezvous *_locking; //!< protects our shared memory. +#ifdef __UNIX__ + int _the_memory; //!< OS index of the memory. +#elif defined(__WIN32__) + void *_the_memory; //!< OS pointer to the memory. +#endif + bool _valid; //!< true if the memory creation succeeded. + basis::astring *_identity; //!< holds the name we were created with. + int _size; //!< size of memory chunk. + + // these do the actual work of getting the memory. + basis::abyte *locked_grab_memory(); + void locked_release_memory(basis::abyte * &to_unlock); + + static basis::astring special_filename(const basis::astring &identity); + //!< provides the name for our shared memory file, if needed. + + // forbidden. + shared_memory(const shared_memory &); + shared_memory &operator =(const shared_memory &); +}; + +} //namespace. + + +#endif // outer guard. + diff --git a/core/library/application/singleton_application.cpp b/core/library/application/singleton_application.cpp new file mode 100644 index 00000000..14b3fa6e --- /dev/null +++ b/core/library/application/singleton_application.cpp @@ -0,0 +1,69 @@ +/*****************************************************************************\ +* * +* Name : singleton_application * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2006-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "singleton_application.h" + +#include +#include + +using namespace basis; +using namespace filesystem; +using namespace processes; + +namespace application { + +singleton_application::singleton_application(const astring &application_name, + const astring &user_name) +: c_initial_try(0), + _app_lock(new rendezvous(filename(application_name).basename().raw() + + "__" + user_name)), + _got_lock(false) +{ +} + +singleton_application::~singleton_application() +{ + release_lock(); + WHACK(_app_lock); +} + +bool singleton_application::already_running() +{ return !allow_this_instance(); } + +bool singleton_application::already_tried() const +{ return c_initial_try > 0; } + +void singleton_application::release_lock() +{ + if (_got_lock) { + _app_lock->unlock(); + _got_lock = false; + } +} + +bool singleton_application::allow_this_instance() +{ + if (_got_lock) return true; // already grabbed it. + + if (!already_tried()) { + _got_lock = _app_lock->lock(rendezvous::IMMEDIATE_RETURN); + if (_got_lock) c_initial_try = 1; // succeeded in locking. + else c_initial_try = 2; // failure. + } + + return _got_lock; +} + +} //namespace. + diff --git a/core/library/application/singleton_application.h b/core/library/application/singleton_application.h new file mode 100644 index 00000000..f086809e --- /dev/null +++ b/core/library/application/singleton_application.h @@ -0,0 +1,75 @@ +#ifndef SINGLETON_APPLICATION_CLASS +#define SINGLETON_APPLICATION_CLASS + +/*****************************************************************************\ +* * +* Name : singleton_application * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2006-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! Implements an application lock to ensure only one is running at once. +/*! + This encapsulates the code used to ensure that only one copy of an + application is running at a time. It can either be made specific to a + user (so that user can run only one at a time) or made global to the entire + machine. +*/ + +#include +#include +#include + +namespace application { + +class singleton_application +{ +public: + singleton_application(const basis::astring &application_name, + const basis::astring &user_name = basis::astring::empty_string()); + //!< constructs a singleton guardian for the "application_name". + /*!< if the "user_name" is non-empty, then it will be used as part of + the lock name. this ensures that separate users can still run the + application at the same time, which is especially important for terminal + servers. for a lock that should affect the whole machine and ignore + users, the "user_name" must be empty. */ + + virtual ~singleton_application(); + + DEFINE_CLASS_NAME("singleton_application"); + + bool already_tried() const; + //!< returns true if the singleton has already tried to lock. + + bool allow_this_instance(); + //!< the application must check this before starting up. + /*!< if this returns false, then the application must exit immediately or + it will be violating the singleton rule. */ + + bool already_running(); + //!< returns false if this program is not already running. + /*!< this is just the opposite of the allow_this_instance() method. */ + + void release_lock(); + //!< let's go of the application lock, if we had previously gotten it. + /*!< this should only be called when the application is about to exit. */ + +private: + int c_initial_try; //!< has the initial locking attempt been made? + /* if c_initial_try is zero, no attempt made yet. if it's 1, then tried + and succeeded. if it's greater than one, then tried and failed. */ + processes::rendezvous *_app_lock; //!< used to lock out other instances. + bool _got_lock; //!< records whether we successfully got the lock or not. +}; + +} //namespace. + +#endif + diff --git a/core/library/application/window_classist.h b/core/library/application/window_classist.h new file mode 100644 index 00000000..5b9a1ccb --- /dev/null +++ b/core/library/application/window_classist.h @@ -0,0 +1,147 @@ +#ifndef WINDOW_CLASSIST_CLASS +#define WINDOW_CLASSIST_CLASS + +/*****************************************************************************\ +* * +* Name : window_classist * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2007-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! An add-in file providing window class registration and a window procedure. +/*! + This file makes it easier to add a very simple window to any console or + win32 application that might need it (possibly because the app does not + create any windows itself, but for crazy insane reasons, a window is still + needed by an external agent, ahem installshield). It implements a very + important part of this process, which is setting a window procedure and + registering a window class. Sometime in 2005 or 2006, a windows update + came through that made these formerly optional practices mandatory (and + broke many of our applications that created windows without a window + procedure or class registration). That occurrence prompted the creation + of this class which tries to provide the bare minimum needed to make + things work again. + + Example Usage: + + // include our code file to embed the window procedure and register class + // methods in whoever needs them. this should only be needed once per + // program. + + #include + + // create our simple window... + + basis::astring window_title = "my_freaky_window"; + basis::astring class_name = "jumbo_stompy_update_crudburger"; + + window_handle f_window = create_simplistic_window(window_title, class_name); + + // and then much later, after the window is no longer needed... + + whack_simplistic_window(f_window); + +*/ + +#include "windoze_helper.h" + +#include + +namespace application { + +#ifndef __WIN32__ + +// this is a placeholder implementation for other platforms. +window_handle create_simplistic_window(const basis::astring &formal(window_title), + const basis::astring &formal(class_name)) { return NIL; } +void whack_simplistic_window(window_handle formal(f_window)) {} + +#else + +//! this is the very simple window procedure used by register_class() below. + +LRESULT CALLBACK window_procedure(HWND hWnd, UINT message, + WPARAM wParam, LPARAM lParam) +{ + + switch (message) { + case WM_COMMAND: { + int identifier, event; + identifier = LOWORD(wParam); + event = HIWORD(wParam); + return DefWindowProc(hWnd, message, wParam, lParam); + break; + } + case WM_PAINT: { + HDC hdc; + PAINTSTRUCT ps; + hdc = BeginPaint(hWnd, &ps); + // hmmm: Add any drawing code here... + EndPaint(hWnd, &ps); + break; + } + case WM_DESTROY: { + PostQuitMessage(0); + break; + } + default: { + return DefWindowProc(hWnd, message, wParam, lParam); + } + } + return 0; +} + +//! returns the registered class as a windows atom. + +ATOM register_class(const basis::astring &name) +{ + WNDCLASSEX wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = (WNDPROC)window_procedure; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GET_INSTANCE_HANDLE(); + wcex.hIcon = 0; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = 0; + basis::to_unicode_persist(temp_class, name); + wcex.lpszClassName = temp_class; + wcex.hIconSm = 0; + + return RegisterClassEx(&wcex); +} + +window_handle create_simplistic_window(const basis::astring &window_title, + const basis::astring &class_name) +{ + register_class(class_name); + window_handle f_window = CreateWindow(basis::to_unicode_temp(class_name), + basis::to_unicode_temp(window_title), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, + 0, CW_USEDEFAULT, 0, NIL, NIL, GET_INSTANCE_HANDLE(), NIL); + ShowWindow(f_window, SW_HIDE); + UpdateWindow(f_window); + return f_window; +} + +void whack_simplistic_window(window_handle f_window) +{ + SendMessage(f_window, WM_CLOSE, NIL, NIL); +//hmmm: is this enough? +} + +#endif // win32 + +} // namespace. + +#endif // outer guard + diff --git a/core/library/application/windoze_helper.cpp b/core/library/application/windoze_helper.cpp new file mode 100644 index 00000000..c9c29109 --- /dev/null +++ b/core/library/application/windoze_helper.cpp @@ -0,0 +1,934 @@ +/*****************************************************************************\ +* * +* Name : windoze_helper * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1994-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "windoze_helper.h" +///#include "array.h" +///#include "build_configuration.h" +///#include "convert_utf.h" +///#include "function.h" +///#include "mutex.h" +///#include "portable.h" +///#include "set.h" +///#include "version_record.h" +#include + +///#include +///#include + +/* +#ifdef __UNIX__ + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif +#ifdef __XWINDOWS__ +//hmmm: need code for the wait cursor stuff. +#endif +#ifdef __WIN32__ + #include + #include + #include + #include +#endif +*/ + +using namespace basis; +using namespace structures; +using namespace configuration; + +#undef static_class_name +#define static_class_name() "windoze_helper" + +/* +//#define DEBUG_PORTABLE + // uncomment for noisy debugging. + +//#define DEBUG_UPTIME + // uncomment to get noisier reporting about system and rolling uptime. + +//#define JUMP_TIME_49_DAYS + // uncomment to make our uptimes start just before the 32 bit uptime rollover. +//#define JUMP_TIME_497_DAYS + // uncomment to make our uptimes start just before the jiffies rollover. + +//#define ENABLE_ROLLOVER_BUG + // uncomment to get old behavior back where the uptime was not rolling + // over properly. this turns off our remainder calculation and leaves the + // conversion as a simple cast, which will fail and get stuck at 2^32-1. + +#define SUPPORT_SHELL_EXECUTE + // if this is not commented out, then the ShellExecute version of launch_ + // -process() is available. when commented out, ShellExecute is turned off. + // disabling this support is the most common because the ShellExecute method + // in win32 was only supported for wk203 and wxp, that is only after + // windows2000 was already available. since nt and w2k don't support this, + // we just usually don't mess with it. it didn't answer a single one of our + // issues on windows vista (wfista) anyway, so it's not helping. + +// ensure we always have debugging turned on if the jump is enabled. +#ifdef JUMP_TIME_49_DAYS + #undef DEBUG_UPTIME + #define DEBUG_UPTIME +#endif +#ifdef JUMP_TIME_497_DAYS + #undef DEBUG_UPTIME + #define DEBUG_UPTIME +#endif +// the JUMP..DAYS macros are mutually exclusive. use none or one, not both. +#ifdef JUMP_TIME_497_DAYS + #ifdef JUMP_TIME_49_DAYS + #error One cannot use both 497 day and 49 day bug inducers + #endif +#endif +#ifdef JUMP_TIME_49_DAYS + #ifdef JUMP_TIME_497_DAYS + #error One cannot use both 49 day and 497 day bug inducers + #endif +#endif +*/ + + +namespace application { + +/* +mutex BASIS_EXTERN &__uptime_synchronizer(); + // used by our low-level uptime methods to protect singleton variables. +mutex BASIS_EXTERN &__process_synchronizer(); + // used for synchronizing our records of child processes. +#ifdef __UNIX__ +int_set BASIS_EXTERN &__our_kids(); + // the static list of processes we've started. +#endif + +const int MAXIMUM_COMMAND_LINE = 32 * KILOBYTE; + // maximum command line that we'll deal with here. + +#ifdef __UNIX__ + const char *UPTIME_REPORT_FILE = "/tmp/uptime_report.log"; +#endif +#ifdef __WIN32__ + const char *UPTIME_REPORT_FILE = "c:/uptime_report.log"; +#endif + +#undef LOG +#define LOG(s) STAMPED_EMERGENCY_LOG(program_wide_logger(), s); + +#define COMPLAIN(to_print) { \ + guards::write_to_console((isprintf("basis/portable::%s: ", func) + to_print).s()); \ +} + +static const double __rollover_point = 2.0 * double(MAXINT); + // this number is our rollover point for 32 bit integers. + +double rolling_uptime() +{ + auto_synchronizer l(__uptime_synchronizer()); + // protect our rollover records. + + static u_int __last_ticks = 0; + static int __rollovers = 0; + + u_int ticks_up = system_uptime(); + // acquire the current uptime as a 32 bit unsigned int. + + if (ticks_up < __last_ticks) { + // rollover happened. increment our tracker. + __rollovers++; + } + __last_ticks = ticks_up; + + double to_return = double(__rollovers) * __rollover_point + double(ticks_up); + +#ifdef DEBUG_UPTIME + #ifdef __WIN32__ + static FILE *__outfile = fopen(UPTIME_REPORT_FILE, "a+b"); + static double old_value = 0; + if (absolute_value(old_value - to_return) > 9.9999) { + // only report when the time changes by more than 10 ms. + fprintf(__outfile, "-> uptime=%.0f\n", to_return); + fflush(__outfile); + old_value = to_return; + if (__rollover_point - to_return <= 40.00001) { + fprintf(__outfile, "---> MAXIMUM UPTIME SOON!\n"); + fflush(__outfile); + } + } + #endif +#endif + + return to_return; +} + +u_int system_uptime() +{ +#ifdef __WIN32__ + return timeGetTime(); +#else + auto_synchronizer l(__uptime_synchronizer()); + + static clock_t __ctps = sysconf(_SC_CLK_TCK); // clock ticks per second. + static const double __multiplier = 1000.0 / double(__ctps); + // the multiplier gives us our full range for the tick counter. + +#ifdef DEBUG_UPTIME + static FILE *__outfile = fopen(UPTIME_REPORT_FILE, "wb"); + if (__multiplier - u_int(__multiplier) > 0.000001) { + fprintf(__outfile, "uptime multiplier is " + "non-integral (%f)!\n", __multiplier); + fflush(__outfile); + } +#endif + + // read uptime info from the OS. + tms uptime; + u_int real_ticks = times(&uptime); + +#ifdef JUMP_TIME_497_DAYS + static u_int old_value_497 = 0; + bool report_497 = (absolute_value(real_ticks - old_value_497) > 99); + if (report_497) { + old_value_497 = real_ticks; // update before changing it. + fprintf(__outfile, "pre kludge497 tix=%u\n", real_ticks); + fflush(__outfile); + } + real_ticks += u_int(49.0 * double(DAY_ms) + 17.0 * double(HOUR_ms)); + if (report_497) { + fprintf(__outfile, "post kludge497 tix=%u\n", real_ticks); + fflush(__outfile); + } +#endif + + // now turn this into the number of milliseconds. + double ticks_up = (double)real_ticks; + ticks_up = ticks_up * __multiplier; // convert to time here. + +#ifdef JUMP_TIME_497_DAYS + #ifndef ENABLE_ROLLOVER_BUG + // add in additional time so we don't have to wait forever. we make the + // big number above rollover, but that's still got 27.5 or so minutes before + // we really rollover. so we add that part of the fraction (lost in the + // haze of the multiplier) in here. we don't add this in unless they are + // not exercising the rollover bug, because we already know that the 497 + // day bug will show without the addition. but when we're already fixing + // the uptime, we jump time a bit forward so we only have to wait a couple + // minutes instead of 27. + ticks_up += 25.0 * MINUTE_ms; + #endif +#endif + +#ifdef JUMP_TIME_49_DAYS + static u_int old_value_49 = 0; + bool report_49 = (absolute_value(u_int(ticks_up) - old_value_49) > 999); + if (report_49) { + old_value_49 = u_int(ticks_up); // update before changing it. + fprintf(__outfile, "pre kludge49 up=%f\n", ticks_up); + fflush(__outfile); + } + ticks_up += 49.0 * double(DAY_ms) + 17.0 * double(HOUR_ms); + if (report_49) { + fprintf(__outfile, "post kludge49 up=%f\n", ticks_up); + fflush(__outfile); + } +#endif + +#ifndef ENABLE_ROLLOVER_BUG + // fix the return value if is has actually gone over the 2^32 limit for uint. + // casting a double larger than 2*MAXINT to a u_int on some platforms does + // not calculate a rolled-over value, but instead leaves the int at 2*MAXINT. + // thus we make sure it will be correct, as long as there are no more than + // 2^32-1 rollovers, which would be about 584,542 millenia. it's unlikely + // earth will last that long, so this calculation seems safe. + u_int divided = u_int(ticks_up / __rollover_point); + double to_return = ticks_up - (double(divided) * __rollover_point); +#else + // we use the previous version of this calculation, which expected a u_int + // to double conversion to provide a modulo operation rather than just leaving + // the u_int at its maximum value (2^32-1). however, that expectation is not + // guaranteed on some platforms (e.g., ARM processor with floating point + // emulation) and thus it becomes a bug around 49 days and 17 hours into + // OS uptime because the value gets stuck at 2^32-1 and never rolls over. + double to_return = ticks_up; +#endif + +#ifdef DEBUG_UPTIME + static u_int old_value = 0; + int to_print = int(u_int(to_return)); + if (absolute_value(int(old_value) - to_print) > 9) { + // only report when the time changes by more than 10 ms. + fprintf(__outfile, "-> uptime=%u\n", to_print); + fflush(__outfile); + old_value = u_int(to_print); + if (__rollover_point - to_return <= 40.00001) { + fprintf(__outfile, "---> MAXIMUM UPTIME SOON!\n"); + fflush(__outfile); + } + } +#endif + + return u_int(to_return); +#endif +} + +void sleep_ms(u_int msec) +{ +#ifdef __UNIX__ + usleep(msec * 1000); +#endif +#ifdef __WIN32__ + Sleep(msec); +#endif +} + +istring null_device() +{ +#ifdef __WIN32__ + return "null:"; +#else + return "/dev/null"; +#endif +} + +#ifdef __WIN32__ +bool event_poll(MSG &message) +{ + message.hwnd = 0; + message.message = 0; + message.wParam = 0; + message.lParam = 0; + if (!PeekMessage(&message, NIL, 0, 0, PM_REMOVE)) + return false; + TranslateMessage(&message); + DispatchMessage(&message); + return true; +} +#endif + +// makes a complaint about a failure. +#ifndef EMBEDDED_BUILD + #define COMPLAIN_QUERY(to_print) { \ + unlink(tmpfile.s()); \ + COMPLAIN(to_print); \ + } +#else + #define COMPLAIN_QUERY(to_print) { \ + COMPLAIN(to_print); \ + } +#endif + +#ifdef __UNIX__ +istring get_cmdline_from_proc() +{ + FUNCDEF("get_cmdline_from_proc"); + isprintf cmds_filename("/proc/%d/cmdline", portable::process_id()); + FILE *cmds_file = fopen(cmds_filename.s(), "r"); + if (!cmds_file) { + COMPLAIN("failed to open our process's command line file.\n"); + return "unknown"; + } + size_t size = 2000; + char *buff = new char[size + 1]; + ssize_t chars_read = getline(&buff, &size, cmds_file); + // read the first line, giving ample space for how long it might be. + fclose(cmds_file); // drop the file again. + if (!chars_read || negative(chars_read)) { + COMPLAIN("failed to get any characters from our process's cmdline file.\n"); + return "unknown"; + } + istring buffer = buff; + delete [] buff; + // clean out quote characters from the name. + for (int i = buffer.length() - 1; i >= 0; i--) { + if (buffer[i] == '"') buffer.zap(i, i); + } + return buffer; +} + +// deprecated; better to use the /proc/pid/cmdline file. +istring query_for_process_info() +{ + FUNCDEF("query_for_process_info"); + istring to_return = "unknown"; + // we ask the operating system about our process identifier and store + // the results in a temporary file. + chaos rando; + isprintf tmpfile("/tmp/proc_name_check_%d_%d.txt", portable::process_id(), + rando.inclusive(0, 128000)); + isprintf cmd("ps h --format \"%%a\" %d >%s", portable::process_id(), + tmpfile.s()); + // run the command to locate our process info. + int sysret = system(cmd.s()); + if (negative(sysret)) { + COMPLAIN_QUERY("failed to run ps command to get process info"); + return to_return; + } + // open the output file for reading. + FILE *output = fopen(tmpfile.s(), "r"); + if (!output) { + COMPLAIN_QUERY("failed to open the ps output file"); + return to_return; + } + // read the file's contents into a string buffer. + char buff[MAXIMUM_COMMAND_LINE]; + size_t size_read = fread(buff, 1, MAXIMUM_COMMAND_LINE, output); + if (size_read > 0) { + // success at finding some text in the file at least. + while (size_read > 0) { + const char to_check = buff[size_read - 1]; + if ( !to_check || (to_check == '\r') || (to_check == '\n') + || (to_check == '\t') ) + size_read--; + else break; + } + to_return.reset(istring::UNTERMINATED, buff, size_read); + } else { + // couldn't read anything. + COMPLAIN_QUERY("could not read output of process list"); + } + unlink(tmpfile.s()); + return to_return; +} +#endif + +// used as a return value when the name cannot be determined. +#ifndef EMBEDDED_BUILD + #define SET_BOGUS_NAME(error) { \ + COMPLAIN(error); \ + if (output) { \ + fclose(output); \ + unlink(tmpfile.s()); \ + } \ + istring home_dir = env_string("HOME"); \ + to_return = home_dir + "/failed_to_determine.exe"; \ + } +#else + #define SET_BOGUS_NAME(error) { \ + COMPLAIN(error); \ + to_return = "unknown"; \ + } +#endif + +istring application_name() +{ + FUNCDEF("application_name"); + istring to_return; +#ifdef __UNIX__ + to_return = get_cmdline_from_proc(); +#elif defined(__WIN32__) + flexichar low_buff[MAX_ABS_PATH + 1]; + GetModuleFileName(NIL, low_buff, MAX_ABS_PATH - 1); + istring buff = from_unicode_temp(low_buff); + buff.to_lower(); // we lower-case the name since windows seems to UC it. + to_return = buff; +#elif defined(EMBEDDED_BUILD) + SET_BOGUS_NAME("embedded_exe"); +#else + #pragma error("hmmm: no means of finding app name is implemented.") + SET_BOGUS_NAME("not_implemented_for_this_OS"); +#endif + return to_return; +} + +istring module_name(const void *module_handle) +{ +#ifdef __UNIX__ +//hmmm: implement module name for linux if that makes sense. + if (module_handle) {} + return application_name(); +#elif defined(__WIN32__) + flexichar low_buff[MAX_ABS_PATH + 1]; + GetModuleFileName((HMODULE)module_handle, low_buff, MAX_ABS_PATH - 1); + istring buff = from_unicode_temp(low_buff); + buff.to_lower(); + return buff; +#else + #pragma message("module_name unknown for this operating system.") + return application_name(); +#endif +} + +u_int system_error() +{ +#if defined(__UNIX__) + return errno; +#elif defined(__WIN32__) + return GetLastError(); +#else + #pragma error("hmmm: no code for error number for this operating system") + return 0; +#endif +} + +istring system_error_text(u_int to_name) +{ +#if defined(__UNIX__) + return strerror(to_name); +#elif defined(__WIN32__) + char error_text[1000]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NIL, to_name, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)error_text, + sizeof(error_text) - 1, NIL); + istring to_return = error_text; + // trim off the ridiculous carriage return they add. + while ( (to_return[to_return.end()] == '\r') + || (to_return[to_return.end()] == '\n') ) + to_return.zap(to_return.end(), to_return.end()); + return to_return; +#else + #pragma error("hmmm: no code for error text for this operating system") + return ""; +#endif +} + +#ifdef __WIN32__ + +bool is_address_valid(const void *address, int size_expected, bool writable) +{ + return address && !IsBadReadPtr(address, size_expected) + && !(writable && IsBadWritePtr((void *)address, size_expected)); +} + +#endif // win32 + +version get_OS_version() +{ + version to_return; +#ifdef __UNIX__ + utsname kernel_parms; + uname(&kernel_parms); + to_return = version(kernel_parms.release); +#elif defined(__WIN32__) + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + ::GetVersionEx(&info); + to_return = version(isprintf("%u.%u.%u.%u", u_short(info.dwMajorVersion), + u_short(info.dwMinorVersion), u_short(info.dwPlatformId), + u_short(info.dwBuildNumber))); +#elif defined(EMBEDDED_BUILD) + // no version support. +#else + #pragma error("hmmm: need version info for this OS!") +#endif + return to_return; +} + +// for non-win32 and non-unix OSes, this might not work. +#if defined(__UNIX__) || defined(__WIN32__) + u_int process_id() { return getpid(); } +#else + #pragma error("hmmm: need process id implementation for this OS!") + u_int process_id() { return 0; } +#endif + +istring current_directory() +{ + istring to_return; +#ifdef __UNIX__ + char buff[MAX_ABS_PATH]; + getcwd(buff, MAX_ABS_PATH - 1); + to_return = buff; +#elif defined(__WIN32__) + flexichar low_buff[MAX_ABS_PATH + 1]; + GetCurrentDirectory(MAX_ABS_PATH, low_buff); + to_return = from_unicode_temp(low_buff); +#else + #pragma error("hmmm: need support for current directory on this OS.") + to_return = "."; +#endif + return to_return; +} + +istring env_string(const istring &variable_name) +{ +#ifdef __WIN32__ + char *value = getenv(variable_name.upper().observe()); + // dos & os/2 require upper case for the name, so we just do it that way. +#else + char *value = getenv(variable_name.observe()); + // reasonable OSes support mixed-case environment variables. +#endif + istring to_return; + if (value) + to_return = istring(value); + return to_return; +} + +bool set_environ(const istring &variable_name, const istring &value) +{ + int ret = 0; +#ifdef __WIN32__ + ret = putenv((variable_name + "=" + value).s()); +#else + ret = setenv(variable_name.s(), value.s(), true); +#endif + return !ret; +} + +timeval fill_timeval_ms(int duration) +{ + timeval time_out; // timeval has tv_sec=seconds, tv_usec=microseconds. + if (!duration) { + // duration is immediate for the check; just a quick poll. + time_out.tv_sec = 0; + time_out.tv_usec = 0; +#ifdef DEBUG_PORTABLE +// LOG("no duration specified"); +#endif + } else { + // a non-zero duration means we need to compute secs and usecs. + time_out.tv_sec = duration / 1000; + // set the number of seconds from the input in milliseconds. + duration -= time_out.tv_sec * 1000; + // now take out the chunk we've already recorded as seconds. + time_out.tv_usec = duration * 1000; + // set the number of microseconds from the remaining milliseconds. +#ifdef DEBUG_PORTABLE +// LOG(isprintf("duration of %d ms went to %d sec and %d usec.", duration, +// time_out.tv_sec, time_out.tv_usec)); +#endif + } + return time_out; +} + +//hmmm: this doesn't seem to account for quoting properly at all? +basis::char_star_array break_line(istring &app, const istring ¶meters) +{ + basis::char_star_array to_return; + int_array posns; + int num = 0; + // find the positions of the spaces and count them. + for (int j = 0; j < parameters.length(); j++) { + if (parameters[j] == ' ') { + num++; + posns += j; + } + } + // first, add the app name to the list of parms. + to_return += new char[app.length() + 1]; + app.stuff(to_return[0], app.length()); + int last_posn = 0; + // now add each space-separated parameter to the list. + for (int i = 0; i < num; i++) { + int len = posns[i] - last_posn; + to_return += new char[len + 1]; + parameters.substring(last_posn, posns[i] - 1).stuff(to_return[i + 1], len); + last_posn = posns[i] + 1; + } + // catch anything left after last separator. + if (last_posn < parameters.length() - 1) { + int len = parameters.length() - last_posn; + to_return += new char[len + 1]; + parameters.substring(last_posn, parameters.length() - 1) + .stuff(to_return[to_return.last()], len); + } + // add the sentinel to the list of strings. + to_return += NIL; +#ifdef DEBUG_PORTABLE + for (int q = 0; to_return[q]; q++) { + printf("%d: %s\n", q, to_return[q]); + } +#endif + // now a special detour; fix the app name to remove quotes, which are + // not friendly to pass to exec. + if (app[0] == '"') app.zap(0, 0); + if (app[app.end()] == '"') app.zap(app.end(), app.end()); + return to_return; +} + +#ifdef __UNIX__ + +void exiting_child_signal_handler(int sig_num) +{ + FUNCDEF("exiting_child_signal_handler"); + if (sig_num != SIGCHLD) { + // uhhh, this seems wrong. + } + auto_synchronizer l(__process_synchronizer()); + for (int i = 0; i < __our_kids().length(); i++) { + int status; + pid_t exited = waitpid(__our_kids()[i], &status, WNOHANG); + if ( (exited == -1) || (exited == __our_kids()[i]) ) { + // negative one denotes an error, which we are going to assume means the + // process has exited via some other method than our wait. if the value + // is the same as the process we waited for, that means it exited. + __our_kids().zap(i, i); + i--; + } else if (exited != 0) { + // zero would be okay; this result we do not understand. +#ifdef DEBUG_PORTABLE + printf("unknown result %d waiting for process %d", exited, + __our_kids()[i]); +#endif + } + } +} +#endif + +u_int launch_process(const istring &app_name_in, const istring &command_line, + int flag, u_int &child_id) +{ + child_id = 0; + istring app_name = app_name_in; + if (app_name[0] != '"') + app_name.insert(0, "\""); + if (app_name[app_name.end()] != '"') + app_name += "\""; +#ifdef __UNIX__ + // unix / linux implementation. + if (flag & RETURN_IMMEDIATELY) { + // they want to get back right away. + pid_t kid_pid = fork(); +#ifdef DEBUG_PORTABLE + printf("launch fork returned %d\n", kid_pid); +#endif + if (!kid_pid) { + // this is the child; we now need to launch into what we were asked for. +#ifdef DEBUG_PORTABLE + printf((isprintf("process %d execing ", process_id()) + app_name + + " parms " + command_line + "\n").s()); +#endif + basis::char_star_array parms = break_line(app_name, command_line); + execv(app_name.s(), parms.observe()); + // oops. failed to exec if we got to here. +#ifdef DEBUG_PORTABLE + printf((isprintf("child of fork (pid %d) failed to exec, " + "error is ", process_id()) + system_error_text(system_error()) + + "\n").s()); +#endif + exit(0); // leave since this is a failed child process. + } else { + // this is the parent. let's see if the launch worked. + if (kid_pid == -1) { + // failure. + u_int to_return = system_error(); +#ifdef DEBUG_PORTABLE + printf((isprintf("parent %d is returning after failing to create, " + "error is ", process_id()) + system_error_text(to_return) + + "\n").s()); +#endif + return to_return; + } else { + // yes, launch worked okay. + child_id = kid_pid; + { + auto_synchronizer l(__process_synchronizer()); + __our_kids() += kid_pid; + } + // hook in our child exit signal handler. + signal(SIGCHLD, exiting_child_signal_handler); + +#ifdef DEBUG_PORTABLE + printf((isprintf("parent %d is returning after successfully " + "creating %d ", process_id(), kid_pid) + app_name + + " parms " + command_line + "\n").s()); +#endif + return 0; + } + } + } else { + // assume they want to wait. + return system((app_name + " " + command_line).s()); + } +#elif defined(__WIN32__) + +//checking on whether we have admin rights for the launch. +//if (IsUserAnAdmin()) { +// MessageBox(0, (istring("IS admin with ") + app_name_in + " " + command_line).s(), "launch process", MB_OK); +//} else { +// MessageBox(0, (istring("NOT admin for ") + app_name_in + " " + command_line).s(), "launch process", MB_OK); +//} + + PROCESS_INFORMATION process_info; + ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION)); + +#ifdef SUPPORT_SHELL_EXECUTE + if (flag & SHELL_EXECUTE) { + // new code for using shell execute method--required on vista for proper + // launching with the right security tokens. + int show_cmd = 0; + if (flag & HIDE_APP_WINDOW) { + // magic that hides a console window for mswindows. + show_cmd = SW_HIDE; + } else { + show_cmd = SW_SHOWNORMAL; + } + + SHELLEXECUTEINFO exec_info; + ZeroMemory(&exec_info, sizeof(SHELLEXECUTEINFO)); + exec_info.cbSize = sizeof(SHELLEXECUTEINFO); + exec_info.fMask = SEE_MASK_NOCLOSEPROCESS // get the process info. + | SEE_MASK_FLAG_NO_UI; // turn off any visible error dialogs. + exec_info.hwnd = GetDesktopWindow(); +//hmmm: is get desktop window always appropriate? + to_unicode_persist(temp_verb, "open"); +//does "runas" work on xp also? or anywhere? + exec_info.lpVerb = temp_verb; + to_unicode_persist(temp_file, app_name); + exec_info.lpFile = temp_file; + to_unicode_persist(temp_parms, command_line); + exec_info.lpParameters = temp_parms; + exec_info.nShow = show_cmd; +// exec_info.hProcess = &process_info; + + BOOL worked = ShellExecuteEx(&exec_info); + if (!worked) + return system_error(); + // copy out the returned process handle. + process_info.hProcess = exec_info.hProcess; + process_info.dwProcessId = GetProcessId(exec_info.hProcess); + } else { +#endif //shell exec + // standard windows implementation using CreateProcess. + STARTUPINFO startup_info; + ZeroMemory(&startup_info, sizeof(STARTUPINFO)); + startup_info.cb = sizeof(STARTUPINFO); + int create_flag = 0; + if (flag & HIDE_APP_WINDOW) { + // magic that hides a console window for mswindows. +// version ver = portable::get_OS_version(); +// version vista_version(6, 0); +// if (ver < vista_version) { +// // we suspect that this flag is hosing us in vista. + create_flag = CREATE_NO_WINDOW; +// } + } + istring parms = app_name + " " + command_line; + bool success = CreateProcess(NIL, to_unicode_temp(parms), NIL, NIL, false, + create_flag, NIL, NIL, &startup_info, &process_info); + if (!success) + return system_error(); + // success then, merge back into stream. + +#ifdef SUPPORT_SHELL_EXECUTE + } +#endif //shell exec + + // common handling for CreateProcess and ShellExecuteEx. + child_id = process_info.dwProcessId; + u_long retval = 0; + if (flag & AWAIT_VIA_POLLING) { + // this type of waiting is done without blocking on the process. + while (true) { + MSG msg; + event_poll(msg); + // check if the process is gone yet. + BOOL ret = GetExitCodeProcess(process_info.hProcess, &retval); + if (!ret) { + break; + } else { + // if they aren't saying it's still active, then we will leave. + if (retval != STILL_ACTIVE) + break; + } + sleep_ms(14); + } + } else if (flag & AWAIT_APP_EXIT) { + // they want to wait for the process to exit. + WaitForInputIdle(process_info.hProcess, INFINITE); + WaitForSingleObject(process_info.hProcess, INFINITE); + GetExitCodeProcess(process_info.hProcess, &retval); + } + // drop the process and thread handles. + if (process_info.hProcess) + CloseHandle(process_info.hProcess); + if (process_info.hThread) + CloseHandle(process_info.hThread); + return (u_int)retval; +#else + #pragma error("hmmm: launch_process: no implementation for this OS.") +#endif + return 0; +} + +//////////////////////////////////////////////////////////////////////////// + +#ifdef __UNIX__ + +char *itoa(int to_convert, char *buffer, int radix) +{ + // slow implementation; may need enhancement for serious speed. + // ALSO: only supports base 10 and base 16 currently. + const char *formatter = "%d"; + if (radix == 16) formatter = "%x"; + isprintf printed(formatter, to_convert); + printed.stuff(buffer, printed.length() + 1); + return buffer; +} + +#endif +*/ + +#ifdef __WIN32__ + +/* +void show_wait_cursor() { SetCursor(LoadCursor(NULL, IDC_WAIT)); } + +void show_normal_cursor() { SetCursor(LoadCursor(NULL, IDC_ARROW)); } + +istring rc_string(UINT id, application_instance instance) +{ + flexichar temp[MAX_ABS_PATH + 1]; + int ret = LoadString(instance, id, temp, MAX_ABS_PATH); + if (!ret) return istring(); + return istring(from_unicode_temp(temp)); +} + +istring rc_string(u_int id) { return rc_string(id, GET_INSTANCE_HANDLE()); } +*/ + +const char *opsystem_name(known_operating_systems which) +{ + switch (which) { + case WIN_95: return "WIN_95"; + case WIN_NT: return "WIN_NT"; + case WIN_2K: return "WIN_2K"; + case WIN_XP: return "WIN_XP"; + case WIN_SRV2K3: return "WIN_SRV2K3"; + case WIN_VISTA: return "WIN_VISTA"; + case WIN_SRV2K8: return "WIN_SRV2K8"; + default: return "UNKNOWN_OS"; + } +} + +known_operating_systems determine_OS() +{ + version osver = application_configuration::get_OS_version(); + if ( (osver.v_major() == 4) && (osver.v_minor() == 0) ) { + if (osver.v_revision() == VER_PLATFORM_WIN32_WINDOWS) return WIN_95; + if (osver.v_revision() == VER_PLATFORM_WIN32_NT) return WIN_NT; + } else if ( (osver.v_major() == 5) && (osver.v_minor() == 0) ) { + return WIN_2K; + } else if ( (osver.v_major() == 5) && (osver.v_minor() == 1) ) { + return WIN_XP; + } else if ( (osver.v_major() == 5) && (osver.v_minor() == 2) ) { + return WIN_SRV2K3; + } else if ( (osver.v_major() == 6) && (osver.v_minor() == 0) ) { + return WIN_VISTA; + } else if ( (osver.v_major() == 6) && (osver.v_minor() == 1) ) { + return WIN_SRV2K8; + } + return UNKNOWN_OS; +} + +#endif // win32 + +} // namespace. + +#undef static_class_name + diff --git a/core/library/application/windoze_helper.h b/core/library/application/windoze_helper.h new file mode 100644 index 00000000..a02b2965 --- /dev/null +++ b/core/library/application/windoze_helper.h @@ -0,0 +1,246 @@ +#ifndef WINDOZE_HELPER_GROUP +#define WINDOZE_HELPER_GROUP + +/* +* Name : windoze_helper definitions +* Author : Chris Koeritz +* Copyright (c) 1994-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! @file windoze_helper.h Aids in achievement of platform independence. +/*! @file windoze_helper.h + These definitions, inclusions and types are aimed at allowing our source + code to remain independent of the underlying windowing and operating + systems. Specifically, windows puts a lot of burden on the developer, + and this file exists to hide all that malarkey. +*/ + +// gnarly headers that are needed for certain types of compilation... + +//unix headers not needed in here for new purpose of file. +#ifdef __UNIX__ + #include +#endif +#ifndef NO_XWINDOWS + #ifdef __XWINDOWS__ + #include + #include + #include + #include + #endif +#endif +#ifdef __WIN32__ + #ifndef STRICT + #define STRICT + #endif + // windows headers... + #define _WINSOCKAPI_ // make windows.h happy about winsock. + #ifndef _AFXDLL + // include ms-windows headers only if we're not doing mfc; mfc has its own + // special way of including the headers. + #include + #else + #include + #include + #endif + // winsock support... + #undef FD_SETSIZE + #define FD_SETSIZE 1000 + // if you don't set this, you can only select on a default of 64 sockets. + #include +#endif + +// forward. +//class version; + +// wrapper classes defined in the sequel... +// +// application_instance -- this program's application object; used mainly +// in ms-windows. +// +// window_handle -- a wrapper for the root of all objects that can be +// referred to in the relevant windowing system. + +#include + +#ifdef __UNIX__ + // the application_instance class is implemented very simply for unix; + // it's a stand-in since unix apps don't refer to an instance handle like + // ms-windows does. + typedef void *application_instance; + + // some definitions to quiet complaints from win32-based code. + #ifndef LONGINT_SIZE + #if defined(__alpha) || (defined(__HOS_AIX__) && defined(_LP64)) \ + || defined(__sparcv9) || defined(__LP64__) + #define LONGINT_SIZE 8 + #else + #define LONGINT_SIZE 4 + #endif + #endif + #if (LONGINT_SIZE == 4) + // be compatible with linux's definitions on this subject. + typedef unsigned long DWORD; + #else + typedef unsigned int DWORD; + #endif + + typedef void *HANDLE; + + //temp; these just mimic function prototypes coming from non-portable code. + typedef void *ATOM; + typedef void *BITMAPINFO; + typedef void *HBITMAP; + typedef void *HDC; + typedef void *RGBQUAD; + #define LoadBitmap(a, b) (a) + #define __stdcall + #define afx_msg + + // windoze_helper definitions for the X windowing system. + #ifndef NO_XWINDOWS // protects regions with similar names. + #ifdef __XWINDOWS__ + typedef Widget window_handle; + //!< main way to access a windowing system object. should be a pointer. + + typedef Colormap window_colormap; + //!< the windowing system representation of a pallette or colormap. + + typedef XColor window_color; + //!< the system representation of color. + + typedef XmString window_string; + //!< a special windowing system dependent kind of character string. + +//is that really fixed? + const int MAXIMUM_COLOR_INTENSITY = 65535; + // largest valid value a color component (R, G or B) can have. + #else + // no x-windows. + typedef void *window_handle; + #endif + #endif +#endif + +#ifdef __WIN32__ + typedef HINSTANCE application_instance; + // our moniker for an application's guts. + + typedef HWND window_handle; + // our alias for window handles in win32. +#endif + +namespace application { + +// istring module_name(const void *module_handle = NIL); + //!< returns the name of the module for the "module_handle" where supported. + /*!< if the handle is NIL, then the program name is returned. */ + + +// u_int system_error(); + //!< gets the most recent system error reported on this thread. + +// istring system_error_text(u_int error_to_show); + //!< returns the OS's string form of the "error_to_show". + /*!< this often comes from the value reported by system_error(). */ + +// istring null_device(); + //!< returns the name of the system's NULL device. + /*!< this device is a black hole where output can be sent, never to be + seen again. this is /dev/null on unix and null: on win32. */ + +// timeval fill_timeval_ms(int milliseconds); + //!< returns a timeval system object that represents the "milliseconds". + /*!< if "milliseconds" is zero, then the returned timeval will + represent zero time passing (rather than infinite duration as some + functions assume). */ + + // this only really helps for win32. + extern application_instance _i_handle; + //!< dll_root.cpp defines this for all dlls. + #define DEFINE_INSTANCE_HANDLE application_instance application::_i_handle = 0 + //!< some applications may need this to use rc_string and others. + /*!< if this macro is used, then the code is impervious to future + changes in how the instance handle works. */ + #define SET_INSTANCE_HANDLE(value) application::_i_handle = value + //!< use this to change the instance handle for this dll or exe. + /*!< note that this should be done only once and by the main thread. */ + #define GET_INSTANCE_HANDLE() application::_i_handle + //!< a handy macro that frees one from knowing the name of the handle. + +//////////////////////////////////////////////////////////////////////////// + + // Unix and Linux specific items are included here. + #ifdef __UNIX__ +//// istring get_cmdline_from_proc(); + //!< returns the command line that /proc has recorded for our process. + +// char *itoa(int to_convert, char *buffer, int radix); + //!< turns the integer "to_convert" into a string stored in the "buffer". + /*!< this is needed on linux since there doesn't seem to be a builtin + version of it. the buffer must be long enough to hold the number's + textual representation! the buffer's length must also account for the + chosen "radix" (the base for the number system, where 10 is decimal, + 16 is hexadecimal, etc). */ + + #define RGB(r, g, b) (b + (g << 8) + (r << 16)) + //!< no idea if that's even approximately right. + #endif + + // ms-windows of more modern types, i.e. win32. + #ifdef __WIN32__ + +// bool event_poll(MSG &message); + //!< tries to process one win32 event and retrieve the "message" from it. + /*!< this is a very general poll and will retrieve any message that's + available for the current thread. the message is actually processed + here also, by calling translate and dispatch. the returned structure + is mainly interesting for knowing what was done. */ + +//hmmm: is there an equivalent to this for unix? +// bool is_address_valid(const void *address, int size_expected, +// bool writable = false); + //!< checks that the address specified is a valid pointer. + /*! also tests that the address's allocated space has the + "size_expected". if "writable" is true, then the pointer is + checked for being writable as well as readable. */ + + #define BROADCAST_HANDLE HWND_BROADCAST + + enum known_operating_systems { + WIN_95, WIN_NT, WIN_2K, WIN_XP, WIN_SRV2K3, WIN_VISTA, WIN_SRV2K8, + UNKNOWN_OS + }; + const char *opsystem_name(known_operating_systems which); + //!< returns the textual form of the known_operating_systems enum. + + known_operating_systems determine_OS(); + //!< returns the operating system that seems to be running currently. + /*!< note that WIN_95 also covers windows 98 and windows ME. */ + +// istring rc_string(u_int id, application_instance instance); + //!< acquires a string from a particular "instance". + /*!< the "id" is the resource identifier for the desired string in + that "instance". */ + +// istring rc_string(u_int id); + //!< simplified from above function by not needing an "instance". + /*!< the "current" dynamic library or executable's resources are sought + for the "id". */ + +// void show_wait_cursor(); + //!< changes the cursor to the "wait" look, which is usually an hourglass. +// void show_normal_cursor(); + //!< changes the cursor to "normal", which is usually a pointing arrow. + + #endif // win32. + +} // namespace. + +#endif // outer guard. + diff --git a/core/library/basis/array.h b/core/library/basis/array.h new file mode 100644 index 00000000..afd703e6 --- /dev/null +++ b/core/library/basis/array.h @@ -0,0 +1,871 @@ +#ifndef ARRAY_CLASS +#define ARRAY_CLASS + +/*****************************************************************************\ +* * +* Name : array * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1989-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "common_outcomes.h" +#include "definitions.h" +#include "enhance_cpp.h" +#include "functions.h" +#include "guards.h" +#include "outcome.h" + +#define DEBUG_ARRAY + // uncomment for the noisier debugging version. + +namespace basis { + +//! Represents a sequential, ordered, contiguous collection of objects. +/*! + This object manages a contiguous array of memory to hold the objects it + contains. The objects to be stored must have a constructor with zero + parameters, since the objects are stored in a C-style array (and array + constructors cannot be given arguments to be passed to the objects). + The objects must also either be flat (containing no pointers) or have an + assignment operator (=) that correctly copies the deep contents. + This class also provides an exponential growth mode for memory to reduce + thrashing; this allows the size pre-allocated to double every time a new + allocation is required during a resize. This causes the allocation to grow + very swiftly, speeding up usage of frequently growing arrays, but this may + not be desired for every array. + terms used here... + blank array: a array with some number of elements, but where those + elements are objects that have been constructed using their default + parameterless constructor. + empty array: a array of zero elements. +*/ + +template +class array : public virtual root_object +{ +public: + //! the flags specify how the array treats its contents and its length. + enum specialc_flags { + NO_SPECIAL_MODES = 0x0, //!< do nothing extra; only valid by itself. + SIMPLE_COPY = 0x1, //!< the contents can be memcpy'd and are not deep. + EXPONENTIAL_GROWTH = 0x2, //!< length is doubled when reallocation happens. + EXPONE = EXPONENTIAL_GROWTH, //!< synonym for EXPONENTIAL_GROWTH. + FLUSH_INVISIBLE = 0x4 //!< blanks out allocated but inaccessible elements. + }; + + DEFINE_CLASS_NAME("array"); + + enum how_to_copy { NEW_AT_END, NEW_AT_BEGINNING, DONT_COPY }; + //!< An enumeration of ways to shift existing contents in an array. + /*!< In the context of dynamically resizing an array of data, a range of + new elements can be added (or removed for that matter) at the end of the + existing space (NEW_AT_END), at the beginning of the existing space + (NEW_AT_BEGINNING), or we might not care about the existing data when + we perform the resize (DONT_COPY). These methods impose a particular + shift on the existing contents if we actually already have enough space + for the new contents. If there is space before the part of the array + that's in use and we want NEW_AT_END, then the existing contents are + jammed up into the front end of the array. */ + + array(int number = 0, const contents *init = NIL, + int flags = EXPONENTIAL_GROWTH | FLUSH_INVISIBLE); + //!< Constructs an array with room for "number" objects. + /*!< The initial contents are copied from "init" unless NIL is passed in + instead. If "init" is not NIL, then it must point to an array of objects + that contains at least "number". The "flags" are a value based on the + special flags being added bit-wise. If "flags" contains SIMPLE_COPY, then + memmove() is used rather than using the C++ object's assignment operator. + Note that SIMPLE_COPY will NOT work if the templated object has a regular + constructor or assignment operator, since those methods will not be + called on copying. If the "flags" contain EXPONENTIAL_GROWTH, then the + true allocation size will be doubled every time a new allocation is + required. when the FLUSH_INVISIBLE flag is included, then the array + elements that go out of scope are returned to the state provided by + the content's default constructor. this ensures that if they ever come + back into scope, they do not yet have any contents. further, if the + elements had any deep contents, those resources should be released. */ + + array(const array ©_from); + //!< copies the contents & sizing information from "copy_from". + + virtual ~array(); //!< destroys the memory allocated for the objects. + + void reset(int number = 0, const contents *initial_contents = NIL); + //!< Resizes this array and sets the contents from an array of contents. + /*< If "initial_contents" is not NIL, then it must contain an array of + "contents" with at least "number" objects in it. If it is NIL, then + the size of the array is changed but the contents are not. note that + any pre-existing elements that were previously out of range might still + have their prior contents; the newly available elements are not necessarily + "blank". thus, before using them, ensure you store appropriate elements + in those positions. */ + + array &operator = (const array ©_from); + //!< Copies the array in "copy_from" into this. + + int length() const { return c_active_length; } + //!< Returns the current reported length of the allocated C array. + + int last() const { return c_active_length - 1; } + //!< Returns the last valid element in the array. + + int flags() const { return c_flags; } + //!< Provides the raw flags value, without interpreting what it means. + + bool exponential() const { return c_flags & EXPONENTIAL_GROWTH; } + //!< Returns true if this allocator will grow exponentially on resize. + + bool simple() const { return c_flags & SIMPLE_COPY; } + //!< Reports whether the templated object is a simple type or not. + + const contents &get(int index) const; + //!< Accesses individual objects stored in "this" at the "index" position. + /*!< If the index is out of range, then a bogus reference (to internally + held garbage) is returned. */ + contents &use(int index); + //!< A non-constant version of get(); the returned object can be modified. + + const contents &operator [] (int index) const { return get(index); } + //!< Synonym for get that provides the expected array indexing syntax. + contents &operator [] (int index) { return use(index); } + //!< Synonym for use that provides the expected array indexing syntax. + + outcome put(int index, const contents &to_put); + //!< Stores an object at the index "index" in the array. + /*!< The outcome is "OUT_OF_RANGE" if the index does not exist. */ + + array concatenation(const array &to_concatenate) const; + //!< Returns the concatenation of "this" and the array "to_concatenate". + array concatenation(const contents &to_concatenate) const; + //!< Returns the concatenation of "this" and the object "to_concatenate". + + array &concatenate(const array &to_concatenate); + //!< Appends the array "to_concatenate" onto "this" and returns "this". + array &concatenate(const contents &to_concatenate); + //!< Appends the object "to_concatenate" onto "this" and returns "this". + array &concatenate(const contents *to_concatenate, int length); + //!< Concatenates a C-array "to_concatenate" onto "this" and returns "this". + /*!< There must be at least "length" elements in "to_concatenate". */ + + array operator + (const array &to_cat) const + { return concatenation(to_cat); } + //!< Synonym for concatenation. + array operator + (const contents &to_concatenate) const + { return concatenation(to_concatenate); } + //!< Synonym for concatenation. + array &operator += (const array &to_concatenate) + { return concatenate(to_concatenate); } + //!< Synonym for concatenate that modifies "this". + array &operator += (const contents &to_concatenate) + { return concatenate(to_concatenate); } + //!< Synonym for concatenate that modifies "this". + + const contents *observe() const { return c_offset; } + //!< Returns a pointer to the underlying C array of data. + /*!< The array contains "length()" number of objects in it. BE CAREFUL. + This version is a constant peek at that pointer. */ + contents *access() { return c_offset; } + //!< A non-constant access of the underlying C-array. BE REALLY CAREFUL. + + void swap_contents(array &other); + //!< Exchanges the contents of "this" and "other". + /*!< No validation is performed but this should always succeed given + arrays constructed properly. */ + + void snarf(array &new_contents); + //!< Drops "this" array's contents into the dustbin and uses "new_contents". + /*!< Afterwards, "new_contents" is an empty array and what used to be + stored there is now in "this" instead. */ + + array subarray(int start, int end) const; + //!< Returns the array segment between the indices "start" and "end". + /*!< This is all characters from "start" to "end" inclusively, as long as + those values are valid for this array. Even then, an intelligent default + is usually assumed if the indices are out of range. */ + + outcome insert(int index, int new_indices); + //!< Adds "new_indices" new positions for objects into the array at "index". + + outcome overwrite(int index, const array &write_with, int count = -1); + //!< Stores the array "write_with" into the current array at the "index". + /*!< The current contents are overwritten with "write_with". If the + index is invalid, then OUT_OF_RANGE is returned. If the "write_with" + array cannot fit due to the boundaries of "this" array, then only the + portion that can fit is used. If "count" is negative, the whole + "write_with" array is used; otherwise, only "count" elements are used. */ + + outcome stuff(int length, contents *to_stuff) const; + //!< Copies at most "length" elements from this into the array "to_stuff". + /*!< This call will fail disastrously if "length" is larger than the + array "to_stuff"'s allocated length. */ + + outcome resize(int new_size, how_to_copy way = NEW_AT_END); + //!< Changes the size of the C array to "new_size". + /*!< If "way" is NEW_AT_END and the array grows, then new space is added + at the end and the prefix of the array is the same as the old array. If + the "way" is NEW_AT_END, but the array shrinks, then the new array is a + prefix of the old array. If "way" is NEW_AT_BEGINNING and the array + grows, then the suffix of the array is the same as the old one and the + space is added at the beginning. if the "way" is NEW_AT_BEGINNING but the + array shrinks, then the new array is a suffix of the old array. if "way" + is DONT_COPY, then the old contents are not copied. keep in mind that + any newly visible elements can be in an arbitrary state and will not + necessarily be freshly constructed. */ + + outcome zap(int start, int end); + //!< Deletes from "this" the objects inclusively between "start" and "end". + /*!< C-array conventions are used (0 through length()-1 are valid if + length() > 0). If either index is out of range, then a default is + assumed. */ + + outcome shrink(); + //!< Cuts loose any allocated space that is beyond the real length. + + outcome retrain(int new_size, const contents *to_copy); + //!< Resizes the C array and stuffs it with the contents in "to_copy". + + enum shift_directions { TO_LEFT, TO_RIGHT }; + //!< All the real contents can be shifted to either the left or right. + + void shift_data(shift_directions where); + //!< The valid portion of the array is moved to the left or right. + /*!< This means that the unused space (dictated by the offset where the + data starts) will be adjusted. This may involve copying the data. */ + + // These are gritty internal information methods and should not be used + // except by appropriately careful code. + int internal_real_length() const { return c_real_length; } + //!< Gritty Internal: the real allocated length. + int internal_offset() const { return int(c_offset - c_mem_block); } + //!< Gritty Internal: the offset from real start to stored data. + const contents *internal_block_start() const { return c_mem_block; } + //!< Gritty Internal: constant peek at the real allocated pointer. + contents *internal_block_start() { return c_mem_block; } + //!< Gritty Internal: the real allocated pointer made accessible. + contents * const *internal_offset_mem() const { return &c_offset; } + //!< Gritty Internal: the start of the actual stored data. + +private: + int c_active_length; //!< the number of objects reported to be in the array. + int c_real_length; // the real number of objects that can be stored. + contents *c_mem_block; //!< a pointer to the objects held for this array. + contents *c_offset; //!< the beginning of the useful part of the memory block. + int c_flags; //!< records the special characteristics of this array. + + outcome allocator_reset(int initial_elements, int blocking); + //!< Allocates space for the "initial_elements" plus the "blocking" factor. + /*!< This resets the C-array to have "initial_elements" indices, plus an + extra pre-allocation of "blocking" that leaves room for expansion. Any + prior contents are whacked. */ +}; + +////////////// + +//! A simple object that wraps a templated array of ints. +class int_array : public array +{ +public: + int_array(int number = 0, const int *initial_contents = 0) + : root_object(), + array(number, initial_contents, SIMPLE_COPY | EXPONE) {} + //!< Constructs an array of "number" integers. + /*!< creates a list of ints based on an initial "number" of entries and + some "initial_contents", which should be a regular C array of ints + with at least as many entries as "number". */ +}; + +////////////// + +//! An array of double floating point numbers. +class double_array : public array +{ +public: + double_array(int len = 0, double *data = NIL) + : root_object(), + array(len, data, SIMPLE_COPY | EXPONE) {} + double_array(const array &to_copy) : array(to_copy) {} +}; + +////////////// + +// implementation code, much longer methods below... + +// GOALS: +// +// 1) provide a slightly smarter allocation method for C arrays and other +// contiguous-storage container classes with better speed and reduced memory +// fragmentation through pre-allocation. this can reduce memory thrashing +// when doing appends and inserts that can be granted with previously +// allocated, but unused, space. +// 2) clean-up bounds failure cases in functions that return a reference by +// always having at least one bogus element in the array for returns. this +// really just requires that we never allow our hidden real length of the +// array to be zero. + +template +array::array(int num, const contents *init, int flags) +: root_object(), c_active_length(0), c_real_length(0), c_mem_block(NIL), c_offset(NIL), c_flags(flags) +{ + if (c_flags > 7) { +#ifdef DEBUG_ARRAY + throw "error: array::constructor: error in parameters! still passing a block size?"; +#endif + c_flags = EXPONE | FLUSH_INVISIBLE; + // drop simple copy, since the caller doesn't know what they're doing. + } + + allocator_reset(num, 1); // get some space. + retrain(num, init); // plug in their contents. +} + +template +array::array(const array &cf) +: root_object(), c_active_length(0), c_real_length(0), c_mem_block(NIL), c_offset(NIL), c_flags(cf.c_flags) +{ + allocator_reset(cf.c_active_length, 1); // get some space. + operator = (cf); // assignment operator does the rest. +} + +template +array::~array() +{ + c_offset = NIL; + if (c_mem_block) delete [] c_mem_block; + c_mem_block = NIL; + c_active_length = 0; + c_real_length = 0; +} + +template +void array::reset(int num, const contents *init) +{ retrain(num, init); } + +template +array &array::operator =(const array &cf) +{ + if (this == &cf) return *this; + c_flags = cf.c_flags; // copy the flags coming in from the other object. + // prepare the array for retraining... + c_offset = c_mem_block; // slide the offset back to the start. + c_active_length = 0; // the length gets reset also. + retrain(cf.c_active_length, cf.observe()); + return *this; +} + +template +contents &array::use(int index) +{ + bounds_return(index, 0, this->last(), bogonic()); + return this->access()[index]; +} + +template +const contents &array::get(int index) const +{ + bounds_return(index, 0, this->last(), bogonic()); + return this->observe()[index]; +} + +template +array &array::concatenate(const array &s1) +{ + // check whether there's anything to concatenate. + if (!s1.length()) return *this; + if (this == &s1) { + // make sure they don't concatenate this array to itself. + return concatenate(array(*this)); + } + int old_len = this->length(); + resize(this->length() + s1.length(), NEW_AT_END); + overwrite(old_len, s1); + return *this; +} + +template +array &array::concatenate(const contents &to_concatenate) +{ + resize(this->length() + 1, NEW_AT_END); + if (!this->simple()) + this->access()[this->last()] = to_concatenate; + else + memcpy(&(this->access()[this->last()]), &to_concatenate, sizeof(contents)); + return *this; +} + +template +array &array::concatenate(const contents *to_concatenate, + int length) +{ + if (!length) return *this; // nothing to do. + const int old_len = this->length(); + resize(this->length() + length, NEW_AT_END); + if (!this->simple()) + for (int i = 0; i < length; i++) + this->access()[old_len + i] = to_concatenate[i]; + else + memcpy(&(this->access()[old_len]), to_concatenate, + length * sizeof(contents)); + return *this; +} + +template +array array::concatenation(const array &s1) const +{ + // tailor the return array to the new size needed. + array to_return(this->length() + s1.length(), NIL, s1.c_flags); + to_return.overwrite(0, *this); // put the first part into the new array. + to_return.overwrite(this->length(), s1); // add the second segment. + return to_return; +} + +template +array array::concatenation(const contents &s1) const +{ + array to_return(this->length() + 1, NIL, c_flags); + to_return.overwrite(0, *this); + if (!this->simple()) + to_return.access()[to_return.last()] = s1; + else + memcpy(&(to_return.access()[to_return.last()]), &s1, sizeof(contents)); + return to_return; +} + +template +array array::subarray(int start, int end) const +{ + bounds_return(start, 0, this->last(), array(0, NIL, c_flags)); + bounds_return(end, 0, this->last(), array(0, NIL, c_flags)); + if (start > end) return array(0, NIL, c_flags); + return array(end - start + 1, &(this->observe()[start]), c_flags); +} + +template +void array::swap_contents(array &other) +{ + if (this == &other) return; // already swapped then, i suppose. + swap_values(this->c_active_length, other.c_active_length); + swap_values(this->c_real_length, other.c_real_length); + swap_values(this->c_offset, other.c_offset); + swap_values(this->c_mem_block, other.c_mem_block); + swap_values(this->c_flags, other.c_flags); +} + +template +outcome array::shrink() +{ + if (!c_mem_block) return common::OUT_OF_MEMORY; + if (c_active_length == c_real_length) return common::OKAY; // already just right. + array new_holder(*this); + // create a copy of this object that is just the size needed. + swap_contents(new_holder); + // swap this object with the copy, leaving the enlarged version behind + // for destruction. + return common::OKAY; +} + +template +outcome array::stuff(int lengthx, contents *to_stuff) const +{ + if (!lengthx || !this->length()) return common::OKAY; + int copy_len = minimum(lengthx, this->length()); + if (!this->simple()) { + for (int i = 0; i < copy_len; i++) + to_stuff[i] = this->observe()[i]; + } else { + memcpy(to_stuff, this->observe(), copy_len * sizeof(contents)); + } + return common::OKAY; +} + +template +outcome array::overwrite(int position, + const array &write_with, int count) +{ + if (!count) return common::OKAY; + if ( (this == &write_with) || !this->length() || !write_with.length()) + return common::BAD_INPUT; + bounds_return(position, 0, this->last(), common::OUT_OF_RANGE); + if ( negative(count) || (count > write_with.length()) ) + count = write_with.length(); + if (position > this->length() - count) + count = this->length() - position; + if (!this->simple()) { + for (int i = position; i < position + count; i++) + this->access()[i] = write_with.observe()[i - position]; + } else { + memcpy(&(this->access()[position]), write_with.observe(), + count * sizeof(contents)); + } + return common::OKAY; +} + +template +outcome array::allocator_reset(int initial, int blocking) +{ +// FUNCDEF("allocator_reset") + if (blocking < 1) { +#ifdef DEBUG_ARRAY + throw "error: array::allocator_reset: has bad block size"; +#endif + blocking = 1; + } + if (initial < 0) initial = 0; // no antimatter arrays. + if (c_mem_block) { + // remove old contents. + delete [] c_mem_block; + c_mem_block = NIL; + c_offset = NIL; + } + c_active_length = initial; // reset the length to the reporting size. + c_real_length = initial + blocking; // compute the real length. + if (c_real_length) { + c_mem_block = new contents[c_real_length]; + if (!c_mem_block) { + // this is an odd situation; memory allocation didn't blow out an + // exception, but the memory block is empty. let's consider that + // a fatal error; we can't issue empty objects. + throw common::OUT_OF_MEMORY; + } + c_offset = c_mem_block; // reset offset to start of array. + } + return common::OKAY; +} + +template +void array::shift_data(shift_directions where) +{ + if (where == TO_LEFT) { + // we want to end up with the data jammed up against the left edge. thus + // we need the offset to be zero bytes from start. + if (c_offset == c_mem_block) + return; // offset already at start, we're done. + // well, we need to move the data. + if (simple()) { + memmove(c_mem_block, c_offset, c_active_length * sizeof(contents)); + } else { + for (contents *ptr = c_offset; ptr < c_offset + c_active_length; ptr++) + c_mem_block[ptr - c_offset] = *ptr; + } + c_offset = c_mem_block; // we've ensured that this is correct. + if (c_flags & FLUSH_INVISIBLE) { + // we need to clean up what might have had contents previously. +// for (contents *p = c_mem_block + c_active_length; p < c_mem_block + c_real_length; p++) +// *p = contents(); + } + } else { + // we want to move the data to the right, so the offset should be the + // difference between the real length and the length. + if (c_offset == c_mem_block + c_real_length - c_active_length) + return; // the offset is already the right size. + if (simple()) { + memmove(&c_mem_block[c_real_length - c_active_length], c_offset, c_active_length * sizeof(contents)); + } else { + for (int i = c_real_length - 1; i >= c_real_length - c_active_length; i--) + c_mem_block[i] = c_offset[i - c_real_length + c_active_length]; + } + c_offset = c_mem_block + c_real_length - c_active_length; // we've now ensured this. + if (c_flags & FLUSH_INVISIBLE) { + // we need to clean up the space on the left where old contents might be. +// for (contents *p = c_mem_block; p < c_offset; p++) +// *p = contents(); + } + } +} + +template +outcome array::resize(int new_size, how_to_copy way) +{ +/// FUNCDEF("resize"); + if (new_size < 0) new_size = 0; // we stifle this. + if (new_size == c_active_length) { + // nothing much to do. + return common::OKAY; + } + // okay, now we at least know that the sizes are different. we save all the + // old information about the array prior to this resizing. + contents *old_s = c_mem_block; // save the old contents... + const int old_len = c_active_length; // and length. + contents *old_off = c_offset; // and offset. + bool delete_old = false; // if true, old memory is whacked once it's copied. + +//hmmm: wasn't there a nice realization that we could bail out early in +// the case where the size is already suffcient? there seems to be +// an extraneous copy case here. +// also it would be nice to have better, more descriptive names for the +// variables here since they are not lending themselves to understanding +// the algorithm. + + // we check whether there's easily enough space in the array already. + // if not, then we have some more decisions to make. + if (c_real_length - (old_off - old_s) < new_size) { + // well, there's not enough space with the current space and offset. + if (c_real_length < new_size) { + // there's really not enough space overall, no fooling. we now will + // create a new block. + c_mem_block = NIL; // zero out the pointer so reset doesn't delete it. + delete_old = true; + int blocking = 1; + if (exponential()) blocking = new_size + 1; + outcome ret = allocator_reset(new_size, blocking); + if (ret != common::OKAY) { + // failure, but don't forget to whack the old glob. +#ifdef DEBUG_ARRAY + throw "error: array::resize: saw array reset failure"; +#endif + delete [] old_s; + return ret; + } + // fall out to the copying phase, now that we have some fresh memory. + } else { + // there is enough space if we shift some things around. + const int size_difference = new_size - c_active_length; + // we compute how much space has to be found in the array somewhere + // to support the new larger size. + if (way == DONT_COPY) { + // simplest case; just reset the offset appropriately so the new_size + // will fit. + c_offset = c_mem_block; + c_active_length = new_size; + } else if (way == NEW_AT_BEGINNING) { + // if the new space is at the beginning, there are two cases. either + // the new size can be accomodated by the current position of the + // data or the data must be shifted to the right. + if (c_offset - c_mem_block < size_difference) { + // we need to shift the data over to the right since the offset isn't + // big enough for the size increase. + shift_data(TO_RIGHT); // resets the offset appropriately. + } + // now we know that the amount of space prior to the real data + // is sufficient to hold what new space is needed. we just need to + // shift the offset back somewhat. + c_offset -= size_difference; + c_active_length = new_size; + } else { + // better only be three ways to do this; we're now assuming the new + // space should be at the end (NEW_AT_END). + // now that we're here, we know there will be enough space if we shift + // the block to the left, but we DO NEED to do this. if we didn't need + // to shift the data, then we would find that: + // c_real_length - old_off >= new_size + // which is disallowed by the guardian conditional around this area. + shift_data(TO_LEFT); // resets the offset for us. + c_active_length = new_size; + } + // we have ensured that we had enough space and we have already shifted + // the data around appropriately. we no longer need to enter the next + // block where we would copy data around if we had to. it has become + // primarily for cases where either we have to copy data because we + // have new storage to fill or where we are shrinking the array. + return common::OKAY; + } + } + + // the blob of code below is offset invariant. by the time we reach here, + // the array should be big enough and the offset should be okay. + c_active_length = new_size; // set length to the new reporting size. + if (way != DONT_COPY) { + int where = 0; // offset for storing into new array. + bool do_copy = false; // if true, then we need to move the data around. + contents *loopc_offset_old = old_off; // offset into original object. + // we should only have to copy the memory in one other case besides our + // inhabiting new memory--when we are asked to resize with the new stuff + // at the beginning of the array. if the new space is at the end, we're + // already looking proper, but if the new stuff is at the beginning, we + // need to shift existing stuff downwards. + if (way == NEW_AT_BEGINNING) { + where = new_size - old_len; // move up existing junk. + if (where) do_copy = true; // do copy, since it's not immobile. + if (where < 0) { + // array shrank; we need to do the loop differently for starting + // from the beginning. we skip to the point in the array that our + // suffix needs to start at. + loopc_offset_old -= where; + where = 0; // reset where so we don't have negative offsets. + } + } + const int size_now = minimum(old_len, c_active_length); + if (delete_old || do_copy) { + contents *offset_in_new = c_offset + where; + contents *posn_in_old = loopc_offset_old; + if (simple()) { + // memmove should take care of intersections. + memmove(offset_in_new, posn_in_old, size_now * sizeof(contents)); + } else { + // we need to do the copies using the object's assignment operator. + if (new_size >= old_len) { + for (int i = size_now - 1; i >= 0; i--) + offset_in_new[i] = posn_in_old[i]; + } else { + for (int i = 0; i < size_now; i++) + offset_in_new[i] = posn_in_old[i]; + } + } + + // we only want to flush the obscured elements when we aren't already + // inhabiting new space. + if ( (c_flags & FLUSH_INVISIBLE) && !delete_old) { + // clear out the space that just went out of scope. we only do this + // for the flushing mode and when we aren't starting from a fresh + // pointer (i.e., delete_old isn't true). + if (new_size < old_len) { +// for (contents *p = posn_in_old; p < offset_in_new; p++) +// *p = contents(); + } + } + } + } + if (delete_old) delete [] old_s; + return common::OKAY; +} + +template +outcome array::retrain(int new_len, const contents *to_set) +{ +/// FUNCDEF("retrain") + if (new_len < 0) new_len = 0; // stifle that bad length. +#ifdef DEBUG_ARRAY + if (to_set && (c_mem_block >= to_set) && (c_mem_block < to_set + new_len) ) { + throw "error: array::retrain: ranges overlap in retrain!"; + } +#endif + outcome ret = resize(new_len, DONT_COPY); + if (ret != common::OKAY) return ret; +#ifdef DEBUG_ARRAY + if (new_len != c_active_length) { + throw "error: array resize set the wrong length"; + } +#endif + if (to_set) { + if (simple()) + memcpy(c_offset, to_set, c_active_length * sizeof(contents)); + else + for (int i = 0; i < c_active_length; i++) + c_offset[i] = to_set[i]; + } else { + if (c_flags & FLUSH_INVISIBLE) { + // no contents provided, so stuff the space with blanks. +// for (int i = 0; i < c_active_length; i++) c_offset[i] = contents(); + } + } + if (c_flags & FLUSH_INVISIBLE) { +// for (contents *ptr = c_mem_block; ptr < c_offset; ptr++) +// *ptr = contents(); +// for (contents *ptr = c_offset + c_active_length; ptr < c_mem_block + c_real_length; ptr++) +// *ptr = contents(); + } + return common::OKAY; +} + +template +outcome array::zap(int position1, int position2) +{ + if (position1 > position2) return common::OKAY; + bounds_return(position1, 0, c_active_length - 1, common::OUT_OF_RANGE); + bounds_return(position2, 0, c_active_length - 1, common::OUT_OF_RANGE); + if (!position1) { + // if they're whacking from the beginning, we just reset the offset. + c_offset += position2 + 1; + c_active_length -= position2 + 1; + return common::OKAY; + } + const int difference = position2 - position1 + 1; + // copy from just above position2 down into position1. + if (simple()) { + if (c_active_length - difference - position1 > 0) + memmove(&c_offset[position1], &c_offset[position1 + difference], + (c_active_length - difference - position1) * sizeof(contents)); + } else { + for (int i = position1; i < c_active_length - difference; i++) + c_offset[i] = c_offset[i + difference]; + } + + outcome ret = resize(c_active_length - difference, NEW_AT_END); + // chop down to new size. +#ifdef DEBUG_ARRAY + if (ret != common::OKAY) { + throw "error: array::zap: resize failure"; + return ret; + } +#endif + return ret; +} + +template +outcome array::insert(int position, int elem_to_add) +{ + if (position < 0) return common::OUT_OF_RANGE; + if (position > this->length()) + position = this->length(); + if (elem_to_add < 0) return common::OUT_OF_RANGE; + how_to_copy how = NEW_AT_END; + if (position == 0) how = NEW_AT_BEGINNING; + resize(this->length() + elem_to_add, how); + + // if the insert wasn't at the front, we have to copy stuff into the new + // locations. + if (how == NEW_AT_END) { + const contents simple_default_object = contents(); + if (!this->simple()) { + for (int i = this->last(); i >= position + elem_to_add; i--) + this->access()[i] = this->observe()[i - elem_to_add]; + for (int j = position; j < position + elem_to_add; j++) + this->access()[j] = simple_default_object; + } else { + memmove(&(this->access()[position + elem_to_add]), + &(this->observe()[position]), (this->length() - position + - elem_to_add) * sizeof(contents)); + for (int j = position; j < position + elem_to_add; j++) + memcpy(&this->access()[j], &simple_default_object, sizeof(contents)); + } + } + return common::OKAY; +} + +template +outcome array::put(int index, const contents &to_put) +{ + bounds_return(index, 0, this->last(), common::OUT_OF_RANGE); + if (!this->simple()) + this->access()[index] = to_put; + else + memcpy(&(this->access()[index]), &to_put, sizeof(contents)); + return common::OKAY; +} + +template +void array::snarf(array &new_contents) +{ + if (this == &new_contents) return; // no blasting own feet off. + reset(); // trash our current storage. + swap_contents(new_contents); +} + +/* +//! a simple wrapper of an array of char *, used by portable::break_line(). +class char_star_array : public array +{ +public: + char_star_array() : array(0, NIL, SIMPLE_COPY | EXPONE + | FLUSH_INVISIBLE) {} + ~char_star_array() { + // clean up all the memory we're holding. + for (int i = 0; i < length(); i++) { + delete [] (use(i)); + } + } +}; +*/ + +} //namespace + +#undef static_class_name + +#endif + diff --git a/core/library/basis/astring.cpp b/core/library/basis/astring.cpp new file mode 100644 index 00000000..55b9daf8 --- /dev/null +++ b/core/library/basis/astring.cpp @@ -0,0 +1,1096 @@ +/* +* Name : astring +* Author : Chris Koeritz +** +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include "astring.h" +#include "definitions.h" +#include "functions.h" +#include "guards.h" + +#include +#include +#include + +#ifdef __WIN32__ + #undef strcasecmp + #undef strncasecmp + #define strcasecmp strcmpi + #define strncasecmp strnicmp +#endif + +//#define DEBUG_STRING + // uncomment for debugging version. + +#define no_increment + // macro just documents a blank parameter in the code. + +namespace basis { + +const int LONGEST_SPRINTF = 600; // the longest a simple sprintf can be here. + +const char CASE_DIFFERENCE = char('A' - 'a'); + // the measurement of the difference between upper and lower case. + +// this factor is used to bias dynamic sprintfs for cases where the length +// is specified, but the actual string is shorter than that length. +const int MAX_FIELD_FUDGE_FACTOR = 64; + +const abyte empty_char_star[] = { 0 }; + // used to initialize empty strings. + +////////////// + +bool astring_comparator(const astring &a, const astring &b) { return a.equal_to(b); } + +int calculate_proper_length(int repeat) { return negative(repeat)? 1 : repeat + 1; } + +////////////// + +astring::astring() +: c_character_manager(1, empty_char_star), + c_held_string((char * const *)c_character_manager.internal_offset_mem()) +{} + +astring::astring(const base_string &initial) +: c_character_manager(strlen(initial.observe()) + 1, (abyte *)initial.observe()), + c_held_string((char * const *)c_character_manager.internal_offset_mem()) +{} + +astring::astring(char initial, int repeat) +: c_character_manager(calculate_proper_length(repeat)) +{ + if (!initial) initial = ' '; // for nulls, we use spaces. + int new_size = c_character_manager.length() - 1; + memset(c_character_manager.access(), initial, new_size); + c_character_manager.put(new_size, '\0'); + c_held_string = (char * const *)c_character_manager.internal_offset_mem(); +} + +astring::astring(const astring &s1) +: base_string(), + c_character_manager(s1.c_character_manager), + c_held_string((char * const *)c_character_manager.internal_offset_mem()) +{ +} + +astring::astring(const char *initial) +: c_character_manager(calculate_proper_length(initial? int(strlen(initial)) : 0)) +{ + c_character_manager.put(0, '\0'); + if (!initial) return; // bail because there's no string to copy. + strcpy(access(), initial); + c_held_string = (char * const *)c_character_manager.internal_offset_mem(); +} + +astring::astring(special_flag flag, const char *initial, ...) +: c_character_manager(1, empty_char_star), + c_held_string((char * const *)c_character_manager.internal_offset_mem()) +{ + if (!initial) return; + if ( (flag != UNTERMINATED) && (flag != SPRINTF) ) { + operator = (astring(astring::SPRINTF, "unknown flag %d", flag)); + return; + } + + va_list args; + va_start(args, initial); + + if (flag == UNTERMINATED) { + // special process for grabbing a string that has no terminating nil. + int length = va_arg(args, int); // get the length of the string out. + c_character_manager.reset(length, (abyte *)initial); + c_character_manager += abyte(0); + va_end(args); + return; + } + + // only other flag currently supported is sprintf, so we do that... + base_sprintf(initial, args); + va_end(args); +} + +astring::~astring() { c_held_string = NIL; } + +const astring &astring::empty_string() { return bogonic(); } + +void astring::text_form(base_string &state_fill) const { state_fill.assign(*this); } + +int astring::length() const { return c_character_manager.length() - 1; } + +byte_array &astring::get_implementation() { return c_character_manager; } + +char *astring::access() { return (char *)c_character_manager.access(); } + +char astring::get(int index) const { return (char)c_character_manager.get(index); } + +const char *astring::observe() const +{ return (const char *)c_character_manager.observe(); } + +bool astring::equal_to(const equalizable &s2) const +{ + const astring *s2_cast = cast_or_throw(s2, *this); + return comparator(*s2_cast) == 0; +} + +bool astring::less_than(const orderable &s2) const +{ + const astring *s2_cast = dynamic_cast(&s2); + if (!s2_cast) throw "error: astring::<: unknown type"; + return comparator(*s2_cast) < 0; +} + +int astring::comparator(const astring &s2) const +{ return strcmp(observe(), s2.observe()); } + +bool astring::equal_to(const char *that) const +{ return strcmp(observe(), that) == 0; } + +bool astring::contains(const astring &to_find) const +{ return (find(to_find, 0) < 0) ? false : true; } + +astring &astring::operator += (const astring &s1) +{ insert(length(), s1); return *this; } + +void astring::shrink() +{ + astring copy_of_this(observe()); + c_character_manager.swap_contents(copy_of_this.c_character_manager); +} + +astring &astring::sprintf(const char *initial, ...) +{ + va_list args; + va_start(args, initial); + astring &to_return = base_sprintf(initial, args); + va_end(args); + return to_return; +} + +astring &astring::base_sprintf(const char *initial, va_list &args) +{ + reset(); + if (!initial) return *this; // skip null strings. + if (!initial[0]) return *this; // skip empty strings. + + // these accumulate parts of the sprintf format within the loop. + char flag_chars[23], width_chars[23], precision_chars[23], modifier_chars[23]; + + // thanks for the inspiration to k&r page 156. + for (const char *traverser = initial; *traverser; traverser++) { +#ifdef DEBUG_STRING + printf("index=%d, char=%c\n", traverser - initial, *traverser); +#endif + + if (*traverser != '%') { + // not a special character, so just drop it in. + *this += *traverser; + continue; + } + traverser++; // go to the next character. +#ifdef DEBUG_STRING + printf("index=%d, char=%c\n", traverser - initial, *traverser); +#endif + if (*traverser == '%') { + // capture the "%%" style format specifier. + *this += *traverser; + continue; + } + bool failure = false; + // becomes set to true if something didn't match in a necessary area. + + seek_flag(traverser, flag_chars, failure); + if (failure) { + *this += '%'; + *this += flag_chars; + continue; + } + seek_width(traverser, width_chars); + seek_precision(traverser, precision_chars); + seek_modifier(traverser, modifier_chars); + get_type_character(traverser, args, *this, flag_chars, + width_chars, precision_chars, modifier_chars); + } + return *this; +} + +void astring::seek_flag(const char *&traverser, char *flag_chars, bool &failure) +{ + flag_chars[0] = '\0'; + failure = false; + bool keep_going = true; + while (!failure && keep_going) { + switch (*traverser) { + case '-': case '+': case ' ': case '\011': case '#': + flag_chars[strlen(flag_chars) + 1] = '\0'; + flag_chars[strlen(flag_chars)] = *traverser++; + break; + default: + // we found a character that doesn't belong in the flags. + keep_going = false; + break; + } + } +#ifdef DEBUG_STRING + if (strlen(flag_chars)) printf("[flag=%s]\n", flag_chars); + else printf("no flags\n"); +#endif +} + +void astring::seek_width(const char *&traverser, char *width_chars) +{ + width_chars[0] = '\0'; + bool no_more_nums = false; + bool first_num = true; + while (!no_more_nums) { + char wideness[2] = { *traverser, '\0' }; + if (first_num && (wideness[0] == '0')) { + strcpy(width_chars, wideness); + traverser++; + } else if (first_num && (wideness[0] == '*') ) { + strcpy(width_chars, wideness); + traverser++; + no_more_nums = true; + } else if ( (wideness[0] <= '9') && (wideness[0] >= '0') ) { + // a failure? + strcat(width_chars, wideness); + traverser++; + } else no_more_nums = true; + first_num = false; + } +#ifdef DEBUG_STRING + if (strlen(width_chars)) printf("[width=%s]\n", width_chars); + else printf("no widths\n"); +#endif +} + +void astring::seek_precision(const char *&traverser, char *precision_chars) +{ + precision_chars[0] = '\0'; + if (*traverser != '.') return; + strcpy(precision_chars, "."); + traverser++; + bool no_more_nums = false; + bool first_num = true; + while (!no_more_nums) { + char preciseness[2] = { *traverser, '\0' }; + if (first_num && (preciseness[0] == '0')) { + strcat(precision_chars, preciseness); + traverser++; + no_more_nums = true; + } else if (first_num && (preciseness[0] == '*') ) { + strcat(precision_chars, preciseness); + traverser++; + no_more_nums = true; + } else if ( (preciseness[0] <= '9') && (preciseness[0] >= '0') ) { + strcat(precision_chars, preciseness); + traverser++; + } else no_more_nums = true; + first_num = false; + } +#ifdef DEBUG_STRING + if (strlen(precision_chars)) printf("[precis=%s]\n", precision_chars); + else printf("no precision\n"); +#endif +} + +void astring::seek_modifier(const char *&traverser, char *modifier_chars) +{ + modifier_chars[0] = '\0'; + switch (*traverser) { + case 'F': case 'N': case 'h': case 'l': case 'L': { + modifier_chars[strlen(modifier_chars) + 1] = '\0'; + modifier_chars[strlen(modifier_chars)] = *traverser++; + break; + } + } +#ifdef DEBUG_STRING + if (strlen(modifier_chars)) printf("[mod=%s]\n", modifier_chars); + else printf("no modifiers\n"); +#endif +} + +void astring::get_type_character(const char * &traverser, va_list &args, + astring &output_string, const char *flag_chars, const char *width_chars, + const char *precision_chars, const char *modifier_chars) +{ + char formatting[120]; + strcpy(formatting, "%"); + strcat(formatting, flag_chars); + strcat(formatting, width_chars); + strcat(formatting, precision_chars); + strcat(formatting, modifier_chars); + char tmposh[2] = { *traverser, '\0' }; + strcat(formatting, tmposh); +#ifdef DEBUG_STRING + printf("format: %s\n", formatting); +#endif + + enum argument_size { bits_8, bits_16, bits_32, bits_64, bits_80 }; + bool ints_are_32_bits; +#ifdef __WIN32__ + ints_are_32_bits = true; +#elif defined(__OS2__) + ints_are_32_bits = true; +#elif defined(__MSDOS__) + ints_are_32_bits = false; +#elif defined(__WIN32__) + ints_are_32_bits = false; +#else + ints_are_32_bits = true; +#endif + argument_size next_argument; + bool use_dynamic_sprintf = false; // for dynamic printing of strings only. + // get the type character first and ensure it's valid. + switch (*traverser) { + case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': + next_argument = bits_16; + if (ints_are_32_bits) next_argument = bits_32; + break; + case 'f': case 'e': case 'g': case 'E': case 'G': + next_argument = bits_64; + break; + case 'c': + next_argument = bits_8; + break; + case 's': + next_argument = bits_32; + use_dynamic_sprintf = true; + break; + case 'n': + next_argument = bits_32; //????? + break; + case 'p': + next_argument = bits_32; //???? + break; + default: + // this is an error; the character is not recognized, so spew out + // any characters accumulated so far as just themselves. +#ifdef DEBUG_STRING + printf("failure in type char: %c\n", *traverser); +#endif + output_string += formatting; + return; + } +/* hmmm: not supported yet. + if (width_chars && (width_chars[0] == '*')) { + } + if (precision_chars && (precision_chars[0] == '*')) { + } +*/ + if (strlen(modifier_chars)) { + switch (modifier_chars[0]) { + case 'N': // near pointer. + next_argument = bits_16; + if (ints_are_32_bits) next_argument = bits_32; + break; + case 'F': // far pointer. + next_argument = bits_32; + break; + case 'h': // short int. + next_argument = bits_16; + break; + case 'l': // long. + next_argument = bits_32; + break; + case 'L': // long double; + next_argument = bits_80; + break; + default: + // a failure has occurred because the modifier character is not + // one of the recognized values. everything is just spewed out. +#ifdef DEBUG_STRING + printf("failure in modifier: %s\n", modifier_chars); +#endif + output_string += formatting; + return; + } + } + // action time: the output string is given a tasty value. + char temp[LONGEST_SPRINTF]; + char *temp2 = NIL; // for dynamic only. + switch (next_argument) { +//hmmm: this switch is where support would need to be added for having two +// arguments (for the '*' case). + case bits_8: case bits_16: + if (ints_are_32_bits) ::sprintf(temp, formatting, va_arg(args, long)); + else ::sprintf(temp, formatting, va_arg(args, int)); + break; + case bits_32: + if (use_dynamic_sprintf) { + // currently we only do dynamic sprintf for strings. + char *to_print = va_arg(args, char *); + // check if it's valid and if we really need to do it dynamically. + if (!to_print) { + // bogus string; put in a complaint. + use_dynamic_sprintf = false; + ::sprintf(temp, "{error:parm=NIL}"); + } else if (strlen(to_print) < LONGEST_SPRINTF - 2) { + // we're within our bounds, plus some safety room, so just do a + // regular sprintf. + use_dynamic_sprintf = false; + ::sprintf(temp, formatting, to_print); + } else { + // it's too long, so we definitely need to do it dynamically. + temp2 = new char[strlen(to_print) + MAX_FIELD_FUDGE_FACTOR]; + ::sprintf(temp2, formatting, to_print); + } + } else ::sprintf(temp, formatting, va_arg(args, void *)); + break; + case bits_64: + ::sprintf(temp, formatting, va_arg(args, double)); + break; + case bits_80: + ::sprintf(temp, formatting, va_arg(args, long double)); + break; + } + if (use_dynamic_sprintf) { + output_string += temp2; + delete [] temp2; + } else output_string += temp; +} + +//hmmm: de-redundify this function, which is identical to the constructor. +void astring::reset(special_flag flag, const char *initial, ...) +{ + reset(); // clear the string out. + if (!initial) return; + if ( (flag != UNTERMINATED) && (flag != SPRINTF) ) { + operator = (astring(astring::SPRINTF, "unknown flag %d", flag)); + return; + } + + va_list args; + va_start(args, initial); + + if (flag == UNTERMINATED) { + // special process for grabbing a string that has no terminating nil. + int length = va_arg(args, int); // get the length of the string out. + c_character_manager.reset(length, (abyte *)initial); + c_character_manager += abyte(0); + va_end(args); + return; + } + + // only other flag currently supported is sprintf, so we do that... + base_sprintf(initial, args); + va_end(args); +} + +void astring::pad(int len, char padding) +{ + if (length() >= len) return; + byte_array pad(len - length()); + memset(pad.access(), padding, pad.length()); + operator += (astring(UNTERMINATED, (char *)pad.observe(), pad.length())); +} + +void astring::trim(int len) +{ + if (length() <= len) return; + zap(len, end()); +} + +astring &astring::operator = (const astring &s1) +{ + if (this != &s1) + c_character_manager = s1.c_character_manager; + return *this; +} + +astring &astring::operator = (const char *s1) +{ + reset(); + *this += s1; + return *this; +} + +void astring::zap(int position1, int position2) +{ + bounds_return(position1, 0, end(), ); + bounds_return(position2, 0, end(), ); + c_character_manager.zap(position1, position2); +} + +void astring::to_lower() +{ + for (int i = 0; i < length(); i++) + if ( (get(i) >= 'A') && (get(i) <= 'Z') ) + c_character_manager.put(i, char(get(i) - CASE_DIFFERENCE)); +} + +void astring::to_upper() +{ + for (int i = 0; i < length(); i++) + if ( (get(i) >= 'a') && (get(i) <= 'z') ) + c_character_manager.put(i, char(get(i) + CASE_DIFFERENCE)); +} + +astring astring::lower() const +{ + astring to_return(*this); + to_return.to_lower(); + return to_return; +} + +astring astring::upper() const +{ + astring to_return(*this); + to_return.to_upper(); + return to_return; +} + +void astring::copy(char *array_to_stuff, int how_many) const +{ + if (!array_to_stuff) return; + array_to_stuff[0] = '\0'; + if ( (how_many <= 0) || (length() <= 0) ) return; + strncpy(array_to_stuff, observe(), minimum(how_many, int(length()))); + array_to_stuff[minimum(how_many, int(length()))] = '\0'; +} + +bool astring::iequals(const astring &that) const +{ return strcasecmp(observe(), that.observe()) == 0; } + +bool astring::iequals(const char *that) const +{ return strcasecmp(observe(), that) == 0; } + +int astring::ifind(char to_find, int position, bool reverse) const +{ return char_find(to_find, position, reverse, false); } + +int astring::find(char to_find, int position, bool reverse) const +{ return char_find(to_find, position, reverse, true); } + +int astring::find_any(const char *to_find, int position, bool reverse) const +{ return char_find_any(to_find, position, reverse, true); } + +int astring::ifind_any(const char *to_find, int position, bool reverse) const +{ return char_find_any(to_find, position, reverse, false); } + +int astring::find_non_match(const char *to_find, int position, + bool reverse) const +{ return char_find_any(to_find, position, reverse, false, true); } + +char simple_lower(char input) +{ + if ( (input <= 'Z') && (input >= 'A') ) return input - CASE_DIFFERENCE; + return input; +} + +int astring::char_find(char to_find, int position, bool reverse, + bool case_sense) const +{ + if (position < 0) return common::OUT_OF_RANGE; + if (position > end()) return common::OUT_OF_RANGE; + if (reverse) { + for (int i = position; i >= 0; i--) { + if (case_sense && (get(i) == to_find)) return i; + else if (simple_lower(get(i)) == simple_lower(to_find)) return i; + } + } else { + if (case_sense) { + const char *const pos = strchr(observe() + position, to_find); + if (pos) return int(pos - observe()); + } else { + for (int i = position; i < length(); i++) + if (simple_lower(get(i)) == simple_lower(to_find)) return i; + } + } + return common::NOT_FOUND; +} + +bool imatches_any(char to_check, const astring &list) +{ + for (int i = 0; i < list.length(); i++) + if (simple_lower(to_check) == simple_lower(list[i])) return true; + return false; +} + +bool matches_any(char to_check, const astring &list) +{ + for (int i = 0; i < list.length(); i++) + if (to_check == list[i]) return true; + return false; +} + +bool matches_none(char to_check, const astring &list) +{ + bool saw_match = false; + for (int i = 0; i < list.length(); i++) + if (to_check == list[i]) { + saw_match = true; + break; + } + return !saw_match; +} + +int astring::char_find_any(const astring &to_find, int position, bool reverse, + bool case_sense, bool invert_find) const +{ + if (position < 0) return common::OUT_OF_RANGE; + if (position > end()) return common::OUT_OF_RANGE; + if (reverse) { + for (int i = position; i >= 0; i--) { + if (!invert_find) { + if (case_sense && matches_any(get(i), to_find)) return i; + else if (imatches_any(get(i), to_find)) return i; + } else { +//printf("rev posn=%d char=%c", i, get(i)); + // case-sensitivity is not used for inverted finds. + if (matches_none(get(i), to_find)) return i; + } + } + } else { + for (int i = position; i < length(); i++) { + if (!invert_find) { + if (case_sense && matches_any(get(i), to_find)) return i; + else if (imatches_any(get(i), to_find)) return i; + } else { + // case-sensitivity is not used for inverted finds. +//printf("fwd posn=%d char=%c", i, get(i)); + if (matches_none(get(i), to_find)) return i; + } + } + } + return common::NOT_FOUND; +} + +int astring::find(const astring &to_find, int posn, bool reverse) const +{ return str_find(to_find, posn, reverse, true); } + +int astring::ifind(const astring &to_find, int posn, bool reverse) const +{ return str_find(to_find, posn, reverse, false); } + +int astring::str_find(const astring &to_find, int posn, bool reverse, + bool case_sense) const +{ + bounds_return(posn, 0, end(), common::OUT_OF_RANGE); + if (!to_find.length()) return common::BAD_INPUT; + + // skip some steps by finding the first place that the first character of + // the string resides in our string. + if (case_sense) + posn = find(to_find[0], posn, reverse); + else posn = ifind(to_find[0], posn, reverse); + if (posn < 0) return common::NOT_FOUND; + +//hmmm: there is a better way to do this loop in terms of the number of +// comparisons performed. knuth morris pratt algorithm? + if (case_sense) { +//hmmm: this could use strncmp too? + if (reverse) { + if (posn > length() - to_find.length()) + posn = length() - to_find.length(); + for (int i = posn; i >= 0; i--) + if (!memcmp((void *)&observe()[i], (void *)to_find.observe(), + to_find.length())) + return i; + } else { + const int find_len = to_find.length(); + const int str_len = length(); + const char first_char = to_find[0]; + bounds_return(posn, 0, str_len - find_len, common::OUT_OF_RANGE); + for (int i = posn - 1; + ( ( (i = find(first_char, i + 1)) >= 0) + && (str_len - i >= find_len) ); no_increment) { + if (!memcmp((void *)&observe()[i], (void *)to_find.observe(), + to_find.length())) + return i; + } + } + } else { + // not case-sensitive. + if (reverse) { + if (posn > length() - to_find.length()) + posn = length() - to_find.length(); + for (int i = posn; i >= 0; i--) + if (!strncasecmp(&observe()[i], to_find.observe(), to_find.length())) + return i; + } else { + bounds_return(posn, 0, length() - to_find.length(), common::OUT_OF_RANGE); + for (int i = posn; i < length() - to_find.length() + 1; i++) + if (!strncasecmp(&observe()[i], to_find.observe(), to_find.length())) + return i; + } + } + return common::NOT_FOUND; +} + +astring astring::operator + (const astring &s1) const +{ + astring to_return(*this); + to_return += s1; + return to_return; +} + +char &astring::operator [] (int position) +{ + if (position < 0) position = 0; + if (position > end()) position = 0; + abyte &found = c_character_manager.use(position); + char &to_return = *((char *)(&found)); + return to_return; +} + +const char &astring::operator [] (int position) const +{ + if (position < 0) position = 0; + if (position > end()) position = 0; + const abyte &found = c_character_manager.get(position); + const char &to_return = *((const char *)(&found)); + return to_return; +} + +int astring::convert(int default_value) const +{ + if (!length()) return default_value; + int to_return; + int fields = sscanf(observe(), "%d", &to_return); + if (fields < 1) return default_value; + return to_return; +} + +long astring::convert(long default_value) const +{ + if (!length()) return default_value; + long to_return; + int fields = sscanf(observe(), "%ld", &to_return); + if (fields < 1) return default_value; + return to_return; +} + +float astring::convert(float default_value) const +{ + if (!length()) return default_value; + float to_return; + int fields = sscanf(observe(), "%f", &to_return); + if (fields < 1) return default_value; + return to_return; +} + +double astring::convert(double default_value) const +{ + if (!length()) return default_value; + double to_return; + int fields = sscanf(observe(), "%lf", &to_return); + if (fields < 1) return default_value; + return to_return; +} + +astring &astring::operator += (const char *s1) +{ + if (!s1) return *this; + int len = length(); + c_character_manager.insert(len, int(strlen(s1))); + memmove((char *)&c_character_manager[len], s1, int(strlen(s1))); + return *this; +} + +astring &astring::operator += (char s1) +{ + int len = length(); + c_character_manager.insert(len, 1); + c_character_manager.put(len, s1); + return *this; +} + +bool astring::compare(const astring &to_compare, int start_first, + int start_second, int count, bool case_sensitive) const +{ + bounds_return(start_first, 0, end(), false); + bounds_return(start_second, 0, to_compare.end(), false); + bounds_return(start_first + count, start_first, length(), false); + bounds_return(start_second + count, start_second, to_compare.length(), false); + + if (!case_sensitive) { + return !strncasecmp(&observe()[start_first], + &to_compare.observe()[start_second], count); + } else { + return !memcmp((void *)&observe()[start_first], + (void *)&to_compare.observe()[start_second], count); + } +} + +/* +int astring::icompare(const char *to_compare, int length_in) const +{ + if (!length_in) return 0; // nothing is equal to nothing. + int real_len = length_in; + // if they're passing a negative length, we use the full length. + if (negative(length_in)) + real_len = length(); + // if we have no length, make the obvious returns now. + int to_compare_len = int(strlen(to_compare)); + if (!real_len) return to_compare_len? -1 : 0; + // if the second string is empty, it's always less than the non-empty. + if (!to_compare_len) return 1; + int to_return = strncasecmp(observe(), to_compare, real_len); + if (negative(length_in) && !to_return && (to_compare_len > length()) ) { + // catch special case for default length when the two are equal except + // that the second string is longer--this means the first is less than + // second, not equal. + return -1; + } else + return to_return; +} +*/ + +/* +bool astring::oy_icompare(const astring &to_compare, int start_first, + int start_second, int count) const +{ + bounds_return(start_first, 0, end(), false); + bounds_return(start_second, 0, to_compare.end(), false); + bounds_return(start_first + count, start_first, length(), false); + bounds_return(start_second + count, start_second, to_compare.length(), false); + const char *actual_first = this->observe() + start_first; + const char *actual_second = to_compare.observe() + start_second; + return !strncasecmp(actual_first, actual_second, count); +} +*/ + +bool astring::substring(astring &target, int start, int bender) const +{ + target.reset(); + if (bender < start) return false; + const int last = end(); // final position that's valid in the string. + bounds_return(start, 0, last, false); + bounds_return(bender, 0, last, false); + target.reset(UNTERMINATED, observe() + start, bender - start + 1); + return true; +} + +astring astring::substring(int start, int end) const +{ + astring to_return; + substring(to_return, start, end); + return to_return; +} + +astring astring::middle(int start, int count) +{ return substring(start, start + count - 1); } + +astring astring::left(int count) +{ return substring(0, count - 1); } + +astring astring::right(int count) +{ return substring(end() - count + 1, end()); } + +void astring::insert(int position, const astring &to_insert) +{ + bounds_return(position, 0, length(), ); + if (this == &to_insert) { + astring copy_of_me(to_insert); + insert(position, copy_of_me); // not recursive because no longer == me. + } else { + c_character_manager.insert(position, to_insert.length()); + c_character_manager.overwrite(position, to_insert.c_character_manager, + to_insert.length()); + } +} + +bool astring::replace(const astring &tag, const astring &replacement) +{ + int where = find(tag); + if (negative(where)) return false; + zap(where, where + tag.end()); + insert(where, replacement); + return true; +} + +bool astring::replace_all(const astring &to_replace, const astring &new_string) +{ + bool did_any = false; + for (int i = 0; i < length(); i++) { + int indy = find(to_replace, i); + if (negative(indy)) break; // get out since there are no more matches. + i = indy; // update our position to where we found the string. + zap(i, i + to_replace.length() - 1); // remove old string. + insert(i, new_string); // plug the new string into the old position. + i += new_string.length() - 1; // jump past what we replaced. + did_any = true; + } + return did_any; +} + +bool astring::replace_all(char to_replace, char new_char) +{ + bool did_any = false; + for (int i = 0; i < length(); i++) { + if (get(i) == to_replace) { + put(i, new_char); + did_any = true; + } + } + return did_any; +} + +bool astring::matches(const astring &match_list, char to_match) +{ + for (int i = 0; i < match_list.length(); i++) + if (to_match == match_list.get(i)) return true; + return false; +} + +void astring::strip(const astring &strip_list, how_to_strip way) +{ + if (way & FROM_FRONT) + while (length() && matches(strip_list, get(0))) + zap(0, 0); + + if (way & FROM_END) + while (length() && matches(strip_list, get(end()))) + zap(end(), end()); +} + +int astring::packed_size() const { return length() + 1; } + +void astring::pack(byte_array &target) const +{ attach(target, (char *)c_character_manager.observe()); } + +bool astring::unpack(byte_array &source) +{ return detach(source, *this); } + +///int astring::icompare(const astring &to_compare, int length_in) const +///{ return icompare(to_compare.observe(), length_in); } + +/* +int astring::slow_strncasecmp(const char *first, const char *second, int length) +{ + int len1 = int(strlen(first)); + int len2 = int(strlen(second)); + if (!length) return 0; // no characters are equal to none. + if (!len1 && !len2) return 0; // equal as empty. + if (!len1 && len2) return -1; // first < second. + if (len1 && !len2) return 1; // first > second. + if (positive(length)) { + len1 = minimum(length, len1); + len2 = minimum(length, len2); + } + for (int i = 0; i < len1; i++) { + if (i > len2 - 1) return 1; // first > second, had more length. + if (simple_lower(first[i]) < simple_lower(second[i])) + return -1; // first < second. + if (simple_lower(first[i]) > simple_lower(second[i])) + return 1; // first > second. + } + // at this point we know second is equal to first up to the length of + // first. + if (len2 > len1) return -1; // second was longer and therefore greater. + return 0; // equal. +} +*/ + +////////////// + +a_sprintf::a_sprintf() : astring() {} + +a_sprintf::a_sprintf(const astring &s) : astring(s) {} + +a_sprintf::a_sprintf(const char *initial, ...) +: astring() +{ + if (!initial) return; + va_list args; + va_start(args, initial); + base_sprintf(initial, args); + va_end(args); +} + +////////////// + +void attach(byte_array &packed_form, const char *to_attach) +{ + const int len = int(strlen(to_attach)); + const int old_pos = packed_form.last(); + packed_form.insert(old_pos + 1, len + 1); + memmove((char *)packed_form.observe() + old_pos + 1, to_attach, len + 1); +} + +bool detach(byte_array &packed_form, astring &to_detach) +{ + if (!packed_form.length()) return false; + // locate the zero termination if possible. + const void *zero_posn = memchr(packed_form.observe(), '\0', + packed_form.length()); + // make sure we could find the zero termination. + if (!zero_posn) { + // nope, never saw a zero. good thing we checked. + to_detach.reset(); + return false; + } + // set the string up using a standard constructor since we found the zero + // position; we know the string constructor will be happy. + to_detach = (char *)packed_form.observe(); + // compute the length of the string we found based on the position of the + // zero character. + int find_len = int((abyte *)zero_posn - packed_form.observe()); + // whack the portion of the array that we consumed. + packed_form.zap(0, find_len); + return true; +} + +////////////// + +// contract fulfillment area. + +base_string &astring::concatenate_string(const base_string &s) +{ + const astring *cast = dynamic_cast(&s); + if (cast) *this += *cast; + else *this += astring(s.observe()); + return *this; +} + +base_string &astring::concatenate_char(char c) +{ + *this += c; + return *this; +} + +base_string &astring::assign(const base_string &s) +{ + const astring *cast = dynamic_cast(&s); + if (cast) *this = *cast; + else *this = astring(s.observe()); + return *this; +} + +base_string &astring::upgrade(const char *s) +{ + *this = s; + return *this; +} + +bool astring::sub_string(base_string &target, int start, int end) const +{ + astring *cast = dynamic_cast(&target); + if (!cast) throw "error: astring::sub_string: unknown type"; + return substring(*cast, start, end); +} + +bool astring::sub_compare(const base_string &to_compare, int start_first, + int start_second, int count, bool case_sensitive) const +{ + const astring *cast = dynamic_cast(&to_compare); + if (cast) return compare(*cast, start_first, start_second, count, case_sensitive); + else return compare(astring(to_compare.observe()), start_first, start_second, + count, case_sensitive); +} + +void astring::insert(int position, const base_string &to_insert) +{ + const astring *cast = dynamic_cast(&to_insert); + if (cast) this->insert(position, *cast); + else this->insert(position, astring(to_insert.observe())); +} + +} //namespace. + diff --git a/core/library/basis/astring.h b/core/library/basis/astring.h new file mode 100644 index 00000000..810e3dd5 --- /dev/null +++ b/core/library/basis/astring.h @@ -0,0 +1,468 @@ +#ifndef ASTRING_CLASS +#define ASTRING_CLASS + +/*****************************************************************************\ +* * +* Name : astring * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "base_string.h" +#include "byte_array.h" +#include "contracts.h" + +#include + +namespace basis { + +//! Provides a dynamically resizable ASCII character string. +/*! + It mimics the standard (char *) type, but provides a slew of helpful + methods as well as enforcing bounds checking on the underlying array. +*/ + +class astring +: public virtual base_string, + public virtual hoople_standard +{ +public: + astring(); + //!< constructs an empty string. + + astring(const char *initial); + //!< constructs a copy of the string passed in "initial". + + astring(char c, int repeat); + //!< constructs a string with "repeat" characters of "c" in it. + /*!< if "c" is the null character (i.e., equal to zero), then the resulting + string will have "repeat" space characters in it. */ + + astring(const astring &s); + //!< Constructs a copy of the string "s". + + astring(const base_string &initial); + //!< constructs a string from the base class. + + enum special_flag { UNTERMINATED = 62, SPRINTF = 84 }; + astring(special_flag way, const char *s, ...); + //!< constructor that sports a few variable parameter constructions. + /*!< + For a flag of "UNTERMINATED", the constructor expects the third + parameter to be an integer, and then it copies that number of + characters from the C-string "s" without assuming that "s" is zero + terminated. + + For a flag of "SPRINTF", a string is constructed using the format specifier + in "s" in a manner similar to the standard library "sprintf" function + (see the standard library for ). If there are no "%" codes in + "s", then the constructor just copies "s" without modification. If "%" + codes are in the character array, then any additional arguments (...) are + interpreted as they would be by sprintf. The length of the + constructed string is tailored to fit the actual contents. If "s" is + null, then the resulting string will be empty. Currently, the "*" + specifier for variable length fields is not supported. */ + + virtual ~astring(); + //!< destroys any storage for the string. + + DEFINE_CLASS_NAME("astring"); + + virtual int comparator(const astring &s2) const; + //!< helps to fulfill orderable contract. + + int length() const; + //!< Returns the current length of the string. + /*!< The length returned does not include the terminating null character + at the end of the string. */ + + int end() const { return length() - 1; } + //!< returns the index of the last (non-null) character in the string. + /*!< If there is no content in the string, then a negative value is + returned. */ + + bool empty() const { return !length(); } + //!< empty() reports if the string is empty, that is, of zero length(). + bool non_empty() const { return !empty(); } + //!< non_empty() reports if the string has some contents. + bool operator ! () const { return empty(); } + //!< the negation operator returns true if the string is empty. + /*!< it can be used in expressions in a readable way, for example: + if (!my_string) { it_is_empty; } */ + bool t() const { return !empty(); } + //!< t() is a shortcut for the string being "true", as in non-empty. + /*!< the logic here is that the string is not false because it's not + empty. for example: if (my_string.t()) { it_is_not_empty; } */ + + static const astring &empty_string(); + //!< useful wherever empty strings are needed, e.g., function defaults. + /*!< note that this is implemented in the opsystem library to avoid bad + issues with static objects mixed into multiple dlls from a static + library. */ + + virtual const char *observe() const; + //!< observes the underlying pointer to the zero-terminated string. + /*!< this does not allow the contents to be modified. this method should + never return NIL. */ + const char *c_str() const { return observe(); } + //!< synonym for observe. mimics the STL method name. + const char *s() const { return observe(); } + //!< synonym for observe. the 's' stands for "string", if that helps. + + virtual char get(int index) const; + //!< a constant peek at the string's internals at the specified index. + + virtual char *access(); + //!< provides access to the actual string held. + /*!< this should never return NIL. be very careful with the returned + pointer: don't destroy or corrupt its contents (e.g., do not mess with + its zero termination). */ + char *c_str() { return access(); } + //!< synonym for access. mimics the STL method. + char *s() { return access(); } + //!< synonym for access. + + char &operator [] (int position); + //!< accesses individual characters in "this" string. + /*!< if the "position" is out of range, the return value is + not meaningful. */ + const char &operator [] (int position) const; + //!< observes individual characters in "this" string. + /*!< if the "position" is out of range, the return value is + not meaningful. */ + + virtual void put(int position, char to_put) { (*this)[position] = to_put; } + //!< stores the character "to_put" at index "position" in the string. + + astring &sprintf(const char *s, ...); + //!< similar to the SPRINTF constructor, but works on an existing string. + /*!< any existing contents in the string are wiped out. */ + + int convert(int default_value) const; + //!< Converts the string into a corresponding integer. + /*!< The conversion starts at index 0 in "this" string, and stores it in + "value". If a valid integer was found, it is returned. otherwise, the + "default_value" is returned. NOTE: be careful of implicit conversions + here; the "default_value" for any of these functions must either be an + object of the exact type needed or must be cast to that type. */ + long convert(long default_value) const; + //!< converts the string to a long integer. + float convert(float default_value) const; + //!< converts the string to a floating point number. + double convert(double default_value) const; + //!< converts the string to a double precision floating point number. + + bool equal_to(const char *that) const; + //!< returns true if "that" is equal to this. + + bool iequals(const astring &that) const; + //!< returns true if this is case-insensitively equal to "that". + bool iequals(const char *that) const; + //!< returns true if this is case-insensitively equal to "that". + + bool compare(const astring &to_compare, int start_first, + int start_second, int count, bool case_sensitive) const; + //!< Compares "this" string with "to_compare". + /*!< The "start_first" is where the comparison begins in "this" string, + and "start_second" where it begins in the "to_compare". The "count" is + the number of characters to compare between the two strings. If either + index is out of range, or "count"-1 + either index is out of range, then + compare returns false. If the strings differ in that range, false is + returned. Only if the strings have identical contents in the range is + true returned. */ + + bool begins(const astring &maybe_prefix) const + { return compare(maybe_prefix, 0, 0, maybe_prefix.length(), true); } + //!< Returns true if "this" string begins with "maybe_prefix". + + bool ibegins(const astring &maybe_prefix) const + { return compare(maybe_prefix, 0, 0, maybe_prefix.length(), false); } + //!< a case-insensitive method similar to begins(). + + //! returns true if this string ends with "maybe_suffix". + bool ends(const astring &maybe_suffix) const { + const int diff = length() - maybe_suffix.length(); + return (diff >= 0) && compare(maybe_suffix, diff, 0, maybe_suffix.length(), true); + } + //!< a case-insensitive method similar to ends(). + bool iends(const astring &maybe_suffix) const { + const int diff = length() - maybe_suffix.length(); + return (diff >= 0) && compare(maybe_suffix, diff, 0, maybe_suffix.length(), false); + } + + astring &operator = (const astring &s); + //!< Sets the contents of this string to "s". + astring &operator = (const char *s); + //!< Sets the contents of this string to "s". + + void reset() { zap(0, end()); } + //!< clears out the contents string. + + void reset(special_flag way, const char *s, ...); + //!< operates like the constructor that takes a 'special_flag'. + + void copy(char *to_stuff, int count) const; + //!< Copies a maximum of "count" characters from this into "to_stuff". + /*!< The target "to_stuff" is a standard C-string. The terminating zero + from this string is also copied. BE CAREFUL: if "count"+1 is greater than + the allocated length of the C-string "to_stuff", then an invalid memory + write will occur. keep in mind that the terminating zero will be put at + position "count" in the C-string if the full "count" of characters are + copied. */ + void stuff(char *to_stuff, int count) const { copy(to_stuff, count); } + //!< a synonym for copy(). + + astring operator + (const astring &s) const; + //!< Returns the concatenation of "this" and "s". + + astring &operator += (const astring &s); + //!< Modifies "this" by concatenating "s" onto it. + + astring &operator += (const char *s); // this is efficient. + //!< synonym for the concatenation operator but uses a char pointer instead. + astring operator + (const char *s) const { return *this + astring(s); } + //!< synonym for the concatenation operator but uses a char pointer instead. + // this method is not efficient. + + astring &operator += (char c); //!< concatenater for single characters. + + int find(char to_find, int position = 0, bool reverse = false) const; + //!< Locates "to_find" in "this". + /*!< find returns the index of "to_find" or "NOT_FOUND". find starts + looking at "position". find returns "OUT_OF_RANGE" if the position is + beyond the bounds of "this". */ + int find(const astring &to_find, int posn = 0, bool reverse = false) const; + //!< finds "to_find" in this string. + + int ifind(char to_find, int position = 0, bool reverse = false) const; + //!< like the find() methods above, but case-insensitive. + int ifind(const astring &to_find, int posn = 0, bool reverse = false) const; + //!< like the find() methods above, but case-insensitive. + + int find_any(const char *to_find, int position = 0, + bool reverse = false) const; + //!< searches for any of the characters in "to_find". + /*!< the first occurrence of any of those is returned, or a negative + number is returned if no matches are found. */ + int ifind_any(const char *to_find, int position = 0, + bool reverse = false) const; + //!< searches case-insensitively for any of the characters in "to_find". + /*!< the first occurrence of any of those is returned, or a negative number + is returned if none are found. */ + int find_non_match(const char *to_find, int position = 0, + bool reverse = false) const; + //!< searches for any character that is not in "to_find" and returns index. + + bool contains(const astring &to_find) const; + //!< Returns true if "to_find" is contained in this string or false if not. + + bool substring(astring &target, int start, int end) const; + //!< a version that stores the substring in an existing "target" string. + + astring substring(int start, int end) const; + //!< Returns the segment of "this" between the indices "start" and "end". + /*!< An empty string is returned if the indices are out of range. */ + + // helper methods similar to other string's choppers. + astring middle(int start, int count); + //!< returns the middle of the string from "start" with "count" characters. + astring left(int count); + //!< returns the left "count" characters from the string. + astring right(int count); + //!< returns the right "count" characters from the string. + + void pad(int length, char padding = ' '); + //!< makes the string "length" characters long. + /*!< this string is padded with the "padding" character if the string is + less than that length initially. */ + void trim(int length); + //!< shortens the string to "length" if it's longer than that. + + void insert(int position, const astring &to_insert); + //!< Copies "to_insert" into "this" at the "position". + /*!< Characters at the index "position" and greater are moved over. */ + virtual void zap(int start, int end); + //!< Deletes the characters between "start" and "end" inclusively. + /*!< C++ array conventions are used (0 through length()-1 are valid). If + either index is out of bounds, then the string is not modified. */ + + void to_lower(); + //!< to_lower modifies "this" by replacing capitals with lower-case. + /*!< every capital letter is replaced with the corresponding lower case + letter (i.e., A becomes a). */ + void to_upper(); + //!< to_upper does the opposite of to_lower (that is, q becomes Q). + astring lower() const; + //!< like to_lower(), but returns a new string rather than modifying this. + astring upper() const; + //!< like to_upper(), but returns a new string rather than modifying this. + + bool replace(const astring &tag, const astring &replacement); + //!< replaces the first occurrence of "tag" text with the "replacement". + /*!< true is returned if the "tag" was actually found and replaced. */ + bool replace_all(char to_replace, char new_char); + //!< changes all occurrences of "to_replace" with "new_char". + bool replace_all(const astring &to_replace, const astring &new_string); + //! changes all occurrences of "to_replace" into "new_string". + + void shrink(); + //!< resizes the string to its minimum possible length. + /*!< this fixes any situations where a null character has been inserted + into the middle of the string. the string is truncated after the first + null charater encountered and its size is corrected. this also repairs + any case where the string was originally longer than it is now. */ + + enum how_to_strip { FROM_FRONT = 1, FROM_END = 2, FROM_BOTH_SIDES = 3 }; + //!< an enumeration describing the strip operations. + + void strip(const astring &strip_list, how_to_strip way = FROM_BOTH_SIDES); + //!< strips all chars from "strip_list" out of "this" given the "way". + + void strip_spaces(how_to_strip way = FROM_BOTH_SIDES) + { strip(" ", way); } + //!< removes excess space characters from string's beginning, end or both. + + void strip_white_spaces(how_to_strip way = FROM_BOTH_SIDES) + { strip(" \t", way); } + //!< like strip_spaces, but includes tabs in the list to strip. + + static bool matches(const astring &match_list, char to_match); + //!< returns true if "to_match" is found in the "match_list" string. + + int packed_size() const; + //!< Reports the size required to pack this string into a byte array. + + void pack(byte_array &target) const; + //!< stores this string in the "target". it can later be unpacked again. + bool unpack(byte_array &source); + //!< retrieves a string (packed with pack()) from "source" into this string. + /*!< note that the string is grabbed from the array destructively; whatever + portion of the byte array was used to store the string will be removed from + the head of the array. */ + +//hmmm: rename this--it is not a simple icompare, but a strncasecmp analogue. +// int icompare(const astring &to_compare, int length = -1) const; + //!< provides a case insensitive comparison routine. + /*!< this uses the best methods available (that is, it uses a system + function if one exists). the string "to_compare" is compared with this + string. if the "length" is negative, then this entire string is compared + with the entire string "to_compare". otherwise, only "length" characters + from this string are compared. if this string is before "to_compare" in + a lexicographic ordering (basically alphabetical), then a negative number + is returned. if this string is after "to_compare", then a positive number + is returned. zero is returned if the two strings are equal for the extent + of interest. */ + +/// int icompare(const char *to_compare, int length = -1) const; + //!< a version of the above for raw character pointers. + +/// static int slow_strncasecmp(const char *first, const char *second, +/// int length = -1); + //!< a replacement for strncasecmp on platforms without them. + /*!< this is slow because it cannot rely on OS methods to perform the + comparison. if the "length" is negative, then the entire string "first" + is compared to "second". otherwise just "length" characters are compared. + this follows the standard library strncasecmp method: the return value can + be in three states: negative, zero and positive. zero means the strings + are identical lexicographically , whereas less than zero means + "this_string" is less than "to_compare" and greater than zero means + "this_string" is greater than "to_compare". */ + + // play-yard for implementing base class requirements. + + // these implement the orderable and equalizable interfaces. + virtual bool equal_to(const equalizable &s2) const; + virtual bool less_than(const orderable &s2) const; + + virtual base_string &concatenate_string(const base_string &s); + virtual base_string &concatenate_char(char c); + virtual base_string &assign(const base_string &s); + virtual base_string &upgrade(const char *s); + virtual bool sub_string(base_string &target, int start, int end) const; + virtual bool sub_compare(const base_string &to_compare, int start_first, + int start_second, int count, bool case_sensitive) const; + virtual void insert(int position, const base_string &to_insert); + virtual void text_form(base_string &state_fill) const; + +private: + byte_array c_character_manager; + //!< hides the real object responsible for implementing much of the class. + + // the real find methods. + int char_find(char to_find, int position, bool reverse, + bool case_sense) const; + // if "invert_find" is true, then non-matches are reported instead of matches. + int char_find_any(const astring &to_find, int position, bool reverse, + bool case_sense, bool invert_find = false) const; + int str_find(const astring &to_find, int posn, bool reverse, + bool case_s) const; + + // the functions below are used in the formatting string constructor. +public: // only for base_sprintf. + astring &base_sprintf(const char *s, va_list &args); +private: + char *const *c_held_string; //!< peeks into the actual pointer for debugging. + + void seek_flag(const char *&traverser, char *flag_chars, bool &failure); + //!< looks for optional flag characters. + void seek_width(const char *&traverser, char *width_chars); + //!< looks for optional width characters. + void seek_precision(const char *&traverser, char *precision_chars); + //!< looks for optional precision characters. + void seek_modifier(const char *&traverser, char *modifier_char); + //!< looks for optional modifier characters. + void get_type_character(const char *&traverser, va_list &args, + astring &output_string, const char *flag_chars, + const char *width_chars, const char *precision_chars, + const char *modifier_chars); + /*!< the required character in a format specifier is either grabbed here or + the other characters are put into the ouput string without formatting. + the "X"_char variables should have been previously gathered by the + seek_"X" functions. */ + + public: byte_array &get_implementation(); private: // for test programs only.... +}; + +////////////// + +//! a_sprintf is a specialization of astring that provides printf style support. +/*! it makes it much easier to call the SPRINTF style constructor but is +otherwise identical to an astring. */ + +class a_sprintf : public astring +{ +public: + a_sprintf(); + a_sprintf(const char *initial, ...); + a_sprintf(const astring &s); +}; + +////////////// + +typedef bool string_comparator_function(const astring &a, const astring &b); + //!< returns true if the strings "a" and "b" are considered equal. + /*!< this provides a prototype for the equality operation, which allows the + notion of equality to be redefined according to a particular function's + implementation. */ + +bool astring_comparator(const astring &a, const astring &b); + //!< implements a string comparator that just does simple astring ==. + +////////////// + +void attach(byte_array &packed_form, const char *to_attach); + //!< Packs a character string "to_attach" into "packed_form". +bool detach(byte_array &packed_form, astring &to_detach); + //!< Unpacks a character string "to_attach" from "packed_form". + +} //namespace. + +#endif + diff --git a/core/library/basis/base_string.h b/core/library/basis/base_string.h new file mode 100644 index 00000000..c2c2cf45 --- /dev/null +++ b/core/library/basis/base_string.h @@ -0,0 +1,98 @@ +#ifndef BASE_STRING_CLASS +#define BASE_STRING_CLASS + +/*****************************************************************************\ +* * +* Name : base_string * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//hmmm: some of these methods could be pulled out to a base_array class. +// that would be a nice further abstraction. + +#include "contracts.h" + +namespace basis { + +//! Defines the base class for all string processing objects in hoople. + +class base_string : public virtual orderable +{ +public: + virtual int length() const = 0; + //!< Returns the current length of the string. + /*!< The length returned does not include the terminating null character + at the end of the string. */ + + virtual const char *observe() const = 0; + //!< observes the underlying pointer to the zero-terminated string. + /*!< this does not allow the contents to be modified. this method should + never return NIL. */ + + virtual char *access() = 0; + //!< provides access to the actual string held. + /*!< this should never return NIL. be very careful with the returned + pointer: don't destroy or corrupt its contents (e.g., do not mess with + its zero termination). */ + + virtual char get(int index) const = 0; + //!< a constant peek at the string's internals at the specified index. + + virtual void put(int position, char to_put) = 0; + //!< stores the character "to_put" at index "position" in the string. + + virtual bool sub_compare(const base_string &to_compare, int start_first, + int start_second, int count, bool case_sensitive) const = 0; + //!< Compares "this" string with "to_compare". + /*!< The "start_first" is where the comparison begins in "this" string, + and "start_second" where it begins in the "to_compare". The "count" is + the number of characters to compare between the two strings. If either + index is out of range, or "count"-1 + either index is out of range, then + compare returns false. If the strings differ in that range, false is + returned. Only if the strings have identical contents in the range is + true returned. If case-sensitive is false, then matches will not require + the caps and lower-case match. */ + + virtual base_string &assign(const base_string &s) = 0; + //!< Sets the contents of this string to "s". + + virtual base_string &upgrade(const char *s) = 0; + //!< Sets the contents of this string to "s". + + virtual void insert(int position, const base_string &to_insert) = 0; + //!< Copies "to_insert" into "this" at the "position". + /*!< Characters at the index "position" and greater are moved over. */ + + virtual void zap(int start, int end) = 0; + //!< Deletes the characters between "start" and "end" inclusively. + /*!< C++ array conventions are used (0 through length()-1 are valid). If + either index is out of bounds, then the string is not modified. */ + + virtual base_string &concatenate_string(const base_string &s) = 0; + //!< Modifies "this" by concatenating "s" onto it. + + virtual base_string &concatenate_char(char c) = 0; + //!< concatenater for single characters. + + virtual bool sub_string(base_string &target, int start, int end) const = 0; + //!< Gets the segment of "this" between the indices "start" and "end". + /*!< false is returned if the range is invalid. */ + + //! sets this string's contents equal to the contents of "to_copy". + /*! this assignment ensures that setting the base class to derived versions will + succeed; otherwise, a base class copy does very little. */ + virtual base_string &operator =(const base_string &to_copy) { return assign(to_copy); } +}; + +} //namespace. + +#endif + diff --git a/core/library/basis/byte_array.h b/core/library/basis/byte_array.h new file mode 100644 index 00000000..cd5239fa --- /dev/null +++ b/core/library/basis/byte_array.h @@ -0,0 +1,132 @@ +#ifndef BYTE_ARRAY_CLASS +#define BYTE_ARRAY_CLASS + +/*****************************************************************************\ +* * +* Name : byte_array * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "array.h" +#include "base_string.h" +#include "contracts.h" +#include "definitions.h" + +#include // for memcmp. + +namespace basis { + +//! A very common template for a dynamic array of bytes. +/*! + byte_array provides a simple wrapper around array, but with the + exponential growth and simple copy modes automatically enabled. + Note that it is almost always best to forward declare byte_arrays in ones + own headers rather than including this header. +*/ + +class byte_array : public array, public virtual orderable +{ +public: + byte_array(int number = 0, const abyte *initial_contents = NIL) + : array(number, initial_contents, SIMPLE_COPY | EXPONE) {} + //!< constructs an array of "number" bytes from "initial_contents". + + byte_array(const byte_array &to_copy) + : root_object(), array(to_copy) {} + //!< constructs an array bytes by copying the "to_copy" array. + + byte_array(const array &to_copy) : array(to_copy) {} + //!< constructs an array bytes by copying the "to_copy" array. + + virtual ~byte_array() {} + + DEFINE_CLASS_NAME("byte_array"); + + //!< returns an array of zero bytes. + /*!< note that this is implemented in the opsystem library to avoid bad + issues with static objects mixed into multiple dlls from a static + library. */ + static const byte_array &empty_array() + { static byte_array g_empty; return g_empty; } + + // these implement the orderable and equalizable interfaces. + virtual bool equal_to(const equalizable &s2) const { + const byte_array *s2_cast = dynamic_cast(&s2); + if (!s2_cast) throw "error: byte_array::==: unknown type"; + return comparator(*s2_cast) == 0; + } + virtual bool less_than(const orderable &s2) const { + const byte_array *s2_cast = dynamic_cast(&s2); + if (!s2_cast) throw "error: byte_array::<: unknown type"; + return comparator(*s2_cast) < 0; + } + + int comparator(const byte_array &s2) const { + return memcmp(observe(), s2.observe(), length()); + } +}; + +////////////// + +//! A base class for objects that can pack into an array of bytes. +/*! + A packable is an abstract object that represents any object that can + be transformed from a potentially deep form into an equivalent flat + form. The flat form is a simple extent of memory stored as bytes. +*/ + +class packable : public virtual root_object +{ +public: + virtual void pack(byte_array &packed_form) const = 0; + //!< Creates a packed form of the packable object in "packed_form". + /*!< This must append to the data in "packed_form" rather than clearing + prior contents. */ + + virtual bool unpack(byte_array &packed_form) = 0; + //!< Restores the packable from the "packed_form". + /*!< This object becomes the unpacked form, and therefore must lose any of + its prior contents that depend on the data in "packed_form". This is up to + the derived unpack function to figure out. The "packed_form" is modified + by extracting all of the pieces that are used for this object; the + remainder stays in "packed_form". true is returned if the unpacking was + successful. */ + + virtual int packed_size() const = 0; + //!< Estimates the space needed for the packed structure. +}; + +////////////// + +// the two templates below can be used to add or remove objects from an array +// of bytes. NOTE: the functions below will only work with objects that are +// already platform-independent. it's better to make structures packable by +// using the attach and detach functions in the "packable" library. + +//! attach_flat() places a copy of "attachment" onto the array of bytes. +template +void attach_flat(byte_array &target, const contents &attachment) +{ target.concatenate(byte_array(sizeof(attachment), (abyte *)&attachment)); } + +//! detach_flat() pulls the "detached" object out of the array of bytes. +template +bool detach_flat(byte_array &source, contents &detached) +{ + if (sizeof(detached) > source.length()) return false; + detached = *(contents *)source.observe(); + source.zap(0, sizeof(detached) - 1); + return true; +} + +} // namespace. + +#endif + diff --git a/core/library/basis/common_outcomes.cpp b/core/library/basis/common_outcomes.cpp new file mode 100644 index 00000000..a30ca268 --- /dev/null +++ b/core/library/basis/common_outcomes.cpp @@ -0,0 +1,54 @@ +/*****************************************************************************\ +* * +* Name : common_outcomes * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "common_outcomes.h" + +namespace basis { + +const char *common::outcome_name(const outcome &to_name) +{ + switch (to_name.value()) { + case OKAY: return "OKAY"; + case NOT_IMPLEMENTED: return "NOT_IMPLEMENTED"; + case OUT_OF_RANGE: return "OUT_OF_RANGE"; + case NOT_FOUND: return "NOT_FOUND"; + case BAD_INPUT: return "BAD_INPUT"; + case BAD_TYPE: return "BAD_TYPE"; + case IS_FULL: return "IS_FULL"; + case IS_EMPTY: return "IS_EMPTY"; + case IS_NEW: return "IS_NEW"; + case EXISTING: return "EXISTING"; + case FAILURE: return "FAILURE"; + case OUT_OF_MEMORY: return "OUT_OF_MEMORY"; + case ACCESS_DENIED: return "ACCESS_DENIED"; + case IN_USE: return "IN_USE"; + case UNINITIALIZED: return "UNINITIALIZED"; + case TIMED_OUT: return "TIMED_OUT"; + case GARBAGE: return "GARBAGE"; + case NO_SPACE: return "NO_SPACE"; + case DISALLOWED: return "DISALLOWED"; + case INCOMPLETE: return "INCOMPLETE"; + case NO_HANDLER: return "NO_HANDLER"; + case NONE_READY: return "NONE_READY"; + case INVALID: return "INVALID"; + case PARTIAL: return "PARTIAL"; + case NO_LICENSE: return "NO_LICENSE"; + case UNEXPECTED: return "UNEXPECTED"; + case ENCRYPTION_MISMATCH: return "ENCRYPTION_MISMATCH"; + default: return "UNKNOWN_OUTCOME"; + } +} + +} // namespace. + diff --git a/core/library/basis/common_outcomes.h b/core/library/basis/common_outcomes.h new file mode 100644 index 00000000..893a5cb2 --- /dev/null +++ b/core/library/basis/common_outcomes.h @@ -0,0 +1,82 @@ +#ifndef COMMON_OUTCOMES_CLASS +#define COMMON_OUTCOMES_CLASS + +/*****************************************************************************\ +* * +* Name : common_outcomes * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "outcome.h" + +namespace basis { + +//! the "common" class defines our common_outcomes. +class common +{ +public: + //! these outcomes are returned by classes that use the basic HOOPLE support. + /*! More complicated classes will need to define their own outcome values to + describe what occurred during the processing of requests. */ + enum outcomes { + DEFINE_API_OUTCOME(OKAY, 0, "Everything is just fine"), + DEFINE_API_OUTCOME(NOT_IMPLEMENTED, -1, + "The invoked method is unimplemented"), + DEFINE_API_OUTCOME(OUT_OF_RANGE, -2, "The value specified was out " + "of bounds"), + DEFINE_API_OUTCOME(NOT_FOUND, -3, "The item sought is not present"), + DEFINE_API_OUTCOME(BAD_INPUT, -4, "Precondition failure--the parameters " + "were inappropriate"), + DEFINE_API_OUTCOME(BAD_TYPE, -5, "The objects are of incompatible types"), + DEFINE_API_OUTCOME(IS_FULL, -6, "There is no room in the storage facility"), + DEFINE_API_OUTCOME(IS_EMPTY, -7, "The container is empty currently"), + DEFINE_API_OUTCOME(IS_NEW, -8, "The item is new"), + DEFINE_API_OUTCOME(EXISTING, -9, "The item was already present"), + DEFINE_API_OUTCOME(FAILURE, -10, "A failure has occurred"), + DEFINE_API_OUTCOME(OUT_OF_MEMORY, -11, "There is not enough memory for the " + "request according to the operating system"), + DEFINE_API_OUTCOME(ACCESS_DENIED, -12, "The request was denied, possibly " + "by the operating system"), + DEFINE_API_OUTCOME(IN_USE, -13, "The object is already in exclusive use"), + DEFINE_API_OUTCOME(UNINITIALIZED, -14, "The object has not been " + "constructed properly"), + DEFINE_API_OUTCOME(TIMED_OUT, -15, "The allowed time has now elapsed"), + DEFINE_API_OUTCOME(GARBAGE, -16, "The request or response has been " + "corrupted"), + DEFINE_API_OUTCOME(NO_SPACE, -17, "A programmatic limit on storage space " + "has been reached"), + DEFINE_API_OUTCOME(DISALLOWED, -18, "The method denied the request"), + DEFINE_API_OUTCOME(INCOMPLETE, -19, "The operation did not finish or the " + "object is not completed"), + DEFINE_API_OUTCOME(NO_HANDLER, -20, "The object type passed in was not " + "understood by the invoked method"), + DEFINE_API_OUTCOME(NONE_READY, -21, "There were no objects available"), + DEFINE_API_OUTCOME(INVALID, -22, "That request or object was invalid"), + DEFINE_API_OUTCOME(PARTIAL, -23, "The request was only partially finished"), + DEFINE_API_OUTCOME(NO_LICENSE, -24, "The software license does not permit" + "this request"), + DEFINE_API_OUTCOME(UNEXPECTED, -25, "This item was unexpected, although " + "not necessarily erroneous"), + DEFINE_API_OUTCOME(ENCRYPTION_MISMATCH, -26, "The request failed due to a " + "mismatch between encryption expected and encryption provided") + }; + + static const char *outcome_name(const outcome &to_name); + //!< Returns a string representation of the outcome "to_name". + /*!< If "to_name" is unknown, because it's not a member of the + common::outcomes enum, then a string reporting that is returned. */ + +}; //class. + +} //namespace. + +#endif // outer guard. + diff --git a/core/library/basis/contracts.h b/core/library/basis/contracts.h new file mode 100644 index 00000000..def88b3e --- /dev/null +++ b/core/library/basis/contracts.h @@ -0,0 +1,188 @@ +#ifndef CONTRACTS_GROUP +#define CONTRACTS_GROUP + +/*****************************************************************************\ +* * +* Name : contracts * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1989-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +/*! @file contracts.h + This is a collection of fairly vital interface classes. +*/ + +#include "outcome.h" + +namespace basis { + +// forward declarations. +class base_string; + +////////////// + +//! Defines an attribute base class that supports get and set operations. + +class attribute : public virtual root_object +{ +public: + virtual const root_object &get() const = 0; + virtual void set(const root_object &new_value) = 0; +}; + +////////////// + +//! Base class for object that can tell itself apart from other instances. +class equalizable : public virtual root_object +{ +public: + virtual bool equal_to(const equalizable &s2) const = 0; + //! the virtual method for object equality. + virtual bool operator == (const equalizable &s2) const { return equal_to(s2); } + //! synactic sugar for comparison operators. +}; + +////////////// + +//! A base for objects that can be alphabetically (lexicographically) ordered. + +class orderable : public virtual equalizable +{ +public: + virtual bool less_than(const orderable &s2) const = 0; + //! the virtual method for object ordering. + virtual bool operator < (const orderable &s2) const { return less_than(s2); } + //! synactic sugar for comparison operators. +}; + +////////////// + +//! Provides an abstract base for logging mechanisms. + +class base_logger : public virtual root_object +{ +public: + virtual outcome log(const base_string &info, int filter) = 0; + //!< writes the information in "info" to the logger using the "filter". + /*!< the derived class can interpret the filter appropriately and only + show the "info" if the filter is enabled. */ +}; + +////////////// + +//! Macro for defining a logging filter value. +#define DEFINE_FILTER(NAME, CURRENT_VALUE, INFO_STRING) NAME = CURRENT_VALUE + +//! These filter values are the most basic, and need to be known everywhere. +enum root_logging_filters { + DEFINE_FILTER(NEVER_PRINT, -1, "This diagnostic entry should be dropped and never seen"), + DEFINE_FILTER(ALWAYS_PRINT, 0, "This diagnostic entry will always be shown or recorded") +}; + +////////////// + +//! Interface for a simple form of synchronization. +/*! + Derived classes must provide a locking operation and a corresponding + unlocking operation. +*/ + +class base_synchronizer : public virtual root_object +{ +public: + virtual void establish_lock() = 0; + virtual void repeal_lock() = 0; +}; + +////////////// + +//! A clonable object knows how to make copy of itself. + +class clonable : public virtual root_object +{ +public: + virtual clonable *clone() const = 0; +}; + +////////////// + +//! Root object for any class that knows its own name. +/*! + This is a really vital thing for debugging to be very helpful, and unfortunately it's not + provided by C++. +*/ + +class nameable : public virtual root_object +{ +public: + virtual const char *class_name() const = 0; + //!< Returns the bare name of this class as a constant character pointer. + /*!< The name returned here is supposed to be just a class name and not + provide any more information than that. It is especially important not to + add any syntactic elements like '::' to the name, since a bare alphanumeric + name is expected. */ +}; + +////////////// + +//! A base class for objects that can provide a synopsis of their current state. +/*! + This helps a lot during debugging and possibly even during normal runtime, since it causes + the object to divulge its internal state for viewing in hopefully readable text. +*/ + +class text_formable : public virtual nameable +{ +public: + virtual const char *class_name() const = 0; // forwarded requirement from nameable. + + virtual void text_form(base_string &state_fill) const = 0; + //!< Provides a text view of all the important info owned by this object. + /*!< It is understood that there could be a large amount of information and that this + function might take a relatively long time to complete. */ +}; + +////////////// + +//! the base class of the most easily used and tested objects in the library. +/*! + Each hoople_standard object must know its name, how to print out its data members, and whether + it's the same as another object or not. +*/ + +class hoople_standard : public virtual text_formable, public virtual equalizable +{ +public: + // this is a union class and has no extra behavior beyond its bases. +}; + +////////////// + +//! a base for classes that can stream their contents out to a textual form. + +class text_streamable : public virtual nameable +{ +public: + virtual bool produce(base_string &target) const = 0; + //!< sends the derived class's member data into the "target" in a reversible manner. + /*!< this should use a tagging system of some sort so that not only can the derived class + verify that its type is really right there in the string, but also that it gets all of its + class data and no other data. the "target" will be destructively consumed, and after a + successful call will no longer contain the object's streamed form at its head. */ + virtual bool consume(const base_string &source) = 0; + //!< chows down on a string that supposedly contains a streamed form. + /*!< the derived class must know how to eat just the portion of the string that holds + its data type and no more. */ +}; + +} //namespace. + +#endif + diff --git a/core/library/basis/definitions.h b/core/library/basis/definitions.h new file mode 100644 index 00000000..c76e10ae --- /dev/null +++ b/core/library/basis/definitions.h @@ -0,0 +1,193 @@ +#ifndef DEFINITIONS_GROUP +#define DEFINITIONS_GROUP + +/*****************************************************************************\ +* * +* Name : definitions * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! @file "definitions.h" Constants and objects used throughout HOOPLE. +/*! @file + Defines a set of useful universal constants (for our chosen universe) and + a set of aliases for convenient abstract and concrete data types. + This is the lowest-level header in hoople and should not include any others. +*/ + +namespace basis { + +////////////// + +// Constants... + +//! The value representing a pointer to nothing, or nothing itself. +#define NIL 0 + +//! A fundamental constant measuring the number of bits in a byte. +#define BITS_PER_BYTE 8 + +//! An approximation of the fundamental circular constant. +#define PI_APPROX 3.14159265358 + +////////////// + +// Data Structures & Functions + +//! This macro just eats what it's passed; it marks unused formal parameters. +#define formal(parameter) + +//! A fairly important unit which is seldom defined... +typedef unsigned char abyte; +/* ridiculous! all to shut microsoft up about ambiguous byte definitions, +which seems like a bug. +struct byte { + byte(unsigned char b = 0) : c_data(b) {} +// byte(char b) : c_data(b) {} + operator unsigned char() const { return c_data; } + operator char() const { return c_data; } + operator int() const { return c_data; } + operator unsigned int() const { return c_data; } + operator bool() const { return (bool)c_data; } + byte operator &(byte and_with) { return c_data & and_with; } + byte operator |(byte or_with) { return c_data & or_with; } + unsigned char c_data; +}; +*/ + +#if defined(UNICODE) && defined(__WIN32__) + //! the flexichar type is always appropriate to hold data for win32 calls. + typedef wchar_t flexichar; +#else + // this version simply defangs any conversions. + typedef char flexichar; +#endif + +//! Abbreviated name for unsigned integers. +typedef unsigned int un_int; +//! Abbreviated name for unsigned short integers. +typedef unsigned short un_short; +//! Abbreviated name for unsigned long integers. +typedef unsigned long un_long; + +// some maximum and minimum values that are helpful. +#ifndef MAXINT32 + //! Maximum 32-bit integer value. + #define MAXINT32 0x7fffffff +#endif +#ifndef MININT32 + //! Minimum 32-bit integer value. + #define MININT32 0x80000000 +#endif +#ifndef MAXINT16 + //! Maximum 32-bit integer value. + #define MAXINT16 0x7fff +#endif +#ifndef MININT16 + //! Minimum 32-bit integer value. + #define MININT16 0x8000 +#endif +#ifndef MAXCHAR + //! Maximum byte-based character value. + #define MAXCHAR 0x7f +#endif +#ifndef MINCHAR + //! Minimum byte-based character value. + #define MINCHAR 0x80 +#endif +#ifndef MAXBYTE + //! Maximum unsigned byte value. + #define MAXBYTE 0xff +#endif +#ifndef MINBYTE + //! Minimum unsigned byte value. + #define MINBYTE 0x00 +#endif + +// Provide definitions for integers with platform independent specific sizes. +// Note that these may have to be adjusted for 64 bit platforms. +typedef char int8; +typedef unsigned char uint8; +typedef signed short int16; +typedef unsigned short uint16; +typedef signed int int32; +typedef unsigned int uint32; + +////////////// + +// useful time constants. + +// the _ms suffix indicates that these are measured in milliseconds. +const int SECOND_ms = 1000; //!< Number of milliseconds in a second. +const int MINUTE_ms = 60 * SECOND_ms; //!< Number of milliseconds in a minute. +const int HOUR_ms = 60 * MINUTE_ms; //!< Number of milliseconds in an hour. +const int DAY_ms = 24 * HOUR_ms; //!< Number of milliseconds in a day. + +// the _s suffix indicates that these are measured in seconds. +const int MINUTE_s = 60; //!< Number of seconds in a minute. +const int HOUR_s = 60 * MINUTE_s; //!< Number of seconds in an hour. +const int DAY_s = 24 * HOUR_s; //!< Number of seconds in a day. + +////////////// + +// useful general constants. + +const int KILOBYTE = 1024; //!< Number of bytes in a kilobyte. +const int MEGABYTE = KILOBYTE * KILOBYTE; //!< Number of bytes in a megabyte. +const int GIGABYTE = MEGABYTE * KILOBYTE; //!< Number of bytes in a gigabyte. +const double TERABYTE = double(GIGABYTE) * double(KILOBYTE); +//double TERABYTE() { return double(GIGABYTE) * double(KILOBYTE); } + //!< Number of bytes in a terabyte. +// /*!< Implemented as a function to avoid annoying link errors for double +// floating point constants in some compilers. */ + +////////////// + +// Super basic objects... + +//! lowest level object for all hoople objects. supports run-time type id. + +class root_object +{ +public: + virtual ~root_object() {} +}; + +////////////// + +// compiler specific dumping ground for global settings... + +#ifdef _MSC_VER + // turns off annoying complaints from visual c++. + #pragma warning(disable : 4251 4275 4003 4800 4355 4786 4290 4996 4407) + #pragma warning(error : 4172) + // 4251 and 4275 turn off warnings regarding statically linked code + // not being marked with dll import/export flags. + // 4003 turns off warnings about insufficient number of parameters passed + // to a macro. + // 4800 turns off the warning about conversion from int to bool not being + // efficient. + // 4355 turns off the warning re 'this' used in base member init list. + // 4786 turns off the warning about 'identifier' truncated to 'number' + // characters in the debug information which frequenly happens when + // STL pair and set templates are expanded. + // 4172 is made an error because this warning is emitted for a dangerous + // condition; the address of a local variable is being returned, making + // the returned object junk in almost all cases. + // 4996 turns off warnings about deprecated functions, which are mostly + // bullshit, since these are mainly the core posix functions. +#endif // ms visual c++. + +////////////// + +} //namespace. + +#endif // outer guard. + diff --git a/core/library/basis/enhance_cpp.h b/core/library/basis/enhance_cpp.h new file mode 100644 index 00000000..98589ec7 --- /dev/null +++ b/core/library/basis/enhance_cpp.h @@ -0,0 +1,101 @@ +#ifndef ENHANCE_CPP_GROUP +#define ENHANCE_CPP_GROUP + +/*****************************************************************************\ +* * +* Name : enhance_cpp * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace basis { + +//! Provides missing language features in C++. +/*! + The most noticeable missing thing in C++ when trying to build debugging + and tracking subsystems with it is *reflection*. This header attempts to + ameliorate some of the worst missing parts, such as the fact that a function + cannot get its own name, and other really helpful features. +*/ + +//hmmm: temporary to hide missing code. +#define frame_tracking_instance +#define __trail_of_function(a, b, c, d, e) + +class enhance_cpp : public virtual root_object +{ +public: + // this class is an encapsulator; any macros are not actual members. + +////////////// + + //! Defines the name of a class by providing a couple standard methods. + /*! This provides a virtual function functionality slice for the class name, + as well as a static version that can be used when no instances of the + class exist yet. */ + #define DEFINE_CLASS_NAME(objname) \ + static const char *static_class_name() { return (objname); } \ + virtual const char *class_name() const { return static_class_name(); } + +////////////// + + //! FUNCDEF sets the name of a function (and plugs it into the callstack). + /*! This macro establishes the function name and should be used at the top + of functions that wish to participate in class based logged as well as the + callstack tracing capability of hoople. A new variable is created on the + stack to track the function's presence until the function exits, at which + time the stack will no longer show it as active. */ + #define FUNCDEF(func_in) \ + const char *func = (const char *)func_in; \ + frame_tracking_instance __trail_of_function(static_class_name(), func, \ + __FILE__, __LINE__, true) + +////////////// + + //! A macro used within the FUNCTION macro to do most of the work. + #define BASE_FUNCTION(func) astring just_function = astring(func); \ + astring function_name = static_class_name(); \ + function_name += astring("::") + just_function + //! This macro sets up a descriptive variable called "function_name". + /*! The variable includes the object's name (static_class_name() must be + implemented for the current object) and the current function's name within + that object (the macro "func" must be defined with that name). */ + #define FUNCTION(func) BASE_FUNCTION(func); \ + function_name += ": "; \ + update_current_stack_frame_line_number(__LINE__) + + //! A macro used within the INSTANCE_FUNCTION macro. + #define BASE_INSTANCE_FUNCTION(func) astring just_function = astring(func); \ + astring function_name = instance_name(); \ + function_name += astring("::") + just_function + //! A function macro that contains more information. + /*! This macro is similar to FUNCTION but it uses the class's instance_name() + method (see root_object). The instance function usually will provide more + information about the class. */ + #define INSTANCE_FUNCTION(func) BASE_INSTANCE_FUNCTION(func); \ + function_name += ": "; \ + update_current_stack_frame_line_number(__LINE__) + +////////////// + + //! __WHERE__ is a macro that combines the file and line number macros. + /*! These are available to most compilers as automatically updated macros + called __FILE__ and __LINE__. This macro can be used anywhere an astring can + be used and reports the current file name and line number. */ + #define __WHERE__ basis::a_sprintf("%s [line %d]", __FILE__, __LINE__) + +}; + +} //namespace. + +#endif + diff --git a/core/library/basis/environment.cpp b/core/library/basis/environment.cpp new file mode 100644 index 00000000..5e50abfb --- /dev/null +++ b/core/library/basis/environment.cpp @@ -0,0 +1,87 @@ +////////////// +// Name : environment +// Author : Chris Koeritz +////////////// +// Copyright (c) 1994-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +#include "environment.h" + +#include +#include +#ifdef __UNIX__ + #include + #include +#endif +#ifdef __WIN32__ + #define _WINSOCKAPI_ // make windows.h happy about winsock. + #include + #include +#endif + +namespace basis { + +astring environment::get(const astring &variable_name) +{ +#ifdef __WIN32__ + char *value = getenv(variable_name.upper().observe()); + // dos & os/2 require upper case for the name, so we just do it that way. +#else + char *value = getenv(variable_name.observe()); + // reasonable OSes support mixed-case environment variables. +#endif + astring to_return; + if (value) + to_return = astring(value); + return to_return; +} + +bool environment::set(const astring &variable_name, const astring &value) +{ + int ret = 0; +#ifdef __WIN32__ + astring assignment = variable_name + "=" + value; + ret = _putenv(assignment.s()); +#else + ret = setenv(variable_name.s(), value.s(), true); +#endif + return !ret; +} + +basis::un_int environment::system_uptime() +{ +#ifdef __WIN32__ + return timeGetTime(); +#else + static clock_t __ctps = sysconf(_SC_CLK_TCK); // clock ticks per second. + static const double __multiplier = 1000.0 / double(__ctps); + // the multiplier gives us our full range for the tick counter. + + // read uptime info from the OS. + tms uptime; + basis::un_int real_ticks = times(&uptime); + + // now turn this into the number of milliseconds. + double ticks_up = (double)real_ticks; + ticks_up = ticks_up * __multiplier; // convert to time here. + + // we use the previous version of this calculation, which expected a basis::u_int + // to double conversion to provide a modulo operation rather than just leaving + // the basis::un_int at its maximum value (2^32-1). however, that expectation is not + // guaranteed on some platforms (e.g., ARM processor with floating point + // emulation) and thus it becomes a bug around 49 days and 17 hours into + // OS uptime because the value gets stuck at 2^32-1 and never rolls over. + return basis::un_int(ticks_up); +#endif +} + +} //namespace. + diff --git a/core/library/basis/environment.h b/core/library/basis/environment.h new file mode 100644 index 00000000..e9823e73 --- /dev/null +++ b/core/library/basis/environment.h @@ -0,0 +1,55 @@ +#ifndef ENVIRONMENT_CLASS +#define ENVIRONMENT_CLASS + +////////////// +// Name : environment +// Author : Chris Koeritz +////////////// +// Copyright (c) 1994-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +#include "astring.h" +#include "definitions.h" + +namespace basis { + +//! Provides access to the system's environment variables. + +class environment : public virtual root_object +{ +public: + static astring get(const astring &variable_name); + //!< looks up the "variable_name" in the current environment variables. + /*!< this returns the value for "variable_name" as it was found in the + operating system's environment variables that are defined at this point + in time for the user and process. the returned string will be empty if no + variable under that name could be found. */ + +// static astring get(const char *variable_name) { return get(astring(variable_name)); } + //!< synonym using simpler char pointer. + + static bool set(const astring &variable_name, const astring &value); + //!< adds or creates "variable_name" in the environment. + /*!< changes the current set of environment variables by adding or + modifying the "variable_name". its new value will be "value". */ + +// static bool set(const char *variable_name, const char *value) +// { return set(astring(variable_name), astring(value)); } + //!< synonym using simpler char pointers. + + static basis::un_int system_uptime(); + //!< gives the operating system's uptime in a small form that rolls over. +}; + +} //namespace. + +#endif + diff --git a/core/library/basis/functions.h b/core/library/basis/functions.h new file mode 100644 index 00000000..777461a9 --- /dev/null +++ b/core/library/basis/functions.h @@ -0,0 +1,157 @@ +#ifndef FUNCTIONS_GROUP +#define FUNCTIONS_GROUP + +/*****************************************************************************\ +* * +* Name : functions * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +/*! @file functions.h + Provides a set of useful mini-functions. +*/ + +#include "definitions.h" + +namespace basis { + +template type maximum(type a, type b) + { return (a > b)? a : b; } + //!< minimum returns the lesser of two values. +template type minimum(type a, type b) + { return (a < b)? a : b; } + //!< maximum returns the greater of two values. + +template type absolute_value(type a) + { return (a >= 0)? a : -a; } + //!< Returns a if a is non-negative, and returns -a otherwise. + +////////////// + +template bool positive(const type &a) { return a > 0; } + //!< positive returns true if "a" is greater than zero, or false otherwise. +template bool non_positive(const type a) { return a <= 0; } + //!< non_positive returns true if "a" is less than or equal to zero. +template bool negative(const type &a) { return a < 0; } + //!< negative returns true if "a" is less than zero. +template bool non_negative(const type &a) { return a >= 0; } + //!< non_negative returns true if "a" is greater than or equal to zero. + +////////////// + +// the following comparisons are borrowed from the STL. they provide the full set of comparison +// operators for any object that implements the equalizable and orderable base classes. +template +bool operator != (const T1 &x, const T2 &y) { return !(x == y); } + +template +bool operator > (const T1 &x, const T2 &y) { return y < x; } + +template +bool operator <= (const T1 &x, const T2 &y) { return !(y < x); } + +template +bool operator >= (const T1 &x, const T2 &y) { return !(x < y); } + +////////////// + +//! dynamically converts a type to a target type, or throws an exception if it cannot. +template +target_type *cast_or_throw(source_type &to_cast, const target_type &ignored) +{ + if (!&ignored) {} // do nothing. + target_type *cast = dynamic_cast(&to_cast); + if (!cast) throw "error: casting problem, unknown RTTI cast."; + return cast; +} + +//! const version of the cast_or_throw template. +template +const target_type *cast_or_throw(const source_type &to_cast, const target_type &ignored) +{ + if (!&ignored) {} // do nothing. + const target_type *cast = dynamic_cast(&to_cast); + if (!cast) throw "error: casting problem, unknown RTTI cast."; + return cast; +} + +////////////// + +template bool range_check(const type &c, const type &low, + const type &high) { return (c >= low) && (c <= high); } + //!< Returns true if "c" is between "low" and "high" inclusive. + +template type square(const type &a) { return a * a; } + //!< Returns the square of the object (which is a * a). + +template void flip_increasing(type &a, type &b) + { if (b < a) { type tmp = a; a = b; b = tmp; } } + //!< Makes sure that two values are in increasing order (a < b). + +template void flip_decreasing(type &a, type &b) + { if (b > a) { type tmp = a; a = b; b = tmp; } } + //!< Makes sure that two values are in decreasing order (a > b). + +template void swap_values(type &a, type &b) + { type tmp = a; a = b; b = tmp; } + //!< Exchanges the values held by "a" & "b". + +template type sign(type a) + { if (a < 0) return -1; else if (a > 0) return 1; else return 0; } + //!< Returns the numerical sign of a number "a". + +////////////// + +// helpful coding / debugging macros: + +//! deletion with clearing of the pointer. +/*! this function simplifies the two step process of deleting a pointer and +then clearing it to NIL. this makes debugging a bit easier since an access +of NIL should always cause a fault, rather than looking like a possibly +valid object. */ +template +void WHACK(contents * &ptr) { if (ptr) { delete ptr; ptr = NIL; } } + +//! Returns an object that is defined statically. +/*! Thus the returned object will never be recreated once this function +is called within the same scope of memory (within a dynamic library or +application). This is useful for templates that want to have access to a +bogus element whose contents don't matter. NOTE: bogonic is not +thread safe! */ +template type &bogonic() { + static type local_bogon; + return local_bogon; +} + +////////////// + +template +type number_of_packets(type message_size, type packet_size) +{ return message_size / packet_size + ((message_size % packet_size) != 0); } + //!< Reports number of packets needed given a total size and the packet size. + /*!< This returns the number of packets needed to contain a contiguous array + of characters with size "message_size" when the number of characters + per packet is "packet_size". */ + +template +type last_packet_size(type message_size, type packet_size) +{ return message_size % packet_size? message_size % packet_size : packet_size; } + //!< Tells how many bytes are used within last packet. + /*< The companion call to number_of_packets; it returns the size of the last + packet in the sequence of packets, taking into account the special case + where the message_size divides evenly. */ + +////////////// + +} //namespace. + +#endif + diff --git a/core/library/basis/gnu_header.h b/core/library/basis/gnu_header.h new file mode 100644 index 00000000..d78b2a4f --- /dev/null +++ b/core/library/basis/gnu_header.h @@ -0,0 +1,29 @@ +#ifndef {NAME}_CLASS +#define {NAME}_CLASS + +////////////// +// Name : {class name} +// Author : {your name} +////////////// +// Copyright (c) 2010-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +//! brief description goes here. +/*! + detailed description goes here. +*/ + +////////////// +// class definition goes here....... +////////////// + +#endif + diff --git a/core/library/basis/guards.cpp b/core/library/basis/guards.cpp new file mode 100644 index 00000000..5bc81378 --- /dev/null +++ b/core/library/basis/guards.cpp @@ -0,0 +1,47 @@ +////////////// +// Name : guards +// Author : Chris Koeritz +////////////// +// Copyright (c) 1989-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +#include "astring.h" +#include "guards.h" + +namespace basis { + +void format_error(const base_string &class_name, const base_string &func_name, + const base_string &error_message, base_string &to_fill) +{ + astring to_return = class_name; + to_return += "::"; + to_return += func_name; + to_return += ": "; + to_return += error_message; + to_fill = to_return; +} + +void throw_error(const base_string &class_name, const base_string &func_name, + const base_string &error_message) +{ + astring to_throw; + format_error(class_name, func_name, error_message, to_throw); + throw to_throw; +} + +void throw_error(const astring &class_name, const astring &func_name, + const astring &error_message) +{ + throw_error((base_string &)class_name, (base_string &)func_name, (base_string &)error_message); +} + +} //namespace. + diff --git a/core/library/basis/guards.h b/core/library/basis/guards.h new file mode 100644 index 00000000..0f24a908 --- /dev/null +++ b/core/library/basis/guards.h @@ -0,0 +1,92 @@ +#ifndef GUARDS_GROUP +#define GUARDS_GROUP + +////////////// +// Name : guards +// Author : Chris Koeritz +////////////// +// Copyright (c) 1989-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +//! The guards collection helps in testing preconditions and reporting errors. +/*! + It also provides checking of boundary conditions, macros for causing + immediate program exit, and other sentinels for constructing preconditions + and postconditions. +*/ + +namespace basis { + +// forward declaration. +class astring; +class base_string; + +////////////// + +// simpler guards first... + +//! Returns true if the value is within the range specified. +template +bool in_range(const contents &value, const contents &low, const contents &high) +{ return !( (high < low) || (value < low) || (value > high) ); } + +////////////// + +//! Verifies that "value" is between "low" and "high", inclusive. +/*! When the number is not in bounds, the function that is currently executing +returns the "to_return" default provided. "to_return" can be empty for +functions that return void. Note: it is also considered a failure for high to +be less than low. */ +#define bounds_return(value, low, high, to_return) \ + { if (!basis::in_range(value, low, high)) return to_return; } + +////////////// + +//! Verifies that "value" is between "low" and "high", inclusive. +/*! "Value" must be an object for which greater than and less than are defined. +The static_class_name() method and func definition are used to tag the +complaint that is emitted when problems are detected. Note that if +CATCH_ERRORS is defined, then the program is _halted_ if the value is out +of bounds. Otherwise, the "to_return" value is returned. */ +#ifdef CATCH_ERRORS + #define bounds_halt(value, low, high, to_return) { \ + if (((value) < (low)) || ((value) > (high))) { \ + throw_error(basis::astring(static_class_name()), basis::astring(func), \ + basis::astring("value ") + #value \ + + " was not in range " + #low + " to " + #high \ + + " at " + __WHERE__); \ + return to_return; \ + } \ + } +#else + #define bounds_halt(a, b, c, d) bounds_return(a, b, c, d) +#endif + +////////////// + +//! writes a string "to_fill" in a nicely formatted manner using the class and function names. +void format_error(const base_string &class_name, const base_string &func_name, + const base_string &error_message, base_string &to_fill); + +//! throws an error that incorporates the class name and function name. +void throw_error(const base_string &class_name, const base_string &func_name, + const base_string &error_message); + +//! synonym method using astrings for easier char * handling. +void throw_error(const astring &class_name, const astring &func_name, + const astring &error_message); + +////////////// + +} // namespace. + +#endif + diff --git a/core/library/basis/makefile b/core/library/basis/makefile new file mode 100644 index 00000000..fce7c926 --- /dev/null +++ b/core/library/basis/makefile @@ -0,0 +1,10 @@ +include cpp/variables.def + +PROJECT = basis +TYPE = library +SOURCE = astring.cpp common_outcomes.cpp utf_conversion.cpp environment.cpp guards.cpp \ + mutex.cpp +TARGETS = basis.lib + +include cpp/rules.def + diff --git a/core/library/basis/mutex.cpp b/core/library/basis/mutex.cpp new file mode 100644 index 00000000..0bf8b943 --- /dev/null +++ b/core/library/basis/mutex.cpp @@ -0,0 +1,111 @@ +/*****************************************************************************\ +* * +* Name : mutex * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +// NOTE: we are explicitly avoiding use of new and delete here because this +// class is needed by our memory allocation object, which would be +// providing the new and delete methods. + +#include "mutex.h" + +#include + +#ifdef __UNIX__ + #include +#endif +#ifdef __WIN32__ + #define _WINSOCKAPI_ // make windows.h happy about winsock. + #include +#endif + +namespace basis { + +mutex::mutex() { construct(); } + +mutex::~mutex() { destruct(); } + +void mutex::establish_lock() { lock(); } + +void mutex::repeal_lock() { unlock(); } + +void mutex::construct() +{ +#ifdef __WIN32__ + c_os_mutex = (CRITICAL_SECTION *)malloc(sizeof(CRITICAL_SECTION)); + InitializeCriticalSection((LPCRITICAL_SECTION)c_os_mutex); +#elif defined(__UNIX__) + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + int ret = -1; +#ifdef __APPLE__ + ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); +#else + ret = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); +#endif + if (ret != 0) { +//printf("failed to initialize mutex attributes!\n"); fflush(NIL); + } + c_os_mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); + pthread_mutex_init((pthread_mutex_t *)c_os_mutex, &attr); + pthread_mutexattr_destroy(&attr); +#else + #pragma error("no implementation of mutexes for this OS yet!") +#endif +} + +void mutex::destruct() +{ + defang(); +} + +void mutex::defang() +{ + if (!c_os_mutex) return; // already defunct. +#ifdef __WIN32__ + DeleteCriticalSection((LPCRITICAL_SECTION)c_os_mutex); + free(c_os_mutex); +#elif defined(__UNIX__) + pthread_mutex_destroy((pthread_mutex_t *)c_os_mutex); + free(c_os_mutex); +#else + #pragma error("no implementation of mutexes for this OS yet!") +#endif + c_os_mutex = 0; +} + +void mutex::lock() +{ + if (!c_os_mutex) return; +#ifdef __WIN32__ + EnterCriticalSection((LPCRITICAL_SECTION)c_os_mutex); +#elif defined(__UNIX__) + pthread_mutex_lock((pthread_mutex_t *)c_os_mutex); +#else + #pragma error("no implementation of mutexes for this OS yet!") +#endif +} + +void mutex::unlock() +{ + if (!c_os_mutex) return; +#ifdef __WIN32__ + LeaveCriticalSection((LPCRITICAL_SECTION)c_os_mutex); +#elif defined(__UNIX__) + pthread_mutex_unlock((pthread_mutex_t *)c_os_mutex); +#else + #pragma error("no implementation of mutexes for this OS yet!") +#endif +} + +} //namespace. + diff --git a/core/library/basis/mutex.h b/core/library/basis/mutex.h new file mode 100644 index 00000000..1efa08e3 --- /dev/null +++ b/core/library/basis/mutex.h @@ -0,0 +1,137 @@ +#ifndef MUTEX_CLASS +#define MUTEX_CLASS + +/*****************************************************************************\ +* * +* Name : mutex * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "contracts.h" + +//! A simple primitive class that encapsulates OS support for mutual exclusion. +/*! + The word "mutex" is an abbreviation for "mutual exclusion". The mutex + provides a simple synchronization object that supports the programming of + critical sections. It is guaranteed to be safe for threads, but it is only + useful within one application rather than between multiple applications. + The mutex_base is hardly ever used directly; instead the mutex class should + be used. +*/ + +namespace basis { + +class mutex : public virtual base_synchronizer +{ +public: + mutex(); //!< Constructs a new mutex. + + virtual ~mutex(); + //!< Destroys the mutex. It should not be locked upon destruction. + + //! Constructor for use with malloc/free instead of new/delete. + void construct(); + + //! Destructor for use with malloc/free instead of new/delete. + void destruct(); + + void lock(); + //!< Clamps down on the mutex, if possible. + /*!< Otherwise the current thread is blocked until the mutex is unlocked. */ + + void unlock(); + //!< Gives up the possession of the mutex. + + virtual void establish_lock(); + //!< Satisfies base class requirements for locking. + virtual void repeal_lock(); + //!< Satisfies base class requirements for unlocking. + +private: + void *c_os_mutex; //!< OS version of the mutex. + + void defang(); + //!< Removes the underlying OS synchronization primitive. + /*!< This method renders this mutex object inoperable. This is useful + when the reason for the lock has vanished, but the mutex object cannot be + deleted yet. Sometimes it may still be referred to, but there is no + longer any critical section to be protected. */ + + mutex(const mutex &); //!< not allowed. + mutex &operator =(const mutex &); //!< not allowed. +}; + +////////////// + +//! auto_synchronizer simplifies concurrent code by automatically unlocking. +/*! + This relies on the base_synchronizer for the form of the objects that + will provide synchronization. The synchronization object is locked when the + auto_synchronizer is created and it is unlocked when the auto_synchronizer + is destroyed. This is most useful when the auto_synchronizer is an automatic + object; the synchronization lock is grabbed at the point of creation (even + in the middle of a function) and it is released when the function or block + scope exits, thus freeing us of the responsibility of always unlocking the + object before exiting from the critical section. + + More Detail: + The auto_synchronizer provides an easy way to provide nearly idiot-proof + synchronization of functions that share the same locking object. By giving + the synchronizer a working object that's derived from synchronization_base, + its mere construction establishes the lock and its destruction releases the + lock. Thus you can protect a critical section in a function by creating + the auto_synchronizer at the top of the function as an automatic object, or + wherever in the function body is appropriate. When the function exits, the + auto_synchronizer will be destroyed as part of the cleanup and the lock will + be released. If there are multiple synchronization objects in a function, + then be very careful. One must order them appropriately to avoid a deadlock. + + for example: @code + mutex my_lock; // the real synchronization primitive. + ... // lots of program in between. + int calculate_average() { + // our function that must control thread concurrency. + auto_synchronizer syncho(my_lock); // establishes the lock. + ... // lots of stuff done in the function in safety from other threads. + } // end of the function. @endcode + + Note that there was no unlock of the mutex above. Remembering to unlock + synchronization primitives is one of the most troublesome requirements of + programming with multiple threads; the auto_synchronizer can be used in + many situations to automate the release of the lock. +*/ + +class auto_synchronizer +{ +public: + auto_synchronizer(base_synchronizer &locker) : _locker(locker) + { _locker.establish_lock(); } + //!< Construction locks the "locker" object for the current program scope. + /*!< This automatically locks a synchronization object until the current + scope (such as a function or even just a block) is exited, which implements + synchronization without needing multiple unlock calls before every return + statement. */ + + ~auto_synchronizer() { _locker.repeal_lock(); } + //!< Releases the lock as this object goes out of scope. + +private: + base_synchronizer &_locker; //!< the locking object. + + // disallowed. + auto_synchronizer(const auto_synchronizer &locker); + auto_synchronizer &operator =(const auto_synchronizer &locker); +}; + +} //namespace. + +#endif + diff --git a/core/library/basis/outcome.h b/core/library/basis/outcome.h new file mode 100644 index 00000000..830ef4d3 --- /dev/null +++ b/core/library/basis/outcome.h @@ -0,0 +1,98 @@ +#ifndef OUTCOME_CLASS +#define OUTCOME_CLASS + +/*****************************************************************************\ +* * +* Name : outcome * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1990-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "definitions.h" + +namespace basis { + +//! Outcomes describe the state of completion for an operation. +/*! + These are an extremely simple representation of a non-exceptional exit + value as an integer. The range of expression is from 0 to MAXINT. + Outcomes are meant to represent the category of 'how' the operation + completed; they do not carry along any results of 'what' was produced. +*/ + +class outcome +{ +public: + outcome(int value = 0) : c_outcome_value(value) {} + //!< Represents the completion of an operation as a particular "value". + /*!< The outcomes partition the input space of the operation and represent + the different conclusions possible during processing. Values for outcomes + must be maintained on a system-wide basis as unique identifiers for them + to be meaningful. Note that zero is reserved as a default outcome value. + This usually translates to an outcome of OKAY. */ + + ~outcome() { c_outcome_value = 0; } //!< destructor resets outcome value. + + bool equal_to(const outcome &to_compare) const + { return c_outcome_value == to_compare.c_outcome_value; } + //!< Returns true if this outcome is equal to "to_compare". + /*!< comparisons between outcomes will operate properly provided that + all system-wide outcome values are unique. */ + + bool operator == (int to_compare) const + { return c_outcome_value == to_compare; } + //!< Returns true if this outcome is equal to the integer "to_compare". + + int value() const { return c_outcome_value; } + // +#include +#ifdef CVTUTF_DEBUG + #include +#endif + +namespace basis { + +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 + +/* --------------------------------------------------------------------- */ + +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 Booleano 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. + */ +Booleano 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. + + --------------------------------------------------------------------- */ + +////////////// + +#ifdef __cplusplus + +transcode_to_utf16::transcode_to_utf16(const char *utf8_input) +: _orig_length(int(strlen(utf8_input)) + 1), + _converted(new UTF16[_orig_length + 1]) + // we don't ever expect the string to get longer going to the larger data + // type, so the current length should be enough. +{ + _result = conversionOK; + if (_orig_length == 1) { + // no length, so only provide a blank string. + _converted[0] = 0; + return; + } + memset((abyte *)_converted, 0, 2 * _orig_length); + // we use these temporary pointers since the converter resets the source + // and target pointers to the end of the conversion. the same pattern + // is used in the code below. + const UTF8 *temp_in = (const UTF8 *)utf8_input; + UTF16 *temp_out = _converted; + _result = ConvertUTF8toUTF16(&temp_in, temp_in + _orig_length, + &temp_out, temp_out + _orig_length, lenientConversion); +} + +transcode_to_utf16::transcode_to_utf16(const astring &utf8_input) +: _orig_length(utf8_input.length() + 1), + _converted(new UTF16[_orig_length]) +{ + _result = conversionOK; + if (_orig_length == 1) { + // no length, so only provide a blank string. + _converted[0] = 0; + return; + } + memset((abyte *)_converted, 0, 2 * _orig_length); + const UTF8 *temp_in = (const UTF8 *)utf8_input.observe(); + UTF16 *temp_out = _converted; + _result = ConvertUTF8toUTF16(&temp_in, temp_in + _orig_length, + &temp_out, temp_out + _orig_length, lenientConversion); +} + +transcode_to_utf16::~transcode_to_utf16() +{ + delete [] _converted; + _converted = NIL; +} + +int transcode_to_utf16::length() const +{ return int(wcslen((wchar_t *)_converted)); } + +////////////// + +transcode_to_utf8::transcode_to_utf8(const UTF16 *utf16_input) +: _orig_length(int(wcslen((const wchar_t *)utf16_input))), + _new_length(_orig_length * 2 + _orig_length / 2 + 1), + // this is just an estimate. it may be appropriate most of the time. + // whatever doesn't fit will get truncated. + _converted(new UTF8[_new_length]) +{ + _result = conversionOK; + if (_orig_length == 0) { + // no length, so only provide a blank string. + _converted[0] = 0; + return; + } + memset(_converted, 0, _new_length); + const UTF16 *temp_in = (const UTF16 *)utf16_input; + UTF8 *temp_out = _converted; + _result = ConvertUTF16toUTF8(&temp_in, temp_in + _orig_length, + &temp_out, temp_out + _new_length, lenientConversion); +} + +transcode_to_utf8::transcode_to_utf8(const wchar_t *utf16_input) +: _orig_length(int(wcslen(utf16_input))), + _new_length(_orig_length * 2 + _orig_length / 2 + 1), + // this is just an estimate. it may be appropriate most of the time. + // whatever doesn't fit will get truncated. + _converted(new UTF8[_new_length > 0 ? _new_length : 1]) +{ + _result = conversionOK; + if (_orig_length == 0) { + // no length, so only provide a blank string. + _converted[0] = 0; + return; + } + memset(_converted, 0, _new_length); + const UTF16 *temp_in = (const UTF16 *)utf16_input; + UTF8 *temp_out = _converted; + _result = ConvertUTF16toUTF8(&temp_in, temp_in + _orig_length, + &temp_out, temp_out + _new_length, lenientConversion); +} + +transcode_to_utf8::~transcode_to_utf8() +{ + delete [] _converted; + _converted = NIL; +} + +int transcode_to_utf8::length() const +{ return int(strlen((char *)_converted)); } + +transcode_to_utf8::operator astring() const +{ return astring((char *)_converted); } + +////////////// + +null_transcoder::null_transcoder(const char *utf8_input, bool make_own_copy) +: _make_own_copy(make_own_copy), + _converted(make_own_copy? new UTF8[strlen(utf8_input) + 1] + : (const UTF8 *)utf8_input) +{ + if (_make_own_copy) { + strcpy((char *)_converted, utf8_input); + } +} + +null_transcoder::null_transcoder(const astring &utf8_input, bool make_own_copy) +: _make_own_copy(make_own_copy), + _converted(make_own_copy? new UTF8[utf8_input.length() + 1] + : (const UTF8 *)utf8_input.s()) +{ + if (_make_own_copy) { + strcpy((char *)_converted, utf8_input.s()); + } +} + +int null_transcoder::length() const +{ return int(strlen((char *)_converted)); } + +#endif //_cplusplus + +} //namespace. + diff --git a/core/library/basis/utf_conversion.h b/core/library/basis/utf_conversion.h new file mode 100644 index 00000000..33d436eb --- /dev/null +++ b/core/library/basis/utf_conversion.h @@ -0,0 +1,328 @@ +#ifndef UTF_CONVERSION_GROUP +#define UTF_CONVERSION_GROUP + +/*****************************************************************************\ +* * +* Name : utf_conversion * +* Author : Unicode, Inc. (C conversion functions) * +* Author : Chris Koeritz (C++ conversion classes) * +* * +******************************************************************************* +* Copyright (c) 2006-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "astring.h" +#include "definitions.h" + +//! @file utf_conversion.h Support for unicode builds. +/*! @file utf_conversion.h + This file provides conversions between UTF-8 and UTF-16 that are essential + to having the code compile in either the UNICODE or regular ANSI character + builds of win32-based code. +*/ + +// original copyright notice still applies to low-level conversion code: +/* + * Copyright 2001-$now 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. + +------------------------------------------------------------------------ */ + +namespace basis { + +/* --------------------------------------------------------------------- + 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 Booleano; /* 0 or 1 */ + +//hmmm: this is some really gross stuff below, just because it's got +// so much of its guts hanging out. make a class out of the lower- +// level conversion stuff and hide all the details. + +/* 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; + +#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); + +Booleano isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); + +#ifdef __cplusplus +} //extern +#endif //cplusplus + +////////////// + +#ifdef __cplusplus + +// The following types and macros help to make it irrelevant what kind of +// win32 build is being done. They will adapt as needed to provide the +// types used in system calls. They are rendered harmless for other operating +// systems or for non-Unicode builds; this is especially useful for POSIX +// compliant functions that required Unicode in win32 but not in Unix systems. + +#if defined(UNICODE) +///holding to test wx && defined(__WIN32__) + //! to_unicode_temp() converts to UTF-16 as needed for win32 system calls. + /*! this conversion is only appropriate to use within expressions. it does + not create a permanent object and will go out of scope when the full + expression does. */ + #define to_unicode_temp(s) transcode_to_utf16(s) + //! the from_unicode_temp() macro converts the win32 unicode string to UTF-8. + /*! this has the same contraint as to_unicode_temp; it does not persist + beyond the lifetime of the expression containing it. */ + #define from_unicode_temp(s) transcode_to_utf8(s) + //! to_unicode_persist creates a UTF-16 object with block scope. + /*! the object with "name" that is created by this macro will exist on + the stack until the block containing it is closed. this is needed when + storing unicode strings into windows structure, for example. */ + #define to_unicode_persist(name, s) transcode_to_utf16 name(s) + //! from_unicode_persist creates a UTF-8 object with block scope. + /*! similar to to_unicode_persist, this is used for longer-lived objects. */ + #define from_unicode_persist(name, s) transcode_to_utf8 name(s) +#else + // these versions of the macros simply defang any conversions. + #define to_unicode_temp(s) null_transcoder(s, false) + #define from_unicode_temp(s) null_transcoder(s, false) + #define to_unicode_persist(name, s) null_transcoder name(s, true) + #define from_unicode_persist(name, s) null_transcoder name(s, true) +#endif + +#ifdef _MSC_VER + //! sends UTF-8 information to the diagnostic view in the IDE. + #define TRACE_PRINT(s) TRACE(_T("%s"), to_unicode_temp(s)) +#endif + +////////////// + +// The next two classes support converting a UTF-8 string into a UTF-16 +// string and vice-versa. They hold onto the converted string and provide +// operators that return it. + +//! handles the conversion of UTF-8 format strings into UTF-16. + +class transcode_to_utf16 : public virtual root_object +{ +public: + transcode_to_utf16(const char *utf8_input); + //!< translates the "utf8_input" from UTF-8 into UTF-16 format. + /*!< the resulting memory is held in this object until destruction, which + makes this especially convenient for in-place conversions. */ + + transcode_to_utf16(const astring &utf8_input); + //!< this conversion converts the astring in "utf8_input" into UTF-16. + + virtual ~transcode_to_utf16(); + + int length() const; + //!< reports the length of the double-byte-based converted string. + + operator const UTF16 * () const { return _converted; } + //!< observes the converted version of the string (constant version). + operator UTF16 * () { return _converted; } + //!< accesses the converted version of the string (modifiable version). + operator const flexichar * () const { return (const flexichar *)_converted; } + //!< accesses the converted version of the string (modifiable version). + operator flexichar * () { return (flexichar *)_converted; } + //!< accesses the converted version of the string (modifiable version). + + ConversionResult _result; +private: + int _orig_length; //!< how long is the original argument? + UTF16 *_converted; //!< the double-byte version of the original string. +}; + +////////////// + +//! handles the conversion of UTF-16 format strings into UTF-8. + +class transcode_to_utf8 : public virtual root_object +{ +public: + transcode_to_utf8(const UTF16 *utf16_input); + //!< translates the "utf16_input" from UTF-16 into UTF-8 format. + /*!< the resulting memory is held in this object until destruction, which + makes this especially convenient for in-place conversions. */ + + transcode_to_utf8(const wchar_t *utf16_input); + //!< converts win32 style wide characters to UTF-8. + + virtual ~transcode_to_utf8(); + + int length() const; + //!< reports the length of the byte-based converted string. + + operator const UTF8 * () const { return _converted; } + //!< observes the converted version of the string (constant version). + operator UTF8 * () { return _converted; } + //!< accesses the converted version of the string (modifiable version). + + operator astring() const; + //!< converts the char pointer into an astring object. + + ConversionResult _result; +private: + int _orig_length; //!< how long is the original argument? + int _new_length; //!< what did we predictively allocate for the result? + UTF8 *_converted; //!< the double-byte version of the original string. +}; + +////////////// + +//! provides an interface like the transcoders above, but stays in UTF-8. + +class null_transcoder : public virtual root_object +{ +public: + //! allocates memory for the converted form if "make_own_copy" is true. + null_transcoder(const char *utf8_input, bool make_own_copy); + //! similar, but supports astrings. + null_transcoder(const astring &utf8_input, bool make_own_copy); + ~null_transcoder() { + if (_make_own_copy) delete [] _converted; + _converted = NIL; + } + + int length() const; + operator char * () { return (char *)_converted; } + operator const char * () const { return (const char *)_converted; } + +private: + bool _make_own_copy; + const UTF8 *_converted; +}; + +#endif //cplusplus + +} //namespace. + +#endif // outer guard. + diff --git a/core/library/configuration/application_configuration.cpp b/core/library/configuration/application_configuration.cpp new file mode 100644 index 00000000..a8f463ea --- /dev/null +++ b/core/library/configuration/application_configuration.cpp @@ -0,0 +1,374 @@ +/* +* Name : application_configuration +* Author : Chris Koeritz + +* Copyright (c) 1994-$now By Author. 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 is online at: +* http://www.fsf.org/copyleft/gpl.html +* Please send any updates to: fred@gruntose.com +*/ + +#include "application_configuration.h" +#include "ini_configurator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ + #include + #include +#endif +#ifdef __WIN32__ + #include + #include +#else + #include +#endif +#ifdef __UNIX__ + #include + #include +#endif +#include +#include +#include +#include + +using namespace basis; +using namespace filesystem; +using namespace mathematics; +using namespace structures; +using namespace textual; + +#undef LOG +#define LOG(to_print) printf("%s\n", astring(to_print).s()) + +namespace configuration { + +const int MAXIMUM_COMMAND_LINE = 32 * KILOBYTE; + // maximum command line that we'll deal with here. + +#ifdef __UNIX__ +astring application_configuration::get_cmdline_from_proc() +{ + FUNCDEF("get_cmdline_from_proc"); + static astring __check_once_app_path; +//hmmm: we want to use a single per app static synch here! + if (__check_once_app_path.length()) return __check_once_app_path; + +#ifdef __APPLE__ + __check_once_app_path = query_for_process_info(); + return __check_once_app_path; +#endif + + // we have not looked this app's name up in the path yet. + a_sprintf cmds_filename("/proc/%d/cmdline", process_id()); + FILE *cmds_file = fopen(cmds_filename.s(), "r"); + if (!cmds_file) { + LOG("failed to open our process's command line file.\n"); + return "unknown"; + } +//hmmm: this would be a lot nicer using a byte filer. + size_t size = 2000; + char *filebuff = new char[size + 1]; + ssize_t chars_read = getline((char **)&filebuff, &size, cmds_file); + // read the first line, giving ample space for how long it might be. + fclose(cmds_file); // drop the file again. + if (!chars_read || negative(chars_read)) { + LOG("failed to get any characters from our process's cmdline file.\n"); + return "unknown"; + } + // copy the buffer into a string, which works great since the entries in the + // command line are all separated by zero characters. + __check_once_app_path = filebuff; + delete [] filebuff; + // clean out quote characters from the name. + for (int i = __check_once_app_path.length() - 1; i >= 0; i--) { + if (__check_once_app_path[i] == '"') __check_once_app_path.zap(i, i); + } + // check if the thing has a path attached to it. if it doesn't, we need to accentuate + // our knowledge about the file. + filename testing(__check_once_app_path); + if (testing.had_directory()) return __check_once_app_path; // all set. + +//hmmm: the below might be better off as a find app in path method, which relies on which. + + // there was no directory component, so we'll try to guess one. + astring temp_filename(environment::get("TMP") + + a_sprintf("/zz_cmdfind.%d", chaos().inclusive(0, 999999999))); + system((astring("which ") + __check_once_app_path + " >" + temp_filename).s()); + FILE *which_file = fopen(temp_filename.s(), "r"); + if (!which_file) { + LOG("failed to open the temporary output from which.\n"); + return "unknown"; + } + // reallocate the file buffer. + size = 2000; + filebuff = new char[size + 1]; + chars_read = getline((char **)&filebuff, &size, which_file); + fclose(which_file); + unlink(temp_filename.s()); + if (!chars_read || negative(chars_read)) { + LOG("failed to get any characters from the which cmd output.\n"); + return "unknown"; + } else { + // we had some luck using 'which' to locate the file, so we'll use this version. + __check_once_app_path = filebuff; + while (parser_bits::is_eol(__check_once_app_path[__check_once_app_path.end()])) { + __check_once_app_path.zap(__check_once_app_path.end(), __check_once_app_path.end()); + } + } + delete [] filebuff; + return __check_once_app_path; // return whatever which told us. +} + +// deprecated; better to use the /proc/pid/cmdline file. +astring application_configuration::query_for_process_info() +{ +// FUNCDEF("query_for_process_info"); + astring to_return = "unknown"; + // we ask the operating system about our process identifier and store + // the results in a temporary file. + chaos rando; + a_sprintf tmpfile("/tmp/proc_name_check_%d_%d.txt", process_id(), + rando.inclusive(0, 128000)); +#ifdef __APPLE__ + a_sprintf cmd("ps -o args=\"\" %d >%s", process_id(), + tmpfile.s()); +#else + a_sprintf cmd("ps h -O \"args\" %d >%s", process_id(), + tmpfile.s()); +#endif + // run the command to locate our process info. + int sysret = system(cmd.s()); + if (negative(sysret)) { + LOG("failed to run ps command to get process info"); + return to_return; + } + // open the output file for reading. + FILE *output = fopen(tmpfile.s(), "r"); + if (!output) { + LOG("failed to open the ps output file"); + return to_return; + } + // read the file's contents into a string buffer. + char buff[MAXIMUM_COMMAND_LINE]; + size_t size_read = fread(buff, 1, MAXIMUM_COMMAND_LINE, output); + if (size_read > 0) { + // success at finding some text in the file at least. + while (size_read > 0) { + const char to_check = buff[size_read - 1]; + if ( !to_check || (to_check == '\r') || (to_check == '\n') + || (to_check == '\t') ) + size_read--; + else break; + } + to_return.reset(astring::UNTERMINATED, buff, size_read); + } else { + // couldn't read anything. + LOG("could not read output of process list"); + } + unlink(tmpfile.s()); + return to_return; +} +#endif + +// used as a return value when the name cannot be determined. +#define SET_BOGUS_NAME(error) { \ + LOG(error); \ + if (output) { \ + fclose(output); \ + unlink(tmpfile.s()); \ + } \ + astring home_dir = env_string("HOME"); \ + to_return = home_dir + "/failed_to_determine.exe"; \ +} + +astring application_configuration::application_name() +{ +// FUNCDEF("application_name"); + astring to_return; +#ifdef __APPLE__ + char buffer[MAX_ABS_PATH] = { '\0' }; + uint32_t buffsize = MAX_ABS_PATH - 1; + _NSGetExecutablePath(buffer, &buffsize); + to_return = (char *)buffer; +#elif __UNIX__ + to_return = get_cmdline_from_proc(); +#elif defined(__WIN32__) + flexichar low_buff[MAX_ABS_PATH + 1]; + GetModuleFileName(NIL, low_buff, MAX_ABS_PATH - 1); + astring buff = from_unicode_temp(low_buff); + buff.to_lower(); // we lower-case the name since windows seems to UC it. + to_return = buff; +#else + #pragma error("hmmm: no means of finding app name is implemented.") + SET_BOGUS_NAME("not_implemented_for_this_OS"); +#endif + return to_return; +} + +#if defined(__UNIX__) || defined(__WIN32__) + basis::un_int application_configuration::process_id() { return getpid(); } +#else + #pragma error("hmmm: need process id implementation for this OS!") + basis::un_int application_configuration::process_id() { return 0; } +#endif + +astring application_configuration::current_directory() +{ + astring to_return; +#ifdef __UNIX__ + char buff[MAX_ABS_PATH]; + getcwd(buff, MAX_ABS_PATH - 1); + to_return = buff; +#elif defined(__WIN32__) + flexichar low_buff[MAX_ABS_PATH + 1]; + GetCurrentDirectory(MAX_ABS_PATH, low_buff); + to_return = from_unicode_temp(low_buff); +#else + #pragma error("hmmm: need support for current directory on this OS.") + to_return = "."; +#endif + return to_return; +} + +// implement the software product function. +const char *application_configuration::software_product_name() +{ +#ifdef GLOBAL_PRODUCT_NAME + return GLOBAL_PRODUCT_NAME; +#else + return "hoople"; +#endif +} + +astring application_configuration::application_directory() +{ return filename(application_name()).dirname().raw(); } + +structures::version application_configuration::get_OS_version() +{ + version to_return; +#ifdef __UNIX__ + utsname kernel_parms; + uname(&kernel_parms); + to_return = version(kernel_parms.release); +#elif defined(__WIN32__) + OSVERSIONINFO info; + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + ::GetVersionEx(&info); + to_return = version(a_sprintf("%u.%u.%u.%u", basis::un_short(info.dwMajorVersion), + basis::un_short(info.dwMinorVersion), basis::un_short(info.dwPlatformId), + basis::un_short(info.dwBuildNumber))); +#else + #pragma error("hmmm: need version info for this OS!") +#endif + return to_return; +} + +////////////// + +const char *PATH_CONFIGURATION_FILENAME() { return "paths.ini"; } + +astring application_configuration::application_configuration_file() +{ + filename cfg_file(application_directory() + "/" + PATH_CONFIGURATION_FILENAME()); + return cfg_file.raw(); +} + +const astring &application_configuration::GLOBAL_SECTION_NAME() { STATIC_STRING("Common"); } + +const astring &application_configuration::LOGGING_FOLDER_NAME() { STATIC_STRING("LogPath"); } + +////////////// + +const int MAX_LOG_PATH = 200; + // the maximum length of the entry stored for the log path. + +/* +astring application_configuration::installation_root() +{ + astring to_return = read_item(LOCAL_FOLDER_NAME()); + if (!to_return) { + // well, if this other guy has a path, we'll give that back. otherwise, + // we don't know what the path should be at all. + to_return = filename(application_configuration_file()).dirname(); + } + return to_return; +} +*/ + +astring application_configuration::get_logging_directory() +{ + // start with the root of our installation. + astring def_log = application_directory(); +///installation_root(); + // add logs directory underneath that. + def_log += "/logs"; + // add the subdirectory for logs. + + // now grab the current value for the name, if any. + astring log_dir = read_item(LOGGING_FOLDER_NAME()); + // get the entry for the logging path. + if (!log_dir) { + // if the entry was absent, we set it. + ini_configurator ini(application_configuration_file(), + ini_configurator::RETURN_ONLY, + ini_configurator::APPLICATION_DIRECTORY); + ini.store(GLOBAL_SECTION_NAME(), LOGGING_FOLDER_NAME(), def_log); + } else { + // they gave us something. let's replace the environment variables + // in their string so we resolve paths and such. + log_dir = parser_bits::substitute_env_vars(log_dir); + } + + // now we make sure the directory exists. + struct stat to_fill; + int stat_ret = stat(log_dir.observe(), &to_fill); + if (stat_ret || !(to_fill.st_mode & S_IFDIR) ) { + // if it's not anything yet or if it's not a directory, then we need + // to create it. +//if it's something besides a directory... should it be deleted? +#ifdef __UNIX__ + int mk_ret = mkdir(log_dir.s(), 0777); +#endif +#ifdef __WIN32__ + int mk_ret = mkdir(log_dir.s()); +#endif + if (mk_ret) return ""; +//can't have a log file if we can't make the directory successfully??? + } + + return log_dir; +} + +astring application_configuration::make_logfile_name(const astring &base_name) +{ return get_logging_directory() + "/" + base_name; } + +///astring application_configuration::core_bin_directory() { return read_item("core_bin"); } + +astring application_configuration::read_item(const astring &key_name) +{ + filename ini_name = application_configuration_file(); + ini_configurator ini(ini_name, ini_configurator::RETURN_ONLY, + ini_configurator::APPLICATION_DIRECTORY); + astring to_return = ini.load(GLOBAL_SECTION_NAME(), key_name, ""); + if (!!to_return) { + // if the string has any length, then we process any environment + // variables found encoded in the value. + to_return = parser_bits::substitute_env_vars(to_return); + } + return to_return; +} + +} // namespace. + diff --git a/core/library/configuration/application_configuration.h b/core/library/configuration/application_configuration.h new file mode 100644 index 00000000..e51bb210 --- /dev/null +++ b/core/library/configuration/application_configuration.h @@ -0,0 +1,132 @@ +#ifndef APPLICATION_CONFIGURATION_GROUP +#define APPLICATION_CONFIGURATION_GROUP + +/*****************************************************************************\ +* * +* Name : path configuration definitions * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace configuration { + +//! Defines installation-specific locations in the file system. + +class application_configuration : public virtual basis::root_object +{ +public: + virtual ~application_configuration() {} + + // these methods are mainly about the application itself. + + static basis::astring application_name(); + //!< returns the full name of the current application. + + static basis::astring application_directory(); + //!< returns the directory name where this application is running from. + + static basis::un_int process_id(); + //!< returns the process id for this task, if that's relevant on the OS. + + // these interface with the operating system. + + static structures::version get_OS_version(); + //!< returns the operating system's version information. + /*!< for linux, this has: major/minor/release/revision as components. + for ms-windows, this has: major/minor/platform_ID/build_number. */ + + static basis::astring current_directory(); + //!< returns the current directory as reported by the operating system. + + // the following are more about the installation than this application... + + static const char *software_product_name(); + //!< This global function is available to the system at large for branding info. + +// static basis::astring installation_root(); + //!< returns the top-level directory of this installation. + /*!< returns the folder containing the application directory (usually) as + well as the other folders of configuration information and fonts and + such needed by this installation. */ + + static const char *APPLICATION_CONFIGURATION_FILENAME(); + //!< the configuration file provides a set of paths needed here. + /*!< this file stores paths that the low-level libraries need for + proper operation. this is just the base filename; the fully qualified + path to the path configuration file is provided below. */ + + static basis::astring application_configuration_file(); + //!< the fully specified path to the main path configuration file. + /*!< the location of this file is determined by the directory where this + executable is running. this is the only configuration file that should + reside there, given the itchy vista compliance needs. */ + +//// static basis::astring core_bin_directory(); + //!< retrieves the core binary directory location from paths.ini. + + static basis::astring get_logging_directory(); + //!< returns the directory where log files will be stored. + + // the following are key names within the main configuration file. + + static const basis::astring &GLOBAL_SECTION_NAME(); + //!< the root section name for our configuration items in the main ini file. + /*!< within the configuration file, there is a section named as above. + this section should only be used to define path configuration entries that + affect the general operation of the system. entries that are specific + to particular programs or subsystems should be contained in their own + section. */ + +/// static const basis::astring &LOCAL_FOLDER_NAME(); + //!< entry name in the config file that points at the installation root. + /*!< this is where all files for this product are stored on "this" machine. */ + + static const basis::astring &LOGGING_FOLDER_NAME(); + //!< the location where the log files for the system are stored. + /*!< this is always considered to be a directory under the local folder. + the make_logfile_name() function (see below) can be used to create a + properly formed filename for logging. */ + + // helper methods. + + static basis::astring read_item(const basis::astring &key_name); + //!< returns the entry listed under the "key_name". + /*!< if the "key_name" is valid for the root configuration file, then this + should always return an appropriate value. a failure is denoted by return + of an empty string. */ + + static basis::astring make_logfile_name(const basis::astring &base_name); + //!< generates an installation appropriate log file name from "base_name". + /*!< creates and returns a full path name for a log file with the + "base_name" specified, using the LOGGING_FOLDER_NAME() entry as the + directory name. if the logging directory does not exist, it is created if + that is possible. the returned name is suitable for logging mechanisms that + need a filename. an empty string is returned on failure to create the + directory. */ + +#ifdef __UNIX__ + #ifdef __APPLE__ + static basis::astring get_cmdline_for_apple(); + #endif + static basis::astring get_cmdline_from_proc(); + //!< retrieves the command line from the /proc hierarchy on linux. + static basis::astring query_for_process_info(); + //!< seeks out process info for a particular process. +#endif +}; + +} // namespace. + +#endif + diff --git a/core/library/configuration/config_watcher.cpp b/core/library/configuration/config_watcher.cpp new file mode 100644 index 00000000..e54b475f --- /dev/null +++ b/core/library/configuration/config_watcher.cpp @@ -0,0 +1,153 @@ +/*****************************************************************************\ +* * +* Name : config_watcher * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2008-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "config_watcher.h" + +#include +#include +#include +#include + +using namespace basis; +using namespace structures; + +namespace configuration { + +config_watcher::config_watcher(configurator &to_watch) +: _watching(to_watch), + _current_config(new table_configurator), + _previous_config(new table_configurator) +{ + rescan(); // fill out our lists. +} + +config_watcher::~config_watcher() +{ + WHACK(_current_config); + WHACK(_previous_config); +} + +bool config_watcher::rescan() +{ + // copy the current configuration into our previous config tracker. + *_previous_config = *_current_config; + // clean out any current items held. + _current_config->reset(); + + // iterate across the sections in the watched config. + string_set sects; + _watching.section_set(sects); + for (int sectindy = 0; sectindy < sects.length(); sectindy++) { + // every entry in the current section gets added to our current config. + astring curr_section = sects[sectindy]; + string_table entries; + _watching.get_section(curr_section, entries); + _current_config->put_section(curr_section, entries); + } + + return true; +} + +string_set config_watcher::new_sections() const +{ + string_set before; + _previous_config->section_set(before); + string_set after; + _current_config->section_set(before); + return after - before; +} + +string_set config_watcher::deleted_sections() const +{ + string_set before; + _previous_config->section_set(before); + string_set after; + _current_config->section_set(before); + return before - after; +} + +string_set config_watcher::changed_sections() const +{ + string_set before; + _previous_config->section_set(before); + string_set after; + _current_config->section_set(before); + string_set possible_changes = before.intersection(after); + string_set definite_changes; + for (int i = 0; i < possible_changes.elements(); i++) { + const astring §_name = possible_changes[i]; + string_table previous_section; + _previous_config->get_section(sect_name, previous_section); + string_table current_section; + _current_config->get_section(sect_name, current_section); + if (current_section != previous_section) + definite_changes += sect_name; + } + return definite_changes; +} + +string_set config_watcher::deleted_items(const astring §ion_name) +{ + string_table previous_section; + _previous_config->get_section(section_name, previous_section); + string_set previous_names; + previous_section.names(previous_names); + string_table current_section; + _current_config->get_section(section_name, current_section); + string_set current_names; + current_section.names(current_names); + return previous_names - current_names; +} + +string_set config_watcher::new_items(const astring §ion_name) +{ + string_table previous_section; + _previous_config->get_section(section_name, previous_section); + string_set previous_names; + previous_section.names(previous_names); + string_table current_section; + _current_config->get_section(section_name, current_section); + string_set current_names; + current_section.names(current_names); + return current_names - previous_names; +} + +string_set config_watcher::changed_items(const astring §ion_name) +{ + string_table previous_section; + _previous_config->get_section(section_name, previous_section); + string_set previous_names; + previous_section.names(previous_names); + string_table current_section; + _current_config->get_section(section_name, current_section); + string_set current_names; + current_section.names(current_names); + + string_set possible_changes = current_names.intersection(previous_names); + string_set definite_changes; + for (int i = 0; i < possible_changes.elements(); i++) { + const astring &curr_item = possible_changes[i]; + astring prev_value; + _previous_config->get(section_name, curr_item, prev_value); + astring curr_value; + _current_config->get(section_name, curr_item, curr_value); + if (prev_value != curr_value) + definite_changes += curr_item; + } + return definite_changes; +} + +} //namespace. + + diff --git a/core/library/configuration/config_watcher.h b/core/library/configuration/config_watcher.h new file mode 100644 index 00000000..bc32345c --- /dev/null +++ b/core/library/configuration/config_watcher.h @@ -0,0 +1,68 @@ +#ifndef CONFIG_WATCHER_CLASS +#define CONFIG_WATCHER_CLASS + +/*****************************************************************************\ +* * +* Name : config_watcher * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2008-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "configurator.h" +#include "table_configurator.h" + +#include +#include + +namespace configuration { + +//! an object that watches the contents of a configurator for changes. +/*! + when given a configurator object to check, this will initially build an + exact copy of the contents seen. when asked to look for changes to the + configurator, the previous version is compared with the current state and + any changed sections are reported. +*/ + +class config_watcher +{ +public: + config_watcher(configurator &to_watch); + //!< watches the configurator for changes and tracks them. + /*!< "to_watch" must exist for lifetime of this class. */ + + virtual ~config_watcher(); + + DEFINE_CLASS_NAME("config_watcher"); + + bool rescan(); + //!< updates the configurator snapshot and enables the comparison methods. + /*!< call this before testing the changed() method. */ + + // these lists describe how sections have changed, if at all. + structures::string_set new_sections() const; + structures::string_set deleted_sections() const; + structures::string_set changed_sections() const; + + // methods for comparing changes within sections in the config. + structures::string_set new_items(const basis::astring §ion_name); + structures::string_set deleted_items(const basis::astring §ion_name); + structures::string_set changed_items(const basis::astring §ion_name); + +private: + configurator &_watching; //!< the config object we examine. + table_configurator *_current_config; //!< most current records. + table_configurator *_previous_config; //!< records we saw earlier. +}; + +} //namespace. + +#endif + diff --git a/core/library/configuration/configlet.cpp b/core/library/configuration/configlet.cpp new file mode 100644 index 00000000..97e324e4 --- /dev/null +++ b/core/library/configuration/configlet.cpp @@ -0,0 +1,177 @@ +/*****************************************************************************\ +* * +* Name : configlet * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "configlet.h" +#include "configurator.h" + +#include +#include + +using namespace basis; + +namespace configuration { + +const astring bogus_default = "OOPS: not supposed to ever be seen. d'oh!"; + +////////////// + +configlet::configlet(const astring §ion, const astring &entry) +: _section(new astring(section)), + _entry(new astring(entry)) +{} + +configlet::configlet(const configlet &to_copy) +: _section(new astring(*to_copy._section)), + _entry(new astring(*to_copy._entry)) +{} + +configlet::~configlet() +{ + WHACK(_section); + WHACK(_entry); +} + +configlet &configlet::operator =(const configlet &to_copy) +{ + if (this == &to_copy) return *this; + *_section = *to_copy._section; + *_entry = *to_copy._entry; + return *this; +} + +const astring &configlet::section() const { return *_section; } + +const astring &configlet::entry() const { return *_entry; } + +void configlet::section(const astring &new_section) const +{ *_section = new_section; } + +void configlet::entry(const astring &new_entry) const +{ *_entry = new_entry; } + +////////////// + +string_configlet::string_configlet(const astring §ion, const astring &entry, + const astring ¤t_value, const astring &default_value) +: configlet(section, entry), + _current(new astring(current_value)), + _default(new astring(default_value)) +{} + +string_configlet::string_configlet(const string_configlet &to_copy) +: configlet(to_copy.section(), to_copy.entry()), + _current(new astring(*to_copy._current)), + _default(new astring(*to_copy._default)) +{ +} + +string_configlet::~string_configlet() +{ + WHACK(_current); + WHACK(_default); +} + +string_configlet &string_configlet::operator =(const string_configlet &to_copy) +{ + if (this == &to_copy) return *this; + (configlet &)*this = to_copy; + *_current = *to_copy._current; + *_default = *to_copy._default; + return *this; +} + +const astring &string_configlet::current_value() const { return *_current; } + +const astring &string_configlet::default_value() const { return *_default; } + +void string_configlet::current_value(const astring &new_current) +{ *_current = new_current; } + +void string_configlet::default_value(const astring &new_default) +{ *_default = new_default; } + +bool string_configlet::load(configurator &config) +{ + if (config.get(section(), entry(), *_current)) return true; // success. + // we failed to read the value... + *_current = *_default; + if (config.behavior() == configurator::AUTO_STORE) + config.put(section(), entry(), *_current); + return false; // don't hide that it wasn't there. +} + +bool string_configlet::store(configurator &config) const +{ return config.put(section(), entry(), *_current); } + +configlet *string_configlet::duplicate() const +{ return new string_configlet(section(), entry(), *_current, *_default); } + +////////////// + +int_configlet::int_configlet(const astring §ion, const astring &entry, + int current_value, int default_value) +: configlet(section, entry), + _current(current_value), + _default(default_value) +{ +} + +int_configlet::~int_configlet() {} + +void int_configlet::current_value(int new_current) +{ _current = new_current; } + +bool int_configlet::load(configurator &config) +{ + astring temp; + bool to_return = config.get(section(), entry(), temp); + // just check if it was already there. + int temp_c = config.load(section(), entry(), _default); + current_value(temp_c); + return to_return; +} + +bool int_configlet::store(configurator &config) const +{ return config.store(section(), entry(), _current); } + +configlet *int_configlet::duplicate() const +{ return new int_configlet(*this); } + +////////////// + +bounded_int_configlet::bounded_int_configlet(const astring §ion, + const astring &entry, int current_value, int default_value, + int minimum, int maximum) +: int_configlet(section, entry, current_value, default_value), + _minimum(minimum), + _maximum(maximum) +{ +} + +bounded_int_configlet::~bounded_int_configlet() {} + +void bounded_int_configlet::current_value(int new_current) +{ + if (new_current < _minimum) + new_current = default_value(); + if (new_current > _maximum) + new_current = default_value(); + int_configlet::current_value(new_current); +} + +configlet *bounded_int_configlet::duplicate() const +{ return new bounded_int_configlet(*this); } + +} //namespace. + diff --git a/core/library/configuration/configlet.h b/core/library/configuration/configlet.h new file mode 100644 index 00000000..bd848c92 --- /dev/null +++ b/core/library/configuration/configlet.h @@ -0,0 +1,175 @@ +#ifndef CONFIGLET_CLASS +#define CONFIGLET_CLASS + +/*****************************************************************************\ +* * +* Name : configlet * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "configurator.h" + +#include +#include + +namespace configuration { + +//! Represents an atom of configuration info. +/*! + The configlet has a location in a configuration repository that is defined + by its section and key name. Derived types can also have a value that is + stored in that location. +*/ + +class configlet : public virtual basis::root_object +{ +public: + configlet(const basis::astring §ion, const basis::astring &entry); + //!< creates a configlet that lives in the "section" at the "entry". + configlet(const configlet &to_copy); + + virtual ~configlet(); + + DEFINE_CLASS_NAME("configlet"); + + configlet &operator =(const configlet &to_copy); + + const basis::astring §ion() const; + //!< observes the section of this configlet. + const basis::astring &entry() const; + //!< observes the entry name of this configlet. + + void section(const basis::astring &new_section) const; + //!< modifies the configlet section location. + void entry(const basis::astring &new_entry) const; + //!< modifies the configlet entry name. + + virtual bool load(configurator &config) = 0; + //!< retrieves the configlet's information from the "config". + /*!< true is returned when this is successful. note that false is returned + if the entry was not originally present; if the configurator has the + AUTO_STORE behavior, then we will write out the default value on failure. + the next load() would be a success in that case, but would return the + default. */ + + virtual bool store(configurator &config) const = 0; + //!< writes the configlet's information out to the "config". + + virtual configlet *duplicate() const = 0; + //!< a virtual copy constructor for configlets. + /*!< the returned object will be a new copy of this configlet. */ + +private: + basis::astring *_section; //!< the section name, with whatever form is appropriate. + basis::astring *_entry; //!< the entry name in the native representation. +}; + +////////////// + +//! a string_configlet holds onto a character string value. +/*! + it has a current value, which could change due to updates to the + configuration, and a default value that probably won't change as often. + if the "load" operation fails, the default value will be used. +*/ + +class string_configlet : public configlet +{ +public: + string_configlet(const basis::astring §ion, const basis::astring &entry, + const basis::astring ¤t_value = basis::astring::empty_string(), + const basis::astring &default_value = basis::astring::empty_string()); + string_configlet(const string_configlet &to_copy); + virtual ~string_configlet(); + + string_configlet &operator =(const string_configlet &to_copy); + + const basis::astring ¤t_value() const; + const basis::astring &default_value() const; + + void current_value(const basis::astring &new_current); + void default_value(const basis::astring &new_default); + + virtual bool load(configurator &config); + virtual bool store(configurator &config) const; + + configlet *duplicate() const; + +private: + basis::astring *_current; + basis::astring *_default; +}; + +////////////// + +//! Stores a simple integer in a configuration repository. + +class int_configlet : public configlet +{ +public: + int_configlet(const basis::astring §ion, const basis::astring &entry, + int current_value = 0, int default_value = 0); + virtual ~int_configlet(); + + int current_value() const { return _current; } + + virtual void current_value(int new_current); + //!< the modifier function is virtual so derived classes can extend. + + int default_value() const { return _default; } + void default_value(int new_default) { _default = new_default; } + + virtual bool load(configurator &config); + virtual bool store(configurator &config) const; + + configlet *duplicate() const; + +private: + int _current; + int _default; +}; + +////////////// + +//! Stores an integer in a configuration repository with range checking. +/*! + a bounded_int_configlet has current and default values but also specifies a + valid range for the current value. if the current value falls outside + of that range (even via a "set" operation), then the default value is + used for the current. +*/ + +class bounded_int_configlet : public int_configlet +{ +public: + bounded_int_configlet(const basis::astring §ion, const basis::astring &entry, + int current_value, int default_value, int minimum, int maximum); + virtual ~bounded_int_configlet(); + + virtual void current_value(int new_current); + + int minimum() const { return _minimum; } + int maximum() const { return _maximum; } + + void minimum(int new_min) { _minimum = new_min; } + void maximum(int new_max) { _maximum = new_max; } + + configlet *duplicate() const; + +private: + int _minimum; + int _maximum; +}; + +} //namespace. + +#endif + diff --git a/core/library/configuration/configurable.h b/core/library/configuration/configurable.h new file mode 100644 index 00000000..c40f54f7 --- /dev/null +++ b/core/library/configuration/configurable.h @@ -0,0 +1,57 @@ +#ifndef CONFIGURABLE_CLASS +#define CONFIGURABLE_CLASS + +/*****************************************************************************\ +* * +* Name : configurable * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! base class for objects that support configuration_list updates. +/*! + The configuration_list implements a set of configuration items and it can + be used to represent a "delta" between the current configuration of an + object and a new configuration that is desired. Objects based on the + configurable class support taking that delta chunk of configuration info + and adapting their current internal configuration to meet the request, if + possible. These objects also support querying their current configuration, + which reports all configuration item names and current settings. +*/ + +#include + + +// forward. +class configuration_list; + +class configurable : public virtual root_object +{ +public: + virtual ~configurable() {} + + DEFINE_CLASS_NAME("configurable"); + + virtual void get_config(configuration_list &to_fill, bool append) const = 0; + //!< interprets the contents of this object as a configuration list. + /*!< the list of configlets can be stored in any configurator object. + if the "append" flag is true, then the list is added to. otherwise it + is cleared first. this method can also be used to retrieve the configlets + that this class defines as its configuration interface. that list can + then be filled in using a configurator and passed to set_config(). */ + + virtual bool set_config(const configuration_list &to_use) = 0; + //!< retrieves the config items from "to_use" and stores them here. + /*!< false is returned if any of the key items are missing or if the + new key cannot be decoded. */ +}; + +#endif + diff --git a/core/library/configuration/configuration_list.cpp b/core/library/configuration/configuration_list.cpp new file mode 100644 index 00000000..97b79650 --- /dev/null +++ b/core/library/configuration/configuration_list.cpp @@ -0,0 +1,97 @@ +/*****************************************************************************\ +* * +* Name : configuration_list * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "configlet.h" +#include "configuration_list.h" + +#include +#include + +#include + +using namespace basis; +using namespace structures; + +namespace configuration { + +class cl_figlet_list : public amorph {}; + +////////////// + +configuration_list::configuration_list() +: _figs(new cl_figlet_list) +{ +} + +configuration_list::~configuration_list() +{ + WHACK(_figs); +} + +void configuration_list::reset() { _figs->reset(); } + +void configuration_list::add(const configlet &new_item) +{ + zap(new_item); + _figs->append(new_item.duplicate()); +} + +const configlet *configuration_list::find(const configlet &to_find) const +{ + for (int i = 0; i < _figs->elements(); i++) { + configlet &curr = *_figs->borrow(i); + if ( (to_find.section() == curr.section()) + && (to_find.entry() == curr.entry()) + && (typeid(curr) == typeid(to_find)) ) { + return &curr; + } + } + return NIL; +} + +bool configuration_list::zap(const configlet &dead_item) +{ + for (int i = 0; i < _figs->elements(); i++) { + configlet &curr = *_figs->borrow(i); + if ( (dead_item.section() == curr.section()) + && (dead_item.entry() == curr.entry()) ) { + _figs->zap(i, i); + return true; + } + } + return false; +} + +bool configuration_list::load(configurator &config) +{ + bool to_return = true; + for (int i = 0; i < _figs->elements(); i++) { + configlet &curr = *_figs->borrow(i); + if (!curr.load(config)) to_return = false; // any failure is bad. + } + return to_return; +} + +bool configuration_list::store(configurator &config) const +{ + bool to_return = true; + for (int i = 0; i < _figs->elements(); i++) { + configlet &curr = *_figs->borrow(i); + if (!curr.store(config)) to_return = false; // any failure is bad. + } + return to_return; +} + +} //namespace. + diff --git a/core/library/configuration/configuration_list.h b/core/library/configuration/configuration_list.h new file mode 100644 index 00000000..0aea0ca7 --- /dev/null +++ b/core/library/configuration/configuration_list.h @@ -0,0 +1,70 @@ +#ifndef CONFIGURATION_LIST_CLASS +#define CONFIGURATION_LIST_CLASS + +/*****************************************************************************\ +* * +* Name : configuration_list * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace configuration { + +// forward. +class cl_figlet_list; +class configlet; +#include + +//! Manages a collection of configlet objects. +/*! + This class provides the ability to operate on the collection of configlets + as a whole. They can be retrieved from or stored to a configurator object. +*/ + +class configuration_list : public virtual basis::root_object +{ +public: + configuration_list(); + virtual ~configuration_list(); + + DEFINE_CLASS_NAME("configuration_list"); + + void reset(); //!< removes all items from the list. + + void add(const configlet &new_item); + //!< adds another configuration atom into the list. + + const configlet *find(const configlet &to_find) const; + //!< locates the actual configlet with the section and entry of "to_find". + /*!< note that this might fail if no matching section and entry are found, + thus returning NIL. the returned object is still kept in the list, so + do not try to destroy it. also note that the object passed in must be + the same type as the object to be found; otherwise, NIL will be + returned. */ + + bool zap(const configlet &dead_item); + //!< removes a previously added configuration item. + /*!< the "dead_item" need only provide the section and entry names. */ + + //! reads the values of all the configlets stored in "config" into this. + bool load(configurator &config); + //! writes the current values of all the configlets in "this" into "config". + bool store(configurator &config) const; + +private: + cl_figlet_list *_figs; //!< our list of configlets. +}; + +} //namespace. + +#endif + diff --git a/core/library/configuration/configurator.cpp b/core/library/configuration/configurator.cpp new file mode 100644 index 00000000..57cab64c --- /dev/null +++ b/core/library/configuration/configurator.cpp @@ -0,0 +1,82 @@ +/*****************************************************************************\ +* * +* Name : configurator * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "configurator.h" + +#include +#include +#include + +using namespace basis; +using namespace structures; + +namespace configuration { + +configurator::~configurator() {} + +astring configurator::load(const astring §ion, const astring &entry, + const astring &default_string) +{ + astring to_return; + if (!get(section, entry, to_return)) { + to_return = default_string; + if (_behavior == AUTO_STORE) put(section, entry, to_return); + // save the entry back if we're in autostore mode. + } + return to_return; +} + +bool configurator::store(const astring §ion, const astring &entry, + const astring &to_store) +{ return put(section, entry, to_store); } + +bool configurator::store(const astring §ion, const astring &entry, + int value) +{ + return store(section, entry, astring(astring::SPRINTF, "%d", value)); +} + +void configurator::sections(string_array &list) +{ + // default implementation does nothing. + list = string_array(); +} + +void configurator::section_set(string_set &list) +{ + string_array temp; + sections(temp); + list = temp; +} + +int configurator::load(const astring §ion, const astring &entry, + int def_value) +{ + astring value_string; + if (!get(section, entry, value_string)) { + if (_behavior == AUTO_STORE) store(section, entry, def_value); + return def_value; + } + return value_string.convert(def_value); +} + +bool configurator::section_exists(const astring §ion) +{ + string_table infos; + // heavy-weight call here... + return get_section(section, infos); +} + +} //namespace. + diff --git a/core/library/configuration/configurator.h b/core/library/configuration/configurator.h new file mode 100644 index 00000000..6286a7c8 --- /dev/null +++ b/core/library/configuration/configurator.h @@ -0,0 +1,127 @@ +#ifndef CONFIGURATOR_CLASS +#define CONFIGURATOR_CLASS + +/*****************************************************************************\ +* * +* Name : configurator * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include + +namespace configuration { + +//! Provides a base class for configuration repositories. +/*! + All items that can be stored are modelled as having an entry name and a + value. Groups of entries are stored in sections, in which the data + usually have some relation to each other or share a common purpose. +*/ + +class configurator : public virtual basis::root_object +{ +public: + enum treatment_of_defaults { AUTO_STORE, RETURN_ONLY }; + //!< Governs how missing items are treated. + /*!< When the default value is used in the get() method below, it can + either be written to the ini file automatically (AUTO_STORE) or it can + just be returned (RETURN_ONLY). */ + + configurator(treatment_of_defaults behavior = RETURN_ONLY) : _behavior(behavior) {} + virtual ~configurator(); + + //! observes the behavior chosen for the load() function. + treatment_of_defaults behavior() const { return _behavior; } + //! modifies the behavior of the load() function. + void behavior(treatment_of_defaults new_behavior) { + _behavior = (new_behavior == RETURN_ONLY)? RETURN_ONLY : AUTO_STORE; + } + + virtual bool get(const basis::astring §ion, const basis::astring &entry, + basis::astring &found) = 0; + //!< Retrieves an item from the configuration store. + /*!< This retrieves a string into "found" that is listed in the "section" + specified under the "entry" name. if the string is not found, false is + returned. */ + + virtual bool put(const basis::astring §ion, const basis::astring &entry, + const basis::astring &to_store) = 0; + //!< Places an item into the configuration store. + /*!< places an entry into the "section" under the "entry" name using the + string "to_store". if the storage was successful, true is returned. + reasons for failure depend on the derived class implementations. */ + + bool store(const basis::astring §ion, const basis::astring &entry, + const basis::astring &to_store); + //!< a synonym for put. + + //! a synonym for get that implements the auto-store behavior. + /*! if the behavior is set to auto-store, then the default value will be + written when no value existed prior to the load() invocation. */ + basis::astring load(const basis::astring §ion, const basis::astring &entry, + const basis::astring &default_value); + + //! stores an integer value from the configuration store. + bool store(const basis::astring §ion, const basis::astring &entry, int value); + //! loads an integer value from the configuration store. + int load(const basis::astring §ion, const basis::astring &entry, int def_value); + + // the various methods below that operate on sections and entries might not + // be provided by all configurators. that is why there are empty default + // (or simplistic and slow) implementations provided below. + + virtual void sections(structures::string_array &list); + //!< retrieves the section names into "list". + + void section_set(structures::string_set &list); + //!< similar to above, but stores section names into a set. + /*!< this never needs to be overridden; it's simply a set instead + of an array. the real sections method is above for string_array. */ + + virtual bool delete_entry(const basis::astring & formal(section), + const basis::astring & formal(entry)) { return false; } + //!< eliminates the entry specified by the "section" and "entry" name. + + virtual bool delete_section(const basis::astring & formal(section) ) + { return false; } + //!< whacks the entire "section" specified. + + virtual bool section_exists(const basis::astring §ion); + //!< returns true if the "section" is found in the configurator. + /*!< the default implementation is quite slow; if there is a swifter means + for a particular type of configurator, then this should be overridden. */ + + virtual bool get_section(const basis::astring & formal(section), + structures::string_table & formal(found) ) { return false; } + //!< retrieves an entire "section", if supported by the derived object. + /*!< the symbol table "found" gets the entries from the "section". + see symbol_table.h for more details about string_tables. true is + returned if the section existed and its contents were put in "found". */ + + virtual bool put_section(const basis::astring & formal(section), + const structures::string_table & formal(to_store) ) { return false; } + //!< stores an entire "section" from the table in "to_store", if supported. + /*!< if any entries already exist in the "section", then they are + eliminated before the new entries are stored. true is returned if the + write was successful. */ + +private: + treatment_of_defaults _behavior; //!< records the treatment for defaults. +}; + +} //namespace. + +#endif + diff --git a/core/library/configuration/ini_configurator.cpp b/core/library/configuration/ini_configurator.cpp new file mode 100644 index 00000000..c65bdcca --- /dev/null +++ b/core/library/configuration/ini_configurator.cpp @@ -0,0 +1,357 @@ +/*****************************************************************************\ +* * +* Name : ini_configurator * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "ini_configurator.h" +#include "application_configuration.h" +#include "variable_tokenizer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef LOG +#define LOG(to_print) printf("%s::%s: %s\n", static_class_name(), func, astring(to_print).s()) + +using namespace basis; +using namespace filesystem; +using namespace structures; + +namespace configuration { + +//#define DEBUG_INI_CONFIGURATOR + // uncomment for noisy version. + +const int MAXIMUM_LINE_INI_CONFIG = 16384; + +// a default we hope never to see in an ini file. +SAFE_STATIC_CONST(astring, ini_configurator::ini_str_fake_default, ("NoTomatoesNorPotatoesNorQuayle")) + +ini_configurator::ini_configurator(const astring &ini_filename, + treatment_of_defaults behavior, file_location_default where) +: configurator(behavior), + _ini_name(new filename), +#ifdef __UNIX__ + _parser(new ini_parser("", behavior)), +#endif + _where(where), + _add_spaces(false) +{ + name(ini_filename); // set name properly. +} + +ini_configurator::~ini_configurator() +{ + WHACK(_ini_name); +#ifdef __UNIX__ + WHACK(_parser); +#endif +} + +astring ini_configurator::name() const { return _ini_name->raw(); } + +void ini_configurator::refresh() +{ +#ifdef __UNIX__ + write_ini_file(); + WHACK(_parser); + _parser = new ini_parser("", behavior()); +#endif +} + +void ini_configurator::name(const astring &name) +{ + *_ini_name = name; + + bool use_appdir = true; + // true if we should put files where programs start for those filenames + // that don't include a directory name. + if (_where == OS_DIRECTORY) use_appdir = false; + if (_where == ALL_USERS_DIRECTORY) use_appdir = false; +#ifndef __WIN32__ + use_appdir = true; +#endif + // we must create the filename if they specified no directory at all. + if (!_ini_name->had_directory()) { + if (use_appdir) { + // this is needed in case there is an ini right with the file; our + // standard is to check there first. + *_ini_name = filename(application_configuration::application_directory(), + _ini_name->basename()); + } else if (!use_appdir && (_where == ALL_USERS_DIRECTORY) ) { + // when the location default is all users, we get that from the + // environment. for the OS dir choice, we leave out the path entirely. + directory::make_directory(environment::get("ALLUSERSPROFILE") + + "/" + application_configuration::software_product_name()); + *_ini_name = filename(environment::get("ALLUSERSPROFILE") + + "/" + application_configuration::software_product_name(), + _ini_name->basename()); + } + } +#ifdef __UNIX__ + // read in the file's contents. + read_ini_file(); +#endif +} + +void ini_configurator::sections(string_array &list) +{ + list = string_array(); + // open our ini file directly as a file. + byte_filer section8(*_ini_name, "rb"); + if (!section8.good()) return; // not a healthy file. + astring line_found; + // iterate through the lines of the ini file and see if we can't find a + // bunch of section names. + while (section8.read(line_found, MAXIMUM_LINE_INI_CONFIG) > 0) { + // is the line in the format "^[ \t]*\[\([^\]]+\)\].*$" ? + // if it is in that format, we add the matched \1 into our list. + line_found.strip_white_spaces(); + if (line_found[0] != '[') continue; // no opening bracket. skip line. + line_found.zap(0, 0); // toss opening bracket. + int close_brack_indy = line_found.find(']'); + if (negative(close_brack_indy)) continue; // no closing bracket. + line_found.zap(close_brack_indy, line_found.end()); + list += line_found; + } +} + +//hmmm: refactor section_exists to use the sections call, if it's faser? +bool ini_configurator::section_exists(const astring §ion) +{ +#ifdef __WIN32__ + string_table infos; + // heavy-weight call here... + return get_section(section, infos); +#else + return _parser->section_exists(section); +#endif +} + +#ifdef __UNIX__ +void ini_configurator::read_ini_file() +{ +#ifdef DEBUG_INI_CONFIGURATOR + FUNCDEF("read_ini_file"); +#endif + _parser->reset(""); // clear out our current contents. + byte_filer ini_file; + bool open_ret = ini_file.open(*_ini_name, "rb"); // simple reading. +#ifdef DEBUG_INI_CONFIGURATOR + if (!open_ret) LOG(astring("failed to open ini file: ") + *_ini_name); + if (!ini_file.good()) LOG(astring("ini file not good: ") + *_ini_name); +#endif + if (!open_ret || !ini_file.good()) { + return; // failure. + } + int file_size = ini_file.length(); // get the file length. + // read the file. + astring contents(' ', file_size + 3); + int bytes_read = ini_file.read((abyte *)contents.observe(), file_size); + contents.zap(bytes_read + 1, contents.end()); + _parser->reset(contents); +} + +void ini_configurator::write_ini_file() +{ +#ifdef DEBUG_INI_CONFIGURATOR + FUNCDEF("write_ini_file"); +#endif + +//hmmm: just set dirty flag and use that for deciding whether to write. +//hmmm: future version, have a thread scheduled to write. + + // open filer with new mode for cleaning. + byte_filer ini_file; + bool open_ret = ini_file.open(*_ini_name, "wb"); + // open the file for binary read/write and drop previous contents. +#ifdef DEBUG_INI_CONFIGURATOR + if (!open_ret) LOG(astring("failed to open ini file: ") + *_ini_name); + if (!ini_file.good()) LOG(astring("ini file not good: ") + *_ini_name); +#endif + if (!open_ret || !ini_file.good()) return; // failure. + + // output table's contents to text. + astring text; + _parser->restate(text, _add_spaces); + ini_file.write((abyte *)text.observe(), text.length()); +} +#endif //UNIX + +bool ini_configurator::delete_section(const astring §ion) +{ +#ifdef __WIN32__ + return put_profile_string(section, "", ""); +#else + // zap the section. + bool to_return = _parser->delete_section(section); + // schedule the file to write. + write_ini_file(); + return to_return; +#endif +} + +bool ini_configurator::delete_entry(const astring §ion, const astring &ent) +{ +#ifdef __WIN32__ + return put_profile_string(section, ent, ""); +#else + // zap the entry. + bool to_return = _parser->delete_entry(section, ent); + // schedule the file to write. + write_ini_file(); + return to_return; +#endif +} + +bool ini_configurator::put(const astring §ion, const astring &entry, + const astring &to_store) +{ +/// FUNCDEF("put"); + if (!to_store.length()) return delete_entry(section, entry); + else if (!entry.length()) return delete_section(section); + else if (!section.length()) return false; +#ifdef __WIN32__ + return put_profile_string(section, entry, to_store); +#else + // write the entry. + bool to_return = _parser->put(section, entry, to_store); + // schedule file write. + write_ini_file(); + return to_return; +#endif +} + +bool ini_configurator::get(const astring §ion, const astring &entry, + astring &found) +{ +#ifndef __WIN32__ + return _parser->get(section, entry, found); +#else + flexichar temp_buffer[MAXIMUM_LINE_INI_CONFIG]; + temp_buffer[0] = 0; + get_profile_string(section, entry, ini_str_fake_default(), + temp_buffer, MAXIMUM_LINE_INI_CONFIG - 1); + found = from_unicode_temp(temp_buffer); + return !(ini_str_fake_default() == found); +#endif +} + +bool ini_configurator::get_section(const astring §ion, string_table &info) +{ +/// FUNCDEF("get_section"); +#ifndef __WIN32__ + return _parser->get_section(section, info); +#else + info.reset(); + const int buffer_size = 200000; + + flexichar low_buff[buffer_size + 3]; + int read_len = GetPrivateProfileSection(to_unicode_temp(section.observe()), + low_buff, buffer_size - 1, to_unicode_temp(name())); + if (!read_len) return false; // assume the API means there was no section. + + low_buff[read_len] = '\1'; // signal beyond the end of the stuff. + low_buff[read_len + 1] = '\0'; // make sure we're still zero terminated. + + bool last_was_nil = false; + // this loop replaces all the embedded nils with separators to allow the + // variable_tokenizer to retrieve all the strings from the section. + for (int i = 0; i < read_len; i++) { + if (!low_buff[i] && last_was_nil) { + // termination condition; we got two nils in a row. + // this is just paranoia; the length should tell us. + break; + } else if (!low_buff[i]) { + low_buff[i] = '\1'; // replace with a separator. + last_was_nil = true; + } else last_was_nil = false; // reset the nil flag. + } + + // now convert to a simple astring. + astring buff = from_unicode_temp(low_buff); + int length = buff.length(); + buff.shrink(); + variable_tokenizer parser("\1", "="); + parser.parse(buff); + info = parser.table(); + return true; +#endif +} + +bool ini_configurator::put_section(const astring §ion, + const string_table &info) +{ +#ifdef __WIN32__ + variable_tokenizer parser("\1", "="); + parser.table() = info; + astring flat = parser.text_form(); + flat += "\1\1"; // add terminating guard. + int len = flat.length(); + for (int i = 0; i < len; i++) { + if (flat[i] == '\1') { + flat[i] = '\0'; + if (flat[i+1] == ' ') { + // if the space character is next, shift it before the nil to avoid + // keys with a preceding space. + flat[i] = ' '; + flat[i + 1] = '\0'; + } + } + } + return WritePrivateProfileSection(to_unicode_temp(section), + to_unicode_temp(flat), to_unicode_temp(name())); +#else + // write the section. + bool to_return = _parser->put_section(section, info); + // schedule file write. + write_ini_file(); + return to_return; +#endif +} + +#ifdef __WIN32__ +bool ini_configurator::put_profile_string(const astring §ion, + const astring &entry, const astring &to_store) +{ + return bool(WritePrivateProfileString(to_unicode_temp(section), + entry.length() ? (flexichar *)to_unicode_temp(entry) : NIL, + to_store.length() ? (flexichar *)to_unicode_temp(to_store) : NIL, + to_unicode_temp(name()))); +} + +void ini_configurator::get_profile_string(const astring §ion, + const astring &entry, const astring &default_value, + flexichar *return_buffer, int buffer_size) +{ + GetPrivateProfileString(section.length() ? + (flexichar *)to_unicode_temp(section) : NIL, + entry.length() ? (flexichar *)to_unicode_temp(entry) : NIL, + to_unicode_temp(default_value), + return_buffer, buffer_size, to_unicode_temp(name())); +} +#endif + +} //namespace. + + diff --git a/core/library/configuration/ini_configurator.h b/core/library/configuration/ini_configurator.h new file mode 100644 index 00000000..ad99906d --- /dev/null +++ b/core/library/configuration/ini_configurator.h @@ -0,0 +1,145 @@ +#ifndef INI_CONFIGURATOR_CLASS +#define INI_CONFIGURATOR_CLASS + +/*****************************************************************************\ +* * +* Name : ini_configurator * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "configurator.h" +#ifndef __WIN32__ + #include "ini_parser.h" + #include +#endif + +#include +#include +#include + +namespace configuration { + +//! Supports a configurator-based interface on text initialization files. + +class ini_configurator : public configurator +{ +public: + //! chooses where the ini file is located if no path to it is provided. + /*! the ini file being manipulated will be stored in either the same + directory as the program being executed (APPLICATION_DIRECTORY) or in the + directory where the operating system resides (OS_DIRECTORY). however, the + OS_DIRECTORY choice only really makes sense on windows. if the flag is + instead ALL_USERS_DIRECTORY, then the directory pointed at by the + $ALLUSERSPROFILE variable will be used on windows; otherwise, the default + is the same as for APPLICATION_DIRECTORY. */ + enum file_location_default { + APPLICATION_DIRECTORY, //!< config files live with application. + OS_DIRECTORY, //!< config files live in operating system directory. + ALL_USERS_DIRECTORY //!< config files live in the "all users" account. + }; + + ini_configurator(const basis::astring &ini_filename, + treatment_of_defaults behavior = RETURN_ONLY, + file_location_default where = ALL_USERS_DIRECTORY); + //!< creates an ini_configurator that stores entries into "ini_filename". + /*!< the ini config will have the "behavior" specified for how to handle + missing items. "where" dictates the file's location if no path is + specified as part of the "ini_filename". */ + + virtual ~ini_configurator(); + + DEFINE_CLASS_NAME("ini_configurator"); + + void refresh(); + //!< useful mainly on unix/linux, where the file is parsed and held in memory. + +//hmmm: where are: +// save_to_file() +// is_modified() +//? + + basis::astring name() const; + //!< observes the name of the file used for ini entries. + void name(const basis::astring &name); + //!< modifies the name of the file used for ini entries. + + virtual bool get(const basis::astring §ion, const basis::astring &entry, + basis::astring &found); + //!< implements the configurator retrieval function. + /*!< this returns true if the entry was present and stores it in + "found". */ + + virtual void sections(structures::string_array &list); + //!< retrieves the section names into "list". + + virtual bool section_exists(const basis::astring §ion); + //!< returns true if the "section" was found in the file. + /*!< NOTE: for an INI file, this is quite a heavy-weight call. */ + + virtual bool put(const basis::astring §ion, const basis::astring &entry, + const basis::astring &to_store); + //!< implements the configurator storage function. + + virtual bool delete_section(const basis::astring §ion); + //!< removes the entire "section" specified. + + virtual bool delete_entry(const basis::astring §ion, const basis::astring &entry); + //!< removes the entry specified by the "section" and "entry" name. + + virtual bool get_section(const basis::astring §ion, structures::string_table &info); + //!< reads the entire "section" into a table called "info". + /*!< on win95, this will fail if the section's data exceeds 32K. */ + + virtual bool put_section(const basis::astring §ion, const structures::string_table &info); + //!< writes a table called "info" into the "section" in the INI file. + /*!< any existing data for that section is wiped out. on win95, this will + fail if the section's data exceeds 32K. */ + + // dictates whether the stored entries will have spaces between '=' + // and the key name and value. only applicable on linux. + bool add_spaces() const { return _add_spaces; } + void add_spaces(bool add_them) { _add_spaces = add_them; } + +private: + filesystem::filename *_ini_name; //!< the file we're manipulating. +#ifdef __UNIX__ + ini_parser *_parser; //!< used for real storage and parsing. +#endif + file_location_default _where; //!< where to find and store the file. + bool _add_spaces; //!< tracks whether we're adding spaces around equals. + +#ifdef __WIN32__ + bool put_profile_string(const basis::astring §ion, const basis::astring &entry, + const basis::astring &to_store); + //!< encapsulates windows' ini storage method. + void get_profile_string(const basis::astring §ion, const basis::astring &entry, + const basis::astring &default_value, basis::flexichar *return_buffer, + int buffer_size); + //!< encapsulates windows' ini retrieval method. +#endif +#ifdef __UNIX__ + void read_ini_file(); + //!< reads the INI file's contents into memory. + void write_ini_file(); + //!< store the current contents into the INI file. +#endif + + // not to be called. + ini_configurator(const ini_configurator &); + ini_configurator &operator =(const ini_configurator &); + + static const basis::astring &ini_str_fake_default(); +}; + +} //namespace. + +#endif + diff --git a/core/library/configuration/ini_parser.cpp b/core/library/configuration/ini_parser.cpp new file mode 100644 index 00000000..bb881c80 --- /dev/null +++ b/core/library/configuration/ini_parser.cpp @@ -0,0 +1,237 @@ +/*****************************************************************************\ +* * +* Name : ini_parser * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "ini_parser.h" +#include "table_configurator.h" +#include "variable_tokenizer.h" + +#include +#include +#include +#include +#include +#include + +//#define DEBUG_INI_PARSER + // uncomment for noisy version. + +#undef LOG +#ifdef DEBUG_INI_PARSER + #define LOG(to_print) printf("%s\n", astring(to_print).s()) +#else + #define LOG(a) {} +#endif + +////////////// + +using namespace basis; +using namespace structures; +using namespace textual; +//using namespace ; + +namespace configuration { + +//algorithm: +// gather section until next section definition or end of file. +// parse the section with variable_tokenizer. +// eat that out of the string. +// repeat. + +ini_parser::ini_parser(const astring &to_parse, treatment_of_defaults behavior) +: table_configurator(behavior), + _well_formed(false), + _preface(new astring) +{ + reset(to_parse); +} + +ini_parser::~ini_parser() +{ + WHACK(_preface); +} + +void ini_parser::chow_through_eol(astring &to_chow) +{ + while (to_chow.length()) { + if (parser_bits::is_eol(to_chow[0])) { + // zap all carriage return type chars now that we found one. + while (to_chow.length() && parser_bits::is_eol(to_chow[0])) { + *_preface += to_chow[0]; + to_chow.zap(0, 0); + } + return; // mission accomplished. + } + *_preface += to_chow[0]; + to_chow.zap(0, 0); + } +} + +/* +//this is a super expensive operation... +// it would be better to have the parser be a bit more intelligent. +void strip_blank_lines(astring &to_strip) +{ + bool last_was_ret = false; + for (int i = 0; i < to_strip.length(); i++) { + if (parser_bits::is_eol(to_strip[i])) { + if (last_was_ret) { + // two in a row; now that's bogus. + to_strip.zap(i, i); + i--; // skip back. + continue; + } + last_was_ret = true; + to_strip[i] = '\n'; // make sure we know which type to look for. + } else { + if (last_was_ret && parser_bits::white_space(to_strip[i])) { + // well, the last was a return but this is white space. that's also + // quite bogus. + to_strip.zap(i, i); + i--; // skip back. + continue; + } + last_was_ret = false; + } + } +} +*/ + +void ini_parser::reset(const astring &to_parse) +{ + _well_formed = false; + table_configurator::reset(); // clean out existing contents. + _preface->reset(); // set the preface string back to nothing. + add(to_parse); +} + +void ini_parser::add(const astring &to_parse) +{ + astring parsing = to_parse; +// strip_blank_lines(parsing); + _preface->reset(); // set the preface string back to nothing. + while (parsing.length()) { + astring section_name; + bool found_sect = parse_section(parsing, section_name); + if (!found_sect) { + // the line is not a section name. toss it. + chow_through_eol(parsing); + continue; // try to find another section name. + } + // we got a section. yee hah. + int next_sect = 0; + for (next_sect = 0; next_sect < parsing.length(); next_sect++) { +// LOG(astring("[") + astring(parsing[next_sect], 1) + "]"); + if (parser_bits::is_eol(parsing[next_sect])) { + // we found the requisite return; let's see if a section beginning + // is just after it. we know nothing else should be, since we stripped + // out the blank lines and blanks after CRs. + if (parsing[next_sect + 1] == '[') { + // aha, found the bracket that should be a section start. + break; // done seeking next section beginning. + } + } + } + // skip back one if we hit the end of the string. + if (next_sect >= parsing.length()) next_sect--; + // now grab what should be all values within a section. + LOG(a_sprintf("bounds are %d to %d, string len is %d.", 0, next_sect, + parsing.length())); + astring sect_parsing = parsing.substring(0, next_sect); + LOG(astring("going to parse: >>") + sect_parsing + "<<"); + parsing.zap(0, next_sect); + variable_tokenizer section_reader("\n", "="); + section_reader.set_comment_chars(";#"); + section_reader.parse(sect_parsing); + LOG(astring("read: ") + section_reader.text_form()); + merge_section(section_name, section_reader.table()); + } + _well_formed = true; +} + +void ini_parser::merge_section(const astring §ion_name, + const string_table &to_merge) +{ + if (!section_exists(section_name)) { + // didn't exist yet, so just plunk it in. + put_section(section_name, to_merge); + return; + } + + // since the section exists, we just write the individual entries from the + // new section. they'll stamp out any old values. + for (int i = 0; i < to_merge.symbols(); i++) + put(section_name, to_merge.name(i), to_merge[i]); +} + +bool ini_parser::parse_section(astring &to_parse, astring §ion_name) +{ + section_name = ""; // reset the section. + + // we have a simple state machine here... + enum states { + SEEKING_OPENING_BRACKET, // looking for the first bracket. + EATING_SECTION_NAME // got a bracket, now getting section name. + }; + states state = SEEKING_OPENING_BRACKET; + + // zip through the string trying to find a valid section name. + for (int i = 0; i < to_parse.length(); i++) { + char curr = to_parse[i]; + LOG(astring("<") + astring(curr, 1) + ">"); + switch (state) { + case SEEKING_OPENING_BRACKET: + // we're looking for the first bracket now... + if (parser_bits::white_space(curr)) continue; // ignore white space. + if (curr != '[') return false; // argh, bad characters before bracket. + state = EATING_SECTION_NAME; // found the bracket. + break; + case EATING_SECTION_NAME: + // we're adding to the section name now... + if (curr == ']') { + // that's the end of the section name. + to_parse.zap(0, i); // remove what we saw. +//should we take out to end of line also? +//eventually up to eol could be kept as a comment? + return true; + } + section_name += curr; // add a character to the name. + break; + default: + //LOG("got to unknown case in section parser!"); + return false; + } + } + // if we got to here, the section was badly formed... the whole string was + // parsed through but no conclusion was reached. + return false; +} + +bool ini_parser::restate(astring &new_ini, bool add_spaces) +{ + new_ini = *_preface; // give it the initial text back again. + string_array sects; + sections(sects); + for (int i = 0; i < sects.length(); i++) { + new_ini += astring("[") + sects[i] + "]" + parser_bits::platform_eol_to_chars(); + string_table tab; + if (!get_section(sects[i], tab)) continue; // serious error. + tab.add_spaces(add_spaces); + new_ini += tab.text_form(); + } + return true; +} + +} //namespace. + + diff --git a/core/library/configuration/ini_parser.h b/core/library/configuration/ini_parser.h new file mode 100644 index 00000000..fb061dab --- /dev/null +++ b/core/library/configuration/ini_parser.h @@ -0,0 +1,124 @@ +#ifndef INI_PARSER_CLASS +#define INI_PARSER_CLASS + +/*****************************************************************************\ +* * +* Name : ini_parser * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "table_configurator.h" + +namespace configuration { + +//! Parses strings in the fairly well-known INI file format. +/*! + Description of INI file format: + + The format expected in this parser for initialization files allows for + three types of entries. These are section headers, comments and value + definitions. + Section headers define the start of a list of value definitions. A + section header is a name in brackets, like [startup]. + A comment is a string of text that will be ignored. Comments are preceded + by an octothorpe ('#') or a semicolon (';'). The parser will keep comments + roughly in the same places they were found in the string that was parsed. + A comment is allowed to follow a section header on the same line. + Value definitions are a pair of strings separated by an equality operator + ('='). The left side of the value definition is referred to here as the + variable's name while the right side is referred to as the variable's value. + Note that any line which is not a comment or a section header is considered + implicitly to be a value definition, even if it does not contain an equals + operator. This is required for parsing certain INI files that don't follow + the established format. Such lines will have an empty string for their + value. + White space (either tab characters or space characters) are allowed before + and after any of these constructs. Spaces may also exist before and after + the equals operator of a value definition, but once the value starts, any + white space is considered part of the value. Trailing white space is not + considered part of a variable name, but white space between the characters + before the equals operator is signficant. Any number of carriage returns + can separate the lines of the INI file. + + Here is an example of a valid INI file: + @code + # Initialization file for Frootnik Corp. + + [common] ; all of our programs use these. + magnification=1 + + text_color=puce + font=atavata + ;;; see font/color document in readme.txt. + + [sourceburger] ; sourceburger application specific settings + + crashnow = 0 + crashlater = 1 + get clue=0 + windowtitle = Source Burger 5000 + + danger will robinson + @endcode +*/ + +class ini_parser : public table_configurator +{ +public: + ini_parser(const basis::astring &to_parse, + treatment_of_defaults behavior = RETURN_ONLY); + //!< constructs an ini_parser by parsing entries out of "to_parse". + /*!< after construction, the success of parsing can be checked using + well_formed(). */ + + ~ini_parser(); + + void reset(const basis::astring &to_parse); + //!< drops any existing information and processes the string "to_parse". + + void add(const basis::astring &to_parse); + //!< merges items parsed from "to_parse" into the current set. + /*!< processes the string "to_parse" as in the reset() method but adds + any new sections found to our configuration. if sections are found with + the same names, then the values found in "to_parse" override the ones + already listed. */ + + bool well_formed() const { return _well_formed; } + //!< returns true if the ini file's contents were in the format expected. + + bool restate(basis::astring &new_ini, bool add_spaces = false); + //!< stores a cleaned version of the internal state into "new_ini". + /*!< if "add_spaces" is true, then the entries will be in the form of + 'x = y' rather than 'x=y'. */ + + void merge_section(const basis::astring §ion_name, const structures::string_table &to_merge); + //!< merges the table "to_merge" into the "section_name". + /*!< any new values from "to_merge" that are not found in the section with + "section_name" in "this" object are added and any existing values will be + replaced. */ + +private: + bool _well_formed; //!< true if the ini file had a valid format. + basis::astring *_preface; //!< information that comes before the first section. + + void chow_through_eol(basis::astring &to_chow); + //!< eats up to an EOL character but adds the text to our preface string. + + bool parse_section(basis::astring &to_parse, basis::astring §ion_name); + //!< looks for a section name in the string "to_parse". + /*!< true is returned on success; success means that a "section_name" was + found and that "to_parse" has been destructively eaten to remove it. */ +}; + +} //namespace. + +#endif + diff --git a/core/library/configuration/ini_roller.cpp b/core/library/configuration/ini_roller.cpp new file mode 100644 index 00000000..36110081 --- /dev/null +++ b/core/library/configuration/ini_roller.cpp @@ -0,0 +1,110 @@ +/*****************************************************************************\ +* * +* Name : ini_roller * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "ini_roller.h" + +#include +#include +#include +///#include + +using namespace basis; +using namespace structures; + +namespace configuration { + +//#define DEBUG_ID_GRANTING + // uncomment if you want verbose granting of unique ids. + +const int ID_FACTOR = 28; + // this many ids are grabbed at once for eventual issuance. + +ini_roller::ini_roller(configurator &config, const astring §ion, + const astring &entry, int min, int max) +: _ini(config), + _ids(new int_roller(min, max)), + _section(new astring(section)), + _entry(new astring(entry)), + _lock(new mutex) +{ + int current = _ini.load(section, entry, min); + _ids->set_current(current); + // make our first requisition of ids. we start here rather than playing + // games with the next_id function. + _ini.store(section, entry, _ids->current() + ID_FACTOR); +} + +ini_roller::~ini_roller() +{ + // force the id to be past what we've allocated, but not too far past. + _ini.store(*_section, *_entry, _ids->current() + 1); + WHACK(_ids); + WHACK(_section); + WHACK(_entry); + WHACK(_lock); +} + +int ini_roller::current_id() const +{ + auto_synchronizer l(*_lock); + return _ids->current(); +} + +int ini_roller::next_id() +{ +#ifdef DEBUG_ID_GRANTING + FUNCDEF("next_id"); +#endif + auto_synchronizer l(*_lock); + int to_return = _ids->current(); + + // this uses a relaxed id issuance policy; the id that's in the INI + // file is only updated when we run out of the range that we allocate for it. + // the roller's current value is used whenever issuing an id, but next_id() + // is always called before that id is actually issued. + + if ( (_ids->current() < _ids->maximum() - 2) + && (_ids->current() % ID_FACTOR) ) { + // no id range grabbing needed yet and no rollover. + _ids->next_id(); +#ifdef DEBUG_ID_GRANTING + LOG(astring(astring::SPRINTF, "standard id issue: %d.", to_return)); +#endif + return to_return; + } + + // now we need to allocate a new range of ids... and store in ini. + int new_range = to_return + ID_FACTOR; +#ifdef DEBUG_ID_GRANTING + LOG(astring(astring::SPRINTF, "finding next range, new start in ini " + "is: %d.", new_range)); +#endif + // if the id wraps around, reset it. + if ( (new_range < 0) || (new_range >= _ids->maximum()) ) + new_range = ID_FACTOR; +#ifdef DEBUG_ID_GRANTING + LOG(astring(astring::SPRINTF, "after check, new ini id is: %d.", + new_range)); +#endif + _ini.store(*_section, *_entry, new_range); + // set the next stored id to the block above where we're using. + _ids->next_id(); // jump to the next one in the range. +#ifdef DEBUG_ID_GRANTING + LOG(astring(astring::SPRINTF, "after store, id is: %d.", to_return)); +#endif + return to_return; +} + +} //namespace. + diff --git a/core/library/configuration/ini_roller.h b/core/library/configuration/ini_roller.h new file mode 100644 index 00000000..f41469ea --- /dev/null +++ b/core/library/configuration/ini_roller.h @@ -0,0 +1,73 @@ +#ifndef INI_ROLLER_CLASS +#define INI_ROLLER_CLASS + +/*****************************************************************************\ +* * +* Name : ini_roller * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "configurator.h" + +#include +#include +#include + +namespace configuration { + +//! Implements an id generator that interacts with a configuration file. +/*! + This provides an int_roller (which provides rolling ids given a range to + issue them from) that is stored in a configurator. The instantiated + object is the real source of the ids, but the configurator file is + periodically updated to reflect the current id state. If a program + is restarted later, then it will start using ids that it had not already + issued in its last run, as long as the configurator is a persistent object. + Note that the range of ids had better be quite large; otherwise the program + could still have live entries under an id that is about to be reissued + due to wrap-around. +*/ + +class ini_roller : public virtual basis::root_object +{ +public: + ini_roller(configurator &config, const basis::astring §ion, + const basis::astring &entry, int min, int max); + //!< creates a roller that updates "config" with the current id value. + /*!< the updates are not continuous, but are done periodically to avoid + constantly writing to the "config". the "section" and "entry" dictate + where the entry is saved in the "config". the "min" and "max" provide the + range of the id, where it will start at "min", increment by one until it + reaches at most "max", and then it will start back at "min" again. note + that "config" must exist for the duration of the ini_roller. */ + + virtual ~ini_roller(); + + DEFINE_CLASS_NAME("ini_roller"); + + int next_id(); + //!< returns the next number to be issued. + + int current_id() const; + //!< returns the current id; this is the one that was last issued. + +private: + configurator &_ini; //!< provides access to the ini file. + structures::int_roller *_ids; //!< the current id number is managed here. + basis::astring *_section; //!< remembers the right section for our id entry. + basis::astring *_entry; //!< remembers the entry name. + basis::mutex *_lock; //!< keeps us thread safe. +}; + +} //namespace. + +#endif + diff --git a/core/library/configuration/makefile b/core/library/configuration/makefile new file mode 100644 index 00000000..1ac89e79 --- /dev/null +++ b/core/library/configuration/makefile @@ -0,0 +1,11 @@ +include cpp/variables.def + +PROJECT = configuration +TYPE = library +SOURCE = application_configuration.cpp config_watcher.cpp configurator.cpp configlet.cpp \ + configuration_list.cpp ini_configurator.cpp ini_parser.cpp ini_roller.cpp \ + section_manager.cpp system_values.cpp table_configurator.cpp variable_tokenizer.cpp +TARGETS = configuration.lib + +include cpp/rules.def + diff --git a/core/library/configuration/section_manager.cpp b/core/library/configuration/section_manager.cpp new file mode 100644 index 00000000..eabf5d76 --- /dev/null +++ b/core/library/configuration/section_manager.cpp @@ -0,0 +1,113 @@ + + + +/*****************************************************************************\ +* * +* Name : section_manager * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "section_manager.h" + +#include +#include +#include +#include + +using namespace basis; +using namespace configuration; +using namespace structures; + +namespace configuration { + +section_manager::section_manager(configurator &config, + const astring &toc_title, const astring &header_prefix) +: _config(config), + _toc_title(new astring(toc_title)), + _section_prefix(new astring(header_prefix)) +{ +} + +section_manager::~section_manager() +{ + WHACK(_toc_title); + WHACK(_section_prefix); +} + +bool section_manager::get_toc(string_table &toc) +{ return _config.get_section(*_toc_title, toc); } + +bool section_manager::get_section_names(string_array §ions) +{ + sections.reset(); // clean up the array they gave us. + string_table toc; + if (!get_toc(toc)) return false; + for (int i = 0; i < toc.symbols(); i++) sections += toc.name(i); + return true; +} + +bool section_manager::section_exists(const astring §ion_name) +{ + if (!section_name) return false; // empty names are invalid. + return _config.load(*_toc_title, section_name, "").t(); +} + +astring section_manager::make_section_heading(const astring §ion) +{ return *_section_prefix + section; } + +bool section_manager::add_section(const astring §ion_name, + const string_table &to_add) +{ + if (!section_name) return false; // empty names are invalid. + if (section_exists(section_name)) return false; + // write the name into the table of contents. + astring section_value = "t"; // place-holder for section entries. + if (!to_add.symbols()) + section_value = ""; // nothing to write; delete the section entry. + if (!_config.put(*_toc_title, section_name, section_value)) + return false; + // create the appropriate header for the new section. + astring header = make_section_heading(section_name); + // if there aren't any elements, the put_section should just wipe out + // the entire section. + return _config.put_section(header, to_add); +} + +bool section_manager::replace_section(const astring §ion_name, + const string_table &replacement) +{ + if (!section_name) return false; // empty names are invalid. + if (!section_exists(section_name)) return false; + if (!zap_section(section_name)) return false; + if (!replacement.symbols()) return true; // nothing to write. + return add_section(section_name, replacement); +} + +bool section_manager::zap_section(const astring §ion_name) +{ + if (!section_name) return false; // empty names are invalid. + astring header = make_section_heading(section_name); + if (!_config.delete_section(header)) return false; + return _config.delete_entry(*_toc_title, section_name); +} + +bool section_manager::find_section(const astring §ion_name, + string_table &found) +{ + if (!section_name) return false; // empty names are invalid. + if (!section_exists(section_name)) return false; + astring header = make_section_heading(section_name); + return _config.get_section(header, found); +} + +} //namespace. + + diff --git a/core/library/configuration/section_manager.h b/core/library/configuration/section_manager.h new file mode 100644 index 00000000..dea180c4 --- /dev/null +++ b/core/library/configuration/section_manager.h @@ -0,0 +1,111 @@ +#ifndef SECTION_MANAGER_CLASS +#define SECTION_MANAGER_CLASS + +/*****************************************************************************\ +* * +* Name : section_manager * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "configurator.h" + +#include + +namespace configuration { + +//! Tracks a collection of related configurations in a configurator. +/*! + If there is a set of items that need to be stored in a configurator, where + each item has its own configuration section, then this object can help out. + It manages a collection of uniquely named sections in a configurator object + and provides a table of contents (TOC) feature for the names of the sections. + Each item lives in its own distinct section but the whole set can be + operated on as one entity. +*/ + +class section_manager : public virtual basis::nameable +{ +public: + section_manager(configurator &config, const basis::astring &toc_title, + const basis::astring &header_prefix); + //!< creates a section_manager that uses the "config" for storage. + /*!< the "toc_title" is the name of the section to be used for storing the + table of contents for the managed configuration items. the "header_prefix" + will be prepended to the names of each section to facilitate locating them. + for example, if the "toc_title" is "client channels" and the "header_prefix" + is "CliChan__", then the resulting configuration might look similar to the + following: @code + [client channels] + joe + ted + [CliChan__joe] + port=58 + [CliChan__ted] + address=13.8.92.4 + auth=primary + @endcode */ + + ~section_manager(); + + DEFINE_CLASS_NAME("section_manager"); + + bool section_exists(const basis::astring §ion_name); + //!< returns true if the section called "section_name" exists in the config. + + bool get_section_names(structures::string_array §ions); + //!< loads the "sections" array with all section names. + /*!< this comes from our table of contents. true is returned if there + were any names to load. */ + + bool add_section(const basis::astring §ion_name, const structures::string_table &to_add); + //!< stores a new section for "section_name" using the table "to_add". + /*!< this will fail if the section already exists. */ + + bool replace_section(const basis::astring §ion, const structures::string_table &replacement); + //!< replaces the contents of "section" with the "replacement" table. + /*!< this will fail if the section does not already exist. */ + + bool zap_section(const basis::astring §ion_name); + //!< removes the data for "section_name" from both the config and TOC. + /*!< this will fail if the section is not present. */ + + bool find_section(const basis::astring §ion_name, structures::string_table &found); + //!< loads the data from "section_name" into the table "found". + /*!< this fails if the section doesn't exist or if the section's contents + couldn't be detokenized into a table of name/value pairs. */ + + configurator &config() { return _config; } + //!< allows access to the configurator we operate on. + /*!< getting single items from the config will be signficantly faster + using it directly; the make_section_heading() method can be used to locate + the proper section. */ + + bool get_toc(structures::string_table &toc); + //!< reads the table of contents into "toc". + + basis::astring make_section_heading(const basis::astring §ion); + //!< provides the appropriate heading string for the "section" name. + /*!< this can be used to find entries using the config(). */ + +private: + configurator &_config; //!< the configuration object we interact with. + basis::astring *_toc_title; //!< the table of contents' section name. + basis::astring *_section_prefix; //!< prefix attached to the name of the section. + + section_manager(const section_manager &); //!< currently forbidden. + section_manager &operator =(const section_manager &); + //!< currently forbidden. +}; + +} //namespace. + +#endif + diff --git a/core/library/configuration/system_values.cpp b/core/library/configuration/system_values.cpp new file mode 100644 index 00000000..c82b515e --- /dev/null +++ b/core/library/configuration/system_values.cpp @@ -0,0 +1,207 @@ +/*****************************************************************************\ +* * +* Name : system_values * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "ini_configurator.h" +#include "system_values.h" + +#include +#include +#include +#include +#include + +using namespace algorithms; +using namespace basis; +using namespace structures; +using namespace textual; + +namespace configuration { + +const int MAX_VALUE_BITS = 8; // we provide 2^n slots in hash. + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +////////////// + +class value_record +{ +public: + astring _name; // the name of the value. + astring _descrip; // the description of the value. + astring _location; // the file defining the value. + + value_record(const astring &name = astring::empty_string(), + const astring &description = astring::empty_string(), + const astring &location = astring::empty_string()) + : _name(name), _descrip(description), _location(location) {} +}; + +////////////// + +class system_values_lookup_list : public int_hash +{ +public: + system_values_lookup_list() : int_hash(MAX_VALUE_BITS) {} + + // finds the symbolic "name" in the table, which is not as efficient as + // lookin up integers. + value_record *text_find(const astring &name, int &value) { + // scoot across all of the ids. + const int_set &cids = ids(); + for (int i = 0; i < cids.elements(); i++) { + int current_id = cids[i]; + value_record *curr = find(current_id); + if (!curr) { +//serious error. + continue; + } + if (curr->_name == name) { + // this is a match to the name they were seeking. + value = current_id; + return curr; + } + } + return NIL; + } +}; + +////////////// + +system_values::system_values(const astring §ion_tag) +: _tag(new astring(section_tag)), + _list(new system_values_lookup_list), + _file(new astring(DEFAULT_MANIFEST)) +{ +// FUNCDEF("constructor"); + open_values(); +} + +system_values::~system_values() +{ + WHACK(_list); + WHACK(_tag); + WHACK(_file); +} + +const char *system_values::DEFAULT_MANIFEST = "manifest.txt"; + // this is the default manifest and it is expected to live right in + // the folder where the applications are. + +bool system_values::use_other_manifest(const astring &manifest_file) +{ + *_file = manifest_file; + return open_values(); +} + +const char *system_values::OUTCOME_VALUES() { return "DEFINE_OUTCOME"; } + +const char *system_values::FILTER_VALUES() { return "DEFINE_FILTER"; } + +const char *system_values::EVENT_VALUES() { return "DEFINE_EVENT"; } + +bool system_values::open_values() +{ +// FUNCDEF("open_values"); + ini_configurator ini(*_file, ini_configurator::RETURN_ONLY, + ini_configurator::APPLICATION_DIRECTORY); + + string_table full_section; + bool got_section = ini.get_section(*_tag, full_section); + if (!got_section) return false; // nothing there to look up. + for (int i = 0; i < full_section.symbols(); i++) { + + string_array items; + list_parsing::parse_csv_line(full_section.name(i), items); + if (items.length() < 4) { + continue; + } + + value_record *entry = new value_record(items[0], items[2], items[3]); + int value = items[1].convert(0); + _list->add(value, entry); + } + + return true; +} + +#define SV_EOL parser_bits::platform_eol_to_chars() + +//hmmm: it might be nice to have an alternate version sorted by name... + +astring system_values::text_form() const +{ + int_set cids = _list->ids(); + + if (!_tag->equal_to("DEFINE_OUTCOME")) { + // sort the list in identifier order. + shell_sort(cids.access(), cids.elements()); + } else { + // sort the list in reverse identifier order, since zero is first + // for outcomes and then they go negative. + shell_sort(cids.access(), cids.elements(), true); + } + + astring to_return("values for "); + to_return += *_tag; + to_return += SV_EOL; + for (int i = 0; i < cids.elements(); i++) { + int current_id = cids[i]; + value_record *curr = _list->find(current_id); + if (!curr) { +//serious error. + continue; + } + to_return += a_sprintf("%d: ", current_id); + to_return += curr->_name + " \"" + curr->_descrip + "\" from " + + curr->_location; + to_return += SV_EOL; + } + return to_return; +} + +bool system_values::lookup(int value, astring &symbolic_name, + astring &description, astring &file_location) +{ + value_record *found = _list->find(value); + if (!found) return false; + symbolic_name = found->_name; + description = found->_descrip; + file_location = found->_location; + return true; +} + +bool system_values::lookup(const astring &symbolic_name, int &value, + astring &description, astring &file_location) +{ + value_record *found = _list->text_find(symbolic_name, value); + if (!found) return false; + description = found->_descrip; + file_location = found->_location; + return true; +} + +int system_values::elements() const { return _list->ids().elements(); } + +bool system_values::get(int index, astring &symbolic_name, int &value, + astring &description, astring &file_location) +{ + bounds_return(index, 0, _list->ids().elements() - 1, false); // bad index. + value = _list->ids()[index]; + return lookup(value, symbolic_name, description, file_location); +} + +} //namespace. + + diff --git a/core/library/configuration/system_values.h b/core/library/configuration/system_values.h new file mode 100644 index 00000000..02c11b7f --- /dev/null +++ b/core/library/configuration/system_values.h @@ -0,0 +1,117 @@ +#ifndef SYSTEM_VALUES_CLASS +#define SYSTEM_VALUES_CLASS + +/*****************************************************************************\ +* * +* Name : system_values * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +//#include + +namespace configuration { + +// forward. +class system_values_lookup_list; + +//! This class provides a way to look up generated values used in the code base. +/*! + The type of value here includes outcomes, events and filters so far. These + items are reported in the build manifest that is included with every release + of the compiled software. The system_values class provides a lookup + interface to the extensible system of unique identifiers which is mapped by + the manifest file. The manifest file is processed like an initialization + file to retrieve the descriptions and names of symbols when given their + numerical identifiers. +*/ + +class system_values : public virtual basis::root_object +{ +public: + system_values(const basis::astring §ion_tag); + //!< provides a lookup on values found in the section named "section_tag". + /*!< the "section_tag" indicates what kind of asset is being looked up in + the manifest. it is always assumed that the manifest file is the main + one defined here in DEFAULT_MANIFEST. it must be a file created by + the value_tagger during the indexing of all value assets defined in the + build. + the valid values for the section names so far are "DEFINE_ OUTCOME", + "DEFINE_ FILTER" and "DEFINE_ EVENT" (with no spaces), but it is better + to use the defined "VALUES" methods below (such as OUTCOME_VALUES()). + outcomes are used by functions to describe how the operation completed. + filters are used to enable different types of logging. events are sent + between information source and sink to indicate the occurrence of an + activity. */ + + virtual ~system_values(); + + DEFINE_CLASS_NAME("system_values"); + + // these provide symbolic versions of the section tag used in the + // constructor. these are preferable to using the string constants + // directly. + static const char *OUTCOME_VALUES(); + //!< values that define the outcomes of operations. + static const char *FILTER_VALUES(); + //!< values that define filters used in logging. + static const char *EVENT_VALUES(); + //!< values that define event objects used in the program. + + static const char *DEFAULT_MANIFEST; + //!< the default manifest file. + /*!< it is expected to live in the folder where the applications are. */ + + bool use_other_manifest(const basis::astring &manifest_file); + //!< supports using a different manifest file than the default. + /*!< the values will now come from the "manifest_file" instead of the + default manifest name shipped with the software release. */ + + virtual basis::astring text_form() const; + //!< shows all items in the table. + + bool lookup(int value, basis::astring &symbolic_name, basis::astring &description, + basis::astring &file_location); + //!< locates a "value" and finds its name, description and location. + /*!< this looks up one of the enumerated values defined for our type of + value. true is returned if the value is meaningful. the "symbolic_name" + is set to the item's name as used inside the source code (i.e., its enum + member's name), the "description" is set to the full textual description + of what that value means, and the "file_location" is set to the name of + the source file that defines the value. */ + + bool lookup(const basis::astring &symbolic_name, int &value, basis::astring &description, + basis::astring &file_location); + //!< similar to the above lookup, but seeks on the "symbolic_name". + /*!< this lookup tries to associate from the textual name of the value + in order to find the integer actual "value" of it. the textual + "description" and "file_location" for that item are also included. */ + + int elements() const; + //!< returns how many items are listed for the types of values specified. + + bool get(int index, basis::astring &symbolic_name, int &value, + basis::astring &description, basis::astring &file_location); + //!< accesses the "index"th item in the list. + +private: + basis::astring *_tag; //!< the name of our section. + system_values_lookup_list *_list; //!< the values and text that we found. + basis::astring *_file; //!< the manifest filename. + + bool open_values(); //!< retrieves the information from the file. +}; + +} //namespace. + +#endif + diff --git a/core/library/configuration/table_configurator.cpp b/core/library/configuration/table_configurator.cpp new file mode 100644 index 00000000..692ed0d0 --- /dev/null +++ b/core/library/configuration/table_configurator.cpp @@ -0,0 +1,197 @@ +/*****************************************************************************\ +* * +* Name : table_configurator * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "table_configurator.h" + +#include +#include +#include +#include + +using namespace basis; +using namespace structures; + +namespace configuration { + +#undef LOG +#define LOG(to_print) printf("%s::%s: %s\n", static_class_name(), func, astring(to_print).s()) + +class table_o_string_tables : public symbol_table +{ +public: +}; + +////////////// + +table_configurator::table_configurator(treatment_of_defaults behavior) +: configurator(behavior), + _real_table(new table_o_string_tables) +{} + +table_configurator::table_configurator(const table_configurator &to_copy) +: configurator(to_copy.behavior()), + _real_table(new table_o_string_tables) +{ *this = to_copy; } + +table_configurator::~table_configurator() +{ + WHACK(_real_table); +} + +table_configurator &table_configurator::operator = + (const table_configurator &to_copy) +{ + if (this == &to_copy) return *this; + reset(); + string_array sects; + const_cast(to_copy).sections(sects); + for (int sectindy = 0; sectindy < sects.length(); sectindy++) { + // every entry in the current section gets added to our current config. + astring curr_section = sects[sectindy]; + string_table entries; + const_cast(to_copy).get_section(curr_section, entries); + put_section(curr_section, entries); + } + + return *this; +} + +void table_configurator::reset() { _real_table->reset(); } + +bool table_configurator::section_exists(const astring §ion) +{ return !!_real_table->find(section); } + +void table_configurator::sections(string_array &to_fill) +{ + to_fill.reset(); + for (int i = 0; i < _real_table->symbols(); i++) + to_fill += _real_table->name(i); +} + +bool table_configurator::delete_section(const astring §ion) +{ return _real_table->whack(section) == common::OKAY; } + +bool table_configurator::delete_entry(const astring §ion, + const astring &ent) +{ + string_table *sect = _real_table->find(section); + if (!sect) return false; + return sect->whack(ent) == common::OKAY; +} + +bool table_configurator::put(const astring §ion, + const astring &entry, const astring &to_store) +{ + if (!to_store.length()) return delete_entry(section, entry); + else if (!entry.length()) return delete_section(section); + string_table *sect = _real_table->find(section); + if (!sect) { + // none exists yet, so add one. + _real_table->add(section, string_table()); + sect = _real_table->find(section); + } + sect->add(entry, to_store); + return true; +} + +bool table_configurator::get(const astring §ion, + const astring &entry, astring &found) +{ + found = ""; + string_table *sect = _real_table->find(section); + if (!sect) return false; + astring *looked = sect->find(entry); + if (!looked) return false; + found = *looked; + return true; +} + +/* +scavenge? +bool is_comment(char to_check, const char *comment_list) +{ + int len = int(strlen(comment_list)); + for (int i = 0; i < len; i++) { + if (to_check == comment_list[i]) + return true; + } + return false; +} +*/ + +/* scavenge? +//hmmm: could we move the commented and clean_comments methods into +// parser bits? +// yes! we should move those; they are no longer used here! + +bool table_configurator::commented(const astring &to_check, + const char *comment_list) +{ + for (int i = 0; i < to_check.length(); i++) { + if (white_space(to_check[i])) + continue; // skip spaces. + if (is_comment(to_check[i], comment_list)) + return true; // started with a comment. + return false; // we had our chance for a comment, but that wasn't it. + } + return false; +} + +astring table_configurator::clean_comments(const astring &to_clean, + const char *comment_list) +{ + FUNCDEF("clean_comments"); +//LOG(astring("clean in with: ") + to_clean); + astring to_return(' ', to_clean.length()); // make a long enough string. + to_return.reset(); // keep allocated buffer size, but throw out contents. + for (int i = 0; i < to_clean.length(); i++) { + if (is_comment(to_clean[i], comment_list)) { + // here we go; the rest is commented out. + break; + } + to_return += to_clean[i]; + } +//LOG(astring("clean out with: ") + to_return); + return to_return; +} +*/ + +bool table_configurator::get_section(const astring §ion, + string_table &info) +{ +/// FUNCDEF("get_section"); + info.reset(); + string_table *sect = _real_table->find(section); + if (!sect) return false; + for (int i = 0; i < sect->symbols(); i++) + info.add(sect->name(i), (*sect)[i]); + return true; +} + +bool table_configurator::put_section(const astring §ion, + const string_table &info) +{ +/// FUNCDEF("put_section"); + string_table *sect = _real_table->find(section); + if (!sect) { + // none exists yet, so add one. + _real_table->add(section, string_table()); + sect = _real_table->find(section); + } + *sect = info; + return true; +} + +} //namespace. + diff --git a/core/library/configuration/table_configurator.h b/core/library/configuration/table_configurator.h new file mode 100644 index 00000000..4beaee00 --- /dev/null +++ b/core/library/configuration/table_configurator.h @@ -0,0 +1,82 @@ +#ifndef TABLE_CONFIGURATOR_CLASS +#define TABLE_CONFIGURATOR_CLASS + +/*****************************************************************************\ +* * +* Name : table_configurator * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "configurator.h" + +#include + +namespace configuration { + +// forward. +class table_o_string_tables; + +//! Supports the configurator interface using a collection of string tables. + +class table_configurator : public virtual configurator +{ +public: + table_configurator(treatment_of_defaults behavior = AUTO_STORE); + //!< Constructor just needs to know what to do for missing items. + /*!< Creates a table_configurator that loads and stores entries into + the internal collection of tables. It will use the "behavior" regarding + missing entries when load() is invoked. */ + + table_configurator(const table_configurator &to_copy); + + virtual ~table_configurator(); + + table_configurator &operator =(const table_configurator &to_copy); + + DEFINE_CLASS_NAME("table_configurator"); + + virtual void sections(structures::string_array &list); + //!< retrieves the section names into "list". + + void reset(); // clears out all contents. + + virtual bool get(const basis::astring §ion, const basis::astring &entry, + basis::astring &found); + //!< implements the configurator retrieval function. + + virtual bool put(const basis::astring §ion, const basis::astring &entry, + const basis::astring &to_store); + //!< implements the configurator storage function. + + virtual bool section_exists(const basis::astring §ion); + //!< true if the "section" is presently in the table config. + + virtual bool delete_section(const basis::astring §ion); + //!< removes the entire "section" specified. + + virtual bool delete_entry(const basis::astring §ion, const basis::astring &entry); + //!< removes the entry specified by the "section" and "entry" name. + + virtual bool get_section(const basis::astring §ion, structures::string_table &info); + //!< reads the entire table held under "section" into a table called "info". + + virtual bool put_section(const basis::astring §ion, const structures::string_table &info); + //!< writes a table called "info" into the "section" held here. + +private: + table_o_string_tables *_real_table; + //!< the data structure we're actually operating on. +}; + +} //namespace. + +#endif + diff --git a/core/library/configuration/variable_tokenizer.cpp b/core/library/configuration/variable_tokenizer.cpp new file mode 100644 index 00000000..7a1d0fd5 --- /dev/null +++ b/core/library/configuration/variable_tokenizer.cpp @@ -0,0 +1,403 @@ +// Name : variable_tokenizer +// Author : Chris Koeritz +/* +* Copyright (c) 1997-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include "variable_tokenizer.h" + +#include +#include +#include +#include +#include +#include + +//#define DEBUG_VARIABLE_TOKENIZER + // uncomment for noisier run. + +const char *SPECIAL_VALUE = " "; + // special value stored for entries with assignment operators but no + // value contents. + +#undef LOG +#ifdef DEBUG_VARIABLE_TOKENIZER + #include + #define LOG(to_print) printf("%s\n", astring(to_print).s()); +#else + #define LOG(to_print) +#endif + +using namespace basis; +using namespace structures; +using namespace textual; + +namespace configuration { + +variable_tokenizer::variable_tokenizer(int max_bits) +: _implementation(new string_table(max_bits)), + _assignments(new astring("=")), + _separators(new astring(",")), + _quotes(new astring), + _nesting(false), + _comments(new astring), + _comment_number(1), + _add_spaces(false) +{} + +variable_tokenizer::variable_tokenizer(const astring &separator, const astring &assignment, + int max_bits) +: _implementation(new string_table(max_bits)), + _assignments(new astring(assignment)), + _separators(new astring(separator)), + _quotes(new astring), + _nesting(false), + _comments(new astring), + _comment_number(1), + _add_spaces(false) +{} + +variable_tokenizer::variable_tokenizer(const astring &separator, const astring &assignment, + const astring "es, bool nesting, int max_bits) +: _implementation(new string_table(max_bits)), + _assignments(new astring(assignment)), + _separators(new astring(separator)), + _quotes(new astring(quotes)), + _nesting(nesting), + _comments(new astring), + _comment_number(1), + _add_spaces(false) +{} + +variable_tokenizer::variable_tokenizer(const variable_tokenizer &to_copy) +: _implementation(new string_table), + _assignments(new astring), + _separators(new astring), + _quotes(new astring), + _nesting(false), + _comments(new astring), + _comment_number(1), + _add_spaces(false) +{ *this = to_copy; } + +variable_tokenizer::~variable_tokenizer() +{ + WHACK(_separators); + WHACK(_assignments); + WHACK(_implementation); + WHACK(_quotes); + WHACK(_comments); +} + +int variable_tokenizer::symbols() const { return _implementation->symbols(); } + +void variable_tokenizer::set_comment_chars(const astring &comments) +{ *_comments = comments; } + +const astring &variable_tokenizer::assignments() const { return *_assignments; } + +const astring &variable_tokenizer::separators() const { return *_separators; } + +const astring &variable_tokenizer::quotes() const { return *_quotes; } + +bool variable_tokenizer::exists(const astring &name) const +{ return !!_implementation->find(name); } + +void variable_tokenizer::reset() { _implementation->reset(); } + +const string_table &variable_tokenizer::table() const { return *_implementation; } + +string_table &variable_tokenizer::table() { return *_implementation; } + +variable_tokenizer &variable_tokenizer::operator =(const variable_tokenizer &to_copy) +{ + if (this == &to_copy) return *this; + *_implementation = *to_copy._implementation; + *_separators = *to_copy._separators; + *_assignments = *to_copy._assignments; + *_quotes = *to_copy._quotes; + _nesting = to_copy._nesting; + _add_spaces = to_copy._add_spaces; + return *this; +} + +astring variable_tokenizer::find(const astring &name) const +{ + astring *found = _implementation->find(name); + if (!found) return ""; + + // check that the contents are not just our significator of emptiness. + if (found->equal_to(SPECIAL_VALUE)) return ""; + return *found; +} + +bool variable_tokenizer::okay_for_variable_name(char to_check) const +{ + if (!to_check || separator(to_check) || assignment(to_check)) return false; + return true; +} + +bool variable_tokenizer::separator(char to_check) const +{ + // special case allows a CR separator to be either flavor. + if (parser_bits::is_eol(to_check) + && (astring::matches(*_separators, '\n') + || astring::matches(*_separators, '\r')) ) return true; + return astring::matches(*_separators, to_check); +} + +bool variable_tokenizer::assignment(char to_check) const +{ return astring::matches(*_assignments, to_check); } + +bool variable_tokenizer::quote_mark(char to_check) const +{ return astring::matches(*_quotes, to_check); } + +bool variable_tokenizer::comment_char(char to_check) const +{ return astring::matches(*_comments, to_check); } + +#define COOL to_tokenize.length() + // true if the string should continue to be parsed. + +// sets "current" to the first character in the string. +#define CHOP { \ + current = to_tokenize[0]; \ + to_tokenize.zap(0, 0); \ +} + +bool variable_tokenizer::parse(const astring &to_tokenize_in) +{ + FUNCDEF("parse"); + astring to_tokenize(to_tokenize_in); // de-const. +//hmmm: do we need a copy? try scooting based on a current pos. + + astring name, value; // accumulated during the loop. + char current; // the most recent character from to_tokenize. + bool just_ate_blank_line = false; + // records when we handle a blank line as a comment. + + // loop over the string. + while (COOL) { + name.reset(); + value.reset(); + + // pre-processing to remove extra eols and white space in front. + if (is_eol_a_separator() && parser_bits::is_eol(to_tokenize[0])) { + CHOP; + // chop any white space but don't eat any non-white space coming up. + while (COOL && parser_bits::white_space(current)) { + CHOP; + if (!parser_bits::white_space(current)) { + // oops; we ate something we shouldn't have, since it will be + // chopped when we get in the main loop. + to_tokenize.insert(0, astring(current, 1)); + } + } + } + + // chop the first character off for analysis. + CHOP; + + // ignore any white space until we hit a variable or other good stuff. + if (parser_bits::white_space_no_cr(current)) + continue; + + // ignore eol unless they are in separator list. + bool handle_as_comment = false; + if (parser_bits::is_eol(current) && !is_eol_a_separator()) { + continue; + } else if (just_ate_blank_line && parser_bits::is_eol(current)) { + just_ate_blank_line = false; + continue; + } else if (parser_bits::is_eol(current) && is_eol_a_separator()) { +//LOG("found eol and it's a separator here"); + handle_as_comment = true; + } + + if (comment_char(current) || handle_as_comment) { + // set our flag since we are going to eat the end of line in any case. + just_ate_blank_line = true; + // seek all text until next separator. + while (COOL && !separator(current)) { + value += current; + CHOP; + } + // add the item with our ongoing comment number. + a_sprintf name("%s%d", STRTAB_COMMENT_PREFIX, _comment_number); + _implementation->add(name, value); + _comment_number++; // go to next comment number to keep unique. +LOG(astring("got comment: ") + name + " -> " + value); + continue; // got our chunk, keep going. + } + + just_ate_blank_line = false; // reset our flag. + + // skip characters we can't use for a variable name. + if (!okay_for_variable_name(current)) continue; + + // we've found the start of a variable. + while (COOL && okay_for_variable_name(current)) { + // accumulate the variable name. + name += current; + CHOP; // get the next character. + } + if (!COOL) { + // we're at the end of the line, so deal with this situation. + if (!separator(current) && !parser_bits::white_space(current) ) + name += current; // get the character from the end of the line. +LOG(astring("last add: ") + name + " -> " + value); + _implementation->add(name, value); // store what we built. + continue; // skip the rest; we're at the END of the line man. + } + + // skip spaces after variable name. + while (COOL && parser_bits::white_space_no_cr(current)) CHOP; + + bool found_assignment = false; // assume there isn't one. + if (assignment(current)) { + // we found the assignment operator and are starting on the value. + CHOP; // skip the assignment operator. + found_assignment = true; + } + + // skip spaces after the assignment statement. + while (COOL && parser_bits::white_space_no_cr(current)) CHOP; + + // track the quoting that we have to deal with in parsing a value. + stack q_stack(!int(_nesting)); + // create an unbounded stack for nesting. + + while (COOL) { + // check if the current character is a quote. + bool ignore_separator = false; + if (quote_mark(current)) { + if (!q_stack.size()) { + // nothing on the stack yet, so start accumulating. + ignore_separator = true; + q_stack.push(current); + } else if (current == q_stack.top()) { + // we got the end of this quoting. + q_stack.pop(); + // check if we're done with any quotes. if not, we still need to + // ignore the separators. + if (q_stack.size()) + ignore_separator = true; + } else { + // if we are using a bounded stack, it means we only support one + // level of quoting at a time. thus, this quote character simply + // falls in as a regular character. but if we're unbound, then + // we can nest arbitrary levels of quotes. + if (q_stack.kind() == stack::UNBOUNDED) + q_stack.push(current); + // we have something on the stack already so we're still ignoring + // separators. we just don't care about this type of quote. + ignore_separator = true; + } + } else if (q_stack.size()) { + // it's not a quote but we're still trying to chow the matching + // quote character. + ignore_separator = true; + } + + // look for the separator. + if (!ignore_separator && separator(current)) { + break; + } + + // accumulate the value. + value += current; + CHOP; // get the next character. + } + // get the last character if it's relevant. + if (!separator(current) && !parser_bits::white_space(current) ) { + value += current; + } + + if (found_assignment && !value) { + // use our special case for empty values, since there was an assignment + // operator but no value afterwards. + value = SPECIAL_VALUE; + } + + // store the accumulated variable name and value, but only if the name + // is non-empty. otherwise, it's not much of a definition. + if (name.t()) { + // strip spaces at the end of the name. + while (parser_bits::white_space_no_cr(name[name.end()])) + name.zap(name.end(), name.end()); + // strip spaces at the end of the value unless it's the special case. + if (!value.equal_to(SPECIAL_VALUE)) { + while (parser_bits::white_space(value[value.end()])) + value.zap(value.end(), value.end()); + } +LOG(astring("normal add: ") + name + " -> " + value); + _implementation->add(name, value); // store what we built. + just_ate_blank_line = true; // flag that we don't want next EOL. + // reset, just in case. + name.reset(); + value.reset(); + } + } + // currently we just kind of bully through whatever string is provided and do not + // flag any error conditions. but people do like to know if it worked or not. they can + // make their own conclusions if there are not enough variables defined for their needs. + return true; +} + +bool variable_tokenizer::is_eol_a_separator() const +{ + for (int i = 0; i < _separators->length(); i++) { + char sep = _separators->get(i); + // correct the separator for platform when it's the end of the line. + if (parser_bits::is_eol(sep)) return true; + } + return false; +} + +void variable_tokenizer::text_form(astring &accumulator) const +{ + accumulator.reset(); + bool added_sep = false; + for (int i = 0; i < _implementation->symbols(); i++) { + added_sep = false; + if (!string_table::is_comment(_implementation->name(i))) { + // a normal assignment is here. + accumulator += _implementation->name(i); + if (_implementation->operator [](i).t()) { + if (_add_spaces) accumulator += " "; + accumulator += _assignments->get(0); + if (_add_spaces) accumulator += " "; + accumulator += _implementation->operator [](i); + } + } else { + // this one is a comment. just spit out the value. + if (_implementation->operator [](i).t()) + accumulator += _implementation->operator [](i); + } + // correct the separator for platform when it's the end of the line. + if (is_eol_a_separator()) { + accumulator += parser_bits::platform_eol_to_chars(); + } else { + added_sep = true; // record that we put a separator in there. + accumulator += _separators->get(0); + accumulator += ' '; + } + } + // strip the final separator and space back off, if we added them. + if (added_sep) + accumulator.zap(accumulator.end() - 1, accumulator.end()); +} + +astring variable_tokenizer::text_form() const +{ + astring accumulator; + text_form(accumulator); + return accumulator; +} + +} //namespace. + diff --git a/core/library/configuration/variable_tokenizer.h b/core/library/configuration/variable_tokenizer.h new file mode 100644 index 00000000..82b08357 --- /dev/null +++ b/core/library/configuration/variable_tokenizer.h @@ -0,0 +1,181 @@ +#ifndef TOKENIZER_CLASS +#define TOKENIZER_CLASS + +/* +* Name : variable_tokenizer +* Author : Chris Koeritz +** +* Copyright (c) 1997-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include + +namespace configuration { + +//! Manages a bank of textual definitions of variables. +/*! + Manipulates strings containing variable definitions where a variable + is syntactically defined as a name, an assignment operator, and a value. + The string can optionally define many variables by placing a separator + character between the definitions. The assignment and separator are + referred to as sentinels in the following docs. + This class also supports quoted values if the appropriate constructor + is used. +*/ + +class variable_tokenizer : public virtual basis::root_object +{ +public: + enum constraints { DEFAULT_MAX_BITS = 7 }; + + variable_tokenizer(int max_bits = DEFAULT_MAX_BITS); + //!< creates a variable_tokenizer with the default characters. + /*!< this will not look for quote characters. the "max_bits" establishes + the hashing width for the internal table of strings; there will be + 2 ^ "max_bits" of space in the table. the default assignment operator + is '=' and the default separator is ','. */ + + variable_tokenizer(const basis::astring &separator, const basis::astring &assignment, + int max_bits = DEFAULT_MAX_BITS); + //!< creates an empty list of tokens and uses the specified sentinel chars. + /*!< the character that is expected to be between name/value pairs is + "separator". the "assignment" character is expected to be between each + name and its value. note that if the "separator" or "assignment" are more + than one character long, these will be taken as a set of valid characters + that can be used for those purposes. */ + + variable_tokenizer(const basis::astring &separator, const basis::astring &assignment, + const basis::astring "es, bool nesting = true, + int max_bits = DEFAULT_MAX_BITS); + //!< similar to the constructor above, but supports quoting. + /*!< if the "quotes" list is not empty, then those characters will be + treated as quoting characters that must be matched in pairs. inside a + quote, separators are ignored. if "nesting" is not true, then only one + level of quotes will be considered; the occurrence of other types of + quotes will be ignored until the original type is completed. */ + + variable_tokenizer(const variable_tokenizer &to_copy); + //!< builds a variable_tokenizer that is identical to "to_copy". + + virtual ~variable_tokenizer(); + + DEFINE_CLASS_NAME("variable_tokenizer"); + + void set_comment_chars(const basis::astring &comments); + //!< establishes a set of characters in "comments" as the comment items. + /*!< comments will be specially handled by being added to the string table + with the comment prefix. this allows them to be regenerated uniquely + later. */ + + variable_tokenizer &operator =(const variable_tokenizer &to_copy); + //!< makes this variable_tokenizer identical to "to_copy". + + int symbols() const; + //!< returns the number of entries in the variable_tokenizer. + + void reset(); + //!< clears all of the entries out. + + const structures::string_table &table() const; + //!< provides a constant peek at the string_table holding the values. + structures::string_table &table(); + //!< provides direct access to the string_table holding the values. + +//fix these docs. + bool parse(const basis::astring &to_tokenize); + //!< parses the string using our established sentinel characters. + /*!< attempts to snag as many value/pairs from "to_tokenize" as are + possible by using the current separator and assignment characters. + E.G.: if the separator is ';' and the assignment character + is '=', then one's string would look something like: @code + TEMP=c:\tmp; GLOB=c:\glob.exe; .... @endcode + whitespace is ignored if it's found (1) after a separator and before + the next variable name, (2) after the variable name and before the + assignment character, (3) after the assignment character and before the + value. this unfortunately implies that white space cannot begin or end + a value. + NOTE: unpredictable results will occur: if one's variables are + improperly formed, if assignment operators are missing or misplaced, + or if the separator character is used within the value. + NOTE: carriage returns are considered white-space and can exist in the + string as described above. + NOTE: parse is additive; if multiple calls to parse() occur, then the + symbol_table will be built from the most recent values found in the + parameters to parse(). if this is not desired, the symbol table's + reset() function can be used to empty out all variables. */ + + basis::astring find(const basis::astring &name) const; + //!< locates the value for a variable named "name" if it exists. + /*!< if "name" doesn't exist, then it returns an empty string. note that + an empty string might also indicate that the value is blank; locate is the + way to tell if a field is really missing. also note that when a variable + name is followed by an assignment operator and an empty value (e.g., + "avversione=" has no value), then a value of a single space character + will be stored. this ensures that the same format is used on the + output side, but it also means that if you access the table directly, + then you will get a space as the value. however, this function returns + an empty string for those entries to keep consistent with expectations. */ + + bool exists(const basis::astring &name) const; + //!< returns true if the "name" exists in the variable_tokenizer. + + basis::astring text_form() const; + //!< creates a new token list as a string of text. + /*!< the first separator and assignment characters in each set are used + to generate it. note that the whitespace that existed in the original + parsed string might not be exactly the same in the generated string. */ + void text_form(basis::astring &to_fill) const; + //!< like text_form() above, but stores into "to_fill". + + // dictates whether the output will have spaces between the assignment + // character and the key name and value. default is to not add them. + bool add_spaces() const { return _add_spaces; } + void add_spaces(bool add_them) { _add_spaces = add_them; } + + bool okay_for_variable_name(char to_check) const; + //!< true if "to_check" is a valid variable name character. + /*!< this includes any characters besides separators and assignments. */ + + const basis::astring &assignments() const; + //!< provides a peek at the assignments list. + const basis::astring &separators() const; + //!< provides a peek at the separators list. + const basis::astring "es() const; + //!< provides a peek at the quotes list. + + bool assignment(char to_check) const; + //!< true if "to_check" is a valid assignment operator. + + bool separator(char to_check) const; + //!< true if "to_check" is a valid separator. + + bool comment_char(char to_check) const; + //!< true if "to_check" is a registered comment character. + + bool is_eol_a_separator() const; + //!< reports whether any of the separators are an EOL character. + + bool quote_mark(char to_check) const; + //!< true if "to_check" is a member of the quotes list. + +private: + structures::string_table *_implementation; //!< holds the parsed values. + basis::astring *_assignments; //!< separates name from value. + basis::astring *_separators; //!< separates name/value pairs from other pairs. + basis::astring *_quotes; //!< the characters that are used for quoting. + bool _nesting; //!< if true, we nest arbitrary levels of quotes. + basis::astring *_comments; //!< if non-empty, characters that begin comments. + int _comment_number; //!< automatically incremented for use in comment tags. + bool _add_spaces; //!< records whether we add spaces around the assignment. +}; + +} //namespace. + +#endif + diff --git a/core/library/crypto/blowfish_crypto.cpp b/core/library/crypto/blowfish_crypto.cpp new file mode 100644 index 00000000..1e8ee443 --- /dev/null +++ b/core/library/crypto/blowfish_crypto.cpp @@ -0,0 +1,281 @@ +/*****************************************************************************\ +* * +* Name : blowfish encryption * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "blowfish_crypto.h" +#include "ssl_init.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace basis; +using namespace loggers; +using namespace mathematics; +using namespace structures; + +namespace crypto { + +const int FUDGE = 128; + // extra space for the cipher's block size. blowfish is only 8 bytes for + // the cipher block size, but we ensure there will definitely be no + // problems. + +#undef set_key + // get rid of a macro we don't want. + +#undef LOG +#define LOG(t) CLASS_EMERGENCY_LOG(program_wide_logger::get(), t) + +//#define DEBUG_BLOWFISH + // uncomment for noisier version. + +#ifdef DEBUG_BLOWFISH + // this macro checks on the validity of the key sizes (in bits). + #define DISCUSS_KEY_SIZE(key_size) \ + if (key_size < minimum_key_size()) { \ + continuable_error(static_class_name(), func, \ + a_sprintf("key size (%d bits) is less than minimum key size %d.", \ + key_size, minimum_key_size())); \ + } \ + if (key_size > maximum_key_size()) { \ + continuable_error(static_class_name(), func, \ + a_sprintf("key size (%d bits) is greater than maximum key size %d.", \ + key_size, maximum_key_size())); \ + } + + // this macro checks that the key in the byte array has enough bytes for + // the key size bits. + #define DISCUSS_PROVIDED_KEY(key_size, key) \ + if (key.length() * BITS_PER_BYTE < key_size) { \ + continuable_error(static_class_name(), func, \ + a_sprintf("key array length (%d) is less than required by key size " \ + "(%d bits).", key.length(), key_size)); \ + } +#else + #define DISCUSS_PROVIDED_KEY(key_size, key) + #define DISCUSS_KEY_SIZE(key_size) +#endif + +blowfish_crypto::blowfish_crypto(int key_size) +: _key_size(key_size), + _key(new byte_array) +{ +// FUNCDEF("constructor [int]"); + static_ssl_initializer(); + DISCUSS_KEY_SIZE(key_size); + if (key_size < minimum_key_size()) + _key_size = minimum_key_size(); + if (key_size > maximum_key_size()) + _key_size = maximum_key_size(); + generate_key(_key_size, *_key); +} + +blowfish_crypto::blowfish_crypto(const byte_array &key, int key_size) +: _key_size(key_size), + _key(new byte_array(key)) +{ +// FUNCDEF("constructor [byte_array/int]"); + // any problems with the key provided are horrid. they will yield a + // non-working blowfish object. + DISCUSS_KEY_SIZE(key_size); + DISCUSS_PROVIDED_KEY(key_size, key); + static_ssl_initializer(); +} + +blowfish_crypto::blowfish_crypto(const blowfish_crypto &to_copy) +: root_object(), + _key_size(to_copy._key_size), + _key(new byte_array(*to_copy._key)) +{ static_ssl_initializer(); } + +blowfish_crypto::~blowfish_crypto() +{ + WHACK(_key); +} + +int blowfish_crypto::key_size() const { return _key_size; } + +const byte_array &blowfish_crypto::get_key() const { return *_key; } + +int blowfish_crypto::minimum_key_size() { return 64; } + +int blowfish_crypto::maximum_key_size() { return 448; } + +blowfish_crypto &blowfish_crypto::operator = (const blowfish_crypto &to_copy) +{ + if (this == &to_copy) return *this; + _key_size = to_copy._key_size; + *_key = *to_copy._key; + return *this; +} + +bool blowfish_crypto::set_key(const byte_array &new_key, int key_size) +{ +// FUNCDEF("set_key"); + if (!new_key.length()) return false; + DISCUSS_KEY_SIZE(key_size); + DISCUSS_PROVIDED_KEY(key_size, new_key); + if ( (key_size < minimum_key_size()) || (key_size > maximum_key_size()) ) + return false; + if (new_key.length() * BITS_PER_BYTE < key_size) return false; + _key_size = key_size; + *_key = new_key; + return true; +} + +void blowfish_crypto::generate_key(int size, byte_array &new_key) +{ +// FUNCDEF("generate_key"); + DISCUSS_KEY_SIZE(size); + if (size < minimum_key_size()) + size = minimum_key_size(); + else if (size > maximum_key_size()) + size = maximum_key_size(); + int bytes = size / BITS_PER_BYTE; // calculate the number of bytes needed. + if (size % BITS_PER_BYTE) bytes++; // add one for non-integral portion. + new_key.reset(bytes); + for (int i = 0; i < bytes; i++) + new_key[i] = static_ssl_initializer().randomizer().inclusive(0, 255); +} + +SAFE_STATIC(mutex, __vector_init_lock, ) + +const byte_array &blowfish_crypto::init_vector() +{ + auto_synchronizer locking(__vector_init_lock()); + static byte_array to_return(EVP_MAX_IV_LENGTH); + static bool initted = false; + if (!initted) { + for (int i = 0; i < EVP_MAX_IV_LENGTH; i++) + to_return[i] = 214 - i; + initted = true; + } + return to_return; +} + +bool blowfish_crypto::encrypt(const byte_array &source, + byte_array &target) const +{ + FUNCDEF("encrypt"); + target.reset(); + if (!_key->length() || !source.length()) return false; + bool to_return = true; + + // initialize an encoding session. + EVP_CIPHER_CTX session; + EVP_CIPHER_CTX_init(&session); + EVP_EncryptInit_ex(&session, EVP_bf_cbc(), NIL, _key->observe(), + init_vector().observe()); + EVP_CIPHER_CTX_set_key_length(&session, _key_size); + + // allocate temporary space for encrypted data. + byte_array encoded(source.length() + FUDGE); + + // encrypt the entire source buffer. + int encoded_len = 0; + int enc_ret = EVP_EncryptUpdate(&session, encoded.access(), &encoded_len, + source.observe(), source.length()); + if (enc_ret != 1) { + continuable_error(class_name(), func, a_sprintf("encryption failed, " + "result=%d.", enc_ret)); + to_return = false; + } else { + // chop any extra space off. +// LOG(a_sprintf("encrypting bytes %d to %d.\n", i, edge)); + encoded.zap(encoded_len, encoded.last()); + target = encoded; + } + + // only add padding if we succeeded with the encryption. + if (enc_ret == 1) { + // finalize the encryption. + encoded.reset(FUDGE); // reinflate for padding. + int pad_len = 0; + enc_ret = EVP_EncryptFinal_ex(&session, encoded.access(), &pad_len); + if (enc_ret != 1) { + continuable_error(class_name(), func, a_sprintf("finalizing encryption " + "failed, result=%d.", enc_ret)); + to_return = false; + } else { +// LOG(a_sprintf("padding added %d bytes.\n", pad_len)); + encoded.zap(pad_len, encoded.last()); + target += encoded; + } + } + + EVP_CIPHER_CTX_cleanup(&session); + return to_return; +} + +bool blowfish_crypto::decrypt(const byte_array &source, + byte_array &target) const +{ + FUNCDEF("decrypt"); + target.reset(); + if (!_key->length() || !source.length()) return false; + bool to_return = true; + EVP_CIPHER_CTX session; + EVP_CIPHER_CTX_init(&session); +//LOG(a_sprintf("key size %d bits.\n", BITS_PER_BYTE * _key->length())); + EVP_DecryptInit_ex(&session, EVP_bf_cbc(), NIL, _key->observe(), + init_vector().observe()); + EVP_CIPHER_CTX_set_key_length(&session, _key_size); + + // allocate enough space for decoded bytes. + byte_array decoded(source.length() + FUDGE); + + int decoded_len = 0; + int dec_ret = EVP_DecryptUpdate(&session, decoded.access(), &decoded_len, + source.observe(), source.length()); + if (dec_ret != 1) { + continuable_error(class_name(), func, "decryption failed."); + to_return = false; + } else { +// LOG(a_sprintf(" decrypted size in bytes is %d.\n", decoded_len)); + decoded.zap(decoded_len, decoded.last()); + target = decoded; + } + + // only process padding if the first part of decryption succeeded. + if (dec_ret == 1) { + decoded.reset(FUDGE); // reinflate for padding. + int pad_len = 0; + dec_ret = EVP_DecryptFinal_ex(&session, decoded.access(), &pad_len); +// LOG(a_sprintf("padding added %d bytes.\n", pad_len)); + if (dec_ret != 1) { + continuable_error(class_name(), func, a_sprintf("finalizing decryption " + "failed, result=%d, padlen=%d, target had %d bytes.", dec_ret, + pad_len, target.length())); + to_return = false; + } else { + int dec_size = pad_len; + decoded.zap(dec_size, decoded.last()); + target += decoded; + } + } + + EVP_CIPHER_CTX_cleanup(&session); + return to_return; +} + +} //namespace. + + diff --git a/core/library/crypto/blowfish_crypto.h b/core/library/crypto/blowfish_crypto.h new file mode 100644 index 00000000..42a62503 --- /dev/null +++ b/core/library/crypto/blowfish_crypto.h @@ -0,0 +1,90 @@ +#ifndef BLOWFISH_CRYPTO_CLASS +#define BLOWFISH_CRYPTO_CLASS + +/*****************************************************************************\ +* * +* Name : blowfish encryption * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +namespace crypto { + +//! Provides BlowFish encryption on byte_arrays using the OpenSSL package. + +class blowfish_crypto : public virtual basis::root_object +{ +public: + blowfish_crypto(int key_size); + //!< this will create a new random key of the "key_size", in bits. + /*!< the valid sizes are from 64 bits to 448 bits (we are forcing a + higher minimum than the published algorithm because we have found smaller + keys to be unreliable during decryption. keys of 168 bits and larger + should be very secure. it is said that if a billion computers each tried + a billion keys a second, then a 168 bit key would take 10 * 10^24 years + to break (using brute force). this is essentially unbreakable since the + age of the universe is only 10 * 10^9 years so far. */ + + blowfish_crypto(const basis::byte_array &key, int key_size); + //!< uses a pre-existing "key". + + blowfish_crypto(const blowfish_crypto &to_copy); //!< copy constructor. + + virtual ~blowfish_crypto(); + + blowfish_crypto &operator = (const blowfish_crypto &to_copy); + + DEFINE_CLASS_NAME("blowfish_crypto"); + + int key_size() const; // returns the size of our key, in bits. + + static int minimum_key_size(); + //!< returns the minimum key size (in bits) supported here. + static int maximum_key_size(); + //!< returns the maximum key size (in bits) supported here. + + const basis::byte_array &get_key() const; //!< returns our current key. + + bool set_key(const basis::byte_array &new_key, int key_size); + //!< sets the encryption key to "new_key". + + static void generate_key(int size, basis::byte_array &new_key); + //!< creates a "new_key" of the "size" (in bits) specified. + + bool encrypt(const basis::byte_array &source, basis::byte_array &target) const; + //!< encrypts the "source" array into the "target" array. + + bool decrypt(const basis::byte_array &source, basis::byte_array &target) const; + //!< decrypts the "target" array from the encrypted "source" array. + + // seldom-needed methods... + + static const basis::byte_array &init_vector(); + //!< returns the initialization vector that is used by this class. + /*!< decryption of chunks that were encrypted by this class will require + the same init vector as this function returns. this is mainly provided + for third-party applications that want to be able to decrypt interoperably + with this class. if you are creating such an application but for some + reason cannot run this class in order to invoke this method, the vector + is created by the algorithm in this class's implementation file + (currently named blowfish_crypto.cpp). */ + +private: + int _key_size; //!< number of bits in the key. + basis::byte_array *_key; //!< our secret key. +}; + +} //namespace. + +#endif + diff --git a/core/library/crypto/makefile b/core/library/crypto/makefile new file mode 100644 index 00000000..97c7376a --- /dev/null +++ b/core/library/crypto/makefile @@ -0,0 +1,12 @@ +CONSOLE_MODE = true + +include cpp/variables.def + +TYPE = library +PROJECT = crypto +SOURCE = blowfish_crypto.cpp rsa_crypto.cpp ssl_init.cpp +USE_SSL = t +TARGETS = crypto.lib + +include cpp/rules.def + diff --git a/core/library/crypto/rsa_crypto.cpp b/core/library/crypto/rsa_crypto.cpp new file mode 100644 index 00000000..f50d3076 --- /dev/null +++ b/core/library/crypto/rsa_crypto.cpp @@ -0,0 +1,315 @@ +/*****************************************************************************\ +* * +* Name : RSA public key encryption * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Supports public (and private) key encryption and decryption using the * +* OpenSSL package's support for RSA encryption. * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "rsa_crypto.h" +#include "ssl_init.h" + +#include +#include +#include +#include + +#include +#include + +using namespace basis; +using namespace loggers; +using namespace mathematics; +using namespace structures; + +namespace crypto { + +// notes from openssl docs: length to be encrypted in a chunk must be less than +// RSA_size(rsa) - 11 for the PKCS #1 v1.5 based padding modes, less than +// RSA_size(rsa) - 41 for RSA_PKCS1_OAEP_PADDING and exactly RSA_size(rsa) +// for RSA_NO_PADDING. + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +//nice printing method... RSA_print_fp(stdout, private_key, 0); + +rsa_crypto::rsa_crypto(int key_size) +: _key(NIL) +{ + _key = generate_key(key_size); // generate_key initializes ssl for us. +} + +rsa_crypto::rsa_crypto(const byte_array &key) +: _key(NIL) +{ + static_ssl_initializer(); + byte_array key_copy = key; + set_key(key_copy); +} + +rsa_crypto::rsa_crypto(rsa_st *key) +: _key(NIL) +{ + static_ssl_initializer(); + set_key(key); +} + +rsa_crypto::rsa_crypto(const rsa_crypto &to_copy) +: root_object(), + _key(NIL) +{ + static_ssl_initializer(); + set_key(to_copy._key); +} + +rsa_crypto::~rsa_crypto() +{ + RSA_free(_key); +} + +const rsa_crypto &rsa_crypto::operator = (const rsa_crypto &to_copy) +{ + if (this == &to_copy) return *this; + set_key(to_copy._key); + return *this; +} + +rsa_st *rsa_crypto::generate_key(int key_size) +{ + FUNCDEF("generate_key"); + if (key_size < 4) key_size = 4; // laughable lower default. + static_ssl_initializer(); + rsa_st *to_return = RSA_generate_key(key_size, 65537, NIL, NIL); + if (!to_return) { + continuable_error(static_class_name(), func, + a_sprintf("failed to generate a key of %d bits.", key_size)); + } + return to_return; +} + +bool rsa_crypto::check_key(rsa_st *key) { return RSA_check_key(key) == 1; } + +bool rsa_crypto::set_key(byte_array &key) +{ + FUNCDEF("set_key [byte_array]"); + if (!key.length()) return false; + if (_key) RSA_free(_key); + _key = RSA_new(); + abyte type; + if (!structures::detach(key, type)) return false; + if ( (type != 'r') && (type != 'u') ) return false; + // get the public key bits first. + byte_array n; + if (!structures::detach(key, n)) return false; + _key->n = BN_bin2bn(n.access(), n.length(), NIL); + if (!_key->n) return false; + byte_array e; + if (!structures::detach(key, e)) return false; + _key->e = BN_bin2bn(e.access(), e.length(), NIL); + if (!_key->e) return false; + if (type == 'u') return true; // done with public key. + + // the rest is for a private key. + byte_array d; + if (!structures::detach(key, d)) return false; + _key->d = BN_bin2bn(d.access(), d.length(), NIL); + if (!_key->d) return false; + byte_array p; + if (!structures::detach(key, p)) return false; + _key->p = BN_bin2bn(p.access(), p.length(), NIL); + if (!_key->p) return false; + byte_array q; + if (!structures::detach(key, q)) return false; + _key->q = BN_bin2bn(q.access(), q.length(), NIL); + if (!_key->q) return false; + byte_array dmp1; + if (!structures::detach(key, dmp1)) return false; + _key->dmp1 = BN_bin2bn(dmp1.access(), dmp1.length(), NIL); + if (!_key->dmp1) return false; + byte_array dmq1; + if (!structures::detach(key, dmq1)) return false; + _key->dmq1 = BN_bin2bn(dmq1.access(), dmq1.length(), NIL); + if (!_key->dmq1) return false; + byte_array iqmp; + if (!structures::detach(key, iqmp)) return false; + _key->iqmp = BN_bin2bn(iqmp.access(), iqmp.length(), NIL); + if (!_key->iqmp) return false; + int check = RSA_check_key(_key); + if (check != 1) { + continuable_error(static_class_name(), func, "failed to check the private " + "portion of the key!"); + return false; + } + + return true; +} + +bool rsa_crypto::set_key(rsa_st *key) +{ + FUNCDEF("set_key [rsa_st]"); + if (!key) return NIL; + // test the incoming key. + int check = RSA_check_key(key); + if (check != 1) return false; + // clean out the old key. + if (_key) RSA_free(_key); + _key = RSAPrivateKey_dup(key); + if (!_key) { + continuable_error(static_class_name(), func, "failed to create a " + "duplicate of the key!"); + return false; + } + return true; +} + +bool rsa_crypto::public_key(byte_array &pubkey) const +{ +// FUNCDEF("public_key"); + if (!_key) return false; + structures::attach(pubkey, abyte('u')); // signal a public key. + // convert the two public portions into binary. + byte_array n(BN_num_bytes(_key->n)); + int ret = BN_bn2bin(_key->n, n.access()); + byte_array e(BN_num_bytes(_key->e)); + ret = BN_bn2bin(_key->e, e.access()); + // pack those two chunks. + structures::attach(pubkey, n); + structures::attach(pubkey, e); + return true; +} + +bool rsa_crypto::private_key(byte_array &privkey) const +{ +// FUNCDEF("private_key"); + if (!_key) return false; + int posn = privkey.length(); + bool worked = public_key(privkey); // get the public pieces first. + if (!worked) return false; + privkey[posn] = abyte('r'); // switch public key flag to private. + // convert the multiple private portions into binary. + byte_array d(BN_num_bytes(_key->d)); + int ret = BN_bn2bin(_key->d, d.access()); + byte_array p(BN_num_bytes(_key->p)); + ret = BN_bn2bin(_key->p, p.access()); + byte_array q(BN_num_bytes(_key->q)); + ret = BN_bn2bin(_key->q, q.access()); + byte_array dmp1(BN_num_bytes(_key->dmp1)); + ret = BN_bn2bin(_key->dmp1, dmp1.access()); + byte_array dmq1(BN_num_bytes(_key->dmq1)); + ret = BN_bn2bin(_key->dmq1, dmq1.access()); + byte_array iqmp(BN_num_bytes(_key->iqmp)); + ret = BN_bn2bin(_key->iqmp, iqmp.access()); + // pack all those in now. + structures::attach(privkey, d); + structures::attach(privkey, p); + structures::attach(privkey, q); + structures::attach(privkey, dmp1); + structures::attach(privkey, dmq1); + structures::attach(privkey, iqmp); + return true; +} + +bool rsa_crypto::public_encrypt(const byte_array &source, + byte_array &target) const +{ +// FUNCDEF("public_encrypt"); + target.reset(); + if (!source.length()) return false; + const int max_chunk = RSA_size(_key) - 12; + + byte_array encoded(RSA_size(_key)); + for (int i = 0; i < source.length(); i += max_chunk) { + int edge = i + max_chunk - 1; + if (edge > source.last()) + edge = source.last(); + int next_chunk = edge - i + 1; + RSA_public_encrypt(next_chunk, &source[i], + encoded.access(), _key, RSA_PKCS1_PADDING); + target += encoded; + } + return true; +} + +bool rsa_crypto::private_decrypt(const byte_array &source, + byte_array &target) const +{ +// FUNCDEF("private_decrypt"); + target.reset(); + if (!source.length()) return false; + const int max_chunk = RSA_size(_key); + + byte_array decoded(max_chunk); + for (int i = 0; i < source.length(); i += max_chunk) { + int edge = i + max_chunk - 1; + if (edge > source.last()) + edge = source.last(); + int next_chunk = edge - i + 1; + int dec_size = RSA_private_decrypt(next_chunk, &source[i], + decoded.access(), _key, RSA_PKCS1_PADDING); + if (dec_size < 0) return false; // that didn't work. + decoded.zap(dec_size, decoded.last()); + target += decoded; + decoded.reset(max_chunk); + } + return true; +} + +bool rsa_crypto::private_encrypt(const byte_array &source, + byte_array &target) const +{ +// FUNCDEF("private_encrypt"); + target.reset(); + if (!source.length()) return false; + const int max_chunk = RSA_size(_key) - 12; + + byte_array encoded(RSA_size(_key)); + for (int i = 0; i < source.length(); i += max_chunk) { + int edge = i + max_chunk - 1; + if (edge > source.last()) + edge = source.last(); + int next_chunk = edge - i + 1; + RSA_private_encrypt(next_chunk, &source[i], + encoded.access(), _key, RSA_PKCS1_PADDING); + target += encoded; + } + return true; +} + +bool rsa_crypto::public_decrypt(const byte_array &source, + byte_array &target) const +{ +// FUNCDEF("public_decrypt"); + target.reset(); + if (!source.length()) return false; + const int max_chunk = RSA_size(_key); + + byte_array decoded(max_chunk); + for (int i = 0; i < source.length(); i += max_chunk) { + int edge = i + max_chunk - 1; + if (edge > source.last()) + edge = source.last(); + int next_chunk = edge - i + 1; + int dec_size = RSA_public_decrypt(next_chunk, &source[i], + decoded.access(), _key, RSA_PKCS1_PADDING); + if (dec_size < 0) return false; // that didn't work. + decoded.zap(dec_size, decoded.last()); + target += decoded; + decoded.reset(max_chunk); + } + return true; +} + +} //namespace. + diff --git a/core/library/crypto/rsa_crypto.h b/core/library/crypto/rsa_crypto.h new file mode 100644 index 00000000..e43f1bdb --- /dev/null +++ b/core/library/crypto/rsa_crypto.h @@ -0,0 +1,102 @@ +#ifndef RSA_CRYPTO_CLASS +#define RSA_CRYPTO_CLASS + +/*****************************************************************************\ +* * +* Name : RSA public key encryption * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +// forward. +struct rsa_st; + +namespace crypto { + +//! Supports public key encryption and decryption. +/*! + This class uses the OpenSSL package's support for RSA encryption. +*/ + +class rsa_crypto : public virtual basis::nameable +{ +public: + rsa_crypto(int key_size); + //!< constructs using a randomized private key of the "key_size". + /*!< the "key_size" must be at least 1024 bits for acceptable security. + smaller keys are considered insecure. */ + + rsa_crypto(const basis::byte_array &key); + //!< constructs with the specified "key" as our private key. + /*!< the "key" is used for encryption rather than generating a random one. + the key is only valid if it was created with this class. also, if the key + is a public key, then only the public_encryption and public_decryption + methods will be available. */ + + rsa_crypto(rsa_st *key); + //!< starts with a pre-existing "key" in the low-level form. + + rsa_crypto(const rsa_crypto &to_copy); + + virtual ~rsa_crypto(); + + const rsa_crypto &operator = (const rsa_crypto &to_copy); + + DEFINE_CLASS_NAME("rsa_crypto"); + + bool set_key(basis::byte_array &key); + //!< resets this object's key to "key". + /*!< the key is only valid if this class created it. note: the "key" + is destructively consumed during the set method; do not pass in your + only copy. */ + + bool set_key(rsa_st *key); + //!< sets our new "key". + /*!< this must be a valid key created via the RSA algorithms. */ + + bool check_key(rsa_st *key); + //!< checks the RSA "key" provided for validity. + + bool public_encrypt(const basis::byte_array &source, basis::byte_array &target) const; + //!< encrypts "source" using our public key and stores it in "target". + /*!< public_encrypt and private_decrypt are a pair. an untrusted user can + encrypt with the public key and only the possessor of the private key + should be able to decrypt it. */ + bool private_decrypt(const basis::byte_array &source, basis::byte_array &target) const; + //!< decrypts "source" using our private key and stores it in "target". + + bool private_encrypt(const basis::byte_array &source, basis::byte_array &target) const; + //!< encrypts "source" using our private key and stores it in "target". + /*!< private_encrypt and public_decrypt are also a pair. the trusted + user with the private key can create encrypted chunks that anyone with + the public key can decrypt. */ + bool public_decrypt(const basis::byte_array &source, basis::byte_array &target) const; + //!< decrypts "source" using our public key and stores it in "target". + + bool public_key(basis::byte_array &pubkey) const; + //!< makes a copy of the public key held here. + bool private_key(basis::byte_array &privkey) const; + //!< makes a copy of the private key held here. + /*!< the private key should never be exposed to anyone else. */ + + static rsa_st *generate_key(int key_size); + //!< creates a random RSA key using the lower-level openssl methods. + +private: + rsa_st *_key; //!< our internal key. +}; + +} //namespace. + +#endif + diff --git a/core/library/crypto/ssl_init.cpp b/core/library/crypto/ssl_init.cpp new file mode 100644 index 00000000..f32da3a4 --- /dev/null +++ b/core/library/crypto/ssl_init.cpp @@ -0,0 +1,72 @@ +/*****************************************************************************\ +* * +* Name : SSL initialization helper * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "ssl_init.h" + +#include +#include +#include + +#include +#include +#include + +using namespace basis; +using namespace mathematics; +using namespace structures; + +namespace crypto { + +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +const int SEED_SIZE = 100; + // the size of the random seed that we'll use. + +// our global initialization object. +SAFE_STATIC_CONST(ssl_init, static_ssl_initializer, ) + +//#define DEBUG_SSL + // uncomment to cause more debugging information to be generated, plus + // more checking to be performed in the SSL support. + +ssl_init::ssl_init() +: c_rando() +{ +#ifdef DEBUG_SSL + CRYPTO_malloc_debug_init(); + CRYPTO_dbg_set_options(V_CRYPTO_MDEBUG_ALL); + CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); +#endif + RAND_seed(random_bytes(SEED_SIZE).observe(), SEED_SIZE); +} + +ssl_init::~ssl_init() +{ + CRYPTO_cleanup_all_ex_data(); + ERR_remove_state(0); + CRYPTO_mem_leaks_fp(stderr); +} + +const chaos &ssl_init::randomizer() const { return c_rando; } + +byte_array ssl_init::random_bytes(int length) const +{ + byte_array seed; + for (int i = 0; i < length; i++) + seed += abyte(c_rando.inclusive(0, 255)); + return seed; +} + +} //namespace. + diff --git a/core/library/crypto/ssl_init.h b/core/library/crypto/ssl_init.h new file mode 100644 index 00000000..7357bd8c --- /dev/null +++ b/core/library/crypto/ssl_init.h @@ -0,0 +1,55 @@ +#ifndef SSL_INIT_CLASS +#define SSL_INIT_CLASS + +/*****************************************************************************\ +* * +* Name : SSL initialization helper * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +namespace crypto { + +//! provides some initialization for the RSA and blowfish crypto. +/*! + This class does the SSL initialization needed before any functions can + be used. It also sets up the random seed for SSL. NOTE: you should never + need to use this class directly; just use the accessor function at the + very bottom and it will be managed globally for the entire program. +*/ + +class ssl_init : public virtual basis::nameable +{ +public: + ssl_init(); + ~ssl_init(); + + DEFINE_CLASS_NAME("ssl_init"); + + basis::byte_array random_bytes(int length) const; + //!< can be used to generate a random array of "length" bytes. + + const mathematics::chaos &randomizer() const; + //!< provides a random number generator for any encryption routines. + +private: + mathematics::chaos c_rando; //!< used for generating random numbers. +}; + +extern const ssl_init &static_ssl_initializer(); + //!< the main method for accessing the SSL initialization support. + +} //namespace. + +#endif + diff --git a/core/library/filesystem/byte_filer.cpp b/core/library/filesystem/byte_filer.cpp new file mode 100644 index 00000000..7aa05d78 --- /dev/null +++ b/core/library/filesystem/byte_filer.cpp @@ -0,0 +1,236 @@ +/*****************************************************************************\ +* * +* Name : byte_filer * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "byte_filer.h" + +#include +#include +#include +#include +#include + +#include +#include +#ifdef __UNIX__ + #include +#endif +#ifdef __WIN32__ + #include +#endif + +//#define DEBUG_BYTE_FILER + // uncomment for noisy version of class. + +using namespace basis; + +namespace filesystem { + +const size_t BTFL_FILE_TELL_LIMIT = size_t(2) * size_t(GIGABYTE); + // the largest a long integer can represent in the tell system call. + +class file_hider +{ +public: + FILE *fp; // the real file pointer. + + file_hider() : fp(NIL) {} +}; + +////////////// + +byte_filer::byte_filer() +: _handle(new file_hider), + _filename(new astring), + _auto_close(true) +{} + +byte_filer::byte_filer(const astring &filename, const astring &perms) +: _handle(new file_hider), + _filename(new astring), + _auto_close(true) +{ open(filename, perms); } + +byte_filer::byte_filer(const char *filename, const char *perms) +: _handle(new file_hider), + _filename(new astring), + _auto_close(true) +{ open(filename, perms); } + +byte_filer::byte_filer(bool auto_close, void *handle) +: _handle(new file_hider), + _filename(new astring), + _auto_close(auto_close) +{ + if (handle) { + _handle->fp = (FILE *)handle; + } +} + +byte_filer::~byte_filer() { close(); WHACK(_handle); WHACK(_filename); } + +astring byte_filer::filename() const { return *_filename; } + +size_t byte_filer::file_size_limit() { return BTFL_FILE_TELL_LIMIT; } + +bool byte_filer::open(const astring &filename, const astring &perms) +{ + close(); + _auto_close = true; // reset since we know we're opening this. + *_filename = filename; +#ifndef __WIN32__ + _handle->fp = filename.t()? fopen(filename.s(), perms.s()) : NIL; +#else + _handle->fp = filename.t()? _wfopen((wchar_t *)(UTF16 *)transcode_to_utf16(filename), + (wchar_t *)(UTF16 *)transcode_to_utf16(perms)) : NIL; + +#ifdef DEBUG_BYTE_FILER + if (!_handle->fp) + wprintf((wchar_t *)(UTF16 *)transcode_to_utf16("could not open: %ls\n"), + (wchar_t *)(UTF16 *)transcode_to_utf16(filename)); +#endif + +#endif + return good(); +} + +void byte_filer::close() +{ + *_filename = ""; + if (_auto_close && _handle->fp) fclose(_handle->fp); + _handle->fp = NIL; +} + +bool byte_filer::good() { return !!_handle->fp; } + +size_t byte_filer::tell() +{ + if (!_handle->fp) return 0; + long to_return = ::ftell(_handle->fp); + if (to_return == -1) { + // if we couldn't get the size, either the file isn't there or the size + // is too big for our OS to report. +///printf(a_sprintf("failed to tell size, calling it %.0f, and one plus that is %.0f\n", double(BTFL_FILE_TELL_LIMIT), double(long(long(BTFL_FILE_TELL_LIMIT) + 1))).s()); + if (good()) return BTFL_FILE_TELL_LIMIT; + else return 0; + } + return size_t(to_return); +} + +void *byte_filer::file_handle() { return _handle->fp; } + +bool byte_filer::eof() { return !_handle->fp ? true : !!feof(_handle->fp); } + +int byte_filer::read(abyte *buff, int size) +{ return !_handle->fp ? 0 : int(::fread((char *)buff, 1, size, _handle->fp)); } + +int byte_filer::write(const abyte *buff, int size) +{ return !_handle->fp ? 0 : int(::fwrite((char *)buff, 1, size, _handle->fp)); } + +int byte_filer::read(byte_array &buff, int desired_size) +{ + buff.reset(desired_size); + int to_return = read(buff.access(), desired_size); + buff.zap(to_return, buff.length() - 1); + return to_return; +} + +int byte_filer::write(const byte_array &buff) +{ return write(buff.observe(), buff.length()); } + +size_t byte_filer::length() +{ + size_t current_posn = tell(); + seek(0, FROM_END); // jump to end of file. + size_t file_size = tell(); // get position. + seek(int(current_posn), FROM_START); // jump back to previous place. + return file_size; +} + +int byte_filer::read(astring &s, int desired_size) +{ + s.pad(desired_size + 2); + int found = read((abyte *)s.observe(), desired_size); + if (non_negative(found)) s[found] = '\0'; + s.shrink(); + return found; +} + +int byte_filer::write(const astring &s, bool add_null) +{ + int len = s.length(); + if (add_null) len++; + return write((abyte *)s.observe(), len); +} + +void byte_filer::flush() +{ + if (!_handle->fp) return; + ::fflush(_handle->fp); +} + +bool byte_filer::truncate() +{ + flush(); + int fnum = fileno(_handle->fp); +#ifdef __WIN32__ + return SetEndOfFile((HANDLE)_get_osfhandle(fnum)); +#else + size_t posn = tell(); + // if we're at the highest point we can be, we no longer trust our + // ability to truncate properly. + if (posn >= file_size_limit()) + return false; + return !ftruncate(fnum, posn); +#endif +} + +bool byte_filer::seek(int where, origins origin) +{ + if (!_handle->fp) return false; + int real_origin; + switch (origin) { + case FROM_START: real_origin = SEEK_SET; break; + case FROM_END: real_origin = SEEK_END; break; + case FROM_CURRENT: real_origin = SEEK_CUR; break; + default: return false; // not a valid choice. + } + int ret = ::fseek(_handle->fp, where, real_origin); + return !ret; +} + +int byte_filer::getline(abyte *buff, int desired_size) +{ + if (!_handle->fp) return 0; + char *ret = ::fgets((char *)buff, desired_size, _handle->fp); + return !ret? 0 : int(strlen((char *)buff)) + 1; +} + +int byte_filer::getline(byte_array &buff, int desired_size) +{ + buff.reset(desired_size + 1); + return getline(buff.access(), desired_size); +} + +int byte_filer::getline(astring &buff, int desired_size) +{ + buff.pad(desired_size + 1); + int to_return = getline((abyte *)buff.access(), desired_size); + if (non_negative(to_return)) buff[to_return] = '\0'; + buff.shrink(); + return to_return; +} + +} //namespace. + + diff --git a/core/library/filesystem/byte_filer.h b/core/library/filesystem/byte_filer.h new file mode 100644 index 00000000..0e8bca78 --- /dev/null +++ b/core/library/filesystem/byte_filer.h @@ -0,0 +1,159 @@ +#ifndef BYTE_FILER_CLASS +#define BYTE_FILER_CLASS + +/*****************************************************************************\ +* * +* Name : byte_filer * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace filesystem { + +// forward declarations. +class file_hider; + +//! Provides file managment services using the standard I/O support. + +class byte_filer +{ +public: + byte_filer(); + //!< constructs an object that doesn't access a file yet. + /*!< use open() to make the object valid. */ + + byte_filer(const basis::astring &filename, const basis::astring &permissions); + //!< opens a file "filename" as specified in "permissions". + /*!< these are identical to the standard I/O permissions: + + - "r" - opens text file for reading. + - "w" - opens text file for writing and discards any previous contents. + - "a" - opens text file for writing at end; appends to contents. + - "r+" - opens text file for update (both reading and writing). + - "w+" - creates a text file for update; any previous contents are lost. + - "a+" - opens or creates a text file for update, appending at end. + + a "b" can be added to the end of these to indicate a binary file should + be used instead of a text file. */ + + byte_filer(const char *filename, const char *permissions); + //!< synonym for above but takes char pointers. + + byte_filer(bool auto_close, void *opened); + //!< uses a previously "opened" stdio FILE handle. be careful! + /*!< the "opened" object must be a valid FILE pointer; void * is used to + avoid pulling in the stdio header. this method will not close the file + handle if "auto_close" is false. */ + + ~byte_filer(); + + static size_t file_size_limit(); + //!< returns the maximum size that seek and length can support. + /*!< use the huge_file class if you need to exceed the stdio limits. */ + + bool open(const basis::astring &filename, const basis::astring &permissions); + //!< opens a file with "filename" and "permissions" as in the constructor. + /*!< if a different file had already been opened, it is closed. */ + + void close(); + //!< shuts down the open file, if any. + /*!< open() will have to be invoked before this object can be used again. */ + + basis::astring filename() const; + //!< returns the filename that this was opened with. + + bool good(); + //!< returns true if the file seems to be in the appropriate desired state. + + size_t length(); + //!< returns the file's total length, in bytes. + /*!< this cannot accurately report a file length if it is file_size_limit() + or greater. */ + + size_t tell(); + //!< returns the current position within the file, in terms of bytes. + /*!< this is also limited to file_size_limit(). */ + + void flush(); + //!< forces any pending writes to actually be saved to the file. + + enum origins { + FROM_START, //!< offset is from the beginning of the file. + FROM_END, //!< offset is from the end of the file. + FROM_CURRENT //!< offset is from current cursor position. + }; + + bool seek(int where, origins origin = FROM_START); + //!< places the cursor in the file at "where", based on the "origin". + /*!< note that if the origin is FROM_END, then the offset "where" should + be a negative number if you're trying to access the interior of the file; + positive offsets indicate places after the actual end of the file. */ + + bool eof(); + //!< returns true if the cursor is at (or after) the end of the file. + + int read(basis::abyte *buffer, int buffer_size); + //!< reads "buffer_size" bytes from the file into "buffer". + /*!< for all of the read and write operations, the number of bytes that + is actually processed for the file is returned. */ + int write(const basis::abyte *buffer, int buffer_size); + //!< writes "buffer_size" bytes into the file from "buffer". + + int read(basis::byte_array &buffer, int desired_size); + //!< reads "buffer_size" bytes from the file into "buffer". + int write(const basis::byte_array &buffer); + //!< writes the "buffer" into the file. + + int read(basis::astring &buffer, int desired_size); + //!< read() pulls up to "desired_size" bytes from the file into "buffer". + /*!< since the read() will grab as much data as is available given that it + fits in "desired_size". null characters embedded in the file are a bad + issue here; some other method must be used to read the file instead (such + as the byte_array read above). the "buffer" is shrunk to fit the zero + terminator that we automatically add. */ + + int write(const basis::astring &buffer, bool add_null = false); + //!< stores the string in "buffer" into the file at the current position. + /*!< if "add_null" is true, then write() adds a zero terminator to what + is written into the file. otherwise just the string's non-null contents + are written. */ + + int getline(basis::abyte *buffer, int desired_size); + //!< reads a line of text (terminated by a return) into the "buffer". + int getline(basis::byte_array &buffer, int desired_size); + //!< reads a line of text (terminated by a return) into the "buffer". + int getline(basis::astring &buffer, int desired_size); + //!< reads a line of text (terminated by a return) into the "buffer". + + bool truncate(); + //!< truncates the file after the current position. + + void *file_handle(); + //!< provides a hook to get at the operating system's file handle. + /*!< this is of the type FILE *, as defined by . */ + +private: + file_hider *_handle; //!< the standard I/O support that we rely upon. + basis::astring *_filename; //!< holds onto our current filename. + bool _auto_close; //!< true if the object should close the file. + + // not to be called. + byte_filer(const byte_filer &); + byte_filer &operator =(const byte_filer &); +}; + +} //namespace. + +#endif + diff --git a/core/library/filesystem/directory.cpp b/core/library/filesystem/directory.cpp new file mode 100644 index 00000000..e4e3adcd --- /dev/null +++ b/core/library/filesystem/directory.cpp @@ -0,0 +1,271 @@ +/*****************************************************************************\ +* * +* Name : directory * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "directory.h" +#include "filename.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef __UNIX__ + #include + #include + #include + #include +#endif +#ifdef __WIN32__ + #include +#endif + +/* +#ifdef __WIN32__ + const int MAX_ABS_PATH = 2048; +#elif defined(__APPLE__) + const int MAX_ABS_PATH = 2048; +#else + const int MAX_ABS_PATH = MAX_ABS_PATH; +#endif +*/ + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +using namespace algorithms; +using namespace basis; +using namespace loggers; +using namespace structures; + +namespace filesystem { + +directory::directory(const astring &path, const char *pattern) +: _scanned_okay(false), + _path(new astring), + _files(new string_array), + _folders(new string_array), + _pattern(new astring(pattern)) +{ reset(path, pattern); } + +directory::directory(const directory &to_copy) +: _scanned_okay(false), + _path(new astring), + _files(new string_array), + _folders(new string_array), + _pattern(new astring) +{ reset(*to_copy._path, to_copy._pattern->observe()); } + +directory::~directory() +{ + _scanned_okay = false; + WHACK(_path); + WHACK(_files); + WHACK(_folders); + WHACK(_pattern); +} + +const astring &directory::path() const { return *_path; } + +const astring &directory::pattern() const { return *_pattern; } + +directory &directory::operator =(const directory &to_copy) +{ + if (this == &to_copy) return *this; // oops. + _scanned_okay = false; + reset(*to_copy._path, to_copy._pattern->observe()); + return *this; +} + +astring directory::absolute_path(const astring &rel_path) +{ + char abs_path[MAX_ABS_PATH + 1]; + abs_path[0] = '\0'; +#ifdef __WIN32__ + if (!_fullpath(abs_path, rel_path.s(), MAX_ABS_PATH)) return ""; + return abs_path; +#else + if (!realpath(rel_path.s(), abs_path)) return ""; + return abs_path; +#endif +} + +astring directory::current() +{ + astring to_return("."); // failure result. +#ifdef __WIN32__ + flexichar buffer[MAX_ABS_PATH + 1] = { '\0' }; + GetCurrentDirectory(MAX_ABS_PATH, buffer); + to_return = from_unicode_temp(buffer); +#else + char buffer[MAX_ABS_PATH + 1] = { '\0' }; + if (realpath(".", buffer)) to_return = buffer; +#endif + return to_return; +} + +bool directory::reset(const astring &path, const char *pattern) +{ *_path = path; *_pattern = pattern; return rescan(); } + +bool directory::move_up(const char *pattern) +{ + astring currdir = current(); + return reset(currdir + "/..", pattern); +} + +bool directory::move_down(const astring &subdir, const char *pattern) +{ + astring currdir = current(); + return reset(currdir + "/" + subdir, pattern); +} + +const string_array &directory::files() const { return *_files; } + +const string_array &directory::directories() const { return *_folders; } + +bool directory::rescan() +{ + FUNCDEF("rescan"); + _scanned_okay = false; + _files->reset(); + _folders->reset(); + astring cur_dir = "."; + astring par_dir = ".."; +#ifdef __WIN32__ + // start reading the directory. + WIN32_FIND_DATA wfd; + astring real_path_spec = *_path + "/" + *_pattern; + HANDLE search_handle = FindFirstFile(to_unicode_temp(real_path_spec), &wfd); + if (search_handle == INVALID_HANDLE_VALUE) return false; // bad path. + do { + // ignore the two standard directory entries. + astring filename_transcoded(from_unicode_temp(wfd.cFileName)); + if (!strcmp(filename_transcoded.s(), cur_dir.s())) continue; + if (!strcmp(filename_transcoded.s(), par_dir.s())) continue; + +#ifdef UNICODE +//temp + to_unicode_persist(fudgemart, filename_transcoded); + if (memcmp((wchar_t*)fudgemart, wfd.cFileName, wcslen(wfd.cFileName)*2)) + printf("failed to compare the string before and after transcoding\n"); +#endif + +//wprintf(to_unicode_temp("file is %ls\n"), (wchar_t*)to_unicode_temp(filename_transcoded)); + + filename temp_name(*_path, filename_transcoded.s()); + + // add this to the appropriate list. + if (temp_name.is_directory()) { + _folders->concatenate(filename_transcoded); + } else { + _files->concatenate(filename_transcoded); + +#ifdef UNICODE + to_unicode_persist(fudgemart2, temp_name.raw()); + FILE *fpjunk = _wfopen(fudgemart2, to_unicode_temp("rb")); + if (!fpjunk) + LOG(astring("failed to open the file for testing: ") + temp_name.raw() + "\n"); + if (fpjunk) fclose(fpjunk); +#endif + + } + } while (FindNextFile(search_handle, &wfd)); + FindClose(search_handle); +#endif +#ifdef __UNIX__ + DIR *dir = opendir(_path->s()); +//hmmm: could check errno to determine what caused the problem. + if (!dir) return false; + dirent *entry = readdir(dir); + while (entry) { + char *file = entry->d_name; + bool add_it = true; + if (!strcmp(file, cur_dir.s())) add_it = false; + if (!strcmp(file, par_dir.s())) add_it = false; + // make sure that the filename matches the pattern also. + if (add_it && !fnmatch(_pattern->s(), file, 0)) { + filename temp_name(*_path, file); + // add this to the appropriate list. + if (temp_name.is_directory()) + _folders->concatenate(file); + else + _files->concatenate(file); + } + entry = readdir(dir); + } + closedir(dir); +#endif + shell_sort(_files->access(), _files->length()); + shell_sort(_folders->access(), _folders->length()); + + _scanned_okay = true; + return true; +} + +bool directory::make_directory(const astring &path) +{ +#ifdef __UNIX__ + int mk_ret = mkdir(path.s(), 0777); +#endif +#ifdef __WIN32__ + int mk_ret = mkdir(path.s()); +#endif + return !mk_ret; +} + +bool directory::remove_directory(const astring &path) +{ +#ifdef __UNIX__ + int rm_ret = rmdir(path.s()); +#endif +#ifdef __WIN32__ + int rm_ret = rmdir(path.s()); +#endif + return !rm_ret; +} + +bool directory::recursive_create(const astring &directory_name) +{ +// FUNCDEF("recursive_create"); + filename dir(directory_name); + string_array pieces; + dir.separate(pieces); + for (int i = 0; i < pieces.length(); i++) { + // check each location along the way. + string_array partial = pieces.subarray(0, i); + filename curr; + curr.join(partial); // this is our current location. + // make sure, if we see a drive letter component, that we call it + // a proper directory name. + if (curr.raw()[curr.raw().end()] == ':') + curr = curr.raw() + "/"; + if (curr.exists()) { + if (curr.is_directory()) { + continue; // that's good. + } + return false; // if it's an existing file, we're hosed. + } + // the directory at this place doesn't exist yet. let's create it. + if (!directory::make_directory(curr.raw())) return false; + } + return true; +} + +} // namespace. diff --git a/core/library/filesystem/directory.h b/core/library/filesystem/directory.h new file mode 100644 index 00000000..7012d7be --- /dev/null +++ b/core/library/filesystem/directory.h @@ -0,0 +1,117 @@ +#ifndef DIRECTORY_CLASS +#define DIRECTORY_CLASS + +/*****************************************************************************\ +* * +* Name : directory * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace filesystem { + +//! Implements a scanner that finds all filenames in the directory specified. + +class directory : public virtual basis::root_object +{ +public: + directory(const basis::astring &path, const char *pattern = "*"); + //!< opens up the "path" specified and scans for files and subdirectories. + /*!< if the location was accessible, then the good() method returns true. + note that the "path" should just be a bare directory without any + wildcards attached. the "pattern" can be specified if you wish to + strain out just a subset of the files in the directory. it must meet + the same requirements that the operating system places on wildcard + patterns. */ + + directory(const directory &to_copy); + + virtual ~directory(); + + directory &operator =(const directory &to_copy); + + DEFINE_CLASS_NAME("directory"); + + bool good() const { return _scanned_okay; } + //!< true if the directory existed and its contents were readable. + + const basis::astring &path() const; + //!< returns the directory that we manage. + + const basis::astring &pattern() const; + //!< returns the pattern that the directory class scans for. + + static basis::astring absolute_path(const basis::astring &relative_path); + //!< returns the absolute path to a file with "relative_path". + /*!< an empty string is returned on failure. */ + + bool reset(const basis::astring &path, const char *pattern = "*"); + //!< gets rid of any current files and rescans the directory at "path". + /*!< a new "pattern" can be specified at this time also. */ + + bool move_up(const char *pattern = "*"); + //!< resets the directory to be its own parent. + + bool move_down(const basis::astring &subdir, const char *pattern = "*"); + //!< changes down into a "subdir" of this directory. + /*!< the "subdir" should be just the file name component to change into. + absolute paths will not work. for example, if a directory "/l/jed" has + a subdirectory named "clampett", then use: @code + my_dir->move_down("clampett") @endcode + */ + + bool rescan(); + //!< reads our current directory's contents over again. + + const structures::string_array &files() const; + //!< returns the list of files that we found in this directory. + /*!< these are all assumed to be located in our given path. to find out + more information about the files themselves, construct a filename object + with the path() and the file of interest. */ + + const structures::string_array &directories() const; + //!< these are the directory names from the folder. + /*!< they can also be examined using the filename object. note that + this does not include the entry for the current directory (.) or the + parent (..). */ + + // static methods of general directory-related interest. + + static basis::astring current(); + //!< returns the current directory, as reported by the operating system. + + static bool make_directory(const basis::astring &path); + //!< returns true if the directory "path" could be created. + + static bool remove_directory(const basis::astring &path); + //!< returns true if the directory "path" could be removed. + + static bool recursive_create(const basis::astring &directory_name); + //!< returns true if the "directory_name" can be created or already exists. + /*!< false returns indicate that the operating system wouldn't let us + make the directory, or that we didn't have sufficient permissions to + access an existing directory to view it or move into it. */ + +private: + bool _scanned_okay; //!< did this directory work out? + basis::astring *_path; //!< the directory we're looking at. + structures::string_array *_files; //!< the list of files. + structures::string_array *_folders; //!< the list of directories. + basis::astring *_pattern; //!< the pattern used to find the files. +}; + +} //namespace. + +#endif + diff --git a/core/library/filesystem/directory_tree.cpp b/core/library/filesystem/directory_tree.cpp new file mode 100644 index 00000000..77ea9472 --- /dev/null +++ b/core/library/filesystem/directory_tree.cpp @@ -0,0 +1,948 @@ +/*****************************************************************************\ +* * +* Name : directory_tree * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "directory.h" +#include "directory_tree.h" +#include "filename.h" +#include "filename_list.h" +#include "filename_tree.h" + +#include +#include +#include +#include +#include +#include + +#include + +using namespace basis; +using namespace structures; +using namespace nodes; +using namespace textual; + +//#define DEBUG_DIRECTORY_TREE + // uncomment for noisier version. + +#undef LOG +#define LOG(to_print) printf("%s::%s: %s\n", static_class_name(), func, astring(to_print).s()) +//CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +////////////// + +namespace filesystem { + +class dir_tree_iterator : public filename_tree::iterator +{ +public: + filename_tree *_current; + + dir_tree_iterator(const filename_tree *initial, + tree::traversal_directions dir) + : filename_tree::iterator(initial, dir), _current(NIL) {} +}; + +////////////// + +directory_tree::directory_tree() +: _scanned_okay(false), + _path(new astring), + _pattern(new astring), + _real_tree(new filename_tree), + _ignore_files(false), + _creator(new fname_tree_creator) +{ +} + +directory_tree::directory_tree(const astring &path, const char *pattern, + bool ignore_files) +: _scanned_okay(false), + _path(new astring(path)), + _pattern(new astring(pattern)), + _real_tree(NIL), + _ignore_files(ignore_files), + _creator(new fname_tree_creator) +{ + reset(path, pattern); +} + +directory_tree::~directory_tree() +{ + _scanned_okay = false; + WHACK(_path); + WHACK(_pattern); + WHACK(_real_tree); + WHACK(_creator); +} + +const astring &directory_tree::path() const { return *_path; } + +int directory_tree::packed_size() const +{ + return 2 * PACKED_SIZE_INT32 + + _path->packed_size() + + _pattern->packed_size() + + _real_tree->recursive_packed_size(); +} + +void directory_tree::pack(byte_array &packed_form) const +{ + attach(packed_form, int(_scanned_okay)); + attach(packed_form, int(_ignore_files)); + _path->pack(packed_form); + _pattern->pack(packed_form); + _real_tree->recursive_pack(packed_form); +} + +bool directory_tree::unpack(byte_array &packed_form) +{ + int temp; + if (!detach(packed_form, temp)) return false; + _scanned_okay = temp; + if (!detach(packed_form, temp)) return false; + _ignore_files = temp; + if (!_path->unpack(packed_form)) return false; + if (!_pattern->unpack(packed_form)) return false; + WHACK(_real_tree); + _real_tree = (filename_tree *)packable_tree::recursive_unpack + (packed_form, *_creator); + if (!_real_tree) { + _real_tree = new filename_tree; // reset it. + return false; + } + return true; +} + +void directory_tree::text_form(astring &target, bool show_files) +{ + dir_tree_iterator *ted = start(directory_tree::prefix); + // create our iterator to do a prefix traversal. + + int depth; // current depth in tree. + filename curr; // the current path the iterator is at. + string_array files; // the filenames held at the iterator. + + while (current(*ted, curr, files)) { + // we have a good directory to show. + directory_tree::depth(*ted, depth); + target += string_manipulation::indentation(depth * 2) + astring("[") + + curr.raw() + "]" + parser_bits::platform_eol_to_chars(); + if (show_files) { + astring names; + for (int i = 0; i < files.length(); i++) names += files[i] + " "; + if (names.length()) { + astring split; + string_manipulation::split_lines(names, split, depth * 2 + 2); + target += split + parser_bits::platform_eol_to_chars(); + } + } + + // go to the next place. + next(*ted); + } + + throw_out(ted); +} + +void directory_tree::traverse(const astring &path, const char *pattern, + filename_tree &add_to) +{ +#ifdef DEBUG_DIRECTORY_TREE + FUNCDEF("traverse"); +#endif + // prepare the current node. + add_to._dirname = filename(path, astring::empty_string()); + add_to._files.reset(); +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("working on node ") + add_to._dirname.raw()); +#endif + + // open the directory. + directory curr(path, "*"); + if (!curr.good()) return; + + if (!_ignore_files) { + // add all the files to the current node. + directory curr_stringent(path, pattern); + add_to._files = curr_stringent.files(); + } + + // now iterate across the directories here and add a sub-node for each one, + // and recursively traverse that sub-node also. + const string_array &dirs = curr.directories(); + for (int i = 0; i < dirs.length(); i++) { + filename_tree *new_branch = NIL; + astring new_path = path + filename::default_separator() + dirs[i]; +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("seeking path: ") + new_path); +#endif + for (int q = 0; q < add_to.branches(); q++) { + filename_tree *curr_kid = (filename_tree *)add_to.branch(q); +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("curr kid: ") + curr_kid->_dirname); +#endif + if (filename(new_path).raw().iequals(filename + (curr_kid->_dirname).raw())) { + new_branch = curr_kid; +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("using existing branch for ") + new_path); +#endif + break; + } + } + if (!new_branch) { +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("adding new branch for ") + new_path); +#endif + new_branch = new filename_tree; + add_to.attach(new_branch); + new_branch->_depth = add_to._depth + 1; + } +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("traversing sub-node ") + new_path); +#endif + traverse(new_path, pattern, *new_branch); + } +} + +bool directory_tree::reset(const astring &path, const char *pattern) +{ + _scanned_okay = false; + WHACK(_real_tree); + *_path = path; + *_pattern = pattern; + _real_tree = new filename_tree; + + // check that the top-level is healthy. + directory curr(path, "*"); + if (!curr.good()) return false; + // our only exit condition; other directories might not be accessible + // underneath, but the top one must be accessible for us to even start + // the scanning. + + traverse(path, pattern, *_real_tree); + _scanned_okay = true;; + return true; +} + +dir_tree_iterator *directory_tree::start_at(filename_tree *start, + traversal_types type) const +{ + // translate to the lower level traversal enum. + tree::traversal_directions dir = tree::prefix; + if (type == infix) dir = tree::infix; + else if (type == postfix) dir = tree::postfix; + + return new dir_tree_iterator(start, dir); +} + +dir_tree_iterator *directory_tree::start(traversal_types type) const +{ + // translate to the lower level traversal enum. + tree::traversal_directions dir = tree::prefix; + if (type == infix) dir = tree::infix; + else if (type == postfix) dir = tree::postfix; + + return new dir_tree_iterator(_real_tree, dir); +} + +bool directory_tree::jump_to(dir_tree_iterator &scanning, + const astring &sub_path) +{ +#ifdef DEBUG_DIRECTORY_TREE + FUNCDEF("jump_to"); +#endif + string_array pieces; + filename(sub_path).separate(pieces); + for (int i = 0; i < pieces.length(); i++) { + filename_tree *curr = dynamic_cast(scanning.current()); +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("at ") + curr->_dirname.raw()); +#endif + string_array sub_pieces = pieces.subarray(i, i); + filename curr_path; + curr_path.join(sub_pieces); + curr_path = filename(curr->_dirname.raw() + filename::default_separator() + + curr_path.raw()); +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("made curr path ") + curr_path.raw()); +#endif + if (!curr) return false; + bool found_it = false; + for (int j = 0; j < curr->branches(); j++) { + filename_tree *sub = dynamic_cast(curr->branch(j)); +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("looking at ") + sub->_dirname.raw()); +#endif + if (sub->_dirname.compare_prefix(curr_path)) { + // this part matches! + scanning.push(sub); +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("found at ") + sub->_dirname.raw()); +#endif + found_it = true; + break; + } + } + if (!found_it) { +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("could not find ") + curr_path.raw()); +#endif + return false; + } + } + return true; +} + +filename_tree *directory_tree::goto_current(dir_tree_iterator &scanning) +{ + if (!scanning._current) { + // this one hasn't been advanced yet, or it's already over with. + scanning._current = (filename_tree *)scanning.next(); + } + // now check that we're healthy. + if (!scanning._current) return NIL; // all done. + + // cast the tree to the right type. + return dynamic_cast(scanning._current); +} + +bool directory_tree::current_dir(dir_tree_iterator &scanning, + filename &dir_name) +{ + dir_name = astring::empty_string(); + filename_tree *tof = goto_current(scanning); + if (!tof) return false; + dir_name = tof->_dirname; + return true; +} + +bool directory_tree::current(dir_tree_iterator &scanning, + filename &dir_name, string_array &to_fill) +{ + // clear any existing junk. + dir_name = astring::empty_string(); + to_fill.reset(); + + filename_tree *tof = goto_current(scanning); + if (!tof) return false; + + // fill in what they wanted. + dir_name = tof->_dirname; + tof->_files.fill(to_fill); + + return true; +} + +bool directory_tree::current(dir_tree_iterator &scanning, + filename &dir_name, filename_list &to_fill) +{ + // clear any existing junk. + dir_name = astring::empty_string(); + to_fill.reset(); + + filename_tree *tof = goto_current(scanning); + if (!tof) return false; + + // fill in what they wanted. + dir_name = tof->_dirname; + to_fill = tof->_files; + + return true; +} + +filename_list *directory_tree::access(dir_tree_iterator &scanning) +{ + filename_tree *tof = goto_current(scanning); + if (!tof) return NIL; + return &tof->_files; +} + +bool directory_tree::depth(dir_tree_iterator &scanning, int &depth) +{ + depth = -1; // invalid as default. + filename_tree *tof = goto_current(scanning); + if (!tof) return false; + depth = tof->_depth; + return true; +} + +bool directory_tree::children(dir_tree_iterator &scanning, int &kids) +{ + kids = -1; // invalid as default. + filename_tree *tof = goto_current(scanning); + if (!tof) return false; + kids = tof->branches(); + return true; +} + +bool directory_tree::next(dir_tree_iterator &scanning) +{ + scanning._current = (filename_tree *)scanning.next(); + return !!scanning._current; +} + +void directory_tree::throw_out(dir_tree_iterator * &to_whack) +{ + WHACK(to_whack); +} + +filename_tree *directory_tree::seek(const astring &dir_name_in, + bool ignore_initial) const +{ + FUNCDEF("seek"); + array examining; + // the list of nodes we're currently looking at. + +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("seeking on root of: ") + *_path); +#endif + + astring dir_name = filename(dir_name_in).raw(); + // set the search path up to have the proper prefix. + if (ignore_initial) + dir_name = path() + filename::default_separator() + + filename(dir_name_in).raw(); + +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("adding root: ") + _real_tree->_dirname); +#endif + examining += _real_tree; + + astring sequel; // holds extra pieces from filename comparisons. + + // chew on the list of nodes to examine until we run out. + while (examining.length()) { + int posn; + bool found = false; + // start looking at all the items in the list, even though we might have + // to abandon the iteration if we find a match. + filename_tree *check = NIL; + for (posn = 0; posn < examining.length(); posn++) { + check = examining[posn]; + filename current(check->_dirname); +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("looking at ") + current.raw()); +#endif + if (current.compare_prefix(dir_name, sequel)) { + // we have a match! +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("matched! at ") + current.raw()); +#endif + found = true; + if (!sequel) { + // whoa! an exact match. we're done now. +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("exact match at ") + current.raw() + "! done!!!"); +#endif + return check; + } else { +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("inexact match because sequel=") + sequel); +#endif + } + break; + } + } + if (!found) return NIL; // we found nothing comparable. + + // we found a partial match. that means we should start looking at this + // node's children for the exact match. + if (!check) { + // this is a serious logical error! + LOG("serious logical error: tree was not located."); + return NIL; + } + examining.reset(); // clear the existing nodes. + for (int i = 0; i < check->branches(); i++) + examining += (filename_tree *)check->branch(i); + } + + return NIL; // we found nothing that looked like that node. +} + +bool directory_tree::calculate(bool just_size) +{ return calculate(_real_tree, just_size); } + +bool directory_tree::calculate(filename_tree *start, bool just_size) +{ + FUNCDEF("calculate"); + dir_tree_iterator *ted = start_at(start, directory_tree::postfix); + // create our iterator to do a postfix traversal. why postfix? well, + // prefix has been used elsewhere and since it doesn't really matter what + // order we visit the nodes here, it's good to change up. + + int depth; // current depth in tree. + filename curr; // the current path the iterator is at. + filename_list *files; // the filenames held at the iterator. + + while (directory_tree::current_dir(*ted, curr)) { + // we have a good directory to show. +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("calcing node ") + curr.raw()); +#endif + files = directory_tree::access(*ted); + directory_tree::depth(*ted, depth); + for (int i = 0; i < files->elements(); i++) { + if (!files->borrow(i)->calculate(curr.raw(), just_size)) { + LOG(astring("failure to calculate ") + files->get(i)->text_form()); + } + } + + directory_tree::next(*ted); + } + + directory_tree::throw_out(ted); + return true; +} + +bool directory_tree::compare_trees(const directory_tree &source, + const directory_tree &target, filename_list &differences, + file_info::file_similarity how_to_compare) +{ + return compare_trees(source, astring::empty_string(), target, + astring::empty_string(), differences, how_to_compare); +} + +bool directory_tree::compare_trees(const directory_tree &source, + const astring &source_start_in, const directory_tree &target, + const astring &target_start_in, filename_list &differences, + file_info::file_similarity how_compare) +{ + FUNCDEF("compare_trees"); + differences.reset(); // prepare it for storage. + + // make sure we get canonical names to work with. + filename source_start(source_start_in); + filename target_start(target_start_in); + + dir_tree_iterator *ted = source.start(directory_tree::prefix); + // create our iterator to do a prefix traversal. + + astring real_source_start = source.path(); + if (source_start.raw().t()) { + // move to the right place. + real_source_start = real_source_start + filename::default_separator() + + source_start.raw(); + if (!directory_tree::jump_to(*ted, source_start.raw())) { + // can't even start comparing. + LOG(astring("failed to find source start in tree, given as ") + + source_start.raw()); + return false; + } + } + + filename curr; // the current path the iterator is at. + filename_list files; // the filenames held at the iterator. + + // calculate where our comparison point is on the source. + int source_pieces = 0; + { + string_array temp; + filename(real_source_start).separate(temp); + source_pieces = temp.length(); + } + + bool seen_zero_pieces = false; + while (directory_tree::current(*ted, curr, files)) { + // we're in a place in the source tree now. let's compare it with the + // target's recollection. + +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("curr dir in tree: ") + curr.raw()); +#endif + + string_array pieces; + curr.separate(pieces); // get the components of the current location. +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("name in pieces:") + pieces.text_form()); +#endif + pieces.zap(0, source_pieces - 1); + // snap the root components out of there. + + filename corresponding_name; + corresponding_name.join(pieces); +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("computed target name as: ") + corresponding_name); +#endif + filename original_correspondence(corresponding_name); + + if (!corresponding_name.raw().t()) { + if (seen_zero_pieces) { +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("breaking out now due to empty correspondence")); +#endif + break; + } + seen_zero_pieces = true; + } + if (target_start.raw().t()) { + corresponding_name = filename(target_start.raw() + + filename::default_separator() + corresponding_name.raw()); + } +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("target with start is: ") + corresponding_name); +#endif + + filename_tree *target_now = target.seek(corresponding_name.raw(), true); + if (!target_now) { + // that entire sub-tree is missing. add all of the files here into + // the list. +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("could not find dir in target for ") + curr.raw() + + " which we computed corresp as " + corresponding_name.raw()); +#endif + } + + // now scan across all the files that are in our source list. + for (int i = 0; i < files.elements(); i++) { + if (!target_now // there was no node, so we're adding everything... + || !target_now->_files.member_with_state(*files[i], how_compare) ) { + // ... or we need to add this file since it's missing. + +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("adding record: ") + files[i]->text_form()); +#endif + + file_info *new_record = new file_info(*files[i]); + // record the file time for use later in saving. + new_record->calculate(curr, true); + astring original = new_record->raw(); +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("current: ") + new_record->raw()); +#endif + + astring actual_name = source_start.raw(); +#ifdef DEBUG_DIRECTORY_TREE + if (actual_name.t()) LOG(astring("sname=") + actual_name); +#endif + if (actual_name.length()) actual_name += filename::default_separator(); + actual_name += original_correspondence.raw(); + if (actual_name.length()) actual_name += filename::default_separator(); + actual_name += new_record->raw(); +#ifdef DEBUG_DIRECTORY_TREE + if (actual_name.t()) LOG(astring("sname=") + actual_name); +#endif + (filename &)(*new_record) = filename(actual_name); + + astring targ_name = corresponding_name.raw(); +#ifdef DEBUG_DIRECTORY_TREE + if (targ_name.t()) LOG(astring("tname=") + targ_name); +#endif + if (targ_name.length()) targ_name += filename::default_separator(); + targ_name += original; +#ifdef DEBUG_DIRECTORY_TREE + if (targ_name.t()) LOG(astring("tname=") + targ_name); +#endif + + new_record->secondary(targ_name); + + differences += new_record; +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("came out as: ") + new_record->text_form()); +#endif + } + } + + // go to the next place. + directory_tree::next(*ted); + } + + directory_tree::throw_out(ted); + + return true; +} + +outcome directory_tree::find_common_root(const astring &finding, bool exists, + filename_tree * &found, astring &reassembled, string_array &pieces, + int &match_place) +{ +#ifdef DEBUG_DIRECTORY_TREE + FUNCDEF("find_common_root"); +#endif + // test the path to find what it is. + filename adding(finding); + if (exists && !adding.good()) + return common::BAD_INPUT; // not a good path. + int file_subtract = 0; // if it's a file, then we remove last component. + if (exists && !adding.is_directory()) file_subtract = 1; + + // break up the path into pieces. + pieces.reset(); + adding.separate(pieces); + + // break up our root into pieces; we must take off components that are + // already in the root. + string_array root_pieces; + filename temp_file(path()); + temp_file.separate(root_pieces); + + // locate the last place where the path we were given touches our tree. + // it could be totally new, partially new, or already contained. + filename_tree *last_match = _real_tree; // where the common root is located. + int list_length = pieces.length() - file_subtract; + reassembled = ""; + + // we must put all the pieces in that already come from the root. + for (int i = 0; i < root_pieces.length() - 1; i++) { + bool add_slash = false; + if (reassembled.length() && (reassembled[reassembled.end()] != '/') ) + add_slash = true; + if (add_slash) reassembled += "/"; + reassembled += pieces[i]; + if (reassembled[reassembled.end()] == ':') { +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("skipping drive component ") + reassembled); +#endif + continue; + } + } + +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("after pre-assembly, path is ") + reassembled); +#endif + + outcome to_return = common::NOT_FOUND; + + for (match_place = root_pieces.length() - 1; match_place < list_length; + match_place++) { + // add a slash if there's not one present already. + bool add_slash = false; + if (reassembled.length() && (reassembled[reassembled.end()] != '/') ) + add_slash = true; + // add the next component in to our path. + if (add_slash) reassembled += "/"; + reassembled += pieces[match_place]; + // special case for dos paths. + if (reassembled[reassembled.end()] == ':') { +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("skipping drive component ") + reassembled); +#endif + continue; + } + reassembled = filename(reassembled).raw(); // force compliance with OS. +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("now seeking ") + reassembled); +#endif + filename_tree *sought = seek(reassembled, false); + if (!sought) { +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("couldn't find ") + reassembled); +#endif + if (!exists && (match_place == list_length - 1)) { + // see if we can get a match on a file rather than a directory, but + // only if we're near the end of the compare. + if (last_match->_files.member(pieces[match_place])) { + // aha! a file match. + to_return = common::OKAY; + match_place--; + break; + } + } + match_place--; + break; + } else { + // record where we last had some success. +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("found subtree for ") + reassembled); +#endif + last_match = sought; + } + } + // this is a success, but our loop structure can put us one past the right + // place. + if (match_place >= list_length) { + match_place = list_length - 1; + to_return = common::OKAY; + } + + found = last_match; + return to_return; +} + +outcome directory_tree::add_path(const astring &new_item, bool just_size) +{ + FUNCDEF("add_path"); + // test the path to find out what it is. + filename adding(new_item); + if (!adding.good()) { + LOG(astring("non-existent new item! ") + new_item); + return common::BAD_INPUT; // not a good path. + } + int file_subtract = 0; // if it's a file, then we remove last component. + if (!adding.is_directory()) file_subtract = 1; +#ifdef DEBUG_DIRECTORY_TREE + if (file_subtract) LOG(astring("adding a file ") + new_item) + else LOG(astring("adding a directory ") + new_item); +#endif + + // find the common root, break up the path into pieces, and tell us where + // we matched. + string_array pieces; + filename_tree *last_match = NIL; + int comp_index; + astring reassembled; // this will hold the common root. + outcome ret = find_common_root(new_item, true, last_match, reassembled, + pieces, comp_index); + if (!last_match) { + LOG(astring("serious error finding common root for ") + new_item + + ", got NIL tree."); + return common::FAILURE; // something serious isn't right. + } + + if (!file_subtract) { + if (ret != common::OKAY) { + // if it's a new directory, we add a new node for traverse to work on. +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("now adding node for ") + reassembled); +#endif + filename_tree *new_branch = new filename_tree; + new_branch->_depth = last_match->_depth + 1; + last_match->attach(new_branch); + last_match = new_branch; + } else { +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("matched properly. reassembled set to ") + reassembled); +#endif + } + } + + if (file_subtract) { + if (ret != common::OKAY) { +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("common gave us posn of: ") + reassembled); +#endif + // handle the case for files now that we have our proper node. + string_array partial_pieces; + filename(reassembled).separate(partial_pieces); + int levels_missing = pieces.length() - partial_pieces.length(); + + // we loop over all the pieces that were missing in between the last + // common root and the file's final location. + for (int i = 0; i < levels_missing; i++) { +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("adding intermediate directory: ") + reassembled); +#endif + filename_tree *new_branch = new filename_tree; + new_branch->_depth = last_match->_depth + 1; + new_branch->_dirname = filename(reassembled).raw(); + last_match->attach(new_branch); + last_match = new_branch; + reassembled += astring("/") + pieces[partial_pieces.length() + i]; + reassembled = filename(reassembled).raw(); // canonicalize. + } + } + + if (!last_match->_files.find(pieces[pieces.last()])) { +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("adding new file ") + pieces[pieces.last()] + + " at " + reassembled); +#endif + file_info *to_add = new file_info(pieces[pieces.last()], 0); + to_add->calculate(reassembled, just_size); + last_match->_files += to_add; + } else { +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("not adding existing file ") + pieces[pieces.last()] + + " at " + reassembled); +#endif + } + } else { + // handle the case for directories. +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("doing traverse in ") + last_match->_dirname + + " to add " + reassembled); +#endif + traverse(reassembled, "*", *last_match); +//hmmm: maybe provide pattern capability instead of assuming all files. + calculate(last_match, just_size); + } + + return common::OKAY; +} + +outcome directory_tree::remove_path(const astring &zap_item) +{ +#ifdef DEBUG_DIRECTORY_TREE + FUNCDEF("remove_path"); +#endif + // find the common root, if one exists. if not, we're not going to do this. + string_array pieces; + filename_tree *last_match = NIL; + int comp_index; + astring reassembled; + outcome ret = find_common_root(zap_item, false, last_match, reassembled, + pieces, comp_index); + if (!last_match) return common::NOT_FOUND; + // if we didn't actually finish iterating to the file, then we're not + // whacking anything. + if (ret != common::OKAY) { +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("got error seeking ") + zap_item + " of " + + common::outcome_name(ret)); +#endif + return ret; + } + + if (comp_index == pieces.last()) { + // if the names match fully, then we're talking about a directory. +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("found directory match for ") + zap_item); +#endif + } else { +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("may have found file match for ") + zap_item); +#endif + filename to_seek(pieces[pieces.last()]); + if (!last_match->_files.member(to_seek)) { + // this file is not a member, so we must say it's not found. +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("couldn't find file match in common root for ") + zap_item); +#endif + return common::NOT_FOUND; + } else { + int indy = last_match->_files.locate(to_seek); +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("found match to remove for ") + zap_item); +#endif + last_match->_files.zap(indy, indy); + return common::OKAY; // done! + } + } + +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("going to whack node at: ") + last_match->_dirname.raw()); +#endif + + // we're whacking directories, so we need to take out last_match and below. + filename_tree *parent = (filename_tree *)last_match->parent(); + if (!parent || (last_match == _real_tree)) { + // this seems to be matching the whole tree. we disallow that. +#ifdef DEBUG_DIRECTORY_TREE + LOG("there's a problem whacking this node; it's the root."); +#endif + return common::BAD_INPUT; + } +#ifdef DEBUG_DIRECTORY_TREE + LOG(astring("pruning tree at ") + last_match->_dirname.raw()); +#endif + parent->prune(last_match); + WHACK(last_match); + + return common::OKAY; +} + +} //namespace. + + diff --git a/core/library/filesystem/directory_tree.h b/core/library/filesystem/directory_tree.h new file mode 100644 index 00000000..9c89ba43 --- /dev/null +++ b/core/library/filesystem/directory_tree.h @@ -0,0 +1,224 @@ +#ifndef DIRECTORY_TREE_CLASS +#define DIRECTORY_TREE_CLASS + +/*****************************************************************************\ +* * +* Name : directory_tree * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "directory.h" +#include "file_info.h" + +#include +#include +#include +#include + +namespace filesystem { + +// forward declarations. +class dir_tree_iterator; +class filename; +class filename_list; +class filename_tree; +class fname_tree_creator; + +//! An object that traverses directory trees and provides a view of all files. + +class directory_tree : public virtual basis::packable +{ +public: + directory_tree(); //!< constructs an empty tree. + + directory_tree(const basis::astring &path, const char *pattern = "*", + bool ignore_files = false); + //!< opens up the "path" specified and scans for files and subdirectories. + /*!< if the location was accessible, then the good() method returns true. + note that the "path" should just be a bare directory without any + wildcards attached. the "pattern" can be specified if you wish to + strain out just a subset of the files in the directory. note that + unlike the directory object, directory_tree applies the wildcard to + filenames only--all sub-directories are included. the pattern must meet + the same requirements that the operating system places on wildcard + patterns. if "ignore_files" is true, then no files are considered and + only the tree of directories is gathered. */ + + ~directory_tree(); + + DEFINE_CLASS_NAME("directory_tree"); + + bool good() const { return _scanned_okay; } + //!< returns true if the directory existed and we read its contents. + + const basis::astring &path() const; + //!< returns the root of the directory tree that we manage. + + bool reset(const basis::astring &path, const char *pattern = "*"); + //!< gets rid of any current files and rescans the directory at "path". + /*!< a new "pattern" can be specified at this time also. true is returned + if the process was started successfully at "path"; there might be + problems with subdirectories, but at least the "path" got validated. */ + + filename_tree *seek(const basis::astring &dir_name, bool ignore_initial) const; + //!< finds the "dir_name" in our tree. + /*!< locates the node that corresponds to the directory name contained in + "dir_name" and returns the filename_tree rooted at that node. if the + "ignore_initial" flag is true, then dir_name is expected to omit the + path() where "this" tree is rooted. */ + + virtual int packed_size() const; + //!< reports the size after packing up the tree. + virtual void pack(basis::byte_array &packed_form) const; + //!< packs the directory_tree into a byte_array. + virtual bool unpack(basis::byte_array &packed_form); + //!< unpacks the directory_tree from a byte_array. + + bool calculate(bool just_size); + //!< visits each file in the directory_tree and calculates its attributes. + /*!< the attributes include file size and checksum. if "just_size" is + true, then no checksum is computed. */ + + bool calculate(filename_tree *start, bool just_size); + //!< a calculate method that starts at a specific node rather than the root. + + basis::outcome add_path(const basis::astring &new_item, bool just_size = false); + //!< adds a "new_item" into the tree. + /*!< this is useful when one knows that new files exist under the + directory, but one doesn't want to recalculate the entire tree. the new + item will automatically be calculated. the item can be either a file or + directory that's under the root. the root directory name should not be + included in the "new_item". */ + + basis::outcome remove_path(const basis::astring &zap_item); + //!< removes the "zap_item" from the tree. + /*!< this only works for cases where one knows that an item has been + removed in the filesystem. if the item is still really there, then the + next rescan will put it back into the tree. */ + + static bool compare_trees(const directory_tree &source, + const directory_tree &target, filename_list &differences, + file_info::file_similarity how_to_compare); + //!< compares the tree in "source" with the tree in "target". + /*!< the two root names may be different, but everything below the root + in "source" will be checked against "target". the "differences" between + the two trees will be compiled. note that this does not perform any disk + access; it merely compares the two trees' current contents. + the "differences" list's members will have a primary filename set to + the source path and an alternate filename set to the location in the + target. the "how_to_compare" value will dictate what aspects of file + equality are used. */ + + static bool compare_trees(const directory_tree &source, + const basis::astring &source_start, const directory_tree &target, + const basis::astring &target_start, filename_list &differences, + file_info::file_similarity how_to_compare); + // compares the trees but not at their roots. the location on the source + // side is specified by "source_start", which must be a path found under + // the "source" tree. similarly, the "target_start" will be the location + // compared with the "source" + "source_start". the "diffs" will still + // be valid with respect to "source" rather than "source_start". + + void text_form(basis::astring &tree_dump, bool show_files = true); + //!< provides a visual representation of the tree in "tree_dump". + /*!< if "show_files" is not true, then only the directories will be + shown. */ + + // Note on the iterator functions: the iterator becomes invalid if the + // directory tree is reset. the only valid operation on the iterator + // at that point is to call throw_out(). + + enum traversal_types { + prefix, //!< prefix means that subnodes are processed after their parent. + infix, //!< infix (for binary trees) goes 1) left, 2) current, 3) right. + postfix //!< postfix means that subnodes are traversed first (depth first). + }; + + dir_tree_iterator *start(traversal_types type) const; + //!< starts an iterator on the directory tree. + + dir_tree_iterator *start_at(filename_tree *start, + traversal_types type) const; + //!< starts the iterator at a specific "start" node. + + static bool jump_to(dir_tree_iterator &scanning, const basis::astring &sub_path); + //!< seeks to a "sub_path" below the iterator's current position. + /*!< tries to take the iterator "scanning" down to a "sub_path" that is + underneath its current position. true is returned on success. */ + + static bool current_dir(dir_tree_iterator &scanning, filename &dir_name); + //!< sets "dir_name" to the directory name at the "scanning" location. + + static bool current(dir_tree_iterator &scanning, filename &dir_name, + structures::string_array &to_fill); + //!< retrieves the information for the iterator's current location. + /*!< fills the "to_fill" array with filenames that are found at the + "scanning" iterator's current position in the tree. the "dir_name" + for that location is also set. if the iterator has ended, then false + is returned. */ + + static bool current(dir_tree_iterator &scanning, filename &dir_name, + filename_list &to_fill); + //!< similar to the above but provides a list of the real underlying type. + + static filename_list *access(dir_tree_iterator &scanning); + //!< more dangerous operation that lets the actual list be manipulated. + /*!< NIL is returned if there was a problem accessing the tree + at the iterator position. */ + + static bool depth(dir_tree_iterator &scanning, int &depth); + //!< returns the current depth of the iterator. + /*!< a depth of zero means the iterator is at the root node for the tree. */ + + static bool children(dir_tree_iterator &scanning, int &children); + //!< returns the number of children for the current node. + + static bool next(dir_tree_iterator &scanning); + //!< goes to the next filename in the "scanning" iterator. + /*!< true is returned if there is an entry there. */ + + static void throw_out(dir_tree_iterator * &to_whack); + //!< cleans up an iterator that was previously opened with start(). + +private: + bool _scanned_okay; //!< did this directory work out? + basis::astring *_path; //!< the directory we're looking at. + basis::astring *_pattern; //!< the pattern used to find the files. + filename_tree *_real_tree; //!< the tree of directory contents we build. + bool _ignore_files; //!< true if they don't care about the files. + fname_tree_creator *_creator; //!< creates blank trees during unpacking. + + static filename_tree *goto_current(dir_tree_iterator &scanning); + //!< goes to the current node for "scanning" and returns the tree there. + /*!< if there are no nodes left, NIL is returned. */ + + void traverse(const basis::astring &path, const char *pattern, + filename_tree &add_to); + //!< recursively adds a "path" given the filename "pattern". + /*!< assuming that we want to add the files at "path" using the "pattern" + into the current node "add_to", we will also scoot down all sub-dirs + and recursively invoke traverse() to add those also. */ + + basis::outcome find_common_root(const basis::astring &path, bool exists, + filename_tree * &common_root, basis::astring &common_path, + structures::string_array &pieces, int &match_place); + //!< locates the node where this tree and "path" have membership in common. + /*!< if "exists" is true, then the "path" is tested for existence and + otherwise it's assumed that the path no longer exists. the "common_root" + is the last node that's in both places, the "common_path" is the name of + that location, the list of "pieces" is "path" broken into its components, + and the "match_place" is the index in "pieces" of the common node. */ +}; + +} //namespace. + +#endif + diff --git a/core/library/filesystem/file_info.cpp b/core/library/filesystem/file_info.cpp new file mode 100644 index 00000000..ed66d7b4 --- /dev/null +++ b/core/library/filesystem/file_info.cpp @@ -0,0 +1,230 @@ +/*****************************************************************************\ +* * +* Name : file_info * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1993-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "file_info.h" +#include "huge_file.h" + +#include +#include +#include +#include +#include +#include + +#include + +#define DEBUG_FILE_INFO + // uncomment for noisy version. + +#undef LOG +#define LOG(to_print) printf("%s::%s: %s\n", static_class_name(), func, astring(to_print).s()) + +using namespace basis; +using namespace structures; + +namespace filesystem { + +file_info::file_info() +: filename(astring::empty_string()), + _file_size(0), + _time(), + _checksum(), + c_secondary(), + c_attachment() +{} + +file_info::file_info(const filename &to_copy, double file_size, + const file_time &time, int checksum) +: filename(to_copy), + _file_size(file_size), + _time(time), + _checksum(checksum), + c_secondary(), + c_attachment() +{} + +file_info::file_info(const file_info &to_copy) +: filename(to_copy), + _file_size(to_copy._file_size), + _time(to_copy._time), + _checksum(to_copy._checksum), + c_secondary(to_copy.c_secondary), + c_attachment(to_copy.c_attachment) +{ +} + +file_info::~file_info() {} + +const byte_array &file_info::attachment() const { return c_attachment; } + +void file_info::attachment(const byte_array &new_attachment) +{ c_attachment = new_attachment; } + +const astring &file_info::secondary() const { return c_secondary; } + +void file_info::secondary(const astring &new_sec) { c_secondary = new_sec; } + +astring file_info::text_form() const +{ + astring to_return = raw() + + a_sprintf(", size=%0.f, chksum=%d", _file_size, _checksum); + if (c_secondary.t()) + to_return += astring(", 2ndary=") + c_secondary; + return to_return; +} + +bool file_info::calculate(const astring &prefix, bool just_size, int checksum_edge) +{ + FUNCDEF("calculate"); + filename full; + if (prefix.t()) full = prefix + "/" + *this; + else full = *this; + if (!full.exists()) { +#ifdef DEBUG_FILE_INFO + LOG(astring("failed to find file: ") + full.raw()); +#endif + return false; + } + // get time again. + _time = file_time(full); + +//#ifdef DEBUG_FILE_INFO +// astring temptext; +// _time.text_form(temptext); +// LOG(astring("file calculate on ") + full.raw() + " time=" + temptext); +//#endif + + // open the file for reading. + huge_file to_read(full.raw(), "rb"); + if (!to_read.good()) { +#ifdef DEBUG_FILE_INFO + LOG(astring("file has non-good status: ") + full.raw()); +#endif + return false; // why did that happen? + } + // set the size appropriately. + _file_size = to_read.length(); + if (just_size) + return true; // done for that case. + + // now read the file and compute a checksum. + uint16 curr_sum = 0; // the current checksum being computed. + byte_array chunk; // temporary chunk of data from file. + +//hmmm: make this optimization (hack) optional! + + // this algorithm takes a chunk on each end of the file for checksums. + // this saves us from reading a huge amount of data, although it will be + // fooled if a huge binary file is changed only in the middle and has the + // same size as before. for most purposes, this is not a problem, although + // databases that are fixed size might fool us. if records are written in + // the middle without updating the head or tail sections, then we're hosed. + + bool skip_tail = false; // true if we don't need the tail piece. + double head_start = 0, head_end = 0, tail_start = 0, + tail_end = _file_size - 1; + if (_file_size <= double(2 * checksum_edge)) { + // we're applying a rule for when the file is too small compared to + // the chunk factor doubled; we'll just read the whole file. + head_end = _file_size - 1; + skip_tail = true; + } else { + // here we compute the ending of the head piece and the beginning of + // the tail piece. each will be about checksum_edge in size. + head_end = minimum(_file_size / 2, double(checksum_edge)) - 1; + tail_start = _file_size - minimum(_file_size / 2, double(checksum_edge)); + } + + // read the head end of the file. + int size_read = 0; + outcome ret = to_read.read(chunk, int(head_end - head_start + 1), size_read); + if (ret != huge_file::OKAY) { +#ifdef DEBUG_FILE_INFO + LOG(astring("reading file failed: ") + full.raw()); +#endif + return false; // failed to read. + } + curr_sum = checksums::rolling_fletcher_checksum(curr_sum, chunk.observe(), + chunk.length()); + + // read the tail end of the file. + if (!skip_tail) { + to_read.seek(tail_start, byte_filer::FROM_START); + ret = to_read.read(chunk, int(tail_end - tail_start + 1), size_read); + if (ret != huge_file::OKAY) { +#ifdef DEBUG_FILE_INFO + LOG(astring("reading tail of file failed: ") + full.raw()); +#endif + return false; // failed to read. + } + curr_sum = checksums::rolling_fletcher_checksum(curr_sum, chunk.observe(), + chunk.length()); + } + + _checksum = curr_sum; + return true; +} + +int file_info::packed_size() const +{ + return filename::packed_size() + + structures::packed_size(_file_size) + + _time.packed_size() + + PACKED_SIZE_INT32 + + c_secondary.packed_size() + + structures::packed_size(c_attachment); +} + +void file_info::pack(byte_array &packed_form) const +{ + filename::pack(packed_form); + attach(packed_form, _file_size); + _time.pack(packed_form); + attach(packed_form, _checksum); + c_secondary.pack(packed_form); + attach(packed_form, c_attachment); +} + +bool file_info::unpack(byte_array &packed_form) +{ + if (!filename::unpack(packed_form)) + return false; + if (!detach(packed_form, _file_size)) + return false; + if (!_time.unpack(packed_form)) + return false; + if (!detach(packed_form, _checksum)) + return false; + if (!c_secondary.unpack(packed_form)) + return false; + if (!detach(packed_form, c_attachment)) + return false; + return true; +} + +file_info &file_info::operator = (const file_info &to_copy) +{ + if (this == &to_copy) + return *this; + (filename &)(*this) = (filename &)to_copy; + c_attachment = to_copy.c_attachment; + _time = to_copy._time; + _file_size = to_copy._file_size; + c_secondary = to_copy.c_secondary; + _checksum = to_copy._checksum; + return *this; +} + +} //namespace. + diff --git a/core/library/filesystem/file_info.h b/core/library/filesystem/file_info.h new file mode 100644 index 00000000..0e29be49 --- /dev/null +++ b/core/library/filesystem/file_info.h @@ -0,0 +1,96 @@ +#ifndef FILE_INFO_CLASS +#define FILE_INFO_CLASS + +/*****************************************************************************\ +* * +* Name : file_info * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1993-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "filename.h" +#include "file_time.h" + +#include +#include + +namespace filesystem { + +//! Encapsulates some measures and calculations based on a file's contents. + +class file_info : public filename +{ +public: + //! this enum encapsulates how files may be compared. + enum file_similarity { + EQUAL_NAME = 0, // we assume name equality is pre-eminent and always required. + EQUAL_CHECKSUM = 0x1, // the files have the same checksum, however computed. + EQUAL_TIMESTAMP = 0x2, // the files have exactly equal timestamps. + EQUAL_FILESIZE = 0x4, // the files have the same sizes. + EQUAL_CHECKSUM_TIMESTAMP_FILESIZE = EQUAL_CHECKSUM & EQUAL_TIMESTAMP & EQUAL_FILESIZE + }; + + double _file_size; //!< the size of the file. + file_time _time; //!< the file's access time. + int _checksum; //!< the checksum for the file. + + file_info(); //!< blank constructor. + + file_info(const filename &to_copy, double file_size, + const file_time &time = file_time(), int checksum = 0); + //!< to get the real file size, timestamp and checksum, invoke the calculate method. + + file_info(const file_info &to_copy); + + virtual ~file_info(); + + DEFINE_CLASS_NAME("file_info"); + + file_info &operator = (const file_info &to_copy); + + basis::astring text_form() const; + + bool calculate(const basis::astring &prefix, bool just_size_n_time, + int checksum_edge = 1 * basis::KILOBYTE); + //!< fills in the correct file size and checksum information for this file. + /*!< note that the file must exist for this to work. if "just_size_n_time" + is true, then the checksum is not calculated. if the "prefix" is not + empty, then it is used as a directory path that needs to be added to + the filename to make it completely valid. this is common if the + filename is stored relative to a path. the "checksum_edge" is used for + checksum calculations; only 2 * checksum_edge bytes will be factored in, + each part from the head and tail ends of the file. */ + + const basis::astring &secondary() const; + //!< observes the alternate form of the name. + void secondary(const basis::astring &new_sec); + //!< accesses the alternate form of the name. + + const basis::byte_array &attachment() const; + //!< returns the chunk of data optionally attached to the file's info. + /*!< this supports extending the file's record with extra data that might + be needed during processing. */ + void attachment(const basis::byte_array &new_attachment); + //!< sets the optional chunk of data hooked up to the file's info. + + // standard streaming operations. + virtual int packed_size() const; + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); + +private: + basis::astring c_secondary; //!< alternate filename for the main one. + basis::byte_array c_attachment; //!< extra information, if needed. +}; + +} //namespace. + +#endif + diff --git a/core/library/filesystem/file_time.cpp b/core/library/filesystem/file_time.cpp new file mode 100644 index 00000000..5f05a4fb --- /dev/null +++ b/core/library/filesystem/file_time.cpp @@ -0,0 +1,146 @@ +/*****************************************************************************\ +* * +* Name : file_time * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "file_time.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef __UNIX__ + #include +#endif +#ifdef __WIN32__ + #include +#endif + +#undef LOG +#define LOG(to_print) printf("%s::%s: %s\n", static_class_name(), func, astring(to_print).s()) + +using namespace basis; + +namespace filesystem { + +file_time::file_time() : _when(0) { reset(""); } + +file_time::file_time(FILE *the_FILE) : _when(0) { reset(the_FILE); } + +file_time::file_time(const time_t &t) : _when(t) {} + +file_time::file_time(const astring &filename) +: _when(0) +{ + FILE *ptr = fopen(filename.s(), "r"); + if (ptr) { + reset(ptr); + fclose(ptr); + } +} + +file_time::~file_time() {} + +bool file_time::set_time(const basis::astring &filename) +{ + // prepare the access time and modified time to match our stamp. + utimbuf held_time; + held_time.actime = raw(); + held_time.modtime = raw(); + // stuff our timestamp on the file. + return !utime(filename.s(), &held_time); +} + +void file_time::reset(const time_t &t) { _when = t; } + +void file_time::reset(const astring &filename) +{ + FILE *ptr = fopen(filename.s(), "r"); + if (ptr) { + reset(ptr); + fclose(ptr); + } +} + +void file_time::reset(FILE *the_FILE_in) +{ + FUNCDEF("reset"); + _when = 0; + FILE *the_file = (FILE *)the_FILE_in; + if (!the_file) { + return; + } + struct stat stat_buffer; + if (fstat(fileno(the_file), &stat_buffer) < 0) { + LOG("stat failure on file"); + } + _when = stat_buffer.st_mtime; +} + +int file_time::compare(const file_time &b) const +{ + if (_when > b._when) return 1; + else if (_when == b._when) return 0; + else return -1; +} + +bool file_time::less_than(const orderable &ft2) const +{ + const file_time *cast = dynamic_cast(&ft2); + if (!cast) return false; + return bool(compare(*cast) < 0); +} + +bool file_time::equal_to(const equalizable &ft2) const +{ + const file_time *cast = dynamic_cast(&ft2); + if (!cast) return false; + return bool(compare(*cast) == 0); +} + +void file_time::text_form(basis::base_string &time_string) const +{ + time_string.assign(basis::astring(class_name()) + basis::a_sprintf("@%x", _when)); +} + +void file_time::readable_text_form(basis::base_string &time_string) const +{ + tm *now = localtime(&_when); // convert to time round hereparts. + time_string.assign(a_sprintf("%d.%02d.%02d %02d:%02d:%02d", + now->tm_year+1900, now->tm_mon+1, now->tm_mday, now->tm_hour, now->tm_min, now->tm_sec)); +} + +// magic value unfortunately, but this is the length of a packed string with 10 characters. +// we do this because the size of 2^32 in decimal requires that many characters, and we also +// want to just have a fixed length chunk for this. we are not worried about larger pieces +// than that because we cannot conveniently handle them anyhow. +int file_time::packed_size() const { return 11; } + +void file_time::pack(byte_array &packed_form) const +{ a_sprintf("%010d", _when).pack(packed_form); } + +bool file_time::unpack(byte_array &packed_form) +{ + astring tempo; + if (!tempo.unpack(packed_form)) return false; + _when = tempo.convert(0L); + return true; +} + +} //namespace + diff --git a/core/library/filesystem/file_time.h b/core/library/filesystem/file_time.h new file mode 100644 index 00000000..0e89d356 --- /dev/null +++ b/core/library/filesystem/file_time.h @@ -0,0 +1,97 @@ +#ifndef FILE_TIME_CLASS +#define FILE_TIME_CLASS + +/*****************************************************************************\ +* * +* Name : file_time * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! A platform independent way to obtain the timestamp of a file. + +#include +#include +#include + +#include +#include + +namespace filesystem { + +class file_time +: public virtual basis::hoople_standard, + public virtual basis::orderable +{ +public: + file_time(); //!< sets up a bogus file_time object. + + file_time(FILE *the_FILE); + //!< sets up the file_time information given a the file stream of interest. + /*!< If the stream is NIL, then the file_time is set up with an invalid + time. */ + + file_time(const basis::astring &filename); + //!< this constructor operates on a file's name rather than a FILE stream. + + file_time(const time_t &init); + //!< starts the file_time with a particular "init" time. + +//hmmm: need a converter that sucks in an earth_time time_locus object. + + virtual ~file_time(); + + DEFINE_CLASS_NAME("file_time"); + + virtual void text_form(basis::base_string &time_string) const; + //!< returns a definitive but sorta ugly version of the file's time. + + virtual void readable_text_form(basis::base_string &time_string) const; + //!< sets "time_string" to a human readable form of the file's time. + + void reset(FILE *the_FILE); + //!< reacquires the time from a different FILE than constructed with. + /*!< this also can connect a FILE to the file_time object after using the + empty constructor. further, it can also be used to refresh a file's time + to account for changes in its timestamp. */ + + void reset(const basis::astring &filename); + //!< parallel version of reset() takes a file name instead of a stream. + + void reset(const time_t &init); + //!< parallel version of reset() takes a time_t instead of a stream. + + time_t raw() const { return _when; } + //!< provides the OS version of the file's timestamp. + + bool set_time(const basis::astring &filename); + //!< sets the time for the the "filename" to the currently held time. + + // Standard comparison operators between this file time and the file time + // "ft2". These are meaningless if either time is invalid. + virtual bool less_than(const basis::orderable &ft2) const; + virtual bool equal_to(const basis::equalizable &ft2) const; + + // supports streaming the time into and out of a byte array. + virtual int packed_size() const; + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); + +private: + time_t _when; //!< our record of the file's timestamp. + + int compare(const file_time &ft2) const; + //!< root comparison function for all the operators. +}; + +} //namespace. + +#endif + diff --git a/core/library/filesystem/filename.cpp b/core/library/filesystem/filename.cpp new file mode 100644 index 00000000..ef148ea8 --- /dev/null +++ b/core/library/filesystem/filename.cpp @@ -0,0 +1,544 @@ +/*****************************************************************************\ +* * +* Name : filename * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1993-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +// implementation note: the filename is kept canonicalized. any constructor +// or assignment operator should ensure this (except the blank constructor). + +#include "filename.h" + +#include +#include + +#include +#include +#include +#ifdef __UNIX__ + #include +#endif +#ifdef __WIN32__ + #include +#endif + +using namespace basis; +using namespace structures; + +class status_info : public stat +{ +}; + +namespace filesystem { + +#if defined(__WIN32__) || defined(__VMS__) + const char DEFAULT_SEPARATOR = '\\'; +#elif defined(__UNIX__) + const char DEFAULT_SEPARATOR = '/'; +#else + #error "We have no idea what the default path separator is." +#endif + +const char *NO_PARENT_DEFAULT = "."; + // used when no directory name can be popped off. + +filename::filename() +: astring(), + _had_directory(false) +{} + +filename::filename(const astring &name) +: astring(name), + _had_directory(true) +{ canonicalize(); } + +filename::filename(const astring &directory, const astring &name_of_file) +: astring(directory), + _had_directory(true) +{ + // if the directory is empty, use the current directory. + if (!directory) { + *this = astring(NO_PARENT_DEFAULT); + _had_directory = false; + } + // check for a slash on the end of the directory. add one if there is none + // currently. + bool add_slash = false; + if ( (directory[directory.end()] != '\\') + && (directory[directory.end()] != '/') ) add_slash = true; + if (add_slash) *this += DEFAULT_SEPARATOR; + *this += name_of_file; + canonicalize(); +} + +filename::filename(const filename &to_copy) +: astring(to_copy), + _had_directory(to_copy._had_directory) +{ canonicalize(); } + +filename::~filename() {} + +astring filename::default_separator() { return astring(DEFAULT_SEPARATOR, 1); } + +astring &filename::raw() { return *this; } + +const astring &filename::raw() const { return *this; } + +bool filename::good() const { return exists(); } + +bool filename::unlink() const { return ::unlink(observe()) == 0; } + +astring filename::null_device() +{ +#ifdef __WIN32__ + return "null:"; +#else + return "/dev/null"; +#endif +} + +bool filename::separator(char is_it) +{ return (is_it == pc_separator) || (is_it == unix_separator); } + +filename &filename::operator = (const filename &to_copy) +{ + if (this == &to_copy) return *this; + (astring &)(*this) = to_copy; + _had_directory = to_copy._had_directory; + return *this; +} + +filename &filename::operator = (const astring &to_copy) +{ + _had_directory = true; + if (this == &to_copy) return *this; + (astring &)(*this) = to_copy; + canonicalize(); + return *this; +} + +astring filename::pop() +{ + astring to_return = basename(); + filename parent_dir = parent(); + if (parent_dir.raw().equal_to(NO_PARENT_DEFAULT)) { + // we haven't gone anywhere. + return ""; // signal that nothing was removed. + } + *this = parent_dir; + return to_return; +} + +filename filename::parent() const { return dirname(); } + +void filename::push(const astring &to_push) +{ + *this = filename(*this, to_push); +} + +void filename::canonicalize() +{ + // turn all the non-default separators into the default. + bool found_sep = false; + for (int j = 0; j < length(); j++) { + if (separator(get(j))) { + found_sep = true; + put(j, DEFAULT_SEPARATOR); + } + } + + // if there wasn't a single directory separator, then they must not have + // specified any directory name for this filename (although it could itself + // be a directory). + if (!found_sep) _had_directory = false; + + // remove all occurrences of double separators except for the first + // double set, which could be a UNC filename. that's why the index below + // starts at one rather than zero. + bool saw_sep = false; + for (int i = 1; i < length(); i++) { + if (separator(get(i))) { + if (saw_sep) { + zap(i, i); + // two in a row is no good, except for the first two. + i--; // skip back one and try again. + continue; + } + saw_sep = true; + } else saw_sep = false; + } + + // we don't crop the last separator if the name's too small. for msdos + // names, that would be chopping a slash off the c:\ style name. + if (length() > 3) { + // zap any separators that are hiding on the end. + const int last = end(); + if (separator(get(last))) zap(last, last); + } else if ( (length() == 2) && (get(1) == ':') ) { + // special case for dos drive names. we turn it back into a valid + // directory rather than leaving it as just "X:". that form of the name + // means something else under dos/windows. + *this += astring(DEFAULT_SEPARATOR, 1); + } +} + +char filename::drive(bool interact_with_fs) const +{ + // first guess: if second letter's a colon, first letter's the drive. + if (length() < 2) + return '\0'; + if (get(1) == ':') + return get(0); + if (!interact_with_fs) + return '\0'; + + // otherwise, retrieve the file system's record for the file. + status_info fill; + if (!get_info(&fill)) + return '\0'; + return char('A' + fill.st_dev); +} + +astring filename::extension() const +{ + astring base(basename().raw()); + int posn = base.find('.', base.end(), true); + if (negative(posn)) + return ""; + return base.substring(posn + 1, base.length() - 1); +} + +astring filename::rootname() const +{ + astring base(basename().raw()); + int posn = base.find('.', base.end(), true); + if (negative(posn)) + return base; + return base.substring(0, posn - 1); +} + +bool filename::get_info(status_info *to_fill) const +{ + int ret = stat(observe(), to_fill); + if (ret) + return false; + return true; +} + +bool filename::is_directory() const +{ + status_info fill; + if (!get_info(&fill)) + return false; + return !!(fill.st_mode & S_IFDIR); +} + +bool filename::is_writable() const +{ + status_info fill; + if (!get_info(&fill)) + return false; + return !!(fill.st_mode & S_IWRITE); +} + +bool filename::is_readable() const +{ + status_info fill; + if (!get_info(&fill)) + return false; + return !!(fill.st_mode & S_IREAD); +} + +bool filename::is_executable() const +{ + status_info fill; + if (!get_info(&fill)) + return false; + return !!(fill.st_mode & S_IEXEC); +} + +int filename::find_last_separator(const astring &look_at) const +{ + int last_sep = -1; + int sep = 0; + while (sep >= 0) { + sep = look_at.find(DEFAULT_SEPARATOR, last_sep + 1); + if (sep >= 0) last_sep = sep; + } + return last_sep; +} + +filename filename::basename() const +{ + astring basename = *this; + int last_sep = find_last_separator(basename); + if (last_sep >= 0) basename.zap(0, last_sep); + return basename; +} + +filename filename::dirname() const +{ + astring dirname = *this; + int last_sep = find_last_separator(dirname); + // we don't accept ripping off the first slash. + if (last_sep >= 1) { + // we can rip the slash and suffix off to get the directory name. however, + // this might be in the form X: on windows. if they want the slash to + // remain, they can use the dirname that appends it. + dirname.zap(last_sep, dirname.end()); + } else { + if (get(0) == DEFAULT_SEPARATOR) { + // handle when we're up at the top of the filesystem. on unix, once + // you hit the root, you can keep going up but you still remain at + // the root. similarly on windoze, if there's no drive name in there. + dirname = astring(DEFAULT_SEPARATOR, 1); + } else { + // there's no slash at all in the filename any more. we assume that + // the directory is the current one, if no other information is + // available. this default is already used by some code. + dirname = NO_PARENT_DEFAULT; + } + } + return dirname; +} + +astring filename::dirname(bool add_slash) const +{ + astring tempname = dirname().raw(); + if (add_slash) tempname += DEFAULT_SEPARATOR; + return tempname; +} + +bool filename::exists() const +{ + if (is_directory()) + return true; + if (!length()) + return false; + return is_readable(); +/// byte_filer opened(observe(), "rb"); +/// return opened.good(); +} + +bool filename::legal_character(char to_check) +{ + switch (to_check) { + case ':': case ';': + case '\\': case '/': + case '*': case '?': case '$': case '&': case '|': + case '\'': case '"': case '`': + case '(': case ')': + case '[': case ']': + case '<': case '>': + case '{': case '}': + return false; + default: return true; + } +} + +void filename::detooth_filename(astring &to_clean, char replacement) +{ + for (int i = 0; i < to_clean.length(); i++) { + if (!legal_character(to_clean[i])) + to_clean[i] = replacement; + } +} + +int filename::packed_size() const +{ + return PACKED_SIZE_INT32 + astring::packed_size(); +} + +void filename::pack(byte_array &packed_form) const +{ + attach(packed_form, int(_had_directory)); + astring::pack(packed_form); +} + +bool filename::unpack(byte_array &packed_form) +{ + int temp; + if (!detach(packed_form, temp)) + return false; + _had_directory = temp; + if (!astring::unpack(packed_form)) + return false; + return true; +} + +void filename::separate(string_array &pieces) const +{ + pieces.reset(); + const astring &raw_form = raw(); + astring accumulator; // holds the names we find. + for (int i = 0; i < raw_form.length(); i++) { + if (separator(raw_form[i])) { + // this is a separator character, so eat it and add the accumulated + // string to the list. + if (!i || accumulator.length()) pieces += accumulator; + // now reset our accumulated text. + accumulator = astring::empty_string(); + } else { + // not a separator, so just accumulate it. + accumulator += raw_form[i]; + } + } + if (accumulator.length()) pieces += accumulator; +} + +void filename::join(const string_array &pieces) +{ + astring constructed_name; // we'll make a filename here. + for (int i = 0; i < pieces.length(); i++) { + constructed_name += pieces[i]; + if (!i || (i != pieces.length() - 1)) + constructed_name += DEFAULT_SEPARATOR; + } + *this = constructed_name; +} + +bool filename::base_compare_prefix(const filename &to_compare, + string_array &first, string_array &second) +{ + separate(first); + to_compare.separate(second); + // that case should never be allowed, since there are some bits missing + // in the name to be compared. + if (first.length() > second.length()) + return false; + + // compare each of the pieces. + for (int i = 0; i < first.length(); i++) { +#if defined(__WIN32__) || defined(__VMS__) + // case-insensitive compare. + if (!first[i].iequals(second[i])) + return false; +#else + // case-sensitive compare. + if (first[i] != second[i]) + return false; +#endif + } + return true; +} + +bool filename::compare_prefix(const filename &to_compare, astring &sequel) +{ + sequel = astring::empty_string(); // clean our output parameter. + string_array first; + string_array second; + if (!base_compare_prefix(to_compare, first, second)) + return false; + + // create the sequel string. + int extra_strings = second.length() - first.length(); + for (int i = second.length() - extra_strings; i < second.length(); i++) { + sequel += second[i]; + if (i != second.length() - 1) sequel += DEFAULT_SEPARATOR; + } + + return true; +} + +bool filename::compare_prefix(const filename &to_compare) +{ + string_array first; + string_array second; + return base_compare_prefix(to_compare, first, second); +} + +bool filename::base_compare_suffix(const filename &to_compare, + string_array &first, string_array &second) +{ + separate(first); + to_compare.separate(second); + // that case should never be allowed, since there are some bits missing + // in the name to be compared. + if (first.length() > second.length()) + return false; + + // compare each of the pieces. + for (int i = first.length() - 1; i >= 0; i--) { +//clean up this computation; the difference in lengths is constant--use that. + int distance_from_end = first.length() - 1 - i; + int j = second.length() - 1 - distance_from_end; +#if defined(__WIN32__) || defined(__VMS__) + // case-insensitive compare. + if (!first[i].iequals(second[j])) + return false; +#else + // case-sensitive compare. + if (first[i] != second[j]) + return false; +#endif + } + return true; +} + +bool filename::compare_suffix(const filename &to_compare, astring &prequel) +{ + prequel = astring::empty_string(); // clean our output parameter. + string_array first; + string_array second; + if (!base_compare_suffix(to_compare, first, second)) + return false; + + // create the prequel string. + int extra_strings = second.length() - first.length(); + for (int i = 0; i < extra_strings; i++) { + prequel += second[i]; + if (i != second.length() - 1) prequel += DEFAULT_SEPARATOR; + } + return true; +} + +bool filename::compare_suffix(const filename &to_compare) +{ + string_array first; + string_array second; + return base_compare_suffix(to_compare, first, second); +} + +bool filename::chmod(int write_mode, int owner_mode) const +{ + int chmod_value = 0; +#ifdef __UNIX__ + if (write_mode & ALLOW_READ) { + if (owner_mode & USER_RIGHTS) chmod_value |= S_IRUSR; + if (owner_mode & GROUP_RIGHTS) chmod_value |= S_IRGRP; + if (owner_mode & OTHER_RIGHTS) chmod_value |= S_IROTH; + } + if (write_mode & ALLOW_WRITE) { + if (owner_mode & USER_RIGHTS) chmod_value |= S_IWUSR; + if (owner_mode & GROUP_RIGHTS) chmod_value |= S_IWGRP; + if (owner_mode & OTHER_RIGHTS) chmod_value |= S_IWOTH; + } +//// chmod_value = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; +#elif defined(__WIN32__) + if (write_mode & ALLOW_READ) { + chmod_value |= _S_IREAD; + } + if (write_mode & ALLOW_WRITE) { + chmod_value |= _S_IWRITE; + } +#else + #error unsupported OS type currently. +#endif + int chmod_result = ::chmod(raw().s(), chmod_value); + if (chmod_result) { +// LOG(astring("there was a problem changing permissions on ") + raw()); + return false; + } + return true; +} + +} //namespace. + diff --git a/core/library/filesystem/filename.h b/core/library/filesystem/filename.h new file mode 100644 index 00000000..a6018ef5 --- /dev/null +++ b/core/library/filesystem/filename.h @@ -0,0 +1,247 @@ +#ifndef FILENAME_CLASS +#define FILENAME_CLASS + +/*****************************************************************************\ +* * +* Name : filename * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1993-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include + +// forward declarations. +class status_info; + +//hmmm: this doesn't really belong here, does it. + +// define useful constant for filesystem path length. +#ifndef MAX_ABS_PATH + #ifdef __WIN32__ + #include + #define MAX_ABS_PATH MAX_PATH + #else + #ifdef __APPLE__ + #include + #else + #include + #endif + #define MAX_ABS_PATH PATH_MAX + #endif +#endif + + +namespace filesystem { + +//! Provides operations commonly needed on file names. + +class filename : public basis::astring, public virtual basis::packable +{ +public: + filename(); //!< blank constructor. + filename(const basis::astring &name); + //!< creates a filename from any part of a full pathname, if possible. + /*!< if the name contains quotes, they are stripped out. */ + filename(const basis::astring &directory, const basis::astring &name_of_file); + //!< constructs a filename from a "directory" and the "name_of_file". + /*!< the "name_of_file" can itself be a directory. */ + filename(const filename &to_copy); //!< copy constructor. + + virtual ~filename(); + + bool good() const; + //!< returns true if the filename seems to be valid. + /*!< this means that not only was the pathname parsed and found valid, + but the file actually exists. */ + + const basis::astring &raw() const; + //!< returns the astring that we're holding onto for the path. + basis::astring &raw(); + //!< accesses the astring that we're holding onto for the path. + /*!< important note: if you change the string with this non-const raw() + method, you MUST call canonicalize() on it again afterwards. */ + + filename &operator = (const filename &to_copy); + //!< provides assignment for this object, plus a simple string. + filename &operator = (const basis::astring &to_copy); + //!< provides assignment for this object, plus a simple string. + /*!< the latter version invokes canonicalize to clean the string up. */ + + void canonicalize(); + //!< cleans up the filename as needed for the current operating system. + /*!< reforms the name by replacing any alternate directory separators with + the operating system's preferred character. */ + + bool exists() const; + //!< returns true if the file exists. + + bool unlink() const; + //!< actually removes the file, if possible. + /*!< if the file was successfully deleted, then true is returned. */ + + filename parent() const; + //!< returns the parent filename for this one. + + basis::astring pop(); + //!< removes the deepest component of the pathname. + /*!< the component might be a file or directory name, but popping beyond + the top-level directory will not succeed. the returned string contains + the component that was removed. it will be a blank string if nothing + could be popped. */ + + void push(const basis::astring &to_push); + //!< pushes a new filename onto the current pathname. + /*!< this only makes sense as a real pathname if this is currently a + directory name and the component "to_push" is a child of that directory + (or one intends to create that component as a child). this is the + opposite of pop. */ + + filename basename() const; + //!< returns the base of the filename; no directory. + filename dirname() const; + //!< returns the directory for the filename. + /*!< if no directory name can be found in the filename, then "." is + returned. */ + basis::astring dirname(bool add_slash) const; + //!< returns the directory for the filename and optionally adds a slash. + /*!< if "add_slash" is true, then the default directory separator will be + present on the end of the string. */ + bool had_directory() const { return _had_directory; } + //!< returns true if the name that we were given had a non-empty directory. + /*!< this allows one to distinguish between a file with the current + directory (.) attached and a file with no directory specified. */ + + char drive(bool interact_with_fs = false) const; + //!< returns the drive letter for the file, without the colon. + /*!< this only makes sense for a fully qualified MS-DOS style name. if no + drive letter is found, then '\0' is returned. if "interact_with_fs" is + true, then the file system will be checked for the actual drive if no + drive letter was found in the contents. */ + + basis::astring extension() const; + //!< returns the extension for the file, if one is present. + + basis::astring rootname() const; + //!< returns the root part of the basename without an extension. + + // status functions return true if the characteristic embodied in + // the name is also true. + + bool is_directory() const; + bool is_writable() const; + bool is_readable() const; + bool is_executable() const; + + enum write_modes { + ALLOW_NEITHER = 0x0, + ALLOW_READ = 0x1, ALLOW_WRITE = 0x2, + ALLOW_BOTH = ALLOW_READ | ALLOW_WRITE + }; + + enum ownership_modes { + NO_RIGHTS = 0x0, + USER_RIGHTS = 0x1, GROUP_RIGHTS = 0x2, OTHER_RIGHTS = 0x4, + ALL_RIGHTS = USER_RIGHTS | GROUP_RIGHTS | OTHER_RIGHTS + }; + + bool chmod(int write_mode, int owner_mode) const; + //!< changes the access rights on the file. + + //! the default separator for directories per operating system. + /*! the PC uses the backward slash to separate file and directory names from + each other, while Unix uses the forward slash. */ + enum directory_separator { pc_separator = '\\', unix_separator = '/' }; + + static bool separator(char is_it); + //!< returns true if the character "is_it" in question is a separator. + + static basis::astring default_separator(); + //!< returns the default separator character for this OS. + + static bool legal_character(char to_check); + //!< returns true if "to_check" is a valid character in a filename. + /*!< this does not consider separator characters; it only looks at the + the name components. also, it is appropriate for the union of the + operating systems we support. */ + + static void detooth_filename(basis::astring &to_clean, char replacement = '_'); + //!< takes any known illegal file system characters out of "to_clean". + /*!< this prepares "to_clean" for use as a component in a larger filename + by ensuring that the file system will not reject the name (as long as a + suitable directory path is prepended to the name and permissions allow + the file to be created or accessed). the "replacement" is used as the + character that is substituted instead of illegal characters. */ + + void separate(structures::string_array &pieces) const; + //!< breaks the filename into its component directories. + /*!< this returns an array containing the component names. the last + component, unless the filename held is actually a directory, should be the + name of the file. if the first character is a directory, then the first + component will be empty. */ + + void join(const structures::string_array &pieces); + //!< undoes a separate() operation to get the filename back. + /*!< "this" is set to a filename made from each of the "pieces". if there + are any directory separators inside the pieces, then they will be removed + by canonicalize(). */ + + // these implement the packing functionality. + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); + virtual int packed_size() const; + + bool compare_prefix(const filename &to_compare, basis::astring &sequel); + //!< examines "this" filename to see if it's a prefix of "to_compare". + /*!< this returns true if all of "this" is the same as the first portion + of "to_compare". that is, if "this" is a prefix of "to_compare", then + true is returned. this will always fail if there are fewer components in + "to_compare". it will always succeed if the two filenames are identical. + on success, the "sequel" is set to the portion of "to_compare" that's + not included in this filename. */ + + bool compare_prefix(const filename &to_compare); + //!< this simpler form doesn't bother with computing the sequel. + + bool compare_suffix(const filename &to_compare, basis::astring &prequel); + //!< compares the back end of a filename to this. + /*!< this is similar to compare_prefix() but it checks to see if the + back end of "this" filename is the same as "to_compare". if "this" is + longer than "to_compare", then failure occurs. only if all of the bits + in "this" are seen in the back of "to_compare" is true returned. */ + + bool compare_suffix(const filename &to_compare); + + static basis::astring null_device(); + //!< returns the name for the black hole device that consumes all input, i.e. /dev/null. + +private: + bool _had_directory; //!< true if _some_ directory was specified on init. +/// basis::astring *_contents; //!< the full path is held here. + + int find_last_separator(const basis::astring &look_at) const; + //!< locates the last separator character in the filename. + + bool get_info(status_info *to_fill) const; + //!< returns information for the filename. + + // helper functions do the real work for comparing. + bool base_compare_prefix(const filename &to_compare, structures::string_array &first, + structures::string_array &second); + bool base_compare_suffix(const filename &to_compare, structures::string_array &first, + structures::string_array &second); +}; + +} //namespace. + +#endif + diff --git a/core/library/filesystem/filename_list.cpp b/core/library/filesystem/filename_list.cpp new file mode 100644 index 00000000..24d1522e --- /dev/null +++ b/core/library/filesystem/filename_list.cpp @@ -0,0 +1,170 @@ +/*****************************************************************************\ +* * +* Name : filename_list * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "filename_list.h" + +#include +#include + +using namespace basis; +using namespace structures; +using namespace textual; + +namespace filesystem { + +filename_list::filename_list() : amorph() {} + +filename_list &filename_list::operator =(const filename_list &to_copy) +{ + if (this == &to_copy) return *this; + reset(); + for (int i = 0; i < to_copy.elements(); i++) { + append(new file_info(*to_copy.get(i))); + } + return *this; +} + +int filename_list::total_files() const { return elements(); } + +int filename_list::packed_size() const +{ return amorph_packed_size(*this); } + +void filename_list::pack(byte_array &packed_form) const +{ amorph_pack(packed_form, *this); } + +bool filename_list::unpack(byte_array &packed_form) +{ return amorph_unpack(packed_form, *this); } + +double filename_list::total_size() const +{ + double to_return = 0; + for (int i = 0; i < elements(); i++) + to_return += get(i)->_file_size; + return to_return; +} + +bool filename_list::calculate_progress(const filename &file, + double current_offset, int ¤t_file, double ¤t_size) +{ + current_file = 0; + current_size = 0; + int posn = locate(file); + if (negative(posn)) { + if (file.raw().t()) return false; // not a member. + // they're at the start of the run. + current_file = 1; + return true; + } + current_file = posn + 1; // position zero in array means file number 1. + double size_finished = 0; + // iterate on all files before the current one. + for (int i = 0; i < posn; i++) { + size_finished += get(i)->_file_size; + } + current_size = size_finished + current_offset; + return true; +} + +filename_list &filename_list::operator = (const string_array &to_copy) +{ + reset(); + for (int i = 0; i < to_copy.length(); i++) { + append(new file_info(to_copy[i], 0)); + } + return *this; +} + +void filename_list::fill(string_array &to_fill) +{ + to_fill.reset(); + for (int i = 0; i < elements(); i++) { + to_fill += get(i)->raw(); + } +} + +const file_info *filename_list::find(const filename &to_check) const +{ + for (int i = 0; i < elements(); i++) { +#if defined(__WIN32__) || defined(__VMS__) + if (to_check.raw().iequals(get(i)->raw())) return get(i); +#else + if (to_check.raw() == get(i)->raw()) return get(i); +#endif + } + return NIL; +} + +int filename_list::locate(const filename &to_find) const +{ + for (int i = 0; i < elements(); i++) { +#if defined(__WIN32__) || defined(__VMS__) + if (to_find.raw().iequals(get(i)->raw())) return i; +#else + if (to_find.raw() == get(i)->raw()) return i; +#endif + } + return common::NOT_FOUND; +} + +bool filename_list::member(const filename &to_check) const +{ + for (int i = 0; i < elements(); i++) { +#if defined(__WIN32__) || defined(__VMS__) + if (to_check.raw().iequals(get(i)->raw())) return true; +#else + if (to_check.raw() == get(i)->raw()) return true; +#endif + } + return false; +} + +bool filename_list::member_with_state(const file_info &to_check, file_info::file_similarity how) +{ + for (int i = 0; i < elements(); i++) { +#if defined(__WIN32__) || defined(__VMS__) + if (to_check.raw().iequals(get(i)->raw())) { +#else + if (to_check.raw() == get(i)->raw()) { +#endif + // once we have matched a name, the other checks will cause us to + // reject any other potential matches after this one if the requested + // check fails. + if ((how & file_info::EQUAL_FILESIZE) && (to_check._file_size != get(i)->_file_size) ) + return false; + if ((how & file_info::EQUAL_TIMESTAMP) && (to_check._time != get(i)->_time) ) + return false; + if ((how & file_info::EQUAL_CHECKSUM) && (to_check._checksum != get(i)->_checksum) ) + return false; + return true; + } + } + return false; +} + +astring filename_list::text_form() const +{ + astring to_return; +//hmmm: a length limit might be nice? + for (int i = 0; i < elements(); i++) { + to_return += a_sprintf("%d. ", i + 1); + if (get(i)) + to_return += get(i)->text_form(); + if (i != elements() - 1) + to_return += parser_bits::platform_eol_to_chars(); + } + return to_return; +} + +} //namespace. + diff --git a/core/library/filesystem/filename_list.h b/core/library/filesystem/filename_list.h new file mode 100644 index 00000000..f5834d79 --- /dev/null +++ b/core/library/filesystem/filename_list.h @@ -0,0 +1,90 @@ +#ifndef FILENAME_LIST_CLASS +#define FILENAME_LIST_CLASS + +/*****************************************************************************\ +* * +* Name : filename_list * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! Implements a list of filenames. +/*! + This is based on an amorph so that adding to the list is efficient. + The underlying type held is actually a file_info rather than a filename. + This should not impose much extra overhead. + + Note: this is a heavyweight header; it shouldn't be used in other headers. +*/ + +#include "file_info.h" + +#include +#include + +namespace filesystem { + +class filename_list +: public structures::amorph, public virtual basis::packable +{ +public: + filename_list(); + + filename_list &operator =(const filename_list &to_copy); + + int total_files() const; + //!< returns the number of files currently held in the list. + + double total_size() const; + //!< returns the full size of all files listed. + + bool calculate_progress(const filename &file, double current_offset, + int ¤t_file, double ¤t_size); + //!< given the last "file" and position, this returns current positioning. + /*!< the "current_file" is set to the index of the "file" in the list + (using 1-based numbering for a 1,2,3,... series), and the "current_size" + is set to the total amount of bytes processed so far. */ + + filename_list &operator = (const structures::string_array &to_copy); + + void fill(structures::string_array &to_fill); + //!< stuffs the array "to_fill" with the filesnames from our list. + + const file_info *find(const filename &to_check) const; + //!< locates the record of information for the filename "to_check". + /*!< do not modify the returned object. it contains the current state + of the file in question. if the file wasn't in the list, then NIL is + returned. */ + + int locate(const filename &to_find) const; + //! finds the index for "to_find" or returns a negative number. + + bool member(const filename &to_check) const; + //!< returns true if "to_check" is listed here. + + bool member_with_state(const file_info &to_check, file_info::file_similarity comparison_method); + //!< returns true if the file "to_check" exists in the list with appropriate equivalence. + /*!< this will fail if the file name isn't present at all. and then it will also not return + true if the size is different (when EQUAL_FILESIZE is true), when the timestamp is different + (when EQUAL_TIMESTAMP is true), and when the checksum is different (you get the idea). */ + + basis::astring text_form() const; + + virtual int packed_size() const; + + virtual void pack(basis::byte_array &packed_form) const; + + virtual bool unpack(basis::byte_array &packed_form); +}; + +} //namespace. + +#endif + diff --git a/core/library/filesystem/filename_tree.cpp b/core/library/filesystem/filename_tree.cpp new file mode 100644 index 00000000..04a42030 --- /dev/null +++ b/core/library/filesystem/filename_tree.cpp @@ -0,0 +1,52 @@ +/*****************************************************************************\ +* * +* Name : filename_tree * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "filename_tree.h" + +#include + +using namespace basis; +using namespace nodes; +using namespace structures; + +namespace filesystem { + +nodes::packable_tree *fname_tree_creator::create() { return new filename_tree; } + +////////////// + +filename_tree::filename_tree() : _depth(0) {} + +filename_tree::~filename_tree() { _dirname = ""; _files.reset(); } + +int filename_tree::packed_size() const { + return PACKED_SIZE_INT32 + _dirname.packed_size() + _files.packed_size(); +} + +void filename_tree::pack(byte_array &packed_form) const { + structures::attach(packed_form, _depth); + _dirname.pack(packed_form); + _files.pack(packed_form); +} + +bool filename_tree::unpack(byte_array &packed_form) { + if (!structures::detach(packed_form, _depth)) return false; + if (!_dirname.unpack(packed_form)) return false; + if (!_files.unpack(packed_form)) return false; + return true; +} + +} //namespace. + + diff --git a/core/library/filesystem/filename_tree.h b/core/library/filesystem/filename_tree.h new file mode 100644 index 00000000..7d857d06 --- /dev/null +++ b/core/library/filesystem/filename_tree.h @@ -0,0 +1,63 @@ +#ifndef FILENAME_TREE_CLASS +#define FILENAME_TREE_CLASS + +/*****************************************************************************\ +* * +* Name : filename_tree * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! This is a support class for the directory_tree. +/*! + This class has been exported from directory_tree's implementation to + avoid redundant code for iteration and such. + Note: this is a heavy-weight header that should not be included in other + headers. +*/ + +#include "filename_list.h" + +#include + +namespace filesystem { + +class filename_tree : public nodes::packable_tree +{ +public: + filename _dirname; //!< the full directory name at this position. + filename_list _files; //!< the filenames that are at this node in the tree. + int _depth; //!< how far below root node are we. + + filename_tree(); + + virtual ~filename_tree(); + + virtual int packed_size() const; + + virtual void pack(basis::byte_array &packed_form) const; + + virtual bool unpack(basis::byte_array &packed_form); +}; + +//! this is the tree factory used in the recursive_unpack. +/*! it meets our needs for regenerating these objects from a streamed form. */ +class fname_tree_creator : public nodes::packable_tree_factory +{ +public: +//// virtual ~fname_tree_creator() {} + virtual nodes::packable_tree *create(); + //!< implements the create() method for filename_trees to support packing. +}; + +} //namespace. + +#endif + diff --git a/core/library/filesystem/heavy_file_ops.cpp b/core/library/filesystem/heavy_file_ops.cpp new file mode 100644 index 00000000..1e63ba31 --- /dev/null +++ b/core/library/filesystem/heavy_file_ops.cpp @@ -0,0 +1,334 @@ +/*****************************************************************************\ +* * +* Name : heavy file operations * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "directory.h" +#include "filename.h" +#include "filename_list.h" +#include "heavy_file_ops.h" +#include "huge_file.h" + +#include +#include +#include + +using namespace basis; +using namespace structures; + +namespace filesystem { + +//#define DEBUG_HEAVY_FILE_OPS + // uncomment for noisier debugging. + +#undef LOG +#ifdef DEBUG_HEAVY_FILE_OPS + #include + #define LOG(to_print) printf("%s::%s: %s\n", static_class_name(), func, astring(to_print).s()) +#else + #define LOG(s) {if(!!s){}} +#endif + +////////////// + +file_transfer_header::file_transfer_header(const file_time &time_stamp) +: _filename(), + _byte_start(0), + _length(0), + _time(time_stamp) +{ +} + +astring file_transfer_header::text_form() const +{ + astring time_text; + _time.text_form(time_text); + return astring("file=") + _filename + + a_sprintf(" start=%d len=%d stamp=", _byte_start, _length) + + time_text; +} + +astring file_transfer_header::readable_text_form() const +{ + astring time_text; + _time.readable_text_form(time_text); + return _filename + + a_sprintf(" [%d bytes, mod ", _length) + + time_text + "]"; +} + +void file_transfer_header::pack(byte_array &packed_form) const +{ + _filename.pack(packed_form); + attach(packed_form, _byte_start); + attach(packed_form, _length); + _time.pack(packed_form); +} + +bool file_transfer_header::unpack(byte_array &packed_form) +{ + if (!_filename.unpack(packed_form)) return false; + if (!detach(packed_form, _byte_start)) return false; + if (!detach(packed_form, _length)) return false; + if (!_time.unpack(packed_form)) return false; + return true; +} + +int file_transfer_header::packed_size() const +{ +byte_array temp; +attach(temp, _byte_start); +//hmmm: really ugly above; we should get a more exact way to know the size of +// packed doubles. + return _filename.length() + 1 + + temp.length() + + sizeof(int) + + _time.packed_size(); +} + +////////////// + +const size_t heavy_file_operations::COPY_CHUNK_FACTOR = 1 * MEGABYTE; + +size_t heavy_file_operations::copy_chunk_factor() +{ return COPY_CHUNK_FACTOR; } + +heavy_file_operations::~heavy_file_operations() {} + // we only need this due to our use of the root_object class_name support. + +const char *heavy_file_operations::outcome_name(const outcome &to_name) +{ + switch (to_name.value()) { + case SOURCE_MISSING: return "SOURCE_MISSING"; + case TARGET_ACCESS_ERROR: return "TARGET_ACCESS_ERROR"; + case TARGET_DIR_ERROR: return "TARGET_DIR_ERROR"; + default: return common::outcome_name(to_name); + } +} + +outcome heavy_file_operations::copy_file(const astring &source, + const astring &destination, int copy_chunk_factor) +{ +#ifdef DEBUG_HEAVY_FILE_OPS + FUNCDEF("copy_file"); +#endif + // check that the source exists... + filename source_path(source); + if (!source_path.exists()) return SOURCE_MISSING; + file_time source_time(source_path); // get the time on the source. + + // make sure the target directory exists... + filename target_path(destination); + filename targ_dir = target_path.dirname(); + if (!directory::recursive_create(targ_dir.raw())) return TARGET_DIR_ERROR; + + // open the source for reading. + huge_file source_file(source, "rb"); + if (!source_file.good()) return SOURCE_MISSING; +//hmmm: could be source is not accessible instead. + + // open target file for writing. + huge_file target_file(destination, "wb"); + if (!target_file.good()) return TARGET_ACCESS_ERROR; + + byte_array chunk; + int bytes_read = 0; + outcome ret; + while ( (ret = source_file.read(chunk, copy_chunk_factor, bytes_read)) + == huge_file::OKAY) { + int bytes_stored; + ret = target_file.write(chunk, bytes_stored); + if (bytes_stored != bytes_read) return TARGET_ACCESS_ERROR; + if (source_file.eof()) break; // time to escape. + } + + // set the time on the target file from the source's time. + source_time.set_time(target_path); + +#ifdef DEBUG_HEAVY_FILE_OPS + astring time; + source_time.text_form(time); + LOG(astring("setting file time for ") + source + " to " + time); +#endif + + return OKAY; +} + +outcome heavy_file_operations::write_file_chunk(const astring &target, + double byte_start, const byte_array &chunk, bool truncate, + int copy_chunk_factor) +{ +#ifdef DEBUG_HEAVY_FILE_OPS +// FUNCDEF("write_file_chunk"); +#endif + if (byte_start < 0) return BAD_INPUT; + + filename targ_name(target); + if (!directory::recursive_create(targ_name.dirname().raw())) + return TARGET_DIR_ERROR; + + if (!targ_name.exists()) { + huge_file target_file(target, "w"); + } + + huge_file target_file(target, "r+b"); + // open the file for updating (either read or write). + if (!target_file.good()) return TARGET_ACCESS_ERROR; + double curr_len = target_file.length(); + + if (curr_len < byte_start) { + byte_array new_chunk; + while (curr_len < byte_start) { + target_file.seek(0, byte_filer::FROM_END); // go to the end of the file. + new_chunk.reset(minimum(copy_chunk_factor, + int(curr_len - byte_start + 1))); + int written; + outcome ret = target_file.write(new_chunk, written); + if (written < new_chunk.length()) return TARGET_ACCESS_ERROR; + curr_len = target_file.length(); + } + } + target_file.seek(byte_start, byte_filer::FROM_START); + // jump to the proper location in the file. + int wrote; + outcome ret = target_file.write(chunk, wrote); + if (wrote != chunk.length()) return TARGET_ACCESS_ERROR; + if (truncate) { + target_file.truncate(); + } + return OKAY; +} + +bool heavy_file_operations::advance(const filename_list &to_transfer, + file_transfer_header &last_action) +{ +#ifdef DEBUG_HEAVY_FILE_OPS + FUNCDEF("advance"); +#endif + int indy = to_transfer.locate(last_action._filename); + if (negative(indy)) return false; // error. + if (indy == to_transfer.elements() - 1) return false; // done. + const file_info *currfile = to_transfer.get(indy + 1); + last_action._filename = currfile->raw(); + last_action._time = currfile->_time; + +#ifdef DEBUG_HEAVY_FILE_OPS + if (currfile->_time == file_time(time_t(0))) + LOG(astring("failed for ") + currfile->raw() + " -- has zero file time"); +#endif + + last_action._byte_start = 0; + last_action._length = 0; + return true; +} + +outcome heavy_file_operations::buffer_files(const astring &source_root, + const filename_list &to_transfer, file_transfer_header &last_action, + byte_array &storage, int maximum_bytes) +{ +#ifdef DEBUG_HEAVY_FILE_OPS +// FUNCDEF("buffer_files"); +#endif + storage.reset(); // clear out the current contents. + + if (!to_transfer.elements()) { + // we seem to be done. + return OKAY; + } + + outcome to_return = OKAY; + + // start filling the array with bytes from the files. + while (storage.length() < maximum_bytes) { + double remaining_in_array = maximum_bytes - storage.length() + - last_action.packed_size(); + if (remaining_in_array < 128) { + // ensure that we at least have a reasonable amount of space left + // for storing into the array. + break; + } + + // find the current file we're at, as provided in record. + if (!last_action._filename) { + // no filename yet. assume this is the first thing we've done. + const file_info *currfile = to_transfer.get(0); + last_action._filename = currfile->raw(); + last_action._time = currfile->_time; + last_action._byte_start = 0; + last_action._length = 0; + } + + const file_info *found = to_transfer.find(last_action._filename); + if (!found) { + // they have referenced a file that we don't have. that's bad news. + return BAD_INPUT; + } + + astring full_file = source_root + "/" + last_action._filename; + huge_file current(full_file, "rb"); + if (!current.good()) { + // we need to skip this file. + if (!advance(to_transfer, last_action)) break; + continue; + } + + if ((last_action._byte_start + last_action._length >= current.length()) + && current.length()) { + // this file is done now. go to the next one. + if (!advance(to_transfer, last_action)) break; + continue; + } + + // calculate the largest piece remaining of that file that will fit in the + // allotted space. + double new_start = last_action._byte_start + last_action._length; + double remaining_in_file = current.length() - new_start; + if (remaining_in_file < 0) remaining_in_file = 0; + double new_len = minimum(remaining_in_file, remaining_in_array); + + // pack this new piece of the file. + current.seek(new_start, byte_filer::FROM_START); + byte_array new_chunk; + int bytes_read = 0; + outcome ret = current.read(new_chunk, int(new_len), bytes_read); + if (bytes_read != new_len) { + if (!bytes_read) { + // some kind of problem reading the file. + if (!advance(to_transfer, last_action)) break; + continue; + } +//why would this happen? just complain, i guess. + } + + // update the record since it seems we're successful here. + last_action._byte_start = new_start; + last_action._length = int(new_len); + + // add in this next new chunk of file. + last_action.pack(storage); // add the header. + storage += new_chunk; // add the new stuff. + + if (!current.length()) { + // ensure we don't get stuck redoing zero length files, which we allowed + // to go past their end above (since otherwise we'd never see them). + if (!advance(to_transfer, last_action)) break; + continue; + } + + // just keep going, if there's space... + } + + return to_return; +} + +} //namespace. + diff --git a/core/library/filesystem/heavy_file_ops.h b/core/library/filesystem/heavy_file_ops.h new file mode 100644 index 00000000..add49683 --- /dev/null +++ b/core/library/filesystem/heavy_file_ops.h @@ -0,0 +1,122 @@ +#ifndef HEAVY_FILE_OPERATIONS_CLASS +#define HEAVY_FILE_OPERATIONS_CLASS + +/*****************************************************************************\ +* * +* Name : heavy file operations * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "filename_list.h" + +#include +#include + +namespace filesystem { + +//! describes one portion of an ongoing file transfer. +/*! this is just a header describing an attached byte package. it is expected +that the bytes follow this in the communication stream. */ + +class file_transfer_header : public basis::packable +{ +public: + basis::astring _filename; //!< the name of the file being transferred. + double _byte_start; //!< the starting location in the file being sent. + int _length; //!< the length of the transferred piece. + file_time _time; //!< the timestamp on the file. + + DEFINE_CLASS_NAME("file_transfer_header"); + + file_transfer_header(const file_time &time_stamp); + //!< refactored to force addition of the time_stamp. + + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); + + virtual int packed_size() const; + + basis::astring text_form() const; + +//hmmm: this could live in lots of other places. file_info for one. + basis::astring readable_text_form() const; + //!< a nicer formatting of the information. +}; + +////////////// + +//! Provides serious file operations, such as copy and partial writing. + +class heavy_file_operations : public virtual basis::root_object +{ +public: + virtual ~heavy_file_operations(); + + enum outcomes { + OKAY = basis::common::OKAY, + BAD_INPUT = basis::common::BAD_INPUT, +// GARBAGE = basis::common::GARBAGE, +// NOT_FOUND = basis::common::NOT_FOUND, +// NONE_READY = basis::common::NONE_READY, +// FAILURE = basis::common::FAILURE, + DEFINE_OUTCOME(SOURCE_MISSING, -43, "The source file is not accessible"), + DEFINE_OUTCOME(TARGET_DIR_ERROR, -44, "The target's directory could not " + "be created"), + DEFINE_OUTCOME(TARGET_ACCESS_ERROR, -45, "The target file could not be " + "created") + }; + static const char *outcome_name(const basis::outcome &to_name); + + DEFINE_CLASS_NAME("heavy_file_operations"); + + static const size_t COPY_CHUNK_FACTOR; + //!< the default copy chunk size for the file copy method. + static size_t copy_chunk_factor(); + //!< method can be exported for use by shared libs. + + static basis::outcome copy_file(const basis::astring &source, const basis::astring &destination, + int copy_chunk_factor = copy_chunk_factor()); + //!< copies a file from the "source" location to the "destination". + /*!< the outcomes could be from this class or from common::outcomes. + the "copy_chunk_factor" is the read buffer size to use while copying. */ + + static basis::outcome write_file_chunk(const basis::astring &target, double byte_start, + const basis::byte_array &chunk, bool truncate = true, + int copy_chunk_factor = copy_chunk_factor()); + //!< stores a chunk of bytes into the "target" file. + /*!< writes the content stored in "chunk" into the file "target" at the + position "byte_start". the entire "chunk" will be used, which means the + file will either be that much larger or have the space between byte_start + and (byte_start + chunk.length() - 1) replaced. if the file is not yet as + large as "byte_start", then it will be expanded appropriately. if + "truncate" is true, then any contents past the new chunk are dropped from + the file. */ + + static basis::outcome buffer_files(const basis::astring &source_root, + const filename_list &to_transfer, file_transfer_header &last_action, + basis::byte_array &storage, int maximum_bytes); + //!< reads files in "to_transfer" and packs them into a "storage" buffer. + /*!< the maximum size allowed in storage is "maximum_bytes". the record + of the last file piece stored in "last_action" allows the next chunk + to be sent in subsequent calls. note that the buffer "storage" is cleared + out before bytes are stored into it; this is not an additive operation. */ + +private: + static bool advance(const filename_list &to_transfer, file_transfer_header &last_action); + //!< advances to the next file in the transfer list "to_transfer". +}; + +////////////// + +} //namespace. + +#endif + diff --git a/core/library/filesystem/huge_file.cpp b/core/library/filesystem/huge_file.cpp new file mode 100644 index 00000000..4c218c56 --- /dev/null +++ b/core/library/filesystem/huge_file.cpp @@ -0,0 +1,280 @@ +/*****************************************************************************\ +* * +* Name : huge_file * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2007-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "byte_filer.h" +#include "huge_file.h" + +#include +#include +#include + +#include + +#undef LOG +#define LOG(to_print) printf("%s::%s: %s\n", static_class_name(), func, astring(to_print).s()) + +//#define DEBUG_HUGE_FILE + // uncomment for noisy version. + +using namespace basis; + +namespace filesystem { + +huge_file::huge_file(const astring &filename, const astring &permissions) +: _real_file(new byte_filer(filename, permissions)), + _file_pointer(0) +{ +} + +huge_file::~huge_file() +{ + WHACK(_real_file); +} + +void huge_file::flush() { _real_file->flush(); } + +bool huge_file::truncate() { return _real_file->truncate(); } + +double huge_file::length() +{ + FUNCDEF("length"); + +//trying to read to see if we're past endpoint. +// if this approach works, length may want to close and reopen file for +// reading, since we can't add any bytes to it for writing just to find +// the length out. + + + double save_posn = _file_pointer; + // skip to the beginning of the file so we can try to find the end. + _file_pointer = 0; + _real_file->seek(0, byte_filer::FROM_START); + size_t naive_size = _real_file->length(); + if (naive_size < _real_file->file_size_limit()) { + // lucked out; we are within normal file size limitations. + seek(save_posn, byte_filer::FROM_START); + return double(naive_size); + } + + double best_highest = 0.0; // the maximum we've safely seeked to. + + size_t big_jump = byte_filer::file_size_limit(); + // try with the largest possible seek at first. + + while (true) { +#ifdef DEBUG_HUGE_FILE + LOG(a_sprintf("best highest=%.0f", best_highest)); +#endif + // iterate until we reach our exit condition, which seems like it must + // always occur eventually unless the file is being monkeyed with. + bool seek_ret = _real_file->seek(int(big_jump), byte_filer::FROM_CURRENT); +#ifdef DEBUG_HUGE_FILE + LOG(a_sprintf(" seek ret=%d", int(seek_ret))); +#endif + byte_array temp_bytes; + int bytes_read = _real_file->read(temp_bytes, 1); + if (bytes_read < 1) + seek_ret = false; +#ifdef DEBUG_HUGE_FILE + LOG(a_sprintf(" read %d bytes", bytes_read)); +#endif + bool at_eof = _real_file->eof(); +#ifdef DEBUG_HUGE_FILE + LOG(a_sprintf(" at_eof=%d", int(at_eof))); +#endif + if (seek_ret && !at_eof) { +#ifdef DEBUG_HUGE_FILE + LOG("seek worked, incrementing best highest and trying same jump again"); +#endif + // the seek worked, so we'll just jump forward again. + best_highest += double(big_jump); + _file_pointer += double(big_jump); + continue; + } else if (seek_ret && at_eof) { +#ifdef DEBUG_HUGE_FILE + LOG("seek worked but found eof exactly."); +#endif + // the seek did worked, but apparently we've also found the end point. + best_highest += double(big_jump); + _file_pointer += double(big_jump); + break; + } else { + // that seek was too large, so we need to back down and try a smaller + // seek size. +#ifdef DEBUG_HUGE_FILE + LOG("seek failed, going back to best highest and trying same jump again"); +#endif + _file_pointer = 0; + _real_file->seek(0, byte_filer::FROM_START); + outcome worked = seek(best_highest, byte_filer::FROM_START); + // this uses our version to position at large sizes. + if (worked != OKAY) { + // this is a bad failure; it says that the file size changed or + // something malfunctioned. we should always be able to get back to + // the last good size we found if the file is static. + LOG(a_sprintf("failed to seek back to best highest %.0f on ", + best_highest) + _real_file->filename()); + // try to repair our ideas about the file by starting the process + // over. +//hmmm: count the number of times restarted and bail after N. + seek_ret = _real_file->seek(0, byte_filer::FROM_START); + _file_pointer = 0; + if (!seek_ret) { + // the heck with this. we can't even go back to the start. this + // file seems to be screwed up now. + LOG(astring("failed to seek back to start of file! on ") + + _real_file->filename()); + return 0; + } + // reset the rest of the positions for our failed attempt to return + // to what we already thought was good. + _file_pointer = 0; + big_jump = byte_filer::file_size_limit(); + best_highest = 0; + continue; + } + // okay, nothing bad happened when we went back to our last good point. + if (big_jump <= 0) { + // success in finding the smallest place that we can't seek between. +#ifdef DEBUG_HUGE_FILE + LOG("got down to smallest big jump, 0!"); +#endif + break; + } + // formula expects that the maximum file size is a power of 2. + big_jump /= 2; +#ifdef DEBUG_HUGE_FILE + LOG(a_sprintf("restraining big jump down to %u.", big_jump)); +#endif + continue; + } + } + + // go back to where we started out. + seek(0, byte_filer::FROM_START); + seek(save_posn, byte_filer::FROM_CURRENT); +#ifdef DEBUG_HUGE_FILE + LOG(a_sprintf("saying file len is %.0f.", best_highest + 1.0)); +#endif + return best_highest + 1.0; +} + +bool huge_file::good() const { return _real_file->good(); } + +bool huge_file::eof() const { return _real_file->eof(); } + +outcome huge_file::move_to(double absolute_posn) +{ +#ifdef DEBUG_HUGE_FILE + FUNCDEF("move_to"); +#endif + double difference = absolute_posn - _file_pointer; + // calculate the size we want to offset. +#ifdef DEBUG_HUGE_FILE + LOG(a_sprintf("abs_pos=%.0f difference=%.0f old_filepoint=%.0f", + absolute_posn, difference, _file_pointer)); +#endif + // if we're at the same place, we don't have to do anything. + if (difference < 0.000001) { +#ifdef DEBUG_HUGE_FILE + LOG("difference was minimal, saying we're done."); +#endif + return OKAY; + } + while (absolute_value(difference) > 0.000001) { + double seek_size = minimum(double(byte_filer::file_size_limit() - 1), + absolute_value(difference)); + if (difference < 0) + seek_size *= -1.0; // flip sign of seek. +#ifdef DEBUG_HUGE_FILE + LOG(a_sprintf(" seeksize=%d", int(seek_size))); +#endif + bool seek_ret = _real_file->seek(int(seek_size), + byte_filer::FROM_CURRENT); + if (!seek_ret) { +#ifdef DEBUG_HUGE_FILE + LOG(a_sprintf("failed to seek %d from current", int(seek_size))); +#endif + return FAILURE; // seek failed somehow. + } + _file_pointer += seek_size; +#ifdef DEBUG_HUGE_FILE + LOG(a_sprintf(" now_filepoint=%.0f", _file_pointer)); +#endif + difference = absolute_posn - _file_pointer; +#ifdef DEBUG_HUGE_FILE + LOG(a_sprintf(" now_difference=%.0f", difference)); +#endif + } + return OKAY; +} + +outcome huge_file::seek(double new_position, byte_filer::origins origin) +{ +#ifdef DEBUG_HUGE_FILE + FUNCDEF("seek"); +#endif + if (origin == byte_filer::FROM_CURRENT) { + return move_to(_file_pointer + new_position); + } else if (origin == byte_filer::FROM_START) { + _file_pointer = 0; + if (!_real_file->seek(0, byte_filer::FROM_START)) + return FAILURE; + return move_to(new_position); + } else if (origin == byte_filer::FROM_END) { +#ifdef DEBUG_HUGE_FILE + LOG("into precarious FROM_END case."); +#endif + double file_len = length(); // could take a scary long time possibly. +#ifdef DEBUG_HUGE_FILE + LOG(a_sprintf(" FROM_END got len %.0f.", file_len)); +#endif + _file_pointer = file_len; + // it's safe, although not efficient, for us to call the length() + // method here. our current version of length() uses the byte_filer's + // seek method directly and only FROM_CURRENT and FROM_START from this + // class's seek method. + _real_file->seek(0, byte_filer::FROM_END); + return move_to(_file_pointer - new_position); + } + // unknown origin. + return BAD_INPUT; +} + +outcome huge_file::read(byte_array &to_fill, int desired_size, int &size_read) +{ +// FUNCDEF("read"); + size_read = 0; + int ret = _real_file->read(to_fill, desired_size); + if (ret < 0) + return FAILURE; // couldn't read the bytes. + _file_pointer += double(size_read); + size_read = ret; + return OKAY; +} + +outcome huge_file::write(const byte_array &to_write, int &size_written) +{ +// FUNCDEF("write"); + size_written = 0; + int ret = _real_file->write(to_write); + if (ret < 0) + return FAILURE; // couldn't write the bytes. + _file_pointer += double(size_written); + size_written = ret; + return OKAY; +} + +} //namespace. + diff --git a/core/library/filesystem/huge_file.h b/core/library/filesystem/huge_file.h new file mode 100644 index 00000000..69a10347 --- /dev/null +++ b/core/library/filesystem/huge_file.h @@ -0,0 +1,97 @@ +#ifndef HUGE_FILE_CLASS +#define HUGE_FILE_CLASS + +/*****************************************************************************\ +* * +* Name : huge_file * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2007-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "byte_filer.h" + +#include +#include +#include +#include + +namespace filesystem { + +//! Supports reading and writing to very large files, > 4 gigabytes. +/*! + The standard file I/O functions only handle files up to 4 gigabytes. This + class extends the range to essentially unlimited sizes, as long as the + operating system can accurately do relative seeks and can read/write to + files of the size needed. +*/ + +class huge_file +{ +public: + huge_file(const basis::astring &filename, const basis::astring &permissions); + //!< opens "filename" for access, where it presumably is a very large file. + /*!< see byte filer for a description of the permissions. + */ + virtual ~huge_file(); + + DEFINE_CLASS_NAME("huge_file"); + + enum outcomes { + OKAY = basis::common::OKAY, + FAILURE = basis::common::FAILURE, + ACCESS_DENIED = basis::common::ACCESS_DENIED, + BAD_INPUT = basis::common::BAD_INPUT + }; + + bool good() const; + //!< reports if the file was opened successfully. + + bool eof() const; + //!< reports when the file pointer has reached the end of the file. + + double length(); + //!< expensive operation accesses the file to find length. + + double file_pointer() const { return _file_pointer; } + //!< returns where we currently are in the file. + + basis::outcome seek(double new_position, + byte_filer::origins origin = byte_filer::FROM_CURRENT); + //!< move the file pointer to "new_position" if possible. + /*!< the relative seek is the easiest type of seek to accomplish with a + huge file. the other types are also supported, but take a bit more to + implement. */ + + basis::outcome move_to(double absolute_posn); + //!< simpler seek just goes from current location to "absolute_posn". + + basis::outcome read(basis::byte_array &to_fill, int desired_size, int &size_read); + //!< reads "desired_size" into "to_fill" if possible. + /*!< "size_read" reports how many bytes were actually read. */ + + basis::outcome write(const basis::byte_array &to_write, int &size_written); + //!< stores the array "to_write" into the file. + /*!< "size_written" reports how many bytes got written. */ + + bool truncate(); + //!< truncates the file after the current position. + + void flush(); + //!< forces any pending writes to actually be saved to the file. + +private: + byte_filer *_real_file; //!< supports us but is subject to 4g limit. + double _file_pointer; //!< position in the file if OS is tracking us. +}; + +} //namespace. + +#endif + diff --git a/core/library/filesystem/makefile b/core/library/filesystem/makefile new file mode 100644 index 00000000..746acf03 --- /dev/null +++ b/core/library/filesystem/makefile @@ -0,0 +1,11 @@ +include cpp/variables.def + +PROJECT = filesystem +TYPE = library +SOURCE = byte_filer.cpp directory.cpp directory_tree.cpp file_info.cpp \ + file_time.cpp filename.cpp filename_list.cpp filename_tree.cpp \ + heavy_file_ops.cpp huge_file.cpp +TARGETS = filesystem.lib + +include cpp/rules.def + diff --git a/core/library/loggers/combo_logger.cpp b/core/library/loggers/combo_logger.cpp new file mode 100644 index 00000000..591bde24 --- /dev/null +++ b/core/library/loggers/combo_logger.cpp @@ -0,0 +1,86 @@ +/*****************************************************************************\ +* * +* Name : combo_logger +* Author : Chris Koeritz +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "combo_logger.h" + +#include +#include +#include +#include +#include + +#ifdef __WIN32__ + #include +#endif +#include +#ifdef __UNIX__ + #include +#endif + +using namespace basis; +using namespace filesystem; +using namespace structures; +using namespace textual; + +namespace loggers { + +//const int REDUCE_FACTOR = 5; + // we whack this portion of the file every time we truncate. if it's set + // to 14, for example, then a 14th of the file is whacked every time whacking + // is needed. + +//const int MAXIMUM_BUFFER_SIZE = 140000; + // the maximum allowed chunk that can be copied from the old logfile + // to the current one. + +//int static_chaos() { return chaos().inclusive(0, 1280004); } + +combo_logger::combo_logger(const astring &filename, int limit, stream_choices target) +: file_logger(filename, limit), + console_logger(target) +{ +} + +void combo_logger::add_filter(int new_filter) +{ + file_logger::add_filter(new_filter); + console_logger::add_filter(new_filter); +} + +void combo_logger::remove_filter(int old_filter) +{ + file_logger::remove_filter(old_filter); + console_logger::remove_filter(old_filter); +} + +void combo_logger::clear_filters() +{ + file_logger::clear_filters(); + console_logger::clear_filters(); +} + +void combo_logger::eol(textual::parser_bits::line_ending to_set) +{ + file_logger::eol(to_set); + console_logger::eol(to_set); +} + +outcome combo_logger::log(const base_string &info, int filter) +{ + console_logger::log(info, filter); + return file_logger::log(info, filter); +} + +} //namespace. + diff --git a/core/library/loggers/combo_logger.h b/core/library/loggers/combo_logger.h new file mode 100644 index 00000000..a593cf4e --- /dev/null +++ b/core/library/loggers/combo_logger.h @@ -0,0 +1,59 @@ +#ifndef COMBO_LOGGER_CLASS +#define COMBO_LOGGER_CLASS + +/*****************************************************************************\ +* * +* Name : combo_logger +* Author : Chris Koeritz +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "file_logger.h" + +namespace loggers { + +//! combines a file_logger with a console logger, behaving like the 'tee' command. +/*! this will output the diagnostic info to both a file and to the console. this is useful +when one would like to see the output as its happening but also have a record for later. */ + +class combo_logger : public virtual file_logger, public virtual console_logger +{ +public: + combo_logger(const basis::astring &filename, + int limit = DEFAULT_LOG_FILE_SIZE, + stream_choices log_target = TO_STDOUT); + + virtual ~combo_logger() {} + + DEFINE_CLASS_NAME("combo_logger"); + + virtual basis::outcome log(const basis::base_string &info, int filter = basis::ALWAYS_PRINT); + + // overrides that enforce properties for both loggers. + virtual void add_filter(int new_filter); + virtual void remove_filter(int old_filter); + virtual void clear_filters(); + virtual void eol(textual::parser_bits::line_ending to_set); +}; + +////////////// + +//! a macro that retasks the program-wide logger as a combo_logger. +#define SETUP_COMBO_LOGGER { \ + basis::base_logger *old_log = program_wide_logger::set \ + (new loggers::combo_logger \ + (loggers::file_logger::log_file_for_app_name())); \ + WHACK(old_log); \ +} + +} //namespace. + +#endif + diff --git a/core/library/loggers/console_logger.cpp b/core/library/loggers/console_logger.cpp new file mode 100644 index 00000000..aeb304b0 --- /dev/null +++ b/core/library/loggers/console_logger.cpp @@ -0,0 +1,63 @@ +/*****************************************************************************\ +* * +* Name : console_logger * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "console_logger.h" +#include "logging_filters.h" + +#include + +using namespace basis; + +namespace loggers { + +console_logger::console_logger(stream_choices target) : c_target(target) +{} + +console_logger::~console_logger() {} + +outcome console_logger::log(const base_string &info, int filter) +{ + +if (filter) {} //temp ignored + + FILE *log_to = stdout; + if (c_target == TO_STDERR) log_to = stderr; + +//hmmm: temp simplified form during bootup of new hoople. +fprintf(log_to, "%s\n", (char *)info.observe()); + +/* +hmmm: need filter set support! + if (member(filter)) { +*/ + // format the output with %s to ensure we get all characters, rather + // than having some get interpreted if we used info as the format spec. +// fprintf(log_to, "%s", (char *)info.s()); + // send the EOL char if the style is appropriate for that. +// if (eol() != NO_ENDING) fprintf(log_to, "%s", get_ending().s()); + + + // write immediately to avoid lost output on crash. + fflush(log_to); + +/* +hmmm: need filter set support! + } +*/ + return common::OKAY; +} + +} //namespace. + + diff --git a/core/library/loggers/console_logger.h b/core/library/loggers/console_logger.h new file mode 100644 index 00000000..c1984df8 --- /dev/null +++ b/core/library/loggers/console_logger.h @@ -0,0 +1,79 @@ +#ifndef CONSOLE_LOGGER_CLASS +#define CONSOLE_LOGGER_CLASS + +/*****************************************************************************\ +* * +* Name : console_logger * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! A logger that sends to the console screen using the standard output device. +/*! + The object can optionally be used to log to the standard error device + instead. +*/ + +#include "standard_log_base.h" + +#include +#include +#include + +namespace loggers { + +class console_logger : public virtual standard_log_base +{ +public: + enum stream_choices { TO_STDOUT, TO_STDERR }; //!< where to send the logging. + + console_logger(stream_choices log_target = TO_STDOUT); + //!< if "standard_error" is true, than stderr is used instead of stdout. + + virtual ~console_logger(); + + DEFINE_CLASS_NAME("console_logger"); + + bool to_standard_output() const { return c_target == TO_STDOUT; } + //!< reports if the logger goes to standard output (default). + + bool to_standard_error() const { return c_target == TO_STDERR; } + //!< reports if the logger goes to standard error instead. + + void set_output(stream_choices target) { c_target = target; } + //!< enables the target of logging to be changed after construction. + + virtual basis::outcome log(const basis::base_string &info, int filter); + //!< sends the string "info" to the standard output device. + /*!< if the "filter" is not in the current filter set, then the + information will be dropped. otherwise the information is displayed, + where appropriate. for some environments, this will essentially throw + it away. for example, in ms-windows, a windowed program will only save + standard out if one redirects standard output to a file, whereas a + console mode program will output the text to its parent prompt. */ + +private: + stream_choices c_target; //!< records whether stdout or stderr is used. +}; + +////////////// + +//!< a macro that retasks the program-wide logger as a console_logger. +#define SETUP_CONSOLE_LOGGER { \ + loggers::standard_log_base *old_log = loggers::program_wide_logger::set \ + (new loggers::console_logger); \ + /* assumes we are good to entirely remove the old logger. */ \ + WHACK(old_log); \ +} + +} // namespace. + +#endif + diff --git a/core/library/loggers/critical_events.cpp b/core/library/loggers/critical_events.cpp new file mode 100644 index 00000000..ebb60bb8 --- /dev/null +++ b/core/library/loggers/critical_events.cpp @@ -0,0 +1,284 @@ +/*****************************************************************************\ +* * +* Name : critical_events * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1989-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "critical_events.h" +#include "program_wide_logger.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef __UNIX__ + #include +#endif + +using namespace basis; +using namespace structures; +using namespace configuration; +using namespace textual; +using namespace timely; + +const int MESSAGE_SPACE_PROVIDED = 4096; + // the strings should not be larger than this for diagnostic / error messages. + +namespace loggers { + +SAFE_STATIC(astring, critical_events::hidden_critical_events_dir, ) + // define the function that holds the directory string. + +astring default_critical_location() +{ + // ensure that the critical events function logs to the appropriate place. + return application_configuration::get_logging_directory(); +} + +SAFE_STATIC(mutex, __critical_event_dir_lock, ) + +basis::un_int critical_events::system_error() +{ +#if defined(__UNIX__) + return errno; +#elif defined(__WIN32__) + return GetLastError(); +#else + #pragma error("hmmm: no code for error number for this operating system") + return 0; +#endif +} + +astring critical_events::system_error_text(basis::un_int to_name) +{ +#if defined(__UNIX__) + return strerror(to_name); +#elif defined(__WIN32__) + char error_text[1000]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NIL, to_name, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)error_text, + sizeof(error_text) - 1, NIL); + astring to_return = error_text; + // trim off the ridiculous carriage return they add. + while ( (to_return[to_return.end()] == '\r') + || (to_return[to_return.end()] == '\n') ) + to_return.zap(to_return.end(), to_return.end()); + return to_return; +#else + #pragma error("hmmm: no code for error text for this operating system") + return ""; +#endif +} + +astring critical_events::critical_events_directory() +{ + static bool initted = false; + if (!initted) { + auto_synchronizer l(__critical_event_dir_lock()); + if (!initted) { + set_critical_events_directory(default_critical_location()); + initted = true; + } + } + return hidden_critical_events_dir(); +} + +void critical_events::set_critical_events_directory(const astring &directory) +{ hidden_critical_events_dir() = directory; } + +void critical_events::write_to_critical_events(const char *to_write) +{ + astring filename = critical_events_directory(); + filename += "/runtime_issues.log"; + FILE *errfile = fopen(filename.s(), "ab"); + if (errfile) { + astring app_name = application_configuration::application_name(); + int indy = app_name.find('/', app_name.end(), true); + if (non_negative(indy)) app_name.zap(0, indy); + indy = app_name.find('\\', app_name.end(), true); + if (non_negative(indy)) app_name.zap(0, indy); + fprintf(errfile, "%s [%s]:%s", time_stamp::notarize(true).s(), + app_name.s(), parser_bits::platform_eol_to_chars()); + fprintf(errfile, "%s%s", to_write, parser_bits::platform_eol_to_chars()); + fclose(errfile); + } +} + +void critical_events::write_to_console(const char *guards_message_space) +{ fprintf(stderr, "%s", (char *)guards_message_space); fflush(stderr); } + +void critical_events::alert_message(const char *info, const char *title) +{ + astring to_print; + if (strlen(title)) { + const char border = '='; + to_print += astring(border, int(strlen(title)) + 4); + to_print += parser_bits::platform_eol_to_chars(); + to_print += border; + to_print += ' '; + to_print += title; + to_print += ' '; + to_print += border; + to_print += parser_bits::platform_eol_to_chars(); + to_print += astring(border, int(strlen(title)) + 4); + to_print += parser_bits::platform_eol_to_chars(); + } + + to_print += info; + program_wide_logger::get().log(to_print, ALWAYS_PRINT); + fflush(NIL); // flush all output streams. +} + +void critical_events::alert_message(const astring &info) { alert_message(info.s()); } + +void critical_events::alert_message(const astring &info, const astring &title) +{ alert_message(info.s(), title.s()); } + +void critical_events::make_error_message(const char *file, int line, + const char *error_class, const char *error_function, + const char *info, char *guards_message_space) +{ + strcpy(guards_message_space, "\nProblem reported for \""); +//hmmm: only copy N chars of each into the space. +// say 40 for class/function each, then the space - consumed for the +// info. get strlen for real on the class and function name to know +// actual size for that computation. + strcat(guards_message_space, error_class); + strcat(guards_message_space, "::"); + strcat(guards_message_space, error_function); + strcat(guards_message_space, "\"\n(invoked at line "); + char line_num[20]; + sprintf(line_num, "%d", line); + strcat(guards_message_space, line_num); + strcat(guards_message_space, " in "); + strcat(guards_message_space, file); + strcat(guards_message_space, " at "); + strcat(guards_message_space, time_stamp::notarize(false).s()); + strcat(guards_message_space, ")\n"); + strcat(guards_message_space, info); + strcat(guards_message_space, "\n\n\n"); +} + +void critical_events::FL_deadly_error(const char *file, int line, const char *error_class, + const char *error_function, const char *info) +{ + FL_continuable_error(file, line, error_class, error_function, info, + "Deadly Error Information"); + CAUSE_BREAKPOINT; + throw "deadly_error"; + // abort() is not as good an approach as throwing an exception. aborts are + // harder to track with some compilers, but all should be able to trap an + // exception. +} + +void critical_events::FL_deadly_error(const astring &file, int line, + const astring &error_class, const astring &error_function, + const astring &info) +{ + FL_deadly_error(file.s(), line, error_class.s(), error_function.s(), + info.s()); +} + +void critical_events::FL_continuable_error_real(const char *file, int line, + const char *error_class, const char *error_function, const char *info, + const char *title) +{ + char guards_message_space[MESSAGE_SPACE_PROVIDED]; + // this routine could still fail, if the call stack is already jammed + // against its barrier. but if that's the case, even the simple function + // call might fail. in any case, we cannot deal with a stack overflow + // type error using this function. but we would rather deal with that + // than make the space static since that would cause corruption when + // two threads wrote errors at the same time. + make_error_message(file, line, error_class, error_function, info, + guards_message_space); + alert_message(guards_message_space, title); +} + +void critical_events::FL_continuable_error(const char *file, int line, + const char *error_class, const char *error_function, const char *info, + const char *title) +{ + FL_continuable_error_real(file, line, error_class, error_function, info, title); +} + +void critical_events::FL_continuable_error(const astring &file, int line, + const astring &error_class, const astring &error_function, + const astring &info, const astring &title) +{ + FL_continuable_error_real(file.s(), line, error_class.s(), + error_function.s(), info.s(), title.s()); +} + +void critical_events::FL_non_continuable_error(const char *file, int line, + const char *error_class, const char *error_function, const char *info, + const char *title) +{ + FL_continuable_error_real(file, line, error_class, error_function, info, + title); + exit(EXIT_FAILURE); // let the outside world know that there was a problem. +} + +void critical_events::FL_non_continuable_error(const astring &file, int line, + const astring &error_class, const astring &error_function, + const astring &info, const astring &title) +{ + FL_continuable_error_real(file.s(), line, error_class.s(), + error_function.s(), info.s(), title.s()); + exit(EXIT_FAILURE); // let the outside world know that there was a problem. +} + +void critical_events::FL_console_error(const char *file, int line, const char *error_class, + const char *error_function, const char *info) +{ + char guards_message_space[MESSAGE_SPACE_PROVIDED]; + make_error_message(file, line, error_class, error_function, info, + guards_message_space); + write_to_console(guards_message_space); +} + +void critical_events::FL_out_of_memory_now(const char *file, int line, + const char *the_class_name, const char *the_func) +{ + FL_non_continuable_error(file, line, the_class_name, the_func, + "Program stopped due to memory allocation failure.", + "Out of Memory Now"); +} + +void critical_events::implement_bounds_halt(const char *the_class_name, const char *the_func, + const char *value, const char *low, const char *high, + const char *error_addition) +{ + char message[400]; + strcpy(message, "bounds error caught"); + strcat(message, error_addition); + strcat(message, ":\r\n"); + strcat(message, value); + strcat(message, " is not between "); + strcat(message, low); + strcat(message, " and "); + strcat(message, high); +#ifdef ERRORS_ARE_FATAL + deadly_error(the_class_name, the_func, message); +#else + continuable_error(the_class_name, the_func, message); +#endif +} + +} //namespace. + diff --git a/core/library/loggers/critical_events.h b/core/library/loggers/critical_events.h new file mode 100644 index 00000000..f814de50 --- /dev/null +++ b/core/library/loggers/critical_events.h @@ -0,0 +1,205 @@ +#ifndef CRITICAL_EVENTS_GROUP +#define CRITICAL_EVENTS_GROUP + +/*****************************************************************************\ +* * +* Name : critical_events * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1989-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace loggers { + +//! This macro wraps the notion of stopping in the debugger. +#ifdef __UNIX__ + #define CAUSE_BREAKPOINT +//hmmm: need a unix equivalent for this. supporting gcc might be enough. +#elif defined(__WIN32__) + #ifdef __MINGW32__ + extern "C" { +// #include +//for debugbreak. does this cause other problems? + } + #define CAUSE_BREAKPOINT +// #define CAUSE_BREAKPOINT __debugbreak() + #else + #define CAUSE_BREAKPOINT __debugbreak() + #endif +#endif + +//! Tests the value "check" to ensure that it's not zero. +/*! This can be used instead of an ASSERT macro to check conditions in +builds with ERRORS_ARE_FATAL turned on. This macro is orthogonal to the +build being built with debugging or release features. */ +#ifdef ERRORS_ARE_FATAL + #define REQUIRE(check) if (!check) CAUSE_BREAKPOINT; +#else + #define REQUIRE(check) +#endif + +// now more dangerous or potent guards... + +//! an extra piece of information used, if available, in bounds_halt below. +//#define BH_ERROR_ADDITION ((basis::astring(" in ") + _global_argv[0]).s()) + +//! Verifies that "value" is between "low" and "high", inclusive. +/*! "Value" must be an object for which greater than and less than are defined. +The static_class_name() method and func definition are used to tag the +complaint that is emitted when problems are detected. Note that if +CATCH_ERRORS is defined, then the program is _halted_ if the value is out +of bounds. Otherwise, the "to_return" value is returned. */ +//#ifdef CATCH_ERRORS +// #define bounds_halt(value, low, high, to_return) { +// if (((value) < (low)) || ((value) > (high))) { +// critical_events::implement_bounds_halt(static_class_name(), func, #value, #low, +// #high, BH_ERROR_ADDITION); +// return to_return; +// } +// } +//#else +// #define bounds_halt(a, b, c, d) bounds_return(a, b, c, d) +//#endif + +////////////// + +//! Provide some macros that will automatically add the file and line number. +/*! These use the functions below to report different types of error +situations and in some cases, exit the program. */ +#define non_continuable_error(c, f, i) \ + critical_events::FL_non_continuable_error(__FILE__, __LINE__, c, f, i, \ + "A Non-Continuable Runtime Problem Has Occurred") +#define continuable_error(c, f, i) \ + critical_events::FL_continuable_error(__FILE__, __LINE__, c, f, i, \ + "Runtime Problem Information") +#define console_error(c, f, i) \ + critical_events::FL_console_error(__FILE__, __LINE__, c, f, i) +#define deadly_error(c, f, i) \ + critical_events::FL_deadly_error(__FILE__, __LINE__, c, f, i) +#define out_of_memory_now(c, f) \ + critical_events::FL_out_of_memory_now(__FILE__, __LINE__, c, f) + +////////////// + +//! Provides a means of logging events for runtime problems. + +class critical_events : public virtual basis::root_object +{ +public: + virtual ~critical_events() {} + + // super handy system inter-operation functions... + + static basis::un_int system_error(); + //!< gets the most recent system error reported on this thread. + + static basis::astring system_error_text(basis::un_int error_to_show); + //!< returns the OS's string form of the "error_to_show". + /*!< this often comes from the value reported by system_error(). */ + + // some noisemaking methods... + + //! Prints out a message to the standard error file stream. + static void write_to_console(const char *message); + + //! shows the message in "info", with an optional "title" on the message. + /*! the message is sent to the program wide logger, if one is expected to + exist in this program. */ + static void alert_message(const char *info, const char *title = "Alert Message"); + static void alert_message(const basis::astring &info); // uses default title. + static void alert_message(const basis::astring &info, const basis::astring &title); // use "title". + + static void write_to_critical_events(const char *message); + //!< sends the "message" to the critical events log file. + /*!< Prints out a message to the specially named file that captures all + serious events written to error logging functions. If you use the + functions in this file, you are likely already writing to this log. */ + + static void set_critical_events_directory(const basis::astring &directory); + //!< sets the internal location where the critical events will be logged. + /*!< this is postponed to a higher level, although the default + will work too. */ + + static basis::astring critical_events_directory(); + //!< returns the current location where critical events are written. + + // this section implements the error macros. + + //! Prints out an error message and then exits the program. + /*! The parameters describe the location where the error was detected. This + call should only be used in test programs or where absolutely necessary + because it causes an almost immediate exit from the program! deadly_error + should be reserved for when the program absolutely has to exit right then, + because this function will actually enforce a crash / abort / break into + debugger action rather than just calling exit(). */ + static void FL_deadly_error(const char *file, int line, const char *classname, + const char *function_name, const char *info); + //! A version that takes strings instead of char pointers. + static void FL_deadly_error(const basis::astring &file, int line, + const basis::astring &classname, const basis::astring &function_name, + const basis::astring &info); + + //! Describes an error like deadly_error, but does not exit the program. + static void FL_continuable_error(const char *file, int line, + const char *classname, const char *function_name, const char *info, + const char *title); + + //! A version using astring instead of char pointers. + static void FL_continuable_error(const basis::astring &file, int line, + const basis::astring &classname, const basis::astring &function_name, + const basis::astring &info, const basis::astring &title); + + //! Shows the same information as continuable_error, but causes an exit. + /*! This is a friendlier program exit than deadly_error. This version can + be used when the program must stop, possibly because of a precondition failure + or a problem in input data or basically whatever. it is not intended to + abort or break into the debugger, but to simply exit(). */ + static void FL_non_continuable_error(const char *file, int line, + const char *classname, const char *function_name, const char *info, + const char *title); + + //! A version using astring instead of char pointers. + static void FL_non_continuable_error(const basis::astring &file, int line, + const basis::astring &classname, const basis::astring &function_name, + const basis::astring &info, const basis::astring &title); + + //! Causes the program to exit due to a memory allocation failure. + static void FL_out_of_memory_now(const char *file, int line, + const char *classname, const char *function_name); + + //! Prints out an error message to the standard error file stream. + static void FL_console_error(const char *file, int line, const char *error_class, + const char *error_function, const char *info); + + //! Used to build our particular type of error message. + /*! It writes an error message into "guards_message_space" using our stylized + object::method formatting. */ + static void make_error_message(const char *file, int line, + const char *error_class, const char *error_function, + const char *info, char *guards_message_space); + + //! Provides the real implementation of bounds_halt to save code space. + static void implement_bounds_halt(const char *the_class_name, const char *func, + const char *value, const char *low, const char *high, + const char *error_addition); + +private: + static basis::astring &hidden_critical_events_dir(); + + static void FL_continuable_error_real(const char *file, int line, + const char *error_class, const char *error_function, const char *info, + const char *title); +}; + +} //namespace. + +#endif + diff --git a/core/library/loggers/definitions_wx.h b/core/library/loggers/definitions_wx.h new file mode 100644 index 00000000..9a4c29e7 --- /dev/null +++ b/core/library/loggers/definitions_wx.h @@ -0,0 +1,44 @@ +#ifndef WX_DEFINITIONS_GROUP +#define WX_DEFINITIONS_GROUP + +/*****************************************************************************\ +* * +* Name : definitions for wx * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Some macros that support the use of WX widgets. * +* * +******************************************************************************* +* Copyright (c) 2006-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +#include + +namespace loggers { + +#ifdef wxUSE_UNICODE + //! converts to wx's UTF-16 string format. + #define to_unicode_wx(s) ((const wxChar *)(const flexichar *)transcode_to_utf16(s)) + //! converts a wx UTF-16 string to UTF-8. + #define from_unicode_wx(s) ((const char *)(const UTF8 *)transcode_to_utf8(s)) +#else + // turn off the unicode macro to ensure nothing gets confused. + #undef UNICODE + // placeholder macros try not to do much. + #define to_unicode_wx(s) ((const wxChar *)null_transcoder(s, false)) + #define from_unicode_wx(s) ((const char *)null_transcoder((const wxChar *)s, false)) +#endif + +} //namespace. + +#endif + diff --git a/core/library/loggers/eol_aware.h b/core/library/loggers/eol_aware.h new file mode 100644 index 00000000..9e6280f8 --- /dev/null +++ b/core/library/loggers/eol_aware.h @@ -0,0 +1,53 @@ +#ifndef EOL_AWARE_CLASS +#define EOL_AWARE_CLASS + +/*****************************************************************************\ +* * +* Name : eol_aware +* Author : Chris Koeritz +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +namespace loggers { + +//! Provides an abstract base for logging mechanisms. +/*! + This assists greatly in generating diagnostic entries polymorphically and in + enabling different logging mechanisms to be plugged in easily. Note that all + of the functions are defined virtually which enables overriding pretty much + all of the base functionality. Use this wisely, if ever. +*/ + +class eol_aware : public virtual basis::root_object +{ +public: + virtual textual::parser_bits::line_ending eol() { return c_ending; } + //!< observes how line endings are to be printed. + + virtual void eol(textual::parser_bits::line_ending to_set) { c_ending = to_set; } + //!< modifies how line endings are to be printed. + + virtual basis::astring get_ending() { return textual::parser_bits::eol_to_chars(c_ending); } + //!< returns a string for the current ending. + + virtual void get_ending(basis::astring &to_end) { to_end = textual::parser_bits::eol_to_chars(c_ending); } + //!< appends a string for the current ending to "to_end". + +private: + textual::parser_bits::line_ending c_ending; //!< the current printing style. +}; + +} //namespace. + +#endif + diff --git a/core/library/loggers/file_logger.cpp b/core/library/loggers/file_logger.cpp new file mode 100644 index 00000000..a18caa14 --- /dev/null +++ b/core/library/loggers/file_logger.cpp @@ -0,0 +1,375 @@ +/*****************************************************************************\ +* * +* Name : file_logger * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "critical_events.h" +#include "file_logger.h" +#include "logging_filters.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __WIN32__ + #include +#endif +#include +#ifdef __UNIX__ + #include +#endif + +using namespace basis; +using namespace configuration; +using namespace filesystem; +using namespace mathematics; +using namespace structures; +using namespace textual; + +namespace loggers { + +const int REDUCE_FACTOR = 5; + // we whack this portion of the file every time we truncate. if it's set + // to 14, for example, then a 14th of the file is whacked every time whacking + // is needed. + +const int MAXIMUM_BUFFER_SIZE = 140000; + // the maximum allowed chunk that can be copied from the old logfile + // to the current one. + +int static_chaos() { + static chaos __hidden_chaos; + return __hidden_chaos.inclusive(0, 1280004); +} + +file_logger::file_logger() +: _filename(new astring()), + _file_limit(DEFAULT_LOG_FILE_SIZE), + _outfile(NIL), + _flock(new mutex) +{ + name(""); +} + +file_logger::file_logger(const astring &initial_filename, int limit) +: _filename(new astring()), + _file_limit(limit), + _outfile(NIL), + _flock(new mutex) +{ + name(initial_filename); + // we don't open the file right away because we don't know they'll ever + // use the thing. +} + +file_logger::~file_logger() +{ + close_file(); + WHACK(_filename); + WHACK(_flock); +} + +basis::astring file_logger::log_file_for_app_name() +{ + filename prog = application_configuration::application_name(); + return application_configuration::make_logfile_name(prog.rootname() + ".log"); +} + +bool file_logger::reopen() +{ + auto_synchronizer l(*_flock); + name(*_filename); + return open_file(); +} + +void file_logger::close_file() +{ + auto_synchronizer l(*_flock); + if (_outfile) _outfile->flush(); + // dump anything that hasn't gone out yet. + WHACK(_outfile); +} + +void file_logger::name(const astring &new_name) +{ + auto_synchronizer l(*_flock); + close_file(); + *_filename = new_name; +} + +int file_logger::size_reduction() const +{ + auto_synchronizer l(*_flock); + return int(_file_limit / REDUCE_FACTOR); +} + +bool file_logger::good() const +{ + auto_synchronizer l(*_flock); + if (!_outfile && !_file_limit) return true; + if (!_outfile) return false; + return _outfile->good(); +} + +astring file_logger::name() const +{ + auto_synchronizer l(*_flock); + return *_filename; +} + +void file_logger::flush() +{ + auto_synchronizer l(*_flock); + if (!_outfile) open_file(); + if (_outfile) _outfile->flush(); +} + +bool file_logger::open_file() +{ + auto_synchronizer l(*_flock); + close_file(); // close any existing log file. + + if (!_file_limit) { + // if there's a limit of zero, we'll never open the file. + return true; + } + + // make sure we've got a name. + if (!*_filename) { + // if the name is empty, they don't want to save to a normal log file. + _outfile = new byte_filer; + return true; + } + + // canonicalize the name so that we use the same tag for synchronization. + // this might still fail if there are some jokers using different relative + // paths to the file. but if it's an absolute name, it should work. + for (int i = 0; i < _filename->length(); i++) + if ((*_filename)[i] == '\\') (*_filename)[i] = '/'; + + // make sure the directory containing the log file exists, if we can. + filename temp_file(*_filename); + filename temp_dir(temp_file.dirname()); + if (!temp_dir.good() || !temp_dir.is_directory()) { + directory::recursive_create(temp_dir); + } + + // if this opening doesn't work, then we just can't log. + _outfile = new byte_filer(*_filename, "a+b"); + return _outfile->good(); +} + +outcome file_logger::log(const base_string &to_show, int filter) +{ + if (!_file_limit) return common::OKAY; + + size_t current_size = 0; + { + auto_synchronizer l(*_flock); + if (!member(filter)) return common::OKAY; + if (!_outfile) open_file(); + if (!_outfile) return common::BAD_INPUT; // file opening failed. + if (!_outfile->good()) return common::BAD_INPUT; + // there is no log file currently. + + // dump the string out. + if (to_show.length()) + _outfile->write((abyte *)to_show.observe(), to_show.length()); +//hmmm: need eol feature again. +// if (eol() != NO_ENDING) { +// astring end = get_ending(); +astring end = parser_bits::platform_eol_to_chars(); + _outfile->write((abyte *)end.s(), end.length()); +// } + current_size = _outfile->tell(); + flush(); + } + + // check if we need to truncate yet. + if (current_size > _file_limit) truncate(_file_limit - size_reduction()); + return common::OKAY; +} + +outcome file_logger::log_bytes(const byte_array &to_log, int filter) +{ + if (!_file_limit) return common::OKAY; + + size_t current_size = 0; + { + auto_synchronizer l(*_flock); + if (!member(filter)) return common::OKAY; + if (!_outfile) open_file(); + if (!_outfile) return common::BAD_INPUT; // file opening failed. + if (!_outfile->good()) return common::BAD_INPUT; + // there is no log file currently. + + // dump the contents out. + if (to_log.length()) + _outfile->write(to_log.observe(), to_log.length()); + current_size = _outfile->tell(); + flush(); + } + + // check if we need to truncate yet. + if (current_size > _file_limit) + truncate(_file_limit - size_reduction()); + return common::OKAY; +} + +outcome file_logger::format_bytes(const byte_array &to_log, int filter) +{ + if (!_file_limit) return common::OKAY; + + { + auto_synchronizer l(*_flock); + if (!member(filter)) return common::OKAY; + if (!_outfile) open_file(); + if (!_outfile) return common::BAD_INPUT; // file opening failed. + if (!_outfile->good()) return common::BAD_INPUT; + // there is no log file currently. + } + + // dump the contents out. + if (to_log.length()) { + astring dumped_form; + byte_formatter::text_dump(dumped_form, to_log); + log(dumped_form); + } + + // check if we need to truncate yet. +// int current_size = _outfile->tell(); +// flush(); +// if (current_size > _file_limit) truncate(_file_limit - size_reduction()); + + return common::OKAY; +} + +//hmmm: should move the truncation functionality into a function on +// the file object. + +void file_logger::truncate(size_t new_size) +{ + auto_synchronizer l(*_flock); + if (!_outfile) open_file(); + if (!_outfile) return; // file opening failed. + if (!_outfile->good()) return; + // there is no log file currently. + + size_t current_size = 0; + +/// // our synchronization scheme allows us to use this inter-application +/// // lock; the logger's own lock is always acquired first. no one else can +/// // grab the "file_lock", so no deadlocks. +/// +/// rendezvous file_lock(*_filename + "_trunclock"); +/// if (!file_lock.healthy()) { +/// critical_events::write_to_critical_events((astring("could not create " +/// "lock for ") + *_filename).s()); +/// return; +/// } +/// // waiting forever until the file lock succeeds. as long as there are +/// // no deadlocks permitted, then this shouldn't be too dangerous... +/// bool got_lock = file_lock.lock(rendezvous::ENDLESS_WAIT); +/// if (!got_lock) { +/// critical_events::write_to_critical_events((astring("could not acquire " +/// "lock for ") + *_filename).s()); +/// return; +/// } + + // make sure we weren't second in line to clean the file. if someone else + // already did this, we don't need to do it again. + _outfile->seek(0, byte_filer::FROM_END); + current_size = _outfile->tell(); + if (current_size <= new_size) { + // release the lock and leave since we don't need to change the file. +/// file_lock.unlock(); + return; + } + // create a bogus temporary name. + astring new_file(astring::SPRINTF, "%s.tmp.%d", name().s(), + static_chaos()); + + // unlink the temp file, if it exists. + unlink(new_file.s()); + + // grab the current size before we close our file. + current_size = _outfile->tell(); + + // redo the main stream for reading also. + WHACK(_outfile); + _outfile = new byte_filer(*_filename, "rb"); + + // open the temp file as blank for writing. + byte_filer *hold_stream = new byte_filer(new_file, "w+b"); + + int start_of_keep = int(current_size - new_size); + + // position the old file where it will be about the right size. + _outfile->seek(start_of_keep, byte_filer::FROM_START); + + astring buff(' ', MAXIMUM_BUFFER_SIZE + 1); + + // we only read as long as the file end isn't hit and we're not past the + // original end of the file. if the file got bigger during the truncation, + // that's definitely not our doing and should not be coddled. we've seen + // a situation where we never thought we'd hit the end of the file yet before + // adding this size check. + size_t bytes_written = 0; // how many bytes have gone out already. +//hmmm: loop could be extracted to some kind of dump from file one into file +// two operation that starts at a particular address and has a particular +// size or range. + while (!_outfile->eof() && (bytes_written <= new_size)) { + // grab a line from the input file. + buff[0] = '\0'; // set it to be an empty string. + int bytes_read = _outfile->read((abyte *)buff.s(), MAXIMUM_BUFFER_SIZE); + if (!bytes_read) + break; + bytes_written += bytes_read; + // write the line and a CR to the output file. + if (!_outfile->eof() || bytes_read) + hold_stream->write((abyte *)buff.s(), bytes_read); + } + WHACK(_outfile); + _outfile = new byte_filer(*_filename, "w+b"); + + // we think the new stream is ready for writing. + size_t hold_size = hold_stream->tell(); + // get the current length of the clipped chunk. + bytes_written = 0; // reset our counter. + + // jump back to the beginning of the temp file. + hold_stream->seek(0, byte_filer::FROM_START); + while (!hold_stream->eof() && (bytes_written <= hold_size) ) { + // scoot all the old data back into our file. + int bytes_read = hold_stream->read((abyte *)buff.s(), MAXIMUM_BUFFER_SIZE); + if (!bytes_read) + break; + // something funky happened; we shouldn't be at the end of the file yet. + bytes_written += bytes_read; + if (!hold_stream->eof() || bytes_read) + _outfile->write((abyte *)buff.s(), bytes_read); + } + WHACK(hold_stream); + unlink(new_file.s()); // trash the temp file. +/// file_lock.unlock(); // repeal the process-wide lock. + name(*_filename); // re-open the regular file with append semantics. +} + +} //namespace. + diff --git a/core/library/loggers/file_logger.h b/core/library/loggers/file_logger.h new file mode 100644 index 00000000..3fc0319b --- /dev/null +++ b/core/library/loggers/file_logger.h @@ -0,0 +1,139 @@ +#ifndef FILE_LOGGER_CLASS +#define FILE_LOGGER_CLASS + +/*****************************************************************************\ +* * +* Name : file_logger * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! Enables the printing of information to a log file. +/*! + The information can be conditionally printed using the filter support. + The log file will automatically be truncated when it passes the size limit. +*/ + +#include "console_logger.h" +#include "eol_aware.h" +#include "filter_set.h" + +#include +#include +#include +#include +#include +#include + +namespace loggers { + +class file_logger : public virtual standard_log_base +{ +public: + file_logger(); + //!< creates a logger without a log file and with the default size limit. + /*!< the log file name can be changed using filename(). */ + + file_logger(const basis::astring &filename, int limit = DEFAULT_LOG_FILE_SIZE); + //!< constructs a logger using the "filename" for output. + /*!< there will be no logging if the "filename" is empty. the "limit" + specifies how large the log file can be (in bytes). */ + + virtual ~file_logger(); + + DEFINE_CLASS_NAME("file_logger"); + + enum limits { + //! this just defines the default for the log file size. + DEFAULT_LOG_FILE_SIZE = 0x10F00D + }; + + bool good() const; + //!< returns true if the logger appears correctly hooked up to a file. + /*!< note that we don't open the file when file_logger is constructed; + it is only opened once the first logging is attempted. */ + + bool reopen(); + //!< closes the current file and attempts to reopen it. + /*!< this is handy if the original opening of the file failed. */ + + basis::outcome log(const basis::base_string &info, int filter = basis::ALWAYS_PRINT); + //!< writes information to the log file (if the filename is valid). + /*!< the "filter" value is checked to see if it is in the current set + of allowed filters. a value of zero is always printed. if the filename() + has not been set, then the information is lost. */ + + basis::outcome log_bytes(const basis::byte_array &to_log, int filter = basis::ALWAYS_PRINT); + //!< sends a stream of bytes "to_log" without interpretation into the log. + /*!< if the "filter" is not enabled, then the info is just tossed out. */ + + basis::outcome format_bytes(const basis::byte_array &to_log, int filter = basis::ALWAYS_PRINT); + //!< fancifully formats a stream of bytes "to_log" and sends them into log. + + basis::astring name() const; + //!< observes the filename where logged information is written. + void name(const basis::astring &new_name); + //!< modifies the filename where logged information will be written. + /*!< if "new_name" is blank, then the logged information will not + be saved. */ + + int limit() const { return int(_file_limit); } + //!< observes the allowable size of the log file. + void limit(int new_limit) { _file_limit = new_limit; } + //!< modifies the allowable size of the log file. + + void flush(); + //!< causes any pending writes to be sent to the output file. + + void truncate(size_t new_size); + //!< chops the file to ensure it doesn't go much over the file size limit. + /*!< this can be used externally also, but be careful with it. */ + + //! returns a log file name for file_logger based on the program name. + /*! for a program named myapp.exe, this will be in the form: + {logging_dir}/myapp.log + */ + static basis::astring log_file_for_app_name(); + +private: + basis::astring *_filename; //!< debugging output file. + size_t _file_limit; //!< maximum length of file before truncation. + filesystem::byte_filer *_outfile; //!< the object that points at our output file. + basis::mutex *_flock; //!< protects the file and other parameters. + + int size_reduction() const; + //!< returns the size of the chunk to truncate from the file. + /*!< this is a percentage of the maximum size allowed. */ + + bool open_file(); + //!< if the opening of the file is successful, then true is returned. + /*!< also, the _outfile member variable is non-zero. */ + + void close_file(); + //!< shuts down the file, if any, we had opened for logging. + + // unavailable. + file_logger(const file_logger &); + file_logger &operator =(const file_logger &); +}; + +////////////// + +//! a macro that retasks the program-wide logger as a file_logger. +#define SETUP_FILE_LOGGER { \ + loggers::standard_log_base *old_log = loggers::program_wide_logger::set \ + (new loggers::file_logger(loggers::file_logger::log_file_for_app_name())); \ + WHACK(old_log); \ +} + +} //namespace. + +#endif + diff --git a/core/library/loggers/filter_set.h b/core/library/loggers/filter_set.h new file mode 100644 index 00000000..19b0baea --- /dev/null +++ b/core/library/loggers/filter_set.h @@ -0,0 +1,76 @@ +#ifndef FILTER_SET_CLASS +#define FILTER_SET_CLASS + +/*****************************************************************************\ +* * +* Name : filter_set +* Author : Chris Koeritz +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include + +namespace loggers { + +//! A simple object that wraps a templated set of ints. +class filter_set : public structures::set, public virtual basis::root_object +{ +public: + filter_set() {} + //!< Constructs an empty set of filters. + + virtual ~filter_set() {} + + filter_set(const structures::set &to_copy) : structures::set(to_copy) {} + //!< Constructs a copy of the "to_copy" array. + + DEFINE_CLASS_NAME("filter_set"); + + //! Adds a member to the filter set. + /*! The filter set is used to check all extended filter values passed to + log and print. if the special filters of ALWAYS_PRINT or NEVER_PRINT are + added, then either everything will be logged or nothing will be. */ + virtual void add_filter(int new_filter) { + basis::auto_synchronizer l(c_lock); + add(new_filter); + } + + //! Removes a member from the filter set. + virtual void remove_filter(int old_filter) { + basis::auto_synchronizer l(c_lock); + remove(old_filter); + } + + //! Returns true if the "filter_to_check" is a member of the filter set. + /*! If "filter_to_check" is ALWAYS_PRINT, this always returns true. If + the value is NEVER_PRINT, false is always returned. */ + virtual bool member(int filter_to_check) { + if (filter_to_check == basis::ALWAYS_PRINT) return true; + if (filter_to_check == basis::NEVER_PRINT) return false; + return structures::set::member(filter_to_check); + } + + //! Resets the filter set to be empty. + virtual void clear_filters() { + basis::auto_synchronizer l(c_lock); + clear(); + } + +private: + basis::mutex c_lock; +}; + +} // namespace. + +#endif + diff --git a/core/library/loggers/logging_filters.h b/core/library/loggers/logging_filters.h new file mode 100644 index 00000000..43ae2b49 --- /dev/null +++ b/core/library/loggers/logging_filters.h @@ -0,0 +1,41 @@ +#ifndef LOGGING_FILTERS_GROUP +#define LOGGING_FILTERS_GROUP + +////////////// +// Name : logging_filters +// Author : Chris Koeritz +////////////// +// Copyright (c) 2010-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +#include + +namespace loggers { + + enum logging_filters { + // synonyms for filters that are important enough to always show. + FILT_FATAL = basis::ALWAYS_PRINT, + FILT_ERROR = basis::ALWAYS_PRINT, + // the first really selectable filters follow... one might notice a + // small similarity to levels available with log4j. + DEFINE_FILTER(FILT_WARNING, 1, "Important or unusual condition occurred in the runtime."), + DEFINE_FILTER(FILT_INFO, 2, "Information from normal runtime activities."), + DEFINE_FILTER(FILT_DEBUG, 3, "Noisy debugging information from objects."), + // occasionally useful filters for locally defined categories. + DEFINE_FILTER(FILT_UNUSUAL, 4, "Unusual but not necessarily erroneous events."), + DEFINE_FILTER(FILT_NETWORK, 5, "Network activity and events."), + DEFINE_FILTER(FILT_DATABASE, 6, "Data storage activities or warnings.") + }; + +} //namespace. + +#endif + diff --git a/core/library/loggers/logging_macros.h b/core/library/loggers/logging_macros.h new file mode 100644 index 00000000..31547a41 --- /dev/null +++ b/core/library/loggers/logging_macros.h @@ -0,0 +1,102 @@ +#ifndef LOGGING_MACROS_GROUP +#define LOGGING_MACROS_GROUP + +/*****************************************************************************\ +* * +* Name : logging macros * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +/*! @file logging_macros.h + these macros can assist in logging. they rely on the base_logger class and the program-wide + logger class for logging services. note that it is often convenient to customize one or more + of these to yield a simpler macro name per project, such as PRINT or LOG. +*/ + +#include +#include +#include + +///hmmm: very temporary until the call stack tracking is available again. +#define update_current_stack_frame_line_number(x) + +//! Logs a string "to_log" on "the_logger" using the "filter". +/*! The filter is checked before the string is allowed to come into +existence, which saves allocations when the item would never be printed +out anyway. */ +#define FILTER_LOG(the_logger, to_log, filter) { \ + if (the_logger.member(filter)) { \ + the_logger.log(to_log, filter); \ + } \ +} + +//! Logs a string at the emergency level, meaning that it always gets logged. +#define EMERGENCY_LOG(the_logger, to_log) \ + FILTER_LOG(the_logger, to_log, basis::ALWAYS_PRINT) + +//! Corresponding functions for including the time and date in the log entry. +#define STAMPED_FILTER_LOG(the_logger, to_log, filter) { \ + if (the_logger.member(filter)) { \ + astring temp_log = to_log; \ + if (temp_log.length()) \ + temp_log.insert(0, timely::time_stamp::notarize(true)); \ + the_logger.log(temp_log, filter); \ + } \ +} +//! Time-stamped logging that will always be printed. +#define STAMPED_EMERGENCY_LOG(the_logger, to_log) \ + STAMPED_FILTER_LOG(the_logger, to_log, basis::ALWAYS_PRINT) + +//! Class specific logging method that uses a filter. +/* These add a class and function name to the log entry. */ +#define CLASS_FILTER_LOG(the_logger, to_log, filter) { \ + update_current_stack_frame_line_number(__LINE__); \ + if (the_logger.member(filter)) { \ + astring temp_log = to_log; \ + if (temp_log.length()) { \ + temp_log.insert(0, timely::time_stamp::notarize(true)); \ + BASE_FUNCTION(func); \ + temp_log += " ["; \ + temp_log += function_name; \ + temp_log += "]"; \ + } \ + the_logger.log(temp_log, filter); \ + } \ + update_current_stack_frame_line_number(__LINE__); \ +} +//! Class specific logging method that always prints. +#define CLASS_EMERGENCY_LOG(the_logger, to_log) \ + CLASS_FILTER_LOG(the_logger, to_log, basis::ALWAYS_PRINT) + +//! Logs information that includes specific class instance information. +/*! This use the instance name of the object, which can include more +information than the simple class name. */ +#define INSTANCE_FILTER_LOG(the_logger, to_log, filter) { \ + update_current_stack_frame_line_number(__LINE__); \ + if (the_logger.member(filter)) { \ + astring temp_log = to_log; \ + if (temp_log.length()) { \ + temp_log.insert(0, timely::time_stamp::notarize(true)); \ + BASE_INSTANCE_FUNCTION(func); \ + temp_log += " ["; \ + temp_log += function_name; \ + temp_log += "]"; \ + } \ + the_logger.log(temp_log, filter); \ + update_current_stack_frame_line_number(__LINE__); \ + } \ +} +//! Logs with class instance info, but this always prints. +#define INSTANCE_EMERGENCY_LOG(the_logger, to_log) \ + INSTANCE_FILTER_LOG(the_logger, to_log, basis::ALWAYS_PRINT) + +#endif + diff --git a/core/library/loggers/makefile b/core/library/loggers/makefile new file mode 100644 index 00000000..dceb386c --- /dev/null +++ b/core/library/loggers/makefile @@ -0,0 +1,10 @@ +include cpp/variables.def + +PROJECT = loggers +TYPE = library +SOURCE = combo_logger.cpp console_logger.cpp critical_events.cpp file_logger.cpp \ + program_wide_logger.cpp +TARGETS = loggers.lib + +include cpp/rules.def + diff --git a/core/library/loggers/program_wide_logger.cpp b/core/library/loggers/program_wide_logger.cpp new file mode 100644 index 00000000..c91d9771 --- /dev/null +++ b/core/library/loggers/program_wide_logger.cpp @@ -0,0 +1,35 @@ +/*****************************************************************************\ +* * +* Name : program_wide_logger * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1994-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "program_wide_logger.h" + +using namespace basis; +using namespace loggers; + +namespace loggers { + +standard_log_base *program_wide_logger::c_the_wide_log = new null_logger; + +standard_log_base &program_wide_logger::get() { return *c_the_wide_log; } + +standard_log_base *program_wide_logger::set(standard_log_base *new_log) +{ + if (!new_log) return NIL; // can't fool me that easily. + standard_log_base *old_log = c_the_wide_log; + c_the_wide_log = new_log; + return old_log; +} + +} //namespace. + diff --git a/core/library/loggers/program_wide_logger.h b/core/library/loggers/program_wide_logger.h new file mode 100644 index 00000000..70280742 --- /dev/null +++ b/core/library/loggers/program_wide_logger.h @@ -0,0 +1,84 @@ +#ifndef PROGRAM_WIDE_LOGGER_FACILITY +#define PROGRAM_WIDE_LOGGER_FACILITY + +/*****************************************************************************\ +* * +* Name : program_wide_logger facility * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "logging_macros.h" + // it seems reasonable to provide the logging macros to any file that is also using + // the program-wide logger. + +#include +#include +#include + +namespace loggers { + +//! A class that provides logging facilities vertically to all of hoople. +/*! + Provides a per-program logging subsystem that can be used from almost + anywhere in the source code. + The program wide logger feature is a globally defined object that + can be switched out to perform different types of logging. + Note: this facility is not thread-safe. The coder must guarantee + that the appropriate logger is set up before cranking up a bunch of + threads that use logging. +*/ + +class program_wide_logger +{ +public: + static loggers::standard_log_base &get(); + //!< Provided by the startup code within each application for logging. + /*!< This can be used like any base_logger object, but the diagnostic items + will be stored into one log file (or other logging mechanism) per + program. */ + + static loggers::standard_log_base *set(loggers::standard_log_base *new_log); + //!< replaces the current program-wide logger with the "new_log". + /*! The "new_log" must be a valid base_logger object. The responsibility + for maintaining "new_log" is taken over by the program-wide logger. + In return, the old program-wide logger is handed back to you and it is + now your responsibility. Be very careful with it if it's owned by other + code and not totally managed by you. */ + +private: + static loggers::standard_log_base *c_the_wide_log; //!< the actual logger. +}; + +////////////// + +//! a trash can for logging; throws away all entries. +/*! + Provides a logger that throws away all of the strings that are logged to it. + This is useful when an interface requires a base_logger but one does not + wish to generate an output file. This object is similar to /dev/null in Unix + (or Linux) and to nul: in Win32. +*/ + +class null_logger : public virtual standard_log_base +{ +public: + virtual ~null_logger() {} + DEFINE_CLASS_NAME("null_logger"); + virtual basis::outcome log(const basis::base_string &info, int filter) { + if (filter || !(&info)) {} + return basis::common::OKAY; + } +}; + +} //namespace. + +#endif // outer guard. + diff --git a/core/library/loggers/standard_log_base.h b/core/library/loggers/standard_log_base.h new file mode 100644 index 00000000..b27fdcd8 --- /dev/null +++ b/core/library/loggers/standard_log_base.h @@ -0,0 +1,44 @@ +#ifndef STANDARD_LOG_BASE_CLASS +#define STANDARD_LOG_BASE_CLASS + +////////////// +// Name : standard_log_base +// Author : Chris Koeritz +////////////// +// Copyright (c) 2010-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +#include "eol_aware.h" +#include "filter_set.h" + +#include + +namespace loggers { + +//! A base class for a very usable logger with a filter_set and eol awareness. +/*! + We add this derived class of base_logger to incorporate some useful functionality + for managing filters without polluting the base class. This class allows the logging + functionality to not deal with a lot of add-ins or chicanery. +*/ + +class standard_log_base +: public virtual basis::base_logger, + public virtual basis::nameable, + public virtual filter_set, + public virtual eol_aware +{ +}; + +} //namespace. + +#endif + diff --git a/core/library/makefile b/core/library/makefile new file mode 100644 index 00000000..c96ec24a --- /dev/null +++ b/core/library/makefile @@ -0,0 +1,31 @@ +include variables.def + +PROJECT = core_libraries +BUILD_BEFORE = algorithms \ + basis \ + structures \ + timely \ + mathematics \ + textual \ + nodes \ + filesystem \ + configuration \ + loggers \ + processes \ + application \ + unit_test \ + tests_basis \ + tests_algorithms \ + tests_structures \ + tests_filesystem \ + tests_mathematics \ + tests_nodes \ + tests_textual \ + tests_timely \ + tests_configuration \ + versions \ + crypto \ + tests_crypto + +include rules.def + diff --git a/core/library/mathematics/averager.h b/core/library/mathematics/averager.h new file mode 100644 index 00000000..e45dad9d --- /dev/null +++ b/core/library/mathematics/averager.h @@ -0,0 +1,170 @@ +#ifndef AVERAGER_CLASS +#define AVERAGER_CLASS + +/*****************************************************************************\ +* * +* Name : averager * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1997-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +namespace mathematics { + +//! Maintains a list of numbers and provides the average for them. +/*! + The list entries can be weighted if desired. If the list grows too large, + the first chunk of the entries is added as a weighted average and the list's + size is reduced appropriately. +*/ + +template +class averager +{ +public: + averager(int entries = 100, bool compacting = true); + //!< creates an averager whose list length is restricted to "entries". + /*!< if "entries" that is zero, then the maximum length is used. if + "compacting" is true, then the entries which need to be removed during + a compaction will be added to the list as a weighted average. if it's + false, then the unfortunate entries are just eliminated. the first style + leads to a more steady state version of the average while the other is + more recent history. note that the lowest reasonable value for "entries" + is eight due to the compaction algorithm; lower values will work, but the + averager will allow the list to grow to eight anyway. */ + + void add(contents value, int count = 1); + //!< adds a new "value" to the averager, with an optional "count". + + contents average() const { return average(0, length() - 1); } + //!< reports the overall average of the whole list. + + int samples() const; + //!< returns the total number of samples recorded in the average. + + int length() const { return _averages.length(); } + //!< returns the current length of the averages list. + + contents average(int start, int end) const; + //!< reports the average over the range from "start" to "end" inclusive. + + struct weighted_entry { contents value; int count; }; + //!< structure holding a weighted chunk of the average. + /*!< an entry in the list of averages has a "value" and a "count" that + measures the weight with which that "value" is considered. */ + + weighted_entry get(int index) const { return _averages.get(index); } + //!< accesses the entry stored at the "index" specified. + + void compact(); + //!< chops off the oldest portion of the averager. + /*!< if this is a compacting style averager, then the older data is + coalesced and added as a weighted entry. */ + + void check_for_compaction(); + //!< checks whether the averager needs to be compacted yet or not. + /*!< the decision is made according to the maximum allowable size in + "entries" passed to the constructor. if "entries" is zero, the maximum + allowable size is used instead. */ + +private: + bool _do_compaction; //!< do truncated values coalesce to a weighted entry? + basis::array _averages; //!< current list. + int _entries; //!< maximum length of list. +}; + +////////////// + +//! keeps an average on a stream of integers. +class int_averager : public averager +{ +public: + int_averager(int entries = 100, bool compacting = true) + : averager(entries, compacting) {} +}; + +////////////// + +// implementations below... + +const int AVERAGER_SIZE_LIMIT = 180000; // the most items we'll try to handle. + +template +averager::averager(int entries, bool compacting) +: _do_compaction(compacting), _averages(), _entries(entries) +{ + int unit_size = sizeof(weighted_entry); + if (basis::negative(_entries) || !_entries) + _entries = int(AVERAGER_SIZE_LIMIT / unit_size); +} + +template +void averager::compact() +{ + if (length() < 8) return; // sorry, you're too short for this wild ride. + int end_whacking = _averages.length() / 4; + if (_do_compaction) { + contents whacked_average = average(0, end_whacking); + _averages.zap(1, end_whacking); + _averages[0].value = whacked_average; + _averages[0].count = end_whacking + 1; + } else _averages.zap(0, end_whacking); +} + +template +void averager::check_for_compaction() +{ + // make sure that we don't go over our allotted space. + int unit_size = sizeof(weighted_entry); + int limit = basis::minimum(AVERAGER_SIZE_LIMIT, _entries * unit_size); + if (int(_averages.length() + 2) * unit_size >= limit) compact(); +} + +template +void averager::add(contents value, int count) +{ + weighted_entry to_add; + to_add.value = value; + to_add.count = count; + check_for_compaction(); + _averages += to_add; +} + +template +contents averager::average(int start, int end) const +{ + bounds_return(start, 0, length() - 1, 0); + bounds_return(end, start, length() - 1, 0); + bounds_return(end - start + 1, 1, length(), 0); + + contents accum = 0; + contents count = 0; + for (int i = start; i <= end; i++) { + accum += get(i).value * get(i).count; + count += get(i).count; + } + if (!count) count = 1; + return accum / count; +} + +template +int averager::samples() const +{ + int to_return = 0; + for (int i = 0; i < length(); i++) to_return += get(i).count; + return to_return; +} + +} //namespace. + +#endif + diff --git a/core/library/mathematics/chaos.h b/core/library/mathematics/chaos.h new file mode 100644 index 00000000..ffb1a310 --- /dev/null +++ b/core/library/mathematics/chaos.h @@ -0,0 +1,113 @@ +#ifndef CHAOS_CLASS +#define CHAOS_CLASS + +////////////// +// Name : chaos +// Author : Chris Koeritz +////////////// +// Copyright (c) 1991-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +#include +#include +#include +#include + +//#define DEBUG_CHAOS + // uncomment for noisy logging. + +#ifdef DEBUG_CHAOS + #include +#endif +#include +#include +#ifdef __UNIX__ + #include +#endif + +namespace mathematics { + +// gets a 32 bit integer from the 15 bit random generator. it's 15 bit because +// rand() only generates numbers up to the MAX_RAND, which in the visual c++ +// case is limited to 0x7FFF. so, we let the first random number generated +// serve as the upper two bytes and we shift another one over a bit to cover +// the whole range of the third and fourth byte, except for that last bit, +// which is added in as a binary random value. +#define GET_32_BIT_RAND_YO \ + basis::un_int ranval = (basis::un_int(rand()) << 16) + (basis::un_int(rand()) << 1) \ + + (basis::un_int(rand()) % 2) + +//! a platform-independent way to acquire random numbers in a specific range. +/*! + This object also re-seeds the underlying system's random seed when retrain() + is invoked. +*/ + +class chaos : public virtual basis::nameable +{ +public: + chaos() { retrain(); } + virtual ~chaos() {} + + DEFINE_CLASS_NAME("chaos"); + + void retrain() + //!< Scrambles the OS's random seed and then provides pseudo-random values. + { + static unsigned int __flaxen = 0; + if (!__flaxen) { + time_t time_num; + time(&time_num); + // "t" is an ugly pointer into system data that contains the time nicely + // broken up into segments. the pointer cannot be freed! + tm *t = localtime(&time_num); + int add_in_milliseconds = basis::environment::system_uptime(); + // create a good random number seed from the time. +#ifdef DEBUG_CHAOS + printf("day %d hour %d min %d sec %d", (int)t->tm_mday, (int)t->tm_hour, + (int)t->tm_min, (int)t->tm_sec); +#endif + // we really don't care very much about the small chance of this being + // initialized to zero again. the chance of that happening more than once + // should be pretty astronomical. + __flaxen = (t->tm_sec + 60 * t->tm_mday + 60 * 31 * t->tm_hour + + 24 * 60 * 31 * t->tm_min) ^ add_in_milliseconds; + // initialize random generator. + srand(int(__flaxen)); + } + } + + // Keep in mind for the inclusive and exclusive functions that a range which + // is too large might not be dealt with well. This is because the random + // functions provided by the O.S. may not support the full range of integers. + + int inclusive(int low, int high) const + //!< Returns a pseudo-random number r, such that "low" <= r <= "high". + { + if (high < low) return low; + unsigned int range = high - low + 1; + GET_32_BIT_RAND_YO; + int adjusted = ranval % range + low; + return adjusted; + } + + int exclusive(int low, int high) const + //!< Returns a pseudo-random number r, such that "low" < r < "high". + { + if (high < low) return low + 1; + unsigned int range = high - low - 1; + GET_32_BIT_RAND_YO; + int adjusted = ranval % range + low + 1; + return adjusted; + } + +}; + +} //namespace. + +#endif + diff --git a/core/library/mathematics/double_plus.h b/core/library/mathematics/double_plus.h new file mode 100644 index 00000000..25e68345 --- /dev/null +++ b/core/library/mathematics/double_plus.h @@ -0,0 +1,101 @@ +#ifndef DOUBLE_PLUS_CLASS +#define DOUBLE_PLUS_CLASS + +/*****************************************************************************\ +* * +* Name : double_plus (an extension for double floating point numbers) * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1993-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "math_ops.h" + +#include +#include +#include + +#include //temp + +//! An extension to floating point primitives providing approximate equality. +/*! + Allows a programmer to ignore issues of rounding errors on floating point + numbers by specifying that two floating point numbers are equivalent if + they are equal within a small number "delta". This can help to eliminate + errors in floating point logic. +*/ + +namespace mathematics { + +class double_plus : public basis::orderable +{ +public: + #define DEFAULT_DELTA 0.0001 + /*!< the delta is the acceptable amount of difference between two floating + point numbers that are considered equivalent by this class. if they + differ by more than that, they are considered non-equivalent (and + hence must be greater than or less than each other). */ + + //! initializes using "init" as the initial value and equality within "delta". + double_plus(double init = 0.0, double delta = DEFAULT_DELTA) : c_value(init), c_delta(delta) {} + + //! initializes this from "to_copy". + double_plus(const double_plus &to_copy) : c_value(to_copy.c_value), c_delta(to_copy.c_delta) {} + + virtual ~double_plus() {} + + DEFINE_CLASS_NAME("double_plus"); + + //! standard assignment operator. + double_plus &operator = (const double_plus &cp) + { c_value = cp.c_value; c_delta = cp.c_delta; return *this; } + + double value() const { return truncate(); } + //!< observes the value held in this. + operator double () const { return truncate(); } + //!< observes the value held in this. + + double delta() const { return c_delta; } + //!< observes the precision for equality comparisons. + void delta(double new_delta) { c_delta = new_delta; } + //!< modifies the precision for equality comparisons. + + double truncate() const { return math_ops::round_it(c_value / c_delta) * c_delta; } + //!< returns a version of the number that is chopped off past the delta after rounding. + + //! returns true if this equals "f2" within the "delta" precision. + virtual bool equal_to(const basis::equalizable &f2) const { + const double_plus *cast = dynamic_cast(&f2); + if (!cast) return false; + return this->d_eq(*cast); + } + + //!< returns true if this is less than "f2". + virtual bool less_than(const basis::orderable &f2) const + { + const double_plus *cast = dynamic_cast(&f2); + if (!cast) return false; + return !this->d_eq(*cast) && (c_value < cast->c_value); + } + +private: + double c_value; //!< the contained floating point value. + double c_delta; //!< the offset within which equality is still granted. + + //! returns true if "to_compare" is within the delta of this. + bool d_eq(const double_plus &to_compare) const { + double diff = basis::absolute_value(c_value - to_compare.value()); + return diff < basis::absolute_value(c_delta); + } +}; + +} //namespace. + +#endif + diff --git a/core/library/mathematics/makefile b/core/library/mathematics/makefile new file mode 100644 index 00000000..08038396 --- /dev/null +++ b/core/library/mathematics/makefile @@ -0,0 +1,9 @@ +include cpp/variables.def + +PROJECT = mathematics +TYPE = library +SOURCE = placeholder.cpp +TARGETS = mathematics.lib + +include cpp/rules.def + diff --git a/core/library/mathematics/math_ops.h b/core/library/mathematics/math_ops.h new file mode 100644 index 00000000..b2f7f879 --- /dev/null +++ b/core/library/mathematics/math_ops.h @@ -0,0 +1,65 @@ +#ifndef MATH_OPS_GROUP +#define MATH_OPS_GROUP + +/*****************************************************************************\ +* * +* Name : mathematical operations * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace mathematics { + +//! A grab-bag of mathematical functions that are frequently useful. + +class math_ops +{ +public: + //! returns the rounded integer value for "to_round". + static int round_it(float to_round) + { + int to_return = int(to_round); + // this uses a simplistic view of rounding. + if (to_round - float(to_return) > 0.5) to_return++; + return to_return; + } + + //! returns the rounded integer value for "to_round". + static int round_it(double to_round) + { + int to_return = int(to_round); + // this uses a simplistic view of rounding. + if (to_round - double(to_return) > 0.5) to_return++; + return to_return; + } + +/* + //! returns the number two to the power "raise_to" (i.e. 2^raise_to). + static basis::u_int pow_2(const basis::u_int raise_to) + { + if (!raise_to) return 1; + basis::u_int to_return = 2; + for (basis::u_int i = 1; i < raise_to; i++) + to_return *= 2; + return to_return; + } +*/ + + //! returns n! (factorial), which is n * (n - 1) * (n - 2) ... + static basis::un_int factorial(int n) + { return (n < 2)? 1 : n * factorial(n - 1); } +}; + +} //namespace. + +#endif + diff --git a/core/library/mathematics/placeholder.cpp b/core/library/mathematics/placeholder.cpp new file mode 100644 index 00000000..bc3d86fe --- /dev/null +++ b/core/library/mathematics/placeholder.cpp @@ -0,0 +1,15 @@ + + +// sole purpose of this is to make the lib be generated, +// even though we currently do not have any code that lives inside it. + + +#include "double_plus.h" + +using namespace mathematics; + +int fu() +{ +double_plus ted; +return (int)ted; +} diff --git a/core/library/nodes/list.cpp b/core/library/nodes/list.cpp new file mode 100644 index 00000000..3429f8e7 --- /dev/null +++ b/core/library/nodes/list.cpp @@ -0,0 +1,270 @@ + + + +/*****************************************************************************\ +* * +* Name : list * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +// POLICIES: +// +// the cursor should never be stored to or deleted; it is merely a scanner that +// runs through the list. +// +// the cursor can point at the head or tail. any storage action is taken to +// mean that it applies to the closest real object, if one exists. any query +// action is taken similarly. + +#include "list.h" +#include "node.h" + +#include + +namespace nodes { + +// nice names for the positions of the next link and the previous link in +// our node's indices. +const int PREVIOUS = 0; +const int NEXT = 1; + +////////////// + +// iterator functions: + +void list::iterator::operator ++() +{ + if (is_tail()) return; // already at the end. + _cursor = _cursor->get_link(NEXT); +} + +void list::iterator::operator --() +{ + if (is_head()) return; // already at the front. + _cursor = _cursor->get_link(PREVIOUS); +} + +bool list::iterator::operator ==(const iterator &to_compare) const +{ return _cursor == to_compare._cursor; } + +const node *list::iterator::observe() +{ + if (!_manager || _manager->empty()) return NIL; + if (*this == _manager->head()) next(); + if (*this == _manager->tail()) previous(); + return _cursor; +} + +node *list::iterator::access() +{ + if (!_manager || _manager->empty()) return NIL; + if (*this == _manager->head()) next(); + if (*this == _manager->tail()) previous(); + return _cursor; +} + +bool list::iterator::is_head() const +{ + if (!_manager) return false; + return _cursor == _manager->_head; +} + +bool list::iterator::is_tail() const +{ + if (!_manager) return false; + return _cursor == _manager->_tail; +} + +void list::iterator::jump_head() +{ + if (!_manager) return; + _cursor = _manager->_head; +} + +void list::iterator::jump_tail() +{ + if (!_manager) return; + _cursor = _manager->_tail; +} + +////////////// + +list::list() +: _head(NIL), _tail(NIL) +{ + _head = new node(2); + _tail = new node(2); + _head->set_link(NEXT, _tail); + _tail->set_link(PREVIOUS, _head); +} + +list::~list() +{ + iterator zapper = head(); + while (!empty()) + zap(zapper); + WHACK(_head); + WHACK(_tail); +} + +bool list::empty() const +{ + if (_head->get_link(NEXT) == _tail) return true; + return false; +} + +bool list::set_index(iterator &where, int new_index) +{ + if (where._manager != this) return false; + if (empty()) return false; + node *skipper = _head->get_link(NEXT); + for (int i = 0; i < new_index; i++) { + skipper = skipper->get_link(NEXT); + if (skipper == _tail) return false; // out of bounds now. + } + where._cursor = skipper; + return true; +} + +bool list::forward(iterator &where, int count) +{ + if (where._manager != this) return false; + if (count <= 0) return true; + if (items_from_tail(where) < count) return false; + if (where.is_head()) where.next(); // skip the head guard. + for (int i = 0; i < count; i++) where.next(); + return true; +} + +bool list::backward(iterator &where, int count) +{ + if (where._manager != this) return false; + if (count <= 0) return true; + if (items_from_head(where) < count) return false; + if (where.is_tail()) where.previous(); // skip the tail guard. + for (int i = 0; i < count; i++) where.previous(); + return true; +} + +int list::elements() const +{ + if (empty()) return 0; + int to_return = 0; + node *skipper = _head->get_link(NEXT); + while (skipper != _tail) { + to_return++; + skipper = skipper->get_link(NEXT); + } + return to_return; +} + +int list::items_from_head(const iterator &where) const +{ + if (where._manager != this) return 0; + if (where.is_head()) return 0; // make sure it's not there already. + int index = 0; + node *skipper = _head->get_link(NEXT); + while ( (where._cursor != skipper) && (skipper != _tail) ) { + index++; + skipper = skipper->get_link(NEXT); + } + return index; +} + +int list::items_from_tail(const iterator &where) const +{ + if (where._manager != this) return 0; + if (where.is_tail()) return 0; // make sure it's not there already. + int index = 0; + node *skipper = _tail->get_link(PREVIOUS); + while ( (where._cursor != skipper) && (skipper != _head) ) { + index++; + skipper = skipper->get_link(PREVIOUS); + } + return index; +} + +/* +node *list::get() +{ + if (empty()) return NIL; // make sure the list isn't empty. + if (_cursor == _head) return _head->get_link(NEXT); + // check special case for pointing at the head. + if (_cursor == _tail) return _tail->get_link(PREVIOUS); + // check special case for pointing at the tail. + return _cursor; +} +*/ + +node *list::remove(iterator &where) +{ + if (where._manager != this) return NIL; + if (empty()) return NIL; + if (where._cursor == _head) + where._cursor = _head->get_link(NEXT); + if (where._cursor == _tail) + where._cursor = _tail->get_link(PREVIOUS); + node *old_cursor = where._cursor; + node *old_previous = old_cursor->get_link(PREVIOUS); + node *old_next = old_cursor->get_link(NEXT); + old_cursor->set_link(PREVIOUS, NIL); + old_cursor->set_link(NEXT, NIL); + old_previous->set_link(NEXT, old_next); + old_next->set_link(PREVIOUS, old_previous); + where._cursor = old_next; + return old_cursor; +} + +void list::zap(iterator &where) { delete remove(where); } + +void list::append(iterator &where, node *new_node) +{ + if (where._manager != this) return; + while (new_node->links() < 2) new_node->insert_link(0, NIL); + if (empty()) where._cursor = _head; + if (where._cursor == _tail) + where._cursor = _tail->get_link(PREVIOUS); + // shift from the tail sentinel to the tail element. + node *save_next = where._cursor->get_link(NEXT); + where._cursor->set_link(NEXT, new_node); + new_node->set_link(PREVIOUS, where._cursor); + new_node->set_link(NEXT, save_next); + save_next->set_link(PREVIOUS, new_node); + where._cursor = new_node; +} + +void list::insert(iterator &where, node *new_node) +{ + if (where._manager != this) return; + while (new_node->links() < 2) new_node->insert_link(0, NIL); + if (empty()) where._cursor = _tail; + if (where._cursor == _head) + where._cursor = _head->get_link(NEXT); + // shift from head sentinel to the head element. + node *save_prior = where._cursor->get_link(PREVIOUS); + where._cursor->set_link(PREVIOUS, new_node); + new_node->set_link(NEXT, where._cursor); + new_node->set_link(PREVIOUS, save_prior); + save_prior->set_link(NEXT, new_node); + where._cursor = new_node; +} + +void list::zap_all() +{ + iterator zapper = head(); + while (!empty()) zap(zapper); +} + +} // namespace. + + + + diff --git a/core/library/nodes/list.h b/core/library/nodes/list.h new file mode 100644 index 00000000..455ebaa4 --- /dev/null +++ b/core/library/nodes/list.h @@ -0,0 +1,190 @@ +#ifndef LIST_CLASS +#define LIST_CLASS + +/*****************************************************************************\ +* * +* Name : list * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + + + +namespace nodes { + +class node; // forward. + +//! Implements a guarded, doubly linked list structure. +/*! + The list is viewed one element at a time, through the monocular of an + iterator, which keeps track of the current position in the list. the + iterator's cursor can be shifted around at will. nodes can be added to + the list before or after the cursor, and the node pointed at by the cursor + can be queried or modified. Using multiple iterators is fine as long as + you guarantee that they either are all just reading the list or that you + have a valid access pattern of reads and writes such that no iterator's + cursor is affected. Note that this list is not thread safe. +*/ + +class list +{ +public: + list(); + //!< constructs a blank list. + + ~list(); + //!< invalidates all contents of the list and destroys all child nodes. + + int elements() const; + //!< returns the number of items currently in the list. + /*!< this is a costly operation. */ + + bool empty() const; + //!< returns true if the list is empty. + /*!< this is really quick compared to checking the number of elements. */ + + //! iterators allow the list to be traversed. + /*! NOTE: it is an error to use an iterator on one list with a different + list; the methods will do nothing or return empty results in this + situation. */ + + class iterator { + public: + iterator(const list *mgr, node *start) : _cursor(start), _manager(mgr) {} + //!< opens up an iterator on a list. + /*!< the preferred method to construct an iterator is to use the + head/tail functions in list. */ + + void operator ++(); //!< go to next item. + void operator --(); //!< go to previous item. + void operator ++(int) { ++(*this); } //!< post-fix synonym for ++. + void operator --(int) { --(*this); } //!< post-fix synonym for --. + + + void next() { (*this)++; } //!< synonym for ++. + void previous() { (*this)--; } //!< synonyms for --. + + bool operator ==(const iterator &to_compare) const; + //!< returns true if the two iterators are at the same position. + + bool is_head() const; + //!< returns true if the cursor is at the head of the list. + /*!< Note: head() and tail() only return true if the iterator is + actually positioned _at_ the head or tail guard. for example, if the + cursor is pointing at the last actual element in the list (but not at + the guard itself), then is_tail() returns false. so, in iteration, + your iterator will actually go past the last valid element before + is_tail() returns true. thus it is very important to check is_tail() + *before* looking at the node with observe() or access(), since those + two will shift the list position to the nearest valid node and away + from the guard. */ + bool is_tail() const; + //!< returns true if the cursor is at the tail of the list. + + void jump_head(); //!< set the iterator to the head. + void jump_tail(); //!< set the iterator to the tail. + + const node *observe(); //!< peek at the current element. + /*!< Note: observe() and access() will return the first element if the + list is positioned at the head (or the last if at the tail), and will + not return NIL for these two positions as long as the list has some + elements in it. the cursor will also have been moved to point at the + element that is returned. + Another Note: it is extremely important that you do not mess with the + links owned by the node (or at least the first two links). + A Third Note: these two functions can return NIL if the list is empty. */ + node *access(); //!< obtain access to the current element. + + private: + node *_cursor; //!< the current position. + friend class list; //!< lists have full access to this object. + const list *_manager; //!< our friendly manager. + }; + + iterator head() const { return iterator(this, _head); } + //!< returns an iterator located at the head of the list. + iterator tail() const { return iterator(this, _tail); } + //!< returns an iterator located at the tail of the list. + + int index(const iterator &it) const { return items_from_head(it); } + //!< returns the zero-based index of the cursor's position from the head. + /*!< this is a synonym for items_from_head(). */ + + bool set_index(iterator &to_set, int new_index); + //!< positions the iterator "to_set" at the index specified. + + // storage and retrieval actions. + // Note: all of these methods shift the iterator onto the next valid node + // if it is positioned at the beginning or end of the list. + + void append(iterator &where, node *new_node); + //!< adds a "new_node" into the list immediately after "where". + /*!< the nodes previously following the current node (if any) will follow + after the "new_node". this does not change the current position. + ownership of "new_node" is taken over by the list. */ + + void insert(iterator &where, node *new_node); + //!< places a "new_node" immediately before the current node in the list. + /*!< the "new_node" will follow the prior precursor to the current node. + this does not change the current position. ownership of "new_node" + is taken over by the list. after the call, the iterator points at + the new node. */ + + node *remove(iterator &where); + //!< extracts the current item from "where" and repairs the hole. + /*!< NIL is returned if the list was empty. the current position is set + to the node that used to be after the node that has been removed. after + the call, the iterator points at the new node. */ + + void zap(iterator &where); + //!< wipes out the item at "where", including its contents. + /*!< the current position is reset to the item after the now defunct + item (or the tail). */ + + void zap_all(); + //!< destroys all the list elements and makes the list empty. + /*!< any existing iterators will be invalidated by this. */ + + // the following positioning functions could fail if the request is out of + // bounds. for example, forward cannot go beyond the end of the list. in + // such cases, false is returned and the list pointer is not moved. + + bool forward(iterator &where, int count); + //!< moves the list pointer "count" items towards the tail. + /*!< Note: forward and backward operate on elements; the head and tail + guard are not included in the count. Also, negative values of "count" + are ignored. */ + bool backward(iterator &where, int count); + //!< moves the list pointer "count" items towards the head. + + int items_from_head(const iterator &where) const; + //!< Returns the number of elements between the current item and the head. + /*!< zero is returned if this is at the first element or the head. */ + int items_from_tail(const iterator &where) const; + //!< Returns the number of elements between the current item and the tail. + /*!< zero is returned if this is at the last element or the tail. */ + +private: + friend class iterator; + node *_head; //!< pointer to the first item. + node *_tail; //!< pointer to the last item. + + bool skip_or_ignore(iterator &where, int count); + //!< zips the list to the position indicated by "count", if it can. + + // not permitted. + list(const list &); + list &operator =(const list &); +}; + +} // namespace. + +#endif + diff --git a/core/library/nodes/makefile b/core/library/nodes/makefile new file mode 100644 index 00000000..b114f9e7 --- /dev/null +++ b/core/library/nodes/makefile @@ -0,0 +1,9 @@ +include cpp/variables.def + +PROJECT = nodes +TYPE = library +SOURCE = list.cpp node.cpp packable_tree.cpp path.cpp symbol_tree.cpp tree.cpp +TARGETS = nodes.lib + +include cpp/rules.def + diff --git a/core/library/nodes/node.cpp b/core/library/nodes/node.cpp new file mode 100644 index 00000000..465fa5df --- /dev/null +++ b/core/library/nodes/node.cpp @@ -0,0 +1,103 @@ +/*****************************************************************************\ +* * +* Name : node * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "node.h" + +#include +#include +#include + +using namespace basis; +using namespace structures; + +namespace nodes { + +// the internal_link class anonymously hangs onto a pointer to the object. +struct internal_link { + node *_connection; + internal_link(node *destination = NIL) : _connection(destination) {} + virtual ~internal_link() { _connection = NIL; } +}; + +class node_link_amorph : public amorph +{ +public: + node_link_amorph(int num) : amorph(num) {} +}; + +////////////// + +node::node(int number_of_links) +: _links(new node_link_amorph(number_of_links)) +{ for (int i = 0; i < number_of_links; i++) set_empty(i); } + +node::~node() +{ + _links->reset(); + WHACK(_links); +} + +int node::links() const { return _links->elements(); } + +// set_empty: assumes used correctly by internal functions--no bounds return. +void node::set_empty(int link_num) +{ + internal_link *blank_frank = new internal_link(NIL); + _links->put(link_num, blank_frank); +} + +#define test_arg(link_num) bounds_return(link_num, 0, _links->elements()-1, ); + +void node::set_link(int link_number, node *new_link) +{ + test_arg(link_number); + (*_links)[link_number]->_connection = new_link; +} + +void node::zap_link(int link_number) +{ + test_arg(link_number); + _links->zap(link_number, link_number); +} + +void node::insert_link(int where, node *to_insert) +{ + // make sure that the index to insert at will not be rejected by the + // amorph insert operation. + if (where > links()) + where = links(); + _links->insert(where, 1); + set_empty(where); + set_link(where, to_insert); +} + +node *node::get_link(int link_number) const +{ + bounds_return(link_number, 0, _links->elements()-1, NIL); + return (*_links)[link_number]->_connection; +} + +int node::which(node *branch_to_find) const +{ + int to_return = common::NOT_FOUND; + for (int i = 0; i <= links() - 1; i++) + if (branch_to_find == get_link(i)) { + to_return = i; + break; + } + return to_return; +} + +} // namespace. + diff --git a/core/library/nodes/node.h b/core/library/nodes/node.h new file mode 100644 index 00000000..1b1a6d64 --- /dev/null +++ b/core/library/nodes/node.h @@ -0,0 +1,137 @@ +#ifndef NODE_CLASS +#define NODE_CLASS + +/*****************************************************************************\ +* * +* Name : node * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace nodes { + +// forward: +class node_link_amorph; + +//! An object representing the interstitial cell in most linked data structures. +/*! + The node is intended as an extensible base class that provides general + connectivity support. Nodes can be connected to each other in + arbitrary ways, but often a derived data type provides more structured + organization. When a node's link is zapped, only the connection is + cut; no destruction is performed. + + Examples: A linked list can be represented as a node with one link that + connects to the succeeding item in the list. A doubly linked list can + be represented by a node with two links; one to the previous node and + the other to the next node. The most general structure might be an + arbitrary graph that can connect nodes to any number of other nodes. +*/ + +class node : public virtual basis::root_object +{ +public: + node(int number_of_links = 0); + //!< the constructor provides for "number_of_links" links initially. + /*!< the table below gives some common numbers for links for a variety of + data structures: @code + + Links Data Structure Purpose of Link(s) + ------ ------------------------- ---------------------------------- + 1 singly linked list points to next node in list + 2 doubly linked list one to previous node, one to next + 2 binary tree point to the two children + n n-ary tree point to the n children + n+1 n-ary doubly linked tree point to n children and 1 parent + m m-ary graph node point to m relatives. + @endcode */ + + virtual ~node(); + //!< the destructor simply invalidates the node. + /*!< this does not rearrange the links as would be appropriate for a + data structure in which only the node to be destroyed is being eliminated. + policies regarding the correct management of the links must be made in + objects derived from node. */ + + int links() const; + //!< Returns the number of links the node currently holds. + + void set_link(int link_number, node *new_link); + //!< Connects the node "new_link" to this node. + /*!< the previous link is lost, but not modified in any way. the address + of the new link is used directly--no copy of the node is made. setting a + link to a null node pointer clears that link. */ + + node *get_link(int link_number) const; + //!< Returns the node that is connected to the specified "link_number". + /*!< if the link is not set, then NIL is returned. */ + + void zap_link(int link_number); + //!< the specified link is removed from the node. + + void insert_link(int where, node *to_add = NIL); + //!< adds a new link prior to the position specified in "where". + /*!< thus a "where" value of less than or equal to zero will add a new + link as the first element. a "where" value greater than or equal to + links() will add a new link after the last element. the node "to_add" + will be stored in the new link. */ + + int which(node *to_find) const; + //!< locates the index where "to_find" lives in our list of links. + /*!< returns the link number for a particular node that is supposedly + connected to this node or common::NOT_FOUND if the node is not one + of the children. */ + +private: + node_link_amorph *_links; //!< the list of connections to other nodes. + + void set_empty(int link_number); + //!< clears the link number specified. + + // disallowed: + node(const node &); + node &operator =(const node &); +}; + +////////////// + +//! the basket class holds an object and supports connecting them as nodes. +/*! + the templated object is required to provide both a default constructor + and a copy constructor. +*/ + +template +class basket : public node +{ +public: + basket(int links, const contents &to_store = contents()) + : node(links), _storage(to_store) {} + + basket(const basket &to_copy) { *this = to_copy; } + + basket &operator = (const contents &to_copy) + { if (&to_copy != this) _storage = to_copy; return *this; } + + const contents &stored() const { return _storage; } + //!< allows a peek at the stored object. + contents &stored() { return _storage; } + //!< provides access to the stored object. + +private: + contents _storage; +}; + +} // end namespace. + +#endif + diff --git a/core/library/nodes/packable_tree.cpp b/core/library/nodes/packable_tree.cpp new file mode 100644 index 00000000..a38f88ad --- /dev/null +++ b/core/library/nodes/packable_tree.cpp @@ -0,0 +1,226 @@ +/*****************************************************************************\ +* * +* Name : packable_tree * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "packable_tree.h" + +#include +#include +#include +#include +#include + +using namespace basis; +using namespace structures; + +//#define DEBUG_PACKABLE_TREE + // uncomment for noisy debugging. + +#undef LOG +#ifdef DEBUG_PACKABLE_TREE + #include + #define LOG(to_print) printf("%s\n", astring(to_print).s()); +#else + #define LOG(s) { if (!!s) {} } +#endif + +namespace nodes { + +// tree commands are used to tell the unpacker what to do with the blobs +// it finds. BRANCHES_FOLLOW indicates that there are a few branches stored +// at the next few contiguous memory positions. ATTACH_BRANCHES means that +// the next branch should be the parent of some number of previous branches. +// FINISH means that the tree is done being stored (or reconstructed). +enum tree_commands { BRANCHES_FOLLOW, ATTACH_BRANCHES, FINISH }; + +////////////// + +packable_tree_factory::~packable_tree_factory() {} + +////////////// + +//! the TCU stores a command about this packed unit's purpose, the number of branches held, and the size of the contents at this node. +struct tree_command_unit : public virtual packable +{ + tree_commands command; + int number; + int size; + + virtual ~tree_command_unit() {} + + virtual int packed_size() const { return 3 * PACKED_SIZE_INT32; } + + virtual void pack(byte_array &packed_form) const { + attach(packed_form, int(command)); + attach(packed_form, number); + attach(packed_form, size); + } + + virtual bool unpack(byte_array &packed_form) { + int cmd; + if (!detach(packed_form, cmd)) return false; + command = (tree_commands)cmd; + if (!detach(packed_form, number)) return false; + if (!detach(packed_form, size)) return false; + return true; + } +}; + +////////////// + +packable_tree::packable_tree() : tree() {} + +void packable_tree::calcit(int &size_accumulator, const packable_tree *current_node) +{ +LOG(a_sprintf("calcing node %x", current_node)); + FUNCDEF("calcit"); + if (!current_node) throw_error(static_class_name(), func, "current node is nil"); + tree_command_unit temp; + size_accumulator += current_node->packed_size() + temp.packed_size(); +LOG(a_sprintf("len A %d", size_accumulator)); +} + +void packable_tree::packit(byte_array &packed_form, const packable_tree *current_node) +{ +LOG(a_sprintf("packing node %x", current_node)); +LOG(a_sprintf("size A %d", packed_form.length())); + FUNCDEF("packit"); + if (!current_node) throw_error(static_class_name(), func, "current node is nil"); + + byte_array temp_store; + +int guess = current_node->packed_size(); + + current_node->pack(temp_store); + +if (temp_store.length() != guess) +throw_error(current_node->class_name(), func, "failure calculating size"); + + tree_command_unit command; + command.size = temp_store.length(); +//hmmm: do we still need a packed size? + if (current_node->branches() == 0) { + command.command = BRANCHES_FOLLOW; + // the branches following are always just one branch. + command.number = 1; + } else { + command.command = ATTACH_BRANCHES; + command.number = current_node->branches(); + } + // stuff the command unit. + command.pack(packed_form); +LOG(a_sprintf("size B %d", packed_form.length())); + packed_form += temp_store; // main chunk is not packed, just added. +LOG(a_sprintf("size C %d", packed_form.length())); +} + +int packable_tree::recursive_packed_size() const +{ + packable_tree *curr = NIL; + int accum = 0; // where we accumulate the length of the packed form. + for (iterator zip2 = start(postfix); (curr = (packable_tree *)zip2.next()); ) + calcit(accum, curr); + tree_command_unit end_command; + accum += end_command.packed_size(); + return accum; +} + +void packable_tree::recursive_pack(byte_array &packed_form) const +{ + packable_tree *curr = NIL; + for (iterator zip2 = start(postfix); (curr = (packable_tree *)zip2.next()); ) + packit(packed_form, curr); + + tree_command_unit end_command; + end_command.number = 1; + end_command.command = FINISH; + end_command.size = 0; + // end command is stored at end. + end_command.pack(packed_form); +} + +packable_tree *packable_tree::recursive_unpack(byte_array &packed_form, + packable_tree_factory &creator) +{ + stack accumulated_trees(0); // unbounded. + tree_command_unit cmd; + // get the first command out of the package. + if (!cmd.unpack(packed_form)) { +//complain. + return false; + } + + packable_tree *new_branch = NIL; + bool failure = false; // set to true if errors occurred. + + // the packed tree is traversed by grabbing a command and then doing what + // it says as far as pulling in children or adding a new branch. + while (cmd.command != FINISH) { + new_branch = creator.create(); + + new_branch->unpack(packed_form); + + if (cmd.command == ATTACH_BRANCHES) { + if (cmd.number > accumulated_trees.size()) { +//log instead: "badly formed packed tree" + failure = true; + break; + } + for (int i = cmd.number; i > 0; i--) { + packable_tree *to_add = (packable_tree *)accumulated_trees + [accumulated_trees.size()-i]; + new_branch->attach(to_add); + } + packable_tree *junk; + for (int j = 0; j < cmd.number; j++) + accumulated_trees.acquire_pop(junk); + accumulated_trees.push(new_branch); + } else if (cmd.command == BRANCHES_FOLLOW) { + accumulated_trees.push(new_branch); + } else { +//log instead: "invalid command in packed tree" + failure = true; + break; + } + if (!cmd.unpack(packed_form)) { +//complain. + failure = true; + break; + } + } + + if (accumulated_trees.size() != 1) { +//log instead: "not all branches were claimed" + failure = true; + } else if (!failure) { + packable_tree *junk; + accumulated_trees.acquire_pop(junk); + } + + // clean up the allocated objects if we saw a failure. + if (failure) { + while (true) { + packable_tree *to_whack; + outcome ret = accumulated_trees.acquire_pop(to_whack); + if (ret == common::IS_EMPTY) break; + if (to_whack != new_branch) + WHACK(to_whack); + } + WHACK(new_branch); + } + + return new_branch; +} + +} // namespace. + diff --git a/core/library/nodes/packable_tree.h b/core/library/nodes/packable_tree.h new file mode 100644 index 00000000..a003db04 --- /dev/null +++ b/core/library/nodes/packable_tree.h @@ -0,0 +1,74 @@ +#ifndef PACKABLE_TREE_CLASS +#define PACKABLE_TREE_CLASS + +/*****************************************************************************\ +* * +* Name : packable_tree * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "tree.h" + +#include + +namespace nodes { + +// forward. +class packable_tree_factory; + +//! A tree object that can be packed into an array of bytes and unpacked again. + +class packable_tree : public tree, public virtual basis::packable +{ +public: + packable_tree(); + //!< constructs a new tree with a root and zero branches. + + // the recursive packing methods here will operate on all nodes starting at this one + // and moving downwards to all branches. + + int recursive_packed_size() const; + //!< spiders the tree starting at this node to calculate the packed size. + + void recursive_pack(basis::byte_array &packed_form) const; + //!< packs the whole tree starting at this node into the packed form. + + static packable_tree *recursive_unpack(basis::byte_array &packed_form, + packable_tree_factory &creator); + //!< unpacks a tree stored in "packed_form" and returns it. + /*!< if NIL is returned, then the unpack failed. the "creator" is needed + for making new derived packable_tree objects of the type stored. */ + + // standard pack, unpack and packed_size methods must be implemented by the derived tree. + +private: + static void packit(basis::byte_array &packed_form, const packable_tree *current_node); + //!< used by our recursive packing methods. + static void calcit(int &size_accumulator, const packable_tree *current_node); + //!< used by recursive packed size calculator. +}; + +////////////// + +class packable_tree_factory +{ +public: + virtual ~packable_tree_factory(); + virtual packable_tree *create() = 0; + //!< a tree factory is needed when we are recreating the packed tree. + /*!< this is because the real type held is always a derived object. + this method should just create a blank object of the appropriate type. */ +}; + +} // namespace. + +#endif + diff --git a/core/library/nodes/path.cpp b/core/library/nodes/path.cpp new file mode 100644 index 00000000..d84325a7 --- /dev/null +++ b/core/library/nodes/path.cpp @@ -0,0 +1,97 @@ +/*****************************************************************************\ +* * +* Name : path * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "node.h" +#include "path.h" + +#include +#include + +#include + +using namespace basis; +using namespace structures; + +#undef LOG +#define LOG(to_print) printf("%s::%s: %s\n", static_class_name(), func, astring(to_print).s()) + +namespace nodes { + +class path_node_stack : public stack +{ +public: + path_node_stack() : stack(0) {} +}; + +////////////// + +path::path(const node *start) +: _stack(new path_node_stack) +{ _stack->push(const_cast(start)); } + +path::path(const path &to_copy) +: _stack(new path_node_stack(*to_copy._stack)) +{} + +path::~path() +{ + while (_stack->elements()) _stack->pop(); + WHACK(_stack); +} + +node *path::operator [] (int index) const { return (*_stack)[index]; } + +int path::size() const { return _stack->size(); } + +node *path::root() const { return (*_stack)[0]; } + +node *path::current() const { return _stack->top(); } + +node *path::follow() const { return _stack->top(); } + +path &path::operator = (const path &to_copy) +{ + if (this == &to_copy) return *this; + *_stack = *to_copy._stack; + return *this; +} + +node *path::pop() +{ + node *to_return; + if (_stack->acquire_pop(to_return) != common::OKAY) + return NIL; + return to_return; +} + +outcome path::push(node *to_add) +{ return _stack->push(to_add); } + +outcome path::push(int index) +{ + if (!_stack->top()->get_link(index)) return common::NOT_FOUND; + return _stack->push(_stack->top()->get_link(index)); +} + +bool path::generate_path(node *to_locate, path &to_follow) const +{ + FUNCDEF("generate_path") + +if (to_locate || to_follow.current()) {} +LOG("hmmm: path::generate_path is not implemented."); +return false; +} + +} // namespace. + diff --git a/core/library/nodes/path.h b/core/library/nodes/path.h new file mode 100644 index 00000000..af6261f4 --- /dev/null +++ b/core/library/nodes/path.h @@ -0,0 +1,93 @@ +#ifndef PATH_CLASS +#define PATH_CLASS + +/*****************************************************************************\ +* * +* Name : path * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace nodes { + +// forward: +class node; +class path_node_stack; + +//! A method for tracing a route from a tree's root to a particular node. +/*! + A path has a starting point at a particular node and a list of links to + take from that node to another node. For the path to remain valid, none + of the links contained within it may be destroyed. +*/ + +class path : public virtual basis::nameable +{ +public: + path(const node *root); + //!< the path is relative to the "root" node. + /*!< this will be the first object pushed onto the stack that we use + to model the path. */ + + path(const path &to_copy); + + ~path(); + + DEFINE_CLASS_NAME("path"); + + path &operator =(const path &to_copy); + + int size() const; + //!< returns the number of items in the path. + + node *root() const; + //!< returns the relative root node for this path. + + node *current() const; + node *follow() const; + //!< Returns the node specified by this path. + /*!< if the path is not valid, NIL is returned. */ + + node *pop(); + //!< returns the top node on the path stack. + /*!< this returns the node at the farthest distance from the relative + root node and removes it from this path. */ + + basis::outcome push(node *to_add); + //!< puts the node "to_add" on the top of the stack. + /*!< adds a node to the path as long as "to_add" is one of the current + node's descendants. */ + + basis::outcome push(int index); + //!< indexed push uses the current top node's index'th link as new top. + /*!< adds the node at "index" of the current top node to the path, + providing that such a link number exists on the current node. */ + + bool generate_path(node *to_locate, path &to_follow) const; + //!< finds the way to get from the root to the "to_locate" node. + /*!< returns true if there is a path between the relative root of + this path and the node "to_locate". if there is such a path, + "to_follow" is set to one of the possible paths. */ + + node *operator [] (int index) const; + //!< returns the node stored at "index", or NIL if "index" is invalid. + +private: + path_node_stack *_stack; //!< implementation of our pathway. +}; + +} // namespace. + +#endif + diff --git a/core/library/nodes/symbol_tree.cpp b/core/library/nodes/symbol_tree.cpp new file mode 100644 index 00000000..a4e455a5 --- /dev/null +++ b/core/library/nodes/symbol_tree.cpp @@ -0,0 +1,227 @@ +/*****************************************************************************\ +* * +* Name : symbol_tree * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "symbol_tree.h" + +#include +#include +#include +#include + +//#define DEBUG_SYMBOL_TREE + // uncomment for totally noisy version. + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +using namespace basis; +using namespace structures; +using namespace textual; + +namespace nodes { + +class symbol_tree_associations : public symbol_table +{ +public: + symbol_tree_associations(int estimated_elements) + : symbol_table(estimated_elements) {} +}; + +////////////// + +symbol_tree::symbol_tree(const astring &node_name, int estimated_elements) +: tree(), + _associations(new symbol_tree_associations(estimated_elements)), + _name(new astring(node_name)) +{ +} + +symbol_tree::~symbol_tree() +{ + WHACK(_name); + WHACK(_associations); +} + +int symbol_tree::children() const { return _associations->symbols(); } + +const astring &symbol_tree::name() const { return *_name; } + +int symbol_tree::estimated_elements() const { return _associations->estimated_elements(); } + +void symbol_tree::rehash(int estimated_elements) { _associations->rehash(estimated_elements); } + +void symbol_tree::hash_appropriately(int estimated_elements) +{ _associations->hash_appropriately(estimated_elements); } + +bool symbol_tree::add(symbol_tree *to_add) +{ +#ifdef DEBUG_SYMBOL_TREE + FUNCDEF("add"); + LOG(astring("adding node for ") + to_add->name()); +#endif + attach(to_add); // add to tree. + _associations->add(to_add->name(), to_add); // add to associations. + return true; +} + +outcome symbol_tree::prune(tree *to_zap_in) +{ +#ifdef DEBUG_SYMBOL_TREE + FUNCDEF("prune"); +#endif + symbol_tree *to_zap = dynamic_cast(to_zap_in); + if (!to_zap) + throw("error: symbol_tree::prune: wrong type of node in prune"); +#ifdef DEBUG_SYMBOL_TREE + LOG(astring("zapping node for ") + to_zap->name()); +#endif + int found = which(to_zap); // find the node in the tree. + if (negative(found)) return common::NOT_FOUND; // not found. +#ifdef DEBUG_SYMBOL_TREE + int kids = _associations->symbols(); +#endif + _associations->whack(to_zap->name()); // remove from associations. +#ifdef DEBUG_SYMBOL_TREE + if (kids - 1 != _associations->symbols()) + throw("error: symbol_tree::prune: failed to crop kid in symtab"); +#endif + tree::prune(to_zap); // remove from tree. + return common::OKAY; +} + +symbol_tree *symbol_tree::branch(int index) const +{ return dynamic_cast(tree::branch(index)); } + +// implementation snagged from basis/shell_sort. +void symbol_tree::sort() +{ + int n = branches(); + symbol_tree *temp; + int gap, i, j; + for (gap = n / 2; gap > 0; gap /= 2) { + for (i = gap; i < n; i++) { + for (j = i - gap; j >= 0 && branch(j)->name() > branch(j + gap)->name(); + j = j - gap) { + // swap the elements that are disordered. + temp = branch(j + gap); + prune_index(j + gap); + insert(j, temp); + temp = branch(j + 1); + prune_index(j + 1); + insert(j + gap, temp); + } + } + } +} + +symbol_tree *symbol_tree::find(const astring &to_find, find_methods how, + string_comparator_function *comp) +{ +#ifdef DEBUG_SYMBOL_TREE + FUNCDEF("find"); +#endif + if (comp == NIL) comp = astring_comparator; +#ifdef DEBUG_SYMBOL_TREE + LOG(astring("finding node called ") + to_find); +#endif + // ensure that we compare the current node. + if (comp(name(), to_find)) return this; + + // perform the upward recursion first, since it's pretty simple. + if (how == recurse_upward) { + symbol_tree *our_parent = dynamic_cast(parent()); + if (!our_parent) return NIL; // done recursing. + return our_parent->find(to_find, how, comp); + } + + // see if our branches match the search term. + symbol_tree **found = _associations->find(to_find, comp); +#ifdef DEBUG_SYMBOL_TREE + if (!found) LOG(to_find + " was not found.") + else LOG(to_find + " was found successfully."); +#endif + if (!found) { + if (how == recurse_downward) { + // see if we can't find that name in a sub-node. + symbol_tree *answer = NIL; + for (int i = 0; i < branches(); i++) { + // we will try each branch in turn and see if it has a child named + // appropriately. + symbol_tree *curr = dynamic_cast(branch(i)); +#ifdef DEBUG_SYMBOL_TREE + LOG(astring("recursing to ") + curr->name()); +#endif + if (curr) + answer = curr->find(to_find, how, comp); + if (answer) + return answer; + } + } + return NIL; + } + return *found; +} + +//hmmm: is this useful elsewhere? +astring hier_prefix(int depth, int kids) +{ + astring indent = string_manipulation::indentation( (depth - 1) * 2); + if (!depth) return ""; + else if (!kids) return indent + "|--"; + else return indent + "+--"; +} + +astring symbol_tree::text_form() const +{ +#ifdef DEBUG_SYMBOL_TREE + FUNCDEF("text_form"); +#endif + astring to_return; + + tree::iterator ted = start(prefix); + // create our iterator to do a prefix traversal. + + tree *curr = (tree *)ted.next(); + +//hmmm: this cast assumes that the tree only contains trees. for more +// safety, we might want a dynamic cast here also. + while (curr) { + // we have a good directory to show. + symbol_tree *curr_cast = dynamic_cast(curr); + if (!curr_cast) { + // something very bad with that... +#ifdef DEBUG_SYMBOL_TREE + LOG("logic error: unknown type in symbol tree."); +#endif + ted.next(); + continue; + } + astring name_to_log = curr_cast->name(); + if (!ted.size()) name_to_log = ""; +#ifdef DEBUG_SYMBOL_TREE + LOG(a_sprintf("depth %d kids %d name %s", ted.size(), curr_cast->branches(), + name_to_log.s())); +#endif + to_return += hier_prefix(curr->depth(), curr_cast->branches()); + to_return += name_to_log; + to_return += parser_bits::platform_eol_to_chars(); + + curr = (tree *)ted.next(); + } + + return to_return; +} + +} // namespace. + diff --git a/core/library/nodes/symbol_tree.h b/core/library/nodes/symbol_tree.h new file mode 100644 index 00000000..c8d4f6dc --- /dev/null +++ b/core/library/nodes/symbol_tree.h @@ -0,0 +1,109 @@ +#ifndef SYMBOL_TREE_CLASS +#define SYMBOL_TREE_CLASS + +/*****************************************************************************\ +* * +* Name : symbol_tree * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "tree.h" + +#include +#include + +namespace nodes { + +// forward. +class symbol_tree_associations; + +//! A symbol table that supports scope nesting and/or trees of symbol tables. +/*! + Note: although the symbol_tree is a tree, proper functioning is only + guaranteed if you stick to its own add / find methods rather than calling + on the base class's methods... but the tree's iterator support should be + used for traversing the symbol_tree and prune should work as expected. +*/ + +class symbol_tree : public tree +{ +public: + symbol_tree(const basis::astring &node_name, int estimated_elements = 100); + //!< creates a symbol_tree node with the "node_name". + /*!< presumably this could be a child of another symbol tree also. the "estimated_elements" + is used to choose a size for the table holding the names. */ + + virtual ~symbol_tree(); + //!< all child nodes will be deleted too. + /*!< if the automatic child node deletion is not good for your purposes, + be sure to unhook the children before deletion of the tree and manage them + separately. */ + + DEFINE_CLASS_NAME("symbol_tree"); + + int children() const; //!< returns the number of children of this node. + + const basis::astring &name() const; //!< returns the name of this node. + + int estimated_elements() const; //!< returns the number of bits in this node's table. + + symbol_tree *branch(int index) const; //!< returns the "index"th branch. + + void rehash(int estimated_elements); + //!< resizes the underlying symbol_table for this node. + + void hash_appropriately(int estimated_elements); + //!< resizes the hashing parameter to limit bucket sizes. + /*!< rehashes the name table so that there will be no more (on average) + than "max_per_bucket" items per hashing bucket. this is the max that will + need to be crossed to find an item, so reducing the number per bucket + speeds access but also requires more memory. */ + + bool add(symbol_tree *to_add); + //!< adds a child to this symbol_tree. + + virtual basis::outcome prune(tree *to_zap); + //!< removes a sub-tree "to_zap". + /*!< the "to_zap" tree had better be a symbol_tree; we are just matching + the lower-level virtual function prototype. note that the tree node + "to_zap" is not destroyed; it is just plucked from the tree. */ + + enum find_methods { just_branches, recurse_downward, recurse_upward }; + + symbol_tree *find(const basis::astring &to_find, + find_methods how, +///= just_branches, + basis::string_comparator_function *comp = NIL); + //!< returns the node specified by "to_find" or NIL. + /*!< this should be fairly quick due to the symbol table's hashing. + the "how" method specifies the type of recursion to be used in searching + if any. if "how" is passed as "just_branches", then only the branches are + checked and no recursion upwards or downwards is performed. if "how" is + "recurse_downward", then all sub-trees under the branches are checked + also. if "how" is given as "recurse_upward", then "this" node and parent + nodes are checked. the "comp" parameter will be used for comparing the + strings if it's passed as non-NIL. */ + + void sort(); + //!< sorts the sub-nodes of this symbol_tree. + + basis::astring text_form() const; + //!< traverses the tree to build a textual list of the nodes. + +private: + symbol_tree_associations *_associations; //!< the link from names to nodes. + basis::astring *_name; //!< the name of this symbol tree node. +}; + +} // namespace. + +#endif + diff --git a/core/library/nodes/tree.cpp b/core/library/nodes/tree.cpp new file mode 100644 index 00000000..a087b0b7 --- /dev/null +++ b/core/library/nodes/tree.cpp @@ -0,0 +1,391 @@ +/*****************************************************************************\ +* * +* Name : tree * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "tree.h" + +#include +#include +#include + +//#define DEBUG_TREE + // uncomment if you want lots of debugging info. + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +using namespace basis; + +namespace nodes { + +const int BACKWARDS_BRANCH = 0; + // BACKWARDS_BRANCH is the branch from this tree to its parent. this is + // steeped in the perspective that the root is the backwards direction (where + // we came from, in a sense) and that the children of this node are the + // forwards direction. + +////////////// + +// iterator methods: + +tree::iterator::iterator(const tree *initial, traversal_directions direction) +: path(initial), _order(direction), _aim(AWAY_FROM_ROOT) +{ +} + +tree::iterator::~iterator() { while (size()) pop(); } + +bool tree::iterator::next_node(tree *&to_return) +{ +#ifdef DEBUG_TREE + FUNCDEF("next_node"); +#endif + to_return = NIL; +#ifdef DEBUG_TREE + if ( (_order != to_branches) + && (_order != reverse_branches) ) { + if (_aim == AWAY_FROM_ROOT) LOG("going down") + else LOG("going up"); + } +#endif + switch (_order) { + case prefix: { + if (_aim == AWAY_FROM_ROOT) { + // going down means this is the first time we have seen the current top + // node on the stack. + to_return = (tree *)(*this)[size() - 1]; +#ifdef DEBUG_TREE +// LOG(a_sprintf("[%s] ", to_return->get_contents()->held().s()); + if (to_return->branches()) LOG("pushing 0") + else LOG("switching direction"); +#endif + if (to_return->branches()) + push(to_return->branch(0)); + else + _aim = TOWARD_ROOT; + } else { + // going up means that we need to get rid of some things before we + // start seeing new nodes again. + if (size() == 1) return false; + // the last node has been seen.... + tree *last = (tree *)pop(); + tree *current_tree = (tree *)current(); + int lastnum = current_tree->which(last); +#ifdef DEBUG_TREE + if (lastnum < current_tree->branches() - 1) + LOG(a_sprintf("going down %d", lastnum+1)) + else LOG("still going up"); +#endif + if (lastnum < current_tree->branches() - 1) { + _aim = AWAY_FROM_ROOT; + push(current_tree->branch(lastnum + 1)); + } // else still going up. + } + break; + } + case infix: { + if (_aim == AWAY_FROM_ROOT) { + // going down means starting on the left branch. + tree *temp = (tree *)current(); +#ifdef DEBUG_TREE + if (temp->branches()) LOG("pushing 0") + else LOG("switching direction"); +#endif + if (temp->branches()) push(temp->branch(0)); + else { + _aim = TOWARD_ROOT; + to_return = (tree *)current(); +#ifdef DEBUG_TREE +// LOG(a_sprintf("[%s] ", to_return->get_contents()->held().s())); +#endif + } + } else { + // going up means that the left branch is done and we need to either + // keep going up or go down the right branch. + if (size() == 1) return false; + // the last node has been seen.... + tree *last = (tree *)pop(); + tree *current_tree = (tree *)current(); + int lastnum = current_tree->which(last); +#ifdef DEBUG_TREE + if (lastnum < 1) LOG(a_sprintf("going down %d", lastnum+1)) + else LOG("still going up"); +#endif + if (lastnum < 1) { + _aim = AWAY_FROM_ROOT; + to_return = (tree *)current(); +#ifdef DEBUG_TREE +/// LOG(a_sprintf("[%s] ", to_return->get_contents()->held().s())); +#endif + push(current_tree->branch(lastnum + 1)); + } // else still going up. + } + break; + } + case to_branches: { + if (_aim == TOWARD_ROOT) return false; + else { + if (size() == 1) { + tree *temp = (tree *)current(); + if (!temp->branches()) + _aim = TOWARD_ROOT; + else + push(temp->branch(0)); + } else { + tree *last = (tree *)pop(); + tree *curr = (tree *)current(); + int lastnum = curr->which(last); + if (lastnum < curr->branches() - 1) + push(curr->branch(lastnum + 1)); + else _aim = TOWARD_ROOT; + to_return = last; + } + } + break; + } + case reverse_branches: { + if (_aim == TOWARD_ROOT) return false; + else { + if (size() == 1) { + tree *temp = (tree *)current(); + if (!temp->branches()) _aim = TOWARD_ROOT; + else push(temp->branch(temp->branches() - 1)); + } else { + tree *last = (tree *)pop(); + tree *curr = (tree *)current(); + int lastnum = curr->which(last); + if (lastnum > 0) push(curr->branch(lastnum - 1)); + else _aim = TOWARD_ROOT; + to_return = last; + } + } + break; + } + default: // intentional fall-through to postfix. + case postfix: { + if (_aim == AWAY_FROM_ROOT) { + // going down means that the bottom is still being sought. + tree *temp = (tree *)current(); +#ifdef DEBUG_TREE + if (temp->branches()) LOG("pushing 0") + else LOG("switching direction"); +#endif + if (temp->branches()) push(temp->branch(0)); + else _aim = TOWARD_ROOT; + } else { + // going up means that all nodes below current have been hit. + if (!size()) return false; // the last node has been seen... + else if (size() == 1) { + to_return = (tree *)pop(); + // this is the last node. + return true; + } + tree *last = (tree *)pop(); + to_return = last; +#ifdef DEBUG_TREE +/// LOG(a_sprintf("[%s] ", to_return->get_contents()->held())); +#endif + tree *current_tree = (tree *)current(); + int lastnum = current_tree->which(last); +#ifdef DEBUG_TREE + if (lastnum < current_tree->branches() - 1) + LOG(a_sprintf("going down %d", lastnum+1)) + else LOG("still going up"); +#endif + if (lastnum < current_tree->branches() - 1) { + _aim = AWAY_FROM_ROOT; + push(current_tree->branch(lastnum + 1)); + } // else still going up. + } + break; + } + } + return true; + // it is assumed that termination conditions cause a return earlier on. +} + +void tree::iterator::whack(tree *to_whack) +{ +#ifdef DEBUG_TREE + FUNCDEF("whack"); +#endif + if (!to_whack) return; // that's a bad goof. + if (size()) { + if (to_whack == current()) { + // we found that this is the one at the top right now. + pop(); +#ifdef DEBUG_TREE + LOG("found node in current top; removing it there."); +#endif + } else if (to_whack->parent() == current()) { + // the parent is the current top. make sure we don't mess up traversal. +#ifdef DEBUG_TREE + LOG("found node's parent as current top; don't know what to do."); +#endif + } else { +#ifdef DEBUG_TREE + LOG("found no match for either node to remove or parent in path."); +#endif + } + } + tree *parent = to_whack->parent(); + if (!parent) { +#ifdef DEBUG_TREE + LOG("no parent node for the one to whack! would have whacked " + "root of tree!"); +#endif + } else { + parent->prune(to_whack); + WHACK(to_whack); + } +} + +tree *tree::iterator::next() +{ +#ifdef DEBUG_TREE + FUNCDEF("next"); +#endif + tree *to_return = NIL; + bool found_tree = false; + while (!found_tree) { + bool still_running = next_node(to_return); + if (to_return || !still_running) found_tree = true; + } + return to_return; +} + +////////////// + +// tree methods: + +tree::tree() +: node(1) +{ set_link(BACKWARDS_BRANCH, NIL); } + +tree::~tree() +{ + // must at least unhook ourselves from the parent so we don't become a lost + // cousin. + tree *my_parent = parent(); + if (my_parent) my_parent->prune(this); + + // iterate over the child nodes and whack each individually. + while (branches()) delete branch(0); + // this relies on the child getting out of our branch list. +} + +tree *tree::parent() const { return (tree *)get_link(BACKWARDS_BRANCH); } + +int tree::branches() const { return links() - 1; } + +tree *tree::branch(int branch_number) const +{ + branch_number++; + bounds_return(branch_number, 1, branches(), NIL); + return (tree *)get_link(branch_number); +} + +int tree::which(tree *branch_to_find) const +{ return node::which((node *)branch_to_find) - 1; } + +tree *tree::root() const +{ + const tree *traveler = this; + // keep looking at the backwards branch until it is a NIL. the tree with + // a NIL BACKWARDS_BRANCH is the root. return that tree. + while (traveler->get_link(BACKWARDS_BRANCH)) + traveler = (tree *)traveler->get_link(BACKWARDS_BRANCH); + return const_cast(traveler); +} + +void tree::attach(tree *new_branch) +{ + if (!new_branch) return; + insert_link(links(), new_branch); + new_branch->set_link(BACKWARDS_BRANCH, this); +} + +void tree::insert(int branch_place, tree *new_branch) +{ + branch_place++; + insert_link(links(), NIL); + if (branch_place >= links()) + branch_place = links() - 1; + for (int i = links() - 1; i > branch_place; i--) + set_link(i, get_link(i-1)); + set_link(branch_place, new_branch); + new_branch->set_link(BACKWARDS_BRANCH, this); +} + +outcome tree::prune(tree *branch_to_cut) +{ + int branch_number = which(branch_to_cut); + if (branch_number == basis::common::NOT_FOUND) return basis::common::NOT_FOUND; + return prune_index(branch_number); +} + +outcome tree::prune_index(int branch_to_cut) +{ + branch_to_cut++; + bounds_return(branch_to_cut, 1, branches(), basis::common::NOT_FOUND); + tree *that_branch = (tree *)get_link(branch_to_cut); + that_branch->set_link(BACKWARDS_BRANCH, NIL); + zap_link(branch_to_cut); + return basis::common::OKAY; +} + +int tree::depth() const +{ + tree *my_root = root(); + const tree *current_branch = this; + int deep = 0; + while (current_branch != my_root) { + current_branch = current_branch->parent(); + deep++; + } + return deep; +} + +//probably okay; we want to use this function rather than non-existent +//node base function which isn't implemented yet. +bool tree::generate_path(path &to_follow) const +{ +if (to_follow.size()) {} +/* + tree *traveller = this; + path to_accumulate(root()); + while (traveller->parent() != NIL) { +// int branch_number = traveller->parent()->which(traveller); +// if (branch_number == BRANCH_NOT_FOUND) non_continuable_error +// (class_name(), "generate_path", "branch not found during path construction"); +// chunk *to_stuff = new chunk +// (SELF_OWNED, (byte *)&branch_number, sizeof(int)); + to_accumulate.push(traveller); + traveller = traveller->parent(); + } + // the order of things on the stack needs to be reversed now. +// path to_return = new stack(*to_accumulate.invert()); +// return to_return; + to_accumulate.invert(); + return to_accumulate; +*/ +return false;//temp. +} + +//hmmm: need text form! + +tree::iterator tree::start(traversal_directions direction) const +{ return iterator(this, direction); } + +} // namespace. + diff --git a/core/library/nodes/tree.h b/core/library/nodes/tree.h new file mode 100644 index 00000000..31d0a99f --- /dev/null +++ b/core/library/nodes/tree.h @@ -0,0 +1,164 @@ +#ifndef TREE_CLASS +#define TREE_CLASS + +/*****************************************************************************\ +* * +* Name : tree * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "node.h" +#include "path.h" + +#include + +namespace nodes { + +//! A dynamically linked tree with an arbitrary number of branches. +/*! + A tree is defined as a node with n branch nodes, where n is dynamic. + Each branch is also a tree. Branch numbers range from 0 through n-1 in + the methods below. Trees can be self-cleaning, meaning that the tree + will destroy all of its children when it is destroyed. + + NOTE: the node indices are not numbered completely obviously; it is better + to use the tree functions for manipulating the node rather than + muddling with it directly. the branch to the tree node's parent is + stored as node zero, in actuality, rather than node zero being the + first branch. +*/ + +class tree : public node +{ +public: + tree(); + //!< constructs a new tree with a root and zero branches. + + virtual ~tree(); + //!< destroys the tree by recursively destroying all child tree nodes. + + DEFINE_CLASS_NAME("tree"); + + virtual tree *branch(int branch_number) const; + //!< Returns the specified branch of this tree. + /*!< NIL is returned if the "branch_number" refers to a branch that + does not exist. */ + + virtual int which(tree *branch_to_find) const; + //!< Returns the branch number for a particular branch in this tree. + /*!< common::NOT_FOUND if the branch is not one of the child nodes. */ + + virtual int branches() const; + //!< Returns the number of branches currently connected to this tree. + + virtual tree *parent() const; + //!< Returns the tree node that is the immediate ancestor of this one. + /*!< if this is the root node, then NIL is returned. */ + + virtual tree *root() const; + //!< Locates and returns the absolute root of the tree containing this tree. + /*!< If this tree IS the absolute root, then "this" is returned. */ + + virtual int depth() const; + //!< Returns the distance of "this" from the root. The root's depth is 0. + + virtual void attach(tree *new_branch); + //!< Attaches the specified branch to the current tree. + + virtual void insert(int branch_place, tree *new_branch); + //!< inserts "new_branch" before the branches starting at "branch_place". + /*!< Places a branch at a particular index and pushes the branches at that + index (and after it) over by one branch. */ + + virtual basis::outcome prune(tree *branch_to_cut); + //!< Removes the specified branch from this tree. + /*!< note that the pruning does not affect the branch being removed; it + just detaches that branch from the tree. if one wants to get rid of the + branch, it should be deleted. if this cannot find the tree specified in + the available branches then the branches of this tree are not touched and + common::NOT_FOUND is returned. */ + virtual basis::outcome prune_index(int branch_to_cut); + //!< Removes the branch at the specified index from this tree. + /*!< if this is given an invalid branch number, then the available + branches then the branches of this tree are not touched and + common::NOT_FOUND is returned. */ + + enum traversal_directions { prefix, infix, postfix, to_branches, + reverse_branches }; + //!< these are the different kinds of tree traversal that are supported. + /*!< "prefix" means that tree nodes will be visited as soon as they are + seen; the deepest nodes have to wait the longest to be seen by the + traversal. "postfix" means that tree nodes are not visited until all of + their ancestors have been visited; the nodes nearer the start of traversal + will wait the longest to be visited. the "infix" direction visits + the left branch, then the starting node, then the right branch. + "infix" is only valid for binary or unary trees; it is an error to + apply "infix" to a tree containing a node that has more than 2 branches. + the direction "to_branches" visits each of the branches in the order + defined by the branch() method. "to_branches" does not visit this + tree's node. "reverse_branches" operates in the opposite direction + of traversal from "to_branches". "reverse_branches" also does not + visit this node. "reverse_branches" can be used to prune off subtrees + during iteration without changing the ordering of the branches; this + is valuable because a pruning operation applied in "to_branches" order + would keep reducing the index of the branches. */ + + enum elevator_directions { TOWARD_ROOT, AWAY_FROM_ROOT }; + //!< movement in the tree is either towards or away from the root. + /*!< distinguishes between motion towards the root node of the tree and + motion away from the root (towards one's children). */ + + class iterator : public path + { + public: + iterator(const tree *initial, traversal_directions direction); + ~iterator(); + + tree *next(); + //!< Returns a pointer to the next tree in the direction of traversal. + /*!< If the traversal is finished, NIL is returned. */ + + void whack(tree *to_whack); + //!< destroys the tree "to_whack". + /*!< whacks the node "to_whack" by patching this iterator so that future + iterations will be correct. it is required that the "to_whack" node + was just returned from a call to next(). + NOTE: this has only been tested with postfix so far. */ + + traversal_directions _order; + elevator_directions _aim; + + private: + bool next_node(tree *&to_return); + //!< sets "to_return" to the next tree in the direction of tree traversal. + /*!< if the next node could not be found in one invocation of next_node, + then "to_return" is set to NIL. the function returns a boolean which + is true only if the iteration process can be continued by another call + to next_node. if the function returns false, the iteration is + complete and "to_return" will always be NIL. */ + }; + + iterator start(traversal_directions direction) const; + //!< Returns a fresh iterator positioned at this tree node. + + virtual bool generate_path(path &to_follow) const; + //!< Returns the path to "this" path_tree from its root. + +private: + // unavailable. + tree(const tree &); + tree &operator =(const tree &); +}; + +} // namespace. + +#endif + diff --git a/core/library/processes/configured_applications.cpp b/core/library/processes/configured_applications.cpp new file mode 100644 index 00000000..1c8e4617 --- /dev/null +++ b/core/library/processes/configured_applications.cpp @@ -0,0 +1,326 @@ +/*****************************************************************************\ +* * +* Name : configured_applications +* Author : Chris Koeritz +* * +******************************************************************************* +* Copyright (c) 2000 By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "configured_applications.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace configuration; +using namespace loggers; +using namespace structures; +using namespace textual; + +namespace processes { + +//#define DEBUG_APP_CONFIG + // uncomment for noisier debugging version. + +#undef LOG +#define LOG(to_print) program_wide_logger::get().log(to_print, ALWAYS_PRINT) + +////////////// + +const char *PRODUCT_HEADING() { return "product"; } + // the string used for our startup entries as a prefix to the product. + +const char *ASSIGN_TOKEN() { return "="; } + // how we distinguish the key from the value for startup entries. + +const char *SEPARATOR_TOKEN() { return ","; } + // the character between separate key/value pairs in the startup string. + +const char *SEPARATOR_TEXT() { return ", "; } + // the string we use for the separator when printing it. + +const char *PARMS_HEADING() { return "parms"; } + // the tag for parameters in the startup entry. + +const char *ONESHOT_HEADING() { return "oneshot"; } + // the key name for startup entries' flag for once only execution. + +////////////// + +configured_applications::configured_applications(const astring &config_file, + const astring &basename) +: _lock(new mutex), + _config(new ini_configurator(config_file, ini_configurator::RETURN_ONLY, + ini_configurator::APPLICATION_DIRECTORY)), + _sector(new section_manager(*_config, astring(basename) + "_TOC", + astring(PRODUCT_HEADING()) + "_")) +{ +// FUNCDEF("constructor"); + string_table startup_info; + if (!find_section(STARTUP_SECTION(), startup_info)) { + // if there's no startup section, we do nothing right now. + LOG(astring("the startup section doesn't exist yet; adding it now.")); + astring entry = make_startup_entry(basename, "", false); + startup_info.add(STARTUP_APP_NAME(), entry); + add_section(STARTUP_SECTION(), startup_info); + } +} + +configured_applications::~configured_applications() +{ + WHACK(_sector); + WHACK(_config); + WHACK(_lock); +} + +const char *configured_applications::STARTUP_SECTION() +{ return "PRIVATE_STARTUP_LNCH1.0"; } + +const char *configured_applications::STARTUP_APP_NAME() +{ return "placeholder"; } + +bool configured_applications::product_exists(const astring &product) +{ + auto_synchronizer l(*_lock); + if (!_sector->section_exists(product)) return false; + return true; +} + +astring configured_applications::find_program(const astring &product, + const astring &app_name, int &level) +{ + auto_synchronizer l(*_lock); + astring heading = _sector->make_section_heading(product); + astring found = _sector->config().load(heading, app_name, ""); +////////////// +//overly specific bits here... +//hmmm: add this in as a specialization provided by real owner of class. + if (!found) { + // we didn't find the entry under the section we wanted to find it in. + // there are a couple cases where we can kludge this section to a + // different name, based on legacy requirements, and still find the + // right item possibly. + if (product.iequals("supervisor")) { + // for some older installs, they say "supervisor" but mean "core". + heading = _sector->make_section_heading("core"); + found = _sector->config().load(heading, app_name, ""); + } else if (product.iequals("lightlink")) { + heading = _sector->make_section_heading("core"); + found = _sector->config().load(heading, app_name, ""); + if (!found) { + // we can take one more remedial step for this phrase. + heading = _sector->make_section_heading("server"); + found = _sector->config().load(heading, app_name, ""); + } + } + } +//end of overly specific. +////////////// + found = parser_bits::substitute_env_vars(found); + + int comma_loc = found.find(","); + if (negative(comma_loc)) return ""; // couldn't find our priority. + level = found.convert(0); + found.zap(0, comma_loc); + + return found; +} + +bool configured_applications::add_program(const astring &product, + const astring &app_name, const astring &full_path, int level) +{ +#ifdef DEBUG_APP_CONFIG + FUNCDEF("add_program"); +#endif + auto_synchronizer l(*_lock); + bool existed = true; + // lookup the section, if it exists. + string_table info_table; + if (!_sector->section_exists(product)) { + existed = false; + } else + find_section(product, info_table); +#ifdef DEBUG_APP_CONFIG + if (existed) { + LOG(astring("section for ") + product + " found:"); + for (int i = 0; i < info_table.symbols(); i++) + LOG(astring("key=") + info_table.name(i) + " value=" + info_table[i]); + } else LOG(astring("section for ") + product + " not found."); +#endif + // remove any existing entry. + info_table.whack(app_name); + // plug in our new entry. + a_sprintf full_entry("%d,%s", level, full_path.s()); + info_table.add(app_name, full_entry); +#ifdef DEBUG_APP_CONFIG + LOG(astring("new section for ") + product + " has:"); + for (int i = 0; i < info_table.symbols(); i++) + LOG(astring("key=") + info_table.name(i) + " value=" + info_table[i]); +#endif + // now call the proper storage function based on whether the section + // existed before or not. + if (existed) return replace_section(product, info_table); + else return add_section(product, info_table); +} + +bool configured_applications::remove_program(const astring &product, + const astring &app_name) +{ +// FUNCDEF("remove_program"); + auto_synchronizer l(*_lock); + // if the section's missing, there's nothing to remove... + string_table info_table; + if (!find_section(product, info_table)) return true; + // the section did exist, so remove any existing entry. + info_table.whack(app_name); + // now call the proper storage function based on whether the section + // existed before or not. + return replace_section(product, info_table); +} + +bool configured_applications::find_section(const astring §ion_name, + string_table &info_found) +{ +// FUNCDEF("find_section"); + info_found.reset(); + auto_synchronizer l(*_lock); + if (!_sector->find_section(section_name, info_found)) { + LOG(section_name + " was not found in the configuration."); + return false; + } + return true; +} + +bool configured_applications::add_section(const astring §ion_name, + const string_table &info_found) +{ + auto_synchronizer l(*_lock); + return _sector->add_section(section_name, info_found); +} + +bool configured_applications::replace_section(const astring §ion_name, + const string_table &info_found) +{ + auto_synchronizer l(*_lock); + return _sector->replace_section(section_name, info_found); +} + +astring configured_applications::make_startup_entry(const astring &product, + const astring &parms, bool one_shot) +{ + return astring(PRODUCT_HEADING()) + ASSIGN_TOKEN() + product + + SEPARATOR_TEXT() + PARMS_HEADING() + ASSIGN_TOKEN() + + parms + SEPARATOR_TEXT() + ONESHOT_HEADING() + ASSIGN_TOKEN() + + astring(astring::SPRINTF, "%d", one_shot); +} + +bool configured_applications::parse_startup_entry(const astring &info, + astring &product, astring &parms, bool &one_shot) +{ +// FUNCDEF("parse_startup_section"); + // parse the items that are in the entry for this program. + variable_tokenizer entry_parser(SEPARATOR_TOKEN(), ASSIGN_TOKEN()); + entry_parser.parse(info); + // grab the pertinent bits for the program to be started. + product = entry_parser.find(PRODUCT_HEADING()); + parms = entry_parser.find(PARMS_HEADING()); +//LOG(astring("parms=") + parms); + astring once = entry_parser.find(ONESHOT_HEADING()); + one_shot = (bool)once.convert(0); + // we require the product part at least. + if (!product) return false; + return true; +} + +bool configured_applications::find_entry(const string_table &table, + const astring &name, astring &location) +{ + // seek the entry in the table specified. + astring *found = table.find(name); + if (!found) return false; + // found the entry using the name. + location = *found; + return true; +} + +bool configured_applications::add_startup_entry(const astring &product, + const astring &app_name, const astring ¶meters, int one_shot) +{ +// FUNCDEF("add_startup_entry"); + auto_synchronizer l(*_lock); + + LOG(astring("product \"") + product + "\", application \"" + app_name + + (one_shot? astring("\", OneShot") : astring("\", MultiUse"))); + + string_table startup_info; + if (!find_section(STARTUP_SECTION(), startup_info)) { + // if there's no startup section, we can't go on. that should have been + // created during startup of this program. + LOG(astring("internal startup section not found!")); + return false; + } + + astring new_entry = make_startup_entry(product, parameters, + one_shot); + startup_info.add(app_name, new_entry); + if (!replace_section(STARTUP_SECTION(), startup_info)) + return false; +//hmmm: that's a bogus error; this is really an internal fup error. + + return true; +} + +bool configured_applications::remove_startup_entry(const astring &product, + const astring &app_name) +{ +// FUNCDEF("remove_startup_entry"); + auto_synchronizer l(*_lock); + + LOG(astring("product \"") + product + "\", application \"" + app_name + "\""); + + string_table startup_info; + if (!find_section(STARTUP_SECTION(), startup_info)) { + // if there's no startup section, we try to add one. + add_section(STARTUP_SECTION(), startup_info); + // if it still doesn't exist afterwards, we're hosed. + if (!find_section(STARTUP_SECTION(), startup_info)) { +/// COMPLAIN_PRODUCT; +//massive fup of some unanticipated sort. +//complain. + return false; + } + } + + // check that the entry already exists for this program. + astring entry_found; + if (!find_entry(startup_info, app_name, entry_found)) { +// COMPLAIN_APPLICATION; + LOG(astring("no entry was found for ") + app_name); + return false; + } + + startup_info.whack(app_name); + if (!replace_section(STARTUP_SECTION(), startup_info)) { +//what happened with that? + return false; + } + + return true; +} + +} //namespace. + + diff --git a/core/library/processes/configured_applications.h b/core/library/processes/configured_applications.h new file mode 100644 index 00000000..d7c6f776 --- /dev/null +++ b/core/library/processes/configured_applications.h @@ -0,0 +1,123 @@ +#ifndef CONFIGURED_APPLICATIONS_CLASS +#define CONFIGURED_APPLICATIONS_CLASS + +/*****************************************************************************\ +* * +* Name : configured_applications +* Author : Chris Koeritz +* * +******************************************************************************* +* Copyright (c) 2000 By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +namespace processes { + +//! Manages the initialization file for a set of registered applications. +/*! + This records the list of programs that are allowed to be executed as well + as the list of applications launched at object startup time. +*/ + +class configured_applications +{ +public: + configured_applications(const basis::astring &config_file, const basis::astring &basename); + //!< manages application settings for in the "config_file". + /*!< the "basename" is used for the section name of a list of products + that can be managed by this class. each product has a set of applications + that are part of the product's full package. */ + + virtual ~configured_applications(); + + // this section has mainly informational functions. + + DEFINE_CLASS_NAME("configured_applications"); + + static const char *STARTUP_SECTION(); + //!< the section where startup info is stored. + + static const char *STARTUP_APP_NAME(); + //!< a special placeholder name that will appear in the startup list. + /*!< it is not to be executed like the other programs for startup. */ + + static bool find_entry(const structures::string_table &table, const basis::astring &name, + basis::astring &location); + //!< returns true if the key "name" for a program is found in the "table". + /*!< the "location" is set to the value found in the table if successful. + */ + + static basis::astring make_startup_entry(const basis::astring &product, + const basis::astring &parms, bool one_shot); + //!< returns the appropriate string for a startup record. + + static bool parse_startup_entry(const basis::astring &info, basis::astring &product, + basis::astring &parms, bool &one_shot); + //!< processes the items in "info" as an application startup list. + /*!< using a string "info" that was listed as the startup entry for an + application, the "product", "parms" and "one_shot" bits are parsed out + and returned. */ + + bool product_exists(const basis::astring &product); + //!< returns true if the section for "product" exists in the TOC. + + basis::astring find_program(const basis::astring &product, const basis::astring &app_name, + int &level); + //!< seeks out the entry for the "product" and "app_name" in our info. + /*!< the returned string will either be empty (on failure) or will contain + the full path to the application (on success). the "level" will specify + the ordering of shutdown, where larger levels are shut down first. */ + + // the following functions actually modify the configuration file. + + bool find_section(const basis::astring §ion_name, structures::string_table &info_found); + //!< locates the entries for "section_name" and stores them in "info_found". + + bool add_section(const basis::astring §ion_name, const structures::string_table &info); + //!< puts a chunk of "info" into the section for "section_name". + /*!< this fails if the section already exists. */ + + bool replace_section(const basis::astring §ion_name, const structures::string_table &info); + //!< replaces the section for "section_name" with "info". + /*!< this fails if the section does not already exist. */ + + bool add_program(const basis::astring &product, const basis::astring &app_name, + const basis::astring &full_path, int level); + //!< registers a program "app_name" into the "product" section. + /*!< the "full_path" specifies where to find the program and the "level" + gives the application an ordering for shutdown. higher levels are shut + down before lower ones. */ + + bool remove_program(const basis::astring &product, const basis::astring &app_name); + //!< takes a previously registered "app_name" out of the list for "product". + + bool add_startup_entry(const basis::astring &product, const basis::astring &app_name, + const basis::astring ¶meters, int one_shot); + //!< establishes the "app_name" as a program launched at object startup. + /*!< adds an entry to the startup section for a program that will be + launched when the application manager restarts. */ + + bool remove_startup_entry(const basis::astring &product, const basis::astring &app_name); + //!< takes an existing entry for the "app_name" out of the startup section. + +private: + basis::mutex *_lock; //!< synchronization protection for our objects. + configuration::ini_configurator *_config; //!< manages our configuration settings. + configuration::section_manager *_sector; //!< keeps track of our product sections. +}; + +} //namespace. + +#endif + diff --git a/core/library/processes/ethread.cpp b/core/library/processes/ethread.cpp new file mode 100644 index 00000000..30efcc88 --- /dev/null +++ b/core/library/processes/ethread.cpp @@ -0,0 +1,328 @@ +/*****************************************************************************\ +* * +* Name : ethread * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "ethread.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __WIN32__ + #include +#elif defined(__UNIX__) + #include +#else + #error unknown OS for thread support. +#endif + +using namespace basis; +using namespace loggers; +using namespace structures; +using namespace timely; + +//#define COUNT_THREADS + // if this is enabled, then threads will be counted when they are created + // or destroyed. + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +namespace processes { + +const int MAXIMUM_CREATE_ATTEMPTS = 20; + // the number of retries we allow to try creating a thread, if the first + // attempt fails. + +const int MINIMUM_SLEEP_PERIOD = 10; + // this is the smallest time we'll sleep for if we're slack. + +const int MAXIMUM_SLEEP_PERIOD = 200; + // the number of milliseconds we use for breaking up longer sleep periods. + +const int SNOOZE_FOR_RETRY = 100; + // how long to sleep when a thread creation fails. + +#ifdef COUNT_THREADS + // singleton thread counter code. + class thread_counter : public virtual root_object { + public: + thread_counter() : _count(0) {} + DEFINE_CLASS_NAME("thread_counter"); + void increment() { + auto_synchronizer l(_lock); + _count++; + } + void decrement() { + auto_synchronizer l(_lock); + _count--; + } + private: + int _count; + mutex _lock; + }; + + SAFE_STATIC(thread_counter, _current_threads, ) + +//hmmm: this seems to not be used anywhere yet. it needs to be accessible +// externally if it's going to serve any useful purpose. + +#endif + +ethread::ethread() +: _thread_ready(false), + _thread_active(false), + _stop_thread(false), + _data(NIL), +#ifdef __UNIX__ + _handle(new pthread_t), +#elif defined(__WIN32__) + _handle(0), +#endif + _sleep_time(0), + _periodic(false), + _next_activation(new time_stamp), + _how(TIGHT_INTERVAL) // unused. +{ +// FUNCDEF("constructor [one-shot]"); +} + +ethread::ethread(int sleep_timer, timed_thread_types how) +: _thread_ready(false), + _thread_active(false), + _stop_thread(false), + _data(NIL), +#ifdef __UNIX__ + _handle(new pthread_t), +#elif defined(__WIN32__) + _handle(0), +#endif + _sleep_time(sleep_timer), + _periodic(true), + _next_activation(new time_stamp), + _how(how) +{ +// FUNCDEF("constructor [periodic]"); + if (sleep_timer < MINIMUM_SLEEP_PERIOD) { + _sleep_time = MINIMUM_SLEEP_PERIOD; + } +} + +ethread::~ethread() +{ + stop(); + WHACK(_next_activation); +#ifdef __UNIX__ + WHACK(_handle); +#endif +} + +///void ethread::pre_thread() {} + +///void ethread::post_thread() {} + +// the reschedule operation assumes that assignment to a time stamp +// object (based on a real numbers) happens indivisibly. +void ethread::reschedule(int delay) +{ + *_next_activation = time_stamp(delay); // start after the delay. +} + +bool ethread::start(void *thread_data) +{ + FUNCDEF("start"); + if (!thread_finished()) return false; // already running. + _data = thread_data; // store the thread's data pointer. + _stop_thread = false; // don't stop now. + _thread_ready = true; // we're starting it now. + _next_activation->reset(); // make "now" the next time to activate. + bool success = false; + int error = 0; + int attempts = 0; + while (attempts++ < MAXIMUM_CREATE_ATTEMPTS) { +#ifdef __UNIX__ + pthread_attr_t attribs; // special flags for creation of thread. + int aret = pthread_attr_init(&attribs); + if (aret) LOG("failed to init attribs."); + aret = pthread_attr_setdetachstate(&attribs, PTHREAD_CREATE_DETACHED); + if (aret) LOG("failed to set detach state."); + int ret = -1; + if (_periodic) + ret = pthread_create(_handle, &attribs, periodic_thread_driver, + (void *)this); + else + ret = pthread_create(_handle, &attribs, one_shot_thread_driver, + (void *)this); + if (!ret) success = true; + else error = ret; +#elif defined(__WIN32__) + if (_periodic) + _handle = _beginthread(periodic_thread_driver, 0, (void *)this); + else + _handle = _beginthread(one_shot_thread_driver, 0, (void *)this); + if (_handle != -1) success = true; + else error = critical_events::system_error(); +#endif + if (success) break; // got it created. + LOG("failed to create thread; trying again..."); + time_control::sleep_ms(SNOOZE_FOR_RETRY); + } + if (!success) { + // couldn't start it, so reset our state. + LOG(astring("failed to create thread, error is ") + + critical_events::system_error_text(error)); + exempt_stop(); + return false; + } + return true; +} + +void ethread::stop() +{ + cancel(); // tell thread to leave. + if (!thread_started()) return; // not running. + while (!thread_finished()) { +#ifdef __WIN32__ + int result = 0; + if (!GetExitCodeThread((HANDLE)_handle, (LPDWORD)&result) + || (result != STILL_ACTIVE)) { + exempt_stop(); + break; + } +#endif + time_control::sleep_ms(10); // wait for thread to leave. + } +} + +void ethread::exempt_stop() +{ + _thread_active = false; + _thread_ready = false; +#ifdef __WIN32__ + _handle = 0; +#endif +} + +#ifdef __UNIX__ +void *ethread::one_shot_thread_driver(void *hidden_pointer) +#elif defined(__WIN32__) +void ethread::one_shot_thread_driver(void *hidden_pointer) +#else +#error unknown thread signature. +#endif +{ +// FUNCDEF("one_shot_thread_driver"); + ethread *manager = (ethread *)hidden_pointer; +#ifdef __UNIX__ + if (!manager) return NIL; +#else + if (!manager) return; +#endif +#ifdef COUNT_THREADS + _current_threads().increment(); +#endif +/// manager->pre_thread(); + manager->_thread_active = true; + manager->perform_activity(manager->_data); +/// manager->post_thread(); + manager->exempt_stop(); +#ifdef COUNT_THREADS + _current_threads().decrement(); +#endif +#ifdef __UNIX__ + pthread_exit(NIL); + return NIL; +#else + _endthread(); +#endif +} + +#ifdef __UNIX__ +void *ethread::periodic_thread_driver(void *hidden_pointer) +#elif defined(__WIN32__) +void ethread::periodic_thread_driver(void *hidden_pointer) +#else +#error unknown thread signature. +#endif +{ +// FUNCDEF("periodic_thread_driver"); + ethread *manager = (ethread *)hidden_pointer; +#ifdef __UNIX__ + if (!manager) return NIL; +#elif defined(__WIN32__) + if (!manager) return; +#endif +#ifdef COUNT_THREADS + _current_threads().increment(); +#endif +/// manager->pre_thread(); + + while (!manager->_stop_thread) { + // for TIGHT_INTERVAL, we reset the next active time here. this is safe + // relative to the reschedule() method, since we're about to do + // perform_activity() right now anyway. this brings about a pretty hard + // interval; if perform_activity() takes N milliseconds, then there will + // only be sleep_time - N (min zero) ms before the next invocation. + if (manager->_how == TIGHT_INTERVAL) + *manager->_next_activation = time_stamp(manager->_sleep_time); + + manager->_thread_active = true; + manager->perform_activity(manager->_data); + manager->_thread_active = false; + + // SLACK_INTERVAL means between activations. we reset the next activation + // here to ensure we wait the period specified for sleep time, including + // whatever time was taken for the activity itself. + if (manager->_how == SLACK_INTERVAL) + *manager->_next_activation = time_stamp(manager->_sleep_time); + + // we do the sleep timing in chunks so that there's not such a huge wait + // when the user stops the thread before the sleep interval elapses. + // snooze until time for the next activation. + while (!manager->_stop_thread) { + int time_diff = int(manager->_next_activation->value() + - time_stamp().value()); + if (time_diff < 0) time_diff = 0; // time keeps on slipping. + // make sure we take our time if we're slack intervalled. + if (manager->_how == SLACK_INTERVAL) { + if (time_diff < MINIMUM_SLEEP_PERIOD) + time_diff = MINIMUM_SLEEP_PERIOD; + } + if (time_diff > MAXIMUM_SLEEP_PERIOD) + time_diff = MAXIMUM_SLEEP_PERIOD; + if (!manager->_stop_thread) + time_control::sleep_ms(time_diff); + if (time_stamp() >= *manager->_next_activation) + break; + } + } +/// manager->post_thread(); + manager->exempt_stop(); +#ifdef COUNT_THREADS + _current_threads().decrement(); +#endif +#ifdef __UNIX__ + pthread_exit(NIL); + return NIL; +#elif defined(__WIN32__) + _endthread(); +#endif +} + +} //namespace. + diff --git a/core/library/processes/ethread.h b/core/library/processes/ethread.h new file mode 100644 index 00000000..9adab465 --- /dev/null +++ b/core/library/processes/ethread.h @@ -0,0 +1,184 @@ +#ifndef ETHREAD_CLASS +#define ETHREAD_CLASS + +/*****************************************************************************\ +* * +* Name : ethread (easy thread) * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +#include + +#ifndef __APPLE__ +#ifdef __UNIX__ +// typedef long unsigned int pthread_t; +#endif +#endif + +namespace processes { + +//! Provides a platform-independent object for adding threads to a program. +/*! + This greatly simplifies creating and managing threads by hiding all the + operating system details. The user just needs to override one virtual + function in their derived object to perform the main activity of their + thread. The thread can be a one time invocation or it can run periodically. + Control over the thread remains in the hands of the program that started + it. +*/ + +class ethread : public virtual basis::root_object +{ +public: + ethread(); + //!< creates a single-shot thread object. + /*!< the OS-level thread is not started until the start() method is + invoked. this constructor creates a thread that will only execute + once; when start() is called, the thread starts up and performs its + activity. it will then stop. to run it again, start() must be invoked + again. however, if the perform_activity() method just keeps running, + then the single-shot thread can live as long as needed. it is important + for such a thread to periodically check should_exit() to avoid having + the program hang-up when it's supposed to be shutting down. */ + + enum timed_thread_types { TIGHT_INTERVAL, SLACK_INTERVAL }; + + ethread(int sleep_timer, timed_thread_types how = SLACK_INTERVAL); + //!< creates a managed thread object that runs on a periodic interval. + /*!< the thread will activate every "sleep_timer" milliseconds. when + start() is invoked, the thread's action (via the perform_activity() + method) will be performed at regular intervals (using the specified value + for "sleep_timer"). the thread will continue activating until the stop() + method is called. a faster interval is used internally during sleep + periods such that calling stop() will not consume the whole "sleep_timer" + period. if the "how" is TIGHT_INTERVAL, then the thread will activate + every "sleep_timer" milliseconds, as accurately as possible. if the "how" + is SLACK_INTERVAL, then the thread will activate after a delay of + "sleep_timer" milliseconds from its last activation. the latter mode + allows the thread to consume its entire intended operation time knowing + that there will still be slack time between when it is active. the + former mode requires the thread to only run for some amount of time less + than its "sleep_timer"; otherwise it will hog a lot of the CPU. */ + + virtual ~ethread(); + + DEFINE_CLASS_NAME("ethread"); + + bool start(void *thread_data); + //!< causes the thread to start, if it has not already been started. + /*!< if the thread has terminated previously, then this will restart the + thread. true is returned if the thread could be started. false is + returned if the thread could not be started or if it is already running. */ + + void stop(); + //!< tells the thread to shutdown and waits for the shutdown to occur. + /*!< this will cause the OS thread to terminate once the current (if any) + perform_activity() invocation completes. the thread may be restarted + with start(). */ + + void cancel() { _stop_thread = true; } + //!< stops the thread but does not wait until it has terminated. + /*!< this is appropriate for use within the perform_activity() method. */ + +// virtual void pre_thread(); + //!< invoked just after after start(), when the OS thread is created. + /*!< the call comes in _from_ the thread itself, so the derived method + must be thread-safe. */ +// virtual void post_thread(); + //!< this is invoked just before the thread is to be terminated. + /*!< the call also comes in from the thread itself, so the implementation + must be thread-safe. */ + + virtual void perform_activity(void *thread_data) = 0; + //!< carries out the main activity of the thread. + /*!< this is called repeatedly by the main thread management function and + so should return as soon as possible. if it does not return fairly + regularly, then the thread shutdown process will not occur until the + function exits on its own. */ + + void exempt_stop(); + //!< this special form of stop() does not wait for the thread to exit. + /*!< it is required in certain weird OS situations where the thread does + not exit properly and stop() would cause an infinite wait. don't use it + unless you are SURE that this is the case. */ + + void reschedule(int delay = 0); + //!< causes a periodic thread to activate after "delay" milliseconds from now. + /*!< this resets the normal activation period, but after the next + activation occurs, the normal activation interval takes over again. */ + + int sleep_time() const { return _sleep_time; } + //!< returns the current periodic thread interval. + /*!< this is only meaningful for periodic threads. */ + + void sleep_time(int new_sleep) { _sleep_time = new_sleep; } + //!< adjusts the period for the thread to the "new_sleep" interval. + /*!< this is only meaningful for periodic threads. */ + + // these functions report on the thread state. + + bool thread_started() const { return _thread_ready; } + //!< returns true if the thread has been started. + /*!< this does not mean it is necessarily active. */ + + bool thread_finished() const { return !_thread_ready; } + //!< returns true if the thread has exited. + /*!< This can happen either by the thread responding to the stop() or + cancel() methods or when the thread stops of its own accord. if this + returns true, it means that the thread will not start up again unless + the user invokes start(). */ + + bool thread_active() const { return _thread_active; } + //!< returns true if the thread is currently performing its activity. + /*!< this information is not necessarily relevant even at the point it is + returned (because of the nature of multethreading), so don't believe this + information for anything important. */ + + bool should_stop() const { return _stop_thread; } + //!< reports whether the thread should stop right now. + /*!< this returns true due to an invocation of stop() or cancel(). */ + +private: + bool _thread_ready; //!< is the thread ready to run (or running)? + bool _thread_active; //!< is the thread currently performing? + bool _stop_thread; //!< true if the thread should stop now. + void *_data; //!< holds the thread's link back to whatever. +#ifdef __UNIX__ + pthread_t *_handle; //!< thread structure for our thread. +#elif defined(__WIN32__) + uintptr_t _handle; //!< thread handle for the active thread, or zero. +#endif + int _sleep_time; //!< threads perform at roughly this interval. + bool _periodic; //!< true if this thread should run repeatedly. + timely::time_stamp *_next_activation; //!< the next time perform_activity is called. + timed_thread_types _how; //!< how is the period evaluated? + + // the OS level thread functions. +#ifdef __UNIX__ + static void *periodic_thread_driver(void *hidden_pointer); + static void *one_shot_thread_driver(void *hidden_pointer); +#elif defined(__WIN32__) + static void periodic_thread_driver(void *hidden_pointer); + static void one_shot_thread_driver(void *hidden_pointer); +#endif + + // forbidden. + ethread(const ethread &); + ethread &operator =(const ethread &); +}; + +} //namespace. + +#endif + diff --git a/core/library/processes/heartbeat.cpp b/core/library/processes/heartbeat.cpp new file mode 100644 index 00000000..7b3d9f68 --- /dev/null +++ b/core/library/processes/heartbeat.cpp @@ -0,0 +1,102 @@ + + + +/*****************************************************************************\ +* * +* Name : heartbeat * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "heartbeat.h" + +#include +#include +#include + +using namespace basis; +using namespace timely; + +namespace processes { + +heartbeat::heartbeat(int misses_allowed, int check_interval) +: _next_heartbeat(new time_stamp()), + _check_interval(0), + _misses_allowed(0), + _misses(0) +{ reset(misses_allowed, check_interval); } + +heartbeat::heartbeat(const heartbeat &to_copy) +: root_object(), + _next_heartbeat(new time_stamp()), + _check_interval(0), + _misses_allowed(0), + _misses(0) +{ *this = to_copy; } + +heartbeat::~heartbeat() { WHACK(_next_heartbeat); } + +time_stamp heartbeat::heartbeat_time() const { return *_next_heartbeat; } + +bool heartbeat::due() const { return time_left() <= 0; } + +void heartbeat::made_request() { _misses++; reset_next_beat(); } + +void heartbeat::kabump() { _misses = 0; reset_next_beat(); } + +void heartbeat::reset_next_beat() +{ *_next_heartbeat = time_stamp(_check_interval); } + +int heartbeat::time_left() const +{ return int(_next_heartbeat->value() - time_stamp().value()); } + +bool heartbeat::dead() const +{ + // two cases mean the timer's dead; (1) if the misses are already too high, + // or (2) if the heartbeat is due and the misses are as many as allowed. + return (_misses > _misses_allowed) + || (due() && (_misses >= _misses_allowed)); +} + +void heartbeat::reset(int misses_allowed, int check_interval) +{ + _misses_allowed = misses_allowed; + _misses = 0; + _check_interval = check_interval; + reset_next_beat(); +} + +astring heartbeat::text_form(bool detailed) const +{ + astring to_return = (dead()? astring("expired, ") : astring("alive, ")); + to_return += (!dead() && due() ? astring("due now, ") + : astring::empty_string()); + to_return += a_sprintf("beats left=%d", misses_left()); + if (detailed) { + to_return += a_sprintf(", missed=%d, interval=%d, ", + missed_so_far(), checking_interval()); + to_return += astring("next=") + heartbeat_time().text_form(); + } + return to_return; +} + +heartbeat &heartbeat::operator =(const heartbeat &to_copy) +{ + if (this == &to_copy) return *this; + _check_interval = to_copy._check_interval; + _misses_allowed = to_copy._misses_allowed; + _misses = to_copy._misses; + *_next_heartbeat = *to_copy._next_heartbeat; + return *this; +} + +} //namespace. + + diff --git a/core/library/processes/heartbeat.h b/core/library/processes/heartbeat.h new file mode 100644 index 00000000..19fe3b32 --- /dev/null +++ b/core/library/processes/heartbeat.h @@ -0,0 +1,114 @@ +#ifndef HEARTBEAT_CLASS +#define HEARTBEAT_CLASS + +/*****************************************************************************\ +* * +* Name : heartbeat * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace processes { + +//! Monitors a periodic heartbeat to track a resource's health. +/*! + The heartbeat is defined as a "request-and-response" based check; when the + next periodic heartbeat is due, a request is sent out. The heartbeat + request is considered successfully dealt with only if a response comes back + for the request. If the user-defined number of requests are sent without + a single response coming back, then the 'patient' is considered dead. +*/ + +class heartbeat : public virtual basis::root_object +{ +public: + heartbeat(int misses_allowed = 500, int check_interval = 10000); + //!< creates a heartbeat monitor with the specified interval and maximum skips permitted. + /*!< this allows the heartbeat request to be missed "misses_allowed" times. + the heartbeats will become due every "check_interval" milliseconds. the + defaults are a joke; you really need to set them. */ + heartbeat(const heartbeat &to_copy); + + ~heartbeat(); + + DEFINE_CLASS_NAME("heartbeat"); + + heartbeat &operator =(const heartbeat &to_copy); + + basis::astring text_form(bool detailed = false) const; + //!< returns a readable form of the heartbeat's information. + + void reset(int misses_allowed, int check_interval); + //!< retrains the heartbeat monitor for a new configuration. + + bool due() const; + //!< is the next heartbeat due yet? + + bool dead() const; + //!< is this object considered dead from missing too many heartbeats? + /*!< this is true if the heartbeat being monitored missed too many + responses to heartbeat requests. if the maximum allowed requests have + been made and there was not even a single response, then the object is + considered dead. */ + + void made_request(); + //!< records that another heartbeat request was sent out. + /*!< the time for the next heartbeat request is reset to the time between + beats. if there were already the maximum allowed number of missed + responses, then the object is now dead. */ + void need_beat() { made_request(); } + //!< a synonym for the made_request() method. + + void kabump(); + //!< registers a heartbeat response and sets the state to healthy. + /*!< this records that a heartbeat response came back from the monitored + object. after this call, there are no heartbeats recorded as missed at + all. */ + void recycle() { kabump(); } + //!< a synonym for kabump(). + + // reporting functions for internal state... + + int missed_so_far() const { return _misses; } + //!< returns the number of heartbeat responses that are pending. + int misses_left() const { return _misses_allowed - _misses; } + //!< the number of misses that this object is still allowed. + + int allowed_misses() const { return _misses_allowed; } + //!< returns the number of misses allowed overall. + int checking_interval() const { return _check_interval; } + //!< returns the period of the heartbeats. + + timely::time_stamp heartbeat_time() const; + //!< returns the time when the next heartbeat will be requested. + /*!< if no heartbeats had been missed yet, then this is the time when + the due() method starts returning true. */ + + int time_left() const; + //!< number of milliseconds left before the next beat will be requested. + /*!< if the number is zero or negative, then a heartbeat is due. */ + +private: + timely::time_stamp *_next_heartbeat; + int _check_interval; + int _misses_allowed; + int _misses; + + void reset_next_beat(); //!< resets the next_heartbeat to our interval. +}; + +} //namespace. + +#endif + diff --git a/core/library/processes/launch_process.cpp b/core/library/processes/launch_process.cpp new file mode 100644 index 00000000..bb8d7ada --- /dev/null +++ b/core/library/processes/launch_process.cpp @@ -0,0 +1,345 @@ + +// Name : launch_process +// Author : Chris Koeritz +/****************************************************************************** +* Copyright (c) 1994-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "launch_process.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef __UNIX__ + #include + #include + #include + #include +#endif +#ifdef __WIN32__ + #include + #include + #include +#endif + +//#define DEBUG_LAUNCH_PROCESS + // uncomment for noisier debugging info. + +using namespace basis; +using namespace configuration; +using namespace loggers; +using namespace structures; +using namespace timely; + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s); + +namespace processes { + +//hmmm: some of these should probably be safe statics. + +mutex &__process_synchronizer() { + static mutex __hidden_synch; + return __hidden_synch; +} + +int_set __our_kids() { + static int_set __hidden_kids; + return __hidden_kids; +} + +#ifdef __WIN32__ +bool launch_process::event_poll(MSG &message) +{ + message.hwnd = 0; + message.message = 0; + message.wParam = 0; + message.lParam = 0; + if (!PeekMessage(&message, NIL, 0, 0, PM_REMOVE)) + return false; + TranslateMessage(&message); + DispatchMessage(&message); + return true; +} +#endif + +#define SUPPORT_SHELL_EXECUTE + // if this is not commented out, then the ShellExecute version of launch_ + // -process() is available. when commented out, ShellExecute is turned off. + // disabling this support is the most common because the ShellExecute method + // in win32 was only supported for wk203 and wxp, that is only after + // windows2000 was already available. since nt and w2k don't support this, + // we just usually don't mess with it. it didn't answer a single one of our + // issues on windows vista (wfista) anyway, so it's not helping. + +//const int MAXIMUM_COMMAND_LINE = 32 * KILOBYTE; + // maximum command line that we'll deal with here. + +#ifdef __UNIX__ +void launch_process::exiting_child_signal_handler(int sig_num) +{ + FUNCDEF("exiting_child_signal_handler"); + if (sig_num != SIGCHLD) { + // uhhh, this seems wrong. + } + auto_synchronizer l(__process_synchronizer()); + for (int i = 0; i < __our_kids().length(); i++) { + int status; + pid_t exited = waitpid(__our_kids()[i], &status, WNOHANG); + if ( (exited == -1) || (exited == __our_kids()[i]) ) { + // negative one denotes an error, which we are going to assume means the + // process has exited via some other method than our wait. if the value + // is the same as the process we waited for, that means it exited. + __our_kids().zap(i, i); + i--; + } else if (exited != 0) { + // zero would be okay; this result we do not understand. +#ifdef DEBUG_LAUNCH_PROCESS + LOG(a_sprintf("unknown result %d waiting for process %d", exited, __our_kids()[i])); +#endif + } + } +} +#endif + +//hmmm: this doesn't seem to account for quoting properly at all? +char_star_array launch_process::break_line(astring &app, const astring ¶meters) +{ + FUNCDEF("break_line"); + char_star_array to_return; + int_array posns; + int num = 0; + // find the positions of the spaces and count them. + for (int j = 0; j < parameters.length(); j++) { + if (parameters[j] == ' ') { + num++; + posns += j; + } + } + // first, add the app name to the list of parms. + to_return += new char[app.length() + 1]; + app.stuff(to_return[0], app.length()); + int last_posn = 0; + // now add each space-separated parameter to the list. + for (int i = 0; i < num; i++) { + int len = posns[i] - last_posn; + to_return += new char[len + 1]; + parameters.substring(last_posn, posns[i] - 1).stuff(to_return[i + 1], len); + last_posn = posns[i] + 1; + } + // catch anything left after last separator. + if (last_posn < parameters.length() - 1) { + int len = parameters.length() - last_posn; + to_return += new char[len + 1]; + parameters.substring(last_posn, parameters.length() - 1) + .stuff(to_return[to_return.last()], len); + } + // add the sentinel to the list of strings. + to_return += NIL; +#ifdef DEBUG_LAUNCH_PROCESS + for (int q = 0; to_return[q]; q++) { + LOG(a_sprintf("%d: %s\n", q, to_return[q])); + } +#endif + // now a special detour; fix the app name to remove quotes, which are + // not friendly to pass to exec. + if (app[0] == '"') app.zap(0, 0); + if (app[app.end()] == '"') app.zap(app.end(), app.end()); + return to_return; +} + +basis::un_int launch_process::run(const astring &app_name_in, const astring &command_line, + int flag, basis::un_int &child_id) +{ +#ifdef DEBUG_LAUNCH_PROCESS + FUNCDEF("run"); +#endif + child_id = 0; + astring app_name = app_name_in; + if (app_name[0] != '"') + app_name.insert(0, "\""); + if (app_name[app_name.end()] != '"') + app_name += "\""; +#ifdef __UNIX__ + // unix / linux implementation. + if (flag & RETURN_IMMEDIATELY) { + // they want to get back right away. + pid_t kid_pid = fork(); +#ifdef DEBUG_LAUNCH_PROCESS + LOG(a_sprintf("launch fork returned %d\n", kid_pid)); +#endif + if (!kid_pid) { + // this is the child; we now need to launch into what we were asked for. +#ifdef DEBUG_LAUNCH_PROCESS + LOG(a_sprintf("process %d execing ", application_configuration::process_id()) + app_name + + " parms " + command_line + "\n"); +#endif + char_star_array parms = break_line(app_name, command_line); + execv(app_name.s(), parms.observe()); + // oops. failed to exec if we got to here. +#ifdef DEBUG_LAUNCH_PROCESS + LOG(a_sprintf("child of fork (pid %d) failed to exec, error is ", + application_configuration::process_id()) + + critical_events::system_error_text(critical_events::system_error()) + + "\n"); +#endif + exit(0); // leave since this is a failed child process. + } else { + // this is the parent. let's see if the launch worked. + if (kid_pid == -1) { + // failure. + basis::un_int to_return = critical_events::system_error(); +#ifdef DEBUG_LAUNCH_PROCESS + LOG(a_sprintf("parent %d is returning after failing to create, " + "error is ", application_configuration::process_id()) + + critical_events::system_error_text(to_return) + + "\n"); +#endif + return to_return; + } else { + // yes, launch worked okay. + child_id = kid_pid; + { + auto_synchronizer l(__process_synchronizer()); + __our_kids() += kid_pid; + } + // hook in our child exit signal handler. + signal(SIGCHLD, exiting_child_signal_handler); + +#ifdef DEBUG_LAUNCH_PROCESS + LOG(a_sprintf("parent %d is returning after successfully " + "creating %d ", application_configuration::process_id(), kid_pid) + app_name + + " parms " + command_line + "\n"); +#endif + return 0; + } + } + } else { + // assume they want to wait. + return system((app_name + " " + command_line).s()); + } +#elif defined(__WIN32__) + +//checking on whether we have admin rights for the launch. +//if (IsUserAnAdmin()) { +// MessageBox(0, (astring("IS admin with ") + app_name_in + " " + command_line).s(), "launch process", MB_OK); +//} else { +// MessageBox(0, (astring("NOT admin for ") + app_name_in + " " + command_line).s(), "launch process", MB_OK); +//} + + PROCESS_INFORMATION process_info; + ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION)); + +#ifdef SUPPORT_SHELL_EXECUTE + if (flag & SHELL_EXECUTE) { + // new code for using shell execute method--required on vista for proper + // launching with the right security tokens. + int show_cmd = 0; + if (flag & HIDE_APP_WINDOW) { + // magic that hides a console window for mswindows. + show_cmd = SW_HIDE; + } else { + show_cmd = SW_SHOWNORMAL; + } + + SHELLEXECUTEINFO exec_info; + ZeroMemory(&exec_info, sizeof(SHELLEXECUTEINFO)); + exec_info.cbSize = sizeof(SHELLEXECUTEINFO); + exec_info.fMask = SEE_MASK_NOCLOSEPROCESS // get the process info. + | SEE_MASK_FLAG_NO_UI; // turn off any visible error dialogs. + exec_info.hwnd = GetDesktopWindow(); +//hmmm: is get desktop window always appropriate? + to_unicode_persist(temp_verb, "open"); +//does "runas" work on xp also? or anywhere? + exec_info.lpVerb = temp_verb; + to_unicode_persist(temp_file, app_name); + exec_info.lpFile = temp_file; + to_unicode_persist(temp_parms, command_line); + exec_info.lpParameters = temp_parms; + exec_info.nShow = show_cmd; +// exec_info.hProcess = &process_info; + + BOOL worked = ShellExecuteEx(&exec_info); + if (!worked) + return critical_events::system_error(); + // copy out the returned process handle. + process_info.hProcess = exec_info.hProcess; + process_info.dwProcessId = GetProcessId(exec_info.hProcess); + } else { +#endif //shell exec + // standard windows implementation using CreateProcess. + STARTUPINFO startup_info; + ZeroMemory(&startup_info, sizeof(STARTUPINFO)); + startup_info.cb = sizeof(STARTUPINFO); + int create_flag = 0; + if (flag & HIDE_APP_WINDOW) { + // magic that hides a console window for mswindows. +// version ver = portable::get_OS_version(); +// version vista_version(6, 0); +// if (ver < vista_version) { +// // we suspect that this flag is hosing us in vista. + create_flag = CREATE_NO_WINDOW; +// } + } + astring parms = app_name + " " + command_line; + bool success = CreateProcess(NIL, to_unicode_temp(parms), NIL, NIL, false, + create_flag, NIL, NIL, &startup_info, &process_info); + if (!success) + return critical_events::system_error(); + // success then, merge back into stream. + +#ifdef SUPPORT_SHELL_EXECUTE + } +#endif //shell exec + + // common handling for CreateProcess and ShellExecuteEx. + child_id = process_info.dwProcessId; + basis::un_long retval = 0; + if (flag & AWAIT_VIA_POLLING) { + // this type of waiting is done without blocking on the process. + while (true) { + MSG msg; + event_poll(msg); + // check if the process is gone yet. + BOOL ret = GetExitCodeProcess(process_info.hProcess, &retval); + if (!ret) { + break; + } else { + // if they aren't saying it's still active, then we will leave. + if (retval != STILL_ACTIVE) + break; + } + time_control::sleep_ms(14); + } + } else if (flag & AWAIT_APP_EXIT) { + // they want to wait for the process to exit. + WaitForInputIdle(process_info.hProcess, INFINITE); + WaitForSingleObject(process_info.hProcess, INFINITE); + GetExitCodeProcess(process_info.hProcess, &retval); + } + // drop the process and thread handles. + if (process_info.hProcess) + CloseHandle(process_info.hProcess); + if (process_info.hThread) + CloseHandle(process_info.hThread); + return (basis::un_int)retval; +#else + #pragma error("hmmm: launch_process: no implementation for this OS.") +#endif + return 0; +} + +} // namespace. + diff --git a/core/library/processes/launch_process.h b/core/library/processes/launch_process.h new file mode 100644 index 00000000..a988c4c5 --- /dev/null +++ b/core/library/processes/launch_process.h @@ -0,0 +1,102 @@ +#ifndef LAUNCH_PROCESS_CLASS +#define LAUNCH_PROCESS_CLASS + +/*****************************************************************************\ +* * +* Name : launch_process +* Author : Chris Koeritz +* * +******************************************************************************* +* Copyright (c) 1994-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +// forward. +struct tagMSG; + +namespace processes { + +//! a simple wrapper of an array of char *, used by launch_process::break_line(). +class char_star_array : public basis::array +{ +public: + char_star_array() : basis::array(0, NIL, SIMPLE_COPY | EXPONE | FLUSH_INVISIBLE) {} + ~char_star_array() { + // clean up all the memory we're holding. + for (int i = 0; i < length(); i++) { + delete [] (use(i)); + } + } +}; + +////////////// + +//! Provides the capability to start processes in a variety of ways to run other applications. + +class launch_process : public virtual basis::nameable +{ +public: + DEFINE_CLASS_NAME("launch_process"); + + virtual ~launch_process() {} + + enum launch_flags { + HIDE_APP_WINDOW = 0x1, + //!< launches the application invisibly if possible. + AWAIT_APP_EXIT = 0x2, + //!< stays in the function until the launched application has exited. + RETURN_IMMEDIATELY = 0x4, + //!< starts the application and comes right back to the caller. + AWAIT_VIA_POLLING = 0x8, + //!< launches the app but polls and doesn't block on its exit. + SHELL_EXECUTE = 0x10 + //!< only valid on windows--uses ShellExecute instead of CreateProcess. + }; + + static basis::un_int run(const basis::astring &app_name, const basis::astring &command_line, + int flag, basis::un_int &child_id); + //!< starts an application using the "app_name" as the executable to run. + /*!< the "command_line" is the set of parameters to be passed to the app. + the return value is OS specific but can be identified using + system_error_text(). usually a zero return means success and non-zero + codes are errors. the "flag" is an XORed value from the process launch + flags that dictates how the app is to be started. in practice, only the + HIDE_APP_WINDOW flag can be combined with other values. if either AWAIT + flag is used, then the return value will be the launched process's own + exit value. the thread or process_id of the launched process is stored + in "child_id" if appropriate. */ + + static char_star_array break_line(basis::astring &app, const basis::astring ¶meters); + //!< prepares an "app" to launch with the "parameters" (via exec). + /*!< this breaks the strings for an application named "app" and its + "parameters" into an array of char * that is appropriate for the execv + function. */ + +private: +#ifdef __UNIX__ + static void exiting_child_signal_handler(int sig_num); + //!< awaits the child processes rather than leaving process handles willy nilly. +#endif +#ifdef __WIN32__ + static bool event_poll(tagMSG &message); + //!< tries to process one win32 event and retrieve the "message" from it. + /*!< this is a very general poll and will retrieve any message that's + available for the current thread. the message is actually processed + here also, by calling translate and dispatch. the returned structure + is mainly interesting for knowing what was done. */ +#endif + +}; + +} // namespace. + +#endif // outer guard. + diff --git a/core/library/processes/letter.cpp b/core/library/processes/letter.cpp new file mode 100644 index 00000000..d197aa19 --- /dev/null +++ b/core/library/processes/letter.cpp @@ -0,0 +1,57 @@ +/*****************************************************************************\ +* * +* Name : letter * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "letter.h" + +#include +#include +#include + +using namespace basis; +using namespace timely; + +namespace processes { + +letter::letter(int type, int start_after) +: _type(type), + _ready_time(new time_stamp(start_after)) +{} + +letter::letter(const letter &to_copy) +: _type(to_copy._type), + _ready_time(new time_stamp(*to_copy._ready_time)) +{ +} + +letter::~letter() +{ + _type = 0; + WHACK(_ready_time); +} + +bool letter::ready_to_send() { return time_stamp() >= *_ready_time; } + +void letter::set_ready_time(int start_after) +{ *_ready_time = time_stamp(start_after); } + +letter &letter::operator =(const letter &to_copy) +{ + if (this == &to_copy) return *this; + _type = to_copy._type; + *_ready_time = *to_copy._ready_time; + return *this; +} + +} //namespace. + diff --git a/core/library/processes/letter.h b/core/library/processes/letter.h new file mode 100644 index 00000000..50c77dfb --- /dev/null +++ b/core/library/processes/letter.h @@ -0,0 +1,73 @@ +#ifndef LETTER_CLASS +#define LETTER_CLASS + +/*****************************************************************************\ +* * +* Name : letter * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace processes { + +//! A virtual base class for pieces of "mail". Used by the mailbox object. + +class letter : public virtual basis::text_formable +{ +public: + letter(int type = 0, int start_after = 0); + //!< constructs a letter with the "type" and initial pause of "start_after". + /*!< a "type" for this letter must be specified, even if it is not intended + to be used. some letter managers may rely on this number identifying + different kinds of mail. the types should be unique within one mailbox. + a "type" of zero indicates an invalid letter. if the "start_after" is + non-zero, then it indicates that this letter should not be sent until + that many milliseconds have elapsed. */ + + letter(const letter &to_copy); + //!< copy constructor for base parts. + + virtual ~letter(); + //!< derived classes should also implement this. + /*!< a virtual destructor should be implemented by each derived class + to take care of class specific cleaning. note that the destructor should + NEVER attempt to use the mailbox system that it was stored in (or any + other mailbox system for that matter). this is necessary for prohibiting + deadlock conditions, but it's not that much of a restriction usually. */ + + letter &operator =(const letter &to_copy); + //!< assignment operator for base object. + + virtual void text_form(basis::base_string &fill) const = 0; + //!< derived letters must print a status blurb describing their contents. + + int type() const { return _type; } + //!< returns the type of letter held here. + + bool ready_to_send(); + //!< returns true if this letter is ready to + + void set_ready_time(int start_after); + //!< resets the time when this letter is ready to be sent. + /*!< the letter will now not be allowed to send until "start_after" + milliseconds from now. once the letter is added to a mailbox, it may + be too late to adjust this duration. */ + +private: + int _type; //!< the kind of mail this item represents. + timely::time_stamp *_ready_time; //!< time when this letter will be ready to send. +}; + +} //namespace. + +#endif + diff --git a/core/library/processes/mail_stop.h b/core/library/processes/mail_stop.h new file mode 100644 index 00000000..12afe524 --- /dev/null +++ b/core/library/processes/mail_stop.h @@ -0,0 +1,72 @@ +#ifndef MAIL_STOP_CLASS +#define MAIL_STOP_CLASS + +/*****************************************************************************\ +* * +* Name : mail_stop * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "safe_callback.h" + +#include + +namespace processes { + +// forward: +class letter; + +//! Base class for routes on which letters are automatically delivered. +/*! + The letters will show up for a particular unique id at this mail stop. + They are delivered by the object serving as the post office. +*/ + +class mail_stop : public safe_callback +{ +public: + + // it is required that the derived mail_stop invoke the end_availability() + // method in its destructor before it destroys any other objects. + + class items_to_deliver : public callback_data_block { + public: + items_to_deliver(const structures::unique_int &id, letter *package) + : _id(id), _package(package) {} + const structures::unique_int &_id; + letter *_package; + }; + + virtual void delivery_for_you(const structures::unique_int &id, letter *package) = 0; + //!< the derived object must provide this function. + /*!< prompts the recipient with the "id" to accept delivery of a "package". + the package can be modified as desired and MUST be recycled before + returning from this function. + IMPORTANT NOTE: the receiver MUST be thread-safe with respect to the + objects that it uses to handle this delivery! this is because mail is + delivered on a thread other than the main program thread. */ + +protected: + //! invoked by the safe callback machinery. + /*! this is implemented in this class and merely re-routes the call to the + more specific delivery_for_you() method. */ + virtual void real_callback(callback_data_block &data) { + items_to_deliver *bits = dynamic_cast(&data); + if (!bits) return; // bad type. + delivery_for_you(bits->_id, bits->_package); + } + +}; + +} //namespace. + +#endif + diff --git a/core/library/processes/mailbox.cpp b/core/library/processes/mailbox.cpp new file mode 100644 index 00000000..52b89adf --- /dev/null +++ b/core/library/processes/mailbox.cpp @@ -0,0 +1,262 @@ +/*****************************************************************************\ +* * +* Name : mailbox * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "letter.h" +#include "mailbox.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace loggers; +using namespace structures; +using namespace textual; + +namespace processes { + +const int MAILBOX_BITS = 9; + // we allow N bits in our table size, which means the table will have 2^N + // elements. careful with that increase... + +class mail_cabinet +{ +public: + amorph _waiting; + + mail_cabinet() : _waiting(0) {} + + ~mail_cabinet() { _waiting.reset(); } + + mail_cabinet(mail_cabinet &formal(to_copy)) { + non_continuable_error("mail_cabinet", "copy constructor", "should never be called"); + } + + mail_cabinet &operator =(mail_cabinet &formal(to_copy)) { + non_continuable_error("mail_cabinet", "assignment operator", + "should never be called"); + return *this; + } +}; + +////////////// + +class mailbox_bank : public int_hash +{ +public: + mailbox_bank() : int_hash (MAILBOX_BITS) {} + ~mailbox_bank() { reset(); } + + void get_ids(int_set &to_fill); + // returns the list of identifiers for people with mailboxes. + + void add_cabinet(const unique_int &id); + // creates a new mail receptacle for the "id". + + bool zap_cabinet(const unique_int &id); + // removes the cabinet for "id". + + void add_item(const unique_int &id, letter *to_add); + // stuffs an item "to_add" in for "id". + + bool get(const unique_int &id, letter * &to_receive); + // retrieves the next waiting package for "id" into "to_receive". + + void clean_up(); + // gets rid of any cabinets without any packages. +}; + +void mailbox_bank::clean_up() +{ + int_set ids; + get_ids(ids); + for (int i = 0; i < ids.elements(); i++) { + mail_cabinet *entry = find(ids[i]); + // if the cabinet has zero elements, we zap it. + if (!entry->_waiting.elements()) zap(ids[i]); + } +} + +void mailbox_bank::get_ids(int_set &to_fill) { to_fill = ids(); } + +void mailbox_bank::add_cabinet(const unique_int &id) +{ + if (find(id.raw_id())) return; // already exists. + mail_cabinet *to_add = new mail_cabinet; + add(id.raw_id(), to_add); +} + +bool mailbox_bank::zap_cabinet(const unique_int &id) +{ + if (!find(id.raw_id())) return false; // doesn't exist. + return zap(id.raw_id()); +} + +void mailbox_bank::add_item(const unique_int &id, letter *to_add) +{ + mail_cabinet *found = find(id.raw_id()); + if (!found) { + add_cabinet(id); + found = find(id.raw_id()); + // there should never be a failure case that would prevent the new cabinet + // from being added (besides overall memory failure). + if (!found) { +//complain + return; + } + } + found->_waiting.append(to_add); +} + +bool mailbox_bank::get(const unique_int &id, letter * &to_receive) +{ + mail_cabinet *found = find(id.raw_id()); + if (!found) return false; // no cabinet, much less any mail. + + if (!found->_waiting.elements()) return false; // no mail waiting. + for (int i = 0; i < found->_waiting.elements(); i++) { + // check if its time is ripe... + if (!found->_waiting.borrow(i)->ready_to_send()) continue; + // get the waiting mail and remove its old slot. + to_receive = found->_waiting.acquire(i); + found->_waiting.zap(i, i); + return true; + } + return false; +} + +////////////// + +mailbox::mailbox() +: _transaction_lock(new mutex), + _packages(new mailbox_bank) +{ +} + +mailbox::~mailbox() +{ + WHACK(_packages); + WHACK(_transaction_lock); +} + +void mailbox::get_ids(int_set &to_fill) +{ + auto_synchronizer l(*_transaction_lock); + _packages->get_ids(to_fill); +} + +void mailbox::drop_off(const unique_int &id, letter *package) +{ + auto_synchronizer l(*_transaction_lock); + _packages->add_item(id, package); +} + +void mailbox::clean_up() +{ + auto_synchronizer l(*_transaction_lock); + _packages->clean_up(); +} + +int mailbox::waiting(const unique_int &id) const +{ + auto_synchronizer l(*_transaction_lock); + mail_cabinet *found = _packages->find(id.raw_id()); + int to_return = 0; // if no cabinet, this is the proper count. + // if there is a cabinet, then get the size. + if (found) + to_return = found->_waiting.elements(); + return to_return; +} + +bool mailbox::pick_up(const unique_int &id, letter * &package) +{ + package = NIL; + auto_synchronizer l(*_transaction_lock); + return _packages->get(id, package); +} + +bool mailbox::close_out(const unique_int &id) +{ + auto_synchronizer l(*_transaction_lock); + bool ret = _packages->zap_cabinet(id); + return ret; +} + +void mailbox::show(astring &to_fill) +{ + auto_synchronizer l(*_transaction_lock); + int_set ids; + _packages->get_ids(ids); + for (int i = 0; i < ids.elements(); i++) { + mail_cabinet &mc = *_packages->find(ids[i]); + to_fill += astring(astring::SPRINTF, "cabinet %d:", ids[i]) + + parser_bits::platform_eol_to_chars(); + for (int j = 0; j < mc._waiting.elements(); j++) { + letter &l = *mc._waiting.borrow(j); + astring text; + l.text_form(text); + to_fill += string_manipulation::indentation(4) + + astring(astring::SPRINTF, "%4ld: ", j + 1) + + text + parser_bits::platform_eol_to_chars(); + } + } +} + +void mailbox::limit_boxes(int max_letters) +{ + auto_synchronizer l(*_transaction_lock); + int_set ids; + _packages->get_ids(ids); + for (int i = 0; i < ids.elements(); i++) { + mail_cabinet &mc = *_packages->find(ids[i]); + if (mc._waiting.elements() > max_letters) { + // this one needs cleaning. + mc._waiting.zap(max_letters, mc._waiting.elements() - 1); + } + } +} + +void mailbox::apply(apply_function *to_apply, void *data_link) +{ + auto_synchronizer l(*_transaction_lock); + int_set ids; + _packages->get_ids(ids); + for (int i = 0; i < ids.elements(); i++) { + mail_cabinet &mc = *_packages->find(ids[i]); + for (int j = 0; j < mc._waiting.elements(); j++) { + letter &l = *mc._waiting.borrow(j); + outcome ret = to_apply(l, ids[i], data_link); + if ( (ret == APPLY_WHACK) || (ret == APPLY_WHACK_STOP) ) { + // they wanted this node removed. + mc._waiting.zap(j, j); + j--; // skip back before missing guy so we don't omit anyone. + if (ret == APPLY_WHACK_STOP) + break; // they wanted to be done with it also. + } else if (ret == APPLY_STOP) { + break; // we hit the exit condition. + } + } + } +} + +} //namespace. + + diff --git a/core/library/processes/mailbox.h b/core/library/processes/mailbox.h new file mode 100644 index 00000000..e41fe946 --- /dev/null +++ b/core/library/processes/mailbox.h @@ -0,0 +1,131 @@ +#ifndef MAILBOX_CLASS +#define MAILBOX_CLASS + +/*****************************************************************************\ +* * +* Name : mailbox * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace processes { + +class letter; +class mailbox_bank; + +//! Implements a thread safe "mail" delivery system. +/*! + Senders can drop packages off into the mailbox and the receivers can get + those packages back out of it. The base class for all mail items is also + provided in this library (letter.h). The name of this object is slightly + misleading; this object is really more of a post office. Each unique id + has its own mailbox slot for receiving mail. +*/ + +class mailbox : public virtual basis::root_object +{ +public: + mailbox(); + virtual ~mailbox(); + + void drop_off(const structures::unique_int &id, letter *package); + //!< drops a "package" in the mailbox for "id". + /*!< note that when you send a package to someone, you give up all + authority and control over that package. hopefully the end recipient + will eventually pick it up and then delete it. if the package is never + received, then this object will delete it. */ + + bool pick_up(const structures::unique_int &id, letter * &package); + //!< returns true if the mailbox for "id" had a "package" to be delivered. + /*!< don't forget to check multiple times on a true outcome, since there + could be more than one package waiting. false is returned when no more + mail is waiting. be careful; "package" could be a bomb. dynamic casts + seem appropriate as a method for ensuring that you get the type of + object you expect. note that once the invoker receives a package, it + is their responsibility to carefully manage it and then delete the + package after handling. not deleting the "package" pointer is grounds + for memory leaks. */ + + int waiting(const structures::unique_int &id) const; + //!< returns the number of items waiting for the "id" specified, if any. + + void get_ids(structures::int_set &to_fill); + //!< stuffs the set "to_fill" with the ids of all mailboxes present. + /*!< if you want only those mailboxes holding one or more letters, then + call the clean_up() method prior to this method. */ + + bool close_out(const structures::unique_int &id); + //!< dumps all packages stored for the "id" and shuts down its mailbox. + /*!< the destructors for those packages should never try to do anything + with the mailbox system or a deadlock could result. true is returned if + the "id" had a registered mailbox; false just indicates there was no box + to clear up. */ + + void show(basis::astring &to_fill); + //!< provides a picture of what's waiting in the mailbox. + /*!< this relies on the derived letter's required text_form() function. */ + + void clean_up(); + //!< removes any empty mailboxes from our list. + + void limit_boxes(int max_letters); + //!< establishes a limit on the number of letters. + /*!< this is a helper function for a very special mailbox; it has a + limited maximum size and any letters above the "max_letters" count will + be deleted. don't use this function on any mailbox where all letters + are important; your mailbox must have a notion of unreliability before + this would ever be appropriate. */ + + enum apply_outcomes { + OKAY = basis::common::OKAY, //!< continue apply process. + + DEFINE_OUTCOME(APPLY_STOP, -46, "Halt the apply process"), + DEFINE_OUTCOME(APPLY_WHACK, -47, "Removes the current letter, but " + "continues"), + DEFINE_OUTCOME(APPLY_WHACK_STOP, -48, "Halts apply and trashes the " + "current letter") + }; + + typedef basis::outcome apply_function(letter ¤t, int uid, void *data_link); + //!< the "apply_function" is what a user of apply() must provide. + /*!< the function will be called on every letter in the mailbox unless one + of the invocations returns APPLY_STOP or APPLY_WHACK_STOP; this causes + the apply process to stop (and zap the node for APPLY_WHACK). the "uid" + is the target for the "current" letter. the "data_link" provides a way + for the function to refer back to a parent class or data package of some + sort. note that all sorts of deadlocks will occur if your apply + function tries to do anything on the mailbox, even transitively. keep + those functions as simple as possible. */ + + void apply(apply_function *to_apply, void *data_link); + //!< calls the "to_apply" function on possibly every letter in the mailbox. + /*!< this iterates until the function returns a 'STOP' outcome. the + "data_link" pointer is passed to the apply function. NOTE: it is NOT safe + to rearrange or manipulate the mailbox in any way from your "to_apply" + function; the only changes allowed are those caused by the return value + from "to_apply". */ + +private: + basis::mutex *_transaction_lock; //!< keeps the state of the mailbox safe. + mailbox_bank *_packages; //!< the collection of mail that has arrived. + + // prohibited. + mailbox(const mailbox &); + mailbox &operator =(const mailbox &); +}; + +} //namespace. + +#endif + diff --git a/core/library/processes/makefile b/core/library/processes/makefile new file mode 100644 index 00000000..57e23f6e --- /dev/null +++ b/core/library/processes/makefile @@ -0,0 +1,12 @@ +include cpp/variables.def + +PROJECT = processes +TYPE = library +TARGETS = processes.lib +SOURCE = configured_applications.cpp ethread.cpp heartbeat.cpp launch_process.cpp \ + letter.cpp mailbox.cpp post_office.cpp \ + process_control.cpp process_entry.cpp rendezvous.cpp safe_callback.cpp safe_roller.cpp \ + state_machine.cpp thread_cabinet.cpp + +include cpp/rules.def + diff --git a/core/library/processes/os_event.h b/core/library/processes/os_event.h new file mode 100644 index 00000000..9e52c388 --- /dev/null +++ b/core/library/processes/os_event.h @@ -0,0 +1,53 @@ +#ifndef OS_EVENT_CLASS +#define OS_EVENT_CLASS + +/*****************************************************************************\ +* * +* Name : OS_event * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1995-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "letter.h" + +#include +#include + +namespace processes { + +// forward. +class post_office; + +//! Models an OS-level event so we can represent activities occurring there. + +class OS_event : public letter, public virtual basis::text_formable +{ +public: + basis::un_int _message; + basis::un_int _parm1; + basis::un_int _parm2; + + DEFINE_CLASS_NAME("OS_event"); + + OS_event(int event_type, basis::un_int message, basis::un_int parm1, basis::un_int parm2) + : letter(event_type), _message(message), _parm1(parm1), _parm2(parm2) {} + + virtual void text_form(basis::base_string &fill) const { + fill.assign(text_form()); + } + basis::astring text_form() const { + return basis::a_sprintf("os_event: msg=%d parm1=%d parm2=%d", _message, _parm1, _parm2); + } +}; + +} //namespace. + +#endif + diff --git a/core/library/processes/post_office.cpp b/core/library/processes/post_office.cpp new file mode 100644 index 00000000..523e4bb3 --- /dev/null +++ b/core/library/processes/post_office.cpp @@ -0,0 +1,391 @@ +/*****************************************************************************\ +* * +* Name : post_office * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "ethread.h" +#include "letter.h" +#include "mailbox.h" +#include "post_office.h" +#include "thread_cabinet.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace configuration; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; + +namespace processes { + +//#define DEBUG_POST_OFFICE + // uncomment if you want the noisy version. + +#undef LOG +#define LOG(a) CLASS_EMERGENCY_LOG(program_wide_logger::get(), a) + +const int CLEANING_INTERVAL = 14 * SECOND_ms; + // the interval between cleaning of extra letters and dead mailboxes. + +const int SNOOZE_TIME_FOR_POSTMAN = 42; + // we'll snooze for this long if absolutely nothing happened during the + // thread's activation. if things are going on, our snooze time is reduced + // by the length of time we were delivering items. + +const int DELIVERIES_ALLOWED = 350; + // the maximum number of deliveries we'll try to get done per thread run. + +////////////// + +//hmmm: arrhhh--maybe we need to spawn a thread per postal route. + +class postal_carrier : public ethread +{ +public: + postal_carrier(post_office &parent, const unique_int &route) + : ethread(SNOOZE_TIME_FOR_POSTMAN, ethread::SLACK_INTERVAL), + _parent(parent), + _route(route) + {} + + DEFINE_CLASS_NAME("postal_carrier"); + + void perform_activity(void *) { + FUNCDEF("perform_activity"); + bool finished; + try { + finished = _parent.deliver_mail_on_route(_route, *this); + } catch(...) { + LOG(astring("caught exception during mail delivery!")); + } + if (!finished) { + // not finished delivering all items. + reschedule(); + } else { + reschedule(SNOOZE_TIME_FOR_POSTMAN); + } + } + +private: + post_office &_parent; + unique_int _route; +}; + +////////////// + +class postal_cache : public mailbox {}; + +////////////// + +class tagged_mail_stop : public virtual text_formable +{ +public: + mail_stop *_route; + unique_int _thread_id; + unique_int _id; + + tagged_mail_stop(const unique_int &id = 0, mail_stop *route = NIL, + const unique_int &thread_id = 0) + : _route(route), _thread_id(thread_id), _id(id) {} + + DEFINE_CLASS_NAME("tagged_mail_stop"); + + virtual void text_form(basis::base_string &fill) const { + fill.assign(text_form()); + } + + virtual astring text_form() const { + return a_sprintf("%s: id=%d, addr=%08lx, thr_id=%d", + static_class_name(), _id.raw_id(), _route, _thread_id.raw_id()); + } +}; + +////////////// + +class route_map : public amorph +{ +public: + tagged_mail_stop *find(const unique_int &id) { + for (int i = 0; i < elements(); i++) { + tagged_mail_stop *curr = borrow(i); + if (curr && (curr->_id == id)) return curr; + } + return NIL; + } + + bool zap(const unique_int &id) { + for (int i = 0; i < elements(); i++) { + tagged_mail_stop *curr = borrow(i); + if (curr && (curr->_id == id)) { + amorph::zap(i, i); + return true; + } + } + return false; + } + +}; + +////////////// + +class letter_morph : public amorph {}; + +////////////// + +post_office::post_office() +: _post(new mailbox), + _routes(new route_map), + _next_cleaning(new time_stamp), + _threads(new thread_cabinet) +{ +} + +post_office::~post_office() +{ + stop_serving(); + WHACK(_post); + WHACK(_routes); + WHACK(_next_cleaning); + WHACK(_threads); +} + +void post_office::show_routes(astring &to_fill) +{ + auto_synchronizer l(c_mutt); +//hmmm: simplify this; just use the int_set returning func and print that. + astring current_line; + astring temp; + if (_routes->elements()) + to_fill += astring("Mail Delivery Routes:") + parser_bits::platform_eol_to_chars(); + + for (int i = 0; i < _routes->elements(); i++) { + const tagged_mail_stop *tag = _routes->get(i); + if (!tag) continue; + temp = astring(astring::SPRINTF, "%d ", tag->_id.raw_id()); + if (current_line.length() + temp.length() >= 80) { + current_line += parser_bits::platform_eol_to_chars(); + to_fill += current_line; + current_line.reset(); + } + current_line += temp; + } + // catch the last line we created. + if (!!current_line) to_fill += current_line; +} + +void post_office::stop_serving() { if (_threads) _threads->stop_all(); } + +void post_office::show_mail(astring &output) +{ + output.reset(); + output += parser_bits::platform_eol_to_chars(); + output += astring("Mailbox Contents at ") + time_stamp::notarize(true) + + parser_bits::platform_eol_to_chars() + parser_bits::platform_eol_to_chars(); + astring box_state; + _post->show(box_state); + if (box_state.t()) output += box_state; + else + output += astring("No items are awaiting delivery.") + + parser_bits::platform_eol_to_chars(); +} + +void post_office::drop_off(const unique_int &id, letter *package) +{ +#ifdef DEBUG_POST_OFFICE + FUNCDEF("drop_off"); + LOG(astring(astring::SPRINTF, "mailbox drop for %d: ", id) + + package->text_form()); +#endif + _post->drop_off(id, package); +#ifdef DEBUG_POST_OFFICE + if (!route_listed(id)) { + LOG(a_sprintf("letter for %d has no route!", id)); + } +#endif +} + +bool post_office::pick_up(const unique_int &id, letter * &package) +{ +#ifdef DEBUG_POST_OFFICE + FUNCDEF("pick_up"); +#endif + bool to_return = _post->pick_up(id, package); +#ifdef DEBUG_POST_OFFICE + if (to_return) + LOG(astring(astring::SPRINTF, "mailbox grab for %d: ", id) + + package->text_form()); +#endif + return to_return; +} + +bool post_office::route_listed(const unique_int &id) +{ + int_set route_set; + get_route_list(route_set); + return route_set.member(id.raw_id()); +} + +void post_office::get_route_list(int_set &route_set) +{ + auto_synchronizer l(c_mutt); + + // gather the set of routes that we should carry mail to. + route_set.reset(); + + if (!_routes->elements()) { + // if there are no elements, why bother iterating? + return; + } + + for (int i = 0; i < _routes->elements(); i++) { + const tagged_mail_stop *tag = _routes->get(i); + if (!tag) continue; + route_set.add(tag->_id.raw_id()); + } +} + +void post_office::clean_package_list(post_office &formal(post), + letter_morph &to_clean) +{ + FUNCDEF("clean_package_list"); + auto_synchronizer l(c_mutt); + + // recycle all the stuff we had in the list. + while (to_clean.elements()) { + letter *package = to_clean.acquire(0); + to_clean.zap(0, 0); + if (!package) { + LOG("saw empty package in list to clean!"); + continue; + } + WHACK(package); + } +} + +bool post_office::deliver_mail_on_route(const unique_int &route, + ethread &carrier) +{ + FUNCDEF("deliver_mail_on_route"); + auto_synchronizer l(c_mutt); + +#ifdef DEBUG_POST_OFFICE + time_stamp enter; +#endif + if (carrier.should_stop()) return true; // get out if thread was told to. + + int deliveries = 0; // number of items delivered so far. + letter_morph items_for_route; + // holds the items that need to be sent to this route. + + // pickup all of the mail that we can for this route. + while (deliveries < DELIVERIES_ALLOWED) { + if (carrier.should_stop()) + return true; // get out if thread was told to. + letter *package; + if (!_post->pick_up(route, package)) { + // there are no more letters for this route. + break; // skip out of the loop. + } + deliveries++; // count this item as a delivery. + items_for_route.append(package); + } + + if (!items_for_route.elements()) return true; // nothing to handle. + + // locate the destination for this route. + tagged_mail_stop *real_route = _routes->find(route); // find the route. + if (!real_route) { + // we failed to find the route we wanted... + LOG(astring(astring::SPRINTF, "route %d disappeared!", route.raw_id())); + clean_package_list(*this, items_for_route); + return true; + } + + // now deliver what we found for this route. + for (int t = 0; t < items_for_route.elements(); t++) { + if (carrier.should_stop()) { + // get out if thread was told to. + return true; + } + letter *package = items_for_route.acquire(t); + // hand the package out on the route. + mail_stop::items_to_deliver pack(route, package); + real_route->_route->invoke_callback(pack); + // the callee is responsible for cleaning up. + } + + bool finished_all = (deliveries < DELIVERIES_ALLOWED); + // true if we handled everything we could have. + + if (carrier.should_stop()) return true; // get out if thread was told to. + + // this bit is for the post office at large, but we don't want an extra + // thread when we've got all these others handy. + bool cleaning_time = time_stamp() > *_next_cleaning; + if (cleaning_time) { + _post->clean_up(); // get rid of dead mailboxes in main post office. + _next_cleaning->reset(CLEANING_INTERVAL); + } + + time_stamp exit; +#ifdef DEBUG_POST_OFFICE + int duration = int(exit.value() - enter.value()); + if (duration > 20) + LOG(a_sprintf("deliveries took %d ms.", duration)); +#endif + return finished_all; +} + +bool post_office::register_route(const unique_int &id, + mail_stop &carrier_path) +{ + auto_synchronizer l(c_mutt); + + tagged_mail_stop *found = _routes->find(id); + if (found) return false; // already exists. + + postal_carrier *po = new postal_carrier(*this, id); + unique_int thread_id = _threads->add_thread(po, false, NIL); + // add the thread so we can record its id. + tagged_mail_stop *new_stop = new tagged_mail_stop(id, &carrier_path, + thread_id); + _routes->append(new_stop); + // add the mail stop to our listings. + po->start(NIL); + // now start the thread so it can begin cranking. + return true; +} + +bool post_office::unregister_route(const unique_int &id) +{ + auto_synchronizer l(c_mutt); + + tagged_mail_stop *tag = _routes->find(id); + if (!tag) return false; // doesn't exist yet. + unique_int thread_id = tag->_id; + _routes->zap(id); + _threads->zap_thread(thread_id); + return true; +} + +} //namespace. + + diff --git a/core/library/processes/post_office.h b/core/library/processes/post_office.h new file mode 100644 index 00000000..da53e6a1 --- /dev/null +++ b/core/library/processes/post_office.h @@ -0,0 +1,115 @@ +#ifndef CENTRAL_MAILBOX_CLASS +#define CENTRAL_MAILBOX_CLASS + +/*****************************************************************************\ +* * +* Name : post_office * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "mail_stop.h" + +#include +#include + +namespace processes { + +class letter; +class letter_morph; +class mailbox; +class postal_cache; +class route_map; +class thread_cabinet; + +//! Manages a collection of mailboxes and implements delivery routes for mail. + +class post_office +{ +public: + post_office(); + + virtual ~post_office(); + //!< stop_serving must be invoked prior to this destructor. + + void stop_serving(); + //!< gets the mailbox to stop delivering items prior to a shutdown. + + // informational functions... + + DEFINE_CLASS_NAME("post_office"); + + void show_mail(basis::astring &to_fill); + //!< prints a snapshot of all currently pending letters into "to_fill". + + void show_routes(basis::astring &to_fill); + //!< writes a listing of the current routes into "to_fill". + + // general delivery services subject to the constraints of the mailbox class. + + void drop_off(const structures::unique_int &id, letter *package); + //!< sends a "package" on its way to the "id" via the registered route. + /*!< note that mail is not rejected if there is no known route to the + mail_stop for the "id"; it is assumed in that case that the recipient + will check at the post office. */ + + bool pick_up(const structures::unique_int &id, letter * &package); + //!< retrieves a "package" intended for the "id" if one exists. + /*!< false is returned if none are available. on success, the "package" is + filled in with the address of the package and it is the caller's + responsibility to destroy or recycle() it after dealing with it. */ + + ////////////// + + // mail delivery and routing support. + + bool register_route(const structures::unique_int &id, mail_stop &carrier_path); + //!< registers a route "carrier_path" for mail deliveries to the "id". + + bool unregister_route(const structures::unique_int &id); + //!< removes a route for the "id". + /*!< this should be done before the object's destructor is invoked since + the letter carrier could be on his way with a letter at an arbitrary time. + also, the mail_stop should be shut down (with end_availability()) at that + time also. if those steps are taken, then the carrier is guaranteed not + to bother the recipient. */ + + bool route_listed(const structures::unique_int &id); + //!< returns true if there is a route listed for the "id". + /*!< this could change at any moment, since another place in the source + code could remove the route just after this call. it is information from + the past by the time it's returned. */ + + ////////////// + + bool deliver_mail_on_route(const structures::unique_int &route, ethread &carrier); + //!< for internal use only--delivers the letters to known routes. + /*!< this function should only be used internally to prompt the delivery + of packages that are waiting for objects we have a route to. it returns + true when all items that were waiting have been sent. */ + +private: + basis::mutex c_mutt; //!< protects our lists and dead letter office from corruption. + mailbox *_post; //!< the items awaiting handling. + route_map *_routes; //!< the pathways that have been defined. + timely::time_stamp *_next_cleaning; //!< when the next mailbox flush will occur. + thread_cabinet *_threads; //!< our list of threads for postal routes. + + void get_route_list(structures::int_set &route_set); + //!< retrieves the list of routes that we have registered. + + void clean_package_list(post_office &post, letter_morph &to_clean); + //!< recycles all of the letters held in "to_clean". +}; + +} //namespace. + +#endif + diff --git a/core/library/processes/process_control.cpp b/core/library/processes/process_control.cpp new file mode 100644 index 00000000..ce0dbf03 --- /dev/null +++ b/core/library/processes/process_control.cpp @@ -0,0 +1,606 @@ + +//NOTE: +// +// this thing is showing bad behavior on win32 when unicode is enabled. +// therefore unicode is currently disabled for win32, which is a shame. +// but something needs to be fixed in our unicode conversion stuff; the unicode versions +// of the file names were not getting correctly back-converted into the ascii counterpart, +// and *that* is broken. +// ** this may be a widespread issue in the win32 code related to unicode right now! +// ** needs further investigation when a reason to better support ms-windows emerges. +// +// => but don't panic... right now things work as long as you do a utf-8 / ascii build, +// which is how everything's configured. + + +/* +* Name : process_control +* Author : Chris Koeritz +** +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include "process_entry.h" +#include "process_control.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef __UNIX__ + #include +#endif + +using namespace basis; +using namespace configuration; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace structures; + +namespace processes { + +#ifdef __WIN32__ + #include + const astring NTVDM_NAME = "ntvdm.exe"; + // the umbrella process that hangs onto 16 bit tasks for NT. + #ifdef _MSCVER + #include + #endif +#endif +#ifdef __UNIX__ + #include + #include +#endif + +//#define DEBUG_PROCESS_CONTROL + // uncomment for noisier debugging. + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +////////////// + +class process_implementation_hider +{ +public: +#ifdef __WIN32__ + // psapi members: + application_instance psapi_dll; + application_instance vdm_dll; + BOOL (WINAPI *enumerate_processes)(basis::un_int *, basis::un_int cb, basis::un_int *); + BOOL (WINAPI *enumerate_modules)(HANDLE, HMODULE *, basis::un_int, basis::un_int *); + basis::un_int (WINAPI *get_module_name)(HANDLE, HMODULE, LPTSTR, basis::un_int); +#ifdef _MSCVER + INT (WINAPI *tasker_16bit)(basis::un_int, TASKENUMPROCEX fp, LPARAM); +#endif + + // toolhelp members: + application_instance kernel32_dll; + HANDLE (WINAPI *create_snapshot)(basis::un_int,basis::un_int); + BOOL (WINAPI *first_process)(HANDLE,LPPROCESSENTRY32); + BOOL (WINAPI *next_process)(HANDLE,LPPROCESSENTRY32); + + // get an atomic view of the process list, which rapidly becomes out of date. +/// HANDLE hSnapShot; + + process_implementation_hider() + : psapi_dll(NIL), vdm_dll(NIL), enumerate_processes(NIL), + enumerate_modules(NIL), get_module_name(NIL), +#ifdef _MSCVER + tasker_16bit(NIL), +#endif + kernel32_dll(NIL), create_snapshot(NIL), first_process(NIL), + next_process(NIL) {} + + ~process_implementation_hider() { + if (psapi_dll) FreeLibrary(psapi_dll); + if (vdm_dll) FreeLibrary(vdm_dll); + if (kernel32_dll) FreeLibrary(kernel32_dll); + psapi_dll = NIL; + vdm_dll = NIL; + kernel32_dll = NIL; + } +#endif +}; + +////////////// + +class process_info_clump +{ +public: + basis::un_int _process_id; + process_entry_array &_to_fill; // where to add entries. + + process_info_clump(basis::un_int id, process_entry_array &to_fill) + : _process_id(id), _to_fill(to_fill) {} +}; + +////////////// + +// top-level functions... + +process_control::process_control() +: _ptrs(new process_implementation_hider), +#ifdef __WIN32__ + _use_psapi(true), +#endif +#ifdef __UNIX__ + _rando(new chaos), +#endif + _healthy(false) +{ + // Check to see if were running under Windows95 or Windows NT. + version osver = application_configuration::get_OS_version(); + +#ifdef __WIN32__ + if (osver.v_revision() == VER_PLATFORM_WIN32_WINDOWS) { + // we're on Windows 95, so use the toolhelp API for the processes. + _use_psapi = false; + } else if (osver.v_major() >= 5) { + // w2k and onward can do the toolhelp instead of psapi. + _use_psapi = false; + } + if (_use_psapi) + _healthy = initialize_psapi_support(); + else + _healthy = initialize_toolhelp_support(); +#endif +#ifdef __UNIX__ + _healthy = true; +#endif +} + +process_control::~process_control() +{ + WHACK(_ptrs); +#ifdef __UNIX__ + WHACK(_rando); +#endif +} + +void process_control::sort_by_name(process_entry_array &v) +{ + process_entry temp; + for (int gap = v.length() / 2; gap > 0; gap /= 2) + for (int i = gap; i < v.length(); i++) + for (int j = i - gap; j >= 0 + && (filename(v[j].path()).basename().raw() + > filename(v[j + gap].path()).basename().raw()); + j = j - gap) + { temp = v[j]; v[j] = v[j + gap]; v[j + gap] = temp; } +} + +void process_control::sort_by_pid(process_entry_array &v) +{ + process_entry temp; + for (int gap = v.length() / 2; gap > 0; gap /= 2) + for (int i = gap; i < v.length(); i++) + for (int j = i - gap; j >= 0 && (v[j]._process_id + > v[j + gap]._process_id); j = j - gap) + { temp = v[j]; v[j] = v[j + gap]; v[j + gap] = temp; } +} + +bool process_control::query_processes(process_entry_array &to_fill) +{ + if (!_healthy) return false; +#ifdef __WIN32__ + if (!_use_psapi) { + // we're on Windows 95 or something, so use the toolhelp API for the + // processes. + return get_processes_with_toolhelp(to_fill); + } else { + // we're on Windows NT and so on; use the process API (PSAPI) to get info. + return get_processes_with_psapi(to_fill); + } +#endif +#ifdef __UNIX__ + return get_processes_with_ps(to_fill); +#endif +} + +#ifdef __WIN32__ +bool process_control::initialize_psapi_support() +{ + // create an instance of the PSAPI dll for querying 32-bit processes and + // an instance of the VDM dll support just in case there are also some + // 16-bit processes. + _ptrs->psapi_dll = LoadLibraryA("psapi.dll"); + if (!_ptrs->psapi_dll) return false; + _ptrs->vdm_dll = LoadLibraryA("vdmdbg.dll"); + if (!_ptrs->vdm_dll) return false; + + // locate the functions that we want to call. + _ptrs->enumerate_processes = (BOOL(WINAPI *)(basis::un_int *,basis::un_int,basis::un_int*)) + GetProcAddress(_ptrs->psapi_dll, "EnumProcesses"); + _ptrs->enumerate_modules + = (BOOL(WINAPI *)(HANDLE, HMODULE *, basis::un_int, basis::un_int *)) + GetProcAddress(_ptrs->psapi_dll, "EnumProcessModules"); + _ptrs->get_module_name + = (basis::un_int (WINAPI *)(HANDLE, HMODULE, LPTSTR, basis::un_int)) + GetProcAddress(_ptrs->psapi_dll, "GetModuleFileNameExA"); +#ifdef _MSCVER + _ptrs->tasker_16bit = (INT(WINAPI *)(basis::un_int, TASKENUMPROCEX, LPARAM)) + GetProcAddress(_ptrs->vdm_dll, "VDMEnumTaskWOWEx"); +#endif + if (!_ptrs->enumerate_processes || !_ptrs->enumerate_modules + || !_ptrs->get_module_name +#ifdef _MSCVER + || !_ptrs->tasker_16bit +#endif + ) return false; + + return true; +} + +bool process_control::initialize_toolhelp_support() +{ + // get hooked up with the kernel dll so we can use toolhelp functions. + _ptrs->kernel32_dll = LoadLibraryA("Kernel32.DLL"); + if (!_ptrs->kernel32_dll) return false; + + // create pointers to the functions we want to invoke. + _ptrs->create_snapshot = (HANDLE(WINAPI *)(basis::un_int,basis::un_int)) + GetProcAddress(_ptrs->kernel32_dll, "CreateToolhelp32Snapshot"); + _ptrs->first_process = (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32)) + GetProcAddress(_ptrs->kernel32_dll, "Process32First"); + _ptrs->next_process = (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32)) + GetProcAddress(_ptrs->kernel32_dll, "Process32Next"); + if (!_ptrs->next_process || !_ptrs->first_process + || !_ptrs->create_snapshot) return false; + return true; +} + +#endif + +bool process_control::zap_process(basis::un_int to_zap) +{ +#ifdef DEBUG_PROCESS_CONTROL + FUNCDEF("zap_process"); +#endif + if (!_healthy) return false; +#ifdef __UNIX__ + int ret = kill(to_zap, 9); + // send the serious take-down signal to the process. + return !ret; +#endif +#ifdef __WIN32__ + HANDLE h = OpenProcess(PROCESS_TERMINATE, false, to_zap); + if (!h) { +#ifdef DEBUG_PROCESS_CONTROL + int err = critical_events::system_error(); + LOG(a_sprintf("error zapping process %d=", to_zap) + + critical_events::system_error_text(err)); +#endif + return false; + } + int exit_code = 0; + BOOL ret = TerminateProcess(h, exit_code); + CloseHandle(h); + return !!ret; +#endif +} + +process_entry process_control::query_process(basis::un_int to_query) +{ +// FUNCDEF("query_process"); + process_entry to_return; + + process_entry_array to_fill; + bool got_em = query_processes(to_fill); + if (!got_em) return to_return; + + for (int i = 0; i < to_fill.length(); i++) { + if (to_fill[i]._process_id == to_query) + return to_fill[i]; + } + +//hmmm: implement more specifically. +#ifdef __UNIX__ +//put in the single process grabber deal. +#endif +#ifdef __WIN32__ +//grab the entry from the list. +#endif + + return to_return; +} + +bool process_control::find_process_in_list(const process_entry_array &processes, + const astring &app_name_in, int_set &pids) +{ +#ifdef DEBUG_PROCESS_CONTROL + FUNCDEF("find_process_in_list"); +#endif + pids.clear(); + astring app_name = app_name_in.lower(); + + version os_ver = application_configuration::get_OS_version(); + + bool compare_prefix = (os_ver.v_major() == 5) && (os_ver.v_minor() == 0); + // we only compare the first 15 letters due to a recently noticed bizarre + // bug where w2k only shows (and reports) the first 15 letters of file + // names through toolhelp. + + bool found = false; // was it seen in process list? + for (int i = 0; i < processes.length(); i++) { + filename path = processes[i].path(); + astring base = path.basename().raw().lower(); + // a kludge for w2k is needed--otherwise we will miss seeing names that + // really are running due to the toolhelp api being busted and only + // reporting the first 15 characters of the name. + if ( (compare_prefix && (base.compare(app_name, 0, 0, 15, false))) + || (base == app_name) ) { + found = true; + pids.add(processes[i]._process_id); + } + } +#ifdef DEBUG_PROCESS_CONTROL + if (!found) + LOG(astring("failed to find the program called ") + app_name); +#endif + return found; +} + +////////////// + +#ifdef __WIN32__ +// this section is the PSAPI version of the query. + +// called back on each 16 bit task. +BOOL WINAPI process_16bit(basis::un_int dwThreadId, WORD module_handle16, WORD hTask16, + PSZ pszModName, PSZ pszFileName, LPARAM lpUserDefined) +{ + process_info_clump *to_stuff = (process_info_clump *)lpUserDefined; + process_entry to_add; + to_add._process_id = to_stuff->_process_id; + to_add._module16 = hTask16; + to_add.path(pszFileName); +//threads, etc? + to_stuff->_to_fill += to_add; + return true; +} + +bool process_control::get_processes_with_psapi(process_entry_array &to_fill) +{ + // prepare the result object. + to_fill.reset(); + + // loop over the process enumeration function until we are sure that we + // have allocated a large enough space for all existing processes. + bool got_all = false; + basis::un_int *pid_list = NIL; + basis::un_int max_size = 428 * sizeof(basis::un_int); + basis::un_int actual_size = 0; + while (!got_all) { + pid_list = (basis::un_int *)HeapAlloc(GetProcessHeap(), 0, max_size); + if (!pid_list) return false; + if (!_ptrs->enumerate_processes(pid_list, max_size, &actual_size)) { + HeapFree(GetProcessHeap(), 0, pid_list); + return false; + } + if (actual_size == max_size) { + // there were too many to store, so whack the partial list. + HeapFree(GetProcessHeap(), 0, pid_list); + max_size *= 2; // try with twice as much space. + } else got_all = true; + } + + // calculate the number of process ids that got stored. + basis::un_int ids = actual_size / sizeof(basis::un_int); + + // examine each process id that we found. + for (basis::un_int i = 0; i < ids; i++) { + // get process information if security permits. +//turn chunk below into "scan process" or something. + HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + false, pid_list[i]); + flexichar process_name[MAX_ABS_PATH + 1] = { '\0' }; + if (hProcess) { + // go over the modules for the process. the first will be the main + // application module and the others will be threads. ??? + basis::un_int max_size = 1 * sizeof(HMODULE); +//hmmm: could do a rescan loop here if too many. + basis::un_int actual_size = 0; + HMODULE *module_handles = new HMODULE[max_size + 1]; + if (!module_handles) { + CloseHandle(hProcess); + HeapFree(GetProcessHeap(), 0, pid_list); + return false; + } + if (_ptrs->enumerate_modules(hProcess, module_handles, max_size, + &actual_size)) { + // we want the name of the first module. + if (!_ptrs->get_module_name(hProcess, *module_handles, process_name, + sizeof(process_name))) + process_name[0] = 0; + } + WHACK(module_handles); + CloseHandle(hProcess); + } + + // we add whatever information we were able to find about this process. + process_entry new_entry; + new_entry._process_id = pid_list[i]; + astring converted_name = from_unicode_temp(process_name); + new_entry.path(converted_name); + +//how to get? performance data helper? +/// new_entry._threads = threads; + to_fill += new_entry; + + // if we're looking at ntvdm, then there might be 16 bit processes + // attached to it. + if (new_entry.path().length() >= NTVDM_NAME.length()) { + astring temp = new_entry.path().substring + (new_entry.path().end() - NTVDM_NAME.length() + 1, + new_entry.path().end()); + temp.to_lower(); +#ifdef _MSCVER +//hmmm: pull this back in for mingw when it seems to be supported, if ever. + if (temp == NTVDM_NAME) { + // set up a callback stampede on the 16 bit processes. + process_info_clump info(pid_list[i], to_fill); + _ptrs->tasker_16bit(pid_list[i], (TASKENUMPROCEX)process_16bit, + (LPARAM)&info); + } +#endif + } + } + + if (pid_list) HeapFree(GetProcessHeap(), 0, pid_list); + return true; +} + +////////////// + +// this is the toolhelp version of the query. + +bool process_control::get_processes_with_toolhelp(process_entry_array &to_fill) +{ + // prepare the result object. + to_fill.reset(); + + // get an atomic view of the process list, which rapidly becomes out of date. + HANDLE hSnapShot; + hSnapShot = _ptrs->create_snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapShot == INVALID_HANDLE_VALUE) return false; + + // start iterating through the snapshot by getting the first process. + PROCESSENTRY32 entry; + entry.dwSize = sizeof(PROCESSENTRY32); + BOOL keep_going = _ptrs->first_process(hSnapShot, &entry); + + // while we see valid processes, iterate through them. + while (keep_going) { + // add an entry for the current process. + process_entry new_entry; + new_entry._process_id = entry.th32ProcessID; + new_entry._references = entry.cntUsage; + new_entry._threads = entry.cntThreads; + new_entry._parent_process_id = entry.th32ParentProcessID; + astring exe_file = from_unicode_temp(entry.szExeFile); + new_entry.path(exe_file); + to_fill += new_entry; + entry.dwSize = sizeof(PROCESSENTRY32); // reset struct size. + keep_going = _ptrs->next_process(hSnapShot, &entry); + } + + CloseHandle(hSnapShot); + return true; +} +#endif // __WIN32__ + +#ifdef __UNIX__ + +#define CLOSE_TMP_FILE { \ +/* continuable_error("process_control", "get_processes_with_ps", error); */ \ + if (output) { \ + fclose(output); \ + unlink(tmpfile.s()); \ + } \ +} + +bool process_control::get_processes_with_ps(process_entry_array &to_fill) +{ + FUNCDEF("get_processes_with_ps"); + to_fill.reset(); + // we ask the operating system to give us a list of processes. + a_sprintf tmpfile("/tmp/proc_list_%d_%d.txt", application_configuration::process_id(), + _rando->inclusive(1, 400000)); + a_sprintf cmd("ps wax --format \"%%p %%a\" >%s", tmpfile.s()); +//hmmm: add more info as we expand the process entry. + FILE *output = NIL; // initialize now to establish variable for our macro. + int sysret = system(cmd.s()); + if (negative(sysret)) { +LOG("got negative return from system()!"); + CLOSE_TMP_FILE; + return false; + } + output = fopen(tmpfile.s(), "r"); + if (!output) { +LOG("failed to open process list file!"); + CLOSE_TMP_FILE; + return false; + } + const int max_buff = 10000; + char buff[max_buff]; + size_t size_read = 1; + astring accumulator; + while (size_read > 0) { + // read bytes from the file. + size_read = fread(buff, 1, max_buff, output); + // if there was anything, store it in the string. + if (size_read > 0) + accumulator += astring(astring::UNTERMINATED, buff, size_read); + } + CLOSE_TMP_FILE; + // parse the string up now. + bool first_line = true; + while (accumulator.length()) { + // eat any spaces in front. + if (first_line) { + // we toss the first line since it's a header with columns. + int cr_indy = accumulator.find('\n'); + accumulator.zap(0, cr_indy); + if (accumulator[accumulator.end()] == '\r') + accumulator.zap(accumulator.end(), accumulator.end()); + first_line = false; + continue; + } + while (accumulator.length() && (accumulator[0] == ' ')) + accumulator.zap(0, 0); + // look for the first part of the line; the process id. + int num_indy = accumulator.find(' '); + if (negative(num_indy)) break; + basis::un_int p_id = accumulator.substring(0, num_indy).convert(0); + accumulator.zap(0, num_indy); + int cr_indy = accumulator.find('\n'); + if (negative(cr_indy)) + cr_indy = accumulator.end() + 1; + astring proc_name = accumulator.substring(0, cr_indy - 1); + if (proc_name[proc_name.end()] == '\r') + proc_name.zap(proc_name.end(), proc_name.end()); + accumulator.zap(0, cr_indy); + int space_indy = proc_name.find(' '); +//hmmm: this is incorrect regarding names that do have spaces in them. + if (negative(space_indy)) + space_indy = proc_name.end() + 1; + process_entry to_add; + to_add._process_id = p_id; + astring path = proc_name.substring(0, space_indy - 1); + // patch the pathname if we see any bracketed items. + int brackets_in = 0; + for (int i = 0; i < path.length(); i++) { + if (path[i] == '[') brackets_in++; + else if (path[i] == ']') brackets_in--; + if (brackets_in) { + // if we see a slash inside brackets, then we patch it so it doesn't + // confuse the filename object's directory handling. + if ( (path[i] == '/') || (path[i] == '\\') ) + path[i] = '#'; + } + } + to_add.path(path); + to_fill += to_add; + } + return true; +} +#endif // __UNIX__ + +} //namespace. + diff --git a/core/library/processes/process_control.h b/core/library/processes/process_control.h new file mode 100644 index 00000000..c6d25861 --- /dev/null +++ b/core/library/processes/process_control.h @@ -0,0 +1,105 @@ +#ifndef PROCESS_CONTROL_CLASS +#define PROCESS_CONTROL_CLASS + +/*****************************************************************************\ +* * +* Name : process_control * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "process_entry.h" + +#include +#include +#include + +namespace processes { + +// forward. +class process_entry_array; +class process_implementation_hider; + +//! Provides a bridge to the operating system for information on processes. +/*! + This object can query the operating system for the current set of processes + or zap a particular process of interest. +*/ + +class process_control : public virtual basis::nameable +{ +public: + process_control(); + virtual ~process_control(); + + DEFINE_CLASS_NAME("process_control"); + + bool healthy() const { return _healthy; } + //!< returns true if this object should be functional. + /*!< if it failed to construct properly, this returns false. usually a + failure indicates that a required dynamic library is missing, such as + "psapi.dll" on win32. */ + + process_entry query_process(basis::un_int to_query); + //!< returns the information for just one process. + + bool query_processes(process_entry_array &to_fill); + //!< finds the processes that are running and drops them into "to_fill". + + bool zap_process(basis::un_int to_zap); + //!< preemptively zaps the process "to_zap". + /*!< this does not invoke any friendly graceful shut down process, but + merely terminates it if possible. */ + + static bool find_process_in_list(const process_entry_array &processes, + const basis::astring &app_name, structures::int_set &pids); + //!< uses a pre-existing list of "processes" to search for the "app_name". + /*!< if the process is found, true is returned and the "pids" are set to + all entries matching the process name. note that this is an approximate + match for some OSes that have a brain damaged process lister (such as + ms-windows); they have programs listed under incomplete names in some + cases. */ + + void sort_by_name(process_entry_array &to_sort); + // sorts the list by process name. + void sort_by_pid(process_entry_array &to_sort); + // sorts the list by process id. + +private: + process_implementation_hider *_ptrs; //!< our OS baggage. +#ifdef __UNIX__ + mathematics::chaos *_rando; //!< used for process list. +#endif +#ifdef __WIN32__ + bool _use_psapi; //!< true if we should be using the PSAPI on NT and family. +#endif + bool _healthy; //!< true if construction succeeded. + +#ifdef __UNIX__ + bool get_processes_with_ps(process_entry_array &to_fill); + //!< asks the ps program what processes exist. +#endif +#ifdef __WIN32__ + // fill in our function pointers to access the kernel functions appropriate + // for either NT (psapi) or 9x (toolhelp). + bool initialize_psapi_support(); + bool initialize_toolhelp_support(); + + bool get_processes_with_psapi(process_entry_array &to_fill); + //!< uses the PSAPI support for windows NT 4 (or earlier?). + bool get_processes_with_toolhelp(process_entry_array &to_fill); + //!< uses the toolhelp support for windows 9x, ME, 2000. +#endif +}; + +} //namespace. + +#endif // outer guard. + diff --git a/core/library/processes/process_entry.cpp b/core/library/processes/process_entry.cpp new file mode 100644 index 00000000..40cf3589 --- /dev/null +++ b/core/library/processes/process_entry.cpp @@ -0,0 +1,86 @@ +/*****************************************************************************\ +* * +* Name : process_entry * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "process_entry.h" + +#include +#include +#include + +using namespace basis; +using namespace filesystem; + +namespace processes { + +process_entry::process_entry() +: _process_id(0), + _references(0), + _threads(0), + _parent_process_id(0), + _module16(0), + _process_path(new astring) +{} + +process_entry::process_entry(const process_entry &to_copy) +: _process_id(0), + _references(0), + _threads(0), + _parent_process_id(0), + _module16(0), + _process_path(new astring) +{ + operator =(to_copy); +} + +process_entry::~process_entry() +{ + WHACK(_process_path); +} + +void process_entry::text_form(basis::base_string &fill) const +{ + fill = text_form(); +} + +process_entry &process_entry::operator =(const process_entry &to_copy) +{ + if (&to_copy == this) return *this; + _process_id = to_copy._process_id; + _references = to_copy._references; + _threads = to_copy._threads; + _parent_process_id = to_copy._parent_process_id; + *_process_path = *to_copy._process_path; + _module16 = to_copy._module16; + return *this; +} + +const astring &process_entry::path() const { return *_process_path; } + +void process_entry::path(const astring &new_path) +{ *_process_path = new_path; } + +astring process_entry::text_form() const +{ +#ifdef __UNIX__ + filename pat(path()); +#else + filename pat(path().lower()); +#endif + return a_sprintf("%s: pid=%u refs=%u thrd=%u par=%u mod16=%u path=%s", + pat.basename().raw().s(), _process_id, _references, _threads, + _parent_process_id, _module16, pat.dirname().raw().s()); +} + +} //namespace. + diff --git a/core/library/processes/process_entry.h b/core/library/processes/process_entry.h new file mode 100644 index 00000000..7c30b31b --- /dev/null +++ b/core/library/processes/process_entry.h @@ -0,0 +1,67 @@ +#ifndef PROCESS_ENTRY_CLASS +#define PROCESS_ENTRY_CLASS + +/*****************************************************************************\ +* * +* Name : process_entry * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include + +namespace processes { + +//! Encapsulates information about OS processes. + +class process_entry : public virtual basis::text_formable +{ +public: + basis::un_int _process_id; //!< the OS identifier of this process. + basis::un_int _references; //!< the number of references to (users of) this process. + basis::un_int _threads; //!< the number of threads in use by this process. + basis::un_int _parent_process_id; //!< the process id of the owning process. + basis::un_short _module16; //!< non-zero if this process is a 16-bit application. + + process_entry(); + process_entry(const process_entry &to_copy); + ~process_entry(); + + DEFINE_CLASS_NAME("process_entry"); + + process_entry &operator =(const process_entry &to_copy); + + const basis::astring &path() const; + void path(const basis::astring &new_path); + + basis::astring text_form() const; + //!< returns a descriptive string for the information here. + + void text_form(basis::base_string &fill) const; //!< base class requirement. + +private: + basis::astring *_process_path; +}; + +////////////// + +//! a handy class that implements an array of process entries. + +class process_entry_array : public basis::array {}; + +////////////// + +} //namespace. + +#endif + diff --git a/core/library/processes/rendezvous.cpp b/core/library/processes/rendezvous.cpp new file mode 100644 index 00000000..3f3385e8 --- /dev/null +++ b/core/library/processes/rendezvous.cpp @@ -0,0 +1,211 @@ +/*****************************************************************************\ +* * +* Name : rendezvous * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//note: after repeated investigation, it seems that if we unlink the rendezvous +// file on destruction, then this hoses up any locks attempted ever after. +// instead of waiting for a lock, new attempts think they can go ahead, +// even though someone else might also have been given the lock. it seems +// we cannot remove the files without destroying the semantics. + +#include "rendezvous.h" + +#include +#include +#include +#include +#include + +#ifdef __UNIX__ + #include + #include + #include +#endif + +using namespace basis; +using namespace filesystem; + +namespace processes { + +//#define DEBUG_RENDEZVOUS + // uncomment for a noisier file. + +#undef LOG +#define LOG(tp) printf("%s%s\n", time_stamp::notarize(true).s(), astring(tp).s()) + // we can only use simple logging here since the rendezvous is relied on + // at very low levels and use of a log_base object would cause infinite + // loops. + +// used for the name of a mutex or part of the unix lock filename. +astring general_lock_name(const astring &root_part) +{ return root_part + "_app_lock"; } + +#ifdef __UNIX__ +// the name of the locking file used in unix. +astring unix_rendez_file(const astring &lock_name) +{ + astring clean_name = lock_name; + // remove troublesome characters from the name. + filename::detooth_filename(clean_name); + // make sure our target directory exists. + + // this choice is only user specific. +// astring tmp_dir = portable::env_string("TMP") + "/rendezvous"; + + // this choice uses a system-wide location. + astring tmp_dir = "/tmp/rendezvous"; + + mkdir(tmp_dir.observe(), 0777); + return tmp_dir + "/ren_" + clean_name; +} +#endif + +rendezvous::rendezvous(const astring &root_name) +: _handle(NIL), + _locked(false), + _root_name(new astring(root_name)) +{ +#ifdef DEBUG_RENDEZVOUS + FUNCDEF("constructor"); +#endif + astring lock_name = general_lock_name(root_name); +#ifdef __UNIX__ + astring real_name = unix_rendez_file(lock_name); + FILE *locking_file = fopen(real_name.s(), "wb"); + if (!locking_file) { +#ifdef DEBUG_RENDEZVOUS + LOG(astring("failure to create locking file ") + real_name + + ": " + critical_events::system_error_text(critical_events::system_error()) ); +#endif + return; + } + // success now. + _handle = locking_file; +#endif +#ifdef __WIN32__ + _handle = CreateMutex(NIL, false, to_unicode_temp(lock_name)); + if (!_handle) return; +#endif +} + +rendezvous::~rendezvous() +{ +#ifdef DEBUG_RENDEZVOUS + FUNCDEF("destructor"); + LOG("okay, into destructor for rendezvous."); +#endif +#ifdef __UNIX__ + if (_handle) { + if (_locked) { + int ret = lockf(fileno((FILE *)_handle), F_ULOCK, sizeof(int)); + if (ret) { +#ifdef DEBUG_RENDEZVOUS + LOG("failure to get lock on file."); +#endif + } + _locked = false; // clear our locked status since we no longer have one. + +//note: after repeated investigation, it seems that if we unlink the rendezvous +// file on destruction, then this hoses up any locks attempted ever after. +// instead of waiting for a lock, new attempts think they can go ahead, +// even though someone else might also have been given the lock. it seems +// we cannot remove the files without destroying the semantics. + } + + fclose((FILE *)_handle); + _handle = NIL; + } +#endif +#ifdef __WIN32__ + if (_handle) CloseHandle((HANDLE)_handle); +#endif + WHACK(_root_name); +} + +void rendezvous::establish_lock() { lock(); } + +void rendezvous::repeal_lock() { unlock(); } + +bool rendezvous::healthy() const +{ + return !!_handle; +} + +bool rendezvous::lock(locking_methods how) +{ +#ifdef DEBUG_RENDEZVOUS + FUNCDEF("lock"); +#endif + if (how == NO_LOCKING) return false; + if (!healthy()) return false; +#ifdef __UNIX__ + int command = F_TLOCK; + if (how == ENDLESS_WAIT) command = F_LOCK; + int ret = lockf(fileno((FILE *)_handle), command, sizeof(int)); + if (ret) { +#ifdef DEBUG_RENDEZVOUS + LOG("failure to get lock on file."); +#endif + return false; + } +#ifdef DEBUG_RENDEZVOUS + LOG("okay, got lock on shared mem."); +#endif + _locked = true; + return true; +#endif +#ifdef __WIN32__ + int timing = 0; // immediate return. + if (how == ENDLESS_WAIT) timing = INFINITE; + int ret = WaitForSingleObject((HANDLE)_handle, timing); + if ( (ret == WAIT_ABANDONED) || (ret == WAIT_TIMEOUT) ) return false; + else if (ret != WAIT_OBJECT_0) { +#ifdef DEBUG_RENDEZVOUS + LOG("got an unanticipated error from waiting for the mutex."); +#endif + return false; + } + _locked = true; + return true; +#endif + return false; +} + +void rendezvous::unlock() +{ +#ifdef DEBUG_RENDEZVOUS + FUNCDEF("unlock"); +#endif + if (!healthy()) return; + if (_locked) { +#ifdef __UNIX__ + int ret = lockf(fileno((FILE *)_handle), F_ULOCK, sizeof(int)); + if (ret) { +#ifdef DEBUG_RENDEZVOUS + LOG("failure to get lock on file."); +#endif + } +#endif +#ifdef __WIN32__ + ReleaseMutex((HANDLE)_handle); +#endif + _locked = false; + } else { +#ifdef DEBUG_RENDEZVOUS + LOG("okay, rendezvous wasn't locked."); +#endif + } +} + +} //namespace. + diff --git a/core/library/processes/rendezvous.h b/core/library/processes/rendezvous.h new file mode 100644 index 00000000..b04f3bd4 --- /dev/null +++ b/core/library/processes/rendezvous.h @@ -0,0 +1,76 @@ +#ifndef RENDEZVOUS_CLASS +#define RENDEZVOUS_CLASS + +/*****************************************************************************\ +* * +* Name : rendezvous * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +namespace processes { + +//! An inter-process synchronization primitive. +/*! + A lock can be created that only one process owns at a time; those that do + not acquire the lock can either return immediately or wait until the current + lock owner releases the rendezvous. This is unlike the mutex object in + basis, since mutexes only synchronize within the same application. The + rendezvous can instead allow synchronization of resources between + applications, but this comes at a higher cost per usage. +*/ + +class rendezvous : public virtual basis::base_synchronizer +{ +public: + rendezvous(const basis::astring &root_name); + //!< the healthy() method should be checked to ensure creation succeeded. + + virtual ~rendezvous(); + //!< any lock held is released and the lower level structures freed. + + DEFINE_CLASS_NAME("rendezvous"); + + bool healthy() const; + //!< returns true if the rendezvous object is operable. + /*!< there are cases where creation of the rendezvous might fail; they + can be trapped here. */ + + //! different ways that the lock() attempt can be made. + enum locking_methods { NO_LOCKING, ENDLESS_WAIT, IMMEDIATE_RETURN }; + + bool lock(locking_methods how = ENDLESS_WAIT); + //!< grab the lock, if possible. + /*!< if this is not the first time locking the same rendezvous, that's + fine as long as the number of unlocks matches the number of locks. */ + + void unlock(); + //!< releases a previously acquired lock. + + // these two methods implement the synchronizer_base interface. + virtual void establish_lock(); + virtual void repeal_lock(); + + const basis::astring &root_name() const; + //!< returns the root name passed in the constructor. + +private: + void *_handle; //!< the real OS version of the inter-app lock. + bool _locked; //!< true if the lock was successful and still locked. + basis::astring *_root_name; //!< the identifier for this rendezvous. +}; + +} //namespace. + +#endif + diff --git a/core/library/processes/safe_callback.cpp b/core/library/processes/safe_callback.cpp new file mode 100644 index 00000000..ca62be94 --- /dev/null +++ b/core/library/processes/safe_callback.cpp @@ -0,0 +1,174 @@ + + + +/*****************************************************************************\ +* * +* Name : safe_callback * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "safe_callback.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace loggers; +using namespace structures; + +namespace processes { + +////////////// + +callback_data_block::~callback_data_block() {} + +////////////// + +class live_object_info +{ +public: + int _references; // the number of times it's been added to the list. + + live_object_info() : _references(1) {} +}; + +////////////// + +static bool _live_objects_are_gone = false; + // flags when the global_live_objects singleton winks out of existence. this + // should prevent ordering conflicts during the static destructions. + +class global_live_objects : public virtual root_object +{ +public: + global_live_objects() : _objects(rotating_byte_hasher(), 12) {} + // note that we have about a 2 billion callback object limit currently. + + ~global_live_objects() { _live_objects_are_gone = true; } + + DEFINE_CLASS_NAME("global_live_objects"); + + // returns true if the "object" is listed as valid. + bool listed(void *object) { + auto_synchronizer l(_lock); + live_object_info *loi = NIL; + return _objects.find(object, loi); + } + + // adds the "object" to the list, or if it's already there, ups the refcount. + void add(void *object) { + auto_synchronizer l(_lock); + live_object_info *loi = NIL; + if (!_objects.find(object, loi)) { + // this is a new item. + _objects.add(object, new live_object_info); + return; + } + // this item already exists. + loi->_references++; + } + + // reduces the refcount on the "object" and removes it if there are zero + // references. + void remove(void *object) { + auto_synchronizer l(_lock); + live_object_info *loi = NIL; + if (!_objects.find(object, loi)) { + // this item doesn't exist??? bad usage has occurred.. + return; + } + // patch its reference count. + loi->_references--; + if (!loi->_references) { + // ooh, it croaked. get rid of it now. + _objects.zap(object); + } + } + +private: + mutex _lock; // protects our list. + hash_table _objects; + // the valid objects are listed here. +}; + +////////////// + +safe_callback::safe_callback() +: _decoupled(false), + _callback_lock(new mutex) +{ begin_availability(); } + +safe_callback::~safe_callback() +{ + if (!_decoupled) + non_continuable_error(class_name(), "destructor", + "the derived safe_callback has not called end_availability() yet.\r\n" + "this violates caveat two of safe_callback (see header)."); + WHACK(_callback_lock); +} + +SAFE_STATIC(global_live_objects, safe_callback::_invocables, ) + +void safe_callback::begin_availability() +{ + // we don't lock the mutex here because there'd better not already be + // a call to the callback function that happens while the safe_callback + // object is still being constructed...! + + _decoupled = false; // set to be sure we know we ARE hooked in. + if (_live_objects_are_gone) return; // hosed. + _invocables().add(this); // list this object as valid. +} + +void safe_callback::end_availability() +{ + if (_decoupled) return; // never unhook any one safe_callback object twice. + if (_live_objects_are_gone) { + // nothing to unlist from. + _decoupled = true; + return; + } + _callback_lock->lock(); // protect access to this object. + _invocables().remove(this); // unlist this object. + _decoupled = true; // we are now out of the action. + _callback_lock->unlock(); // release lock again. + // we shoot the lock here so that people hear about it immediately. they + // will then be released from their pending but failed callback invocations. + WHACK(_callback_lock); +} + +bool safe_callback::invoke_callback(callback_data_block &new_data) +{ + auto_synchronizer l(*_callback_lock); + // we now have the lock. + if (!_invocables().listed(this)) return false; + // this object is no longer valid, so we must not touch it. + if (_decoupled) return false; + // object is partially decoupled already somehow. perhaps this instance + // has already been connected but another hook-up exists at some place + // else in the derivation hierarchy. they'd better be careful to shut + // down all the safe_callbacks before proceeding to destroy... + // if they do that, they're fine, since the shutdown of any owned data + // members would be postponed until after all the callbacks had been + // removed. + real_callback(new_data); + return true; +} + +} //namespace. + + + diff --git a/core/library/processes/safe_callback.h b/core/library/processes/safe_callback.h new file mode 100644 index 00000000..9ee33d2a --- /dev/null +++ b/core/library/processes/safe_callback.h @@ -0,0 +1,152 @@ +#ifndef SAFE_CALLBACK_CLASS +#define SAFE_CALLBACK_CLASS + +/*****************************************************************************\ +* * +* Name : safe_callback * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace processes { + +// forward. +class callback_data_block; +class global_live_objects; + +//! A reasonably easy way to make callbacks safe from shutdown ordering issues. +/*! + If an object implementing an unsafe, standard callback is destroyed, then + all of the pending callbacks on other threads are jeopardized. Objects with + unsafe objects will not fare well in a multi-threaded program at all. This + class ensures that no callback can ever occur on a dead object, but this + promise is subject to the caveats below. + + Caveat One: + + if you have synchronization control over objects that you own, please + DO NOT have them locked while your callback base class is being destroyed + NOR when you call the end_availability() method. allowing them to be locked + at those times can result in a program deadlock. ensuring this is a simple + matter of having a properly formed destructor, as in caveat two. + + Caveat Two: + + an object that implements safe_callback MUST invoke the end_availability + method as the FIRST thing in its destructor. otherwise, if some portions + of the object are shutdown before the safe_callback is stopped, then the + active callback invocation can have the rug pulled out from under it and + suddenly be working with bad objects. here is an example of a safe + destructor implementation: @code + + my_safe_callback::~my_safe_callback() { + // safely revoke this object's listing before other destructions. + end_availability(); + // destroy other objects now... + WHACK(first_thing); //...etc.... + } @endcode + + note also: if your object owns or is derived from more than one + safe_callback, then ALL of those must have their end_availability methods + invoked before ANY of the other objects owned can be destroyed. we + recommend against deriving from more than one safe_callback just for + simplicity's sake. +*/ + +class safe_callback +{ +public: + safe_callback(); + //!< construction signifies that the callback is now in operation. + + void end_availability(); + //!< prepares to shut down this object. + /*!< This removes the object from the list of available callbacks. this + MUST be called in a derived destructor BEFORE any other objects owned by + the derived class are destroyed. */ + + virtual ~safe_callback(); + //!< actually destroys the object. + /*!< don't allow this to be called without having invoked + end_availability() at the top of your derived class's destructor. + otherwise, you have broken your code by failing caveat two, above. */ + + DEFINE_CLASS_NAME("safe_callback"); + + bool decoupled() const { return _decoupled; } + //!< if true, then end_availability() was already invoked on the object. + /*!< if this is returning true, then one can trust that the object is + properly deregistered and safely decoupled from the callback. a return of + false means that the object still might be hooked into callbacks and it is + not yet safe to destroy the object. */ + + bool invoke_callback(callback_data_block &new_data); + //!< this function is invoked by a user of the safe_callback derived object. + /*!< this performs some safety checks before invoking the real_callback() + method. given the caveats are adhered to, we automatically ensure that the + object to be called actually exists in a healthy state. we also enforce + that the called object cannot be destroyed until after any active + callback invocation is finished, and that any pending callbacks are + rejected once the object is invalid. + true is returned if the safe callback was actually completed. a false + return indicates that the object had already shut down. perhaps that's + a sign that the caller should now remove the object if it hasn't already + been removed externally... certainly if the call is rejected more than + once, there's no reason to keep invoking the callback. + each safe_callback implements its own locking to ensure that the + object is still alive. thus, derived objects don't need to provide some + separate proof of their health. this also allows the derived object + to mostly ignore callback-related locking in its own synchronization + code (mostly--see caveats above). */ + +protected: + virtual void real_callback(callback_data_block &new_data) = 0; + //!< derived classes implement this to provide their callback functionality. + /*!< this call will be prevented from ever occurring on an invalid "this" + pointer (given the caveats above are adhered to). */ + +private: + bool _decoupled; //!< true if we have ended our availability. + basis::mutex *_callback_lock; //!< synchronizes access to this object. + void begin_availability(); + //!< allows the safe_callback derived object to be called. + /*!< if this was never invoked, then attempts to callback are rejected. */ + +public: + global_live_objects &_invocables(); + //!< provides access to the program-wide list of healthy callback objects. + /*!< this should not be used by anyone external to the safe_callback + implementation. */ +}; + +////////////// + +//! a simple place-holder that anonymizes the type passed to the callback. +/*! + the virtual destructor above ensures that RTTI can be used if necessary; + objects of different class types can be differentiated when passed to the + callback. +*/ +class callback_data_block +{ +public: + virtual ~callback_data_block(); +}; + +////////////// + +} //namespace. + +#endif + diff --git a/core/library/processes/safe_roller.cpp b/core/library/processes/safe_roller.cpp new file mode 100644 index 00000000..85dd8acd --- /dev/null +++ b/core/library/processes/safe_roller.cpp @@ -0,0 +1,74 @@ +/*****************************************************************************\ +* * +* Name : safe_roller * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "safe_roller.h" + +#include +#include +#include +#include + +using namespace basis; +using namespace structures; + +namespace processes { + +SAFE_STATIC(mutex, __roller_synch, ) + +void safe_add(int &to_change, int addition) +{ + auto_synchronizer l(__roller_synch()); + to_change += addition; +} + +////////////// + +safe_roller::safe_roller(int start_of_range, int end_of_range) +: _rolling(new int_roller(start_of_range, end_of_range)), + _lock(new mutex) +{ +} + +safe_roller::~safe_roller() +{ + WHACK(_rolling); + WHACK(_lock); +} + +int safe_roller::next_id() +{ + _lock->lock(); + int to_return = _rolling->next_id(); + _lock->unlock(); + return to_return; +} + +int safe_roller::current() const +{ + _lock->lock(); + int to_return = _rolling->current(); + _lock->unlock(); + return to_return; +} + +void safe_roller::set_current(int new_current) +{ + _lock->lock(); + _rolling->set_current(new_current); + _lock->unlock(); +} + +} //namespace. + + diff --git a/core/library/processes/safe_roller.h b/core/library/processes/safe_roller.h new file mode 100644 index 00000000..6c9a1643 --- /dev/null +++ b/core/library/processes/safe_roller.h @@ -0,0 +1,76 @@ +#ifndef SAFE_ROLLER_CLASS +#define SAFE_ROLLER_CLASS + +/*****************************************************************************\ +* * +* Name : safe_roller * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +namespace processes { + +//! Implements a thread-safe roller object. +/*! + Integers can be generated by this object without concern for corruption by + multiple threads. +*/ + +class safe_roller +{ +public: + safe_roller(int start_of_range = 0, int end_of_range = MAXINT32); + //!< Provides numbers between the start and end in a thread-safe way. + /*!< constructs a roller that runs from the value "start_of_range" and + will stay smaller than "end_of_range" (unless the initial parameters are + screwed up; this class doesn't validate the start and end of range). + when the roller hits the end of range, then its value is reset to the + "start_of_range" again. */ + + ~safe_roller(); + + int next_id(); + //!< returns a unique (per instance of this type) id. + + int current() const; + //!< returns the current id to be used; be careful! + /*!< this value will be the next one returned, so only look at the + current id and don't use it unwisely. this function is useful if you want + to assign an id provisionally but might not want to complete the + issuance of it. */ + + void set_current(int new_current); + //!< allows the current id to be manipulated. + /*!< this must be done with care lest existing ids be re-used. */ + +private: + structures::int_roller *_rolling; //!< the id generator. + basis::mutex *_lock; //!< thread synchronization. + + // forbidden... + safe_roller(const safe_roller &); + safe_roller &operator =(const safe_roller &); +}; + +////////////// + +void safe_add(int &to_change, int addition); + //!< thread-safe integer addition. + /*!< the number passed in "addition" is atomically added to the number + referenced in "to_change". this allows thread-safe manipulation of + a simple number. */ + +} //namespace. + +#endif + diff --git a/core/library/processes/state_machine.cpp b/core/library/processes/state_machine.cpp new file mode 100644 index 00000000..3704bf98 --- /dev/null +++ b/core/library/processes/state_machine.cpp @@ -0,0 +1,505 @@ +/*****************************************************************************\ +* * +* Name : state_machine * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "state_machine.h" + +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace loggers; +using namespace timely; + +namespace processes { + +////////////// + +//#define DEBUG_STATE_MACHINE + // uncomment if you want the debugging version. + +//hmmm: implement logging... +#undef LOG +#ifndef DEBUG_STATE_MACHINE + #define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print) +#else + #define LOG(to_print) {} +#endif + +////////////// + +// checks whether the machine passed in is valid or not. +#define CHECK_VALID(m) \ + if (!m._current) return false; \ + if (!m._last) return false + +// checks whether the current and next states exist or not. +#define CHECK_STATES \ + if (!current) return false; \ + if (!next) return false; \ + int indy = state_index(current); \ + if (negative(indy)) return false; \ + if (negative(state_index(next))) return false + +// moves to a new state and resets the new state's timestamp. +#define MOVE_STATE(m, next, type, trigger) \ + m._last = m._current; \ + m._current = next; \ + m._type = type; \ + m._trig = trigger; \ + m._start->reset() + +// locates a state or returns. +#define FIND_STATE(state) \ + int indy = state_index(state); \ + if (negative(indy)) return + +////////////// + +struct override { int current; int next; int duration; + override(int _current = 0, int _next = 0, int _duration = 0) + : current(_current), next(_next), duration(_duration) {} +}; + +struct transition_info { + enum transition_type { SIMPLE, RANGE, TIMED }; + transition_type type; + int next_state; + int low_trigger, high_trigger; + int time_span; + + transition_info() {} // blank. + transition_info(int next) : type(SIMPLE), next_state(next) {} + transition_info(int next, int time) : type(TIMED), next_state(next), + time_span(time) {} + transition_info(int next, int low, int high) : type(RANGE), + next_state(next), low_trigger(low), high_trigger(high) {} +}; + +struct state_info { + int state_id; // id for this state. + array transitions; + + state_info() : state_id(0) {} // blank. + state_info(int state_id_in) : state_id(state_id_in) {} +}; + +////////////// + +class state_machine_override_array : public array {}; +class state_machine_state_array : public array {}; + +////////////// + +state_machine::state_machine() +: _current(0), + _last(0), + _trig(0), + _type(SIMPLE), + _start(new time_stamp()), + _name(new astring()), + _overrides(new state_machine_override_array) +{} + +state_machine::state_machine(const state_machine &to_copy) +: root_object(), + _current(0), + _last(0), + _trig(0), + _type(SIMPLE), + _start(new time_stamp()), + _name(new astring()), + _overrides(new state_machine_override_array) +{ *this = to_copy; } + +state_machine::~state_machine() +{ + WHACK(_start); + WHACK(_name); + WHACK(_overrides); +} + +int state_machine::update() { return 0; } + +void state_machine::set_name(const astring &name) { *_name = name; } + +astring state_machine::get_name() const { return *_name; } + +state_machine &state_machine::operator = (const state_machine &to_copy) +{ + if (&to_copy == this) return *this; + _current = to_copy._current; + _last = to_copy._last; + _trig = to_copy._trig; + _type = to_copy._type; + *_start = *to_copy._start; + *_name = *to_copy._name; + *_overrides = *to_copy._overrides; +//careful when overrides becomes hidden internal type. + return *this; +} + +int state_machine::duration_index(int current, int next) const +{ + for (int i = 0; i < _overrides->length(); i++) + if ( ((*_overrides)[i].current == current) + && ((*_overrides)[i].next == next) ) + return i; + return common::NOT_FOUND; +} + +void state_machine::set_state(int new_current, int new_last, int trigger, + transition_types type) +{ + _current = new_current; + _last = new_last; + _trig = trigger; + _type = type; + _start->reset(); +} + +void state_machine::override_timing(int current, int next, int duration) +{ + int indy = duration_index(current, next); + if (non_negative(indy)) { + // found an existing record for this current/next pair. + if (!duration) { + // zero duration means removal. + _overrides->zap(indy, indy); + return; + } + // reset the duration. + (*_overrides)[indy].duration = duration; + return; + } + // no existing record, so add one. + *_overrides += override(current, next, duration); +} + +int state_machine::duration_override(int current, int next) const +{ + int indy = duration_index(current, next); + if (negative(indy)) return 0; + return (*_overrides)[indy].duration; +} + +time_stamp state_machine::start() const { return *_start; } + +////////////// + +transition_map::transition_map() +: _valid(false), + _start_state(0), + _state_list(new state_machine_state_array) +{} + +transition_map::~transition_map() +{ + _valid = false; + WHACK(_state_list); +} + +// informational functions: + +int transition_map::states() const { return _state_list->length(); } + +int transition_map::state_index(int state_id) const +{ + for (int i = 0; i < states(); i++) + if ((*_state_list)[i].state_id == state_id) return i; + return common::NOT_FOUND; +} + +int transition_map::transition_index(int state_index, int next, int &start) +{ + bounds_return(state_index, 0, states() - 1, common::BAD_INPUT); + state_info &state = (*_state_list)[state_index]; + bounds_return(start, 0, state.transitions.length() - 1, common::BAD_INPUT); + // loop over the transitions by using our external index. + for (start = start; start < state.transitions.length(); start++) + if (state.transitions[start].next_state == next) { + start++; // position it after this index. + return start - 1; // return this index. + } + return common::NOT_FOUND; +} + +// configurational functions: + +void transition_map::reconfigure() { _valid = false; } + +outcome transition_map::validate(int &examine) +{ + // check for a bad starting state... + examine = _start_state; + FIND_STATE(_start_state) BAD_START; + + if (!check_reachability(examine)) return UNREACHABLE; + // a state is unreachable from the starting state. + if (!check_overlapping(examine)) return OVERLAPPING_RANGES; + // bad (overlapping) ranges were found in one state. + _valid = true; // set us to operational. + return OKAY; +} + +bool transition_map::add_state(int state_number) +{ + if (valid()) return false; // this is operational; no more config! + if (!state_number) return false; // zero is disallowed. + if (non_negative(state_index(state_number))) return false; // already exists. + *_state_list += state_info(state_number); + return true; +} + +bool transition_map::set_start(int starting_state) +{ + if (valid()) return false; // this is operational; no more config! + if (!starting_state) return false; + + FIND_STATE(starting_state) false; // doesn't exist. + + _start_state = starting_state; + return true; +} + +bool transition_map::add_simple_transition(int current, int next) +{ + if (valid()) return false; // this is operational; no more config! + CHECK_STATES; + (*_state_list)[indy].transitions += transition_info(next); + return true; +} + +bool transition_map::add_range_transition(int current, int next, int low, int high) +{ + if (valid()) return false; // this is operational; no more config! + CHECK_STATES; + (*_state_list)[indy].transitions += transition_info(next, low, high); + return true; +} + +bool transition_map::add_timed_transition(int current, int next, int length) +{ + if (valid()) return false; // this is operational; no more config! + CHECK_STATES; + state_info &found = (*_state_list)[indy]; + // locates any existing timed transition and re-uses its slot. + for (int i = 0; i < found.transitions.length(); i++) + if (found.transitions[i].type == transition_info::TIMED) { + found.transitions[i] = transition_info(next, length); + return true; + } + // no existing timed transition found, so add a new one. + (*_state_list)[indy].transitions += transition_info(next, length); + return true; +} + +// operational functions: + +bool transition_map::make_transition(state_machine &m, int next) +{ + if (!valid()) return false; // this is not operational yet! + CHECK_VALID(m); +#ifdef DEBUG_STATE_MACHINE + if (negative(state_index(m._current))) + LOG(astring("(%s) transition_map::make_transition: bad logic error; machine's " + "state is missing.", m._name->s())); + if (negative(state_index(next))) + LOG(astring("(%s) transition_map::make_transition: next state parameter is invalid.", + m._name->s())); +#endif + FIND_STATE(m._current) false; // bad next state. + int start = 0; + if (negative(transition_index(indy, next, start))) return false; + // no transition exists for that next state. + MOVE_STATE(m, next, state_machine::SIMPLE, 0); + int trig = m.update(); + if (trig) return pulse(m, trig); + return true; +} + +bool transition_map::pulse(state_machine &m, int trigger) +{ + if (!valid()) return false; // this is not operational yet! + CHECK_VALID(m); +#ifdef DEBUG_STATE_MACHINE + if (negative(state_index(m._current))) + LOG(astring("(%s) transition_map::pulse: bad logic error; state is missing.", m._name->s())); +#endif + FIND_STATE(m._current) false; // logic error! + state_info &found = (*_state_list)[indy]; + for (int i = 0; i < found.transitions.length(); i++) { + if (found.transitions[i].type == transition_info::RANGE) { + // found the right type of transition. + transition_info &tran = found.transitions[i]; + if ( (tran.low_trigger <= trigger) + && (tran.high_trigger >= trigger) ) { + // found a transition with an acceptable range. + MOVE_STATE(m, tran.next_state, state_machine::RANGE, trigger); + int trig = m.update(); + if (trig) return pulse(m, trig); + return true; + } + } + } + return false; +} + +bool transition_map::time_slice(state_machine &m) +{ + if (!valid()) return false; // this is not operational yet! + CHECK_VALID(m); + +#ifdef DEBUG_STATE_MACHINE + if (negative(state_index(m._current))) + LOG(astring("(%s) transition_map::time_slice: bad logic error; state " + "is missing.", m._name->s())); +#endif + FIND_STATE(m._current) false; // logic error! + + state_info &found = (*_state_list)[indy]; + for (int i = 0; i < found.transitions.length(); i++) { + if (found.transitions[i].type == transition_info::TIMED) { + // found the right type of transition. + transition_info &tran = found.transitions[i]; + int duration = tran.time_span; + int override = m.duration_override(m._current, tran.next_state); + if (override) duration = override; + if (*m._start < time_stamp(-duration)) { + // found a transition with an expired time. + MOVE_STATE(m, tran.next_state, state_machine::TIMED, 0); + int trig = m.update(); + if (trig) return pulse(m, trig); + return true; + } + } + } + return false; +} + +bool transition_map::reset(state_machine &m) +{ + if (!valid()) return false; // this is not operational yet! + m._current = _start_state; + m._last = _start_state; + m._trig = 0; + m._type = state_machine::SIMPLE; + m._start->reset(); + return true; +} + +bool transition_map::check_overlapping(int &examine) +{ + FUNCDEF("check_overlapping"); + examine = 0; + for (int i = 0; i < states(); i++) { + examine = i; + for (int j = 0; j < (*_state_list)[i].transitions.length(); j++) { + transition_info &a = (*_state_list)[i].transitions[j]; + if (a.type != transition_info::RANGE) continue; + for (int k = j + 1; k < (*_state_list)[i].transitions.length(); k++) { + transition_info &b = (*_state_list)[i].transitions[k]; + if (b.type != transition_info::RANGE) continue; + if ( (b.low_trigger <= a.low_trigger) + && (b.high_trigger >= a.low_trigger) ) { + LOG(astring("intersecting range on low end!")); + return false; + } + if ( (b.low_trigger <= a.high_trigger) + && (b.high_trigger >= a.high_trigger) ) { + LOG(astring("intersecting range on high end!")); + return false; + } + } + } + } + return true; +} + +bool transition_map::check_reachability(int &examine) +{ + FUNCDEF("check_reachability"); + examine = 0; + + // list_to_add: the list of states that are definitely reachable and that + // need to be considered. + int_array list_to_add; + list_to_add += _start_state; + + // already_checked: those states that have already been completely considered + // as to their reachability. + array already_checked(states()); + for (int i = 0; i < already_checked.length(); i++) + already_checked[i] = false; + + while (list_to_add.length()) { + // the next state that IS reachable has all of the states reachable from + // it added to the list of states that are to be checked... + examine = list_to_add[0]; + int indy = state_index(examine); + if (negative(indy)) { + LOG(a_sprintf("bad state index for state %d!", examine)); + return false; + } +#ifdef DEBUG_STATE_MACHINE + LOG(a_sprintf("state to add is %d, list size=%d.", examine, + list_to_add.length())); +#endif + // delete the item that we are now checking. + list_to_add.zap(0, 0); + + // check whether this item has already had its kids (those states reachable + // from it) added to the list to add. if so, skip it. + if (already_checked[indy]) continue; + + // update the information for this state we are considering in the list of + // already checked states. + already_checked[indy] = true; + + state_info &found = (*_state_list)[indy]; + + // all states this one can reach are added to the list to check. + for (int i = 0; i < found.transitions.length(); i++) { +#ifdef DEBUG_STATE_MACHINE + LOG(astring("checking transition %d: ", i)); +#endif + int now_reachable = found.transitions[i].next_state; +#ifdef DEBUG_STATE_MACHINE + LOG(astring("now reaching %d.", now_reachable)); +#endif + if (now_reachable == examine) continue; + else { + int indy = state_index(now_reachable); + if (already_checked[indy]) continue; + } + list_to_add += now_reachable; + } + } +#ifdef DEBUG_STATE_MACHINE + LOG("done checking reachability."); +#endif + for (int j = 0; j < already_checked.length(); j++) + if (!already_checked.get(j)) { + examine = (*_state_list)[j].state_id; + LOG(a_sprintf("state %d is not reachable", examine)); + return false; + } + return true; // all states are reachable. +} + +} //namespace. + diff --git a/core/library/processes/state_machine.h b/core/library/processes/state_machine.h new file mode 100644 index 00000000..3d2761e8 --- /dev/null +++ b/core/library/processes/state_machine.h @@ -0,0 +1,348 @@ +#ifndef STATE_MACHINE_CLASS +#define STATE_MACHINE_CLASS + +/*****************************************************************************\ +* * +* Name : state_machine * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +namespace processes { + +class state_machine_override_array; +class state_machine_state_array; + +//! Monitors objects with multiple states and the transitions between states. +/*! + A state machine is a computational abstraction for any control process + that has discrete states and transitions between the states. + As used here, the "state_machine" is a base class for objects that can be + manipulated by the "transition_map" class. Your transition map must be + validated and you must use it to reset() your state_machine object before + any activity can occur. (One aspect of validation is that all states must be + reachable from the user-specified starting state. The other requirements + are documented below for transition_map.) Once validation is done, the + transition map manages the machine through the various states that are + defined and applies trigger values to modify the current state when asked + to do so. It also applies any defined timed transitions automatically. + This implementation of state machines is configured once (by defining the + transition_map object), but then the machines can be run repeatedly (via + the state_machine object). +*/ + +class state_machine : public virtual basis::root_object +{ +public: + state_machine(); + //!< sets up the machine in a blank state. + + state_machine(const state_machine &to_copy); + //!< copies the machine provided in "to_copy". + + virtual ~state_machine(); + + DEFINE_CLASS_NAME("state_machine"); + + state_machine &operator =(const state_machine &to_copy); + //!< assigns this to a copy of the machine provided in "to_copy". + + virtual int update(); + //!< this is the main implementation function provided by derived classes. + /*!< this is implemented by derived classes that want to perform an action + when the state machine is pulsed (see below in transition_map), which is + why it is not pure virtual; a state machine might still be useful as a + state tracking object rather than needing to implement extended + functionality. this function is invoked whenever the state changes due to + a timed, range based or simple transition. one must be careful when + servicing this transition not to enmesh oneself in a snarled invocation + situation; if make_transition or time_slice or pulse are invoked from this + update function and conditions are right for another transition, then the + update function will be re-entered recursively. if the value returned + from update is non-zero, it is used to pulse the state machine again as a + new trigger value (this is safer than explicitly calling one of the + transition causing functions). note that this assumes that zero is an + invalid trigger. if your triggers include zero as a valid value, don't + try returning it through update; use pulse to force the triggering to + accept the invalid trigger instead. */ + + int current() const { return _current; } + //!< returns the current state. + /*!< NOTE: a zero value for a state means that it is uninitialized. */ + + int last() const { return _last; } + //!< returns the last active state. + + int trigger() const { return _trig; } + //!< returns the trigger that caused this state. + /*!< this is only meaningful when the transition was caused by a range + transition. if it was, then ranged() will return true. */ + + enum transition_types { SIMPLE, RANGE, TIMED }; + //!< the three types of transitions supported. + + bool simple() const { return _type == SIMPLE; } + //!< returns true if the last transition was a simple type. + bool ranged() const { return _type == RANGE; } + //!< returns true if the last transition was a ranged type. + bool timed() const { return _type == TIMED; } + //!< returns true if the last transition was a timed type. + + void set_state(int new_current, int new_last, int trigger, + transition_types type); + //!< sets the current state to "new_current" and the previous state to "new_last". + /*!< the "trigger" causing the transition, if any, will be stored also. + the "type" of transition is stored also. the time stamp for time spent in + the current state is reset. be very careful with this; if the two states + do not conform to the legal transitions in your map, then the state + machine will not behave properly. */ + + timely::time_stamp start() const; + //!< start time for the current state. + /*!< this records how long we've been in this state. */ + + void set_name(const basis::astring &name); + //!< sets the name to be printed in any debugging information for "this". + + basis::astring get_name() const; + //!< retrieves the object's current name. + + void override_timing(int current, int next, int duration); + //!< modifies the recorded timing for timed transitions. + /*!< this overrides the transition_map's time-out value for the transition + between the states "current" and "next". a time-out of "duration" + will be used instead of whatever was specified when the map was set + up. to erase an override, use zero as the "duration". */ + + int duration_override(int current, int next) const; + //!< reports if there's a duration override for a timed transition. + /*!< this returns the amount of time that this particular state_machine is + allowed to exist in the "current" state before progressing to the "next" + state. this has nothing to do with the transition_map; it is valid only + for this state_machine object. zero is returned if no override exists. */ + +private: + friend class transition_map; + //!< manager buddy object. + int _current; //!< the current state of the state machine. + int _last; //!< the previous state for the state machine. + int _trig; //!< the last trigger value, if _ranged is true. + transition_types _type; //!< what kind of transition just occurred. + timely::time_stamp *_start; //!< the time this state started. + basis::astring *_name; //!< the name this state_machine reports itself as. + state_machine_override_array *_overrides; //!< the timing overrides. + + int duration_index(int current, int next) const; + //!< finds the index of a duration override in our list. + /*!< this locates the duration override specified for the transition + between "current" and "next". it returns the index of that override, or + a negative number if the override is not found. */ +}; + +////////////// + +//! The transition_map manages state machines and causes state changes to occur. +/*! + The transition_map is a heavyweight class that is a repository for all + information about transitions and which manages pushing the state machines + through the proper states. + + The transition_map guarantees these characteristics: + + 0) the below characteristics are checked (in validate) and no state + machine object is allowed to operate until they are satisfied, + + 1) the machine starts in the specified starting state, + + 2) the current state is always one that has been added and approved, + + 3) transitions are allowed between states only if the transition has + been added and approved, + + 4) the update() function is invoked every time the machine hits a + transition between states (even if it is a transition to the same + state), + + 5) range transitions are non-intersecting within one state, +*** 5 is unimplemented *** + + 6) all states are reachable from the starting state by some valid + transition, and + + 7) each state has no more than one timed transition. + + if any of these conditions are violated, then validate() will fail. + the machine will also not operate properly (at all) until the conditions + are satisfied by validate(). the states and transitions should + thus be carefully examined before turning them into a state machine.... +*/ + +class transition_map : public virtual basis::root_object +{ +public: + transition_map(); + virtual ~transition_map(); + + // informational functions... + + DEFINE_CLASS_NAME("transition_map"); + + bool valid() const { return _valid; } + //!< returns true if the transition_map is valid and ready for operation. + /*!< once the validate() call has succeeded and valid() is true, no more + configuration functions (see below) may be called until the reconfigure() + function is invoked. */ + + int states() const; + //!< returns the current number of states. + + // validation functions... + + enum outcomes { + OKAY = basis::common::OKAY, + DEFINE_OUTCOME(BAD_START, -49, "The start state has not been properly " + "specified"), + DEFINE_OUTCOME(OVERLAPPING_RANGES, -50, "The ranges overlap for two " + "transitions from a state"), + DEFINE_OUTCOME(UNREACHABLE, -51, "There is an unreachable state in the map") + }; + basis::outcome validate(int &examine); + //!< checks to that all required transition conditions are satisfied. + /*!< OKAY is returned if they are and the map is then ready to operate. + other outcomes are returned if one or more of the conditions are not met: + BAD_START means that the starting state is badly specified. + OVERLAPPING_RANGES means that one state has two transitions that do not + have mutually exclusive ranges. UNREACHABLE means that a state is not + reachable from the starting state. for all of these cases, the "examine" + parameter is set to a state related to the problem. */ + + void reconfigure(); + //!< puts the transition_map back into an unvalidated state. + /*!< this allows the characteristics of the map to be changed. validate() + must be called again before resuming operation using this map. */ + + // configuration functions... + + // NOTE: all of the functions below will fail if the transition_map has + // already been validated previously and reconfigure() has not been called. + + // NOTE: a zero value for a state means that it is uninitialized. thus, zero + // is never allowed as a value for a state or a transition endpoint. zero is + // grudgingly allowed as a trigger value, although that interferes with the + // state_machine object's update() method. + + bool add_state(int state_number); + //!< registers a legal state in the transition_map. + /*!< no action will be taken for any state until it is registered. the + operation returns true for successful registration and false for errors + (such as when a state is already registered or is invalid). */ + + bool set_start(int starting_state); + //!< assigns a state as the first state. + /*!< if the "starting_state" does not already exist, it is an error and + false is returned. */ + + bool add_simple_transition(int current, int next); + //!< adds a transition between "current" and "next". + /*!< it is an error to use either an invalid "current" state or an invalid + "next" state. these errors are detected during the validate() function. + this type of transition is unspecialized--it does not respond to triggers + and does not occur due to timing. to make a state_machine undergo this + transition, make_transition() must be used. */ + + bool add_range_transition(int current, int next, int low, int high); + //!< adds a transition that listens to triggers in the pulse() method. + /*!< this uses "low" and "high" as the inclusive range of triggers that + will cause a transition to the "next" state when a state_machine is pulsed + in the "current" state. it is erroneous for these trigger values to + intersect with other values in the same "current" state. */ + bool add_timed_transition(int current, int next, int duration); + //!< adds a transition that occurs after a timeout with no other activity. + /*!< adds a timed transition to the "next" state when the "current" state + has lasted "duration" milliseconds. this relies on the time_slice function + being called periodically, with appropriate regularity for the specified + "duration" (e.g., if one's time-out is 100 ms, then one might want to call + time_slice() every 20 ms or so to ensure that the transition is at most 20 + ms late in firing. NOTE: this is not an argument for calling time_slice() + as fast as possible; it is an argument for realizing that the "duration" + must be carefully considered to meet one's timing deadlines). */ + + // transition functions... + + // NOTE: all of these actions will fail if the transition_map has not been + // validated yet. + + bool make_transition(state_machine &m, int next); + //!< changes the state to the "next" listed for "m" given the current state. + /*!< it is an error to make a transition that has not been specified + through an add_X() transition function (false is returned). if the + transition succeeds, then the current_state will be "next". */ + + bool pulse(state_machine &m, int trigger); + //!< applies a "trigger" to possibly cause a range transition. + /*!< this causes the state_machine to accept the "trigger" as input and + perform at least one traversal of the transition_map. if the trigger + value is found in one of the range transitions for the current state, then + the next state specified in that transition becomes the current state and + the update() function is invoked (and true is returned). if the trigger + is not found in any range transition, then false is returned. */ + + bool time_slice(state_machine &m); + // allows the transition_map to process any timed transitions that may be + // required for the state_machine "m". the return value is true if such + // a transition was found. + + bool reset(state_machine &m); + // resets the state_machine to the starting state in the transition_map. + // the update function is NOT invoked at the time of the reset. true is + // returned if the reset was successful; it would fail if the transition + // map has not been validated yet. + +private: + bool _valid; //!< records whether we've been validated or not. + int _start_state; //!< remembers the default starting state. + state_machine_state_array *_state_list; + //!< the list of transitions between states. + + bool check_overlapping(int &examine); + //!< a validation function that looks for erroneous range transitions. + /*!< this returns true if there are no overlapping ranges found in the + range transitions for each state. */ + + bool check_reachability(int &examine); + //!< returns true if all states are reachable from the starting state. + + int state_index(int state_id) const; + //!< returns the index of "state_id" in states, if it exists. + + int transition_index(int state_index, int next, int &start); + //!< locates a transition into "next" for a state in our list. + /*!< returns the index of a transition between the state at "state_index" + and the "next" state by starting the search at index "start". if there + is no matching transition from the "start" index on in the transition + list, then a negative number is returned. "start" is updated to point + to the index just after a found transition, or to some random number if + the index is not found. */ + + bool check_states(); + //!< returns false if the current machine is hosed up. + + // disallowed functions for now: + transition_map(const transition_map &); + transition_map &operator =(const transition_map &); +}; + +} //namespace. + +#endif + diff --git a/core/library/processes/thread_cabinet.cpp b/core/library/processes/thread_cabinet.cpp new file mode 100644 index 00000000..19bc679f --- /dev/null +++ b/core/library/processes/thread_cabinet.cpp @@ -0,0 +1,231 @@ +/*****************************************************************************\ +* * +* Name : thread_cabinet * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "ethread.h" +#include "thread_cabinet.h" + +#include +#include +#include +#include + +#undef LOCKIT +#define LOCKIT auto_synchronizer l(*_lock) + // grabs the mutex for access to the list. + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +//#define DEBUG_THREAD_CABINET + // uncomment for noisier version. + +using namespace basis; +using namespace loggers; +using namespace structures; +using namespace timely; + +namespace processes { + +class thread_record +{ +public: + ethread *_thread; + unique_int _id; + + thread_record(const unique_int &id, ethread *t) + : _thread(t), _id(id) {} + + ~thread_record() { + _thread->stop(); + WHACK(_thread); + } +}; + +////////////// + +class thread_amorph : public amorph +{ +public: +}; + +////////////// + +thread_cabinet::thread_cabinet() +: _lock(new mutex), + _threads(new thread_amorph), + _next_id(new int_roller(1, MAXINT32 - 1)), + _no_additions(0) +{ +} + +thread_cabinet::~thread_cabinet() +{ + WHACK(_threads); + WHACK(_lock); + WHACK(_next_id); +} + +int thread_cabinet::threads() const { return _threads->elements(); } + +unique_int thread_cabinet::add_thread(ethread *to_add, bool start_it, + void *parm) +{ +#ifdef DEBUG_THREAD_CABINET + FUNCDEF("add_thread"); +#endif + LOCKIT; + if (_no_additions) { +#ifdef DEBUG_THREAD_CABINET + LOG("no additions flag is true; destroying the thread and failing out."); +#endif + // can't just leave it unhooked hanging out in space... + WHACK(to_add); + return 0; + } + int use_id = _next_id->next_id(); + if (start_it) { + to_add->start(parm); + } else { +#ifdef DEBUG_THREAD_CABINET + if (to_add->thread_finished()) + LOG(a_sprintf("thread %x is not going to be started and it " + "hasn't started yet!", to_add)); +#endif + } + _threads->append(new thread_record(use_id, to_add)); + return use_id; +} + +bool thread_cabinet::any_running() const +{ + LOCKIT; + for (int i = 0; i < _threads->elements(); i++) { + if (_threads->borrow(i)->_thread->thread_started()) return true; + } + return false; +} + +void thread_cabinet::start_all(void *ptr) +{ + LOCKIT; + for (int i = 0; i < _threads->elements(); i++) { + if (_threads->borrow(i)->_thread->thread_finished()) { + _threads->borrow(i)->_thread->start(ptr); + } + } +} + +void thread_cabinet::cancel_all() +{ + FUNCDEF("cancel_all"); + { + LOCKIT; // short lock. + _no_additions++; // keep people from adding new threads. + for (int i = 0; i < _threads->elements(); i++) { + _threads->borrow(i)->_thread->cancel(); + } + } + LOCKIT; + _no_additions--; // allow new threads again. + if (_no_additions < 0) + continuable_error(class_name(), func, "error in logic regarding " + "no additions."); +} + +void thread_cabinet::stop_all() +{ + FUNCDEF("stop_all"); + { + LOCKIT; // short lock. + _no_additions++; // keep people from adding new threads. + } + cancel_all(); // signal all threads to leave. + // pause to give them a little while to leave. + time_control::sleep_ms(20); + while (true) { + LOCKIT; // short lock. + if (!_threads->elements()) break; // done; nothing left. + clean_debris(); // remove any that did stop. + time_control::sleep_ms(20); // snooze for a short while. + } + LOCKIT; + _no_additions--; // allow new threads again. + if (_no_additions < 0) + continuable_error(class_name(), func, "error in logic regarding " + "no additions."); +} + +bool thread_cabinet::zap_thread(const unique_int &to_whack) +{ + LOCKIT; + for (int i = 0; i < _threads->elements(); i++) { + if (_threads->borrow(i)->_id == to_whack) { + // this is the one they want zapped. + _threads->zap(i, i); + return true; + } + } + return false; +} + +bool thread_cabinet::cancel_thread(const unique_int &to_cancel) +{ + LOCKIT; + for (int i = 0; i < _threads->elements(); i++) { + if (_threads->borrow(i)->_id == to_cancel) { + // this is the one to signal regarding its own demise. + _threads->borrow(i)->_thread->cancel(); + return true; + } + } + return false; +} + +void thread_cabinet::clean_debris() +{ +#ifdef DEBUG_THREAD_CABINET + FUNCDEF("clean_debris"); +#endif + LOCKIT; + for (int i = 0; i < _threads->elements(); i++) { + if (_threads->borrow(i)->_thread->thread_finished()) { + // this one's no longer doing anything. +#ifdef DEBUG_THREAD_CABINET + LOG(a_sprintf("clearing thread %x out.", _threads->borrow(i)->_thread)); +#endif + _threads->zap(i, i); + i--; // skip back before the whack. + } + } +} + +int_set thread_cabinet::thread_ids() const +{ + LOCKIT; + int_set to_return; + for (int i = 0; i < _threads->elements(); i++) + to_return += _threads->borrow(i)->_id.raw_id(); + return to_return; +} + +ethread *thread_cabinet::get_thread(int index) +{ + LOCKIT; + thread_record *rec = _threads->borrow(index); + if (rec) return rec->_thread; + return NIL; +} + +} //namespace. + diff --git a/core/library/processes/thread_cabinet.h b/core/library/processes/thread_cabinet.h new file mode 100644 index 00000000..f8e94e73 --- /dev/null +++ b/core/library/processes/thread_cabinet.h @@ -0,0 +1,104 @@ +#ifndef THREAD_CABINET_CLASS +#define THREAD_CABINET_CLASS + +/*****************************************************************************\ +* * +* Name : thread_cabinet * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +// forward. +#include "ethread.h" + +#include +#include +#include +#include + +namespace processes { + +class thread_amorph; + +//! Manages a collection of threads. +/*! + The thread cabinet allows one to corral a bunch of threads in one place + and treat them as a group if necessary. +*/ + +class thread_cabinet +{ +public: + thread_cabinet(); + virtual ~thread_cabinet(); + + DEFINE_CLASS_NAME("thread_cabinet"); + + structures::unique_int add_thread(ethread *to_add, bool start_it, void *parm); + //!< adds a thread to be managed by the thread_cabinet. + /*!< the thread cabinet takes over responsibility for the thread "to_add". + if "start_it" is true, then the thread is started. otherwise it is + left in whatever state it was in. the "parm" is passed to the thread's + start() method. */ + + bool zap_thread(const structures::unique_int &to_whack); + //!< removes the thread with the id "to_whack". + /*!< if it's found and stopped and removed, true is returned. note that + if the thread is found, then this will wait until the thread exits before + whacking it. */ + + bool cancel_thread(const structures::unique_int &to_cancel); + //!< shuts down the thread "to_cancel" as quickly as possible. + /*!< this calls the cancel() method on the thread "to_cancel", which tells + the thread to stop as soon as possible. once it has stopped, the + clean_debris() method will throw it and other stopped threads out. */ + + int threads() const; //!< number of threads being managed here. + + structures::int_set thread_ids() const; + //!< returns the identifiers of all threads managed by this object. + + bool any_running() const; + //!< returns true if any threads are currently running. + + void start_all(void *pointer); + //!< cranks up any threads that are not already running. + /*!< the "pointer" will be provided to any threads that are started. */ + + void cancel_all(); + //!< signals to all threads that they should exit as soon as possible. + /*!< this does not actually wait for them to exit. */ + + void stop_all(); + //!< makes all of the threads quit. + /*!< they are cleaned up after they have stopped running also. any + attempts to add threads while this method is operating will be rejected. */ + + ethread *get_thread(int index); + //!< this returns the thread at "index" in our list. + /*!< note that this is not safe to use if other threads could be removing + threads from the cabinet or calling clean_debris(). */ + + void clean_debris(); + //!< clean out threads that have finished. + /*!< note that if threads were added to the list without starting them, + then these get cleaned out also. */ + +private: + basis::mutex *_lock; //!< synchronizes our thread list. + thread_amorph *_threads; //!< the list of threads we're managing. + structures::int_roller *_next_id; //!< the id to be assigned to the next thread. + int _no_additions; //!< true if we're not allowed to add threads currently. +}; + +} //namespace. + +#endif + diff --git a/core/library/structures/amorph.h b/core/library/structures/amorph.h new file mode 100644 index 00000000..d2104c70 --- /dev/null +++ b/core/library/structures/amorph.h @@ -0,0 +1,520 @@ +#ifndef AMORPH_CLASS +#define AMORPH_CLASS + +/*****************************************************************************\ +* * +* Name : amorph * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1989-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "object_packers.h" + +#include +#include +#include +#include + +//! A dynamic container class that holds any kind of object via pointers. +/*! + The object is considered owned by the amorph unless re-acquired from it, + and will be deleted when the amorph is destroyed. + + An amorph has a specified number of fields at any one time, but the number + can be changed with "zap", "insert" and "adjust". Fields in the amorph + are either full or empty, where an empty field in the amorph has NIL as + its content. "put" adds a new field to the amorph. "get" retrieves the + contents of a field as a constant. "acquire" is used to check a field + out of the amorph, meaning that the amorph no longer possesses that field. + The legal range of indices in an amorph is from 0 through + "elements() - 1". In general, a range violation for an index is + treated as an invalid request and is ignored (although BAD_INDEX is + returned by functions with compatible return values). + + Model: + + The policy followed in amorph is that once an object is checked in with + "put" or "append", then the amorph owns that object. The object must not + be destroyed externally, because the amorph will automatically destroy + the object when either: 1) the amorph itself is destroyed, or 2) another + object is entered into the same field. If the stored object must be + destroyed externally, then it should be checked out of the amorph with + "acquire"; after that, the pointer may be deleted. "get" and "borrow" + return a pointer to the stored item while leaving it checked in to the + amorph. it is safe to modify what was "borrow"ed or to look at what one + "get"s, but do not destroy the pointers returned from either method. +*/ + +namespace structures { + +template +class amorph : private basis::array +{ +public: + amorph(int elements = 0); + //!< constructs an amorph capable of holding "elements" pointers. + + ~amorph(); + + int elements() const { return this->length(); } + //!< the maximum number of elements currently allowed in this amorph. + + int valid_fields() const { return _fields_used; } + //!< Returns the number of fields that have non-NIL contents. + /*!< This might be different from the number of total elements. */ + + void adjust(int new_max); + //!< Changes the maximum number of elements for this amorph. + /*!< If the new number is smaller than the original, then the fields at + index "new_maximum" and upwards are thrown away. existing fields are + kept. */ + + void reset(int new_maximum = 0); + //!< like adjust but doesn't keep existing contents. + + basis::outcome put(int field, const contents *data); + //!< Enters an object into the field at index "field" in the amorph. + /*!< If "data" is NIL, then the field is cleared. The amorph considers the + pointer "data" to be its own property after put is invoked; "data" + should not be destructed since the amorph will automatically do so. + This restriction does not hold if the object is checked back out of + the amorph with acquire(). */ + + basis::outcome append(const contents *data); + //!< puts "data" on the end of this amorph. + /*!< adds an element to the end of the amorph by increasing the amorph size + (with "adjust") and putting the element into the new spot (with "put"). */ + + basis::outcome operator += (const contents *data) { return append(data); } + //!< a synonym for append. + + //! Returns a constant pointer to the information at the index "field". + /*! If no information is stored or the field is out range, then NIL is + returned. */ + const contents *get(int field) const; + //! Returns a pointer to the information at the index "field". + /*! Also returns NIL for invalid indexes. DO NOT destroy the returned + pointer; it is still owned by the amorph. */ + contents *borrow(int field); + + //! synonym for get. + const contents *operator [] (int field) const { return get(field); } + //! synonym for borrow. + contents *operator [] (int field) { return borrow(field); } + + contents *acquire(int field); + //!< Retrieves a "field" from the amorph, taking responsibility for it back. + /*!< This function is similar to get, except that the contents are pulled + out of the amorph. The contents will no longer be destroyed when the + amorph is destroyed. To store the modified contents again, use put, + and then the amorph will take over management of the contents again. + Note that the index is not zapped with this function; the acquired + "field" will simply hold a null pointer. */ + + basis::outcome clear(int field); + //!< Clears the contents of the field specified. + /*!< Clearing an empty field has no effect. Clearing an invalid field has + no effect. NOTE: clearing the contents means that the contents are + destroyed, not just disconnected. */ + + void clear_all(); + //!< Clears every field in the amorph. + + basis::outcome zap(int start, int end); + //!< Removes a range of indices from the amorph. + /*!< This does not just clear the field associated with the specified index + as "clear" does, it actually changes the number of total elements by + removing the indices from the amorph. The new amorph contains the old + elements up to just before the "start" and from the "end" + 1 through the + end of the amorph. AMORPH_BAD_INDEX is returned if either index is out of + range. If the zap succeeds, then AMORPH_OKAY is returned, even if the + "end" is less than the "start". */ + + basis::outcome insert(int position, int lines_to_add); + //!< Adds "lines_to_add" indices to the amorph at the index "position". + /*!< If "lines_to_add" is non-positive, the request is ignored. Inserting + at a position beyond the bounds of the array is ignored, but a position AT + elements() is allowed (it is an append...). */ + + int find_empty(basis::outcome &o) const; + //!< Returns the index of a free field if there are any. + /*!< The returned index is invalid if the "o" is IS_FULL. */ + + const contents *next_valid(int &field) const; + //!< Returns the contents of the next valid element at or after "field". + /*!< "field" is set to the location where an entry was found, if one was + actually found. If none exists at "field" or after it, then NIL is + returned. */ + + int find(const contents *to_locate, basis::outcome &o); + //!< Searches the amorph for the contents specified. + /*!< Since only pointers to the contents are maintained, the search is + based on finding a pointer in the amorph that is identical to "to_locate". + if "o" is OKAY, then the index of the entry is returned. If + "o" is NOT_FOUND, then the contents are not present. */ + + void swap_contents(amorph &other); + //!< Exchanges the contents of "this" and "other". + /*!< No validation is performed but this should always succeed given + amorphs that are constructed properly. */ + +private: + int _fields_used; //!< The number of fields currently full of info. + + void check_fields(const char *where) const; + //!< Crashes out if the field count is wrong. + /*!< This is only used for the debugging version. */ + + void set_nil(int start, int end); + // Puts NIL in the indices between start and end. + /*!< Does not delete whatever used to reside there; it just sets the + pointers to NIL. */ + + // not to be used: amorphs should not be copied because it is intended that + // they support storing heavyweight objects that either have no copy + // constructors or have high-cost copy constructors. + amorph(const amorph &to_copy) {} //!< not to be used. + amorph &operator = (const amorph &to_copy) { return *this; } + //!< not to be used. +}; + +////////////// + +// these extensions are phrased as macros to avoid including the headers +// necessary for compiling them. to use them, just put the macro name in +// the file where the template is needed. + +////////////// + +//! This can be used when the templated object has a copy constructor. +/*! this makes the "to_assign" into a copy of the "to_copy" amorph. */ +template +void amorph_assign(amorph &to_assign, + const amorph &to_copy); + +////////////// + +//! support for packing an amorph into an array of bytes. +/*! + this can be used when the underlying object is based on packable or + supports the same pack/unpack methods. note that if there are empty spots in + the amorph, they are not packed. when the packed form is unpacked again, it will + have only the first N elements set, where N is the number of non-empty items. +*/ +template +void amorph_pack(basis::byte_array &packed_form, const amorph &to_pack); + +//! unpacks the amorph from an array of bytes. +template +bool amorph_unpack(basis::byte_array &packed_form, amorph &to_unpack); + +//! reports how large the packed form will be. +template +int amorph_packed_size(const amorph &to_pack); + +// implementation for longer methods... + +//#define DEBUG_AMORPH + // uncomment to enable more testing, as well as complaints on errors. + +#undef static_class_name +#define static_class_name() "amorph" + // used in bounds_halt macro. + +#undef AMO_ALERT +#ifdef DEBUG_AMORPH + #include + #define AMO_ALERT(a, b, c) basis::throw_error(basis::astring(a), basis::astring(func), basis::astring(b) + " -- " + c) + #define CHECK_FIELDS check_fields(func) +#else + #define AMO_ALERT(a1, a2, a3) {} + #define CHECK_FIELDS { if (!func) {} } +#endif + +////////////// + +template +amorph::amorph(int elements) +: basis::array(elements, NIL, basis::array::SIMPLE_COPY + | basis::array::EXPONE | basis::array::FLUSH_INVISIBLE), + _fields_used(0) +{ + FUNCDEF("constructor"); + set_nil(0, elements - 1); + CHECK_FIELDS; +} + +template +amorph::~amorph() +{ + FUNCDEF("destructor"); + CHECK_FIELDS; + clear_all(); +} + +template +void amorph::set_nil(int start, int end) +{ + for (int i = start; i <= end; i++) + basis::array::put(i, (contents *)NIL); +} + +template +void amorph::check_fields(const char *where) const +{ + FUNCDEF("check_fields"); + int counter = 0; + for (int i = 0; i < elements(); i++) + if (basis::array::get(i)) counter++; + if (_fields_used != counter) { + AMO_ALERT("amorph", basis::a_sprintf("check_fields for %s", where), + "error in _fields_used count"); + } +} + +template +void amorph::reset(int new_maximum) +{ + FUNCDEF("reset"); + CHECK_FIELDS; + adjust(new_maximum); + clear_all(); +} + +template +void amorph::clear_all() +{ + FUNCDEF("clear_all"); + CHECK_FIELDS; + for (int i = 0; i < elements(); i++) clear(i); +} + +template +basis::outcome amorph::append(const contents *data) +{ + FUNCDEF("append"); + CHECK_FIELDS; + adjust(elements() + 1); + return put(elements() - 1, (contents *)data); +} + +template +const contents *amorph::get(int field) const +{ + FUNCDEF("get"); + CHECK_FIELDS; + bounds_return(field, 0, elements() - 1, NIL); + return basis::array::observe()[field]; +} + +template +void amorph::adjust(int new_maximum) +{ + FUNCDEF("adjust"); + CHECK_FIELDS; + if (new_maximum < 0) return; // bad input here. + int old_max = elements(); + if (new_maximum == old_max) return; // nothing to do. + if (new_maximum < old_max) { + // removes the elements beyond the new size of the amorph. + zap(new_maximum, old_max - 1); + // we're done tuning it. + return; + } + + // we get to here if we need more space than we used to. + int new_fields = new_maximum - old_max; + + basis::array::insert(old_max, new_fields); + for (int i = old_max; i < new_maximum; i++) { + basis::array::put(i, NIL); + } +} + +template +basis::outcome amorph::insert(int position, int lines_to_add) +{ + FUNCDEF("insert"); + CHECK_FIELDS; + bounds_return(position, 0, elements(), basis::common::OUT_OF_RANGE); + basis::outcome o = basis::array::insert(position, lines_to_add); + if (o != basis::common::OKAY) return basis::common::OUT_OF_RANGE; + set_nil(position, position + lines_to_add - 1); + return basis::common::OKAY; +} + +template +basis::outcome amorph::zap(int start_index, int end_index) +{ + FUNCDEF("zap"); + CHECK_FIELDS; + bounds_return(start_index, 0, elements() - 1, basis::common::OUT_OF_RANGE); + bounds_return(end_index, 0, elements() - 1, basis::common::OUT_OF_RANGE); + if (end_index < start_index) return basis::common::OKAY; + for (int i = start_index; i <= end_index; i++) clear(i); + basis::outcome o = basis::array::zap(start_index, end_index); + return (o == basis::common::OKAY? basis::common::OKAY : basis::common::OUT_OF_RANGE); +} + +template +basis::outcome amorph::put(int field, const contents *data) +{ + FUNCDEF("put"); + CHECK_FIELDS; + bounds_return(field, 0, elements() - 1, basis::common::OUT_OF_RANGE); + contents *to_whack = acquire(field); + WHACK(to_whack); + if (data) { + basis::array::access()[field] = (contents *)data; + _fields_used++; + } + return basis::common::OKAY; +} + +template +int amorph::find_empty(basis::outcome &o) const +{ + FUNCDEF("find_empty"); + CHECK_FIELDS; + if (_fields_used == elements()) { o = basis::common::IS_FULL; return 0; } + for (int i = 0; i < elements(); i++) + if (!basis::array::get(i)) { o = basis::common::OKAY; return i; } + AMO_ALERT("amorph", "empty", "_fields_used is incorrect"); + return basis::common::IS_FULL; +} + +template +const contents *amorph::next_valid(int &field) const +{ + FUNCDEF("next_valid"); + CHECK_FIELDS; + bounds_return(field, 0, elements() - 1, NIL); + for (int i = field; i < elements(); i++) + if (basis::array::get(i)) { + field = i; + return basis::array::get(i); + } + return NIL; +} + +template +basis::outcome amorph::clear(int field) +{ + FUNCDEF("clear"); + CHECK_FIELDS; + return this->put(field, NIL); +} + +template +contents *amorph::acquire(int field) +{ + FUNCDEF("acquire"); + CHECK_FIELDS; + contents *to_return = borrow(field); + if (to_return) { + _fields_used--; + basis::array::access()[field] = NIL; + } + return to_return; +} + +template +int amorph::find(const contents *to_locate, basis::outcome &o) +{ + FUNCDEF("find"); + CHECK_FIELDS; + if (!_fields_used) { o = basis::common::NOT_FOUND; return 0; } + for (int i = 0; i < elements(); i++) { + if (basis::array::get(i) == to_locate) { + o = basis::common::OKAY; + return i; + } + } + o = basis::common::NOT_FOUND; + return 0; +} + +template +contents *amorph::borrow(int field) +{ + FUNCDEF("borrow"); + CHECK_FIELDS; + bounds_return(field, 0, elements() - 1, NIL); + return basis::array::access()[field]; +} + +template +void amorph::swap_contents(amorph &other) +{ + FUNCDEF("swap_contents"); + CHECK_FIELDS; + int hold_fields = _fields_used; + _fields_used = other._fields_used; + other._fields_used = hold_fields; + this->basis::array::swap_contents(other); +} + +template +int amorph_packed_size(const amorph &to_pack) +{ + int parts_size = 0; + for (int i = 0; i < to_pack.elements(); i++) { + const contents *current = to_pack.get(i); + if (current) parts_size += current->packed_size(); + } + return PACKED_SIZE_INT32 + parts_size; +} + +template +void amorph_pack(basis::byte_array &packed_form, const amorph &to_pack) +{ + structures::attach(packed_form, to_pack.elements()); + for (int i = 0; i < to_pack.elements(); i++) { + const contents *current = to_pack.get(i); + if (current) current->pack(packed_form); + } +} + +template +bool amorph_unpack(basis::byte_array &packed_form, amorph &to_unpack) +{ + to_unpack.reset(); + int elem = 0; + if (!structures::detach(packed_form, elem)) return false; + for (int i = 0; i < elem; i++) { + contents *to_add = new contents; + if (!to_add->unpack(packed_form)) { delete to_add; return false; } + to_unpack.append(to_add); + } + return true; +} + +template +void amorph_assign(amorph &to_assign, + const amorph &to_copy) +{ + if (&to_assign == &to_copy) return; + to_assign.clear_all(); + if (to_assign.elements() != to_copy.elements()) { + to_assign.zap(0, to_assign.elements() - 1); + to_assign.insert(0, to_copy.elements()); + } + for (int i = 0; i < to_assign.elements(); i++) { + if (to_copy.get(i)) to_assign.put(i, new contents(*to_copy.get(i))); + else to_assign.put(i, (contents *)NIL); + } +} + +#undef static_class_name + +} //namespace. + +#endif + diff --git a/core/library/structures/bit_vector.cpp b/core/library/structures/bit_vector.cpp new file mode 100644 index 00000000..8c00528e --- /dev/null +++ b/core/library/structures/bit_vector.cpp @@ -0,0 +1,317 @@ +/*****************************************************************************\ +* * +* Name : bit_vector * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1990-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "bit_vector.h" + +#include +#include +#include +#include + +//#define DEBUG_BIT_VECTOR + // uncomment this to get debugging noise. + +#undef LOG +#ifdef DEBUG_BIT_VECTOR + #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) +#else + #define LOG(s) {} +#endif + +using namespace basis; + +namespace structures { + +bit_vector::bit_vector() +: _implementation(new byte_array(0, NIL)), _number_of_bits(0) +{} + +bit_vector::bit_vector(int number_of_bits, const abyte *initial) +: _implementation(new byte_array(0, NIL)), _number_of_bits(0) +{ + reset(number_of_bits); + if (!initial) return; + _implementation->reset(number_of_packets(number_of_bits, + int(BITS_PER_BYTE)), initial); +} + +bit_vector::bit_vector(const bit_vector &to_copy) +: _implementation(new byte_array(*to_copy._implementation)), + _number_of_bits(to_copy._number_of_bits) +{} + +bit_vector::~bit_vector() { WHACK(_implementation); } + +bit_vector &bit_vector::operator = (const bit_vector &to_copy) +{ + if (this == &to_copy) return *this; + *_implementation = *to_copy._implementation; + _number_of_bits = to_copy._number_of_bits; + return *this; +} + +bit_vector::operator const byte_array & () const { return *_implementation; } + +int bit_vector::bits() const { return _number_of_bits; } + +bool bit_vector::whole() const { return negative(find_first(0)); } + +bool bit_vector::empty() const { return negative(find_first(1)); } + +bool bit_vector::operator == (const bit_vector &that) const +{ return compare(that, 0, _number_of_bits); } + +bool bit_vector::on(int position) const +{ return get_bit(into_two_dim(position)); } + +bool bit_vector::off(int position) const { return !on(position); } + +void bit_vector::resize(int number_of_bits) +{ +//printf("resize invoked, old size %d, new size %d...\n", bits(), number_of_bits); + if (negative(number_of_bits)) return; + if (bits() == number_of_bits) return; + int old_size = bits(); + _number_of_bits = number_of_bits; + int number_of_bytes = number_of_packets(number_of_bits, int(BITS_PER_BYTE)); + _implementation->resize(number_of_bytes); + // clear new space if the vector got larger. + if (old_size < number_of_bits) { + // lazy reset doesn't compute byte boundary, just does 8 bits. + for (int i = old_size; i < old_size + 8; i++) { + clear(i); +//printf("clearing bit %d.\n", i); + } + // clear the bytes remaining. + int old_bytes = number_of_packets(old_size + 8, int(BITS_PER_BYTE)); + for (int j = old_bytes; j < number_of_bytes; j++) { +//printf("clearing bit %d through %d.\n", j * 8, j * 8 + 7); + _implementation->use(j) = 0; + } + } +} + +void bit_vector::reset(int number_of_bits) +{ + resize(number_of_bits); + memset(_implementation->access(), 0, _implementation->length()); +} + +bit_vector::two_dim_location bit_vector::into_two_dim(int position) const +{ + two_dim_location to_return; + to_return.c_byte = position / BITS_PER_BYTE; + to_return.c_offset = position % BITS_PER_BYTE; + return to_return; +} + +bool bit_vector::get_bit(const two_dim_location &pos_in2) const +{ + bounds_return(pos_in2.c_byte * BITS_PER_BYTE + pos_in2.c_offset, 0, + _number_of_bits - 1, false); + abyte test_mask = abyte(1 << pos_in2.c_offset); + return TEST(abyte(_implementation->get(pos_in2.c_byte)), test_mask); +} + +void bit_vector::set_bit(int position, bool value) +{ + bounds_return(position, 0, bits() - 1, ); + set_bit(into_two_dim(position), value); +} + +void bit_vector::set_bit(const two_dim_location &pos_in2, bool set_it) +{ + abyte test_mask = abyte(1 << pos_in2.c_offset); + if (set_it) SET(_implementation->use(pos_in2.c_byte), test_mask); + else CLEAR((abyte &)_implementation->get(pos_in2.c_byte), test_mask); +} + +bool bit_vector::operator [](int position) const +{ + bounds_return(position, 0, _number_of_bits - 1, false); + return get_bit(into_two_dim(position)); +} + +void bit_vector::light(int position) +{ + bounds_return(position, 0, _number_of_bits - 1, ); + set_bit(into_two_dim(position), true); +} + +void bit_vector::clear(int position) +{ + bounds_return(position, 0, _number_of_bits - 1, ); + set_bit(into_two_dim(position), false); +} + +int bit_vector::find_first(bool to_find) const +{ + const abyte whole_set = abyte(0xFF); + // search through the whole bytes first. + for (int full_byte = 0; full_byte < _implementation->length(); full_byte++) { + if ( (to_find && _implementation->get(full_byte)) + || (!to_find && (_implementation->get(full_byte) != whole_set)) ) { + // the first appropriate byte is searched for the first appropriate bit. + for (int i = full_byte * BITS_PER_BYTE; i < minimum + (int(_number_of_bits), (full_byte+1)*BITS_PER_BYTE); i++) { + if (on(i) == to_find) return i; + } + return common::NOT_FOUND; + } + } + return common::NOT_FOUND; +} + +bool bit_vector::compare(const bit_vector &that, int start, int stop) const +{ + for (int i = start; i <= stop; i++) { + if (on(i) != that.on(i)) return false; + } + return true; +} + +astring bit_vector::text_form() const +{ + astring to_return; + int bits_on_line = 0; + const int estimated_elements_on_line = 64; + for (int i = 0; i < _number_of_bits; i++) { + // below prints out the bit number heading. + if (bits_on_line == 0) { + if (i != 0) to_return += "\n"; + if (i < 10000) to_return += " "; + if (i < 1000) to_return += " "; + if (i < 100) to_return += " "; + if (i < 10) to_return += " "; + to_return += a_sprintf("%d", i); + to_return += " | "; + } + if (on(i)) to_return += "1"; + else to_return += "0"; + bits_on_line++; + if (bits_on_line >= estimated_elements_on_line) bits_on_line = 0; + else if ( !(bits_on_line % BITS_PER_BYTE) ) to_return += " "; + } + to_return += "\n"; + return to_return; +} + +bit_vector bit_vector::subvector(int start, int end) const +{ + bounds_return(start, 0, bits() - 1, bit_vector()); + bounds_return(end, 0, bits() - 1, bit_vector()); + int size = end - start + 1; + bit_vector to_return(size); + for (int i = start; i <= end; i++) to_return.set_bit(i - start, on(i)); + return to_return; +} + +bool bit_vector::overwrite(int start, const bit_vector &to_write) +{ + bounds_return(start, 0, bits() - 1, false); + int end = start + to_write.bits() - 1; + bounds_return(end, 0, bits() - 1, false); + for (int i = start; i <= end; i++) set_bit(i, to_write[i - start]); + return true; +} + +enum endian { LEFT_ENDIAN, RIGHT_ENDIAN }; +endian host_byte_order = LEFT_ENDIAN; // bytes within words. +endian host_bit_order = LEFT_ENDIAN; // bits within bytes. + +// probably the treatment for right endian in either case +// of bytes or bits is wrong. + +bool bit_vector::set(int start, int size, basis::un_int source) +{ + bounds_return(start, 0, bits() - 1, false); + int end = start + size - 1; + bounds_return(end, 0, bits() - 1, false); + bounds_return(size, 1, 32, false); + bit_vector from_int(32, (abyte *)&source); + +// is this algorithm even remotely near the truth? + if (host_bit_order == RIGHT_ENDIAN) + from_int._implementation->resize(size, byte_array::NEW_AT_BEGINNING); + else from_int.resize(size); // left endian machine. + overwrite(start, from_int); + return true; +} + +basis::un_int bit_vector::get(int start, int size) const +{ + int end = start + size - 1; + bit_vector segment = subvector(start, end); + // padding to bytes. + int new_length = segment.bits(); + if (new_length % 8) { + new_length = ( (new_length+8) / 8) * 8; + LOG(a_sprintf("new size is %d.", new_length)); + } + segment.resize(new_length); + + if (host_bit_order == RIGHT_ENDIAN) { + bit_vector new_segment(segment.bits()); + for (int i = 0; i < segment.bits(); i += 8) + for (int j = i; j < i + BITS_PER_BYTE; j++) + if (j < segment.bits()) + new_segment.set_bit(i + (7 - (j - i)), segment[j]); + segment = new_segment; // copy the new form. + } + + LOG("new seg after bit copy:"); + LOG(segment); + + basis::un_int to_return = 0; + + int bytes_used = number_of_packets(segment.bits(), int(BITS_PER_BYTE)); + // 4 = # of bytes in a int. + for (int i = minimum(4, bytes_used) - 1; i >= 0; i--) { +#ifdef DEBUG_BIT_VECTOR + bit_vector tmp(8, &segment._implementation->get(i)); + LOG(a_sprintf("%d: src bits %s", i, tmp.text_form().s())); +#endif + +#ifdef DEBUG_BIT_VECTOR + bit_vector tmp4(32, (abyte *)&to_return); + LOG(a_sprintf("%d: pre shift dest %s", i, tmp4.text_form().s())); +#endif + if (host_byte_order == LEFT_ENDIAN) to_return <<= 8; + else to_return >>= 8; +#ifdef DEBUG_BIT_VECTOR + bit_vector tmp5(32, (abyte *)&to_return); + LOG(a_sprintf("%d: post shift dest %s", i, tmp5.text_form().s())); +#endif + + basis::un_int mask = segment._implementation->get(i); + if (host_byte_order == RIGHT_ENDIAN) mask <<= 23; +#ifdef DEBUG_BIT_VECTOR + bit_vector tmp3(32, (abyte *)&to_return); + LOG(a_sprintf("%d: pre dest bits %s", i, tmp3.text_form().s())); +#endif + SET(to_return, mask); +#ifdef DEBUG_BIT_VECTOR + bit_vector tmp2(32, (abyte *)&to_return); + LOG(a_sprintf("%d: post dest bits %s", i, tmp2.text_form().s())); +#endif + } + +#ifdef DEBUG_BIT_VECTOR + bit_vector tmp(32, (abyte *)&to_return); + LOG(a_sprintf("final bits %s", tmp.text_form().s())); +#endif + return to_return; +} + +} //namespace. diff --git a/core/library/structures/bit_vector.h b/core/library/structures/bit_vector.h new file mode 100644 index 00000000..55abae95 --- /dev/null +++ b/core/library/structures/bit_vector.h @@ -0,0 +1,162 @@ +#ifndef BIT_VECTOR_CLASS +#define BIT_VECTOR_CLASS + +/*****************************************************************************\ +* * +* Name : bit_vector * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1990-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +namespace structures { + +//! An array of bits with operations for manipulating and querying individual bits. + +class bit_vector +{ +public: + bit_vector(); + //!< creates a zero length bit_vector. + + bit_vector(int size, const basis::abyte *initial = NIL); + //!< creates a bit_vector able to store "size" bits. + /*!< if initial is NIL, the vector is initialized to zero. otherwise, the + bits are copied from "initial". "initial" must be large enough for the + copying to succeed. */ + + bit_vector(const bit_vector &to_copy); + + ~bit_vector(); + + bit_vector &operator =(const bit_vector &to_copy); + + int bits() const; + //!< returns the number of bits in the vector. + + bool operator [] (int position) const; + //!< returns the value of the bit at the position specified. + + bool on(int position) const; + //!< returns true if the bit at "position" is set. + + bool off(int position) const; + //!< returns true if the bit at "position" is clear. + + void set_bit(int position, bool value); + //!< sets the bit at "position" to a particular "value". + + bool whole() const; + //!< returns true if entire vector is set. + + bool empty() const; + //!< returns true if entire vector is unset. + + int find_first(bool to_find) const; + //!< Seeks the first occurrence of "to_find". + /*!< locates the position at which a bit equal to to_find is located or it + returns common::NOT_FOUND if no bit of that value is in the vector. */ + + void light(int position); + //!< sets the value of the bit at "position". + + void clear(int position); + //!< clears the value of the bit at "position". + + void resize(int size); + //!< Changes the size of the bit_vector to "size" bits. + /*!< This keeps any bits that still fit. Any new bits are set to zero. */ + + void reset(int size); + //!< resizes the bit_vector and clears all bits in it. + + bool compare(const bit_vector &that, int start, int stop) const; + //!< true if "this" is the same as "that" between "start" and "stop". + + bool operator == (const bit_vector &that) const; + //!< returns true if "this" is equal to "that". + /*!< neither vector is changed. if the vectors do not have the same size, + false is returned. */ + + basis::astring text_form() const; + //!< prints a nicely formatted list of bits to a string. + + bit_vector subvector(int start, int end) const; + //!< Extracts a chunk of the vector between "start" and "end". + /*!< Returns a bit_vector that is copied from this one starting at "start" + and running until "end". An empty bit vector is returned if the indices + are out of range. */ + + bool overwrite(int start, const bit_vector &to_write); + //!< Stores bits from "to_write" into "this" at "start". + /*!< overwrites the contents of this bit_vector with the contents of + "to_write", beginning at "start" in this bit_vector. true is returned + for a successful write. false will be returned if the "start" is out of + range or if "to_write" is too large. */ + + basis::un_int get(int start, int size) const; + //!< gets a portion of the vector as an unsigned integer. + /*!< returns an integer (as interpreted by the operating system) where the + pattern of bits in that integer is identical to the bits in this + bit_vector, beginning at "start" and including enough bits for an + integer of "size" bits. */ + + bool set(int start, int size, basis::un_int source); + //!< puts the "source" value into the vector at "start". + /*!< sets the pattern of bits in this bit_vector beginning at "start" + identically to how they are stored in the integer "source", where the + integer is expected to be using "size" of its bits. the bits are copied + starting from the low end of "source", where the operating system + defines what the low end is. true is returned for a successful copying. */ + + operator const basis::byte_array &() const; + //!< returns a copy of the low-level implementation of the bit vector. + /*!< the first bit is stored at the bit in first byte, and so forth. */ + +private: + basis::byte_array *_implementation; //!< holds the real state of the bits. + int _number_of_bits; //!< the total number of bits possible in this vector. + + struct two_dim_location { int c_byte; int c_offset; }; + //!< a two-dimensional position given by byte number and offset within byte. + + two_dim_location into_two_dim(int position) const; + /*!< turns a bit position in the vector into a two dimensional position + of the byte number and a bit offset within that byte. */ + bool get_bit(const two_dim_location &pos_in2) const; + //!< returns the value of the bit given its two dimensional location. + void set_bit(const two_dim_location &pos_in2, bool value); + //!< sets the value of the bit if "value", and clears it if not. +}; + +////////////// + +// NOTE: these are operations on numbers, NOT on bit_vectors. + +//! returns a number based on "to_modify" but with "bits" turned on. +template +void SET(type &to_modify, type bits) { to_modify |= bits; } + +//! returns a number based on "to_modify" but with "bits" turned off. +template +void CLEAR(type &to_modify, type bits) { to_modify &= (type)~bits; } + +//! returns non-zero if the "bits" bit pattern is turned on in "to_test". +template +bool TEST(type to_test, type bits) { return bool(!(!(to_test & bits))); } + +////////////// + +} //namespace. + +#endif + diff --git a/core/library/structures/byte_hasher.h b/core/library/structures/byte_hasher.h new file mode 100644 index 00000000..f7631259 --- /dev/null +++ b/core/library/structures/byte_hasher.h @@ -0,0 +1,56 @@ +#ifndef ROTATING_BYTE_HASHER_CLASS +#define ROTATING_BYTE_HASHER_CLASS + +/*****************************************************************************\ +* * +* Name : rotating_byte_hasher * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "byte_hasher.h" +#include "checksums.h" +#include "hash_table.h" + +#include + +namespace structures { + +//! Implements a hashing algorithm based on the contents stored in an object. +/*! + This will only be usable for key types that have flat members; keys with + pointers limit the meaning of the hash value, or destroy the meaning if the + pointer value can change between lookups. Note that objects based on RTTI + will probably never work with this either since the compiler stores extra + data as part of the binary form for those objects. +*/ + +class rotating_byte_hasher : public virtual hashing_algorithm +{ +public: + virtual ~rotating_byte_hasher() {} + + virtual basis::un_int hash(const void *key_data, int key_length) const + { return checksums::hash_bytes(key_data, key_length); } + //!< returns a value that can be used for indexing into a hash table. + /*!< the returned value is loosely based on the "key_data" and the + "key_length" we are provided with. note: do not use a huge key length + for this or your hash table will be very slow; the key should probably + be limited to 16 or less. */ + + virtual hashing_algorithm *clone() const + { return new rotating_byte_hasher; } + //!< implements cloning of the algorithm object. +}; + +} //namespace. + +#endif + diff --git a/core/library/structures/checksums.cpp b/core/library/structures/checksums.cpp new file mode 100644 index 00000000..16be2f1f --- /dev/null +++ b/core/library/structures/checksums.cpp @@ -0,0 +1,98 @@ +/*****************************************************************************\ +* * +* Name : checksums group * +* Authors: Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "checksums.h" + +#include + +using namespace basis; + +namespace structures { + +const int HIGHEST_MOD_VALUE = 32014; + +unsigned int checksums::bizarre_checksum(const abyte *data, int length) +{ + int sum = 0; + for (int i = 0; i < length; i++) sum += data[i] % 23 + i % 9; + sum = (sum % (HIGHEST_MOD_VALUE - 1)) + 1; + return (unsigned int)sum; +} + +// fletcher checksum is from Dr. Dobbs Journal May 1992, pp. 32-38. + +basis::un_short checksums::fletcher_checksum(const abyte *data, int length) +{ + int sum1 = 0; + basis::un_int sum2 = 0; + const abyte *buffer = data; + + while (length--) { + sum1 += int(*buffer++); + // check for overflow into high byte. + if (sum1 > 255) sum1++; + sum1 &= 0xFF; // remove any bits in high byte for next sum. + sum2 += basis::un_int(sum1); + } + if (sum1 == 255) sum1 = 0; + unsigned int fletch = basis::un_int(sum2 & 0xFF); + fletch <<= 8; + fletch |= basis::un_int(sum1 & 0xFF); + + return basis::un_short(fletch); +} + +basis::un_short checksums::rolling_fletcher_checksum(basis::un_short previous, const abyte *data, + int len) +{ return previous ^ fletcher_checksum(data, len); } + +abyte checksums::byte_checksum(const abyte *data, int length) +{ + abyte to_return = 0; + for (int i = 0; i < length; i++) to_return += abyte(data[i]); + return to_return; +} + +basis::un_int checksums::short_checksum(const abyte *data, int length) +{ + basis::un_int to_return = 0; + for (int i = 0; i < length; i++) to_return += data[i]; + return to_return; +} + +basis::un_int checksums::hash_bytes(const void *key_data, int key_length) +{ + if (!key_data) return 0; // error! + if (!key_length) return 0; // ditto! + + abyte *our_key = (abyte *)key_data; + abyte hashed[4] = { 0, 0, 0, 0 }; + + int fill_posn = 0; + for (int i = 0; i < key_length; i++) { + // add to the primary area. + hashed[fill_posn] = hashed[fill_posn] + our_key[i]; + fill_posn++; + if (fill_posn >= 4) fill_posn = 0; + // add to the secondary area (the next in rotation after primary). + hashed[fill_posn] = hashed[fill_posn] + (our_key[i] / 4); + } + + basis::un_int to_return = 0; + for (int j = 0; j < 4; j++) to_return = (to_return << 8) + hashed[j]; + return to_return; +} + +} //namespace. + diff --git a/core/library/structures/checksums.h b/core/library/structures/checksums.h new file mode 100644 index 00000000..4f3f0ba8 --- /dev/null +++ b/core/library/structures/checksums.h @@ -0,0 +1,61 @@ +#ifndef CHECKSUMS_GROUP +#define CHECKSUMS_GROUP + +/*****************************************************************************\ +* * +* Name : checksums group * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +/* @file checksums.h + A collection of useful checksum calculators. +*/ + +namespace structures { + +class checksums +{ +public: + + static basis::abyte byte_checksum(const basis::abyte *data, int length); + //!< simple byte-sized checksum based on additive roll-over. + + static basis::un_int short_checksum(const basis::abyte *data, int length); + //!< simple shorty checksum based on additive roll-over. + + static basis::un_short fletcher_checksum(const basis::abyte *data, int length); + //!< A positionally computed error detection value. + + static basis::un_short rolling_fletcher_checksum(basis::un_short previous, const basis::abyte *data, int len); + //!< Fletcher checksums applied to streaming data. + /*!< this is not strictly a fletcher checksum, but it uses the normal + fletcher checksum on the specified data and XORs it with the "previous" + value of the checksum. this leads to a regenerable number that should + always be the same if done on the same data using the same chunking + factor (the "len"), although of course the last piece of data does not + have to be "len" bytes. */ + + static unsigned int bizarre_checksum(const basis::abyte *data, int length); + //!< A different type of checksum with somewhat unknown properties. + /*!< It attempts to be incorporate positioning of the bytes. */ + + static basis::un_int hash_bytes(const void *key_data, int key_length); + //!< returns a value that can be used for indexing into a hash table. + /*!< the returned value is loosely based on the "key_data" and the + "key_length" we are provided with. */ +}; + +} //namespace. + +#endif + diff --git a/core/library/structures/hash_table.h b/core/library/structures/hash_table.h new file mode 100644 index 00000000..4eb02856 --- /dev/null +++ b/core/library/structures/hash_table.h @@ -0,0 +1,597 @@ +#ifndef HASH_TABLE_CLASS +#define HASH_TABLE_CLASS + +/*****************************************************************************\ +* * +* Name : hash_table * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "amorph.h" + +#include +#include +#include +#include +#include + +#include + +namespace structures { + +// forward. +template class internal_hash_array; + +//! A hashing algorithm takes a key and derives a related integer from it. +/*! + The hashing_algorithm is used in hash_tables for placing objects into slots + that can be easily found again. The numerical hash should be faster than + actually doing a search on what might be unsorted data otherwise. +*/ + +class hashing_algorithm : public virtual basis::clonable +{ +public: + virtual basis::un_int hash(const void *key_data, int key_length) const = 0; + //!< returns a hash value based on the "key_data" and "key_length". + /*!< the "key_length" is provided from the sizeof() of the key type used + in the hash_table (below). it is up to the implementor to create a hash + value that spreads the keys to be hashed appropriately. if similar keys + create same or similar hash values, then the efficiency of the hash_table + is compromised. */ + + virtual hashing_algorithm *clone() const = 0; + //!< supports virtual construction of new algorithm objects. +}; + +////////////// + +//! Implements hashing into buckets for quick object access. +/*! + The buckets are currently simple lists, but if the hashing algorithm is well + chosen, then that's not a major problem. This makes lookups a lot faster + than a linear search, but no particular performance guarantees are made at + this time (hmmm). +*/ + +template + // the key_type must support valid copy constructor, assignment operator and + // equality operators. the contents need not support any special operations; + // it is always considered as just a pointer. +class hash_table : public virtual basis::nameable +{ +public: + hash_table(const hashing_algorithm &hasher, int estimated_elements); + //!< Creates a table using the "hasher" that is ready to store "estimated_elements". + /*!< the "hasher" must provide the hashing algorithm for the two types + specified. if the implementation is thread safe, then one object can + be constructed to use with all the same types of hash tables. + note that "hasher" must exist longer than any classes based on it; do + not let "hasher" go out of scope or the hash table will explode. */ + + virtual ~hash_table(); + //!< destroys any objects left in the hash_table. + + DEFINE_CLASS_NAME("hash_table"); + + void rehash(int estimated_elements); + //!< resizes the hashing structures to optimise for a new size of "estimated_elements". + /*!< this is a potentially expensive operation. */ + + enum outcomes { + IS_NEW = basis::common::IS_NEW, + EXISTING = basis::common::EXISTING + }; + + static int calculate_num_slots(int estimated_elements); + //!< a helper method that lets us know what n is for how many 2^n slots we should have. + + int elements() const; + //!< the number of valid items we found by traversing the hash table. + /*!< this is a very costly method. */ + + int estimated_elements() const { return c_estim_elements; } + //!< returns the size of table we're optimized for. + + basis::outcome add(const key_type &key, contents *to_store); + //!< Stores "to_store" into the table given its "key" for hashing. + /*!< This places an entry in the hash table with the contents "to_store", + using the "key" structure to identify it. the return value reports whether + the "key" was already in the list or not. if the "key" was already in use, + then the contents for it get replaced with "to_store". note that the hash + table assumes ownership of the "to_store" object but just makes a copy of + the key. thus, if an item is replaced, the previous contents are + destroyed. */ + + basis::outcome fast_dangerous_add(const key_type &key, contents *to_store); + //!< Like the add method above, but doesn't check for duplicates. + /*!< This should only ever be used when one has already guaranteed that + the table doesn't have a duplicate item for the "key" specified. */ + + bool find(const key_type &key, contents * &item_found) const; + //!< locates the item specified by the "key", if possible. + /*!< true is returned on success and the "item_found" is filled in. the + "item_found" is a pointer to the actual data held in the table, so do not + destroy or otherwise damage the "item_found". */ + + contents *find(const key_type &key) const + { contents *c = NIL; return find(key, c)? c : NIL; } + //!< simplified form of above find() method. + + contents *acquire(const key_type &key); + //!< retrieves the contents held for "key" out of the table. + /*!< afterwards, the contents pointer is the caller's responsibility; it + is no longer in the table and must be destroyed externally. if no item + was found for the "key", then NIL is returned. */ + + bool zap(const key_type &key); + //!< removes the entry with the "key" specified. + /*!< true is returned if the item was found and destroyed. */ + + void reset(); + //!< removes all entries in the table and returns it to a pristine state. + + typedef bool apply_function(const key_type &key, contents ¤t, + void *data_link); + //!< the "apply_function" is what a user of the "apply" method must supply. + /*!< the function will be called on every item in the table unless one of + the invocations returns false; this causes the apply process to stop. + the "data_link" provides a way for the function to refer back to an + parent class of some sort. */ + + void apply(apply_function *to_apply, void *data_link); + //!< Invokes the function "to_apply" on every entry in the table. + /*!< This calls the "to_apply" function on every item in the catalog + until the function returns false. The "data_link" pointer is available to + the applied method and can be used to communicate an object for use during + the iteration. NOTE: it is NOT safe to rearrange or manipulate the table + in any way from your "to_apply" function. */ + + basis::outcome add(key_type *key, contents *to_store, bool check_dupes = true); + //!< specialized add for a pre-existing pointer "key". + /*!< responsibility for the "key" is taken over by the hash table, as of + course it is for the "to_store" pointer. this just allows others to + generate new keys and hand them over to us when it might otherwise be + very costly to copy the key structure. if "check_dupes" is not true, + then that asserts that you have independently verified that there's no + need to check whether the key is already present. */ + + bool verify() const; + //!< returns true if the hash table is internally consistent. + /*!< this is mainly for debugging. */ + +private: + int c_estim_elements; //!< expected running size for the hash table. + hashing_algorithm *_hasher; //!< algorithm for getting hash value. + internal_hash_array *_table; //!< storage area. + int _last_iter; + //!< tracks where we left off iterating. we restart just after that spot. + + hash_table(const hash_table &to_copy); + //!< not allowed; use the copy_hash_table function below. + hash_table &operator =(const hash_table &to_copy); + //!< not allowed; use the copy_hash_table function below. + +public: + internal_hash_array &table_access() const; + //!< special accessor for the copy_hash_table method only. +}; + +////////////// + +//! Copies the entire hash table, given a contents type that supports copying. +/*! + Provides a copy operation on hash tables where the contents object supports + a copy constructor, which is not appropriate to require in general. The + hash_table held in "target" will be wiped out and replaced with items + equivalent to those in "source". */ + +template +void copy_hash_table(hash_table &target, + const hash_table &source); + +////////////// + +// implementations for longer methods below.... + +// this is a very special micro-managed class. it holds two pointers which +// it neither creates nor destroys. thus all interaction with it must be +// careful about removing these objects at the appropriate time. we don't +// want automatic memory management since we want a cheap copy on the items +// in the buckets. + +template +class hash_wrapper : public virtual basis::root_object +{ +public: + key_type *_id; + contents *_data; + + hash_wrapper(key_type *id = NIL, contents *data = NIL) + : _id(id), _data(data) {} +}; + +////////////// + +template +class bucket + : public basis::array >, + public virtual basis::root_object +{ +public: + bucket() : basis::array >(0, NIL, + basis::byte_array::SIMPLE_COPY | basis::byte_array::EXPONE + | basis::byte_array::FLUSH_INVISIBLE) {} + + int find(const key_type &to_find) { + for (int i = 0; i < this->length(); i++) { + key_type *curr_key = this->get(i)._id; +//hmmm: if curr key is not set, is that a logic error? it seems like we +// are seeing the potential for this. + if (curr_key && (to_find == *curr_key)) + return i; + } + return basis::common::NOT_FOUND; + } +}; + +////////////// + +template +class internal_hash_array : public amorph > +{ +public: + internal_hash_array(int estimated_elements) + : amorph > + (hash_table::calculate_num_slots(estimated_elements)) {} +}; + +////////////// + +template +hash_table::hash_table(const hashing_algorithm &hasher, int estimated_elements) +: c_estim_elements(estimated_elements), + _hasher(hasher.clone()), + _table(new internal_hash_array(estimated_elements)), + _last_iter(0) +{} + +template +hash_table::~hash_table() +{ +#ifdef EXTREME_CHECKING + FUNCDEF("destructor"); + if (!verify()) deadly_error(class_name(), func, "state did not verify."); +#endif + reset(); + basis::WHACK(_table); + basis::WHACK(_hasher); +} + +template +int hash_table::calculate_num_slots(int estimated_elements) +{ +//printf("elems wanted = %d\n", estimated_elements); + int log_2_truncated = int(log(float(estimated_elements)) / log(2.0)); +//printf("log 2 of elems, truncated = %d\n", log_2_truncated); + int slots_needed_for_elems = (int)pow(2.0, double(log_2_truncated + 1)); +//printf("slots needed = %d\n", slots_needed_for_elems ); + return slots_needed_for_elems; +} + +// the specialized copy operation. +template +void copy_hash_table(hash_table &target, + const hash_table &source) +{ +#ifdef EXTREME_CHECKING + FUNCDEF("copy_hash_table"); + if (!source.verify()) + deadly_error(class_name(), func, "source state did not verify."); +#endif + target.reset(); + for (int i = 0; i < source.table_access().elements(); i++) { + bucket *buck = source.table_access().borrow(i); + if (!buck) continue; + for (int j = 0; j < buck->length(); j++) { + target.add(*((*buck)[j]._id), new contents(*((*buck)[j]._data))); + } + } +#ifdef EXTREME_CHECKING + if (!target.verify()) + deadly_error(class_name(), func, "target state did not verify."); +#endif + #undef class_name +} + +template +void hash_table::reset() +{ +#ifdef EXTREME_CHECKING + FUNCDEF("reset"); + if (!verify()) deadly_error(class_name(), func, "state did not verify."); +#endif + // iterate over the list whacking the content items in the buckets. + for (int i = 0; i < _table->elements(); i++) { + bucket *buck = _table->acquire(i); + if (!buck) continue; + for (int j = 0; j < buck->length(); j++) { + basis::WHACK((*buck)[j]._data); // eliminate the stored data. + basis::WHACK((*buck)[j]._id); // eliminate the stored key. + buck->zap(j, j); // remove the element. + j--; // skip back before whack happened. + } + basis::WHACK(buck); + } +#ifdef EXTREME_CHECKING + if (!verify()) + deadly_error(class_name(), func, "state did not verify afterwards."); +#endif +} + +template +bool hash_table::verify() const +{ + for (int i = 0; i < _table->elements(); i++) { + const bucket *buck = _table->borrow(i); + if (!buck) continue; // that's acceptable. + for (int j = 0; j < buck->length(); j++) { + const hash_wrapper &wrap = (*buck)[j]; + if (!wrap._data) { +// program_wide_logger::get().log(a_sprintf("hash table: no data segment at position %d.", j)); + return false; + } + if (!wrap._id) { +// program_wide_logger::get().log(a_sprintf("hash table: no identifier at position %d.", j)); + return false; + } + } + } + return true; +} + +template +internal_hash_array &hash_table + ::table_access() const +{ return *_table; } + +template +void hash_table::rehash(int estimated_elements) +{ +#ifdef EXTREME_CHECKING + FUNCDEF("rehash"); + if (!verify()) deadly_error(class_name(), func, "state did not verify."); +#endif + typedef bucket buckie; + hash_table new_hash(*_hasher, estimated_elements); + // this is the new table that will be used. + + // scoot through the existing table so we can move items into the new one. + for (int i = 0; i < _table->elements(); i++) { + buckie *b = _table->borrow(i); // look at the current element. + if (!b) continue; // nothing here, keep going. + for (int j = b->length() - 1; j >= 0; j--) { + key_type *k = b->use(j)._id; + contents *c = b->use(j)._data; + new_hash.add(k, c); + } + b->reset(); + // clean out any items in the buckets here now that they've moved. + } + + // now flip the contents of the new guy into "this". + _table->reset(); // toss previous stuff. + _table->adjust(new_hash._table->elements()); + for (int q = 0; q < new_hash._table->elements(); q++) + _table->put(q, new_hash._table->acquire(q)); + // reset other data members. + c_estim_elements = new_hash.c_estim_elements; + _last_iter = 0; +#ifdef EXTREME_CHECKING + if (!verify()) + deadly_error(class_name(), func, "state did not verify afterwards."); +#endif +} + +template +basis::outcome hash_table::add(const key_type &key, + contents *to_store) +{ return add(new key_type(key), to_store); } + +template +basis::outcome hash_table::add(key_type *key, + contents *to_store, bool check_dupes) +{ +#ifdef EXTREME_CHECKING + FUNCDEF("add"); + if (!verify()) deadly_error(class_name(), func, "state did not verify."); +#endif + // get a hash value. + basis::un_int hashed = _hasher->hash((const void *)key, sizeof(key_type)); + // make the value appropriate for our table. + hashed = hashed % _table->elements(); + // see if the key already exists there. + bucket *buck = _table->borrow(hashed); + if (!buck) { + // this slot doesn't exist yet. + buck = new bucket; + _table->put(hashed, buck); + } + if (!check_dupes) { + // we aren't even going to check for its existence. + *buck += hash_wrapper(key, to_store); + return IS_NEW; + } + int indy = buck->find(*key); + if (basis::negative(indy)) { + // that value was not seen yet, so we're adding a new entry. + *buck += hash_wrapper(key, to_store); +#ifdef EXTREME_CHECKING + if (!verify()) + deadly_error(class_name(), func, "state did not verify afterwards."); +#endif + return IS_NEW; + } + // that key already existed, so we'll re-use its slot with the new data. + basis::WHACK((*buck)[indy]._data); + basis::WHACK(key); // toss since we're not using. + (*buck)[indy]._data = to_store; +#ifdef EXTREME_CHECKING + if (!verify()) + deadly_error(class_name(), func, "state did not verify afterwards."); +#endif + return EXISTING; +} + +template +basis::outcome hash_table::fast_dangerous_add + (const key_type &key, contents *to_store) +{ return add(new key_type(key), to_store, false); } + +template +bool hash_table::find(const key_type &key, + contents * &item_found) const +{ +#ifdef EXTREME_CHECKING + FUNCDEF("find"); + if (!verify()) deadly_error(class_name(), func, "state did not verify."); +#endif + item_found = NIL; + // get a hash value. + basis::un_int hashed = _hasher->hash((const void *)&key, sizeof(key)); + // make the value appropriate for our table. + hashed = hashed % _table->elements(); + // see if the key exists in the bucket. + bucket *buck = _table->borrow(hashed); + if (!buck) return false; + int indy = buck->find(key); + if (basis::negative(indy)) return false; // not there. + // was there, so retrieve the data. + item_found = (*buck)[indy]._data; + return true; +} + +template +contents *hash_table::acquire(const key_type &key) +{ +#ifdef EXTREME_CHECKING + FUNCDEF("acquire"); + if (!verify()) deadly_error(class_name(), func, "state did not verify."); +#endif + // get a hash value. + basis::un_int hashed = _hasher->hash((const void *)&key, sizeof(key)); + // make the value appropriate for our table. + hashed = hashed % _table->elements(); + // see if the key exists in the bucket. + bucket *buck = _table->borrow(hashed); + if (!buck) return NIL; + int indy = buck->find(key); + if (basis::negative(indy)) return NIL; // nope, not there. + contents *to_return = (*buck)[indy]._data; + basis::WHACK((*buck)[indy]._id); // clean the id. + buck->zap(indy, indy); // toss the storage blob for the item. +#ifdef EXTREME_CHECKING + if (!verify()) + deadly_error(class_name(), func, "state did not verify afterwards."); +#endif + return to_return; +} + +template +bool hash_table::zap(const key_type &key) +{ +#ifdef EXTREME_CHECKING + FUNCDEF("zap"); + if (!verify()) deadly_error(class_name(), func, "state did not verify."); +#endif + // get a hash value. + basis::un_int hashed = _hasher->hash((const void *)&key, sizeof(key)); + // make the value appropriate for our table. + hashed = hashed % _table->elements(); + // see if the key exists in the bucket. + bucket *buck = _table->borrow(hashed); + if (!buck) return false; + int indy = buck->find(key); + if (basis::negative(indy)) { + // nope, not there. + return false; + } + basis::WHACK((*buck)[indy]._data); // delete the data held. + basis::WHACK((*buck)[indy]._id); // delete the data held. + buck->zap(indy, indy); // toss the storage blob for the item. + if (!buck->length()) { + // clean up this bucket now. + buck = _table->acquire(hashed); + basis::WHACK(buck); + } +#ifdef EXTREME_CHECKING + if (!verify()) + deadly_error(class_name(), func, "state did not verify afterwards."); +#endif + return true; +} + +template +void hash_table::apply(apply_function *to_apply, + void *data_link) +{ +#ifdef EXTREME_CHECKING + FUNCDEF("apply"); + if (!verify()) deadly_error(class_name(), func, "state did not verify."); +#endif + typedef bucket buckie; + int bucks_seen = 0; + int posn = _last_iter; // start at the same place we left. + while (bucks_seen++ < _table->elements()) { + if ( (posn < 0) || (posn >= _table->elements()) ) + posn = 0; + buckie *b = _table->borrow(posn); + _last_iter = posn++; + // record where the iteration last touched and increment next position. + // we must do this before we check if the bucket exists or we'll rotate + // on this same place forever. + if (!b) continue; // nothing here, keep going. + for (int j = 0; j < b->length(); j++) { + contents *c = b->use(j)._data; + if (!c) { +#ifdef EXTREME_CHECKING + deadly_error("hash_table", "apply", "logic error--missing pointer"); +#endif + continue; + } + if (!to_apply(*b->use(j)._id, *c, data_link)) return; + } + } +} + +template +int hash_table::elements() const +{ +#ifdef EXTREME_CHECKING + FUNCDEF("elements"); + if (!verify()) deadly_error(class_name(), func, "state did not verify."); +#endif + typedef bucket buckie; + int to_return = 0; + for (int i = 0; i < _table->elements(); i++) { + bucket *buck = _table->borrow(i); + if (!buck) continue; // nothing to count. + to_return += buck->length(); + } + return to_return; +} + +#undef static_class_name + +} //namespace. + +#endif // outer guard. + diff --git a/core/library/structures/int_hash.h b/core/library/structures/int_hash.h new file mode 100644 index 00000000..bd5f3494 --- /dev/null +++ b/core/library/structures/int_hash.h @@ -0,0 +1,132 @@ +#ifndef INT_HASH_CLASS +#define INT_HASH_CLASS + +/*****************************************************************************\ +* * +* Name : int_hash * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "byte_hasher.h" +#include "hash_table.h" + +#include +#include + +namespace structures { + +//! A hash table for storing integers. +/*! + Implements a hash table indexed on integers that maintains a separate set of + identifiers for listing the items that are presently in the hash table. This + slows down additions somewhat, but finds are not affected. The advantage of + the separate index is that the apply() method is much faster. +*/ + +template +class int_hash : public hash_table +{ +public: + int_hash(int max_bits); + ~int_hash(); + + const int_set &ids() const; + void ids(int_set &ids) const; + //!< provides the current list of valid identifiers. + + basis::outcome add(int key, contents *to_store); + //!< overrides base add() and ensures that the id list stays up to date. + contents *acquire(int key); + //!< overrides base acquire() by ensuring that the ids stay up to date. + bool zap(int key); + //!< overrides base zap() method plus keeps id list updated. + void reset(); + //!< overrides base reset() and ensures that the id list stays up to date. + + typedef bool apply_function(const int &key, contents ¤t, + void *data_link); + + void apply(apply_function *to_apply, void *data_link); + //!< operates on every item in the int_hash table. + +private: + int_set *_ids; + //!< a separate list of the identifiers stored here. + /*! this provides a fairly quick way to iterate rather than having to span + the whole hash table. it does slow down zap() a bit though. */ +}; + +////////////// + +// implementations below... + +template +int_hash::int_hash(int max_bits) +: hash_table(rotating_byte_hasher(), max_bits), + _ids(new int_set) +{} + +template +int_hash::~int_hash() +{ WHACK(_ids); } + +template +const int_set &int_hash::ids() const { return *_ids; } + +template +void int_hash::ids(int_set &ids) const { ids = *_ids; } + +template +basis::outcome int_hash::add(int key, contents *to_store) +{ + _ids->add(key); + return hash_table::add(key, to_store); +} + +template +contents *int_hash::acquire(int key) +{ + _ids->remove(key); + return hash_table::acquire(key); +} + +template +bool int_hash::zap(int key) +{ + _ids->remove(key); + return hash_table::zap(key); +} + +template +void int_hash::reset() +{ + _ids->clear(); + hash_table::reset(); +} + +template +void int_hash::apply(apply_function *to_apply, void *data_link) +{ + for (int i = 0; i < _ids->elements(); i++) { + int current = (*_ids)[i]; + contents *found = hash_table::find(current); + if (!found) { + _ids->remove(current); + continue; + } + to_apply(current, *found, data_link); + } +} + +} //namespace. + +#endif // outer guard. + diff --git a/core/library/structures/makefile b/core/library/structures/makefile new file mode 100644 index 00000000..78155565 --- /dev/null +++ b/core/library/structures/makefile @@ -0,0 +1,10 @@ +include cpp/variables.def + +PROJECT = structures +TYPE = library +SOURCE = bit_vector.cpp checksums.cpp memory_limiter.cpp object_packers.cpp \ + static_memory_gremlin.cpp string_hasher.cpp string_table.cpp version_record.cpp +TARGETS = structures.lib + +include cpp/rules.def + diff --git a/core/library/structures/matrix.h b/core/library/structures/matrix.h new file mode 100644 index 00000000..96ec3e07 --- /dev/null +++ b/core/library/structures/matrix.h @@ -0,0 +1,352 @@ +#ifndef MATRIX_CLASS +#define MATRIX_CLASS + +/*****************************************************************************\ +* * +* Name : matrix * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1993-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include + +namespace structures { + +//! Represents a two-dimensional array of objects. +/*! + The indices range from zero to (maximum_value - 1) for rows and columns. +*/ + +template +class matrix : protected basis::array +{ +public: + matrix(int rows = 0, int cols = 0, contents *data = NIL); + //!< the "data" array must have at least "rows" * "cols" contents in it. + matrix(const matrix &to_copy); + + ~matrix() {} + + int rows() const { return _rows; } + int columns() const { return _cols; } + + matrix &operator = (const matrix &to_copy); + + contents &get(int row, int column); + const contents &get(int row, int column) const; + //!< retrieves the contents from the specified "row" and "column". + + bool put(int row, int column, const contents &to_put); + //!< stores "to_put" into the matrix at "row" and "column". + + contents *operator[] (int row); + //!< dangerous: indexes by "row" into the underlying contents. + /*!< the result can be used as a pointer to the whole row of data, or it + can be indexed into by column to find a row/column position. this is + dangerous if one is not careful about ensuring that the column used is + within bounds. if the "row" parameter is out of bounds, then NIL is + returned. */ + const contents *operator[] (int row) const; + //!< dangerous: constant peek into data for "row" in underlying contents. + + matrix submatrix(int row, int column, int rows, int columns) const; + //!< returns a submatrix of this one starting at "row" and "column". + /*!< The returned matrix should contain "rows" rows and "columns" columns. + if the size or position are out of bounds, an empty matrix is returned. */ + void submatrix(matrix &sub, int row, int column, int rows, int cols) const; + //!< like submatrix() above, but stores into the "sub" parameter. + + matrix transpose() const; + //!< provides the transposed form of this matrix. + void transpose(matrix &resultant) const; + //!< stores the transpose of this matrix in "resultant". + + basis::array get_row(int row); + //!< return the vector at the "row", or an empty array if "row" is invalid. + basis::array get_column(int column); + //!< return the vector at the "column", or an empty array if it's invalid. + + bool insert_row(int before_row); + //!< inserts a row before the "before_" parameter. + /*!< all existing data is preserved in the matrix, although it may get + moved over depending on where it's located with respect to "before_row". */ + bool insert_column(int before_column); + //!< inserts a column before the "before_" parameter. + + void reset(int rows = 0, int columns = 0); + //!< empties the matrix and changes its size. + + void redimension(int new_rows, int new_columns); + //!< changes the size to contain "new_rows" by "new_columns" elements. + /*!< data that was held previously stays in the array as long as its row + and column indices are still valid. */ + + bool zap_row(int row_to_zap); + bool zap_column(int column_to_zap); + //!< removes a row or column from the matrix. + /*!< the matrix becomes one row or column less than before and all data + from the deleted vector is lost. */ + +private: + int _rows; //!< number of rows in the matrix. + int _cols; //!< number of columns in the matrix. + + int compute_index(int row, int column) const; + //!< returns the flat index that corresponds to the "row" and "column". +}; + +////////////// + +//! A matrix of integers. +class int_matrix : public matrix +{ +public: + int_matrix(int rows = 0, int cols = 0, int *data = NIL) + : matrix(rows, cols, data) {} + int_matrix(const matrix &to_copy) : matrix(to_copy) {} +}; + +//! A matrix of strings. +class string_matrix : public matrix +{ +public: + string_matrix(int rows = 0, int cols = 0, basis::astring *data = NIL) + : matrix(rows, cols, data) {} + string_matrix(const matrix &to_copy) : matrix(to_copy) {} +}; + +//! A matrix of double floating point numbers. +class double_matrix : public matrix +{ +public: + double_matrix(int rows = 0, int cols = 0, double *data = NIL) + : matrix(rows, cols, data) {} + double_matrix(const matrix &to_copy) : matrix(to_copy) {} +}; + +////////////// + +// implementation for longer methods... + +//hmmm: the operations for zapping use extra memory. they could easily +// be done as in-place copies. + +#undef static_class_name +#define static_class_name() "matrix" + // used in bounds_halt macro. + +template +matrix::matrix(int r, int c, contents *dat) +: basis::array(r*c, dat), _rows(r), _cols(c) {} + +template +matrix::matrix(const matrix &to_copy) +: basis::array(0), _rows(0), _cols(0) +{ *this = to_copy; } + +template +matrix &matrix::operator = (const matrix &to_copy) +{ + if (&to_copy == this) return *this; + basis::array::operator = (to_copy); + _rows = to_copy._rows; + _cols = to_copy._cols; + return *this; +} + +template +contents *matrix::operator[] (int r) +{ return &basis::array::operator [] (compute_index(r, 0)); } + +template +const contents *matrix::operator[] (int r) const +{ return &basis::array::operator [] (compute_index(r, 0)); } + +template +const contents &matrix::get(int r, int c) const +{ return basis::array::get(compute_index(r, c)); } + +template +contents &matrix::get(int row, int column) +{ return basis::array::operator [] (compute_index(row, column)); } + +template +int matrix::compute_index(int row, int column) const +{ return column + row * _cols; } + +template +void matrix::reset(int rows_in, int columns_in) +{ + if ( (_rows == rows_in) && (_cols == columns_in) ) { + // reuse space, but reset the objects to their initial state. + for (int i = 0; i < basis::array::length(); i++) + basis::array::operator [](i) = contents(); + return; + } + + _rows = 0; + _cols = 0; + basis::array::reset(0); + + this->insert(0, rows_in * columns_in); + _rows = rows_in; + _cols = columns_in; +} + +template +void matrix::redimension(int new_rows, int new_columns) +{ + if ( (_rows == new_rows) && (_cols == new_columns) ) return; + matrix new_this(new_rows, new_columns); + for (int r = 0; r < minimum(new_rows, rows()); r++) + for (int c = 0; c < minimum(new_columns, columns()); c++) + new_this[r][c] = (*this)[r][c]; + *this = new_this; +} + +template +bool matrix::put(int row, int column, const contents &to_put) +{ + if ( (row >= rows()) || (column >= columns()) ) + return false; + (operator [](row))[column] = to_put; + return true; +} + +template +matrix matrix::submatrix(int row, int column, int rows_in, + int columns_in) const +{ + matrix to_return; + submatrix(to_return, row, column, rows_in, columns_in); + return to_return; +} + +template +void matrix::submatrix(matrix &sub, int row, int column, + int rows_in, int columns_in) const +{ + sub.reset(); + if ( (row >= rows()) || (row + rows_in >= rows()) ) return; + if ( (column >= columns()) || (column + columns_in >= columns()) ) return; + sub.reset(rows_in, columns_in); + for (int r = row; r < row + rows_in; r++) + for (int c = column; c < column + columns_in; c++) + sub[r - row][c - column] = (*this)[r][c]; +} + +template +matrix matrix::transpose() const +{ + matrix to_return; + transpose(to_return); + return to_return; +} + +template +void matrix::transpose(matrix &resultant) const +{ + resultant.reset(columns(), rows()); + for (int i = 0; i < rows(); i++) + for (int j = 0; j < columns(); j++) + resultant[j][i] = (*this)[i][j]; +} + +template +basis::array matrix::get_row(int row) +{ + basis::array to_return; + if (row >= rows()) return to_return; + to_return.reset(columns()); + for (int i = 0; i < columns(); i++) + to_return[i] = get(row, i); + return to_return; +} + +template +basis::array matrix::get_column(int column) +{ + basis::array to_return; + if (column >= columns()) return to_return; + to_return.reset(rows()); + for (int i = 0; i < rows(); i++) + to_return[i] = get(i, column); + return to_return; +} + +template +bool matrix::zap_row(int row_to_zap) +{ + FUNCDEF("zap_row"); + bounds_halt(row_to_zap, 0, rows() - 1, false); + const int start = compute_index(row_to_zap, 0); + // this is only safe because the indices are stored in row-major order (which + // i hope means the right thing). in any case, the order is like so: + // 1 2 3 4 + // 5 6 7 8 + // thus we can whack a whole row contiguously. + basis::array::zap(start, start + columns() - 1); + _rows--; + return true; +} + +template +bool matrix::zap_column(int column_to_zap) +{ + FUNCDEF("zap_column"); + bounds_halt(column_to_zap, 0, columns() - 1, false); + // this starts at the end, which keeps the indexes meaningful. otherwise + // the destruction interferes with finding the elements. + for (int r = rows(); r >= 0; r--) { + const int loc = compute_index(r, column_to_zap); + basis::array::zap(loc, loc); + } + _cols--; + return true; +} + +template +bool matrix::insert_row(int position) +{ + FUNCDEF("insert_row"); + bounds_halt(position, 0, rows(), false); + // see comment in zap_row for reasoning about the below. + basis::array::insert(compute_index(position, 0), columns()); + _rows++; + // clear out those spaces. + for (int c = 0; c < columns(); c++) + put(position, c, contents()); + return true; +} + +template +bool matrix::insert_column(int position) +{ + FUNCDEF("insert_column"); + bounds_halt(position, 0, columns(), false); + // similarly to zap_column, we must iterate in reverse. + for (int r = rows(); r >= 0; r--) + basis::array::insert(compute_index(r, position), 1); + _cols++; + // clear out those spaces. + for (int r = 0; r < rows(); r++) + put(r, position, contents()); + return true; +} + +#undef static_class_name + +} //namespace. + +#endif + diff --git a/core/library/structures/memory_limiter.cpp b/core/library/structures/memory_limiter.cpp new file mode 100644 index 00000000..b0756a8a --- /dev/null +++ b/core/library/structures/memory_limiter.cpp @@ -0,0 +1,171 @@ +/*****************************************************************************\ +* * +* Name : memory_limiter * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "int_hash.h" +#include "memory_limiter.h" + +#include + +#include + +using namespace basis; + +namespace structures { + +#undef LOG +#define LOG(to_print) printf("%s\n", astring(to_print).s()) + +class ml_memory_record +{ +public: + int _usage; + + ml_memory_record(int initial) : _usage(initial) {} +}; + +////////////// + +class ml_memory_state_meter : public int_hash +{ +public: + ml_memory_state_meter() : int_hash(10) {} +}; + +////////////// + +memory_limiter::memory_limiter(int overall_limit, int individual_limit) +: _overall_limit(overall_limit), + _individual_limit(individual_limit), + _overall_size(0), + _individual_sizes(new ml_memory_state_meter) +{ +} + +memory_limiter::~memory_limiter() +{ + WHACK(_individual_sizes); +} + +void memory_limiter::reset() +{ + _overall_size = 0; + _individual_sizes->reset(); +} + +const int_set &memory_limiter::individuals_listed() const +{ return _individual_sizes->ids(); } + +ml_memory_record *memory_limiter::find_individual(int individual) const +{ + ml_memory_record *to_return = NIL; + if (!_individual_sizes->find(individual, to_return)) return NIL; + // no record for that guy. + return to_return; +} + +int memory_limiter::individual_usage(int individual) const +{ + ml_memory_record *found = find_individual(individual); + if (!found) return 0; + return found->_usage; +} + +int memory_limiter::individual_space_left(int individual) const +{ + if (!individual_limit()) return 0; + return individual_limit() - individual_usage(individual); +} + +astring memory_limiter::text_form(int indent) const +{ + astring to_return; + astring indentat(' ', indent); + + astring allowed = overall_limit()? + astring(astring::SPRINTF, "%dK", overall_limit() / KILOBYTE) + : "unlimited"; + astring avail = overall_limit()? + astring(astring::SPRINTF, "%dK", overall_space_left() / KILOBYTE) + : "unlimited"; + + to_return += astring(astring::SPRINTF, "Overall Limit=%s, Allocations=%dK, " + "Free Space=%s", allowed.s(), overall_usage() / KILOBYTE, avail.s()); + to_return += "\n"; + + int_set individuals = _individual_sizes->ids(); + for (int i = 0; i < individuals.elements(); i++) { + astring allowed = individual_limit()? + astring(astring::SPRINTF, "%dK", individual_limit() / KILOBYTE) + : "unlimited"; + astring avail = individual_limit()? + astring(astring::SPRINTF, "%dK", + individual_space_left(individuals[i]) / KILOBYTE) : "unlimited"; + + to_return += indentat + astring(astring::SPRINTF, "individual %d: " + "Limit=%s, Used=%dK, Free=%s", individuals[i], allowed.s(), + individual_usage(individuals[i]) / KILOBYTE, avail.s()); + to_return += "\n"; + } + if (!individuals.elements()) { + to_return += indentat + "No allocations owned currently."; + to_return += "\n"; + } + return to_return; +} + +bool memory_limiter::okay_allocation(int individual, int memory_desired) +{ +// FUNCDEF("okay_allocation"); + // check the overall allocation limits first. + if (_overall_limit + && (_overall_size + memory_desired > _overall_limit) ) return false; + // now check sanity of this request. + if (_individual_limit && (memory_desired > _individual_limit) ) return false; + // now check the allocations per user. + ml_memory_record *found = find_individual(individual); + if (!found) { + _individual_sizes->add(individual, new ml_memory_record(0)); + found = find_individual(individual); + if (!found) { + LOG("ERROR: adding a new record to the memory state!"); + return false; + } + } + if (_individual_limit + && (found->_usage + memory_desired > _individual_limit) ) + return false; + found->_usage += memory_desired; + _overall_size += memory_desired; + return true; +} + +bool memory_limiter::record_deletion(int individual, int memory_deleted) +{ + if (memory_deleted < 0) return false; // bogus. + // make sure the individual exists. + ml_memory_record *found = find_individual(individual); + if (!found) return false; + // the individual must have actually allocated at least that much previously. + if (found->_usage < memory_deleted) return false; + // okay, we think that's reasonable. + found->_usage -= memory_deleted; + _overall_size -= memory_deleted; + // clean out an empty locker. + if (!found->_usage) _individual_sizes->zap(individual); + return true; +} + +} //namespace. + + diff --git a/core/library/structures/memory_limiter.h b/core/library/structures/memory_limiter.h new file mode 100644 index 00000000..1cb0e9fc --- /dev/null +++ b/core/library/structures/memory_limiter.h @@ -0,0 +1,116 @@ +#ifndef MEMORY_LIMITER_CLASS +#define MEMORY_LIMITER_CLASS + +/*****************************************************************************\ +* * +* Name : memory_limiter * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace structures { + +// forward. +class ml_memory_record; +class ml_memory_state_meter; + +//! Tracks memory currently in use by memory manager objects. +/*! + The manager is given the ability to control overall memory usage as well + as to track memory usage per each user of the memory (assuming that the + memory is granted to unique users indexable via an integer). +*/ + +class memory_limiter +{ +public: + memory_limiter(int overall_limit, int individual_limit); + //!< creates a limiter that allows "overall_limit" bytes to be in use. + /*!< any attempts to add new memory after that limit is reached will be + rejected. if "overall_limit" is zero, then no limit is enforced on the + amount of memory that can be used in total. the "individual_limit" + specifies per-user limits, where each user of memory is identified by a + unique integer and where all users are granted equal rights to allocate + memory. "individual_limit" can also be given as zero, meaning no limit + is enforced per individual. note that "overall_limit" should usually be + many multiples of the "individual_limit", as appropriate to how many users + are expected. */ + + virtual ~memory_limiter(); + + DEFINE_CLASS_NAME("memory_limiter"); + + int overall_limit() const { return _overall_limit; } + //!< returns the current overall limit. + int individual_limit() const { return _individual_limit; } + //!< returns the current individual limit. + + int overall_usage() const { return _overall_size; } + //!< returns the size used by all managed memory. + + int overall_space_left() const { return overall_limit() - overall_usage(); } + //!< returns the overall space left for allocation. + + int individual_usage(int individual) const; + //!< returns the amount of memory used by "individual". + + int individual_space_left(int individual) const; + //!< returns the space left for the individual specified. + + basis::astring text_form(int indent = 0) const; + //!< returns a string that lists out the overall plus individual limits. + /*!< "indent" is used for spacing the printed rows of information. */ + + bool okay_allocation(int individual, int memory_desired); + //!< returns true if "individual" may allocate "memory_desired" bytes. + /*!< false indicates that this memory must not be allocated if the limits + are to be adhered to, either because there is already too much used in + the system at large or because this user is already using their limit. */ + + bool record_deletion(int individual, int memory_deleted); + //!< acknowledges that the "individual" freed "memory_deleted" bytes. + /*!< returns true if the "individual" is known and if "memory_deleted" + could be subtracted from that object's usage count. failure of this method + indicates that this class is not being used properly; if memory was + okayed to be granted in okay_allocation, then the paired record_deletion + will always succeed (in any arbitrary order where the okay_allocation + succeeds and proceeds the matching record_deletion). if there are no + remaining allocations for this individual, then its record is removed. */ + + void reset(); + //!< returns the object to a pristine state. + + const structures::int_set &individuals_listed() const; + //!< reports the current set of individuals using memory. + /*!< to know whether one is using this class appropriately, check the + returned set. if one does not think there should be any memory in use, + then the set should be empty and overall_usage() should return zero. + if the overall_usage() is zero, but there are members in the set, then + there is an implementation error in memory_limiter. otherwise, if the + set is non-empty, then deleted memory has not been recorded. */ + +private: + int _overall_limit; //!< how many total bytes allowed? + int _individual_limit; //!< how many bytes may each user consume? + int _overall_size; //!< the current measured overall memory usage in bytes. + ml_memory_state_meter *_individual_sizes; //!< tracks memory per individual. + + ml_memory_record *find_individual(int individual) const; + //!< locates the record held for the "individual" specified or returns NIL. +}; + +} //namespace. + +#endif + diff --git a/core/library/structures/object_packers.cpp b/core/library/structures/object_packers.cpp new file mode 100644 index 00000000..233ee8ad --- /dev/null +++ b/core/library/structures/object_packers.cpp @@ -0,0 +1,279 @@ +/*****************************************************************************\ +* * +* Name : object_packers * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1995-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "object_packers.h" + +#include + +using namespace basis; + +namespace structures { + +// rotate_in and snag_out do most of the real "work", if any. + +void rotate_in(byte_array &attach_into, int to_attach, int size_in_bytes) +{ + basis::un_int temp = basis::un_int(to_attach); + for (int i = 0; i < size_in_bytes; i++) { + attach_into += abyte(temp % 0x100); + temp >>= 8; + } +} + +void snag_out(byte_array &eat_from, basis::un_int &accumulator, int size_in_bytes) +{ + accumulator = 0; + for (int i = 0; i < size_in_bytes; i++) { + accumulator <<= 8; + accumulator += eat_from[size_in_bytes - i - 1]; + } + eat_from.zap(0, size_in_bytes - 1); +} + +////////////// + +int packed_size(const byte_array &packed_form) +{ return 2 * sizeof(int) + packed_form.length(); } + +void attach(byte_array &packed_form, const byte_array &to_attach) +{ + obscure_attach(packed_form, to_attach.length()); + packed_form += to_attach; +} + +bool detach(byte_array &packed_form, byte_array &to_detach) +{ + un_int len = 0; + if (!obscure_detach(packed_form, len)) return false; + if (packed_form.length() < (int)len) return false; + to_detach = packed_form.subarray(0, len - 1); + packed_form.zap(0, len - 1); + return true; +} + +////////////// + +// these are the only "real" attach/detach functions on number types. the +// others are all faking it by calling these. + +void attach(byte_array &packed_form, basis::un_int to_attach) +{ rotate_in(packed_form, to_attach, 4); } + +bool detach(byte_array &packed_form, basis::un_int &to_detach) +{ + if (packed_form.length() < 4) return false; + basis::un_int temp; + snag_out(packed_form, temp, 4); + to_detach = basis::un_int(temp); + return true; +} + +void attach(byte_array &packed_form, basis::un_short to_attach) +{ rotate_in(packed_form, to_attach, 2); } + +bool detach(byte_array &packed_form, basis::un_short &to_detach) +{ + if (packed_form.length() < 2) return false; + basis::un_int temp; + snag_out(packed_form, temp, 2); + to_detach = basis::un_short(temp); + return true; +} + +void attach(byte_array &packed_form, abyte to_attach) +{ packed_form += to_attach; } + +bool detach(byte_array &packed_form, abyte &to_detach) +{ + if (packed_form.length() < 1) return false; + to_detach = packed_form[0]; + packed_form.zap(0, 0); + return true; +} + +////////////// + +void attach(byte_array &packed_form, int to_attach) +{ attach(packed_form, basis::un_int(to_attach)); } + +bool detach(byte_array &packed_form, int &to_detach) +{ return detach(packed_form, (basis::un_int &)to_detach); } + +//void attach(byte_array &packed_form, basis::un_long to_attach) +//{ attach(packed_form, basis::un_int(to_attach)); } + +//bool detach(byte_array &packed_form, basis::un_long &to_detach) +//{ return detach(packed_form, (basis::un_int &)to_detach); } + +//void attach(byte_array &packed_form, long to_attach) +//{ attach(packed_form, basis::un_int(to_attach)); } + +//bool detach(byte_array &packed_form, long &to_detach) +//{ return detach(packed_form, (basis::un_int &)to_detach); } + +void attach(byte_array &packed_form, short to_attach) +{ attach(packed_form, basis::un_short(to_attach)); } + +bool detach(byte_array &packed_form, short &to_detach) +{ return detach(packed_form, (basis::un_short &)to_detach); } + +void attach(byte_array &packed_form, char to_attach) +{ attach(packed_form, abyte(to_attach)); } + +bool detach(byte_array &packed_form, char &to_detach) +{ return detach(packed_form, (abyte &)to_detach); } + +void attach(byte_array &packed_form, bool to_attach) +{ attach(packed_form, abyte(to_attach)); } + +////////////// + +// can't assume that bool is same size as byte, although it should fit +// into a byte just fine. +bool detach(byte_array &packed_form, bool &to_detach) +{ + abyte chomp; + if (!detach(packed_form, chomp)) return false; + to_detach = !!chomp; + return true; +} + +// operates on a number less than 1.0 that we need to snag the next digit +// to the right of the decimal point from. +double break_off_digit(double &input) { +//printf(astring(astring::SPRINTF, "break input=%f\n", input).s()); + input *= 10.0; +//printf(astring(astring::SPRINTF, "after mult=%f\n", input).s()); + double mod_part = fmod(input, 1.0); +//printf(astring(astring::SPRINTF, "modded=%f\n", mod_part).s()); + double to_return = input - mod_part; +//printf(astring(astring::SPRINTF, "to ret=%f\n", to_return).s()); + input -= to_return; + return to_return; +} + +//hmmm: not very efficient! it's just packing and wasting bytes doing it... +int packed_size(double to_pack) +{ + byte_array packed; + attach(packed, to_pack); + return packed.length(); +} + +void attach(byte_array &packed_form, double to_pack) +{ + int exponent = 0; + double mantissa = frexp(to_pack, &exponent); + abyte pos = mantissa < 0.0? false : true; + mantissa = fabs(mantissa); +//printf("mant=%10.10f pos=%d expon=%d\n", mantissa, int(pos), exponent); + packed_form += pos; + attach(packed_form, exponent); + byte_array mantis; + // even if the double has 52 bits for mantissa (where ms docs say 44), + // a 16 digit bcd encoded number should handle the size (based on size of + // 2^52 in digits). + for (int i = 0; i < 9; i++) { + double dig1 = break_off_digit(mantissa); +//printf(astring(astring::SPRINTF, "break digit=%d\n", int(dig1)).s()); + double dig2 = break_off_digit(mantissa); +//printf(astring(astring::SPRINTF, "break digit=%d\n", int(dig2)).s()); + mantis += abyte(dig1 * 16 + dig2); + } + attach(packed_form, mantis); +//printf("attach exit\n"); +} + +bool detach(byte_array &packed_form, double &to_unpack) +{ +//printf("detach entry\n"); + if (packed_form.length() < 1) return false; // no sign byte. + abyte pos = packed_form[0]; +//printf(astring(astring::SPRINTF, "pos=%d\n", int(pos)).s()); + packed_form.zap(0, 0); + int exponent; + if (!detach(packed_form, exponent)) return false; +//printf(astring(astring::SPRINTF, "expon=%d\n", exponent).s()); + byte_array mantis; + if (!detach(packed_form, mantis)) return false; + double mantissa = 0; + for (int i = mantis.last(); i >= 0; i--) { + abyte chop = mantis[i]; + double dig1 = chop / 16; +//printf(astring(astring::SPRINTF, "break digit=%d\n", int(dig1)).s()); + double dig2 = chop % 16; +//printf(astring(astring::SPRINTF, "break digit=%d\n", int(dig2)).s()); + mantissa += dig2; + mantissa /= 10; + mantissa += dig1; + mantissa /= 10; + } +//printf(astring(astring::SPRINTF, "mant=%10.10f\n", mantissa).s()); + to_unpack = ldexp(mantissa, exponent); + if (!pos) to_unpack = -1.0 * to_unpack; +//printf("pos=%d\n", int(pos)); +//printf(astring(astring::SPRINTF, "to_unpack=%f\n", to_unpack).s()); +//printf("detach exit\n"); + return true; +} + +void attach(byte_array &packed_form, float to_pack) +{ attach(packed_form, double(to_pack)); } + +bool detach(byte_array &packed_form, float &to_unpack) +{ + double real_unpack; + bool to_return = detach(packed_form, real_unpack); + to_unpack = (float)real_unpack; + return to_return; +} + +////////////// + +void obscure_attach(byte_array &packed_form, un_int to_attach) +{ +//printf("initial value=%x\n", to_attach); + basis::un_int first_part = 0xfade0000; +//printf("first part curr=%x\n", first_part); + basis::un_int second_part = 0x0000ce0f; +//printf("second part curr=%x\n", second_part); + first_part = first_part | (to_attach & 0x0000ffff); +//printf("first part now=%x\n", first_part); + second_part = second_part | (to_attach & 0xffff0000); +//printf("second part now=%x\n", second_part); + attach(packed_form, first_part); + attach(packed_form, second_part); +} + +bool obscure_detach(byte_array &packed_form, un_int &to_detach) +{ + basis::un_int first_part; + basis::un_int second_part; + if (!detach(packed_form, first_part)) return false; + if (!detach(packed_form, second_part)) return false; +//printf("first part after unpack=%x\n", first_part); +//printf("second part after unpack=%x\n", second_part); + if (basis::un_int(first_part & 0xffff0000) != basis::un_int(0xfade0000)) return false; +//printf("first part with and=%x\n", first_part & 0xffff0000); + if (basis::un_int(second_part & 0x0000ffff) != basis::un_int(0x0000ce0f)) return false; +//printf("second part with and=%x\n", second_part & 0x0000ffff); + to_detach = int( (second_part & 0xffff0000) + (first_part & 0x0000ffff) ); +//printf("final result=%x\n", to_detach); + return true; +} + +////////////// + +} // namespace + diff --git a/core/library/structures/object_packers.h b/core/library/structures/object_packers.h new file mode 100644 index 00000000..fecb7876 --- /dev/null +++ b/core/library/structures/object_packers.h @@ -0,0 +1,178 @@ +#ifndef OBJECT_PACKERS_CLASS +#define OBJECT_PACKERS_CLASS + +/*****************************************************************************\ +* * +* Name : object_packers * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1995-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +namespace structures { + +// the sizes in bytes of common objects. if the compiler doesn't match these, there will +// probably be severe tire damage. +const int PACKED_SIZE_BYTE = 1; +const int PACKED_SIZE_INT16 = 2; +const int PACKED_SIZE_INT32 = 4; + +// these functions pack and unpack popular data types. + +void attach(basis::byte_array &packed_form, bool to_attach); + //!< Packs a bool "to_attach" into "packed_form". +bool detach(basis::byte_array &packed_form, bool &to_detach); + //!< Unpacks a bool "to_detach" from "packed_form". + +void attach(basis::byte_array &packed_form, basis::abyte to_attach); + //!< Packs a byte "to_attach" into "packed_form". +bool detach(basis::byte_array &packed_form, basis::abyte &to_detach); + //!< Unpacks a byte "to_detach" from "packed_form". + +int packed_size(const basis::byte_array &packed_form); + //!< Reports the size required to pack a byte array into a byte array. +void attach(basis::byte_array &packed_form, const basis::byte_array &to_attach); + //!< Packs a byte_array "to_attach" into "packed_form". +bool detach(basis::byte_array &packed_form, basis::byte_array &to_detach); + //!< Unpacks a byte_array "to_detach" from "packed_form". + +void attach(basis::byte_array &packed_form, char to_attach); + //!< Packs a char "to_attach" into "packed_form". +bool detach(basis::byte_array &packed_form, char &to_detach); + //!< Unpacks a char "to_detach" from "packed_form". + +int packed_size(double to_pack); + //!< Reports how large the "to_pack" will be as a stream of bytes. +void attach(basis::byte_array &packed_form, double to_pack); + //!< Packs a double precision floating point "to_attach" into "packed_form". +bool detach(basis::byte_array &packed_form, double &to_unpack); + //!< Unpacks a double precision floating point "to_attach" from "packed_form". + +void attach(basis::byte_array &packed_form, float to_pack); + //!< Packs a floating point "to_attach" into "packed_form". +bool detach(basis::byte_array &packed_form, float &to_unpack); + //!< Unpacks a floating point "to_attach" from "packed_form". + +void attach(basis::byte_array &packed_form, int to_attach); + //!< Packs an integer "to_attach" into "packed_form". + /*!< This method and the other simple numerical storage methods use a little + endian ordering of the bytes. They are platform independent with respect to + endianness and will reassemble the number properly on any platform. */ +bool detach(basis::byte_array &packed_form, int &to_detach); + //!< Unpacks an integer "to_attach" from "packed_form". + +void obscure_attach(basis::byte_array &packed_form, basis::un_int to_attach); + //!< like the normal attach but shifts in some recognizable sentinel data. + /*!< this is slightly more sure than a simple integer attachment. it can + be used to make sure upcoming data is probably a valid int. */ +bool obscure_detach(basis::byte_array &packed_form, basis::un_int &to_detach); + //!< shifts the number back and checks validity, false returned if corrupted. + +/* +void attach(basis::byte_array &packed_form, long to_attach); + //!< Packs a long integer "to_attach" into "packed_form". +bool detach(basis::byte_array &packed_form, long &to_detach); + //!< Unpacks a long integer "to_attach" from "packed_form". +*/ + +void attach(basis::byte_array &packed_form, short to_attach); + //!< Packs a short integer "to_attach" into "packed_form". +bool detach(basis::byte_array &packed_form, short &to_detach); + //!< Unpacks a short integer "to_attach" from "packed_form". + +void attach(basis::byte_array &packed_form, basis::un_int to_attach); + //!< Packs an unsigned integer "to_attach" into "packed_form". +bool detach(basis::byte_array &packed_form, basis::un_int &to_detach); + //!< Unpacks an unsigned integer "to_attach" from "packed_form". + +/* +void attach(basis::byte_array &packed_form, basis::un_long to_attach); + //!< Packs an unsigned long integer "to_attach" into "packed_form". +bool detach(basis::byte_array &packed_form, basis::un_long &to_detach); + //!< Unpacks an unsigned long integer "to_attach" from "packed_form". +*/ + +void attach(basis::byte_array &packed_form, basis::un_short to_attach); + //!< Packs an unsigned short integer "to_attach" into "packed_form". +bool detach(basis::byte_array &packed_form, basis::un_short &to_detach); + //!< Unpacks an unsigned short integer "to_attach" from "packed_form". + +////////////// + +// helpful template functions for packing. + +//! provides a way to pack any array that stores packable objects. +template +void pack_array(basis::byte_array &packed_form, const basis::array &to_pack) { + obscure_attach(packed_form, to_pack.length()); + for (int i = 0; i < to_pack.length(); i++) to_pack[i].pack(packed_form); +} + +//! provides a way to unpack any array that stores packable objects. +template +bool unpack_array(basis::byte_array &packed_form, basis::array &to_unpack) { + to_unpack.reset(); + basis::un_int len; + if (!obscure_detach(packed_form, len)) return false; + basis::array swappy_array(len, NIL, to_unpack.flags()); + // we create an array of the specified length to see if it's tenable. + if (!swappy_array.observe()) return false; // failed to allocate. + for (int i = 0; i < (int)len; i++) { + if (!swappy_array[i].unpack(packed_form)) + return false; + } + // now that we've got exactly what we want, plunk it into the result array. + swappy_array.swap_contents(to_unpack); + return true; +} + +//! provides space estimation for the objects to be packed. +template +int packed_size_array(const basis::array &to_pack) { + int to_return = sizeof(int) * 2; // obscure version uses double int size. + for (int i = 0; i < to_pack.length(); i++) + to_return += to_pack[i].packed_size(); + return to_return; +} + +//! Packs flat objects into an array of bytes. +/*! Similar to pack above, but operates on arrays with simple +objects that do not support functional pack and unpack. */ +template +void pack_simple(basis::byte_array &packed_form, const basis::array &to_pack) { + obscure_attach(packed_form, to_pack.length()); + for (int i = 0; i < to_pack.length(); i++) + attach(packed_form, to_pack[i]); +} + +//! Unpacks flat objects from an array of bytes. +/*! Similar to unpack above, but operates on arrays with simple +objects that do not support functional pack and unpack. */ +template +bool unpack_simple(basis::byte_array &packed_form, basis::array &to_unpack) { + to_unpack.reset(); + basis::un_int len; + if (!obscure_detach(packed_form, len)) return false; + basis::array swappy_array(len, NIL, to_unpack.flags()); + if (!swappy_array.observe()) return false; // failed to allocate. + for (int i = 0; i < len; i++) { + if (!detach(packed_form, swappy_array[i])) + return false; + } + swappy_array.swap_contents(to_unpack); + return true; +} + +} // namespace + +#endif + diff --git a/core/library/structures/pointer_hash.h b/core/library/structures/pointer_hash.h new file mode 100644 index 00000000..dce57a56 --- /dev/null +++ b/core/library/structures/pointer_hash.h @@ -0,0 +1,134 @@ +#ifndef POINTER_HASH_CLASS +#define POINTER_HASH_CLASS + +/*****************************************************************************\ +* * +* Name : pointer_hash * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "byte_hasher.h" +#include "hash_table.h" +#include "pointer_hash.h" +#include "set.h" + +// forward. +class pointer_set; + +namespace structures { + +//! A hash table for storing pointers. +/*! + Implements a hash table indexed on pointer values that maintains a separate + set to list the items that are presently in the hash table. This slows down + additions somewhat, but finds are not affected. The advantage of the + separate index is that the apply() method is much faster. +*/ + +template +class pointer_hash : public hash_table +{ +public: + pointer_hash(int estimated_elements); + ~pointer_hash(); + + const pointer_set &ids() const; + void ids(pointer_set &ids) const; + //!< provides the current list of valid identifiers. + + basis::outcome add(void *key, contents *to_store); + //!< overrides base add() and ensures that the id list stays up to date. + contents *acquire(void *key); + //!< overrides base acquire() by ensuring that the ids stay up to date. + bool zap(void *key); + //!< overrides base zap() method plus keeps id list updated. + void reset(); + //!< overrides base reset() and ensures that the id list stays up to date. + + typedef bool apply_function(const void * &key, contents ¤t, + void *data_link); + + void apply(apply_function *to_apply, void *data_link); + //!< operates on every item in the pointer_hash table. + +private: + pointer_set *_ids; + //!< a separate list of the identifiers stored here. + /*! this provides a fairly quick way to iterate rather than having to span + the whole hash table. it does slow down zap() a bit though. */ +}; + +////////////// + +// implementations for larger methods below... + +template +pointer_hash::pointer_hash(int estimated_elements) +: hash_table(rotating_byte_hasher(), estimated_elements), + _ids(new pointer_set) +{} + +template +pointer_hash::~pointer_hash() +{ WHACK(_ids); } + +template +const pointer_set &pointer_hash::ids() const { return *_ids; } + +template +void pointer_hash::ids(pointer_set &ids) const { ids = *_ids; } + +template +basis::outcome pointer_hash::add(void *key, contents *to_store) +{ + _ids->add(key); + return hash_table::add(key, to_store); +} + +template +contents *pointer_hash::acquire(void *key) +{ + _ids->remove(key); + return hash_table::acquire(key); +} + +template +bool pointer_hash::zap(void *key) +{ + _ids->remove(key); + return hash_table::zap(key); +} + +template +void pointer_hash::reset() +{ + _ids->clear(); + hash_table::reset(); +} + +template +void pointer_hash::apply(apply_function *to_apply, void *data_link) +{ + for (int i = 0; i < _ids->elements(); i++) { + void *current = (*_ids)[i]; + contents *found = hash_table::find(current); + if (!found) { + _ids->remove(current); + continue; + } + to_apply(current, *found, data_link); + } +} + +} //namespace. + +#endif // outer guard. + diff --git a/core/library/structures/roller.h b/core/library/structures/roller.h new file mode 100644 index 00000000..9308e710 --- /dev/null +++ b/core/library/structures/roller.h @@ -0,0 +1,125 @@ +#ifndef ROLLER_CLASS +#define ROLLER_CLASS + +/*****************************************************************************\ +* * +* Name : roller * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace structures { + +//! Maintains a pseudo-unique identifier number and issues a new one on demand. +/*! + The unique id is templated on the type of identifier, but the type used must + support: + 1) assigment to a value, + 2) a greater than or equal operator (>=), and + 3) the increment operator (++). + Zero is often treated as the invalid / empty / inactive identifier, but + roller does not prevent its use since some ranges might need to bridge + between negative and positive numbers. +*/ + +template +class roller +{ +public: + roller(contents start_of_range, contents end_of_range); + //!< constructs a roller between the start and end ranges. + /*!< this constructs a roller that runs from the value "start_of_range" + and will stay smaller than "end_of_range" (unless the initial parameters + are screwed up; this class doesn't validate the start and end of range). + when the roller hits the end of range, then its value is reset to the + "start_of_range" again. */ + + ~roller(); + + // these report the constructor parameters. + contents minimum() { return _start_of_range; } + //!< the smallest value that the roller can have. + contents maximum() { return _end_of_range; } + //!< the outer limit of the roller; it should never reach this. + + contents next_id(); + //!< returns a unique (per instance of this type) id. + + contents current() const; + //!< returns the current id to be used; be careful! + /*!< this value will be the next one returned, so only look at the current + id and don't use it unwisely. this function is useful if you want to + assign an id provisionally but might not want to complete the issuance of + it. */ + + void set_current(contents new_current); + //!< allows the current id to be manipulated. + /*!< this must be done with care lest existing ids be re-used. */ + +private: + contents _current_id; //!< the next id to bring forth. + contents _start_of_range; //!< first possible value. + contents _end_of_range; //!< one more than last possible value. +}; + +////////////// + +//! A roller that's based on integers. This is the most common type so far. + +class int_roller : public roller +{ +public: + int_roller(int start_of_range, int end_of_range) + : roller(start_of_range, end_of_range) {} +}; + +////////////// + +// implementations below... + +template +roller::roller(contents start, contents end) +: _current_id(start), _start_of_range(start), _end_of_range(end) {} + +template +void roller::set_current(contents new_current) +{ + _current_id = new_current; + if (_current_id >= _end_of_range) _current_id = _start_of_range; +} + +template roller::~roller() {} + +template contents roller::current() const +{ return _current_id; } + +template contents roller::next_id() +{ + contents to_return = _current_id; + if (to_return == _end_of_range) { + // somehow the id to return is at the end of the range. this probably + // means the end of range condition wasn't detected last time due to an + // error in the parameters or the operation of == or ++ in the templated + // class. + _current_id = _start_of_range; + to_return = _current_id; + } + _current_id++; // next id. + if (_current_id == _end_of_range) _current_id = _start_of_range; + // reset the current position when hits the end of the range. + return to_return; +} + +} //namespace. + +#endif + diff --git a/core/library/structures/set.h b/core/library/structures/set.h new file mode 100644 index 00000000..3215080f --- /dev/null +++ b/core/library/structures/set.h @@ -0,0 +1,315 @@ +#ifndef SET_CLASS +#define SET_CLASS + +/*****************************************************************************\ +* * +* Name : set * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "object_packers.h" +#include "string_array.h" + +#include +#include +#include +#include + +namespace structures { + +//! Emulates a mathematical set, providing several standard set operations. +/*! + Note: this is not an efficient object and it should not be used for + sets of non-trivial sizes. +*/ + +template +class set : public basis::array +{ +public: + //! Constructs a set with "num" elements, copying them from "init". + /*! Be very careful to ensure that the array "init" has sufficient length + for "num" elements to be copied from it. */ + set(int num = 0, const contents *init = NIL, + basis::un_short flags = basis::array::EXPONE) + : basis::array(num, init, flags) {} + + ~set() {} //!< Destroys any storage held for the set. + + int elements() const { return this->length(); } + //!< Returns the number of elements in this set. + + bool empty() const { return elements() == 0; } + //!< Returns true if the set has no elements. + bool non_empty() const { return elements() != 0; } + //!< Returns true if the set has some elements. + + void clear() { this->reset(); } //!< Empties out this set. + + bool member(const contents &to_test) const; + //!< Returns true if the item "to_test" is a member of this set. + + bool add(const contents &to_add); + //!< Adds a new element "to_add" to the set. + /*!< This always succeeds, but will return true if the item was not + already present. */ + + set &operator += (const contents &to_add) + { add(to_add); return *this; } + //!< An algebraic operator synonym for add() that operates on the contents. + set &operator += (const set &to_add) + { unionize(to_add); return *this; } + //!< An algebraic operator synonym for add() that operates on a set. + + bool remove(const contents &to_remove); + //!< Removes the item "to_remove" from the set. + /*!< If it was not present, false is returned and the set is unchanged. */ + + set &operator -= (const contents &to_zap) + { remove(to_zap); return *this; } + //!< An algebraic operator synonym for remove that operates on the contents. + set &operator -= (const set &to_zap) + { differentiate(to_zap); return *this; } + //!< An algebraic operator synonym for remove that operates on a set. + + set set_union(const set &union_with) const; + //!< Implements the set union of "this" with "union_with". + /*!< This returns the set formed from the union of "this" set with the set + specified in "union_with". (unfortunately, the name "set_union" must + be used to distinguish from the C keyword "union".) */ + + void unionize(const set &union_with); + //!< Makes "this" set a union of "this" and "union_with". + + set operator + (const set &uw) const { return set_union(uw); } + //!< A synonym for set_union. + + set intersection(const set &intersect_with) const; + //!< Returns the intersection of "this" with the set in "intersect_with". + + set operator * (const set &iw) const { return intersection(iw); } + //!< A synonym for intersection. + + set difference(const set &differ_with) const; + //!< Returns the difference of this with "differ_with". + /*!< Difference is defined as the subset of elements in "this" that are not + also in "differ_with". */ + + void differentiate(const set &differ_with); + //!< Makes "this" set equal to the difference of "this" and "differ_with". + /*!< That is, after the call, "this" will only contain elements that were + not also in "differ_with". */ + + set operator - (const set &dw) const { return difference(dw); } + //!< A synonym for difference. + + int find(const contents &to_find) const; + //!< Returns the integer index of the item "to_find" in this set. + /*!< This returns a negative number if the index cannot be found. Note + that this only makes sense within our particular implementation of set as + an array. */ + + //! Zaps the entry at the specified "index". + /*! This also treats the set like an array. The index must be within the + bounds of the existing members. */ + bool remove_index(int index) + { return this->zap(index, index) == basis::common::OKAY; } +}; + +////////////// + +//! provides a way to pack any set that stores packable objects. +template +void pack(basis::byte_array &packed_form, const set &to_pack) +{ + obscure_attach(packed_form, to_pack.elements()); + for (int i = 0; i < to_pack.elements(); i++) to_pack[i].pack(packed_form); +} + +//! provides a way to unpack any set that stores packable objects. +template +bool unpack(basis::byte_array &packed_form, set &to_unpack) +{ + to_unpack.clear(); + basis::un_int len; + if (!obscure_detach(packed_form, len)) return false; + contents to_fill; + for (int i = 0; i < (int)len; i++) { + if (!to_fill.unpack(packed_form)) return false; + to_unpack.add(to_fill); + } + return true; +} + +////////////// + +//! A simple object that wraps a templated set of ints. +class int_set : public set, public virtual basis::root_object +{ +public: + int_set() {} + //!< Constructs an empty set of ints. + int_set(const set &to_copy) : set(to_copy) {} + //!< Constructs a copy of the "to_copy" array. + + DEFINE_CLASS_NAME("int_set"); +}; + +//! A simple object that wraps a templated set of strings. +class string_set : public set, public virtual basis::packable +{ +public: + string_set() {} + //!< Constructs an empty set of strings. + string_set(const set &to_copy) + : set(to_copy) {} + //!< Constructs a copy of the "to_copy" array. + string_set(const string_array &to_copy) { + for (int i = 0; i < to_copy.length(); i++) + add(to_copy[i]); + } + + DEFINE_CLASS_NAME("string_set"); + + bool operator == (const string_set &compare) const { + for (int i = 0; i < elements(); i++) + if (get(i) != compare.get(i)) return false; + return true; + } + + operator string_array() const { + string_array to_return; + for (int i = 0; i < length(); i++) + to_return += get(i); + return to_return; + } + + virtual void pack(basis::byte_array &packed_form) const + { structures::pack(packed_form, *this); } + virtual bool unpack(basis::byte_array &packed_form) + { return structures::unpack(packed_form, *this); } + virtual int packed_size() const { + int to_return = sizeof(int) * 2; // length packed in, using obscure. + for (int i = 0; i < length(); i++) + to_return += get(i).length() + 1; + return to_return; + } +}; + +//! A set of pointers that hides the platform's pointer size. +class pointer_set : public set +{ +public: + pointer_set() {} + //!< Constructs an empty set of void pointers. + pointer_set(const set &to_copy) : set(to_copy) + {} + //!< Constructs a copy of the "to_copy" array. +}; + +////////////// + +// implementation for longer functions is below... + +template +bool set::member(const contents &to_test) const +{ + for (int i = 0; i < elements(); i++) + if (to_test == this->get(i)) + return true; + return false; +} + +template +bool set::add(const contents &to_add) +{ + if (member(to_add)) return false; + concatenate(to_add); + return true; +} + +template +int set::find(const contents &to_find) const +{ + for (int i = 0; i < elements(); i++) + if (to_find == this->get(i)) + return i; + return basis::common::NOT_FOUND; +} + +template +bool set::remove(const contents &to_remove) +{ + for (int i = 0; i < elements(); i++) + if (to_remove == this->get(i)) { + this->zap(i, i); + return true; + } + return false; +} + +template +set set::intersection(const set &intersect_with) const +{ + set created(0, NIL, this->flags()); + const set *smaller = this; + const set *larger = &intersect_with; + if (elements() > intersect_with.elements()) { + // switch the smaller one into place. + smaller = &intersect_with; + larger = this; + } + for (int i = 0; i < smaller->length(); i++) + if (larger->member(smaller->get(i))) + created.concatenate(smaller->get(i)); + return created; +} + +template +set set::set_union(const set &union_with) const +{ + set created = *this; + for (int i = 0; i < union_with.elements(); i++) + created.add(union_with.get(i)); + return created; +} + +template +void set::unionize(const set &union_with) +{ + for (int i = 0; i < union_with.elements(); i++) + add(union_with.get(i)); +} + +template +set set::difference(const set &differ_with) const +{ + set created = *this; + for (int i = 0; i < differ_with.elements(); i++) { + if (created.member(differ_with[i])) + created.remove(differ_with[i]); + } + return created; +} + +template +void set::differentiate(const set &differ_with) +{ + for (int i = 0; i < differ_with.elements(); i++) { + if (member(differ_with[i])) + remove(differ_with[i]); + } +} + +} // namespace. + +#endif + diff --git a/core/library/structures/stack.h b/core/library/structures/stack.h new file mode 100644 index 00000000..edf3c6e5 --- /dev/null +++ b/core/library/structures/stack.h @@ -0,0 +1,208 @@ +#ifndef STACK_CLASS +#define STACK_CLASS + +/*****************************************************************************\ +* * +* Name : stack * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1990-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace structures { + +//! An abstraction that represents a stack data structure. +/*! + This behaves like a standard stack of objects, but it additionally allows + access to any item in the stack via an array style bracket operator. +*/ + +template +class stack +{ +public: + enum stack_kinds { BOUNDED, UNBOUNDED }; + + stack(int elements = 0); + //!< Creates a stack with room for the specified number of "elements". + /*!< If "elements" is zero, then the stack is an UNBOUNDED stack that + has no set limit on the number of elements it can contain (besides the + amount of memory available). on an unbounded stack, a result of IS_FULL + will never be returned--instead a memory allocation failure would occur. + If "elements" is greater than zero, then the stack is a BOUNDED stack + which can hold at maximum "elements" number of objects. for bounded + stacks, if there is to be an allocation failure, it will happen at the + time of stack construction, rather than during execution. */ + + stack(const stack &to_copy); + //!< constructs a stack as a copy of "to_copy". + + ~stack(); + //!< destroys anything left on the stack. + + void reset(); + //!< throws out all contents on the stack. + + stack_kinds kind() const { return _kind; } + //!< returns the type of stack that was constructed. + + basis::outcome push(const contents &element); + //!< Enters a new element onto the top of the stack. + /*!< if the stack is too large to add another element, then IS_FULL is + returned. if the element to push is nil, the stack is unchanged and + IS_EMPTY is returned. */ + + basis::outcome pop(); + //!< Removes the top element on the stack. + /*!< If the stack has no elements to be popped off, then IS_EMPTY is + returned. The element that was popped is destroyed. */ + + contents &top(); + //!< Returns the top element from the stack but doesn't change the stack. + /*!< This method does not pop the element! If the stack is empty, then a + bogus contents object is returned. */ + + basis::outcome acquire_pop(contents &to_stuff); + //!< Used to grab the top off of the stack. + /*!< this is basically a call to top() followed by a pop(). if there was + no top, then IS_EMPTY is returned. */ + + int size() const; + //!< returns the size of the stack. + /*!< if the stack is empty, then 0 is returned. */ + + stack &operator =(const stack &to_copy); + //!< makes this stack a copy of "to_copy". + + contents &operator [](int index); + //!< Accesses the item at position "index" in the stack. + /*!< Allows access to the stack in an impure fashion; elements other than + the top can be examined. Efforts to access elements that do not exist + are ignored. The range for the element numbers is as in C and runs + from 0 to size() - 1. */ + + void invert(); + //!< Inverts this stack, meaning that the old bottom is the new top. + + int elements() const; + //!< Returns the number of elements used by the stack. + /*!< For a bounded stack, this returns the number of elements the stack + was constructed to hold. For an unbounded stack, it returns the current + number of elements (which is the same as size()). Note though that it is + different from size() for a bounded size stack! */ + +private: + basis::array _store; //!< holds the contents of the stack. + stack_kinds _kind; //!< the type of stack we've got. + int _valid_fields; //!< count of the number of items actually in use here. +}; + +////////////// + +// implementations below... + +template +stack::stack(int elements) +: _store(elements >= 0? elements : 0), + _kind(_store.length()? BOUNDED : UNBOUNDED), + _valid_fields(0) +{} + +template +stack::stack(const stack &to_copy) +: _store(0), _valid_fields(0) +{ operator = (to_copy); } + +template stack::~stack() {} + +template +int stack::size() const { return _valid_fields; } + +template +void stack::reset() +{ + while (pop() == basis::common::OKAY) {} // pop off elements until all are gone. +} + +template +int stack::elements() const { return _store.length(); } + +template +basis::outcome stack::push(const contents &element) +{ + if (_kind == BOUNDED) { + if (_valid_fields >= elements()) return basis::common::IS_FULL; + basis::outcome result = _store.put(_valid_fields, element); + if (result != basis::common::OKAY) return basis::common::IS_FULL; + } else _store.concatenate(element); + _valid_fields++; + return basis::common::OKAY; +} + +template +basis::outcome stack::pop() +{ + if (_valid_fields < 1) return basis::common::IS_EMPTY; + if (_kind == UNBOUNDED) + _store.zap(_store.length() - 1, _store.length() - 1); + _valid_fields--; + return basis::common::OKAY; +} + +template +contents &stack::top() +{ return _store[_valid_fields - 1]; } + +template +stack &stack::operator = (const stack &to_copy) +{ + if (this == &to_copy) return *this; + reset(); + _kind = to_copy._kind; + _store.reset(to_copy._store.length()); + for (int i = 0; i < to_copy._store.length(); i++) + _store.put(i, to_copy._store[i]); + _valid_fields = to_copy._valid_fields; + return *this; +} + +template +void stack::invert() +{ + for (int i = 0; i < _store.length() / 2; i++) { + contents hold = _store.get(i); + int exchange_index = _store.length() - i - 1; + _store.put(i, _store.get(exchange_index)); + _store.put(exchange_index, hold); + } +} + +template +contents &stack::operator [](int index) +{ + if (index >= _valid_fields) index = -1; // force a bogus return. + return _store[index]; +} + +template +basis::outcome stack::acquire_pop(contents &to_stuff) +{ + if (!_valid_fields) return basis::common::IS_EMPTY; + to_stuff = _store[_valid_fields - 1]; + if (_kind == UNBOUNDED) _store.zap(elements()-1, elements()-1); + _valid_fields--; + return basis::common::OKAY; +} + +} //namespace. + +#endif + diff --git a/core/library/structures/static_memory_gremlin.cpp b/core/library/structures/static_memory_gremlin.cpp new file mode 100644 index 00000000..b84bbb69 --- /dev/null +++ b/core/library/structures/static_memory_gremlin.cpp @@ -0,0 +1,250 @@ +/*****************************************************************************\ +* * +* Name : static_memory_gremlin * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "static_memory_gremlin.h" + +#include + +#include +//temp! needed for fake continuable error etc + +#include +#include + +using namespace basis; + +namespace structures { + +//#define DEBUG_STATIC_MEMORY_GREMLIN + // comment this out to eliminate debugging print-outs. otherwise they + // are controlled by the class interface. + +//#define SKIP_STATIC_CLEANUP + // don't uncomment this unless you want all static objects to be left + // allocated on shutdown of the program. that's very sloppy but may + // sometimes be needed for testing. + +////////////// + +const int SMG_CHUNKING_FACTOR = 32; + // we'll allocate this many indices at a time. + +////////////// + +static bool __global_program_is_dying = false; + // this is set to true when no more logging or access to static objects + // should be allowed. + +////////////// + +class gremlin_object_record +{ +public: + root_object *c_object; + const char *c_name; +}; + +////////////// + +static_memory_gremlin::static_memory_gremlin() +: c_lock(), + c_top_index(0), + c_actual_size(0), + c_pointers(NIL), + c_show_debugging(false) +{ + ensure_space_exists(); +} + +static_memory_gremlin::~static_memory_gremlin() +{ + __global_program_is_dying = true; + // now the rest of the program is on notice; we're practically gone. + +#ifdef DEBUG_STATIC_MEMORY_GREMLIN + if (c_show_debugging) + printf("SMG: beginning static object shutdown...\n"); +#endif + +#ifndef SKIP_STATIC_CLEANUP + // clean up any allocated pointers in reverse order of addition. + while (c_top_index > 0) { + // make sure we fixate on which guy is shutting down. some new ones + // could be added on the end of the list as a result of this destruction. + int zapped_index = c_top_index - 1; + gremlin_object_record *ptr = c_pointers[zapped_index]; + c_pointers[zapped_index] = NIL; + // since we know the one we're zapping, we no longer need that index. + c_top_index--; + // this should allow us to keep chewing on items that are newly being + // added during static shutdown, since we have taken the current object + // entirely out of the picture. + if (ptr) { +#ifdef DEBUG_STATIC_MEMORY_GREMLIN + if (c_show_debugging) + printf((astring("SMG: deleting ") + ptr->c_object->instance_name() + + " called " + ptr->c_name + + a_sprintf(" at index %d.\n", zapped_index) ).s()); +#endif + WHACK(ptr->c_object); + WHACK(ptr); + } + } +#endif + delete [] c_pointers; + c_pointers = NIL; +} + +bool static_memory_gremlin::__program_is_dying() { return __global_program_is_dying; } + +mutex &static_memory_gremlin::__memory_gremlin_synchronizer() +{ + static mutex __globabl_synch_mem; + return __globabl_synch_mem; +} + +int static_memory_gremlin::locate(const char *unique_name) +{ + auto_synchronizer l(c_lock); + for (int i = 0; i < c_top_index; i++) { + if (!strcmp(c_pointers[i]->c_name, unique_name)) return i; + } + return common::NOT_FOUND; +} + +root_object *static_memory_gremlin::get(const char *unique_name) +{ + auto_synchronizer l(c_lock); + int indy = locate(unique_name); + if (negative(indy)) return NIL; + return c_pointers[indy]->c_object; +} + +const char *static_memory_gremlin::find(const root_object *ptr) +{ + auto_synchronizer l(c_lock); + for (int i = 0; i < c_top_index; i++) { + if (ptr == c_pointers[i]->c_object) + return c_pointers[i]->c_name; + } + return NIL; +} + +bool static_memory_gremlin::put(const char *unique_name, root_object *to_put) +{ + auto_synchronizer l(c_lock); + int indy = locate(unique_name); + // see if that name already exists. + if (non_negative(indy)) { +#ifdef DEBUG_STATIC_MEMORY_GREMLIN + if (c_show_debugging) + printf((astring("SMG: cleaning out old object ") + + c_pointers[indy]->c_object->instance_name() + + " called " + c_pointers[indy]->c_name + + " in favor of object " + to_put->instance_name() + + " called " + unique_name + + a_sprintf(" at index %d.\n", indy)).s()); +#endif + WHACK(c_pointers[indy]->c_object); + c_pointers[indy]->c_object = to_put; + return true; + } +#ifdef DEBUG_STATIC_MEMORY_GREMLIN + if (c_show_debugging) + printf((astring("SMG: storing ") + to_put->instance_name() + + " called " + unique_name + + a_sprintf(" at index %d.\n", c_top_index)).s()); +#endif + ensure_space_exists(); + c_pointers[c_top_index] = new gremlin_object_record; + c_pointers[c_top_index]->c_object = to_put; + c_pointers[c_top_index]->c_name = unique_name; + c_top_index++; + return true; +} + +void static_memory_gremlin::ensure_space_exists() +{ + auto_synchronizer l(c_lock); + if (!c_pointers || (c_top_index + 1 >= c_actual_size) ) { + // never had any contents yet or not enough space exists. +#ifdef DEBUG_STATIC_MEMORY_GREMLIN + if (c_show_debugging) + printf(a_sprintf("SMG: adding space for top at %d.\n", c_top_index).s()); +#endif + c_actual_size += SMG_CHUNKING_FACTOR; + typedef gremlin_object_record *base_ptr; + gremlin_object_record **new_ptr = new base_ptr[c_actual_size]; + if (!new_ptr) { + throw "error: static_memory_gremlin::ensure_space_exists: failed to allocate memory for pointer list"; + } + for (int i = 0; i < c_actual_size; i++) new_ptr[i] = NIL; + for (int j = 0; j < c_actual_size - SMG_CHUNKING_FACTOR; j++) + new_ptr[j] = c_pointers[j]; + if (c_pointers) delete [] c_pointers; + c_pointers = new_ptr; + } +} + +// this function ensures that the space for the global objects is kept until +// the program goes away. if it's the first time through, then the gremlin +// gets created; otherwise the existing one is used. this function should +// always be called by the main program before any attempts to use global +// features like SAFE_STATIC or the program wide logger. it is crucial that no +// user-level threads have been created in the program before this is called. +static_memory_gremlin &static_memory_gremlin::__hoople_globals() +{ + static bool _initted = false; // tells whether we've gone through yet. + static static_memory_gremlin *_internal_gremlin = NIL; + // holds our list of shared pieces... + + if (!_initted) { +#ifdef DEBUG_STATIC_MEMORY_GREMLIN + printf("%s: initializing HOOPLE_GLOBALS now.\n", _global_argv[0]); +#endif + +#ifdef ENABLE_MEMORY_HOOK + void *temp = program_wide_memories().provide_memory(1, __FILE__, __LINE__); + // invoke now to get memory engine instantiated. + program_wide_memories().release_memory(temp); // clean up junk. +#endif + +#ifdef ENABLE_CALLSTACK_TRACKING + program_wide_stack_trace().full_trace_size(); + // invoke now to get callback tracking instantiated. +#endif + FUNCDEF("HOOPLE_GLOBALS remainder"); + // this definition must be postponed until after the objects that would + // track it actually exist. + if (func) {} // shut up the compiler about using it. + + // this simple approach is not going to succeed if the SAFE_STATIC macros + // are used in a static library which is then used in more than one dynamic + // library on win32. this is because each dll in win32 will have a + // different version of certain static objects that should only occur once + // per program. this problem is due to the win32 memory model, but in + // hoople at least this has been prevented; our only static library that + // appears in a bunch of dlls is basis and it is not allowed to use the + // SAFE_STATIC macro. + _internal_gremlin = new static_memory_gremlin; + _initted = true; + } + + return *_internal_gremlin; +} + +////////////// + +} // namespace. + diff --git a/core/library/structures/static_memory_gremlin.h b/core/library/structures/static_memory_gremlin.h new file mode 100644 index 00000000..e53ee6d9 --- /dev/null +++ b/core/library/structures/static_memory_gremlin.h @@ -0,0 +1,199 @@ +#ifndef STATIC_MEMORY_GREMLIN_CLASS +#define STATIC_MEMORY_GREMLIN_CLASS + +/*****************************************************************************\ +* * +* Name : static_memory_gremlin * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace structures { + +// forward declarations. +class gremlin_object_record; + +//! Holds onto memory chunks that are allocated globally within the program. +/*! + The objects managed by the gremlin do not get destroyed until after the + program begins shutting down. This file also provides the SAFE_STATIC macros + that can be used for allocating static objects safely in a multi-threaded + program. +*/ + +class static_memory_gremlin : public virtual basis::nameable +{ +public: + static_memory_gremlin(); + + ~static_memory_gremlin(); + /*!< when destroyed, it is assumed that the program's lifetime is over and + all objects stored here are now unnecessary. this implements a + regenerative scheme for when static shutdowns occur out of order; if an + object has already been destroyed, it is recreated for the purposes of + other statics being shutdown. eventually this should stabilize since + it's completely unacceptable for static objects to depend on each other + in a cycle. */ + + DEFINE_CLASS_NAME("static_memory_gremlin"); + + static bool __program_is_dying(); + //!< Reports whether the program is shutting down currently. + /*!< If this flag is true, then code should generally just return without + doing anything where possible. It's especially important for long + running loops or active threads to stop if they see this, although + the normal program exit processes usually make it unnecessary. */ + + static static_memory_gremlin &__hoople_globals(); + //!< Holds onto objects that have been allocated in a program-wide scope. + /*!< These objects will have a lifetime up to the point of normal static + object destruction and then they will be cleaned up. */ + + static basis::mutex &__memory_gremlin_synchronizer(); + //!< private object for static_memory_gremlin's use only. + + void enable_debugging(bool verbose) { c_show_debugging = verbose; } + //!< if "verbose" is true, then the object will produce a noisy log. + + bool put(const char *unique_name, basis::root_object *ptr); + //!< adds a "ptr" to the set of static objects under the "unique_name". + /*!< the name must really be unique or objects will collide. we recommend + using an identifier based on a line number and filename where the static + is going to be placed (see the safe static implementation below). */ + + basis::root_object *get(const char *unique_name); + //!< locates the pointer held for the "unique_name", if any. + /*!< if no pointer exists, then NIL is returned. NOTE: the returned + pointer must not be destroyed, since the object could be used at any time + during the program's lifetime. */ + + const char *find(const basis::root_object *ptr); + //!< locates the name for "ptr" in our objects. + /*!< if it does not exist, then NIL is returned. */ + + void ensure_space_exists(); + /*!< makes sure that the list of objects is large enough to contain all of + the identifiers that have been issued. */ + +private: + basis::mutex c_lock; //!< protects object's state. + int c_top_index; //!< top place that's occupied in the list. + int c_actual_size; //!< the real number of indices in list. + gremlin_object_record **c_pointers; //!< storage for the static pointers. + bool c_show_debugging; //!< if true, then the object will log noisily. + + int locate(const char *unique_name); + //!< returns the index number of the "unique_name". +}; + +////////////// + +//! Statically defines a singleton object whose scope is the program's lifetime. +/*! + SAFE_STATIC is a macro that dynamically creates a function returning a particular + data type. the object is allocated statically, so that the same object will be + returned ever after until the program is shut down. the allocation is guarded so + that multiple threads cannot create conflicting static objects. + + note: in ms-win32, if you use this macro in a static library (rather than + a DLL), then the heap context is different. thus you could actually have + multiple copies of the underlying object. if the object is supposed to + be global and unique, then that's a problem. relocating the definitions + to a dll while keeping declarations in the static lib works (see the + program wide logger approach in ). + "type" is the object class to return. + "func_name" is the function to be created. + "parms" must be a list of parameters in parentheses or nothing. + + example usage: @code + SAFE_STATIC(connection_table, conntab, (parm1, parm2)) @endcode + will define a function: connection_table &conntab() + that returns a connection table object which has been created safely, + given that the main synchronizer was called from the main thread at least + once. @code + SAFE_STATIC(astring, static_string_doodle, ) @endcode + will define a static astring object named "static_string_doodle" that uses + the default constructor for astrings. +*/ +#define SAFE_STATIC(type, func_name, parms) \ + type &func_name() { SAFE_STATIC_IMPLEMENTATION(type, parms, __LINE__); } + +//! this version returns a constant object instead. +#define SAFE_STATIC_CONST(type, func_name, parms) \ + const type &func_name() \ + { SAFE_STATIC_IMPLEMENTATION(type, parms, __LINE__); } + +//! Statically defines a string for the rest of the program's life. +/*! This macro can be used to make functions returning a string a little +simpler. This can only be used when the string is constant. The usual way +to use this macro is in a function that returns a constant reference to a +string. The macro allocates the string to be returned statically so that all +future calls will refer to the stored string rather than recreating it again. */ +#define STATIC_STRING(str) \ + SAFE_STATIC_IMPLEMENTATION(astring, (str), __LINE__) + +////////////// + +//! this macro creates a unique const char pointer based on file location. +#define UNIQUE_NAME_BASED_ON_SOURCE(name, linenum) \ + static const char *name = "file:" __FILE__ ":line:" #linenum + +//! this blob is just a chunk of macro implementation for SAFE_STATIC... +/*! if the static object isn't initialized yet, we'll create it and store it +in the static_memory_gremlin. we make sure that the program isn't shutting +down, because that imposes a new requirement--previously created statics might +have already been destroyed. thus, during the program shutdown, we carefully +recreate any objects that were already toast. */ +#define SAFE_STATIC_IMPLEMENTATION(type, parms, linenum) \ +/*hmmm: bring all this back.*/ \ +/* const char *func = "allocation"; */ \ +/* frame_tracking_instance __trail_of_function("safe_static", func, \ + __FILE__, __LINE__, true); */ \ + UNIQUE_NAME_BASED_ON_SOURCE(__uid_name, linenum); \ +/* program_wide_memories(); */ \ + static basis::root_object *_hidden = NIL; \ + /* if haven't initialized yet, then definitely need to lock carefully. */ \ + if (structures::static_memory_gremlin::__program_is_dying() || !_hidden) { \ + basis::auto_synchronizer l(structures::static_memory_gremlin::__memory_gremlin_synchronizer()); \ + if (structures::static_memory_gremlin::__program_is_dying()) { \ + /* we can't rely on the pointer since we're shutting down currently. */ \ + _hidden = structures::static_memory_gremlin::__hoople_globals().get(__uid_name); \ + } \ + if (!_hidden) { /* make sure no one scooped us. */ \ + /* try to retrieve an existing one first and use it if there. */ \ + _hidden = structures::static_memory_gremlin::__hoople_globals().get(__uid_name); \ + if (!_hidden) { \ + _hidden = new type parms ; /* create the object finally. */ \ + /* store our object using the unique name for it. */ \ + if (!structures::static_memory_gremlin::__hoople_globals().put(__uid_name, _hidden)) { \ + /* we failed to allocate space. this is serious. */ \ + throw __uid_name; \ + } \ + } \ + } \ + } \ + if (!_hidden) { \ + /* grab the pointer that was stored, in case we're late getting here. */ \ + /* another thread might have scooped the object creation. */ \ + _hidden = structures::static_memory_gremlin::__hoople_globals().get(__uid_name); \ + } \ + return *dynamic_cast(_hidden) + +// historical note: the SAFE_STATIC approach has existed since about 1998. +// however, the static_memory_gremlin's role in this started much later. + +} //namespace. + +#endif + diff --git a/core/library/structures/string_array.h b/core/library/structures/string_array.h new file mode 100644 index 00000000..22d5c3f4 --- /dev/null +++ b/core/library/structures/string_array.h @@ -0,0 +1,127 @@ +#ifndef STRING_ARRAY_CLASS +#define STRING_ARRAY_CLASS + +/*****************************************************************************\ +* * +* Name : string_array * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1993-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "object_packers.h" + +#include +#include +#include + +namespace structures { + +//! An array of strings with some additional helpful methods. + +class string_array +: public basis::array, + public virtual basis::packable, + public virtual basis::equalizable +{ +public: + string_array(int number = 0, const basis::astring *initial_contents = NIL) + : basis::array(number, initial_contents, + EXPONE | FLUSH_INVISIBLE) {} + //!< Constructs an array of "number" strings. + /*!< creates a list of strings based on an initial "number" of entries and + some "initial_contents", which should be a regular C array of astrings + with at least as many entries as "number". */ + + //! a constructor that operates on an array of char pointers. + /*! be very careful with the array to ensure that the right number of + elements is provided. */ + string_array(int number, const char *initial_contents[]) + : basis::array(number, NIL, EXPONE | FLUSH_INVISIBLE) { + for (int i = 0; i < number; i++) { + put(i, basis::astring(initial_contents[i])); + } + } + + string_array(const basis::array &to_copy) + : basis::array(to_copy) {} + //!< copy constructor that takes a templated array of astring. + + DEFINE_CLASS_NAME("string_array"); + + //! Prints out a formatted view of the contained strings and returns it. + basis::astring text_format(const basis::astring &separator = ",", + const basis::astring &delimiter = "\"") const { + basis::astring to_return; + for (int i = 0; i < length(); i++) { + to_return += delimiter; + to_return += get(i); + to_return += delimiter; + if (i < last()) + to_return += separator; + } + return to_return; + } + + basis::astring text_form() const { return text_format(); } + //!< A synonym for the text_format() method. + +//hmmm: extract as re-usable equality operation. + + //! Compares this string array for equality with "to_compare". + bool equal_to(const equalizable &to_compare) const { + const string_array *cast = cast_or_throw(to_compare, *this); + if (length() != cast->length()) + return false; + for (int i = 0; i < length(); i++) + if (cast->get(i) != get(i)) + return false; + return true; + } + + //! locates string specified and returns its index, or negative if missing. + int find(const basis::astring &to_find) const { + for (int i = 0; i < length(); i++) { + if (to_find.equal_to(get(i))) return i; + } + return basis::common::NOT_FOUND; + } + + //! Returns true if all of the elements in this are the same in "second". + /*! The array "second" can have more elements, but must have all of the + items listed in this string array. */ + bool prefix_compare(const string_array &second) const { + if (second.length() < length()) return false; + if (!length()) return false; + for (int i = 0; i < length(); i++) + if ((*this)[i] != second[i]) return false; + return true; + } + + //! Packs this string array into the "packed_form" byte array. + virtual void pack(basis::byte_array &packed_form) const + { pack_array(packed_form, *this); } + + //! Unpacks a string array from the "packed_form" byte array. + virtual bool unpack(basis::byte_array &packed_form) + { return unpack_array(packed_form, *this); } + + //! Returns the number of bytes this string array would consume if packed. + virtual int packed_size() const { + int to_return = sizeof(int) * 2; // length packed in, using obscure. + for (int i = 0; i < length(); i++) + to_return += get(i).length() + 1; + return to_return; + } +}; + +} //namespace. + +#endif + diff --git a/core/library/structures/string_hash.h b/core/library/structures/string_hash.h new file mode 100644 index 00000000..9a75ba9c --- /dev/null +++ b/core/library/structures/string_hash.h @@ -0,0 +1,40 @@ +#ifndef STRING_HASH_CLASS +#define STRING_HASH_CLASS + +/*****************************************************************************\ +* * +* Name : string_hash * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "hash_table.h" +#include "string_hasher.h" + +#include + +namespace structures { + +//! Implements a hash table indexed on character strings. + +template +class string_hash : public hash_table +{ +public: + string_hash(int estimated_elements) + : hash_table(astring_hasher(), estimated_elements) {} + + ~string_hash() {} +}; + +} //namespace. + +#endif // outer guard. + diff --git a/core/library/structures/string_hasher.cpp b/core/library/structures/string_hasher.cpp new file mode 100644 index 00000000..0762e794 --- /dev/null +++ b/core/library/structures/string_hasher.cpp @@ -0,0 +1,87 @@ +/*****************************************************************************\ +* * +* Name : string_hasher * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "string_hasher.h" + +#include +#include + +using namespace basis; + +namespace structures { + +const int MAX_STRING_CHARS_USED = 3; + // we use this many characters from the string in question (if they + // exist) at each end of the string. + +////////////// + +hashing_algorithm *string_hasher::clone() const +{ return new string_hasher; } + +basis::un_int string_hasher::hash(const void *key_data, int key_length_in) const +{ + if (!key_data) return 0; // error! + if (key_length_in <= 1) return 0; // ditto! + + abyte *our_key = (abyte *)key_data; + abyte hashed[4] = { 0, 0, 0, 0 }; + + int key_length = minimum(key_length_in - 1, MAX_STRING_CHARS_USED); + + int fill_posn = 0; + // add the characters from the beginning of the string. + for (int i = 0; i < key_length; i++) { + // add to the primary area. + hashed[fill_posn] = hashed[fill_posn] + our_key[i]; + fill_posn++; + if (fill_posn >= 4) fill_posn = 0; + // add to the secondary area (the next in rotation after primary). + hashed[fill_posn] = hashed[fill_posn] + (our_key[i] / 4); + } + // add the characters from the end of the string. + for (int k = key_length_in - 2; + (k >= 0) && (k >= key_length_in - 1 - key_length); k--) { + // add to the primary area. + hashed[fill_posn] = hashed[fill_posn] + our_key[k]; + fill_posn++; + if (fill_posn >= 4) fill_posn = 0; + // add to the secondary area (the next in rotation after primary). + hashed[fill_posn] = hashed[fill_posn] + (our_key[k] / 4); + } + + basis::un_int to_return = 0; + for (int j = 0; j < 4; j++) to_return = (to_return << 8) + hashed[j]; + return to_return; +} + +////////////// + +hashing_algorithm *astring_hasher::clone() const +{ return new astring_hasher; } + +basis::un_int astring_hasher::hash(const void *key_data, int key_length_in) const +{ + if (!key_data) return 0; // error. + const astring *real_key = (const astring *)key_data; + if (real_key->length() + 1 != key_length_in) { +// printf("differing key lengths, string len=%d, key len=%d\n", +// real_key->length() + 1, key_length_in); + } + return string_hasher().hash((const void *)real_key->observe(), + real_key->length() + 1); +} + +} //namespace. + diff --git a/core/library/structures/string_hasher.h b/core/library/structures/string_hasher.h new file mode 100644 index 00000000..87e45e9b --- /dev/null +++ b/core/library/structures/string_hasher.h @@ -0,0 +1,54 @@ +#ifndef STRING_HASHER_CLASS +#define STRING_HASHER_CLASS + +/*****************************************************************************\ +* * +* Name : string_hasher * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "hash_table.h" + +namespace structures { + +//! Implements a simple hashing algorithm for strings. +/*! This uses a portion of the string's contents to create a hash value. */ + +class string_hasher : public virtual hashing_algorithm +{ +public: + virtual basis::un_int hash(const void *key_data, int key_length) const; + //!< returns a value that can be used to index into a hash table. + /*!< the returned value is loosely based on the "key_data" and the + "key_length" we are provided with. it is expected that the "key_data" + really is a 'char' pointer whose length is "key_length" (including the + zero terminator at the end). */ + + virtual hashing_algorithm *clone() const; + //!< implements cloning of the algorithm object. +}; + +////////////// + +class astring_hasher : public virtual hashing_algorithm +{ +public: + virtual basis::un_int hash(const void *key_data, int key_length) const; + //!< similar to string_hasher, but expects "key_data" as an astring pointer. + + virtual hashing_algorithm *clone() const; + //!< implements cloning of the algorithm object. +}; + +} //namespace. + +#endif + diff --git a/core/library/structures/string_table.cpp b/core/library/structures/string_table.cpp new file mode 100644 index 00000000..fb0b7f52 --- /dev/null +++ b/core/library/structures/string_table.cpp @@ -0,0 +1,110 @@ +/*****************************************************************************\ +* * +* Name : string_table * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "object_packers.h" +#include "string_table.h" +#include "symbol_table.h" + +#include +#include + +using namespace basis; + +namespace structures { + +string_table::string_table(const string_table &to_copy) +: symbol_table(to_copy.estimated_elements()), + _add_spaces(false) +{ + *this = to_copy; +} + +string_table::~string_table() {} + +bool string_table::is_comment(const astring &to_check) +{ return to_check.begins(STRTAB_COMMENT_PREFIX); } + +string_table &string_table::operator = (const string_table &to_copy) +{ + if (this == &to_copy) return *this; + (symbol_table &)*this = (const symbol_table &)to_copy; + _add_spaces = to_copy._add_spaces; + return *this; +} + +astring string_table::text_form() const +{ + astring output; + const char *space_char = ""; + if (_add_spaces) space_char = " "; + for (int i = 0; i < symbols(); i++) { + if (is_comment(name(i))) + output += a_sprintf("%s\n", operator[](i).s()); + else + output += a_sprintf("%s%s=%s%s\n", name(i).s(), space_char, + space_char, operator[](i).s()); + } + return output; +} + +bool string_table::operator ==(const string_table &to_compare) const +{ + if (to_compare.symbols() != symbols()) return false; + for (int i = 0; i < symbols(); i++) { + const astring &key = name(i); + astring *str1 = find(key); + astring *str2 = to_compare.find(key); + if (!str2) return false; + if (*str1 != *str2) return false; + } + return true; +} + +int string_table::packed_size() const +{ + int size = sizeof(int); + for (int i = 0; i < symbols(); i++) { + size += name(i).length(); + size += operator[](i).length(); + } + return size; +} + +void string_table::pack(byte_array &packed_form) const +{ + structures::attach(packed_form, symbols()); + for (int i = 0; i < symbols(); i++) { + name(i).pack(packed_form); + operator[](i).pack(packed_form); + } +} + +bool string_table::unpack(byte_array &packed_form) +{ + reset(); + int syms; + if (!structures::detach(packed_form, syms)) return false; + for (int i = 0; i < syms; i++) { + astring name, content; + if (!name.unpack(packed_form)) return false; + if (!content.unpack(packed_form)) return false; + outcome ret = add(name, content); + if (ret != common::IS_NEW) return false; + } + return true; +} + +} //namespace. + + diff --git a/core/library/structures/string_table.h b/core/library/structures/string_table.h new file mode 100644 index 00000000..611c6258 --- /dev/null +++ b/core/library/structures/string_table.h @@ -0,0 +1,82 @@ +#ifndef STRING_TABLE_CLASS +#define STRING_TABLE_CLASS + +/*****************************************************************************\ +* * +* Name : string_table * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "symbol_table.h" + +#include +#include + +namespace structures { + +//! Provides a symbol_table that holds strings as the content. +/*! This is essentially a table of named strings. */ + +class string_table +: public symbol_table, + public virtual basis::packable, + public virtual basis::hoople_standard +{ +public: + string_table(int estimated_elements = 100) : symbol_table(estimated_elements), + _add_spaces(false) {} + //!< the "estimated_elements" specifies how many items to prepare to efficiently hold. + string_table(const string_table &to_copy); + virtual ~string_table(); + + DEFINE_CLASS_NAME("string_table"); + + string_table &operator = (const string_table &to_copy); + + bool operator ==(const string_table &to_compare) const; + + virtual bool equal_to(const equalizable &to_compare) const { + const string_table *cast = dynamic_cast(&to_compare); + if (!cast) return false; + return operator ==(*cast); + } + + #define STRTAB_COMMENT_PREFIX "#comment#" + //!< anything beginning with this is considered a comment. + /*!< a numerical uniquifier should be appended to the string to ensure that + multiple comments can be handled per table. */ + + static bool is_comment(const basis::astring &to_check); + + basis::astring text_form() const; + //!< prints the contents of the table into the returned string. + /*!< if names in the table start with the comment prefix (see above), then + they will not be printed as "X=Y" but instead as just "Y". */ + + virtual void text_form(basis::base_string &fill) const { fill = text_form(); } + + // dictates whether the output will have spaces between the assignment + // character and the key name and value. default is to not add them. + bool add_spaces() const { return _add_spaces; } + void add_spaces(bool add_them) { _add_spaces = add_them; } + + virtual int packed_size() const; + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); + +private: + bool _add_spaces; // records whether we add spaces around the assignment. +}; + +} //namespace. + +#endif + diff --git a/core/library/structures/symbol_table.h b/core/library/structures/symbol_table.h new file mode 100644 index 00000000..ee4bc246 --- /dev/null +++ b/core/library/structures/symbol_table.h @@ -0,0 +1,467 @@ +#ifndef SYMBOL_TABLE_CLASS +#define SYMBOL_TABLE_CLASS + +/*****************************************************************************\ +* * +* Name : symbol_table * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "pointer_hash.h" +#include "string_hash.h" +#include "symbol_table.h" + +#include +#include +#include + +namespace structures { + +template class internal_symbol_indexer; +template class internal_symbol_info; +template class internal_symbol_list; + +//! Maintains a list of names, where each name has a type and some contents. + +template +class symbol_table +{ +public: + //! constructs a symbol table with sufficient size for "estimated_elements". + /*! the "estimated_elements" dictates how large the symbol table's key space is. the + number of keys that can be stored without collisions (assuming perfect distribution + from the hash function) will be close to the number of elements specified. */ + symbol_table(int estimated_elements = 100); + + symbol_table(const symbol_table &to_copy); + + ~symbol_table(); + + int symbols() const; + //!< returns the number of symbols listed in the table. + + int estimated_elements() const; + //!< returns the number of symbols the table is optimized for. + + void rehash(int estimated_elements); + //!< resizes underlying table to support "estimated_elements". + + void hash_appropriately(int estimated_elements); + //!< Resizes the number of table slots to have space for "estimated_elements". + + symbol_table &operator =(const symbol_table &to_copy); + + basis::outcome add(const basis::astring &name, const contents &storage); + //!< Enters a symbol name into the table along with some contents. + /*!< If the name already exists in the table, then the previous contents + are replaced with these, but EXISTING is returned. If this is a new entry, + then IS_NEW is returned instead. */ + + const basis::astring &name(int index) const; + //!< returns the name held at the "index". + /*!< if the index is invalid, then a bogus name is returned. */ + + void names(string_set &to_fill) const; + //!< returns the names of all the symbols currently held. + + contents &operator [] (int index); + //!< provides access to the symbol_table's contents at the "index". + const contents &operator [] (int index) const; + //!< provides a constant peek at the contents at the "index". + + const contents &get(int index) const { return operator[](index); } + //!< named equivalent for the bracket operator. + contents &use(int index) { return operator[](index); } + //!< named equivalent for the bracket operator. + + contents *find(const basis::astring &name) const; + //!< returns the contents held for "name" or NIL if it wasn't found. + + contents *find(const basis::astring &name, + basis::string_comparator_function *comparator) const; + //!< Specialized search via a comparison method "comparator". + /*!< Searches for a symbol by its "name" but uses a special comparison + function "comparator" to determine if the name is really equal. This + method is by its nature slower than the main find method, since all buckets + must be searched until a match is found. It is just intended to provide + extensibility. */ + + int dep_find(const basis::astring &name) const; + //!< Searches for a symbol by its "name". + /*!< Returns the index or NOT_FOUND. NOTE: this is deprecated; it is far + faster to use the first find method above. */ + + //! Locates the symbol at position "index" and stores it to the parameters. + /*! retrieve() accesses the "index"th symbol in the table and returns the + held contents for it as well as its name. if the outcome is OKAY, then + the returned information is valid. otherwise, the search failed. */ + basis::outcome retrieve(int index, basis::astring &symbol_name, contents &contains) const; + + basis::outcome whack(const basis::astring &name); + //!< removes a symbol from the table. + /*!< this succeeds only if the symbol name is present in the table. */ + + basis::outcome zap_index(int index); + //!< zaps the entry at the specified index. slower than whack(). + + void reset(); // *_symbol_list; //!< our table of symbols. + internal_symbol_indexer *_tracker; //!< indexed lookup support. + + internal_symbol_info *get_index(int index) const; + //!< returns the info item at "index" or NIL. +}; + +////////////// + +template +bool symbol_table_compare(const symbol_table &a, + const symbol_table &b); + //!< returns true if table "a" and table "b" have the same contents. + +////////////// + +// implementations below... + +//#define DEBUG_SYMBOL_TABLE + // uncomment for noisier debugging. + +#ifdef DEBUG_SYMBOL_TABLE + #undef LOG + #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) +#endif + +////////////// + +// this structure keeps track of our symbol table elements. + +template +class internal_symbol_info +{ +public: + contents _content; // the object provided by the user. + basis::astring _name; // symbol's name. + + internal_symbol_info(const contents &content, const basis::astring &name) + : _content(content), _name(name) {} +}; + +////////////// + +// this is our tracking system that allows us to offer array like services. +// each symbol held has an address as one of our wrappers. thus we can +// list those addresses in a hash table along with listing the contents +// in the main hash table. the pointer_hash gives us a list of ids set, so +// we can have some ordering for the items in the table. + +template +class internal_pointer_hider +{ +public: + internal_symbol_info *_held; + + internal_pointer_hider(internal_symbol_info *held) : _held(held) {} +}; + +template +class internal_symbol_indexer +: public pointer_hash > +{ +public: + internal_symbol_indexer(int elems) + : pointer_hash >(elems) {} +}; + +////////////// + +// this object maintains the symbol table's contents. + +template +class internal_symbol_list +: public string_hash > +{ +public: + internal_symbol_list(int elems) + : string_hash >(elems) {} +}; + +////////////// + +// the main algorithms class implementing the external interface. + +#undef static_class_name +#define static_class_name() "symbol_table" + +template +symbol_table::symbol_table(int estimated_elements) +: _symbol_list(new internal_symbol_list(estimated_elements)), + _tracker(new internal_symbol_indexer(estimated_elements)) +{} + +template +symbol_table::symbol_table(const symbol_table &to_copy) +: _symbol_list(new internal_symbol_list + (to_copy._symbol_list->estimated_elements())), + _tracker(new internal_symbol_indexer(to_copy.estimated_elements())) +{ *this = to_copy; } + +template +symbol_table::~symbol_table() +{ + WHACK(_symbol_list); + WHACK(_tracker); +} + +template +int symbol_table::estimated_elements() const +{ return _symbol_list->estimated_elements(); } + +template +void symbol_table::rehash(int estimated_elements) +{ + _symbol_list->rehash(estimated_elements); + _tracker->rehash(estimated_elements); +} + +template +void symbol_table::hash_appropriately(int new_elements) +{ rehash(new_elements); } + +template +int symbol_table::symbols() const +{ return _tracker->ids().elements(); } + +template +symbol_table &symbol_table:: + operator =(const symbol_table &to_copy) +{ + if (this == &to_copy) return *this; + reset(); + for (int i = 0; i < to_copy.symbols(); i++) { + internal_symbol_info *info = to_copy.get_index(i); + if (info) { + internal_symbol_info *new_info + = new internal_symbol_info(*info); + _symbol_list->add(info->_name, new_info); + internal_pointer_hider *new_track = + new internal_pointer_hider(new_info); + _tracker->add(new_info, new_track); + } + } + return *this; +} + +template +void symbol_table::reset() +{ + _symbol_list->reset(); + _tracker->reset(); +} + +template +const basis::astring &symbol_table::name(int index) const +{ + bounds_return(index, 0, symbols() - 1, basis::bogonic()); + return get_index(index)->_name; +} + +template +void symbol_table::names(string_set &to_fill) const +{ + to_fill.reset(); + for (int i = 0; i < symbols(); i++) + to_fill += get_index(i)->_name; +} + +template +internal_symbol_info *symbol_table:: + get_index(int index) const +{ + bounds_return(index, 0, symbols() - 1, NIL); + return _tracker->find(_tracker->ids()[index])->_held; +} + +template +const contents &symbol_table::operator [] (int index) const +{ + bounds_return(index, 0, symbols() - 1, basis::bogonic()); + internal_symbol_info *found = get_index(index); + if (!found) return basis::bogonic(); + return found->_content; +} + +template +contents &symbol_table::operator [] (int index) +{ + bounds_return(index, 0, symbols() - 1, basis::bogonic()); + internal_symbol_info *found = get_index(index); + if (!found) return basis::bogonic(); + return found->_content; +} + +template +contents *symbol_table::find(const basis::astring &name) const +{ +// FUNCDEF("find [name]"); + internal_symbol_info *found = _symbol_list->find(name); + if (!found) return NIL; + return &found->_content; +} + +template +int symbol_table::dep_find(const basis::astring &name) const +{ + internal_symbol_info *entry = _symbol_list->find(name); + if (!entry) return basis::common::NOT_FOUND; + + for (int i = 0; i < symbols(); i++) { + if (_tracker->ids()[i] == entry) return i; + } + return basis::common::NOT_FOUND; // this is bad; it should have been found. +} + +template +struct sym_tab_apply_data +{ + basis::string_comparator_function *_scf; + contents *_found; + basis::astring _to_find; + + sym_tab_apply_data() : _scf(NIL), _found(NIL) {} +}; + +template +bool sym_tab_finder_apply(const basis::astring &key, contents ¤t, + void *data_link) +{ +#ifdef DEBUG_SYMBOL_TABLE + FUNCDEF("sym_tab_finder_apply"); +#endif + sym_tab_apply_data *package + = (sym_tab_apply_data *)data_link; +#ifdef DEBUG_SYMBOL_TABLE + LOG(basis::astring(" checking ") + key); +#endif + bool equals = package->_scf(key, package->_to_find); + if (equals) { + package->_found = ¤t; + return false; // done. + } + return true; // keep going. +} + +template +contents *symbol_table::find(const basis::astring &name, + basis::string_comparator_function *comparator) const +{ +#ifdef DEBUG_SYMBOL_TABLE + FUNCDEF("find [comparator]"); +#endif + if (!comparator) return find(name); // devolve to simplified call. +#ifdef DEBUG_SYMBOL_TABLE + LOG(basis::astring("looking for ") + name); +#endif + sym_tab_apply_data pack; + pack._to_find = name; + pack._scf = comparator; + // iterate across all the items in the hash. + _symbol_list->apply(sym_tab_finder_apply, &pack); + return pack._found; +} + +template +basis::outcome symbol_table::add(const basis::astring &name, const contents &to_add) +{ +// FUNCDEF("add"); + internal_symbol_info *found = _symbol_list->find(name); + if (!found) { + // not there already. + internal_symbol_info *new_item + = new internal_symbol_info(to_add, name); + _symbol_list->add(name, new_item); + internal_pointer_hider *new_track = + new internal_pointer_hider(new_item); + _tracker->add(new_item, new_track); + return basis::common::IS_NEW; + } + // overwrite the existing contents. + found->_content = to_add; + return basis::common::EXISTING; +} + +template +basis::outcome symbol_table::zap_index(int index) +{ + basis::astring dead_name = name(index); + return whack(dead_name); +} + +template +basis::outcome symbol_table::whack(const basis::astring &name) +{ +// FUNCDEF("whack"); + internal_symbol_info *sep_ind = _symbol_list->find(name); + // we need this pointer so we can find the tracker entry easily. + bool found_it = _symbol_list->zap(name); + if (found_it) { + _tracker->zap(sep_ind); + } + return found_it? basis::common::OKAY : basis::common::NOT_FOUND; +} + +template +basis::outcome symbol_table::retrieve(int index, basis::astring &name, + contents &got) const +{ + bounds_return(index, 0, symbols() - 1, basis::common::NOT_FOUND); + internal_symbol_info *found = get_index(index); + name = found->_name; + got = found->_content; + return basis::common::OKAY; +} + +////////////// + +template +bool symbol_table_compare(const symbol_table &a, + const symbol_table &b) +{ +// FUNCDEF("symbol_table_compare"); + + string_set names_a; + a.names(names_a); + string_set names_b; + b.names(names_b); + if (names_a != names_b) return false; + + for (int i = 0; i < names_a.elements(); i++) { + const basis::astring ¤t_key = names_a[i]; + const contents *a_value = a.find(current_key); + const contents *b_value = b.find(current_key); + if (!a_value || !b_value) continue; // not good. + if (*a_value != *b_value) return false; + } + return true; +} + +#ifdef DEBUG_SYMBOL_TABLE + #undef LOG +#endif + +#undef static_class_name + +} //namespace. + +#endif + + diff --git a/core/library/structures/unique_id.h b/core/library/structures/unique_id.h new file mode 100644 index 00000000..43f87183 --- /dev/null +++ b/core/library/structures/unique_id.h @@ -0,0 +1,111 @@ +#ifndef UNIQUE_ID_CLASS +#define UNIQUE_ID_CLASS + +/*****************************************************************************\ +* * +* Name : unique_id * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1999-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace structures { + +//! Provides an abstraction for the responsibilities of a unique identifier. +/*! + These are generally used as a way of masking the underlying ID object while + providing some equality comparisons. It is especially useful when the + underlying object is not itself an object, but just a simple type. +*/ + +template +class unique_id : public virtual basis::equalizable +{ +public: + unique_id(uniquifier initial_value) : _id(initial_value) {} + //!< Constructs a unique id from the "initial_value". + + unique_id(const unique_id &to_copy) { *this = to_copy; } + //!< Constructs a unique id as a copy of the "to_copy" object. + + ~unique_id() {} + + virtual bool equal_to(const equalizable &to_compare) const { + const unique_id *cast = dynamic_cast *>(&to_compare); + if (!cast) throw "error: unique_id::==: unknown type"; + return cast->_id == _id; + } + + //! Returns true if the held id is the same as "to_compare". + /*! The templated uniquifying type absolutely must provide an equality + operator (==) and an assignment operator (=). */ + bool operator == (const unique_id &to_compare) const + { return _id == to_compare._id; } + + //! Sets this id to be the same as "to_copy". + unique_id & operator = (const unique_id &to_copy) + { if (this != &to_copy) _id = to_copy._id; return *this; } + + uniquifier raw_id() const { return _id; } + //!< Returns the held identifier in its native form. + + void set_raw_id(uniquifier new_value) { _id = new_value; } + //!< Sets the held identifier to "new_value". + +private: + uniquifier _id; //!< the held object in its native form. +}; + +////////////// + +//! A unique identifier class that supports sorting. +/*! + The orderable version can be compared for magnitude and permits sorting + the ids based on the underlying type. The underlying type must implement + at least the less than operator. +*/ + +template +class orderable_unique_id : public unique_id +{ +public: + orderable_unique_id(const uniquifier &initial_value) + : unique_id(initial_value) {} + orderable_unique_id(const unique_id &initial_value) + : unique_id(initial_value) {} + ~orderable_unique_id() {} + + /*! the "uniquifier" type absolutely must provide a less than operator (<) + and it must meet the requirements of the "unique_id" template. */ + bool operator < (const unique_id &to_compare) const + { return this->raw_id() < to_compare.raw_id(); } +}; + +////////////// + +//! A unique identifier based on integers. + +class unique_int : public unique_id +{ +public: + unique_int(int initial = 0) : unique_id(initial) {} + //!< implicit default for "initial" of zero indicates bogus id. + + bool operator ! () const { return raw_id() == 0; } + //!< provides a way to test whether an id is valid. + /*!< This uses the implicit assumption that a zero id is invalid or + unassigned. */ +}; + +} //namespace. + +#endif + diff --git a/core/library/structures/version_record.cpp b/core/library/structures/version_record.cpp new file mode 100644 index 00000000..f3bfc659 --- /dev/null +++ b/core/library/structures/version_record.cpp @@ -0,0 +1,269 @@ +/*****************************************************************************\ +* * +* Name : version structures: version, version_record * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "string_array.h" +#include "version_record.h" + +#include + +#include + +using namespace basis; + +namespace structures { + +void *version::__global_module_handle() +{ + static void *__mod_hand = 0; + return __mod_hand; +} + +version::version() +: _components(new string_array) +{ + set_component(MAJOR, "0"); + set_component(MINOR, "0"); + set_component(REVISION, "0"); + set_component(BUILD, "0"); +} + +version::version(const string_array &version_info) +: _components(new string_array(version_info)) +{ + for (int i = 0; i < _components->length(); i++) { + if (!(*_components)[i]) { + // this component is blank; replace it with a zero. + (*_components)[i] = "0"; + } + } +} + +version::version(const astring &formatted_string) +: _components(new string_array) +{ + astring verstring = formatted_string; + // scan through and replace bogus bits with reasonable bits. + for (int j = 0; j < verstring.length(); j++) { + if (verstring[j] == ',') verstring[j] = '.'; + // replace commas with periods. + } + // locate the pieces of the version string. + for (int i = 0; i < verstring.length(); i++) { + int perindy = verstring.find('.', i); + if (negative(perindy)) { + if (verstring.length() - i > 0) { + // add any extra bits after the last period in the string. + *_components += verstring.substring(i, verstring.end()); + } + break; + } + // we found a period, so include anything between the current position and + // that as a component. + *_components += verstring.substring(i, perindy - 1); + i = perindy; + // set i to be at the next period; it will be incremented past that. + } +} + +version::version(int maj, int min, int rev, int build) +: _components(new string_array) +{ + *this = version(a_sprintf("%u.%u.%u.%u", basis::un_int(maj), basis::un_int(min), basis::un_int(rev), + basis::un_int(build))); +} + +version::version(const version &to_copy) +: _components(new string_array) +{ *this = to_copy; } + +version::~version() { WHACK(_components); } + +version &version::operator =(const version &to_copy) +{ + if (this != &to_copy) *_components = *to_copy._components; + return *this; +} + +astring version::text_form() const { return flex_text_form(); } + +int version::components() const { return _components->length(); } + +int version::v_major() const +{ return int(get_component(MAJOR).convert(0)); } + +int version::v_minor() const +{ return int(get_component(MINOR).convert(0)); } + +int version::v_revision() const +{ return int(get_component(REVISION).convert(0)); } + +int version::v_build() const +{ return int(get_component(BUILD).convert(0)); } + +astring version::get_component(int index) const +{ + bounds_return(index, 0, _components->length() - 1, "0"); + return (*_components)[index]; +} + +void version::set_component(int index, const astring &to_set) +{ + if (_components->length() <= index) + _components->resize(index + 1); + if (to_set.t()) + (*_components)[index] = to_set; + else + (*_components)[index] = "0"; +} + +bool version::equal_to(const equalizable &to_test) const +{ + const version *cast = cast_or_throw(to_test, *this); + return *_components == *cast->_components; +} + +bool version::less_than(const orderable &to_test) const +{ + const version *cast = cast_or_throw(to_test, *this); + if (v_major() < cast->v_major()) return true; + else if (v_major() > cast->v_major()) return false; + if (v_minor() < cast->v_minor()) return true; + else if (v_minor() > cast->v_minor()) return false; + if (v_revision() < cast->v_revision()) return true; + else if (v_revision() > cast->v_revision()) return false; + if (v_build() < cast->v_build()) return true; + return false; +} + +bool version::compatible(const version &to_test) const +{ +//fix to be more general + return (v_major() == to_test.v_major()) + && (v_minor() == to_test.v_minor()) + && (v_revision() == to_test.v_revision()); +} + +bool version::bogus() const +{ return !v_major() && !v_minor() && !v_revision() && !v_build(); } + +#define SEPARATE \ + if (style != DOTS) to_return += ", "; \ + else to_return += "." + +astring version::flex_text_form(version_style style, int where) const +{ + // note: the conversions below are to ensure proper treatment of the + // size of the int16 types by win32. + astring to_return; + + if (style == DETAILED) to_return += "major "; + astring temp = get_component(MAJOR); + if (!temp) temp = "0"; + to_return += temp; + if (where == MAJOR) return to_return; + SEPARATE; + + if (style == DETAILED) to_return += "minor "; + temp = get_component(MINOR); + if (!temp) temp = "0"; + to_return += temp; + if (where == MINOR) return to_return; + SEPARATE; + + if (style == DETAILED) to_return += "revision "; + temp = get_component(REVISION); + if (!temp) temp = "0"; + to_return += temp; + if (where == REVISION) return to_return; + SEPARATE; + + if (style == DETAILED) to_return += "build "; + temp = get_component(BUILD); + if (!temp) temp = "0"; + to_return += temp; + + // other components don't have handy names. + if (where > BUILD) { + for (int i = BUILD + 1; i < where; i++) { + SEPARATE; + if (style == DETAILED) to_return += a_sprintf("part_%d ", i + 1); + temp = get_component(i); + if (!temp) temp = "0"; + to_return += temp; + } + } + + return to_return; +} + +version version::from_text(const astring &to_convert) +{ return version(to_convert); } + +int version::packed_size() const +{ + return _components->packed_size(); +} + +void version::pack(byte_array &target) const +{ _components->pack(target); } + +bool version::unpack(byte_array &source) +{ + if (!_components->unpack(source)) return false; + return true; +} + +////////////// + +#define VR_NEWLINE to_return += "\n" + +version_record::~version_record() +{} + +astring version_record::text_form() const +{ + astring to_return; + to_return += "Description: "; + to_return += description; + VR_NEWLINE; + to_return += "File Version: "; + to_return += file_version.text_form(); + VR_NEWLINE; + to_return += "Internal Name: "; + to_return += internal_name; + VR_NEWLINE; + to_return += "Original Name: "; + to_return += original_name; + VR_NEWLINE; + to_return += "Product Name: "; + to_return += product_name; + VR_NEWLINE; + to_return += "Product Version: "; + to_return += product_version.text_form(); + VR_NEWLINE; + to_return += "Company Name: "; + to_return += company_name; + VR_NEWLINE; + to_return += "Copyright: "; + to_return += copyright; + VR_NEWLINE; + to_return += "Trademarks: "; + to_return += trademarks; + VR_NEWLINE; + + return to_return; +} + +} //namespace. + diff --git a/core/library/structures/version_record.h b/core/library/structures/version_record.h new file mode 100644 index 00000000..55ffed35 --- /dev/null +++ b/core/library/structures/version_record.h @@ -0,0 +1,218 @@ +#ifndef VERSION_STRUCTURE_GROUP +#define VERSION_STRUCTURE_GROUP + +/*****************************************************************************\ +* * +* Name : version & version_record * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +/*! @file version_record.h + Note that this header is tuned for forward declaration of the version + and version_record objects; this is a better approach than including this + somewhat heavy header file in other header files. +*/ + +#include "string_array.h" + +#include +#include + +namespace structures { + +//! Holds a file's version identifier. +/*! + The version structures can be used in any of our components because + they're not platform specific. They maintain information about a file + that is part of the released product. +*/ + +class version : public virtual basis::packable, public virtual basis::orderable +{ +public: + version(); //!< constructs a blank version. + + version(const structures::string_array &version_info); + //!< constructs a version from a list of strings that form the components. + /*!< note that if a component is an empty string, it is changed to be a + zero ("0"). */ + + version(const basis::astring &formatted_string); + //!< the version components will be parsed from the "formatted_string". + /*!< the version component separator is the period ('.') or the comma (',') + character. */ + + version(int major, int minor, int rev = 0, int build = 0); + //!< constructs a win32 style version structure from the information. + + version(const version &to_copy); + //!< constructs a copy of "to_copy". + + virtual ~version(); + + version &operator =(const version &to_copy); + //!< assigns this to the "to_copy". + + DEFINE_CLASS_NAME("version"); + + virtual basis::astring text_form() const; + + bool equal_to(const equalizable &to_test) const; + //!< compares two versions for exact equality. + /*!< to perform a check of win32 build compatibility, use the + compatible() method. */ + + bool less_than(const orderable &to_test) const; + //!< reports if this version is less than "to_test". + /*!< supplies the other operator needed for the full set of comparisons + (besides equality). the basis namespace provides templates for the rest + of the comparison operators in . */ + + int components() const; + //!< reports the number of components that make up this version. + + basis::astring get_component(int index) const; + //!< returns the component at the specified index. + /*!< note that if an entry is an empty string, then a string with zero + in it is returned ("0"). */ + + void set_component(int index, const basis::astring &to_set); + //!< sets the component at "index" to "to_set". + /*!< an empty string for "to_set" is turned into a zero. */ + + enum version_places { MAJOR, MINOR, REVISION, BUILD }; + //!< these are names for the first four components of the version. + /*!< there may be more components than four on some platforms. */ + + enum version_style { DOTS, COMMAS, DETAILED }; + //!< different ways that a version object can be displayed. + /*!< DOTS is in the form "1.2.3.4" + COMMAS is in the form "1, 2, 3, 4" + DETAILED is in the form "major 1, minor 2, ..." + */ + + basis::astring flex_text_form(version_style style = DOTS, int including = -1) const; + //!< returns a textual form of the version number. + /*!< the place passed in "including" specifies how much of the version + to print, where a negative number means all components. for example, if + "including" is MINOR, then only the first two components (major and minor + components) are printed. */ + + static version from_text(const basis::astring &to_convert); + //!< returns a version structure parsed from "to_convert". + + virtual int packed_size() const; + virtual void pack(basis::byte_array &target) const; + virtual bool unpack(basis::byte_array &source); + + ////////////// + + // the following methods mainly help on win32 platforms, where the version + // components are always simple short integers. there are only ever four + // of these. if one adheres to that same scheme on other platforms, then + // these functions may be helpful. otherwise, with the mixed alphanumeric + // versions one sees on unix, these are not so great. + + int v_major() const; + //!< major version number. + /*!< major & minor are the most significant values for a numerical version. + these are the familiar numbers often quoted for software products, like + "jubware version 8.2". */ + int v_minor() const; + //!< minor version number. + int v_revision() const; + //!< revision level. + /*!< in the hoople code and the clam system, this number is changed for + every new build. when two versions of a file are the same in major, + minor and revision numbers, then they are said to be compatible. for + those using this version scheme, it asserts that dll compatibility has not + been broken if one swaps those two files in an installation. after the + swap, any components that are dependent on the dll must all link properly + against the replacement file. when in doubt, increment the version number. + some folks automatically increment the revision level every week. */ + int v_build() const; + //!< build number. + /*!< this number is not considered important when comparing file + compatibility. the compatible() method always returns true if two files + differ only in the "build" number (rather than major, minor or revision). + this allows patches to be created with a newer (larger) build number, but + still link fine with existing dlls. since the file is distinguished by + more than just its time stamp, it allows changes to an installation to be + tracked very precisely. some folks keep a catalog of patched components + for each software release and index the patch details by the different + build numbers. */ + + bool compatible(const version &that) const; + //!< returns true if this is compatible with "that" version on win32. + /*!< that means that all version components are the same except for the + last one, the build number. we allow the build numbers to fluctuate so + that patched components can be installed without causing version + complaints. */ + + bool bogus() const; + //!< returns true if the version held here is clearly bogus. + /*!< this means that all four numbers are zero. */ + +////////////// + + static void *__global_module_handle(); + //!< a static resource required to identify the actual win32 module that this lives in. + /*!< this handle is stored statically when the libraries are started up. it records + the handle of the module they belong to for later use in checking versions. */ + +//hmmm: storage here is still missing! + +private: + structures::string_array *_components; //!< our list of version components. +}; + +////////////// + +//! Holds all information about a file's versioning. +/*! Not all of these fields are meaningful on every platform. */ + +class version_record : public virtual basis::root_object +{ +public: + virtual ~version_record(); + + DEFINE_CLASS_NAME("version_record"); + + basis::astring text_form() const; + // returns a view of the fields in this record. + + // these describe a particular file: + basis::astring description; // the purpose of this file. + version file_version; // the version number for this file. + basis::astring internal_name; // the internal name of the file. + basis::astring original_name; // name of file before possible renamings. + + // these describe the project that the file belongs to: + basis::astring product_name; // the product this file belongs to. + version product_version; // the version of the product. + + // these describe the creators of the file: + basis::astring company_name; // name of the company that created the file. + + // legal matters: + basis::astring copyright; // copyright info for this file. + basis::astring trademarks; // trademarks related to the file. + + // extra pieces not stored in record (yet). + basis::astring web_address; // the location of the company on the web. +}; + +////////////// + +} //namespace. + +#endif + diff --git a/core/library/tests_algorithms/makefile b/core/library/tests_algorithms/makefile new file mode 100644 index 00000000..46f442b3 --- /dev/null +++ b/core/library/tests_algorithms/makefile @@ -0,0 +1,12 @@ +include cpp/variables.def + +PROJECT = tests_algorithms +TYPE = test +TARGETS = test_sorts.exe +DEFINITIONS += USE_HOOPLE_DLLS +LOCAL_LIBS_USED = unit_test application processes loggers configuration mathematics nodes \ + structures textual timely filesystem structures basis +RUN_TARGETS = $(ACTUAL_TARGETS) + +include cpp/rules.def + diff --git a/core/library/tests_algorithms/test_sorts.cpp b/core/library/tests_algorithms/test_sorts.cpp new file mode 100644 index 00000000..fefcb2e5 --- /dev/null +++ b/core/library/tests_algorithms/test_sorts.cpp @@ -0,0 +1,86 @@ +/* +* Name : test_sorts +* Author : Chris Koeritz +** +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace algorithms; +using namespace application; +using namespace basis; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +const int MAX_ELEMENTS = 1200; + +const int MAX_VALUE = 28000; + +#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger().get(), to_print) + +class test_sorts : virtual public unit_base, virtual public application_shell +{ +public: + test_sorts() : application_shell() {} + DEFINE_CLASS_NAME("test_sorts"); + virtual int execute(); +}; + +int test_sorts::execute() +{ + FUNCDEF("execute"); + + int *list = new int[MAX_ELEMENTS]; + for (int i = 0; i < MAX_ELEMENTS; i++) + list[i] = randomizer().inclusive(0, MAX_VALUE); + +//astring ret; +//for (int i = 0; i < MAX_ELEMENTS; i++) ret += a_sprintf("%d ", list[i]); +//LOG(ret); +//LOG("-------------"); + + // check a normal sort. + shell_sort(list, MAX_ELEMENTS); + int last = -1; + for (int j = 0; j < MAX_ELEMENTS; j++) { + ASSERT_FALSE(list[j] < last, "ordering check - list should be ordered at first check"); + last = list[j]; + } + + // re-randomize the list. + for (int i = 0; i < MAX_ELEMENTS; i++) + list[i] = randomizer().inclusive(0, MAX_VALUE); + + // check a reversed sort. + shell_sort(list, MAX_ELEMENTS, true); + last = MAX_VALUE + 100; // past the maximum we'll include in the list. + for (int j = 0; j < MAX_ELEMENTS; j++) { + ASSERT_FALSE(list[j] > last, "ordering check - list should be ordered at second check"); + last = list[j]; + } + + // clean up now. + delete [] list; + + return final_report(); +} + +HOOPLE_MAIN(test_sorts, ) + diff --git a/core/library/tests_basis/checkup.cpp b/core/library/tests_basis/checkup.cpp new file mode 100644 index 00000000..cf0a53bb --- /dev/null +++ b/core/library/tests_basis/checkup.cpp @@ -0,0 +1,52 @@ +/* +* Name : checkup +* Author : Chris Koeritz +** +* Copyright (c) 1990-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include "checkup.h" + +#include +#include + +using namespace basis; +using namespace loggers; +using namespace unit_test; + +namespace system_checkup { + +#undef UNIT_BASE_THIS_OBJECT +#define UNIT_BASE_THIS_OBJECT testing +#undef static_class_name +#define static_class_name() astring("system_checkup") + +bool check_system_characteristics(unit_base &testing) +{ + FUNCDEF("check_system_characteristics") + // a big assumption is that the size of an unsigned character is just + // one byte. if this is not true, probably many things would break... + int byte_size = sizeof(abyte); + ASSERT_EQUAL(byte_size, 1, "byte size should be 1 byte"); + int int16_size = sizeof(int16); + ASSERT_FALSE( (sizeof(uint16) != int16_size) || (int16_size != 2), + "uint16 size should be 2 bytes"); + int uint_size = sizeof(un_int); +//hmmm: the checks are actually redundant... + ASSERT_FALSE( (uint_size != sizeof(int)) || (uint_size != 4), + "un_int size should be 2 bytes"); + // all tests successfully passed. + return true; +} + +#undef UNIT_BASE_THIS_OBJECT +#define UNIT_BASE_THIS_OBJECT (*this) +#undef static_class_name + +} // namespace + diff --git a/core/library/tests_basis/checkup.h b/core/library/tests_basis/checkup.h new file mode 100644 index 00000000..8d2b47c3 --- /dev/null +++ b/core/library/tests_basis/checkup.h @@ -0,0 +1,33 @@ +#ifndef CHECKUP_GROUP +#define CHECKUP_GROUP + +/* +* Name : checkup +* Author : Chris Koeritz +* Purpose: +* Checks that certain critical properties are upheld by the runtime +* environment. This could be invoked at the beginning of a main program to +* check these characteristics once before continuing execution. +** +* Copyright (c) 1990-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include + +namespace system_checkup { + + bool check_system_characteristics(unit_test::unit_base &testing); + // used to verify that this compilation system possesses some desired + // characteristics. true is returned if everything checks out, and false + // is returned if some assumption proves untrue. + +} + +#endif + diff --git a/core/library/tests_basis/makefile b/core/library/tests_basis/makefile new file mode 100644 index 00000000..16165886 --- /dev/null +++ b/core/library/tests_basis/makefile @@ -0,0 +1,14 @@ +include cpp/variables.def + +PROJECT = tests_basis +TYPE = test +SOURCE = checkup.cpp +TARGETS = test_array.exe test_boilerplate.exe test_mutex.exe test_string.exe \ + test_system_preconditions.exe +DEFINITIONS += USE_HOOPLE_DLLS +LOCAL_LIBS_USED = unit_test application processes loggers configuration mathematics nodes \ + structures textual timely filesystem structures basis +RUN_TARGETS = $(ACTUAL_TARGETS) + +include cpp/rules.def + diff --git a/core/library/tests_basis/test_array.cpp b/core/library/tests_basis/test_array.cpp new file mode 100644 index 00000000..e7d04a6b --- /dev/null +++ b/core/library/tests_basis/test_array.cpp @@ -0,0 +1,929 @@ +/*****************************************************************************\ +* * +* Name : test_array * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//#define DEBUG_ARRAY + // when this flag is turned on, extra checking will be done in the allocator. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +///#include //temp + +using namespace application; +using namespace basis; +using namespace loggers; +using namespace mathematics; +using namespace timely; +using namespace unit_test; + +//const float MAX_TEST_DURATION_ms = 28 * SECOND_ms; +const float MAX_TEST_DURATION_ms = 200; + // each of the tests calling on the templated tester will take this long. + +const int MAX_SIMULTANEOUS_OBJECTS = 42; // the maximum arrays tested. + +const int MIN_OBJECT = -30; // the smallest array we'll create. +const int MAX_OBJECT = 98; // the largest array we'll create. + +const int MIN_BLOCK = 100; // the smallest exemplar we'll use. +const int MAX_BLOCK = MAX_OBJECT * 2; // the largest exempler. + +//#define DEBUG_TEST_ARRAY + // uncomment for noisy version. + +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +static chaos a_randomizer; + +////////////// + +class test_array : public application_shell, public unit_base +{ +public: + test_array() + : application_shell(), + unit_base() + { +//hmmm: should go into app shell +////SETUP_CONSOLE_LOGGER; +} + DEFINE_CLASS_NAME("test_array"); + virtual int execute(); + void dump_array(array &ar, const char *name); + void test_arrays_of_void_pointer(); + void test_iteration_speed(); + + template + void array_tester(test_array &ta, const contents &formal(bogus), basis::un_short flags); + +}; + +////////////// + +void test_array::dump_array(array &ar, const char *name) +{ +#ifdef DEBUG_TEST_ARRAY + FUNCDEF("dump_array"); + LOG(a_sprintf("array named \"%s\" has:", name)); + for (int i = 0; i < ar.length(); i++) { + LOG(a_sprintf("\t%4d: %d", i, (int)ar[i])); + } +#else + if (ar.length() && name) {} +#endif +} + +void test_array::test_arrays_of_void_pointer() +{ + FUNCDEF("void pointer test"); + const int MAX_VOID_ARRAY = 20; + array argh(MAX_VOID_ARRAY, NIL, byte_array::SIMPLE_COPY + | byte_array::EXPONE | byte_array::FLUSH_INVISIBLE); + array argh2(argh); + ASSERT_EQUAL(argh.length(), MAX_VOID_ARRAY, "check first array length"); + ASSERT_EQUAL(argh2.length(), MAX_VOID_ARRAY, "check copied array length"); + int wrong_counter = 0; + for (int o = 0; o < MAX_VOID_ARRAY; o++) + if (argh[o] != argh2[o]) wrong_counter++; + ASSERT_EQUAL(wrong_counter, 0, "compare array contents"); + + // fill it with values. + int starter; + for (int i = 0; i < MAX_VOID_ARRAY; i++) + argh[i] = (void *)(&starter + i); + dump_array(argh, "first version"); + + // check the values. + wrong_counter = 0; + for (int j = 0; j < MAX_VOID_ARRAY; j++) + if (argh[j] != (void *)(&starter + j) ) wrong_counter++; + ASSERT_EQUAL(wrong_counter, 0, "compare values that were set"); + + // add a constant to the values. + for (int k = 0; k < MAX_VOID_ARRAY; k++) + argh[k] = (void *)((int *)argh[k] + 23); + dump_array(argh, "second version"); + + // check assignment. + argh2 = argh; + wrong_counter = 0; + for (int n = 0; n < MAX_VOID_ARRAY; n++) + if (argh2[n] != (void *)(&starter + n + 23)) wrong_counter++; + ASSERT_EQUAL(wrong_counter, 0, "compare values that were assigned"); + + // now test that values are kept in place after rearrangement. + argh.zap(3, 4); + dump_array(argh, "third version"); + wrong_counter = 0; + for (int l = 0; l < 3; l++) + if (argh[l] != (void *)(&starter + l + 23)) wrong_counter++; + ASSERT_EQUAL(wrong_counter, 0, "zap low values test"); + wrong_counter = 0; + for (int m = 3; m < MAX_VOID_ARRAY - 2; m++) + if (argh[m] != (void *)(&starter + m + 2 + 23)) wrong_counter++; + ASSERT_EQUAL(wrong_counter, 0, "zap high values test"); +//hmmm: add more tests if void pointer arrays seem in question. +} + +////////////// + +static astring blank_string; + +// jethro is a simple object for containment below. +class jethro +{ +public: + jethro(const astring &i = blank_string) : _truck(i) {} + + astring _truck; + + bool operator ==(const jethro &tc) const { return tc._truck == _truck; } +}; + +////////////// + +// the test_content object is an object with proper copy constructor +// and assignment operator that also has deep contents. +class test_content +{ +public: + abyte _q; + astring _ted; + astring _jed; + int_array _ned; + + test_content(abyte q = 3) + : _q(q), _ted("bl"), _jed("orp"), + _ned(12, NIL) +/* _med(3, 2), + _red(2, 4) */ + { + for (int i = 0; i < _ned.length(); i++) + _ned[i] = -i; +/* + for (int r = 0; r < _med.rows(); r++) + for (int c = 0; c < _med.columns(); c++) + _med[r][c] = jethro(astring((r*c) % 256, 1)); + for (int j = 0; j < _red.rows(); j++) + for (int k = 0; k < _red.columns(); k++) + _red[j][k] = j * k; +*/ + } + + bool operator ==(const test_content &tc) const { + if (tc._q != _q) return false; + if (tc._ted != _ted) return false; + if (tc._jed != _jed) return false; + if (tc._ned.length() != _ned.length()) return false; + for (int i = 0; i < _ned.length(); i++) + if (tc._ned[i] != _ned[i]) return false; + +/* + if (tc._med.rows() != _med.rows()) return false; + if (tc._med.columns() != _med.columns()) return false; + for (int c = 0; c < _med.columns(); c++) + for (int r = 0; r < _med.rows(); r++) + if (tc._med.get(r, c) != _med.get(r, c)) return false; + + if (tc._red.rows() != _red.rows()) return false; + if (tc._red.columns() != _red.columns()) return false; + for (int j = 0; j < _red.rows(); j++) + for (int k = 0; k < _red.columns(); k++) + if (tc._red.get(j, k) != _red.get(j, k)) return false; +*/ + + return true; + } + + operator abyte() const { return _q; } +}; + +////////////// + +template +bool compare_arrays(const array &a, const array &b) +{ + if (a.length() != b.length()) return false; + for (int i = 0; i < a.length(); i++) + if (a[i] != b[i]) return false; + return true; +} + +////////////// + +// this is a templated test for arrays, with some requirements. the contents +// object must support a constructor that takes a simple byte, whether +// that's meaningful for the object or not. if your type to test doesn't +// have that, derive a simple object from it, give it that constructor, and +// then it can be used below. the object must also support comparison with +// == and != for this test to be used. it must also provide a conversion +// to integer that returns the value passed to the constructor. + +template +void test_array::array_tester(test_array &ta, const contents &formal(bogus), basis::un_short flags) +{ + FUNCDEF("array_tester"); + // the space that all training for arrays comes from. + contents *junk_space = new contents[MAX_OBJECT + MAX_BLOCK]; + for (int i = 0; i < MAX_OBJECT - 1; i++) + junk_space[i] = contents(a_randomizer.inclusive('a', 'z')); + junk_space[MAX_OBJECT + MAX_BLOCK - 1] = '\0'; + + array *testers[MAX_SIMULTANEOUS_OBJECTS]; + for (int c = 0; c < MAX_SIMULTANEOUS_OBJECTS; c++) { + // set up the initial array guys. + testers[c] = new array(a_randomizer.inclusive(MIN_OBJECT, MAX_OBJECT), + NIL, flags); + // copy the randomized junk space into the new object. + for (int i = 0; i < testers[c]->length(); i++) + testers[c]->put(i, junk_space[i]); + } + + // these are the actions we try out with the array during the test. + // the first and last elements must be identical to the first and last + // tests to perform. + enum actions { first, do_train = first, do_size, do_assign, + do_access, do_zap, do_resizer, do_shrink, do_reset, do_indices, + do_concatenating, do_concatenating2, do_subarray, do_insert, + do_overwrite, do_stuff, do_memory_paring, do_snarf, do_flush, + do_observe, last = do_observe }; + + time_stamp exit_time(MAX_TEST_DURATION_ms); + while (time_stamp() < exit_time) { + int index = a_randomizer.inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1); + int choice = a_randomizer.inclusive(first, last); + switch (choice) { + case do_train: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_train")); +#endif + int new_size = a_randomizer.inclusive(MIN_OBJECT, MAX_OBJECT); + testers[index]->retrain(new_size, (contents *)junk_space); + int wrong_counter = 0; + for (int i = 0; i < new_size; i++) + if (junk_space[i] != (*testers[index])[i]) wrong_counter++; + ASSERT_EQUAL(wrong_counter, 0, "test contents after retrain"); + break; + } + case do_size: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_size")); +#endif + array old_version = *testers[index]; + bool at_front = bool(a_randomizer.inclusive(0, 1)); + int new_size = a_randomizer.inclusive(MIN_OBJECT, MAX_OBJECT); + bool smaller = new_size < old_version.length(); + int difference = absolute_value(new_size - old_version.length()); + testers[index]->resize(new_size, + at_front? old_version.NEW_AT_BEGINNING + : old_version.NEW_AT_END); + if (!smaller && difference) { + // neuter the contents in the new section so we can compare. this + // space is filled with whatever the object's constructor chooses. + if (at_front) { + for (int i = 0; i < difference; i++) + testers[index]->put(i, 'Q'); + } else { + for (int i = old_version.length(); + i < old_version.length() + difference; i++) + testers[index]->put(i, 'Q'); + } + } + // now compute an equivalent form of what the state should be. + array equivalent(0, NIL, flags); + if (at_front) { + if (smaller) { + equivalent = old_version.subarray(difference, + old_version.length() - 1); + } else { + array blank(difference, NIL, flags); + for (int i = 0; i < blank.length(); i++) + blank[i] = 'Q'; + equivalent = blank + old_version; + } + } else { + if (smaller) { + equivalent = old_version.subarray(0, old_version.length() + - difference - 1); + } else { + array blank(difference, NIL, flags); + for (int i = 0; i < blank.length(); i++) + blank[i] = 'Q'; + equivalent = old_version + blank; + } + } + ASSERT_EQUAL(equivalent.length(), testers[index]->length(), + "check length of resized form"); + ASSERT_TRUE(compare_arrays(*testers[index], equivalent), + "check contents of resized form"); + break; + } + case do_assign: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_assign")); +#endif + array arrh = *testers[index]; // copy old value. + int to_assign = a_randomizer.inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1); + *testers[index] = *testers[to_assign]; + ASSERT_TRUE(compare_arrays(*testers[index], *testers[to_assign]), + "check result of assign copying array"); + *testers[to_assign] = arrh; // recopy contents to new place. + ASSERT_TRUE(compare_arrays(*testers[to_assign], arrh), + "check result of second assign"); + break; + } + case do_access: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_access")); +#endif + int start = a_randomizer.inclusive(0, testers[index]->length()); + int end = a_randomizer.inclusive(0, testers[index]->length()); + flip_increasing(start, end); + array accumulator(0, NIL, flags); + for (int i = start; i < end; i++) { + contents c = contents(a_randomizer.inclusive(1, 255)); + testers[index]->access()[i] = c; + accumulator += c; + } + for (int j = start; j < end; j++) + ASSERT_EQUAL(accumulator[j - start], (*testers[index])[j], + "comparison accessing at index must be equal"); + break; + } + case do_indices: { +#ifndef CATCH_ERRORS // only works if not catching errors. + #ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_indices")); + #endif + // does some tests on bad indices. + contents c1 = testers[index]->operator[](-50); + contents c2 = testers[index]->operator[](-MAX_OBJECT); + bool test = (c1 == c2); + ASSERT_TRUE(test, "invalid values should be the same"); + int tests = a_randomizer.inclusive(100, 500); + for (int i = 0; i < tests; i++) { + int indy = a_randomizer.inclusive(-1000, MAX_OBJECT * 3); + // testing if we can access without explosions. + contents c3 = testers[index]->operator[](indy); + contents c4 = c3; + ASSERT_EQUAL(c3, c4, "for do_indices, values should be the same"); + } +#endif + break; + } + case do_observe: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_observe")); +#endif + // tests contents returned by observe. + int start = a_randomizer.inclusive(0, testers[index]->length()); + int end = a_randomizer.inclusive(0, testers[index]->length()); + flip_increasing(start, end); + double total_1 = 0; + for (int i = start; i < end; i++) + total_1 += testers[index]->observe()[i]; + double total_2 = 0; + for (int j = end - 1; j >= start; j--) + total_2 += testers[index]->observe()[j]; + ASSERT_EQUAL(total_1, total_2, "totals should match up"); + break; + } + case do_resizer: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_resizer")); +#endif + // tests whether the array will reuse space when it should be able to. + array &arrh = *testers[index]; + // fill with known data. + int i; + for (i = 0; i < arrh.length(); i++) + arrh[i] = contents((i + 23) % 256); + // record the old starting point. + const contents *start = arrh.internal_block_start(); + int zap_amount = a_randomizer.inclusive(1, arrh.length() - 1); + // now take out a chunk from the array at the beginning. + arrh.zap(0, zap_amount - 1); + // test the contents. + for (i = 0; i < arrh.length(); i++) + ASSERT_EQUAL(arrh[i], contents((i + 23 + zap_amount) % 256), + "the resized form should have same contents after zap"); + // now add back in the space we ate. + arrh.resize(arrh.length() + zap_amount, arrh.NEW_AT_END); + // check the pointer; it should not have changed. + ASSERT_EQUAL(start, arrh.internal_block_start(), + "the resized form should have same start address"); + // test the contents again. they should start at zero and have the + // same zap_amount offset. the stuff past the original point shouldn't + // be tested since we haven't set it. + for (i = 0; i < arrh.length() - zap_amount; i++) + ASSERT_EQUAL(arrh[i], contents((i + 23 + zap_amount) % 256), + "the resized form should still have same contents"); + // now a test of all values through the zap_amount. + arrh.zap(0, zap_amount - 1); + for (i = 0; i < zap_amount; i++) { + arrh.resize(arrh.length() + 1, arrh.NEW_AT_END); + ASSERT_EQUAL(start, arrh.internal_block_start(), + "the slowly resized form should have same start address"); + } + // test the contents once more. they should start at zero and have + // double the zap_amount offset. the stuff past the original point + // shouldn't be tested since we haven't set it. + for (i = 0; i < arrh.length() - 2 * zap_amount; i++) + ASSERT_EQUAL(arrh[i], contents((i + 23 + 2 * zap_amount) % 256), + "the slowly resized form should have same contents"); + + // the tests below should be nearly identical to the ones above, but + // they use the NEW_AT_BEGINNING style of copying. + + // fill with known data. + for (i = 0; i < arrh.length(); i++) + arrh[i] = contents((i + 23) % 256); + // record the old starting point. + start = arrh.internal_block_start(); + zap_amount = a_randomizer.inclusive(1, arrh.length() - 1); + // now take out a chunk from the array at the beginning. + arrh.zap(0, zap_amount - 1); + // test the contents. + for (i = 0; i < arrh.length(); i++) + ASSERT_EQUAL(arrh[i], contents((i + 23 + zap_amount) % 256), + "the resized form with known data should have right contents"); + // now add back in the space we ate. + arrh.resize(arrh.length() + zap_amount, + arrh.NEW_AT_BEGINNING); + // check the pointer; it should not have changed. + ASSERT_EQUAL(start, arrh.internal_block_start(), + "the resized form with known data should have same start address"); + // test the contents again. they should start at zap_amount but have + // the same zap_amount offset. the stuff before the original point + // shouldn't be tested since we haven't set it. + for (i = zap_amount; i < arrh.length(); i++) + ASSERT_EQUAL(arrh[i], contents((i + 23) % 256), + "the known data resized form should have same contents"); + // now a test of all values through the zap_amount. + arrh.zap(0, zap_amount - 1); + for (i = 0; i < zap_amount; i++) { + arrh.resize(arrh.length() + 1, arrh.NEW_AT_BEGINNING); + ASSERT_EQUAL(start, arrh.internal_block_start(), + "the known data slowly resized form should have same start address"); + } + // test the contents once more. the zap_amount offset should stay the + // same since we clobbered the place we added originally, then added + // more space in. so we skip the first part still. + for (i = zap_amount; i < arrh.length(); i++) + ASSERT_EQUAL(arrh[i], contents((i + 23) % 256), + "the known data slowly resized form should have same contents"); + break; + } + case do_zap: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_zap")); +#endif + int start; + int end; + bool erroneous = false; + int chose = a_randomizer.inclusive(1, 100); + if (chose <= 90) { + // there's a ninety percent chance we pick a range that's valid. + start = a_randomizer.inclusive(0, testers[index]->length() - 1); + end = a_randomizer.inclusive(0, testers[index]->length() - 1); + } else if (chose <= 95) { + // and a 5 percent chance we pick to choose the zero index as our + // start; this gets at the code for fast zapping. + start = 0; + end = a_randomizer.inclusive(0, testers[index]->length() - 1); + } else { + // and a 5 percent chance we'll allow picking a bad index. the + // actual chance of picking a bad one is less. + erroneous = true; + start = a_randomizer.inclusive(-2, testers[index]->length() + 3); + end = a_randomizer.inclusive(-2, testers[index]->length() + 3); + } + flip_increasing(start, end); + array old_version = *testers[index]; + testers[index]->zap(start, end); + if (!erroneous) { + array old_head = old_version.subarray(0, start - 1); + array old_tail = old_version.subarray(end + 1, + old_version.length() - 1); + array equivalent = old_head + old_tail; + ASSERT_EQUAL(equivalent.length(), testers[index]->length(), + "the zapped form should not have erroneous length"); + ASSERT_TRUE(compare_arrays(*testers[index], equivalent), + "the zapped form should not have erroneous contents"); + } + break; + } + case do_reset: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_reset")); +#endif + int junk_start = a_randomizer.inclusive(MIN_BLOCK, MAX_BLOCK); + int junk_end = a_randomizer.inclusive(MIN_BLOCK, MAX_BLOCK); + flip_increasing(junk_start, junk_end); + int len = junk_end - junk_start + 1; + testers[index]->reset(len, (contents *)&junk_space[junk_start]); + ASSERT_EQUAL(testers[index]->length(), len, "reset should have proper length"); + for (int i = 0; i < len; i++) + ASSERT_EQUAL(testers[index]->get(i), junk_space[junk_start + i], + "reset should copy data"); + break; + } + case do_shrink: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_shrink")); +#endif + array garp = *testers[index]; + testers[index]->shrink(); + int new_diff = testers[index]->internal_real_length() + - testers[index]->length(); + ASSERT_TRUE(compare_arrays(garp, *testers[index]), "shrink should keep contents"); + // now force a real shrinkage. + if (testers[index]->length() < 5) continue; // need some room. + // pick an element to keep that is not first or last. + int kept = a_randomizer.inclusive(1, testers[index]->last() - 1); + // whack all but the lucky element. + testers[index]->zap(kept + 1, testers[index]->last()); + testers[index]->zap(0, kept - 1); + // shrink it again, now a big shrink. + testers[index]->shrink(); + // check the difference now. + new_diff = testers[index]->internal_real_length() + - testers[index]->length(); + ASSERT_FALSE(new_diff > 1, "massive shrink size should be correct"); + + // restore the original contents and block size. + *testers[index] = garp; + break; + } + case do_concatenating: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_concatenating")); +#endif + for (int i = 0; i < a_randomizer.inclusive(1, 20); i++) { + contents new_c = contents(a_randomizer.inclusive('a', 'z')); + testers[index]->concatenate(new_c); + ASSERT_EQUAL(new_c, testers[index]->get(testers[index]->last()), + "value should be equal after concatenate"); + } + int indy = a_randomizer.inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1); + array flirpan = *testers[indy]; + int start = a_randomizer.inclusive(0, flirpan.length() - 1); + int end = a_randomizer.inclusive(0, flirpan.length() - 1); + flip_increasing(start, end); + flirpan = flirpan.subarray(start, end); + array grumzor = *testers[index]; // old copy. + testers[index]->concatenate(flirpan); + array bubula = grumzor + flirpan; + ASSERT_TRUE(compare_arrays(bubula, *testers[index]), + "contents should be correct after concatenate or concatenation"); + contents first_value; + contents second_value; + if (testers[index]->length() >= 1) + first_value = testers[index]->get(0); + if (testers[index]->length() >= 2) + second_value = testers[index]->get(1); + const int max_iters = a_randomizer.inclusive(1, 42); + for (int j = 0; j < max_iters; j++) { + contents new_c = contents(a_randomizer.inclusive('a', 'z')); + *testers[index] = *testers[index] + new_c; + // correct our value checks if new indices became available. + if (testers[index]->length() == 1) { + first_value = testers[index]->get(0); + } else if (testers[index]->length() == 2) { + second_value = testers[index]->get(1); + } + + ASSERT_EQUAL(new_c, testers[index]->get(testers[index]->last()), + "value should not be wrong after concatenation"); + + ASSERT_FALSE((testers[index]->length() >= 1) && (first_value != testers[index]->get(0)), + "first value should not be corrupted"); + ASSERT_FALSE((testers[index]->length() >= 2) && (second_value != testers[index]->get(1)), + "second value should not be corrupted"); + + *testers[index] += new_c; + // we don't have to correct the first value any more, but might have + // to correct the second. + if (testers[index]->length() == 2) { + second_value = testers[index]->get(1); + } + ASSERT_EQUAL(new_c, testers[index]->get(testers[index]->last()), + "value should be right after second concatenation"); + ASSERT_FALSE( (testers[index]->length() >= 1) && (first_value != testers[index]->get(0)), + "first value should be uncorrupted after second concat"); + ASSERT_FALSE((testers[index]->length() >= 2) && (second_value != testers[index]->get(1)), + "second value should be uncorrupted after second concat"); + } + break; + } + case do_concatenating2: { + // this tests the new concatenate content array method for array. +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_concatenating2")); +#endif + array &flirpan = *testers[index]; + int new_len = a_randomizer.inclusive(0, 140); + contents *to_add = new contents[new_len]; + for (int i = 0; i < new_len; i++) { + int rando = a_randomizer.inclusive(0, MAX_BLOCK - 1); + to_add[i] = junk_space[rando]; + } + int old_len = flirpan.length(); + flirpan.concatenate(to_add, new_len); + for (int i = 0; i < new_len; i++) { + ASSERT_EQUAL(flirpan[i + old_len], to_add[i], + "value should not be wrong after content array concatenation"); + } + delete [] to_add; // clean up. + break; + } + case do_subarray: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_subarray")); +#endif + array flirpan = *testers[index]; + int start = a_randomizer.inclusive(0, flirpan.length() - 1); + int end = a_randomizer.inclusive(0, flirpan.length() - 1); + flip_increasing(start, end); + flirpan = flirpan.subarray(start, end); + for (int i = 0; i < end - start; i++) + ASSERT_EQUAL(flirpan[i], testers[index]->get(i + start), + "subarray should produce right array"); + break; + } + case do_memory_paring: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_memory_paring")); +#endif + for (int i = 0; i < MAX_SIMULTANEOUS_OBJECTS; i++) { + // zap extra junk off so we bound the memory usage. + if (testers[i]->length() > MAX_OBJECT) { + testers[i]->zap(MAX_OBJECT, testers[i]->length() - 1); + testers[i]->shrink(); + } + } + break; + } + case do_snarf: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_snarf")); +#endif + array flirpan = *testers[index]; +// int start = a_randomizer.inclusive(0, flirpan.length() - 1); +// int end = a_randomizer.inclusive(0, flirpan.length() - 1); +// flip_increasing(start, end); +// flirpan = flirpan.subarray(start, end); + int rand_index = a_randomizer.inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1); + if (index == rand_index) continue; // skip it; try again later. + array nugwort = *testers[rand_index]; + // perform a swap between two of our arrays. + array temp_hold(0, NIL, flags); + temp_hold.snarf(*testers[index]); + testers[index]->snarf(*testers[rand_index]); + testers[rand_index]->snarf(temp_hold); + // the copies should have flipped places now. check them. + ASSERT_EQUAL(flirpan.length(), testers[rand_index]->length(), + "snarf needs to produce right length at A"); + for (int i = 0; i < flirpan.length(); i++) + ASSERT_EQUAL(flirpan[i], testers[rand_index]->get(i), + "snarf needs to produce right array at A"); + ASSERT_EQUAL(nugwort.length(), testers[index]->length(), + "snarf needs to produce right length at B"); + for (int j = 0; j < nugwort.length(); j++) + ASSERT_EQUAL(nugwort[j], testers[index]->get(j), + "snarf needs to produce right array at B"); + break; + } + case do_flush: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_flush")); +#endif + array flirpan = *testers[index]; + +///fix up it up in it. + +/// flirpan.reset( + + + break; + } + + case do_insert: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_insert")); +#endif + array hold = *testers[index]; + int where = a_randomizer.inclusive(0, hold.last()); + int how_many = a_randomizer.inclusive(0, 25); + testers[index]->insert(where, how_many); + for (int i = 0; i < where; i++) + ASSERT_EQUAL(hold[i], testers[index]->get(i), + "should have good contents on left after insert"); + for (int j = where + how_many; j < testers[index]->length(); j++) + ASSERT_EQUAL(hold[j - how_many], testers[index]->get(j), + "should have good contents on right after insert"); + break; + } + case do_overwrite: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_overwrite")); +#endif + if (!testers[index]->length()) continue; + array hold = *testers[index]; + int index2 = index; + while (index2 == index) + index2 = a_randomizer.inclusive(0, MAX_SIMULTANEOUS_OBJECTS - 1); + array &hold2 = *testers[index2]; + if (!hold2.length()) continue; + int write_indy = a_randomizer.inclusive(0, hold.last()); + int write_len = minimum(hold2.length(), (hold.length() - write_indy)); +// LOG(a_sprintf("len1 = %d len2 = %d wrindy=%d wrlen=%d", hold.length(), hold2.length(), write_indy, write_len)); + outcome ret = testers[index]->overwrite(write_indy, + *testers[index2], write_len); + ASSERT_EQUAL(ret.value(), common::OKAY, + astring("should not have had outcome=") + common::outcome_name(ret)); + for (int i = 0; i < write_indy; i++) + ASSERT_EQUAL(hold[i], testers[index]->get(i), + "should have good contents on left after overwrite"); + for (int j = write_indy; j < write_indy + write_len; j++) + ASSERT_EQUAL(hold2[j - write_indy], testers[index]->get(j), + "should have good contents in middle after overwrite"); + for (int k = write_indy + write_len; k < testers[index]->length(); k++) + ASSERT_EQUAL(hold[k], testers[index]->get(k), + "should have good contents on right after overwrite"); + break; + } + case do_stuff: { +#ifdef DEBUG_TEST_ARRAY + LOG(a_sprintf("do_stuff")); +#endif + array &hold = *testers[index]; + contents burgers[100]; + int stuff_len = a_randomizer.inclusive(0, hold.length()); + stuff_len = minimum(stuff_len, 100); + outcome ret = hold.stuff(stuff_len, burgers); + ASSERT_EQUAL(ret.value(), common::OKAY, + astring("should not have had outcome=") + common::outcome_name(ret)); + for (int i = 0; i < stuff_len; i++) + ASSERT_EQUAL(burgers[i], hold[i], + "should have good contents after stuff"); + break; + } + default: { + ASSERT_FALSE(true, "test cases should have no invalid choices!"); + break; + } + } + } + + // clean up. + delete [] junk_space; + for (int d = 0; d < MAX_SIMULTANEOUS_OBJECTS; d++) delete testers[d]; +} + +////////////// + +struct gerkin { int l; abyte *p; char *r; void *pffttt; }; + +gerkin borgia; + +class foop +{ +public: + virtual ~foop() {} + virtual gerkin *boorba() = 0; +}; + +class boop : public foop +{ +public: + virtual gerkin *boorba() { return &borgia; } +}; + +void test_array::test_iteration_speed() +{ + FUNCDEF("test_iteration_speed"); + const int MAX_CHUNK = 100000; + const int MAX_REPS = 20; + byte_array chunky(MAX_CHUNK); + + { + time_stamp start; + int sum = 0; + for (int j = 0; j < MAX_REPS; j++) { + for (int i = 0; i < MAX_CHUNK; i++) { + sum += chunky[i]; + } + } + int duration = int(time_stamp().value() - start.value()); + + a_sprintf message("iteration over %d elements took %d ms,\t" + "or %f ms per 1000 iters.", + MAX_CHUNK * MAX_REPS, duration, + double(duration) / double(MAX_CHUNK * MAX_REPS) * 1000.0); +//base_logger &b = program_wide_logger::get(); + LOG(message); + } + + { + time_stamp start; + int sum = 0; + const abyte *head = chunky.observe(); + for (int j = 0; j < MAX_REPS; j++) { + for (int i = 0; i < MAX_CHUNK; i++) { + sum += head[i]; + } + } + int duration = int(time_stamp().value() - start.value()); + + LOG(a_sprintf("less-safe iteration over %d elements took %d ms,\tor %f ms per 1000 iters.", + MAX_CHUNK * MAX_REPS, duration, + double(duration) / double(MAX_CHUNK * MAX_REPS) * 1000.0)); + } + { + time_stamp start; + boop tester; +// int sum = 0; + for (int j = 0; j < MAX_REPS; j++) { + for (int i = 0; i < MAX_CHUNK; i++) { +// chunky.modus().sampler(); + tester.boorba(); + } + } + int duration = int(time_stamp().value() - start.value()); + + LOG(a_sprintf("virtual-function-only over %d elements took %d ms,\tor %f ms per 1000 iters.", + MAX_CHUNK * MAX_REPS, duration, + double(duration) / double(MAX_CHUNK * MAX_REPS) * 1000.0)); + } +} + +////////////// + +int test_array::execute() +{ + FUNCDEF("execute"); +#if 0 +// if enabled for the abusive type tests, then allow this one... + test_iteration_speed(); +// LOG(a_sprintf("did iteration test.")); +#endif + + int_array checking_start; + ASSERT_FALSE(checking_start.length(), + "int_array should not have contents from empty constructor"); + + double_plus bogus4 = float(12.32); + array_tester(*this, bogus4, + byte_array::SIMPLE_COPY | byte_array::EXPONE + | byte_array::FLUSH_INVISIBLE); +// LOG(a_sprintf("did float array test.")); + + int bogus2 = 39; + array_tester(*this, bogus2, + byte_array::SIMPLE_COPY | byte_array::EXPONE + | byte_array::FLUSH_INVISIBLE); +// LOG(a_sprintf("did int array test.")); + + test_content bogus3(12); + array_tester(*this, bogus3, byte_array::EXPONE + | byte_array::FLUSH_INVISIBLE); +// LOG(a_sprintf("did test_content array test.")); + + test_arrays_of_void_pointer(); +// LOG(a_sprintf("did void * array test.")); + + abyte bogus1 = 'c'; + array_tester(*this, bogus1, + byte_array::SIMPLE_COPY | byte_array::EXPONE + | byte_array::FLUSH_INVISIBLE); +// LOG(a_sprintf("did byte array test.")); + +//// LOG("array:: works for those functions tested."); + + return final_report(); +} + +HOOPLE_MAIN(test_array, ) + diff --git a/core/library/tests_basis/test_boilerplate.cpp b/core/library/tests_basis/test_boilerplate.cpp new file mode 100644 index 00000000..6a2ea798 --- /dev/null +++ b/core/library/tests_basis/test_boilerplate.cpp @@ -0,0 +1,59 @@ +/*****************************************************************************\ +* * +* Name : test_boilerplate * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Puts an object through its pacess--this is intended to provide the basic * +* framework for a unit test using the hoople testing framework. * +* * +******************************************************************************* +* Copyright (c) 2011-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), to_print) + +////////////// + +class test_boilerplate : virtual public unit_base, virtual public application_shell +{ +public: + test_boilerplate() : unit_base() {} + DEFINE_CLASS_NAME("test_boilerplate"); + virtual int execute(); +}; + +HOOPLE_MAIN(test_boilerplate, ); + +////////////// + +int test_boilerplate::execute() +{ + FUNCDEF("execute"); +//do some testing + ASSERT_TRUE(true, "true is somehow not true?"); + return final_report(); +} + diff --git a/core/library/tests_basis/test_mutex.cpp b/core/library/tests_basis/test_mutex.cpp new file mode 100644 index 00000000..47da9475 --- /dev/null +++ b/core/library/tests_basis/test_mutex.cpp @@ -0,0 +1,301 @@ +/*****************************************************************************\ +* * +* Name : test_mutex * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1994-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __WIN32__ + #include +#endif + +using namespace application; +using namespace basis; +using namespace loggers; +using namespace mathematics; +using namespace timely; +using namespace processes; +using namespace structures; +using namespace unit_test; + +//#define DEBUG_MUTEX + // uncomment for a verbose test run. + +const int MAX_MUTEX_TIMING_TEST = 2000000; + // the number of times we'll lock and unlock a mutex. + +const int DEFAULT_FISH = 32; + // the number of threads, by default. + +const int DEFAULT_RUN_TIME = 2 * SECOND_ms; + // the length of time to run the program. + +const int THREAD_PAUSE_LOWEST = 0; +const int THREAD_PAUSE_HIGHEST = 48; + // this is the range of random sleeps that a thread will take after + // performing it's actions. + +const int MIN_SAME_THREAD_LOCKING_TESTS = 100; +const int MAX_SAME_THREAD_LOCKING_TESTS = 1000; + // the range of times we'll test recursively locking the mutex. + +int concurrent_biters = 0; + // the number of threads that are currently active. + +int grab_lock = 0; + // this is upped whenever a fish obtains access to the mutex. + +mutex &guard() { static mutex _muttini; return _muttini; } + // the guard ensures that the grab lock isn't molested by two fish at + // once... hopefully. + +astring protected_string; + // this string is protected only by the mutex of guard(). + +#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print) + // our macro for logging with a timestamp. + +// expects guardian mutex to already be locked once when coming in. +void test_recursive_locking(chaos &_rando) +{ + int test_attempts = _rando.inclusive(MIN_SAME_THREAD_LOCKING_TESTS, + MAX_SAME_THREAD_LOCKING_TESTS); + int locked = 0; + for (int i = 0; i < test_attempts; i++) { + bool lock = !!(_rando.inclusive(0, 1)); + if (lock) { + guard().lock(); + locked++; // one more lock. + } else { + if (locked > 0) { + // must be sure we are not already locally unlocked completely. + guard().unlock(); + locked--; + } + } + } + for (int j = 0; j < locked; j++) { + // drop any locks we had left during the test. + guard().unlock(); + } +} + +//hmmm: how are these threads different so far? they seem to do exactly +// the same thing. maybe one should eat chars from the string. + +#undef UNIT_BASE_THIS_OBJECT +#define UNIT_BASE_THIS_OBJECT c_testing + +class piranha : public ethread +{ +public: + chaos _rando; // our randomizer. + unit_base &c_testing; // provides for test recording. + + piranha(unit_base &testing) : ethread(0), c_testing(testing) { + FUNCDEF("constructor"); + safe_add(concurrent_biters, 1); + ASSERT_TRUE(concurrent_biters >= 1, "the piranha is very noticeable"); +//LOG(astring(astring::SPRINTF, "there are %d biters.", concurrent_biters)); + } + + virtual ~piranha() { + FUNCDEF("destructor"); + safe_add(concurrent_biters, -1); +//LOG("reached piranha destructor."); + } + + DEFINE_CLASS_NAME("piranha"); + + void perform_activity(void *formal(data)) { + FUNCDEF("perform_activity"); + { + // we grab the lock. + auto_synchronizer locked(guard()); + // in this case, we make use of auto-synchronizer, handily testing it as well. + ASSERT_TRUE(&locked != NIL, "auto_synchronizer should grab the mutex object's lock"); + // this is not a real test, but indicates that we did actually increase the number of + // unit tests by one, since we're using auto_synchronizer now. + safe_add(grab_lock, 1); + ASSERT_TRUE(grab_lock <= 1, "grab lock should not already be active"); + protected_string += char(_rando.inclusive('a', 'z')); + + test_recursive_locking(_rando); + + safe_add(grab_lock, -1); + } + // dropped the lock. snooze a bit. + if (!should_stop()) + time_control::sleep_ms(_rando.inclusive(THREAD_PAUSE_LOWEST, THREAD_PAUSE_HIGHEST)); + } + +}; + +class barracuda : public ethread +{ +public: + chaos _rando; // our randomizer. + unit_base &c_testing; // provides for test recording. + + barracuda(unit_base &testing) : ethread(0), c_testing(testing) { + FUNCDEF("constructor"); + safe_add(concurrent_biters, 1); + ASSERT_TRUE(concurrent_biters >= 1, "our presence should have been noticed"); +//LOG(astring(astring::SPRINTF, "there are %d biters.", concurrent_biters)); + } + + virtual ~barracuda() { + FUNCDEF("destructor"); + safe_add(concurrent_biters, -1); +//LOG("reached barracuda destructor."); + } + + DEFINE_CLASS_NAME("barracuda"); + + void perform_activity(void *formal(data)) { + FUNCDEF("perform_activity"); + // we grab the lock. + guard().lock(); + safe_add(grab_lock, 1); + ASSERT_TRUE(grab_lock <= 1, "grab lock should not already be active"); + + test_recursive_locking(_rando); + + protected_string += char(_rando.inclusive('a', 'z')); + safe_add(grab_lock, -1); + guard().unlock(); + // done with the lock. sleep for a while. + if (!should_stop()) + time_control::sleep_ms(_rando.inclusive(THREAD_PAUSE_LOWEST, THREAD_PAUSE_HIGHEST)); + } +}; + +////////////// + +#undef UNIT_BASE_THIS_OBJECT +#define UNIT_BASE_THIS_OBJECT (*this) + +class test_mutex : virtual public unit_base, virtual public application_shell +{ +public: + chaos _rando; // our randomizer. + + test_mutex() : application_shell() {} + + DEFINE_CLASS_NAME("test_mutex"); + + int execute(); +}; + +int test_mutex::execute() +{ + FUNCDEF("execute"); + + { + // we check how long a lock and unlock of a non-locked mutex will take. + // this is important to know so that we aren't spending much of our time + // locking mutexes just due to the mechanism. + mutex ted; + time_stamp mutt_in; + for (int qb = 0; qb < MAX_MUTEX_TIMING_TEST; qb++) { + ted.lock(); + ted.unlock(); + } + time_stamp mutt_out; +#ifdef DEBUG_MUTEX + log(a_sprintf("%d mutex lock & unlock pairs took %f seconds,", + MAX_MUTEX_TIMING_TEST, + (mutt_out.value() - mutt_in.value()) / SECOND_ms)); + log(a_sprintf("or %f ms per (lock+unlock).", + (mutt_out.value() - mutt_in.value()) / MAX_MUTEX_TIMING_TEST)); +#endif + } + + // make sure the guard is initialized before the threads run. + guard().lock(); + guard().unlock(); + + amorph thread_list; + + for (int i = 0; i < DEFAULT_FISH; i++) { + ethread *t = NIL; + if (i % 2) t = new piranha(*this); + else t = new barracuda(*this); + thread_list.append(t); + ethread *q = thread_list[thread_list.elements() - 1]; + ASSERT_EQUAL(q, t, "amorph pointer equivalence is required"); + // start the thread we added. + thread_list[thread_list.elements() - 1]->start(NIL); + } + + time_stamp when_to_leave(DEFAULT_RUN_TIME); + while (when_to_leave > time_stamp()) { + time_control::sleep_ms(100); + } + +//hmmm: try just resetting the amorph; +// that should work fine. + +#ifdef DEBUG_MUTEX + LOG("now cancelling all threads...."); +#endif + + for (int j = 0; j < thread_list.elements(); j++) thread_list[j]->cancel(); + +#ifdef DEBUG_MUTEX + LOG("now stopping all threads...."); +#endif + + for (int k = 0; k < thread_list.elements(); k++) thread_list[k]->stop(); + + int threads_active = 0; + for (int k = 0; k < thread_list.elements(); k++) { + if (thread_list[k]->thread_active()) threads_active++; + } + ASSERT_EQUAL(threads_active, 0, "threads should actually have stopped by now"); + +#ifdef DEBUG_MUTEX + LOG("resetting thread list...."); +#endif + + thread_list.reset(); // should whack all threads. + + ASSERT_EQUAL(concurrent_biters, 0, "threads should all be gone by now"); + +#ifdef DEBUG_MUTEX + LOG("done exiting from all threads...."); + + LOG(astring(astring::SPRINTF, "the accumulated string had %d characters " + "which means\nthere were %d thread activations from %d threads.", + protected_string.length(), protected_string.length(), + DEFAULT_FISH)); +#endif + + return final_report(); +} + +HOOPLE_MAIN(test_mutex, ) + diff --git a/core/library/tests_basis/test_string.cpp b/core/library/tests_basis/test_string.cpp new file mode 100644 index 00000000..f89318a9 --- /dev/null +++ b/core/library/tests_basis/test_string.cpp @@ -0,0 +1,1279 @@ +/*****************************************************************************\ +* * +* Name : test_string * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//#define DEBUG_STRING + // set this to enable debugging features of the string class. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __WIN32__ + #include +#endif +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +//HOOPLE_STARTUP_CODE; + +//#define DEBUG_STRING_TEST + // uncomment for testing version. + +const float TEST_RUNTIME_DEFAULT = .02 * MINUTE_ms; + // the test, by default, will run for this long. + +////////////// + +class test_string : public application_shell, public unit_base +{ +public: + test_string() {} + ~test_string() {} + + DEFINE_CLASS_NAME("test_string"); + + virtual int execute(); + + void run_test_01(); + void run_test_02(); + void run_test_03(); + void run_test_04(); + void run_test_05(); + void run_test_06(); + void run_test_07(); + void run_test_08(); + void run_test_09(); + void run_test_10(); + void run_test_11(); + void run_test_12(); + void run_test_13(); + void run_test_14(); + void run_test_15(); + void run_test_16(); + void run_test_17(); + void run_test_18(); + void run_test_19(); + void run_test_20(); + void run_test_21(); + void run_test_22(); + void run_test_23(); + void run_test_24(); + void run_test_25(); + void run_test_26(); + void run_test_27(); + void run_test_28(); + void run_test_29(); + void run_test_30(); + void run_test_31(); + void run_test_32(); + void run_test_33(); + void run_test_34(); + void run_test_35(); + void run_test_36(); + void run_test_37(); + void run_test_38(); + void run_test_39(); + void run_test_40(); + void run_test_41(); + void run_test_42(); +}; + +////////////// + +chaos rando; + +#define LOG(s) EMERGENCY_LOG(program_wide_logger::get(), s) + +#define WHERE __WHERE__.s() + +// test: reports an error if the condition evaluates to non-zero. +#define test(expr) { \ + ASSERT_FALSE(expr, astring("operator test should work: ") + #expr); \ +} + +static basis::astring staticity_test("yo!"); + // used below to check whether static strings are looking right. + +////////////// + +void test_string::run_test_01() +{ + FUNCDEF("run_test_01"); + +// const int TEST_EMPTY = 10000000; +// time_stamp started; +// for (int i = 0; i < TEST_EMPTY; i++) { +// astring glob = astring::empty_string(); +// } +// int duration = int(time_stamp().value() - started.value()); +// LOG(a_sprintf("duration of empty string test=%d ms", duration)); + + // test simple string operations, like construction, length, equality. + astring fred1("hyeargh!"); + astring fred2("matey."); + astring fred3; + fred3 = fred1; + fred3 += fred2; + astring fred4(fred2); + + ASSERT_EQUAL(fred1.length(), int(strlen(fred1.s())), "length should be correct (a)."); + ASSERT_EQUAL(fred2.length(), int(strlen(fred2.s())), "length should be correct (b)."); + ASSERT_EQUAL(fred3.length(), int(strlen(fred3.s())), "length should be correct (c)."); + ASSERT_EQUAL(fred4.length(), int(strlen(fred4.s())), "length should be correct (d)."); + +#ifdef DEBUG_STRING_TEST + LOG("[ " + fred1 + " & " + fred2 + "] -> " + fred3); +#endif + + ASSERT_EQUAL(fred1, astring("hyeargh!"), "failure in comparison (a)."); + ASSERT_EQUAL(fred2, astring("matey."), "failure in comparison (b)."); + ASSERT_EQUAL(fred3, astring("hyeargh!matey."), "failure in comparison (c)."); + ASSERT_EQUAL(fred4, astring("matey."), "failure in comparison (d-1)."); + ASSERT_EQUAL(fred4, fred2, "failure in comparison (d-2)."); + + a_sprintf nullo; + ASSERT_EQUAL(nullo, astring(), "forward blank a_sprintf isn't blank."); + ASSERT_EQUAL(astring(), nullo, "backward blank a_sprintf isn't blank."); + ASSERT_EQUAL(nullo, astring::empty_string(), "forward blank a_sprintf isn't empty."); + ASSERT_EQUAL(astring::empty_string(), nullo, "backward blank a_sprintf isn't empty."); +} + +void test_string::run_test_02() +{ + FUNCDEF("run_test_02"); + // assorted tests involving strings as pointers. + astring *fred1 = new astring("flipper ate"); + astring *fred2 = new astring(" my sandwich."); + astring *fred3 = new astring; + *fred3 = *fred1; + *fred3 += *fred2; + + // testing adding a null to a string. + *fred2 += (char *)NIL; + *fred3 += (char *)NIL; + +#ifdef DEBUG_STRING_TEST + LOG(astring("[ ") + *fred1 + " & " + *fred2 + "] -> " + *fred3); +#endif + + ASSERT_EQUAL(*fred1, astring("flipper ate"), "flipper A failure in comparison"); + ASSERT_EQUAL(*fred2, astring(" my sandwich."), "sandwich A failure in comparison"); + ASSERT_EQUAL(*fred3, astring("flipper ate my sandwich."), "full f-s A failure in comparison"); + delete fred1; + delete fred2; + delete fred3; +} + +void test_string::run_test_03() +{ + FUNCDEF("run_test_03"); + // tests some things about zap. + astring fleermipe("hello my frobious."); + fleermipe.zap(0, fleermipe.length() - 1); + ASSERT_EQUAL(fleermipe.length(), 0, "length not 0 after deleting entire astring"); +} + +void test_string::run_test_04() +{ + FUNCDEF("run_test_04"); + astring test_string("this test string will be chopped up."); +#ifdef DEBUG_STRING_TEST + LOG(astring("original is: ") + test_string); +#endif + astring fred(test_string.s()); + fred.zap(0, fred.find('w')); + +#ifdef DEBUG_STRING_TEST + LOG(astring("now, the one chopped through 'w' is: ") + fred); +#endif + + ASSERT_EQUAL(fred, astring("ill be chopped up."), "first zap failed"); + + astring blorg(test_string); + blorg.zap(blorg.find('p'), blorg.length() - 1); +#ifdef DEBUG_STRING_TEST + LOG(astring("now the one chopped from p to the end: ") + blorg); +#endif + + ASSERT_EQUAL(blorg, astring("this test string will be cho"), "second zap failed"); + + astring fleen; + fleen += test_string; + fleen.zap(7, 14); +#ifdef DEBUG_STRING_TEST + LOG(astring("now the one with 7 through 14 missing: ") + fleen); +#endif + + ASSERT_EQUAL(fleen, astring("this teg will be chopped up."), "third zap failed"); + +#ifdef DEBUG_STRING_TEST + LOG(astring("original astring is now: ") + test_string); +#endif + ASSERT_EQUAL(test_string, astring("this test string will be chopped up."), + "original astring was changed"); +} + +void test_string::run_test_05() +{ + FUNCDEF("run_test_05"); +#ifdef DEBUG_STRING_TEST + LOG("about to test weird things:"); +#endif + astring frieda("glorp"); + astring jorb(frieda); + astring *kleeg = new astring(jorb.s()); + astring plok = frieda; + test(frieda != jorb); + test(jorb != *kleeg); + test(*kleeg != plok); + test(plok != frieda); + astring glorp("glorp"); + test(frieda != glorp); + + WHACK(kleeg); + +#ifdef DEBUG_STRING_TEST + LOG("strings matched up okay."); +#endif + + // test new features sprintf is relying upon. + astring bubba("gumpternations"); + bubba += "_02193"; +#ifdef DEBUG_STRING_TEST + LOG(astring("bubba = ") + bubba); +#endif + ASSERT_EQUAL(bubba, astring("gumpternations_02193"), "+= on char pointer failed."); + + astring bubelah("laksos"); + bubelah += '3'; + bubelah += '8'; + bubelah += '2'; +#ifdef DEBUG_STRING_TEST + LOG(astring("bubelah = ") + bubelah); +#endif + ASSERT_EQUAL(bubelah, astring("laksos382"), "+= on char failed."); + + astring simple_spr0(astring::SPRINTF, "%% yoga splorch %%"); +#ifdef DEBUG_STRING_TEST + LOG(astring("simple sprintf 0 = ") + simple_spr0); +#endif + ASSERT_EQUAL(simple_spr0, astring("% yoga splorch %"), "simple sprintf 0 is wrong"); + astring simple_spr1(astring::SPRINTF, "%d", 23); +#ifdef DEBUG_STRING_TEST + LOG(astring("simple sprintf 1 = ") + simple_spr1); +#endif + ASSERT_EQUAL(simple_spr1, astring("23"), "simple sprintf 1 is wrong"); + astring simple_spr2(astring::SPRINTF, "%s", "yoyo"); +#ifdef DEBUG_STRING_TEST + LOG(astring("simple sprintf 2 = ") + simple_spr2); +#endif + ASSERT_EQUAL(simple_spr2, astring("yoyo"), "simple sprintf 2 is wrong"); + + astring sprintest(astring::SPRINTF, "%s has got me up the %s some %d " + "times, in %p with %d and %lu.", "marge", "ladder", 32, &kleeg, + 812377487L, 213123123L); + astring sprintest2; + sprintest2.reset(astring::SPRINTF, "%s has got me up the %s some %d " + "times, in %p with %d and %lu.", "marge", "ladder", 32, &kleeg, + 812377487L, 213123123L); + astring addr(astring::SPRINTF, "%p", &kleeg); +#ifdef DEBUG_STRING_TEST + LOG("here is your astring sir..."); + LOG(sprintest); + LOG("and addr we will see is..."); + LOG(addr); +#endif + if (sprintest != astring(astring::SPRINTF, "marge has got me up the " + "ladder some 32 times, in %s with 812377487 and 213123123.", addr.s())) + "constructed astring is wrong"; + if (sprintest2 != astring(astring::SPRINTF, "marge has got me up the " + "ladder some 32 times, in %s with 812377487 and 213123123.", addr.s())) + "reset astring is wrong"; +} + +void test_string::run_test_06() +{ + FUNCDEF("run_test_06"); + astring bungee; + bungee += "this astring"; + bungee += " has been constructed gradua"; + bungee += 'l'; + bungee += 'l'; + bungee += 'y'; + astring blorpun(astring::SPRINTF, " out of severa%c", 'l'); + bungee += blorpun; + bungee += " different bits,\nincluding"; + astring freeple(astring::SPRINTF, "%s constructed %s blarg from %f ", " this", "silly", 3.14159265358); + bungee += freeple; + bungee += "radians awa"; + bungee += 'y'; + bungee += '.'; + bungee += "\nhow does it look?\n"; +#ifdef DEBUG_STRING_TEST + LOG(bungee); +#endif + +//MessageBox(0, bungee.s(), "yo, got this", MB_ICONINFORMATION | MB_OK); + ASSERT_EQUAL(bungee, astring("this astring has been constructed gradually out of several different bits,\nincluding this constructed silly blarg from 3.141593 radians away.\nhow does it look?\n"), "constructed astring is wrong"); +} + +void test_string::run_test_07() +{ + FUNCDEF("run_test_07"); + astring a("axes"); + astring b("bakesales"); + astring x("xylophone"); + + test(a >= x); + test(a == b); + test(a > b); + test(b >= x); + test(x <= a); + test(x != x); + test(a != a); +#ifdef DEBUG_STRING_TEST + LOG("comparisons worked"); +#endif +} + +void test_string::run_test_08() +{ + FUNCDEF("run_test_08"); +#ifdef DEBUG_STRING_TEST + LOG("now testing + operator"); +#endif + astring a("fred"); + astring b(" is"); + astring c(" his"); + astring d(" name."); + astring e; + e = a + b + c + d; +#ifdef DEBUG_STRING_TEST + LOG(astring("result is: ") + e); +#endif + astring f; + f = d + c + b + a; +#ifdef DEBUG_STRING_TEST + LOG(astring("reverse is: ") + f); +#endif + astring g; + g = a + c + d + b; +#ifdef DEBUG_STRING_TEST + LOG(astring("tibetan style is: ") + g); +#endif + ASSERT_EQUAL(e, astring("fred is his name."), "astring looks wrong"); + ASSERT_EQUAL(f, astring(" name. his isfred"), "astring looks wrong"); + ASSERT_EQUAL(g, astring("fred his name. is"), "astring looks wrong"); +} + +void test_string::run_test_09() +{ + FUNCDEF("run_test_09"); + astring bleer(astring::SPRINTF, "urghla burgla #%d\n", 23); + char holder[50]; + for (int i = 0; i < bleer.length(); i++) { + bleer.stuff(holder, i); +#ifdef DEBUG_STRING_TEST + LOG(astring(astring::SPRINTF, "%d", i) + " has: " + holder); +#endif + astring my_copy(bleer); + my_copy.zap(i, bleer.length() - 1); + ASSERT_EQUAL(my_copy, astring(holder), "should see no inaccurate stuff() call"); + } +} + +void test_string::run_test_10() +{ + FUNCDEF("run_test_10"); +#ifdef DEBUG_STRING_TEST + LOG("The tenth ones:"); +#endif + astring george("this one will be mangled."); + ASSERT_EQUAL(george.length(), int(strlen(george.s())), "length is incorrect (q)."); + astring tmp1(george.substring(1, 7)); // constructor. + astring tmp2 = george.substring(10, george.length() - 1); // constructor. +#ifdef DEBUG_STRING_TEST + LOG(tmp1 + "... " + tmp2); +#endif + ASSERT_INEQUAL(tmp1, tmp2, "bizarre equality occurred!"); + ASSERT_FALSE( (tmp1 > tmp2) || (tmp2 < tmp1), "bizarre comparison error."); + ASSERT_EQUAL(george.length(), int(strlen(george.s())), "length is incorrect (z)."); +#ifdef DEBUG_STRING_TEST + LOG(george.substring(1, 7)); + LOG("... "); + LOG(george.substring(10, george.length() - 1)); +#endif + ASSERT_EQUAL(george.length(), int(strlen(george.s())), "length is incorrect (a)."); + george.insert(14, "terribly "); + ASSERT_EQUAL(george.length(), int(strlen(george.s())), "length is incorrect (b)."); +#ifdef DEBUG_STRING_TEST + LOG(george); +#endif + astring chunky; + astring mssr_boef_la_tet("eeyoy eye eye capn"); + mssr_boef_la_tet.substring(chunky, 2, 7); + ASSERT_EQUAL(chunky, astring("yoy ey"), "contents wrong after substring"); + + astring fred(george); + ASSERT_TRUE(george.compare(fred, 0, 0, george.length() - 1, true), "did not work"); + ASSERT_TRUE(george.compare(fred, 8, 8, george.length() - 1 - 8, true), "partial did not work"); + + astring taco1("iLikeTacosNSuch"); + astring taco2("iLikeTaCosNSuch"); + ASSERT_TRUE(taco1.compare(taco2, 0, 0, taco1.length() - 1, false), + "tacos case-insensitive compare A did not work"); + ASSERT_FALSE(taco1.compare(taco2, 0, 0, taco1.length() - 1, true), + "tacos case-sensitive compare B did not work"); + + ASSERT_EQUAL(george.length(), int(strlen(george.s())), "length is incorrect (c)."); + george.zap(14, 22); +#ifdef DEBUG_STRING_TEST + LOG(george); +#endif + astring fred_part; + fred_part = fred.substring(0, 13); + ASSERT_EQUAL(fred_part.length(), int(strlen(fred_part.s())), "length incorrect (d)."); + ASSERT_TRUE(george.compare(fred_part, 0, 0, 13, true), "did not work"); + fred_part = fred.substring(23, fred.length() - 1); + ASSERT_EQUAL(fred_part.length(), int(strlen(fred_part.s())), "length incorrect (e)."); + ASSERT_TRUE(george.compare(fred_part, 14, 0, fred_part.length() - 1, true), "did not work"); +#ifdef DEBUG_STRING_TEST + LOG("compares okay"); +#endif +} + +void test_string::run_test_11() +{ + FUNCDEF("run_test_11"); + astring empty; + ASSERT_FALSE(empty.length(), "empty string judged not"); + ASSERT_TRUE(!empty, "not on empty string doesn't work"); + astring non_empty("grungee"); + ASSERT_TRUE(non_empty.length(), "non-empty string judged empty"); + ASSERT_FALSE(!non_empty, "not on non-empty string doesn't work"); +} + +void test_string::run_test_12() +{ + FUNCDEF("run_test_12"); + astring elf0(astring::SPRINTF, "%%_%%_%%"); + ASSERT_FALSE(strcmp(elf0.s(), "%_%_%"), "failed %% printing"); + + char fred[6] = { 'h', 'y', 'a', 'r', 'g', '\0' }; + astring fred_copy(astring::UNTERMINATED, fred, 5); + ASSERT_EQUAL(fred_copy.length(), 5, "length of copy is wrong"); + ASSERT_EQUAL(fred_copy, astring("hyarg"), "length of copy is wrong"); + + char hugh[6] = { 'o', 'y', 'o', 'b', 'o', 'y' }; + astring hugh_copy(astring::UNTERMINATED, hugh, 3); + ASSERT_EQUAL(hugh_copy.length(), 3, "length of copy is wrong"); + ASSERT_EQUAL(hugh_copy, astring("oyo"), "length of copy is wrong"); + + astring another_copy; + another_copy.reset(astring::UNTERMINATED, fred, strlen(fred)); + ASSERT_EQUAL(another_copy.length(), 5, "length of reset copy is wrong"); + ASSERT_EQUAL(another_copy, astring("hyarg"), "length of reset copy is wrong"); +} + +void test_string::run_test_13() +{ +// FUNCDEF("run_test_13"); + // check for possible memory leaks in these combined ops.... 13th. + const astring churg("borjh sjh oiweoklj"); + astring pud = churg; + astring flug = "kase iqk aksjir kljasdo"; + const char *nerf = "ausd qwoeui sof zjh qwei"; + astring snorp = nerf; + pud = "snugbo"; + snorp = flug; + astring pundit("murph"); + pundit = nerf; +} + +void test_string::run_test_14() +{ + FUNCDEF("run_test_14"); + // test the new dynamic sprintf'ing for long strings... 14th. + const int longish_size = 5000; + char temp[longish_size]; + for (int i = 0; i < longish_size; i++) + temp[i] = char(rando.inclusive(1, 255)); + temp[longish_size - 1] = '\0'; + a_sprintf longish("this is a really darned long string of stuff: %s,\nbut doesn't it look interesting?", temp); + // still with us? + longish.zap(longish.length() / 3, 2 * longish.length() / 3); + longish += longish; + ASSERT_EQUAL(longish.length(), int(strlen(longish.s())), "length is incorrect."); +} + +void test_string::run_test_15() +{ + FUNCDEF("run_test_15"); + // test the new dynamic sprintf'ing for long strings... 15th. + int try_1 = 32767; + astring testy(astring::SPRINTF, "%d", try_1); + ASSERT_INEQUAL(testy.convert(95), 95, "default value returned, so it failed."); + ASSERT_EQUAL(testy.convert(95), try_1, "correct value was not returned."); + + long try_2 = 2938754L; + testy = astring(astring::SPRINTF, "%ld", try_2); + ASSERT_INEQUAL((double)testy.convert(98L), (double)98L, "default value returned, so it failed."); + ASSERT_EQUAL((double)testy.convert(98L), (double)try_2, "correct value was not returned."); + + testy = astring(astring::SPRINTF, "%ld", try_2); + ASSERT_INEQUAL((double)testy.convert(98L), (double)98L, "default value returned, so it failed."); + ASSERT_EQUAL((double)testy.convert(98L), (double)try_2, "correct value was not returned."); + + float try_3 = float(2938.754); // weird msvcpp error if don't cast. + testy = astring(astring::SPRINTF, "%f", try_3); + float found = testy.convert(float(98.6)); + ASSERT_INEQUAL(found, 98.6, "default value returned, so it failed."); + ASSERT_EQUAL(found, try_3, "correct value was not returned."); + + { + double try_4 = 25598437.712385; + testy = astring(astring::SPRINTF, "%f", try_4); + double found2 = testy.convert(double(98.6)); + ASSERT_INEQUAL(found2, 98.6, "default value returned, so it failed."); + ASSERT_EQUAL(found2, try_4, "correct value was not returned."); + } + + { + double try_4 = 25598437.712385e38; + testy = astring(astring::SPRINTF, "%f", try_4); + double found2 = testy.convert(double(98.6)); + ASSERT_INEQUAL(found2, 98.6, "default value returned, so it failed."); + ASSERT_EQUAL(found2, try_4, "correct value was not returned."); + } +} + +void test_string::run_test_16() +{ + FUNCDEF("run_test_16"); + // the 16th test group tests boundary checking. + astring gorf("abc"); + for (int i = -3; i < 25; i++) gorf[i] = 't'; + ASSERT_EQUAL(gorf, astring("ttt"), "correct value was not returned (a)."); + ASSERT_EQUAL(gorf.length(), 3, "length is incorrect (a)."); + gorf.insert(3, astring("bru")); + ASSERT_EQUAL(gorf, astring("tttbru"), "correct value was not returned (b)."); + ASSERT_EQUAL(gorf.length(), 6, "length is incorrect (b)."); + gorf.insert(35, astring("snu")); + ASSERT_EQUAL(gorf, astring("tttbru"), "correct value was not returned (c)."); + ASSERT_EQUAL(gorf.length(), 6, "length is incorrect (c)."); + gorf.insert(-30, astring("eep")); + ASSERT_EQUAL(gorf, astring("tttbru"), "correct value was not returned (d)."); + ASSERT_EQUAL(gorf.length(), 6, "length is incorrect (d)."); +} + +void test_string::run_test_17() +{ +// FUNCDEF("run_test_17"); + // 17th test checks construction of temporaries. +/* this test set causes the obnoxious 16 bit codeguard error from hell, as + does use of temporary objects in ostream << operators. argh! */ + astring("jubjo"); + astring("obo"); + astring("flrrpominort").substring(3, 6); +} + +void test_string::run_test_18() +{ +#ifdef DEBUG_STRING_TEST + FUNCDEF("run_test_18"); +#endif + // 18th test group checks windows related functions. +#ifdef AFXDLL + AfxSetResourceHandle(GET_INSTANCE_HANDLE()); + // required for mfc to see the proper instance handle. + + // this tests the "constructor from resource". + astring libname = rc_string(IDS_BASIS_NAME); + ASSERT_EQUAL(libname, astring("Basis Library"), + astring("library name is a mismatch: comes out as \"") + libname + "\"."); + #define IDS_SOME_BAD_UNKNOWN_STRING_HANDLE 30802 + astring bogus_name = rc_string(IDS_SOME_BAD_UNKNOWN_STRING_HANDLE); + ASSERT_FALSE(bogus_name.length(), "bogus rc string had length"); + + // tests conversions from CString to astring and back. + astring george("yo yo yo"); + CString hal = convert(george); + astring borgia = convert(hal); + +#ifdef DEBUG_STRING_TEST + LOG(astring("cstringy conversions: ") + george); + LOG((const char *)hal); + LOG(borgia); +#endif + + ASSERT_EQUAL(borgia, george, "got the wrong value"); +#endif +} + +void test_string::run_test_19() +{ + FUNCDEF("run_test_19"); + // 19th test group is devoted to anthony wenzel's % printing bug. + astring problematic_example(astring::SPRINTF, "this should have %d% more " + "stuffing than before!", 20); +//MessageBox(0, astring("got a string of \"%s!\"", problematic_example.s()).s(), "yo!", MB_OK); + ASSERT_EQUAL(problematic_example, astring("this should have 20% more stuffing than before!"), "failure to print correct phrase"); +} + +void test_string::run_test_20() +{ +#ifdef DEBUG_STRING_TEST + FUNCDEF("run_test_20"); +#endif + // 20th test group is devoted to another wenzelbug. + + // Hey, I just found out (in an ugly way) that the following doesn't work: + char myText[] = "OK"; + astring myString(astring::SPRINTF, "%04s", myText); +#ifdef DEBUG_STRING_TEST + LOG(astring("first try gets: ") + myString); +#endif + + char myText8[] = "OK"; + char my_text_4[90]; + sprintf(my_text_4, "%4s", myText8); +#ifdef DEBUG_STRING_TEST + LOG(astring("second try gets: ") + astring(my_text_4)); +#endif + + // Looks like you don't handle the "%04s" arguments properly. I can make + // it work as follows: + char myText2[] = "OK"; + char myText3[50]; + sprintf(myText3, "%4s", myText2); + astring myString2(myText3); +#ifdef DEBUG_STRING_TEST + LOG(astring("third try gets: ") + myString2); +#endif +} + +void test_string::run_test_21() +{ + FUNCDEF("run_test_21"); + // 21st test group checks out the strip spaces and replace functions. + astring spacey(" dufunk "); + astring temp = spacey; + temp.strip_spaces(astring::FROM_FRONT); + ASSERT_EQUAL(temp, astring("dufunk "), "created wrong string"); + temp = spacey; + temp.strip_spaces(astring::FROM_END); + ASSERT_EQUAL(temp, astring(" dufunk"), "created wrong string"); + temp = spacey; + temp.strip_spaces(astring::FROM_BOTH_SIDES); + ASSERT_EQUAL(temp, astring("dufunk"), "created wrong string"); + + astring placemat("mary had a red hooded cobra she kept around her neck " + "and it hissed at the people as they walked by her tent."); + ASSERT_TRUE(placemat.replace("had", "bought"), "replace failed"); + ASSERT_TRUE(!placemat.replace("hoded", "bought"), "replace didn't fail but should have"); + ASSERT_TRUE(placemat.replace("she", "funkateria"), "replace failed"); + ASSERT_TRUE(placemat.replace("hooded", "hood"), "replace failed"); + ASSERT_TRUE(placemat.replace("cobra", "in the"), "replace failed"); + + int indy = placemat.find("kept"); + ASSERT_FALSE(negative(indy), "couldn't find string"); + placemat[indy - 1] = '.'; + placemat.zap(indy, placemat.end()); + ASSERT_EQUAL(placemat, astring("mary bought a red hood in the funkateria."), "got wrong result string"); +} + +void test_string::run_test_22() +{ + FUNCDEF("run_test_22"); + // 22nd test: morgan's find bug. + astring B("buzz*buzz*"); + { + int x = B.find('*'); // correctly finds the first *. + ASSERT_EQUAL(x, 4, "got wrong index for first"); + x++; + x = B.find('*', x); // correctly finds the second *. + ASSERT_EQUAL(x, 9, "got wrong index for second"); + x++; // now x == B.length(). + x = B.find('*', x); + // error was: finds the second * again (and again and again and + // again...). At this point it should return OUT_OF_RANGE. + ASSERT_FALSE(x > 0, "got wrong outcome for third"); + } + { + int x = B.find("z*"); // correctly finds the first z*. + ASSERT_EQUAL(x, 3, "got wrong index for fourth"); + x++; + x = B.find("z*", x); // correctly finds the second *. + ASSERT_EQUAL(x, 8, "got wrong index for fifth"); + x++; // now x == B.length(). + x = B.find("z*", x); + // error was: finds the second * again (and again and again and + // again...). At this point it should return OUT_OF_RANGE. + ASSERT_FALSE(x > 0, "got wrong outcome for sixth"); + } +} + +void test_string::run_test_23() +{ + FUNCDEF("run_test_23"); + // 23rd test: test the new strip function. + astring strip_string("!@#$%^&*()"); + + astring no_stripper("this shouldn't change"); + no_stripper.strip(strip_string, astring::FROM_BOTH_SIDES); + ASSERT_EQUAL(no_stripper, astring("this shouldn't change"), "first test failed comparison"); + + astring strippee_orig("&$(*(@&@*()()!()@*(@(*fudge#((@(*@**#)(@#)(#"); + astring strippee(strippee_orig); + strippee.strip(strip_string, astring::FROM_BOTH_SIDES); + ASSERT_EQUAL(strippee, astring("fudge"), "second test failed comparison"); + + strippee = strippee_orig; + strippee.strip(strip_string, astring::FROM_FRONT); + ASSERT_EQUAL(strippee, astring("fudge#((@(*@**#)(@#)(#"), "third test failed comparison"); + + strippee = strippee_orig; + strippee.strip(strip_string, astring::FROM_END); + ASSERT_EQUAL(strippee, astring("&$(*(@&@*()()!()@*(@(*fudge"), "fourth test failed comparison"); +} + +void test_string::run_test_24() +{ + FUNCDEF("run_test_24"); +#ifdef __WIN32__ + // 24th test group tests _bstr_t conversions. + _bstr_t beast("abcdefgh"); +#ifdef DEBUG_STRING_TEST + LOG(astring("the beast is ") + beast.operator char *() + + astring(astring::SPRINTF, " with length %d", beast.length())); +#endif + astring convert = beast; +#ifdef DEBUG_STRING_TEST + LOG(astring("the convert is ") + convert + + astring(astring::SPRINTF, " with length %d", convert.length())); +#endif + ASSERT_EQUAL(convert, astring("abcdefgh"), "first test failed comparison"); + + astring jethro("i want a hog sandwich"); + _bstr_t pork = string_convert::to_bstr_t(jethro); + ASSERT_FALSE(strcmp(pork.operator char *(), jethro.s()), "second test failed comparison"); +#endif +} + +void test_string::run_test_25() +{ + FUNCDEF("run_test_25"); + // 25th test group tests simple comparisons. + astring fred = "asdoiuaoisud"; + ASSERT_INEQUAL(fred, astring(), "first test failed comparison"); + astring bub = fred; + ASSERT_EQUAL(fred, bub, "second test failed comparison"); + fred = ""; + ASSERT_EQUAL(fred, astring(), "third test failed comparison"); + ASSERT_FALSE(fred.length(), "fourth test failed comparison"); +} + +void test_string::run_test_26() +{ +// FUNCDEF("run_test_26"); + // 26th test group does simple time_stamp::notarize operations. these are more for + // ensuring boundschecker gets to see some of this. + astring t2 = time_stamp::notarize(false); + astring t4 = time_stamp::notarize(true); +} + +void test_string::run_test_27() +{ +// FUNCDEF("run_test_27"); + // 27th test group plays around with idate in an attempt to get + // boundschecker to complain. + timely::day_in_year d1 = date_now(); + timely::clock_time t1 = time_now(); + timely::time_locus dt1 = now(); + astring sd1 = d1.text_form(); + astring st1 = t1.text_form(); + astring sdt1 = dt1.text_form_long(); +} + +void test_string::run_test_28() +{ + FUNCDEF("run_test_28"); + // 28th test group does sprintfs on shorts and such. + basis::un_int in1 = 27; + basis::un_short in2 = 39; + char in3 = 'Q'; + astring testy(astring::SPRINTF, "%u:%hu:%c", in1, in2, in3); + ASSERT_EQUAL(testy, astring("27:39:Q"), "fourth test failed comparison"); +} + +void test_string::run_test_29() +{ + FUNCDEF("run_test_29"); + // 29th test group tries out the packing support. + astring a("would an onion smell so sweet?"); + byte_array p1; + a.pack(p1); + astring b; + ASSERT_TRUE(b.unpack(p1), "first unpack failed"); + ASSERT_EQUAL(b, a, "first comparison failed"); + a = "128 salamanders cannot be wrong."; + a.pack(p1); + ASSERT_TRUE(b.unpack(p1), "second unpack failed"); + ASSERT_EQUAL(b, a, "second comparison failed"); +} + +void standard_sprintf_test(const char *parm_string) +{ +// FUNCDEF("standard_sprintf_test"); + astring print_into(' ', 20000); + print_into[0] = '\0'; +//check these!!!: + int i1 = int(rando.inclusive(0, 23945)); + long l1 = long(rando.inclusive(-2394, 2998238)); + un_int u1 = basis::un_int(rando.inclusive(0, 124395)); + un_short s1 = basis::un_short(rando.inclusive(0, 65535)); + sprintf(print_into.s(), "%d %ld %u %hu %s", i1, l1, u1, s1, parm_string); + sprintf(print_into.s(), "%c %d %c %s %s %lu", char(rando.inclusive('a', 'z')), + int(rando.inclusive(0, 23945)), char(rando.inclusive('A', 'Z')), + parm_string, parm_string, basis::un_long(rando.inclusive(0, 2998238))); +} + +void test_string::run_test_30() +{ + // 30th test group checks astring sprintf. + FUNCDEF("run_test_30"); + astring parm_string = string_manipulation::make_random_name(40, 140); + astring print_into(' ', 20000); + print_into[0] = '\0'; + int i1 = int(rando.inclusive(0, 23945)); + long l1 = long(rando.inclusive(-2394, 2998238)); + un_int u1 = basis::un_int(rando.inclusive(0, 124395)); + un_short s1 = basis::un_short(rando.inclusive(0, 65535)); + char test_same[20010]; + sprintf(test_same, "%d %ld %u %hu %s", i1, l1, u1, s1, parm_string.s()); + print_into.sprintf("%d %ld %u %hu %s", i1, l1, u1, s1, parm_string.s()); + ASSERT_EQUAL(astring(test_same), print_into, "sprintf should get same results as standard"); +//do test for this one too. + print_into.sprintf("%c %d %c %s %s %lu", char(rando.inclusive('a', 'z')), + int(rando.inclusive(0, 23945)), char(rando.inclusive('A', 'Z')), + parm_string.observe(), parm_string.s(), basis::un_long(rando.inclusive(0, 2998238))); +} + +void test_string::run_test_31() +{ + FUNCDEF("run_test_31"); + // testing of character repeat constructor. + astring dashes('-', 20); + astring non_plusses('+', 0); + astring plusses('+', 1); + ASSERT_EQUAL(dashes.length(), 20, astring("dashes has wrong length!")); + ASSERT_EQUAL(dashes, astring("--------------------"), astring("dashes has wrong contents! '") + dashes + "' vs. '" + astring("--------------------'")); + ASSERT_FALSE(non_plusses.length(), astring("non_plusses has wrong length!")); + ASSERT_EQUAL(plusses.length(), 1, astring("plusses has wrong length!")); + ASSERT_EQUAL(plusses, astring("+"), astring("plusses has wrong contents!")); + ASSERT_EQUAL(plusses, astring('+', 1), astring("plusses second compare failed!")); +} + +void test_string::run_test_32() +{ + FUNCDEF("run_test_32"); + // tests creating a huge string and ripping it apart. + + const int CHUNK_SIZE = 2 * MEGABYTE; + // the block factor for our string. we expect not to go above this during + // the testing. + const int MIN_ADDITION = 10000; const int MAX_ADDITION = 80000; + // these are the largest chunk we add to a string at a time. + const int BUILD_AND_BURN_ITERATIONS = 1; + // number of times to test building up and tearing down. + + // the string we build up and tear down. + astring slab; + +//hmmm: maybe have a mixed phase where tearing and adding +// happens frequently and interspersed. + + for (int iters = 0; iters < BUILD_AND_BURN_ITERATIONS; iters++) { + + int size = 0; // independent count of expected size. + // we don't want to bother allocating the big chunk more than once, so + // we'll add to the string until it's within range of going over. + while (slab.length() < CHUNK_SIZE - MAX_ADDITION - 20) { +//make this into add_a_chunk + astring addition = string_manipulation::make_random_name(MIN_ADDITION, + MAX_ADDITION); + slab += addition; + size += addition.length(); + ASSERT_EQUAL(size, slab.length(), astring("size is incorrect after add!")); + } + + // now we eat it up. + while (slab.length()) { +//make this into zap_a_chunk + int zap_start = rando.inclusive(0, slab.end()); + int zap_end = rando.inclusive(0, slab.end()); + flip_increasing(zap_start, zap_end); + int range_length = zap_end - zap_start + 1; + ASSERT_FALSE(negative(range_length), astring("flip_increasing failed!")); + slab.zap(zap_start, zap_end); + size -= range_length; + ASSERT_EQUAL(size, slab.length(), astring("size is incorrect after zap!")); + } + } +} + +void test_string::run_test_33() +{ + FUNCDEF("run_test_33"); + // 33rd test group exercises the replace functions. + { + astring to_modify("\\\\feeby\\path\\yo"); + ASSERT_TRUE(to_modify.replace("\\", "/"), "failed to replace the string"); + ASSERT_EQUAL(to_modify, astring("/\\feeby\\path\\yo"), "produced wrong resultant string"); + while (to_modify.replace("\\", "/")) {} + ASSERT_EQUAL(to_modify, astring("//feeby/path/yo"), "produced wrong final string"); + } + { + astring to_modify("\\superduper\\dynamo\\looper"); + ASSERT_TRUE(to_modify.replace("\\", "/"), "failed to replace the string"); + ASSERT_EQUAL(to_modify, astring("/superduper\\dynamo\\looper"), "produced wrong resultant string"); + while (to_modify.replace("\\", "/")) {} + ASSERT_EQUAL(to_modify, astring("/superduper/dynamo/looper"), "produced wrong final string"); + } + { + astring id = "/SRV=1/SRC=1"; + astring id1 = id; + while (id1.replace("/", " ")) {} +// LOG(astring("replacing / with ' ' in first test (was ") + id + +// ") gives " + id1); + ASSERT_EQUAL(id1, astring(" SRV=1 SRC=1"), "produced wrong final string"); + + astring id2 = id; + while (id2.replace("=", ":")) {} +// LOG(astring("replacing = with : in second test (was ") + id + +// ") gives " + id2); + ASSERT_EQUAL(id2, astring("/SRV:1/SRC:1"), "produced wrong final string"); + } +} + +void test_string::run_test_34() +{ +// FUNCDEF("run_test_34"); + +//not in use right now. + +} + +void test_string::run_test_35() +{ + FUNCDEF("run_test_35"); + // test the shrink method. + astring termo('R', 2812); + ASSERT_EQUAL(termo.length(), 2812, "length should be as requested"); + termo[1008] = '\0'; + termo.shrink(); + ASSERT_EQUAL(termo.get_implementation().internal_real_length(), 1010, a_sprintf("failure in shrunken size: " "wanted 1010 and got %d.", termo.get_implementation().internal_real_length())); + astring termo2('R', 1008); + ASSERT_EQUAL(termo, termo2, "wrong value produced"); +} + +void test_string::run_test_36() +{ + FUNCDEF("run_test_36"); + // test the text form on a string array (which is mildly related to strings; + // this could be moved to a common templates test someday). + string_array torpid; + torpid += "york"; + torpid += "burger"; + torpid += "petunia"; + torpid += "dumptruck"; + ASSERT_EQUAL(torpid.text_form(), astring("\"york\",\"burger\",\"petunia\",\"dumptruck\""), "wrong value computed"); + string_array sacral; + sacral += "gumboat"; + ASSERT_EQUAL(sacral.text_form(), astring("\"gumboat\""), "wrong value computed"); + + string_array paknid; + paknid += "gorboochy"; + paknid += "rangolent"; + byte_array packed; + structures::pack_array(packed, paknid); + + string_array upnort; + ASSERT_TRUE(structures::unpack_array(packed, upnort), "failed to unpack"); + ASSERT_FALSE(packed.length(), "array still has bytes!"); + + string_array stongent; + stongent += "pemulack"; + stongent += "bluzzent"; + stongent += "crupto"; + stongent += "floonack"; + stongent += "atoona"; + packed.reset(); + structures::pack_array(packed, stongent); + + string_array belzorp; + ASSERT_TRUE(structures::unpack_array(packed, belzorp), "failed to unpack"); + ASSERT_FALSE(packed.length(), "array still has bytes!"); +} + +void test_string::run_test_37() +{ + FUNCDEF("run_test_37"); + // 37th test group used to try out the old packing support, but now is + // just the same as test 29. it would be good to make this different. + astring a("would an onion smell so sweet?"); + byte_array p1; + a.pack(p1); + astring b; + ASSERT_TRUE(b.unpack(p1), "first unpack failed"); + ASSERT_EQUAL(b, a, "first comparison failed"); + a = "128 salamanders cannot be wrong."; + a.pack(p1); + ASSERT_TRUE(b.unpack(p1), "second unpack failed"); + ASSERT_EQUAL(b, a, "second comparison failed"); +} + +void test_string::run_test_38() +{ +// FUNCDEF("run_test_38"); + double to_print = 2.345; + a_sprintf non_deadly("%.1f", to_print); +/// LOG(astring("printed: ") + non_deadly); + + to_print = 1.797E+254; + // this value breaks things. + + char bucket[2000]; + bucket[0] = '\0'; + sprintf(bucket, "%.1f", to_print); +/// LOG(astring("sprintf printed: ") + bucket); + + a_sprintf deadly("%.1f", to_print); +/// LOG(astring("printed: ") + deadly); +} + +void test_string::run_test_39() +{ + FUNCDEF("run_test_39"); + const char *find_set = "!?.;"; + astring test_1 = "how do i get to balthazar square? it stinks!"; + ASSERT_EQUAL(test_1.find_any(find_set), 32, "first find returned wrong result"); + ASSERT_EQUAL(test_1.find_any(find_set, 33), 44, "second find returned wrong result"); + ASSERT_EQUAL(test_1.find_any(find_set, 40, true), 32, "third find returned wrong result"); +} + +void test_string::run_test_40() +{ + FUNCDEF("run_test_40"); + int test_num = 1; + #define test_name() a_sprintf("test %d: ", test_num) + { + astring target = "xabab"; + astring from = "ab"; + astring to = "dc"; + ASSERT_TRUE(target.replace_all(from, to), test_name() + "didn't find from string"); + ASSERT_EQUAL(target, astring("xdcdc"), test_name() + "didn't replace properly"); + test_num++; + } + { + astring target = "xabab"; + astring from = "ab"; + astring to = "ab"; + ASSERT_TRUE(target.replace_all(from, to), test_name() + "didn't find from string"); + ASSERT_EQUAL(target, astring("xabab"), test_name() + "didn't replace properly"); + test_num++; + } + { + astring target = "xabab"; + astring from = "ab"; + astring to = "a"; + ASSERT_TRUE(target.replace_all(from, to), test_name() + "didn't find from string"); + ASSERT_EQUAL(target, astring("xaa"), test_name() + "didn't replace properly"); + test_num++; + } + { + astring target = "ababx"; + astring from = "ab"; + astring to = "a"; + ASSERT_TRUE(target.replace_all(from, to), test_name() + "didn't find from string"); + ASSERT_EQUAL(target, astring("aax"), test_name() + "didn't replace properly"); + test_num++; + } + { + astring target = "suzzle rumpetzzes gnargle rezztor"; + astring from = "zz"; + astring to = "zzz"; + ASSERT_TRUE(target.replace_all(from, to), test_name() + "didn't find from string"); + ASSERT_EQUAL(target, astring("suzzzle rumpetzzzes gnargle rezzztor"), test_name() + "didn't replace properly"); + test_num++; + } + { + astring target = "qqqq"; + astring from = "q"; + astring to = "qqq"; + ASSERT_TRUE(target.replace_all(from, to), test_name() + "didn't find from string"); + ASSERT_EQUAL(target, astring("qqqqqqqqqqqq"), test_name() + "didn't replace properly"); + test_num++; + } + { + astring target = "glorg snorp pendle funk"; + astring from = " "; + astring to = ""; + ASSERT_TRUE(target.replace_all(from, to), test_name() + "didn't find from string"); + ASSERT_EQUAL(target, astring("glorgsnorppendlefunk"), test_name() + "didn't replace properly"); + test_num++; + } +} + +void test_string::run_test_41() +{ + FUNCDEF("run_test_41"); + int test_num = 0; + #define test_name() a_sprintf("test %d: ", test_num) + { + test_num++; + astring target = "xabab"; + const char *finding1 = "ab"; + ASSERT_EQUAL(target.find_non_match(finding1, 0, false), 0, test_name() + "didn't find right location A"); + const char *finding2 = "xb"; + ASSERT_EQUAL(target.find_non_match(finding2, target.length() - 1, true), 3, test_name() + "didn't find right location B"); + const char *finding3 = "c"; + ASSERT_EQUAL(target.find_non_match(finding3, 0, false), 0, test_name() + "wrong answer for test C"); + ASSERT_EQUAL(target.find_non_match(finding3, target.length() - 1, true), target.length() - 1, + test_name() + "wrong answer for test D"); + } + { + test_num++; + astring target = "abcadefghoota"; + const char *finding1 = "bcdfghot"; + ASSERT_EQUAL(target.find_non_match(finding1, 0, false), 0, test_name() + "didn't find right location A"); + ASSERT_EQUAL(target.find_non_match(finding1, 1, false), 3, test_name() + "didn't find right location B"); + ASSERT_EQUAL(target.find_non_match(finding1, target.length() - 1, true), target.length() - 1, test_name() + "didn't find right location C"); + ASSERT_EQUAL(target.find_non_match(finding1, target.length() - 2, true), 5, test_name() + "didn't find right location D"); + ASSERT_EQUAL(target.find_non_match(finding1, 3, false), 3, test_name() + "didn't find right location E"); + ASSERT_EQUAL(target.find_non_match(finding1, 4, false), 5, test_name() + "didn't find right location F"); + + } + +} + +// exercise the middle, right and left methods. +void test_string::run_test_42() +{ + FUNCDEF("run_test_42"); + + astring hobnob("all the best robots are bending robots"); + + ASSERT_EQUAL(hobnob.middle(5, 7), astring("he best"), "failed to find middle of string"); + ASSERT_EQUAL(hobnob.right(10), astring("ing robots"), "failed to find right side of string"); + ASSERT_EQUAL(hobnob.left(6), astring("all th"), "failed to find right side of string"); +} + +////////////// + +int test_string::execute() +{ + FUNCDEF("execute"); + +// ASSERT_EQUAL(0, 1, "fake assertion to test jenkins log parsing"); + ASSERT_EQUAL(1, 1, "non-fake assertion to test jenkins log parsing"); + + ASSERT_EQUAL(staticity_test, astring("yo!"), "wrong contents"); + + time_stamp end_time(TEST_RUNTIME_DEFAULT); + // when we're done testing. + + int i = 0; // iteration counter. + while (time_stamp() < end_time) { + // we run the test until our time runs out. + i++; // next iteration. +#ifdef DEBUG_STRING_TEST + LOG(astring(astring::SPRINTF, "index %d", i)); +#endif + + // beginning of test sets. + run_test_01(); + run_test_02(); + run_test_03(); + run_test_04(); + run_test_05(); + run_test_06(); + run_test_07(); + run_test_08(); + run_test_09(); + run_test_10(); + run_test_11(); + run_test_12(); + run_test_13(); + run_test_14(); + run_test_15(); + run_test_16(); + run_test_17(); + run_test_18(); + run_test_19(); + run_test_20(); + run_test_21(); + run_test_22(); + run_test_23(); + run_test_24(); + run_test_25(); + run_test_26(); + run_test_27(); + run_test_28(); + run_test_29(); +//too slow run_test_30(); + run_test_31(); + run_test_32(); + run_test_33(); +//retired run_test_34(); + run_test_35(); + run_test_36(); + run_test_37(); + run_test_38(); + run_test_39(); + run_test_40(); + run_test_41(); + run_test_42(); + } + + return final_report(); +} + +HOOPLE_MAIN(test_string, ) + + diff --git a/core/library/tests_basis/test_system_preconditions.cpp b/core/library/tests_basis/test_system_preconditions.cpp new file mode 100644 index 00000000..f18f17d6 --- /dev/null +++ b/core/library/tests_basis/test_system_preconditions.cpp @@ -0,0 +1,169 @@ +/* +* Name : test_system_preconditions +* Author : Chris Koeritz +** +* Copyright (c) 1993-$now By Author. 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 is online at: +* http://www.fsf.org/copyleft/gpl.html +* Please send any updates to: fred@gruntose.com +*/ + +#include "checkup.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace application; +using namespace basis; +using namespace configuration; +using namespace loggers; +using namespace system_checkup; +using namespace structures; +using namespace unit_test; + +class test_system_preconditions : virtual public unit_base, virtual public application_shell +{ +public: + test_system_preconditions() : application_shell() {} + DEFINE_CLASS_NAME("test_system_preconditions"); + virtual int execute(); +}; + +////////////// + +#undef UNIT_BASE_THIS_OBJECT +#define UNIT_BASE_THIS_OBJECT c_parent + +class burpee +{ +public: + burpee(unit_base &parent) : c_parent(parent), my_string(new astring) { *my_string = "balrog"; } + DEFINE_CLASS_NAME("burpee"); + virtual ~burpee() { + FUNCDEF("destructor"); + WHACK(my_string); + ASSERT_FALSE(my_string, "whack test should not fail to clear string"); + } + +protected: + unit_base &c_parent; + +private: + astring *my_string; +}; + +#undef UNIT_BASE_THIS_OBJECT + +////////////// + +#define UNIT_BASE_THIS_OBJECT c_parent + +class florba : public burpee +{ +public: + florba(unit_base &parent) : burpee(parent), second_string(new astring) + { *second_string = "loquacious"; } + DEFINE_CLASS_NAME("florba"); + virtual ~florba() { + FUNCDEF("destructor"); + WHACK(second_string); + ASSERT_FALSE(second_string, "whack test should clear string in derived class"); + } + +private: + astring *second_string; +}; + +#undef UNIT_BASE_THIS_OBJECT + +////////////// + +// back to default now. +#define UNIT_BASE_THIS_OBJECT (*this) + +struct testing_file_struct : public FILE {}; + +// NOTE: an important part of this test program is running it under something +// like boundschecker to ensure that there are no memory leaks caused by +// invoking WHACK. apparently diab 3 is unable to implement WHACK correctly. + +int test_system_preconditions::execute() +{ + FUNCDEF("execute") + // let's see what this system is called. + log(astring("The name of this software system is: ") + + application_configuration::software_product_name()); + ASSERT_TRUE(strlen(application_configuration::software_product_name()), + "product should not be blank"); + + // and what this program is called. + log(astring("The application is called: ") + application_configuration::application_name()); + ASSERT_TRUE(application_configuration::application_name().length(), + "application name should not be blank"); + + // testing compiler's ansi c++ compliance. + for (int q = 0; q < 198; q++) { + int treno = q; + int malfoy = treno * 3; +// log(a_sprintf("%d", malfoy)); + } + // this should not be an error. the scope of q should be within the loop and + // not outside of it. + int q = 24; + ASSERT_FALSE(q > 190, "no weirdness should happen with compiler scoping"); + + // test that the WHACK function operates properly. + burpee *chunko = new burpee(*this); + florba *lorkas = new florba(*this); + burpee *alias = lorkas; + + WHACK(chunko); + WHACK(alias); + ASSERT_FALSE(chunko, "chunko whack test should succeed"); + ASSERT_FALSE(alias, "aliased lorkas whack test should succeed"); + ASSERT_TRUE(lorkas, "original lorkas should not have been cleared"); + lorkas = NIL; + + ASSERT_EQUAL((int)sizeof(testing_file_struct), (int)sizeof(FILE), + "struct size test, sizeof testing_file_struct and sizeof FILE should not differ"); + + // now do the crucial tests on the OS, platform, compiler, etc. + ASSERT_TRUE(check_system_characteristics(*this), + "required system characteristics should be found"); + +#ifdef __WIN32__ + known_operating_systems os = determine_OS(); + if (os == WIN_95) + printf("This is windows 95.\n"); + else if (os == WIN_NT) + printf("This is windows NT.\n"); + else if (os == WIN_2K) + printf("This is windows 2000.\n"); + else if (os == WIN_XP) + printf("This is windows XP.\n"); + else + printf("This OS is unknown.\n"); +#endif + + version os_ver = application_configuration::get_OS_version(); + printf("OS version: %s\n", os_ver.text_form().s()); + + return final_report(); +} + +HOOPLE_MAIN(test_system_preconditions, ) + diff --git a/core/library/tests_configuration/makefile b/core/library/tests_configuration/makefile new file mode 100644 index 00000000..04995769 --- /dev/null +++ b/core/library/tests_configuration/makefile @@ -0,0 +1,10 @@ +include cpp/variables.def + +PROJECT = tests_configuration +TYPE = test +TARGETS = test_section_manager.exe test_tokenizer.exe +LOCAL_LIBS_USED = unit_test application loggers configuration textual timely filesystem \ + structures basis +RUN_TARGETS = $(ACTUAL_TARGETS) + +include cpp/rules.def diff --git a/core/library/tests_configuration/test_section_manager.cpp b/core/library/tests_configuration/test_section_manager.cpp new file mode 100644 index 00000000..c8358237 --- /dev/null +++ b/core/library/tests_configuration/test_section_manager.cpp @@ -0,0 +1,124 @@ +/* +* Name : test_section_manager +* Author : Chris Koeritz +* Purpose: Tests that the section manager is writing sections properly and keeping its + table of contents correctly. +** +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +//#define DEBUG_SECTION_MANAGER + // uncomment for debugging version. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace configuration; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +//#ifdef DEBUG_SECTION_MANAGER +// #include +//#endif + +class test_section_manager : public virtual unit_base, virtual public application_shell +{ +public: + test_section_manager() {} + DEFINE_CLASS_NAME("test_section_manager"); + virtual int execute(); +}; + +////////////// + +int test_section_manager::execute() +{ + FUNCDEF("execute"); + { + astring TEST = "First Test"; + ini_configurator ini("t_section_manager_1.ini", ini_configurator::AUTO_STORE); + section_manager mangler(ini, "TOC", "BORK:"); + // clean up the ini file for our testing.... + string_array names; + if (mangler.get_section_names(names)) { + for (int i = 0; i < names.length(); i++) mangler.zap_section(names[i]); + ini.delete_section("TOC"); // remove table of contents. + } + + // now add some entries... + string_table contents1; + contents1.add("oink", "bozoot"); + contents1.add("gink", "rinkum"); + contents1.add("sorty", "figulat"); + contents1.add("crinkish", "wazir"); + ASSERT_TRUE(mangler.add_section("burny", contents1), + TEST + ": couldn't add the first section!"); + string_table temp_1; + ASSERT_TRUE(mangler.find_section("burny", temp_1), + TEST + ": couldn't retrieve the first section!"); +#ifdef DEBUG_SECTION_MANAGER + printf("first section has:\n%s\n", temp_1.text_form().s()); + printf("we want:\n%s\n", contents1.text_form().s()); +#endif + ASSERT_EQUAL(temp_1, contents1, TEST + ": first section's contents are incorrect!"); + contents1.add("glurp", "locutusburger"); + ASSERT_FALSE(mangler.add_section("burny", contents1), + TEST + ": incorrectly allowing re-add of first section!"); + ASSERT_TRUE(mangler.replace_section("burny", contents1), + TEST + ": failing to replace first section!"); + temp_1.reset(); + ASSERT_TRUE(mangler.find_section("burny", temp_1), + TEST + ": couldn't retrieve the first section (2)!"); + ASSERT_EQUAL(temp_1, contents1, TEST + ": first section's contents are incorrect (2)!"); + + string_table contents2; + contents2.add("tsingha", "tsinglo"); + contents2.add("chunk", "midgets"); + contents2.add("burn", "barns in texas"); + contents2.add("chump", "will not be elected"); + contents2.add("geezerplant", "water weekly"); + ASSERT_TRUE(mangler.add_section("itchy", contents2), + TEST + ": couldn't add the second section!"); + string_table temp_2; + ASSERT_TRUE(mangler.find_section("itchy", temp_2), + TEST + ": couldn't retrieve the second section!"); + ASSERT_EQUAL(temp_2, contents2, TEST + ": second section's contents are incorrect!"); + // test that first section is still there with second having been added. + ASSERT_TRUE(mangler.find_section("burny", temp_1), + TEST + ": couldn't retrieve the first section (3)!"); + ASSERT_EQUAL(temp_1, contents1, TEST + ": first section's contents are incorrect (3)!"); + +//more! + } + { +// astring TEST = "Second Test"; + } + + return final_report(); +} + +////////////// + +HOOPLE_MAIN(test_section_manager, ); + diff --git a/core/library/tests_configuration/test_tokenizer.cpp b/core/library/tests_configuration/test_tokenizer.cpp new file mode 100644 index 00000000..62b5ea8f --- /dev/null +++ b/core/library/tests_configuration/test_tokenizer.cpp @@ -0,0 +1,285 @@ +/* +* Name : test_tokenizer +* Author : Chris Koeritz +* Purpose: Puts the variable_tokenizer through some paces. +** +* Copyright (c) 1998-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace configuration; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), astring(to_print)) + +const int MAX_LINE_SIZE = 1000; + // the largest line we will deal with in a file. + +class test_tokenizer : public virtual unit_base, public virtual application_shell +{ +public: + test_tokenizer() {} + DEFINE_CLASS_NAME("test_tokenizer"); + virtual int execute(); +}; + +////////////// + +int test_tokenizer::execute() +{ + FUNCDEF("execute"); + { + astring test_set_1 = "\n\ +[frederick]\n\ +samba=dance\n\ +tantalus rex=gumby\n\ +57 chevy heap=\"16 anagrams of misty immediately\"\n\ +lingus distractus\n\ +shouldus havus assignmentum=\n\ +above better be parsed = 1\n\ +;and this comment too yo\n\ +ted=agent 12\n"; + + astring TEST = "First Test: "; + astring testing = test_set_1; + LOG(astring("file before parsing:") + testing); + variable_tokenizer jed("\n\r", "="); + ASSERT_TRUE(jed.parse(testing), TEST + "jed should be parseable"); + astring out; + jed.text_form(out); + variable_tokenizer gorp("\n\r", "="); + ASSERT_TRUE(gorp.parse(out), TEST + "gorp should be parseable"); + LOG(astring("file after parsing:") + out); + LOG("and in tabular form:"); + LOG(jed.table().text_form()); + +//for (int i = 0; i < gorp.table().symbols(); i++) { +//astring name, value; +//gorp.table().retrieve(i, name, value); +//LOG(a_sprintf("item %d: name=\"%s\" value=\"%s\"", i, name.s(), value.s())); +//} + + ASSERT_TRUE(jed.exists("[frederick]"), TEST + "jed section header was omitted!"); + ASSERT_EQUAL(jed.find("[frederick]"), astring(""), + TEST + "jed section header had unexpected contents!"); + ASSERT_EQUAL(jed.find("ted"), astring("agent 12"), + TEST + "jed's ted is missing or invalid!"); + ASSERT_FALSE(jed.find("shouldus havus assignmentum").t(), + TEST + "jed's shouldus had contents but shouldn't!"); + astring value = *jed.table().find("shouldus havus assignmentum"); + ASSERT_EQUAL(value, astring(" "), TEST + "jed shouldus had wrong contents, not special!"); + ASSERT_TRUE(gorp.exists("[frederick]"), TEST + "gorp section header was omitted!"); + ASSERT_EQUAL(gorp.find("[frederick]"), astring(""), + TEST + "gorp section header had unexpected contents!"); + ASSERT_EQUAL(gorp.find("ted"), astring("agent 12"), + TEST + "gorp's ted is missing or invalid!"); + ASSERT_FALSE(gorp.find("shouldus havus assignmentum").t(), + TEST + "gorp's shouldus had contents but shouldn't!"); + value = *gorp.table().find("shouldus havus assignmentum"); + ASSERT_EQUAL(value, astring(" "), TEST + "gorp shouldus had wrong contents, not special!"); + } + { + astring test_set_2 = "Name=SRV, Parent=, Persist=Y, Entry=Y, Required=Y, Desc=Server, Tbl=Server"; + + astring TEST = "Second Test: "; + astring testing = test_set_2; + LOG(astring("file before parsing:") + testing); + variable_tokenizer jed(",", "="); + ASSERT_TRUE(jed.parse(testing), TEST + "jed should be parseable"); + astring out; + jed.text_form(out); + LOG(astring("file after parsing:") + out); + LOG("and in tabular form:"); + LOG(jed.table().text_form()); + ASSERT_EQUAL(jed.find("Name"), astring("SRV"), TEST + "Name is missing or invalid!"); + ASSERT_FALSE(jed.find("Parent").t(), TEST + "Parent had contents but shouldn't!"); + astring value = *jed.table().find("Parent"); + ASSERT_EQUAL(value, astring(" "), TEST + "Parent had wrong contents, not special!"); + ASSERT_EQUAL(jed.find("Persist"), astring("Y"), TEST + "Persist is missing or invalid!"); + } + + { + astring test_set_3 = "\n\ +[frederick]\n\ +samba=dance\n\ +tantalus rex=gumby \"don#t\n\n'play'\nthat\" homey '\n\ndog\n\n yo \"\ncreen\" arf'\n\ +57 chevy heap=\"16 anagrams of misty immediately\"\n\ +lingus distractus\n\ +shouldus havus assignmentum=\n\ +above better be parsed = 1\n\ +;and this comment too yo\n\ +ted=agent 12\n"; + + astring TEST = "Third Test: "; + astring testing = test_set_3; + LOG(astring("file before parsing:") + testing); + variable_tokenizer jed("\n\r", "=", "\'\""); + ASSERT_TRUE(jed.parse(testing), TEST + "jed should be parseable"); + astring out; + jed.text_form(out); + variable_tokenizer gorp("\n\r", "=", "\'\""); + ASSERT_TRUE(gorp.parse(out), TEST + "gorp should be parseable"); + LOG(astring("file after parsing:") + out); + LOG("and in tabular form:"); + LOG(jed.table().text_form()); + ASSERT_TRUE(jed.exists("[frederick]"), TEST + "jed section header was omitted!"); + ASSERT_EQUAL(jed.find("[frederick]"), astring(""), + TEST + "jed section header had unexpected contents!"); + ASSERT_EQUAL(jed.find("ted"), astring("agent 12"), TEST + "jed ted is missing or invalid!"); + ASSERT_FALSE(jed.find("shouldus havus assignmentum").t(), + TEST + "jed shouldus had contents but shouldn't!"); + astring value = *jed.table().find("shouldus havus assignmentum"); + ASSERT_EQUAL(value, astring(" "), TEST + "jed shouldus had wrong contents, not special!"); + ASSERT_TRUE(gorp.exists("[frederick]"), TEST + "gorp section header was omitted!"); + ASSERT_EQUAL(gorp.find("[frederick]"), astring(""), + TEST + "gorp section header had unexpected contents!"); + ASSERT_EQUAL(gorp.find("ted"), astring("agent 12"), TEST + "gorp second ted is missing or invalid!"); + ASSERT_FALSE(gorp.find("shouldus havus assignmentum").t(), + TEST + "gorp shouldus had contents but shouldn't!"); + value = *gorp.table().find("shouldus havus assignmentum"); + ASSERT_EQUAL(value, astring(" "), TEST + "gorp shouldus wrong contents, was not special!"); + ASSERT_TRUE(gorp.exists("tantalus rex"), TEST + "gorp tantalus rex is missing!"); + ASSERT_EQUAL(gorp.find("tantalus rex"), + astring("gumby \"don#t\n\n'play'\nthat\" homey '\n\ndog\n\n yo " + "\"\ncreen\" arf'"), + TEST + "gorp tantalus rex has incorrect contents!"); + } + { + astring test_set_4 = "\n\ +[garfola]\n\ +treadmill=\"this ain't the place\nwhere'n we been done\nseein' no quotes\"\n\ +borfulate='similarly \"we\" do not like\nthe \" quote \" type thing here'\n\ +"; + + astring TEST = "Fourth Test: "; + astring testing = test_set_4; + LOG(astring("file before parsing:\n") + testing); + variable_tokenizer jed("\n\r", "=", "\'\"", false); + ASSERT_TRUE(jed.parse(testing), TEST + "jed should be parseable"); + astring out; + jed.text_form(out); + variable_tokenizer gorp("\n\r", "=", "\'\"", false); + ASSERT_TRUE(gorp.parse(out), TEST + "gorp should be parseable"); + LOG(astring("file after parsing:\n") + out); + LOG("and in tabular form:"); + LOG(jed.table().text_form()); + ASSERT_TRUE(gorp.exists("[garfola]"), TEST + "section header was omitted!"); + ASSERT_EQUAL(gorp.find("[garfola]"), astring(""), + TEST + "section header had unexpected contents!"); + ASSERT_TRUE(gorp.exists("treadmill"), TEST + "treadmill is missing!"); + ASSERT_EQUAL(gorp.find("treadmill"), + astring("\"this ain't the place\nwhere'n we been done\nseein' no quotes\""), + TEST + "treadmill has incorrect contents!"); + ASSERT_TRUE(gorp.exists("borfulate"), TEST + "borfulate is missing!"); + ASSERT_EQUAL(gorp.find("borfulate"), + astring("'similarly \"we\" do not like\nthe \" quote \" type thing here'"), + TEST + "borfulate has incorrect contents!"); + } + { + astring test_set_5 = "\n\ + x~35; y~92 ;#comment ; d ~83 ; e~ 54 ; ? new comment ;sud ~ xj23-8 ; nigh ~2"; + + astring TEST = "Fifth Test: "; + astring testing = test_set_5; + LOG(astring("file before parsing:\n") + testing); + variable_tokenizer jed(";", "~"); + jed.set_comment_chars("#?"); + ASSERT_TRUE(jed.parse(testing), TEST + "jed should be parseable"); + astring out; + jed.text_form(out); + LOG(astring("file after parsing:\n") + out); + LOG("and in tabular form:"); + LOG(jed.table().text_form()); + + variable_tokenizer gorp(";", "~"); + gorp.set_comment_chars("#?"); + ASSERT_TRUE(gorp.parse(out), TEST + "gorp should be parseable"); + LOG("gorp in tabular form:"); + LOG(gorp.table().text_form()); +//hmmm: need equalizable/orderable on table? + ASSERT_TRUE(gorp.table() == jed.table(), TEST + "gorp text not same as jed!"); + + ASSERT_EQUAL(jed.find("x"), astring("35"), TEST + "value for x missing or invalid"); + ASSERT_EQUAL(jed.find("y"), astring("92"), TEST + "value for y missing or invalid"); + ASSERT_EQUAL(jed.find("d"), astring("83"), TEST + "value for d missing or invalid"); + ASSERT_EQUAL(jed.find("e"), astring("54"), TEST + "value for e missing or invalid"); + ASSERT_EQUAL(jed.find("sud"), astring("xj23-8"), TEST + "value for sud missing or invalid"); + ASSERT_EQUAL(jed.find("nigh"), astring("2"), TEST + "value for nigh missing or invalid"); + } + { + astring test_set_6 = "\r\n\r\n\r\ +# this is yet another test with comments.\r\n\ +; we want to be sure stuff works right.\r\n\ +crumpet=tempest\r\n\ + moomar=18\r\n\ +shagbot =once upon a time there was a man \r\n\ +\t\t\tpunzola megamum =brandle the handle \r\n\ +trapzoot= uhhh\r\n\ +mensch = racer X\r\n\ +\r\n\r\n\r\n"; + + astring TEST = "Sixth Test: "; + astring testing = test_set_6; + LOG(astring("file before parsing:\n===========\n") + testing + "\n==========="); + variable_tokenizer jed("\n\r", "="); + jed.set_comment_chars("#;"); + ASSERT_TRUE(jed.parse(testing), TEST + "jed should be parseable"); + astring out; + jed.text_form(out); + LOG(astring("file after parsing:\n===========\n") + out + "\n==========="); + LOG("and in tabular form:"); + LOG(jed.table().text_form()); + + variable_tokenizer gorp("\n\r", "="); + gorp.set_comment_chars("#;"); + ASSERT_TRUE(gorp.parse(out), TEST + "gorp should be parseable"); + LOG("gorp in tabular form:"); + LOG(gorp.table().text_form()); +LOG(a_sprintf("gorp has %d fields, jed has %d fields", gorp.symbols(), jed.symbols())); + ASSERT_TRUE(gorp.table() == jed.table(), TEST + "gorp text not same as jed!"); + + ASSERT_EQUAL(jed.find("crumpet"), astring("tempest"), + TEST + "value for crumpet missing or invalid"); + ASSERT_EQUAL(jed.find("moomar"), astring("18"), + TEST + "value for moomar missing or invalid"); + ASSERT_EQUAL(jed.find("shagbot"), astring("once upon a time there was a man"), + TEST + "value for shagbot missing or invalid"); + ASSERT_EQUAL(jed.find("trapzoot"), astring("uhhh"), + TEST + "value for trapzoot missing or invalid"); + ASSERT_EQUAL(jed.find("punzola megamum"), astring("brandle the handle"), + TEST + "value for punzola missing or invalid"); + ASSERT_EQUAL(jed.find("mensch"), astring("racer X"), + TEST + "value for mensch missing or invalid"); + } + + return final_report(); +} + +////////////// + +HOOPLE_MAIN(test_tokenizer, ); + diff --git a/core/library/tests_crypto/makefile b/core/library/tests_crypto/makefile new file mode 100644 index 00000000..d7d3f49e --- /dev/null +++ b/core/library/tests_crypto/makefile @@ -0,0 +1,12 @@ +include cpp/variables.def + +PROJECT = tests_crypto +TYPE = test +TARGETS = test_blowfish_crypto.exe test_rsa_crypto.exe +LOCAL_LIBS_USED = unit_test crypto application processes loggers configuration textual timely \ + filesystem structures basis +USE_SSL = t +RUN_TARGETS = $(ACTUAL_TARGETS) + +include cpp/rules.def + diff --git a/core/library/tests_crypto/test_blowfish_crypto.cpp b/core/library/tests_crypto/test_blowfish_crypto.cpp new file mode 100644 index 00000000..e6fdd4b0 --- /dev/null +++ b/core/library/tests_crypto/test_blowfish_crypto.cpp @@ -0,0 +1,194 @@ +/* +* Name : test blowfish encryption +* Author : Chris Koeritz +* Purpose: Exercises the BlowFish encryption methods in the crypto library. +** +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace application; +using namespace basis; +using namespace crypto; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace processes; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), to_print) + +//#define DEBUG_BLOWFISH + // uncomment for noisier run. + +const int TEST_RUNS_PER_KEY = 5; // encryption test cycles done on each key. + +const int THREAD_COUNT = 10; // number of threads testing blowfish at once. + +const int ITERATIONS = 4; // number of test runs in our testing threads. + +const int MAX_STRING = 20000; // largest chunk that we'll try to encrypt. + +////////////// + +class test_blowfish; // forward. + +class blowfish_thread : public ethread +{ +public: + blowfish_thread(test_blowfish &parent) : ethread(), _parent(parent) {} + + void perform_activity(void *ptr); + // try out random blowfish keys on randomly chosen chunks of the fodder. + +private: + test_blowfish &_parent; +}; + +////////////// + +class test_blowfish : virtual public unit_base, virtual public application_shell +{ +public: + test_blowfish() + : _fodder(string_manipulation::make_random_name(MAX_STRING + 1, MAX_STRING + 1)) {} + DEFINE_CLASS_NAME("test_blowfish"); + + int execute(); + +private: + astring _fodder; // chunks taken from this are encrypted and decrypted. + time_stamp _program_start; // the time at which we started executing. + thread_cabinet _threads; // manages our testing threads. + friend class blowfish_thread; // bad practice, but saves time in test app. +}; + +int test_blowfish::execute() +{ + FUNCDEF("execute"); + int left = THREAD_COUNT; + while (left--) { + _threads.add_thread(new blowfish_thread(*this), true, NIL); + } + + while (_threads.threads()) { +#ifdef DEBUG_BLOWFISH + LOG(astring("cleaning debris.")); +#endif + _threads.clean_debris(); + time_control::sleep_ms(1000); + } + +#ifdef DEBUG_BLOWFISH + int duration = int(time_stamp().value() - _program_start.value()); + LOG(a_sprintf("duration for %d keys and encrypt/decrypt=%d ms,", + ITERATIONS * TEST_RUNS_PER_KEY * THREAD_COUNT, duration)); + LOG(a_sprintf("that comes to %d ms per cycle.\n", int(double(duration + / TEST_RUNS_PER_KEY / ITERATIONS / THREAD_COUNT)))); +#endif + + return final_report(); +} + +////////////// + +#undef UNIT_BASE_THIS_OBJECT +#define UNIT_BASE_THIS_OBJECT (*dynamic_cast(application_shell::single_instance())) + +void blowfish_thread::perform_activity(void *) +{ + FUNCDEF("perform_activity"); + int left = ITERATIONS; + while (left--) { + time_stamp key_start; + blowfish_crypto bc(_parent.randomizer().inclusive + (blowfish_crypto::minimum_key_size(), + blowfish_crypto::maximum_key_size())); +#ifdef DEBUG_BLOWFISH + LOG(a_sprintf("%d bit key has:", bc.key_size())); + astring dumped_key = byte_formatter::text_dump(bc.get_key()); + LOG(a_sprintf("%s", dumped_key.s())); +#endif + int key_dur = int(time_stamp().value() - key_start.value()); + +#ifdef DEBUG_BLOWFISH + LOG(a_sprintf(" key generation took %d ms", key_dur)); +#endif + + for (int i = 0; i < TEST_RUNS_PER_KEY; i++) { + byte_array key; + byte_array iv; + + int string_start = _parent.randomizer().inclusive(0, MAX_STRING - 1); + int string_end = _parent.randomizer().inclusive(0, MAX_STRING - 1); + flip_increasing(string_start, string_end); + astring ranstring = _parent._fodder.substring(string_start, string_end); +//LOG(a_sprintf("encoding %s\n", ranstring.s()); +//LOG(a_sprintf("string length encoded: %d\n", ranstring.length()); + + byte_array target; + + time_stamp test_start; + bool worked = bc.encrypt(byte_array(ranstring.length() + 1, + (abyte*)ranstring.s()), target); + int enc_durat = int(time_stamp().value() - test_start.value()); + ASSERT_TRUE(worked, "phase 1 should not fail to encrypt the string"); + + byte_array recovered; + test_start.reset(); + worked = bc.decrypt(target, recovered); + int dec_durat = int(time_stamp().value() - test_start.value()); + ASSERT_TRUE(worked, "phase 1 should not fail to decrypt the string"); +// LOG(a_sprintf("original has %d chars, recovered has %d chars\n", +// ranstring.length(), recovered.length() - 1)); + + astring teddro = (char *)recovered.observe(); +//LOG(a_sprintf("decoded %s\n", teddro.s())); + +#ifdef DEBUG_BLOWFISH + if (teddro != ranstring) { + LOG(a_sprintf("error!\toriginal has %d chars, recovered has %d chars\n", + ranstring.length(), recovered.length() - 1)); + LOG(a_sprintf("\tencoded %s\n", ranstring.s())); + LOG(a_sprintf("\tdecoded %s\n", teddro.s())); + } +#endif + ASSERT_EQUAL(teddro, ranstring, "should not fail to regenerate the original string"); + +#ifdef DEBUG_BLOWFISH + LOG(a_sprintf(" encrypt %d ms, decrypt %d ms, data %d bytes\n", + enc_durat, dec_durat, string_end - string_start + 1)); +#endif + time_control::sleep_ms(0); // take a rest. + } + time_control::sleep_ms(0); // take a rest. + } +} + +HOOPLE_MAIN(test_blowfish, ) + diff --git a/core/library/tests_crypto/test_rsa_crypto.cpp b/core/library/tests_crypto/test_rsa_crypto.cpp new file mode 100644 index 00000000..56529125 --- /dev/null +++ b/core/library/tests_crypto/test_rsa_crypto.cpp @@ -0,0 +1,205 @@ +/* +* Name : test RSA public key encryption +* Author : Chris Koeritz +* Purpose: +* Exercises the RSA encryption functions from the crypto library. +** +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace application; +using namespace basis; +using namespace crypto; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace processes; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +//#define DEBUG_RSA_CRYPTO + // uncomment for noisy run. + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), to_print) + +const int KEY_SIZE = 1024; + // the size of the RSA key that we'll create. + +const int MAX_STRING = 4000; + // the largest chunk that we'll try to encrypt. + +const int THREAD_COUNT = 5; // number of threads testing rsa at once. + +const int ITERATIONS = 6; // number of test runs in our testing threads. + +////////////// + +class test_rsa; // forward. + +class rsa_thread : public ethread +{ +public: + rsa_thread(test_rsa &parent) : ethread(), _parent(parent) {} + + void perform_activity(void *ptr); + // try out random rsa keys on randomly chosen chunks of the fodder. + +private: + test_rsa &_parent; +}; + +////////////// + +class test_rsa : public virtual unit_base, virtual public application_shell +{ +public: + test_rsa() + : _fodder(string_manipulation::make_random_name(MAX_STRING + 1, MAX_STRING + 1)) {} + virtual ~test_rsa() {} + DEFINE_CLASS_NAME("test_rsa"); + + const astring &fodder() const { return _fodder; } + + int execute(); + +private: + astring _fodder; // chunks taken from this are encrypted and decrypted. + time_stamp _program_start; // the time at which we started executing. + thread_cabinet _threads; // manages our testing threads. + friend class rsa_thread; // bad practice, but saves time in test app. +}; + +int test_rsa::execute() +{ + FUNCDEF("execute"); + int left = THREAD_COUNT; + while (left--) { + _threads.add_thread(new rsa_thread(*this), true, NIL); + } + + while (_threads.threads()) { +#ifdef DEBUG_RSA_CRYPTO + LOG(astring("cleaning debris.")); +#endif + _threads.clean_debris(); + time_control::sleep_ms(1000); + } + +#ifdef DEBUG_RSA_CRYPTO + int duration = int(time_stamp().value() - _program_start.value()); + LOG(a_sprintf("duration for %d keys and encrypt/decrypt=%d ms,", + ITERATIONS * THREAD_COUNT, duration)); + LOG(a_sprintf("that comes to %d ms per cycle.", int(double(duration + / ITERATIONS / THREAD_COUNT)))); +#endif + + return final_report(); +} + +////////////// + +#undef UNIT_BASE_THIS_OBJECT +#define UNIT_BASE_THIS_OBJECT (*dynamic_cast(application_shell::single_instance())) + +void rsa_thread::perform_activity(void *) +{ + FUNCDEF("perform_activity"); + int left = ITERATIONS; + while (left--) { + time_stamp start; + + rsa_crypto rc_private_here(KEY_SIZE); + int key_durat = int(time_stamp().value() - start.value()); + + byte_array public_key; + rc_private_here.public_key(public_key); // get our public portion. + byte_array private_key; + rc_private_here.private_key(private_key); // get our private portion. + +//RSA_print_fp(stdout, private_key, 0); +//RSA_print_fp(stdout, public_key, 0); + + int string_start = _parent.randomizer().inclusive(0, MAX_STRING); + int string_end = _parent.randomizer().inclusive(0, MAX_STRING); + flip_increasing(string_start, string_end); + astring ranstring = _parent.fodder().substring(string_start, string_end); + byte_array target; + + // the first phase tests the outsiders sending back data that only we, + // with our private key, can decrypt. + + start.reset(); + rsa_crypto rc_pub(public_key); + bool worked = rc_pub.public_encrypt(byte_array(ranstring.length() + 1, + (abyte*)ranstring.s()), target); + int pub_enc_durat = int(time_stamp().value() - start.value()); + ASSERT_TRUE(worked, "phase 1 shouldn't fail to encrypt the string"); + + rsa_crypto rc_priv(private_key); + byte_array recovered; + start.reset(); + worked = rc_priv.private_decrypt(target, recovered); + int priv_dec_durat = int(time_stamp().value() - start.value()); + ASSERT_TRUE(worked, "phase 1 should not fail to decrypt the string"); + + astring teddro = (char *)recovered.observe(); + + ASSERT_EQUAL(teddro, ranstring, "should not fail to get back the data"); + + // the second phase tests us using our private key to encrypt data which + // anyone with the public key can decode. + + start.reset(); + worked = rc_priv.private_encrypt(byte_array(ranstring.length() + 1, + (abyte*)ranstring.s()), target); + int priv_enc_durat = int(time_stamp().value() - start.value()); + ASSERT_TRUE(worked, "phase 2 should not fail to encrypt the string"); + + start.reset(); + worked = rc_pub.public_decrypt(target, recovered); + int pub_dec_durat = int(time_stamp().value() - start.value()); + ASSERT_TRUE(worked, "phase 2 should not fail to decrypt the string"); + + teddro = (char *)recovered.observe(); + + ASSERT_EQUAL(teddro, ranstring, "should not fail to get back the data here either"); + +#ifdef DEBUG_RSA_CRYPTO + LOG(a_sprintf("key generation: %d ms, public encrypt: %d ms, private " + "decrypt: %d ms", key_durat, pub_enc_durat, priv_dec_durat)); + LOG(a_sprintf("data size: %d bytes, private encrypt: %d ms, public " + "decrypt: %d ms", + string_end - string_start + 1, priv_enc_durat, pub_dec_durat)); +#endif + + time_control::sleep_ms(0); // take a rest. + } +} + +HOOPLE_MAIN(test_rsa, ) + diff --git a/core/library/tests_filesystem/makefile b/core/library/tests_filesystem/makefile new file mode 100644 index 00000000..b517463e --- /dev/null +++ b/core/library/tests_filesystem/makefile @@ -0,0 +1,13 @@ +include cpp/variables.def + +PROJECT = tests_filesystem +TYPE = test +TARGETS = test_byte_filer.exe test_directory.exe test_directory_tree.exe test_file_info.exe \ + test_file_time.exe test_filename.exe test_huge_file.exe +DEFINITIONS += USE_HOOPLE_DLLS +LOCAL_LIBS_USED = unit_test application configuration filesystem loggers mathematics nodes \ + structures textual timely structures basis +RUN_TARGETS = $(ACTUAL_TARGETS) + +include cpp/rules.def + diff --git a/core/library/tests_filesystem/test_byte_filer.cpp b/core/library/tests_filesystem/test_byte_filer.cpp new file mode 100644 index 00000000..8261d398 --- /dev/null +++ b/core/library/tests_filesystem/test_byte_filer.cpp @@ -0,0 +1,235 @@ +/*****************************************************************************\ +* * +* Name : test_byte_filer * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace configuration; +using namespace mathematics; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +class test_byte_filer : virtual public unit_base, virtual public application_shell +{ +public: + test_byte_filer() : application_shell() {} + DEFINE_CLASS_NAME("test_byte_filer"); + int run_simple_test(); + int run_file_scan(); + virtual int execute(); +}; + +const astring &TEST_FILE() +{ + const char *TEST_FILE_BASE = "/zz_garbage.txt"; + static astring __hidden_filename; + if (!__hidden_filename) { + __hidden_filename = environment::get("TMP"); + if (!__hidden_filename) __hidden_filename = "/tmp"; + __hidden_filename += astring(TEST_FILE_BASE); + } + return __hidden_filename; +} + +int test_byte_filer::run_simple_test() +{ + FUNCDEF("run_simple_test"); +#ifdef DEBUG + LOG("ahoy, beginning file test..."); + LOG(astring("test file is ") + TEST_FILE()); +#endif + + chaos randomizer; + +//hmmm: move to t_filename. + // test filename's exist operation. + byte_filer garbage(TEST_FILE().s(), "wb"); + garbage.write("oy.\n"); + garbage.close(); + filename test1(TEST_FILE()); + ASSERT_TRUE(test1.exists(), "exists test file should exist"); + filename test2("c:\\this_file_shouldNt_exist_ever.txt"); + ASSERT_FALSE(test2.exists(), "weird file should not existed"); + // test again to make sure it didn't create it. + ASSERT_FALSE(test2.exists(), "weird file should still not exist"); + test1.unlink(); + + int block_size = randomizer.inclusive(3000, 30000); +#ifdef DEBUG + LOG(a_sprintf("block size=%d", block_size)); +#endif + abyte *original_block = new abyte[block_size]; + for (int i = 0; i < block_size; i++) + original_block[i] = abyte(randomizer.inclusive(32, 126)); + unsigned int original_checksum + = checksums::bizarre_checksum((abyte *)original_block, block_size); + if (original_checksum) {} // compiler quieting. +#ifdef DEBUG + LOG(a_sprintf("random block checksum=%d", original_checksum)); +#endif + { + byte_array to_stuff_in_file(block_size, original_block); + delete [] original_block; + byte_filer fred(TEST_FILE(), "w+"); + fred.write(to_stuff_in_file); + } +#ifdef DEBUG + LOG(astring("about to compare file to checksum")); +#endif + { + abyte *temp_array = new abyte[21309]; + byte_array to_fake_stuff(21309, temp_array); + delete [] temp_array; + byte_filer fred(TEST_FILE(), "r"); +#ifdef DEBUG + LOG(astring("about to try writing to file")); +#endif + int should_be_failure = fred.write(to_fake_stuff); + ASSERT_EQUAL(should_be_failure, 0, "write on read only, should not succeed"); + +/// int fredsize = int(fred.size()); +/// fred.chunk_factor(fredsize); + +#ifdef DEBUG + LOG(a_sprintf("about to try reading from file %d bytes", fredsize)); +#endif + byte_array file_contents; + int bytes_read = fred.read(file_contents, block_size * 2); + ASSERT_EQUAL(bytes_read, block_size, "reading entire file should get proper size"); + un_int check_2 = checksums::bizarre_checksum((abyte *)file_contents.access(), file_contents.length()); + ASSERT_EQUAL((int)check_2, (int)original_checksum, "should read correct contents for checksum"); + } + +#define FACTOR 1354 + + { + int numpacs = number_of_packets(block_size, FACTOR); + byte_filer fred(TEST_FILE(), "rb"); +///file::READ_ONLY); +/// fred.chunk_factor(FACTOR); + int whole_size = 0; + for (int i = 0; i < numpacs; i++) { + byte_array blob_i; + int bytes_in = fred.read(blob_i, FACTOR); + ASSERT_FALSE(bytes_in > FACTOR, "we should never somehow read in more than we asked for"); + whole_size += blob_i.length(); + } + ASSERT_EQUAL(whole_size, fred.length(), "chunking comparison should see sizes as same"); + } + +// test writing out a copy and comparing them... there's no == on files! + + ASSERT_TRUE(filename(TEST_FILE()).unlink(), "cleanup should be able to remove temporary file"); + + // it seems everything worked during our tests. + return 0; +} + +int test_byte_filer::run_file_scan() +{ + FUNCDEF("run_file_scan"); + chaos randomizer; + + string_array files(_global_argc, (const char **)_global_argv); + files.zap(0, 0); // toss the first element since that's our app filename. + + if (!files.length()) { + // pretend they gave us the list of files in the TMP directory. some of + // these might fail if they're locked up. +// astring tmpdir = environment::get("TMP"); + astring tmpdir = application_configuration::current_directory(); + directory dir(tmpdir); + for (int i = 0; i < dir.files().length(); i++) { + // skip text files since we use those right here. + if ( (dir.files()[i].ends(".txt")) || (dir.files()[i].ends(".txt")) ) + continue; + astring chewed_string = tmpdir + "/" + dir.files()[i]; + files += chewed_string; + } +//LOG(astring("added files since no cmd args: ") + files.text_form()); + } + + byte_array data_found; + for (int i = 0; i < files.length(); i++) { + astring curr = files[i]; +// LOG(a_sprintf("file %d: ", i) + curr); + byte_filer test(curr, "rb"); + if (!test.good()) { + LOG(astring("good check: ") + curr + " cannot be opened. is this bad?"); + continue; + } + + // check that we get the expected position report from scooting to the + // end of a file. + test.seek(0, byte_filer::FROM_END); + ASSERT_EQUAL((int)test.tell(), (int)test.length(), "seek check should get to end as expected"); + test.seek(0, byte_filer::FROM_START); + + size_t len = test.length(); +//log(a_sprintf("file len is %.0f", double(len))); + size_t posn = 0; + while ( (posn < len) && !test.eof() ) { + size_t readlen = randomizer.inclusive(1, 256 * KILOBYTE); +//log(a_sprintf("read %u bytes, posn now %d bytes", readlen, posn)); + int bytes_read = int(test.read(data_found, int(readlen))); + ASSERT_TRUE(bytes_read >= 0, "reading should not fail to read some bytes"); + if (bytes_read > 0) { + posn += bytes_read; + } + } + ASSERT_TRUE(test.eof(), "eof check should see us at eof"); + ASSERT_EQUAL((int)posn, (int)len, "eof check should be at right position"); +// log(astring("successfully read ") + curr); + } + + return 0; +} + +int test_byte_filer::execute() +{ +// FUNCDEF("execute"); + int ret = run_simple_test(); + if (ret) return ret; // failed. + ret = run_file_scan(); + if (ret) return ret; // failed here. + + return final_report(); +} + +HOOPLE_MAIN(test_byte_filer, ) + diff --git a/core/library/tests_filesystem/test_directory.cpp b/core/library/tests_filesystem/test_directory.cpp new file mode 100644 index 00000000..e2e70165 --- /dev/null +++ b/core/library/tests_filesystem/test_directory.cpp @@ -0,0 +1,99 @@ +/* +* Name : test_directory +* Author : Chris Koeritz +* Purpose: +* Tests the directory object out to see if it scans properly. +** +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace mathematics; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +////////////// + +class test_directory : public virtual unit_base, public virtual application_shell +{ +public: + test_directory() : application_shell() {} + DEFINE_CLASS_NAME("test_directory"); + int execute(); +}; + +////////////// + +int test_directory::execute() +{ + FUNCDEF("execute"); + { + astring path = "/tmp"; // default path. +#ifdef __WIN32__ + path = "c:/"; // default path for windoze. +#endif + if (application::_global_argc >= 2) + path = application::_global_argv[1]; + + astring pattern = "*"; + if (application::_global_argc >= 3) + pattern = application::_global_argv[2]; + +// log(astring("Scanning directory named \"") + path + "\""); +// log(astring("Using pattern-match \"") + pattern + "\""); + + directory dir(path, pattern.s()); + ASSERT_TRUE(dir.good(), "the current directory should be readable"); +// log(path + " contained these files:"); + astring names; + for (int i = 0; i < dir.files().length(); i++) { + names += dir.files()[i] + " "; + } + astring split; + string_manipulation::split_lines(names, split, 4); +// log(split); +// log(path + " contained these directories:"); + names = ""; + for (int i = 0; i < dir.directories().length(); i++) { + names += dir.directories()[i] + " "; + } + string_manipulation::split_lines(names, split, 4); +// log(split); + } +//hmmm: the above test proves zilch. +// it needs to do this differently. +// instead of relying on someone else's folder, pick and make our own. +// then fill it with some known stuff. +// verify then that the read form is identical! + + + +//more tests! + + return final_report(); +} + +HOOPLE_MAIN(test_directory, ) + diff --git a/core/library/tests_filesystem/test_directory_tree.cpp b/core/library/tests_filesystem/test_directory_tree.cpp new file mode 100644 index 00000000..c1894aca --- /dev/null +++ b/core/library/tests_filesystem/test_directory_tree.cpp @@ -0,0 +1,207 @@ +/* +* Name : test_directory_tree +* Author : Chris Koeritz +* Purpose: +* Tests the directory_tree object on some well-known directories. +** +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace mathematics; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +const bool JUST_SIZES = false; + // determines if we'll only compare file size and time. + +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +class test_directory_tree : public virtual unit_base, virtual public application_shell +{ +public: + test_directory_tree() : application_shell() {} + DEFINE_CLASS_NAME("test_directory_tree"); + int execute(); +}; + +int test_directory_tree::execute() +{ + FUNCDEF("execute"); + + astring path = "/usr/include"; +#ifdef __WIN32__ + // default path for windoze uses an area that should always exist. + path = environment::get("COMMONPROGRAMFILES"); +#endif + + // process the command line parameters, which are optionally a directory name and + // a pattern to use when scanning. + if (_global_argc >= 2) + path = _global_argv[1]; + + astring pattern = "*"; + if (_global_argc >= 3) + pattern = _global_argv[2]; + + { +// log(astring("Scanning directory tree at \"") + path + "\""); +// log(astring("Using pattern-match \"") + pattern + "\""); + + directory_tree dir(path, pattern.s()); + ASSERT_TRUE(dir.good(), "directory_tree construction should succeed and be readable."); + + dir_tree_iterator *ted = dir.start(directory_tree::prefix); + // create our iterator to do a prefix traversal. + + int depth; // current depth in tree. + filename curr; // the current path the iterator is at. + string_array files; // the filenames held at the iterator. + + while (directory_tree::current(*ted, curr, files)) { + // we have a good directory to show. + directory_tree::depth(*ted, depth); +// log(string_manipulation::indentation(depth * 2) + astring("[") +// + curr.raw() + "]"); + astring names; + for (int i = 0; i < files.length(); i++) names += files[i] + " "; + if (names.length()) { + astring split; + string_manipulation::split_lines(names, split, depth * 2 + 2); +// log(split); + } + + // go to the next place. + directory_tree::next(*ted); + } + + directory_tree::throw_out(ted); + } + + { + // second test group. seek operation. +//scan the directory, create some temporary directories and junk filenames +//therein, then seek to that location. + + } + + { + // third test group. tree comparison operation. +// log(astring("Self-comparing directory tree at \"") + path + "\""); +// log(astring("Using pattern-match \"") + pattern + "\""); + +// LOG("reading tree 1."); + directory_tree dir(path, pattern.s()); + ASSERT_TRUE(dir.good(), "the directory should be readable for self-compare"); + + // now read a copy of the tree also. +// LOG("reading tree 2."); + directory_tree dir2(path, pattern.s()); + ASSERT_TRUE(dir2.good(), "the directory should read the second time fine too"); + +// LOG("comparing the two trees."); + filename_list diffs; + directory_tree::compare_trees(dir, dir2, diffs, file_info::EQUAL_CHECKSUM_TIMESTAMP_FILESIZE); +//LOG(diffs.text_form()); + + ASSERT_FALSE(diffs.elements(), "there should be no differences comparing identical dirs"); + } + + { + // fourth test: see if the calculate function works. +// log(astring("Calculating sums for tree at \"") + path + "\""); +// log(astring("Using pattern-match \"") + pattern + "\""); + +// LOG("reading tree 1."); + directory_tree dir(path, pattern.s()); + ASSERT_TRUE(dir.good(), "the directory should be readable for checksums"); + + // now read a copy of the tree also. +// LOG("reading tree 2."); + directory_tree dir2(path, pattern.s()); + ASSERT_TRUE(dir2.good(), "checksummer should be able to read second time also"); + +// LOG("calculating checksums for tree 1."); + ASSERT_TRUE(dir.calculate(JUST_SIZES), "the first checksummer tree can be calculated"); + +// LOG("calculating checksums for tree 2."); + ASSERT_TRUE(dir2.calculate(JUST_SIZES), "the second checksummer tree can be calculated"); + +// LOG("comparing the two trees."); + filename_list diffs; + directory_tree::compare_trees(dir, dir2, diffs, file_info::EQUAL_CHECKSUM_TIMESTAMP_FILESIZE); +//LOG(diffs.text_form()); + + ASSERT_FALSE(diffs.elements(), "no checksummer differences should be seen for identical directories"); + } + + { + // fifth test: see if the packing works. +// log(astring("Reading tree for packing at \"") + path + "\""); +// log(astring("Using pattern-match \"") + pattern + "\""); + +// LOG("reading tree."); + directory_tree dir(path, pattern.s()); + ASSERT_TRUE(dir.good(), "packer directory should be read"); + +// LOG("calculating checksums for tree."); + ASSERT_TRUE(dir.calculate(JUST_SIZES), "the first packer tree can be calculated"); + + byte_array packed_form; + int size_packed = dir.packed_size(); + dir.pack(packed_form); +//LOG(a_sprintf("tree became %d abyte array", packed_form.length())); + ASSERT_EQUAL(size_packed, packed_form.length(), "packed size should be right"); + + directory_tree dir2; + ASSERT_TRUE(dir2.unpack(packed_form), "second tree can be unpacked from the first"); + +// LOG("comparing the two trees."); + filename_list diffs; + directory_tree::compare_trees(dir, dir2, diffs, file_info::EQUAL_CHECKSUM_TIMESTAMP_FILESIZE); +//LOG(diffs.text_form()); + + ASSERT_FALSE(diffs.elements(), "identical directories should stay same after packing"); + + directory_tree::compare_trees(dir2, dir, diffs, file_info::EQUAL_CHECKSUM_TIMESTAMP_FILESIZE); + ASSERT_FALSE(diffs.elements(), "no differences for reverse compare identical dirs"); + } + +// nth test: +// combine the results of the second test with a comparison like in the +// third test. delete all of those temporary files that were added. +// rescan tree. make sure that a tree containing the temporaries +// when compared with the current post-deletion tree produces a list +// that contains all the temporary files and directories. + + +//hmmm: more tests! + + return final_report(); +} + +HOOPLE_MAIN(test_directory_tree, ) + diff --git a/core/library/tests_filesystem/test_file_info.cpp b/core/library/tests_filesystem/test_file_info.cpp new file mode 100644 index 00000000..cc0a41d2 --- /dev/null +++ b/core/library/tests_filesystem/test_file_info.cpp @@ -0,0 +1,111 @@ +/* +* Name : test_file_info +* Author : Chris Koeritz +* Copyright (c) 1991-$now By Author. This program is free software; you can * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace timely; +using namespace unit_test; + +//#define DEBUG_TEST_FILE_INFO + // uncomment for noisy version. + +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +static chaos a_randomizer; + +////////////// + +class test_file_info : public application_shell, public unit_base +{ +public: + test_file_info() : application_shell(), unit_base() {} + + DEFINE_CLASS_NAME("test_file_info"); + + virtual int execute(); +}; + +////////////// + +//hmmm: stolen from ssl_init. +byte_array random_bytes(int length) +{ + byte_array seed; + for (int i = 0; i < length; i++) + seed += abyte(chaos().inclusive(0, 255)); + return seed; +} + +int test_file_info::execute() +{ + FUNCDEF("execute"); +#ifdef __UNIX__ + file_time absurdity_time("/"); +#endif +#ifdef __WIN32__ + file_time absurdity_time("c:/"); +#endif + + // test storing info via the constructor. + file_info testing(filename("/usr/schrodingers/dog/got/away"), 7298238); + testing._time = absurdity_time; + testing._checksum = 1283412; + ASSERT_EQUAL((int)testing._file_size, (int)7298238, "constructor file size"); + ASSERT_EQUAL(testing._time, absurdity_time, "constructor file time"); + ASSERT_EQUAL(testing._checksum, 1283412, "constructor checksum"); + ASSERT_EQUAL((filename &)testing, filename("/usr/schrodingers/dog/got/away"), + "constructor filename"); + + // test packing the object and packed_size. + byte_array packed; + int size = testing.packed_size(); + testing.pack(packed); + ASSERT_EQUAL(size, packed.length(), "basic packed size accuracy"); + file_info unstuffy; + ASSERT_TRUE(unstuffy.unpack(packed), "basic unpacking"); + + // test validity after unpacking. + ASSERT_EQUAL((int)unstuffy._file_size, (int)7298238, "constructor file size"); + ASSERT_EQUAL(unstuffy._time, absurdity_time, "constructor file time"); + ASSERT_EQUAL(unstuffy._checksum, 1283412, "constructor checksum"); + ASSERT_EQUAL((filename &)unstuffy, filename("/usr/schrodingers/dog/got/away"), + "constructor filename"); + + // test the extra bits, the attachment and secondary name. + astring seconame = "glorabahotep"; + testing.secondary(seconame ); + const byte_array randobytes = random_bytes(chaos().inclusive(37, 4128)); + testing.attachment(randobytes); + packed.reset(); + size = testing.packed_size(); + testing.pack(packed); + ASSERT_EQUAL(size, packed.length(), "secondary packed size accuracy"); + ASSERT_TRUE(unstuffy.unpack(packed), "secondary unpacking"); + // test that the secondary name and attachment came back. + ASSERT_EQUAL(seconame, unstuffy.secondary(), "secondary name incorrect"); + ASSERT_EQUAL(randobytes, unstuffy.attachment(), "secondary attachment inaccurate"); + + return final_report(); +} + +HOOPLE_MAIN(test_file_info, ) + diff --git a/core/library/tests_filesystem/test_file_time.cpp b/core/library/tests_filesystem/test_file_time.cpp new file mode 100644 index 00000000..6e79f9a2 --- /dev/null +++ b/core/library/tests_filesystem/test_file_time.cpp @@ -0,0 +1,107 @@ +/* +* Name : test_file_time +* Author : Chris Koeritz +* Copyright (c) 1991-$now By Author. This program is free software; you can * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef __UNIX__ + #include +#endif + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace timely; +using namespace unit_test; + +//#define DEBUG_TEST_FILE_INFO + // uncomment for noisy version. + +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +static chaos a_randomizer; + +////////////// + +class test_file_time : public application_shell, public unit_base +{ +public: + test_file_time() : application_shell(), unit_base() {} + + DEFINE_CLASS_NAME("test_file_time"); + + virtual int execute(); +}; + +////////////// + +int test_file_time::execute() +{ + FUNCDEF("execute"); + +#ifdef __UNIX__ + astring toppy("/"); +#endif +#ifdef __WIN32__ + astring toppy("c:/ntldr"); // will work for any modern windows OS. +#endif + + // test storing info via the constructor. + file_time absurdity_time(toppy); + FILE *topdir = fopen(toppy.s(), "r"); + file_time nutty_time(topdir); + struct stat sbuffer; + int filenum = fileno(topdir); + int stat_okay = fstat(filenum, &sbuffer); + ASSERT_FALSE(stat_okay, "failure to read filetime"); + file_time goofy_time(sbuffer.st_mtime); + fclose(topdir); + file_time testing(goofy_time); // copy ctor. + // test that they all got the same idea from the file. + ASSERT_EQUAL(absurdity_time, nutty_time, "filename vs. FILE ctor"); + ASSERT_EQUAL(absurdity_time, goofy_time, "filename vs. time_t ctor"); + ASSERT_EQUAL(absurdity_time, testing, "filename vs. copy ctor"); + ASSERT_EQUAL(nutty_time, goofy_time, "FILE vs. time_t ctor"); + ASSERT_EQUAL(nutty_time, testing, "FILE vs. copy ctor"); + // one reversed direction check. + ASSERT_EQUAL(goofy_time, absurdity_time, "time_t vs. filename ctor"); + + // test packing the object and packed_size. + byte_array packed; + int size = testing.packed_size(); + testing.pack(packed); + ASSERT_EQUAL(size, packed.length(), "packed size accuracy"); + file_time unstuffy; + ASSERT_TRUE(unstuffy.unpack(packed), "unpacking"); + ASSERT_EQUAL((double)testing.raw(), (double)unstuffy.raw(), "unpacked contents should be equal to prior"); + + // test the text_form method. + astring text; + testing.text_form(text); + ASSERT_INEQUAL(text.length(), 0, "text_form produces text"); + + // test validity after unpacking. + ASSERT_EQUAL(unstuffy, goofy_time, "constructor file size"); + + return final_report(); +} + +HOOPLE_MAIN(test_file_time, ) + diff --git a/core/library/tests_filesystem/test_filename.cpp b/core/library/tests_filesystem/test_filename.cpp new file mode 100644 index 00000000..8f08de45 --- /dev/null +++ b/core/library/tests_filesystem/test_filename.cpp @@ -0,0 +1,226 @@ +/* +* Name : test_filename +* Author : Chris Koeritz +** +* Copyright (c) 1993-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#define DEBUG_FILENAME_TEST + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace mathematics; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +class test_filename : virtual public unit_base, public virtual application_shell +{ +public: + test_filename() : application_shell() {} + DEFINE_CLASS_NAME("test_filename"); + virtual int execute(); + void clean_sequel(astring &sequel); +}; + +void test_filename::clean_sequel(astring &sequel) +{ sequel.replace_all('\\', '/'); } + +int test_filename::execute() +{ + FUNCDEF("execute") + { + // first test group. + filename gorgeola(""); + ASSERT_FALSE(gorgeola.exists(), "an empty filename should not exist"); + } + + { + // second test group. + astring GROUP = "separate-- "; + filename turkey("/omega/ralph/turkey/buzzard.txt"); + string_array pieces; + turkey.separate(pieces); + ASSERT_TRUE(pieces[1].equal_to("omega"), GROUP + "the first piece didn't match."); + ASSERT_TRUE(pieces[2].equal_to("ralph"), GROUP + "the second piece didn't match."); + ASSERT_TRUE(pieces[3].equal_to("turkey"), GROUP + "the third piece didn't match."); + ASSERT_TRUE(pieces[4].equal_to("buzzard.txt"), GROUP + "the fourth piece didn't match."); + ASSERT_EQUAL(pieces.length(), 5, GROUP + "the list was the wrong length"); + } + + { + // third test group. + astring GROUP = "third: test compare_prefix "; + filename turkey("/omega/ralph/turkey/buzzard.txt"); + filename murpin1("/omega"); + filename murpin2("/omega/ralph"); + filename murpin3("/omega/ralph/turkey"); + filename murpin4("/omega/ralph/turkey/buzzard.txt"); + filename murpin_x1("ralph/turkey/buzzard.txt"); + filename murpin_x2("/omega/ralph/turkey/buzzard.txt2"); + filename murpin_x3("/omega/turkey/buzzard.txt"); + filename murpin_x4("/omega/ralph/turkey/b0/buzzard.txt"); + filename murpin_x5("moomega/ralph/turkey"); + + astring sequel; + ASSERT_TRUE(murpin1.compare_prefix(turkey, sequel), GROUP + "first should match but didn't"); + clean_sequel(sequel); + ASSERT_TRUE(sequel.equal_to("ralph/turkey/buzzard.txt"), GROUP + "first sequel was wrong"); + ASSERT_TRUE(murpin2.compare_prefix(turkey, sequel), GROUP + "second should match but didn't"); + clean_sequel(sequel); + ASSERT_TRUE(sequel.equal_to("turkey/buzzard.txt"), GROUP + "second sequel was wrong"); + ASSERT_TRUE(murpin3.compare_prefix(turkey, sequel), GROUP + "third should match but didn't"); + clean_sequel(sequel); + ASSERT_TRUE(sequel.equal_to("buzzard.txt"), GROUP + "third sequel was wrong"); + ASSERT_TRUE(murpin4.compare_prefix(turkey, sequel), GROUP + "fourth should match but didn't"); + ASSERT_FALSE(sequel.t(), GROUP + "fourth had a sequel but shouldn't"); + + ASSERT_FALSE(murpin_x1.compare_prefix(turkey, sequel), + GROUP + "x-first should not match but did"); + ASSERT_FALSE(sequel.t(), + GROUP + "x-first had a sequel but shouldn't"); + ASSERT_FALSE(murpin_x2.compare_prefix(turkey, sequel), + GROUP + "x-second should not match but did"); + ASSERT_FALSE(sequel.t(), + GROUP + "x-second had a sequel but shouldn't"); + ASSERT_FALSE(murpin_x3.compare_prefix(turkey, sequel), + GROUP + "x-third should not match but did"); + ASSERT_FALSE(sequel.t(), + GROUP + "x-third had a sequel but shouldn't"); + ASSERT_FALSE(murpin_x4.compare_prefix(turkey, sequel), + GROUP + "x-fourth should not match but did"); + ASSERT_FALSE(sequel.t(), + GROUP + "x-fourth had a sequel but shouldn't"); + ASSERT_FALSE(murpin_x5.compare_prefix(turkey, sequel), + GROUP + "x-fifth should not match but did"); + ASSERT_FALSE(sequel.t(), + GROUP + "x-fifth had a sequel but shouldn't"); + + // check that the functions returning no sequel are still correct. + ASSERT_TRUE(murpin1.compare_prefix(turkey), GROUP + "the two versions differed!"); + ASSERT_FALSE(murpin_x1.compare_prefix(turkey), GROUP + "x-the two versions differed!"); + } + + { + // fourth test group. + astring GROUP = "fourth: test compare_suffix "; + filename turkey("/omega/ralph/turkey/buzzard.txt"); + filename murpin1("turkey\\buzzard.txt"); + filename murpin2("turkey/buzzard.txt"); + filename murpin3("ralph/turkey/buzzard.txt"); + filename murpin4("omega/ralph/turkey/buzzard.txt"); + filename murpin5("/omega/ralph/turkey/buzzard.txt"); + + ASSERT_TRUE(murpin1.compare_suffix(turkey), GROUP + "compare 1 failed"); + ASSERT_TRUE(murpin2.compare_suffix(turkey), GROUP + "compare 2 failed"); + ASSERT_TRUE(murpin3.compare_suffix(turkey), GROUP + "compare 3 failed"); + ASSERT_TRUE(murpin4.compare_suffix(turkey), GROUP + "compare 4 failed"); + ASSERT_TRUE(murpin5.compare_suffix(turkey), GROUP + "compare 5 failed"); + + ASSERT_FALSE(turkey.compare_suffix(murpin1), GROUP + "compare x.1 failed"); + } + + { + // fifth test group. + // tests out the canonicalization method on any parameters given on + // the command line, including the program name. + astring GROUP = "fifth: canonicalize command-line paths "; +// log(GROUP, ALWAYS_PRINT); + for (int i = 0; i < application::_global_argc; i++) { + filename canony(application::_global_argv[i]); +// log(a_sprintf("parm %d:\n\tfrom \"%s\"\n\t to \"%s\"", i, application::_global_argv[i], +// canony.raw().s()), ALWAYS_PRINT); + +//hmmm: the above wasn't really a test so much as a look at what we did. +// we should run the canonicalizer against a set of known paths so we can know what to +// expect. + + } + } + + { + // sixth test group. + astring GROUP = "sixth: testing pop and push "; + // test dossy paths. + filename test1("c:/flug/blumen/klemper/smooden"); +//log(astring("base=") + test1.basename(), ALWAYS_PRINT); + ASSERT_EQUAL(test1.basename(), astring("smooden"), GROUP + "basename 1 failed"); +//log(astring("got past basename 1 test that was failing.")); + ASSERT_EQUAL(test1.dirname(), filename("c:/flug/blumen/klemper"), + GROUP + "d-dirname 1 failed"); +//log(astring("got past a test or so after that.")); + filename test2 = test1; + astring popped = test2.pop(); + ASSERT_EQUAL(popped, astring("smooden"), GROUP + "dpop 1 return failed"); + ASSERT_EQUAL(test2, filename("c:/flug/blumen/klemper"), GROUP + "dpop 1 failed"); + test2.pop(); + test2.pop(); + ASSERT_EQUAL(test2, filename("c:/flug"), GROUP + "dpop 2 failed"); + popped = test2.pop(); + ASSERT_EQUAL(popped, astring("flug"), GROUP + "dpop 1 return failed"); + ASSERT_EQUAL(test2, filename("c:/"), GROUP + "dpop 3 failed"); + test2.pop(); + ASSERT_EQUAL(test2, filename("c:/"), GROUP + "dpop 3 failed"); + test2.push("flug"); + test2.push("blumen"); + test2.push("klemper"); + ASSERT_EQUAL(test2, filename("c:/flug/blumen/klemper"), GROUP + "dpush 1 failed"); + // test unix paths. + filename test3("/flug/blumen/klemper/smooden"); + ASSERT_EQUAL(test3.basename(), astring("smooden"), GROUP + "basename 1 failed"); + ASSERT_EQUAL(test3.dirname(), filename("/flug/blumen/klemper"), + GROUP + "u-dirname 1 failed"); + filename test4 = test3; + popped = test4.pop(); + ASSERT_EQUAL(popped, astring("smooden"), GROUP + "upop 1 return failed"); + ASSERT_EQUAL(test4, filename("/flug/blumen/klemper"), GROUP + "upop 1 failed"); + test4.pop(); + test4.pop(); + ASSERT_EQUAL(test4, filename("/flug"), GROUP + "upop 2 failed"); + popped = test4.pop(); + ASSERT_EQUAL(popped, astring("flug"), GROUP + "upop 1 return failed"); + ASSERT_EQUAL(test4, filename("/"), GROUP + "upop 3 failed"); + test4.pop(); + ASSERT_EQUAL(test4, filename("/"), GROUP + "upop 3 failed"); + test4.push("flug"); + test4.push("blumen"); + test4.push("klemper"); + ASSERT_EQUAL(test4, filename("/flug/blumen/klemper"), GROUP + "upush 1 failed"); + } + { + // seventh test group. + astring GROUP = "seventh: testing pack and unpack "; + filename test1("/usr/local/athabasca"); + byte_array packed; + int size_guess = test1.packed_size(); + test1.pack(packed); + ASSERT_EQUAL(size_guess, packed.length(), GROUP + "packed_size 1 failed"); + filename test2; + ASSERT_TRUE(test2.unpack(packed), GROUP + "unpack 1 failed"); + ASSERT_EQUAL(test2, test1, GROUP + "packed contents differ, 1 failed"); + } + + return final_report(); +} + +HOOPLE_MAIN(test_filename, ) + diff --git a/core/library/tests_filesystem/test_huge_file.cpp b/core/library/tests_filesystem/test_huge_file.cpp new file mode 100644 index 00000000..f9209eb7 --- /dev/null +++ b/core/library/tests_filesystem/test_huge_file.cpp @@ -0,0 +1,110 @@ +/* +* Name : test_huge_file +* Author : Chris Koeritz +** +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace configuration; +using namespace mathematics; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +class test_huge_file : public virtual unit_base, virtual public application_shell +{ +public: + test_huge_file() : application_shell() {} + DEFINE_CLASS_NAME("test_huge_file"); + void run_file_scan(); + virtual int execute(); +}; + +void test_huge_file::run_file_scan() +{ + FUNCDEF("run_file_scan"); + chaos randomizer; + + string_array files(application::_global_argc, (const char **)application::_global_argv); + files.zap(0, 0); // toss the first element since that's our app filename. + + if (!files.length()) { + // pretend they gave us the list of files in the TMP directory. some of + // these might fail if they're locked up. +// astring tmpdir = environment::get("TMP"); + astring tmpdir = application_configuration::current_directory(); + directory dir(tmpdir); + for (int i = 0; i < dir.files().length(); i++) { + // skip text files since we use those right here. + if (dir.files()[i].ends(".txt")) + continue; + astring chewed_string = tmpdir + "/" + dir.files()[i]; + files += chewed_string; + } +//LOG(astring("added files since no cmd args: ") + files.text_form()); + } + + byte_array data_found; + for (int i = 0; i < files.length(); i++) { + astring curr = files[i]; +// LOG(a_sprintf("file %d: ", i) + curr); + huge_file test(curr, "rb"); + ASSERT_TRUE(test.good(), "good check should say yes, it's good."); + double len = test.length(); +//log(a_sprintf("file len is %.0f", len)); + double posn = 0; + while ( (posn < len) && !test.eof() ) { + int readlen = randomizer.inclusive(1, 256 * KILOBYTE); +//log(a_sprintf("read %.0f bytes, posn now %.0f bytes", double(readlen), posn)); + int bytes_read = 0; + outcome ret = test.read(data_found, readlen, bytes_read); + ASSERT_EQUAL(ret.value(), huge_file::OKAY, "should be able to read file"); + if (ret == huge_file::OKAY) { + posn += bytes_read; + } + } + ASSERT_TRUE(test.eof(), "eof check should be at eof."); + if (posn != len) + log(a_sprintf("failed check, want %.0f, got %.0f", double(len), double(posn))); + ASSERT_EQUAL(posn, len, "eof check should be at right position: "); +// log(astring("successfully read ") + curr); + } +} + +int test_huge_file::execute() +{ + FUNCDEF("execute"); + run_file_scan(); + return final_report(); +} + +HOOPLE_MAIN(test_huge_file, ) + diff --git a/core/library/tests_mathematics/makefile b/core/library/tests_mathematics/makefile new file mode 100644 index 00000000..bef10eb2 --- /dev/null +++ b/core/library/tests_mathematics/makefile @@ -0,0 +1,12 @@ +include cpp/variables.def + +PROJECT = tests_mathematics +TYPE = test +TARGETS = test_chaos.exe test_double_plus.exe test_math_ops.exe +DEFINITIONS += USE_HOOPLE_DLLS +LOCAL_LIBS_USED = unit_test application configuration filesystem loggers mathematics nodes \ + structures processes textual timely structures basis +RUN_TARGETS = $(ACTUAL_TARGETS) + +include cpp/rules.def + diff --git a/core/library/tests_mathematics/test_chaos.cpp b/core/library/tests_mathematics/test_chaos.cpp new file mode 100644 index 00000000..7be780f4 --- /dev/null +++ b/core/library/tests_mathematics/test_chaos.cpp @@ -0,0 +1,99 @@ +/* +* Name : test_chaos +* Author : Chris Koeritz +** +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +//#define DEBUG_CHAOS + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace mathematics; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define MAX_RANDOM_BINS 40 +#define MAX_TEST_CYCLES 10008 +#define AVG_EXPECTED_PER_BIN (double(MAX_TEST_CYCLES) / double(MAX_RANDOM_BINS)) +#define VARIATION_ALLOWED (AVG_EXPECTED_PER_BIN * 0.1) +#define ANOMALIES_ALLOWED (MAX_RANDOM_BINS / 4) + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), astring(to_print)) + +class test_chaos : virtual public unit_base, virtual public application_shell +{ +public: + test_chaos() : application_shell() {} + DEFINE_CLASS_NAME("test_chaos"); + virtual int execute(); +}; + +int test_chaos::execute() +{ + FUNCDEF("execute"); +#ifdef DEBUG_CHAOS + LOG(a_sprintf("average expected=%f, variation allowed=%f", + AVG_EXPECTED_PER_BIN, VARIATION_ALLOWED)); +#endif + int results[MAX_RANDOM_BINS]; + for (int k = 0; k < MAX_RANDOM_BINS; k++) results[k] = 0; + chaos randomizer; + + for (int i = 0; i < MAX_TEST_CYCLES; i++) { + // first test if exclusivity is ensured... + int res = randomizer.exclusive(0, MAX_RANDOM_BINS - 1); + ASSERT_FALSE( (res <= 0) || (res >= MAX_RANDOM_BINS - 1), + "exclusive test should not go out of bounds"); + // then test for our statistics. + int base = randomizer.inclusive(-1000, 1000); + // pick a base for the number below. + res = randomizer.inclusive(base, base + MAX_RANDOM_BINS - 1); + ASSERT_FALSE( (res < base) || (res > base + MAX_RANDOM_BINS - 1), + "inclusive test should not go out of bounds"); +//LOG(a_sprintf("adding it to %d bin", res - base)); + results[res - base]++; + } +#ifdef DEBUG_CHAOS + LOG("Anomalies:"); +#endif + int failed_any = false; + for (int j = 0; j < MAX_RANDOM_BINS; j++) { + if (absolute_value(results[j] - AVG_EXPECTED_PER_BIN) > VARIATION_ALLOWED) { + failed_any++; +#ifdef DEBUG_CHAOS + LOG(astring(astring::SPRINTF, "%d: difference=%f", + j, double(results[j] - AVG_EXPECTED_PER_BIN))); +#endif + } + } +#ifdef DEBUG_CHAOS + if (!failed_any) LOG("None") + else LOG(a_sprintf("Saw %d anomalies of %d allowed.", failed_any, ANOMALIES_ALLOWED)); +#endif + + ASSERT_FALSE(failed_any > ANOMALIES_ALLOWED, + "probability anomalies should be less than the allowed number"); + return final_report(); +} + +HOOPLE_MAIN(test_chaos, ) + diff --git a/core/library/tests_mathematics/test_double_plus.cpp b/core/library/tests_mathematics/test_double_plus.cpp new file mode 100644 index 00000000..df216d9c --- /dev/null +++ b/core/library/tests_mathematics/test_double_plus.cpp @@ -0,0 +1,69 @@ +/* +* Name : test_double_plus +* Author : Chris Koeritz +* Purpose: Tests the double_plus class out. +** +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace geometric; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace unit_test; + +typedef double_plus floot; + +class test_double_plus : public virtual unit_base, public virtual application_shell +{ +public: + test_double_plus() : application_shell() {} + DEFINE_CLASS_NAME("test_double_plus"); + virtual int execute(); +}; + +int test_double_plus::execute() +{ + FUNCDEF("execute"); + floot x1 = 43.8106392325; + floot x2 = 43.8106; + ASSERT_EQUAL(x1, x2, "these doubles should be close enough"); + + floot y1 = 16.78; + floot y2 = 16.798273773; + ASSERT_INEQUAL(y1, y2, "these doubles shouldn't be close enough"); + + floot z1(16.8, 0.1); + floot z2(16.798273773, 0.1); + ASSERT_EQUAL(a_sprintf("%.3f", z2.truncate()), astring("16.800"), + "truncate should calculate proper string"); + ASSERT_EQUAL(z1, z2, "these doubles should be close enough with short delta"); + + floot q1(16.75, 0.01); + floot q2(16.749273773, 0.01); + ASSERT_EQUAL(a_sprintf("%.3f", q2.truncate()), astring("16.750"), + "wider truncate should calculate proper string"); + ASSERT_EQUAL(q1, q2, "next couple doubles should be close enough with small delta"); + + return final_report(); +} + +////////////// + +HOOPLE_MAIN(test_double_plus, ) + diff --git a/core/library/tests_mathematics/test_math_ops.cpp b/core/library/tests_mathematics/test_math_ops.cpp new file mode 100644 index 00000000..19f966a1 --- /dev/null +++ b/core/library/tests_mathematics/test_math_ops.cpp @@ -0,0 +1,60 @@ +/*****************************************************************************\ +* * +* Name : test_math_ops * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1993-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +//using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +class test_math_ops : virtual public unit_base, virtual public application_shell +{ +public: + test_math_ops() {} + DEFINE_CLASS_NAME("test_math_ops"); + virtual int execute(); +}; + +////////////// + +int test_math_ops::execute() +{ + FUNCDEF("execute"); + // test one: make sure factorial is working. + basis::un_int fact3 = math_ops::factorial(3); + ASSERT_EQUAL(fact3, 6, "3! did not equal 6"); + basis::un_int fact8 = math_ops::factorial(8); + ASSERT_EQUAL(fact8, 40320, "8! did not equal 40320"); + basis::un_int fact10 = math_ops::factorial(10); + ASSERT_EQUAL(fact10, 3628800, "10! did not equal 3628800"); + + return final_report(); +} + +HOOPLE_MAIN(test_math_ops, ) + diff --git a/core/library/tests_nodes/makefile b/core/library/tests_nodes/makefile new file mode 100644 index 00000000..30ddb492 --- /dev/null +++ b/core/library/tests_nodes/makefile @@ -0,0 +1,11 @@ +include cpp/variables.def + +PROJECT = tests_node +TYPE = test +TARGETS = test_list.exe test_node.exe test_packable_tree.exe test_symbol_tree.exe test_tree.exe +LOCAL_LIBS_USED = unit_test application nodes loggers processes filesystem configuration timely textual structures basis +DEFINITIONS += USE_HOOPLE_DLLS +RUN_TARGETS = $(ACTUAL_TARGETS) + +include cpp/rules.def + diff --git a/core/library/tests_nodes/test_list.cpp b/core/library/tests_nodes/test_list.cpp new file mode 100644 index 00000000..14a4fc7b --- /dev/null +++ b/core/library/tests_nodes/test_list.cpp @@ -0,0 +1,140 @@ +/*****************************************************************************\ +* * +* Name : test_list * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1993-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace configuration; +using namespace loggers; +using namespace mathematics; +using namespace nodes; +using namespace structures; +using namespace unit_test; + +//#define DEBUG_LIST + // uncomment this line to get more debugging output. + +const int DEFAULT_ITERATIONS = 50; + // the default number of times we run through our phase loop. + +typedef basket t_node; + // the object we store in the list, a templated integer. + +#define CASTER(bare_node) static_cast(bare_node) + // turns a node pointer into our special t_node. + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), to_print) + +////////////// + +class test_list : virtual public unit_base, virtual public application_shell +{ +public: + test_list() : unit_base() {} + DEFINE_CLASS_NAME("test_list"); + virtual int execute(); +}; + +HOOPLE_MAIN(test_list, ); + +////////////// + +int test_list::execute() +{ + FUNCDEF("execute"); + + list the_list; + chaos randomizer; + + int iterations_left = DEFAULT_ITERATIONS; + while (iterations_left-- > 0) { + + // run through the phases below as many times as we are told. + + { + // phase for adding a random number into the list. + int to_add = randomizer.inclusive(0, 100000); + + // seek the correct insertion place to keep the list ordered. + list::iterator iter = the_list.head(); + while (!iter.is_tail() && iter.observe() + && (CASTER(iter.observe())->stored() <= to_add) ) + iter++; + the_list.insert(iter, new t_node(2, to_add)); + } + + { + // test the list invariant (which is that all elements should be sorted + // in non-decreasing order). + list::iterator iter = the_list.tail(); + // initialize our comparator. + int bigger = CASTER(iter.observe())->stored(); + // loop backwards until we hit the head. + while (!iter.is_head()) { + // check that the last value is not less than the current value. + ASSERT_FALSE(bigger < CASTER(iter.observe())->stored(), + "invariant check should not find a mal-ordering in the list"); + bigger = CASTER(iter.observe())->stored(); + iter--; + } + } + + { + // if the conditions are favorable, we whack at least one element out of + // the list. + if (randomizer.inclusive(1, 100) < 20) { + int elem = the_list.elements(); + int to_whack = randomizer.inclusive(0, elem - 1); + + // start at the head of the list... + list::iterator iter = the_list.head(); + // and jump to the element we chose. + the_list.forward(iter, to_whack); + ASSERT_EQUAL(the_list.index(iter), to_whack, + "forward should not see logic error where index of element to zap is incorrect"); + ASSERT_FALSE(iter.is_tail(), + "forward should not see logic error where we get to the tail somehow"); + the_list.zap(iter); + } + } + + } + +#ifdef DEBUG_LIST + list::iterator iter = the_list.head(); + log(astring("")); + log(astring("list contents:")); + int indy = 0; + while (!iter.is_tail()) { + int item = CASTER(iter.observe())->stored(); + log(a_sprintf("item #%d: %d", indy, item)); + indy++; + iter++; + } +#endif + + return final_report(); +} + + diff --git a/core/library/tests_nodes/test_node.cpp b/core/library/tests_nodes/test_node.cpp new file mode 100644 index 00000000..82dfb77c --- /dev/null +++ b/core/library/tests_nodes/test_node.cpp @@ -0,0 +1,87 @@ +/*****************************************************************************\ +* * +* Name : t_node * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Tests out the node base class. * +* * +******************************************************************************* +* Copyright (c) 1989-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//hmmm: make this a more aggressive and realistic test. try implementing +// some list algorithms or graph algorithms to push node around. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace nodes; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +class test_node : public virtual unit_base, public virtual application_shell +{ +public: + test_node() {} + DEFINE_CLASS_NAME("test_node"); + virtual int execute(); +}; + +void bogon(byte_array *fred) +{ + if (fred) LOG("eep") + else LOG("eek"); +} + +int test_node::execute() +{ + FUNCDEF("execute"); + + byte_array blank; + basket fred(2, blank); + basket george(2, blank); + basket f_end1(0); + basket f_end2(0); + basket g_end1(0); + basket g_end2(0); + + node root; + + // add some links to the linkless root. + root.insert_link(0, &fred); + root.insert_link(1, &george); + + // set the pre-existing links to our end points. + fred.set_link(0, &f_end1); + fred.set_link(1, &f_end2); + george.set_link(0, &g_end1); + george.set_link(1, &g_end2); + + return final_report(); +} + +HOOPLE_MAIN(test_node, ); + diff --git a/core/library/tests_nodes/test_packable_tree.cpp b/core/library/tests_nodes/test_packable_tree.cpp new file mode 100644 index 00000000..3d971dda --- /dev/null +++ b/core/library/tests_nodes/test_packable_tree.cpp @@ -0,0 +1,204 @@ +////////////// +// Name : test_packable_tree +// Author : Chris Koeritz +////////////// +// Copyright (c) 1992-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +//! tests some critical properties for the packable tree. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __WIN32__ + #include +#endif +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace nodes; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +//#define DEBUG_PACKABLE_TREE + // set this to enable debugging features of the string class. + +//HOOPLE_STARTUP_CODE; + +//#define DEBUG_PACKABLE_TREE_TEST + // uncomment for testing version. + +#define LOG(s) EMERGENCY_LOG(program_wide_logger::get(), s) + +#define WHERE __WHERE__.s() + +#define FUNKIT(str) basis::a_sprintf("%s: %s", func, basis::astring(str).s()) + +// test: reports an error if the condition evaluates to non-zero. +int compnum = 0; +const float TEST_RUNTIME_DEFAULT = .02 * MINUTE_ms; + // the test, by default, will run for this long. + +////////////// + +class test_packable_tree : public application_shell, public unit_base +{ +public: + test_packable_tree() {} + ~test_packable_tree() {} + + DEFINE_CLASS_NAME("test_packable_tree"); + + virtual int execute() { + run_test(); + return final_report(); + } + + void run_test(); +}; + +HOOPLE_MAIN(test_packable_tree, ) + +////////////// + +//! it's not the one tree (c). this is just a derived packable_tree we can test with. + +class many_tree : public packable_tree +{ +public: + many_tree(const file_info &inf) : c_inf(new file_info(inf)) {} + + virtual ~many_tree() { WHACK(c_inf); } + + file_info get_info() const { return *c_inf; } + + virtual int packed_size() const { + return c_inf->packed_size(); + } + + virtual void pack(basis::byte_array &packed_form) const { + c_inf->pack(packed_form); + } + + virtual bool unpack(basis::byte_array &packed_form) { + if (!c_inf->unpack(packed_form)) return false; +//other pieces? + return true; + } + +private: + file_info *c_inf; +}; + +////////////// + +//! the factory that creates our special type of tree. + +class tree_defacto : public packable_tree_factory +{ +public: + packable_tree *create() { return new many_tree(file_info()); } +}; + +////////////// + +void test_packable_tree::run_test() +{ + FUNCDEF("run_test"); + + const file_info farfle(filename("arf"), 2010); + const file_info empty; + const file_info snood(filename("wookie"), 8888); + + { + // simple creation, packing, unpacking, destruction tests on a blank object. + many_tree gruntcake(farfle); + byte_array packed_form; + int pack_guess = gruntcake.packed_size(); + gruntcake.pack(packed_form); + ASSERT_EQUAL(pack_guess, packed_form.length(), FUNKIT("packed length is incorrect")); + many_tree untbake_target(empty); + ASSERT_TRUE(untbake_target.unpack(packed_form), FUNKIT("unpack operation failed")); + ASSERT_EQUAL(untbake_target.get_info(), gruntcake.get_info(), + FUNKIT("unpack had wrong contents")); + } + + { + // recursive packing tests... + // first layer. + many_tree *spork = new many_tree(farfle); + many_tree *limpet = new many_tree(empty); + many_tree *congo = new many_tree(snood); + many_tree *dworkin = new many_tree(empty); + many_tree *greep = new many_tree(farfle); + // second layer. + many_tree *flep = new many_tree(snood); + many_tree *glug = new many_tree(empty); + many_tree *aptitoot = new many_tree(farfle); + // third layer. + many_tree *grog = new many_tree(snood); + // connect first to second. + flep->attach(spork); + flep->attach(limpet); + glug->attach(congo); + aptitoot->attach(dworkin); + aptitoot->attach(greep); + // connect second to third. + grog->attach(flep); + grog->attach(glug); + grog->attach(aptitoot); + + // now recursively pack that bad boy three level tree. + byte_array packed; + int size_guess = grog->recursive_packed_size(); + grog->recursive_pack(packed); + ASSERT_EQUAL(size_guess, packed.length(), "recursive_packed_size failed"); + tree_defacto factotum; + packable_tree *unpacked = many_tree::recursive_unpack(packed, factotum); + ASSERT_TRUE(unpacked, "recursive_unpack failed"); + ASSERT_TRUE(dynamic_cast(unpacked), "recursive_unpack has wrong type"); + many_tree *survivor = dynamic_cast(unpacked); + +if (survivor) { +} + +//compare trees? + + } + +} + +////////////// + diff --git a/core/library/tests_nodes/test_symbol_tree.cpp b/core/library/tests_nodes/test_symbol_tree.cpp new file mode 100644 index 00000000..ef9917c4 --- /dev/null +++ b/core/library/tests_nodes/test_symbol_tree.cpp @@ -0,0 +1,93 @@ +/*****************************************************************************\ +* * +* Name : test_symbol_tree * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Creates a symbol_tree and performs some operations on it to assure * +* basic functionality. * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include +//#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace nodes; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +#define DEBUG_SYMBOL_TREE + +class test_symbol_tree : public virtual unit_base, virtual public application_shell +{ +public: + test_symbol_tree() {} + DEFINE_CLASS_NAME("test_symbol_tree"); + int execute(); +}; + +int test_symbol_tree::execute() +{ + LOG("please check memory usage and record it, then hit a key to start testing."); + + try { + symbol_tree t("blork"); + symbol_tree *curr = &t; + for (int i = 0; i < 40000; i++) { + // if the current node has any branches, we'll jump on one as the next + // place. + if (curr->branches()) { + // move to a random branch. + int which = randomizer().inclusive(0, curr->branches() - 1); + curr = (symbol_tree *)curr->branch(which); + } + astring rando = string_manipulation::make_random_name(1, 10); + curr->add(new symbol_tree(rando)); + } + LOG("check memory usage now with full size. then hit a key."); + } catch (...) { + LOG("crashed during tree stuffing."); + return 1; + } + + LOG("check memory usage after the run. then hit a key to end " + "the program."); + +//create a tree structure... +//perform known operations and validate shape of tree. + + return final_report(); +} + +////////////// + +HOOPLE_MAIN(test_symbol_tree, ) + diff --git a/core/library/tests_nodes/test_tree.cpp b/core/library/tests_nodes/test_tree.cpp new file mode 100644 index 00000000..5d3516ce --- /dev/null +++ b/core/library/tests_nodes/test_tree.cpp @@ -0,0 +1,307 @@ +/* +* Name : test_tree * +* Author : Chris Koeritz * +* Purpose: * +* Tests out the tree class. * +** +* Copyright (c) 1993-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace nodes; +using namespace loggers; +using namespace structures; +using namespace unit_test; + +//#define DEBUG_TEST_TREE + // uncomment if you want the noisy streams version. + +const int test_iterations = 20; + +class bogotre : public packable, public tree +{ +public: + bogotre(const char *start = NIL) : who_cares(42), i_sure_dont('l'), + another_useless_int(23) { + astring to_init(start); + if (to_init.length() < 1) to_init += "ack"; + to_init.stuff(the_actual_string, minimum(to_init.length()+1, 500)); + } + DEFINE_CLASS_NAME("bogotre"); + virtual ~bogotre() {} + virtual void pack(byte_array &packed_form) const; + virtual bool unpack(byte_array &to_unpack); + virtual int packed_size() const; + virtual abyte *held() const { return (abyte *)the_actual_string; } + virtual void print() const { +#ifdef DEBUG_TEST_TREE + printf(the_actual_string); +#endif + } + +private: + char the_actual_string[500]; + int who_cares; + char i_sure_dont; + int another_useless_int; +}; + +////////////// + +// forward. +typedef bogotre larch; +typedef void (applier)(larch *apply_to); +typedef tree::iterator traveller; + +class test_tree : public virtual unit_base, virtual public application_shell +{ +public: + test_tree() : application_shell() {} + DEFINE_CLASS_NAME("test_tree"); + virtual int execute(); + static void print_node(larch *curr_node); + static larch *next(larch *&move, larch *hook, traveller &skip); + static void apply(larch *apply_to, applier *to_apply, + tree::traversal_directions order); +}; + +////////////// + +#undef UNIT_BASE_THIS_OBJECT +#define UNIT_BASE_THIS_OBJECT (*dynamic_cast(application_shell::single_instance())) + +int bogotre::packed_size() const +{ return strlen(the_actual_string) + 1 + sizeof(int) * 2 + sizeof(abyte); } + +void bogotre::pack(byte_array &packed_form) const +{ + FUNCDEF("pack"); + astring(the_actual_string).pack(packed_form); + structures::attach(packed_form, who_cares); + structures::attach(packed_form, i_sure_dont); + structures::attach(packed_form, another_useless_int); +} + +bool bogotre::unpack(byte_array &packed_form) +{ + FUNCDEF("unpack"); + // 5 is the magic knowledge of minimum packed string. +//hmmm: make the minimum packed size a property of packables? + ASSERT_FALSE(packed_form.length() < + int(1 + sizeof(who_cares) + sizeof(i_sure_dont) + sizeof(another_useless_int)), + "size of package should be correct"); + astring unpacked; + ASSERT_TRUE(unpacked.unpack(packed_form), "should be able to retrieve string"); + ASSERT_TRUE(structures::detach(packed_form, who_cares), "should retrieve who_cares"); + ASSERT_TRUE(structures::detach(packed_form, i_sure_dont), "should retrieve i_sure_dont"); + ASSERT_TRUE(structures::detach(packed_form, another_useless_int), + "should retrieve another_..."); + + ASSERT_EQUAL(who_cares, 42, "bogotre_unpack - right value held in first int"); + ASSERT_EQUAL(i_sure_dont, 'l', "bogotre_unpack - right character held"); + ASSERT_EQUAL(another_useless_int, 23, "bogotre_unpack - right value held in second int"); + return true; +} + +////////////// + +/* +bogotre *togen(char *to_store) +{ bogotre *to_return = new bogotre(astring(to_store).s()); return to_return; } +*/ + +void test_tree::print_node(larch *curr_node) +{ + FUNCDEF("print_node"); + ASSERT_TRUE(curr_node, "tree shouldn't be nil"); + bogotre *real_curr = dynamic_cast(curr_node); + ASSERT_TRUE(real_curr, "contents shouldn't be nil"); + astring to_examine((char *)real_curr->held()); +#ifdef DEBUG_TEST_TREE + to_examine += " "; + printf(to_examine.s()); +//remove it again if we reenable the cut. +#endif +// if (to_examine == to_look_for) real_curr->cut(); +} + +#undef UNIT_BASE_THIS_OBJECT +#define UNIT_BASE_THIS_OBJECT (*this) + +////////////// + +larch *test_tree::next(larch *&move, larch *formal(hook), traveller &skip) +{ move = dynamic_cast(skip.next()); return move; } + +void test_tree::apply(larch *apply_to, applier *to_apply, + tree::traversal_directions order) +{ + larch *curr = NIL; + for (traveller skippy = apply_to->start(order); + next(curr, apply_to, skippy); ) to_apply(curr); +} + +int test_tree::execute() +{ + FUNCDEF("execute"); + for (int qq = 0; qq < test_iterations; qq++) { + larch *e1 = new larch("a"); + larch *e2 = new larch("b"); + larch *e3 = new larch("+"); + e3->attach(e1); + e3->attach(e2); + + larch *e4 = new larch("c"); + larch *e5 = new larch("-"); + e5->attach(e3); + e5->attach(e4); + + larch *e6 = new larch(">"); + larch *e7 = new larch("23"); + e6->attach(e5); + e6->attach(e7); + + larch *e8 = new larch("d"); + larch *e9 = new larch("="); + e9->attach(e8); + e9->attach(e6); + +#ifdef DEBUG_TEST_TREE + printf("infix is "); +#endif + apply(e9, print_node, tree::infix); +#ifdef DEBUG_TEST_TREE + printf("\nprefix is "); +#endif + apply(e9, print_node, tree::prefix); +#ifdef DEBUG_TEST_TREE + printf("\npostfix is "); +#endif + apply(e9, print_node, tree::postfix); +#ifdef DEBUG_TEST_TREE + printf("\n"); + printf("branches is "); +#endif + apply(e9, print_node, tree::to_branches); +#ifdef DEBUG_TEST_TREE + printf("\n"); + printf("branches reversed is "); +#endif + apply(e9, print_node, tree::reverse_branches); +#ifdef DEBUG_TEST_TREE + printf("\n"); + printf("before first pack"); +#endif + byte_array packed_e9(0); + int sizzle = e9->packed_size(); + e9->pack(packed_e9); + ASSERT_EQUAL(sizzle, packed_e9.length(), "packed size should agree with results"); +#ifdef DEBUG_TEST_TREE + printf("after first pack, size is %d\n", packed_e9.length()); +#endif + larch *new_e9 = new larch(); + new_e9->unpack(packed_e9); +#ifdef DEBUG_TEST_TREE + printf("New tree after unpacking is (infix order):\n"); +#endif + apply(new_e9, print_node, tree::infix); +#ifdef DEBUG_TEST_TREE + printf("\n"); +#endif +/* +#ifdef DEBUG_TEST_TREE + printf("the following dumps are in the order: infix, prefix, postfix.\n\n"); + printf("now trying cut on the character '>':\n"); +#endif + to_look_for = ">"; + new_e9->apply(&print_node, tree::infix); +#ifdef DEBUG_TEST_TREE + p("\n"); +#endif + new_e9->apply(&print_node, tree::prefix); +#ifdef DEBUG_TEST_TREE + p("\n"); +#endif + new_e9->apply(&print_node, tree::postfix); +#ifdef DEBUG_TEST_TREE + p("\nnow trying cut on the character +:\n"); +#endif + to_look_for = "+"; + new_e9->apply(&print_node, tree::infix); +#ifdef DEBUG_TEST_TREE + p("\n"); +#endif + new_e9->apply(&print_node, tree::prefix); +#ifdef DEBUG_TEST_TREE + p("\n"); +#endif + new_e9->apply(&print_node, tree::postfix); +#ifdef DEBUG_TEST_TREE + p("\n"); +#endif + to_look_for = ""; + +#ifdef DEBUG_TEST_TREE + p("okay, trying to resume at -\n"); +#endif + e5->resume(&print_node, tree::infix); +#ifdef DEBUG_TEST_TREE + p("\n"); +#endif + e5->resume(&print_node, tree::prefix); +#ifdef DEBUG_TEST_TREE + p("\n"); +#endif + e5->resume(&print_node, tree::postfix); +#ifdef DEBUG_TEST_TREE + p("\n"); +#endif +*/ +#ifdef DEBUG_TEST_TREE + printf("deleting\n"); +#endif + delete e9; +/* +printf("second pack\n"); + byte_array second_pack; +printf("packing\n"); + new_e9->pack(second_pack); +#ifdef DEBUG_TEST_TREE + printf("after second pack, size is %d\n", size); +#endif +*/ + delete new_e9; +/* + larch *newest_e9 = new larch(SELF_CLEANING); + newest_e9->unpack(second_pack); +#ifdef DEBUG_TEST_TREE + printf("after second unpack... tree is (infix):\n"); +#endif + newest_e9->apply(print_node, tree::infix); + delete newest_e9; +#ifdef DEBUG_TEST_TREE + p("\n"); +#endif +*/ + } + return final_report(); +} + +HOOPLE_MAIN(test_tree, ) + diff --git a/core/library/tests_structures/bogon.cpp b/core/library/tests_structures/bogon.cpp new file mode 100644 index 00000000..4bd88123 --- /dev/null +++ b/core/library/tests_structures/bogon.cpp @@ -0,0 +1,53 @@ +/*****************************************************************************\ +* * +* Name : bogon * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* A simple test object for amorphs. * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "bogon.h" + +#include + +using namespace basis; +using namespace structures; + +bogon::bogon(abyte *to_copy) : my_held(NIL) +{ + if (to_copy) { + astring t((char *)to_copy); + if (t.length()) { + my_held = new abyte[t.length() + 1]; + t.stuff((char *)my_held, t.length() + 1); + } + } +} + +bogon::bogon(const bogon &to_copy) : my_held(NIL) { operator = (to_copy); } + +bogon &bogon::operator = (const bogon &to_copy) { + if (this == &to_copy) return *this; + astring t((char *)to_copy.my_held); + if (my_held) delete [] my_held; + my_held = new abyte[t.length() + 1]; + t.stuff((char *)my_held, t.length() + 1); + return *this; +} + +bogon::~bogon() { if (my_held) delete [] my_held; } + +abyte *bogon::held() const { return my_held; } + +int bogon::size() const { return my_held? int(strlen((char *)my_held) + 1) : 0; } + diff --git a/core/library/tests_structures/bogon.h b/core/library/tests_structures/bogon.h new file mode 100644 index 00000000..77edfa59 --- /dev/null +++ b/core/library/tests_structures/bogon.h @@ -0,0 +1,49 @@ +#ifndef BOGON_CLASS +#define BOGON_CLASS + +/*****************************************************************************\ +* * +* Name : bogon * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* A simple test object for amorphs. * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#define DEBUG_ARRAY +#define DEBUG_AMORPH + +#include +#include +#include + +class bogon +{ +public: + bogon(basis::abyte *to_copy); + + bogon(const bogon &to_copy); + + bogon &operator = (const bogon &to_copy); + + ~bogon(); + + basis::abyte *held() const; + + int size() const; + +private: + basis::abyte *my_held; +}; + +#endif + diff --git a/core/library/tests_structures/makefile b/core/library/tests_structures/makefile new file mode 100644 index 00000000..eb038004 --- /dev/null +++ b/core/library/tests_structures/makefile @@ -0,0 +1,14 @@ +include cpp/variables.def + +PROJECT = tests_structures +TYPE = test +SOURCE = bogon.cpp +TARGETS = test_amorph.exe test_hash_table.exe test_int_hash.exe test_matrix.exe \ + test_memory_limiter.exe test_packing.exe test_stack.exe test_unique_id.exe \ + test_bit_vector.exe test_set.exe test_string_table.exe test_symbol_table.exe \ + test_version.exe +LOCAL_LIBS_USED = unit_test application loggers configuration textual timely filesystem \ + structures basis +RUN_TARGETS = $(ACTUAL_TARGETS) + +include cpp/rules.def diff --git a/core/library/tests_structures/test_amorph.cpp b/core/library/tests_structures/test_amorph.cpp new file mode 100644 index 00000000..e5dfb1ca --- /dev/null +++ b/core/library/tests_structures/test_amorph.cpp @@ -0,0 +1,536 @@ +/* +* Name : test_byte_array_amorph +* Author : Chris Koeritz +* Purpose: +* Puts the amorph object through its paces. +** +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include "bogon.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace application; +using namespace basis; +using namespace mathematics; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define DEBUG_ARRAY + // uncomment to enable array debugging. + +#define DEBUG_AMORPH + // uncomment to enable amorph debugging. + +//#define DEBUG_TEST_AMORPH + // uncomment for this program to be noisier. + +#ifdef DEBUG_TEST_AMORPH + #define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), to_print) +#else + #define LOG(to_print) {} +#endif + +////////////// + +class t_amorph : virtual public unit_base, virtual public application_shell +{ +public: + t_amorph() : unit_base() {} + DEFINE_CLASS_NAME("t_amorph"); + int test_bogon_amorph(); + int test_byte_array_amorph(); + byte_array fake_pack(amorph &me); + int compare(amorph &one, amorph &two); + amorph *fake_amorph_unpack(byte_array &packed_amorph); + int compare(const amorph &one, const amorph &two); + + struct blob_hold { int size; int offset; }; + + virtual int execute(); +}; + +#define PACK_BLOB_SIZE(max_limbs) (max_limbs * sizeof(blob_hold)) + +HOOPLE_MAIN(t_amorph, ); + +////////////// + +const int default_test_iterations = 2; + +const int MAX_LIMBS = 200; + // the highest number of items stored in the amorphs here. + +const int MIN_CHUBBY = 60; + // the smallest chunk to allocate for storing text strings... all strings + // must therefore be shorter than this length. +const int MAX_RANDO = 275; + // the maximum amount of space to add when allocating a randomly sized chunk. + +#define PROGRAM_NAME astring("test_amorph") + +int t_amorph::compare(amorph &one, amorph &two) +{ + FUNCDEF("compare amorph"); + ASSERT_EQUAL(one.elements(), two.elements(), "elements comparison"); + if (one.elements() != two.elements()) return false; + ASSERT_EQUAL(one.valid_fields(), two.valid_fields(), "valid fields comparison"); + if (one.valid_fields() != two.valid_fields()) return false; + for (int i = 0; i < one.elements(); i++) { + if (!one.get(i) && !two.get(i)) continue; + ASSERT_FALSE(!one.get(i) || !two.get(i), "inequal emptiness"); + ASSERT_EQUAL(one.get(i)->length(), two.get(i)->length(), "inequal sizes"); + if (one.get(i)->length() > 0) { + ASSERT_INEQUAL(one[i]->observe(), two[i]->observe(), "pointer in use twice"); + ASSERT_FALSE(memcmp(one[i]->observe(), two[i]->observe(), one[i]->length()), + "inequal contents"); + } + } + return true; +} + +byte_array t_amorph::fake_pack(amorph &me) +{ + FUNCDEF("fake_pack"); + // snagged from the packable_amorph pack function! + // count the whole size needed to store the amorph. + int amo_size = 0; + amorph hold_packed_bits(me.elements()); + + for (int i = 0; i < me.elements(); i++) + if (me.get(i) && me.get(i)->length()) { + byte_array packed_item; + attach(packed_item, *me[i]); + byte_array *to_stuff = new byte_array(packed_item); + hold_packed_bits.put(i, to_stuff); + amo_size += packed_item.length(); + } + int len = amo_size + sizeof(int) + PACK_BLOB_SIZE(me.elements()); + + // allocate a storage area for the packed form. + byte_array to_return(len); + int temp = me.elements(); + memcpy((int *)to_return.access(), &temp, sizeof(int)); + // size of package is stored at the beginning of the memory. + + int current_offset = sizeof(int); + // the indices into the packed form are located after the amorph header. + blob_hold *blob_array = (blob_hold *)(to_return.access() + current_offset); + current_offset += PACK_BLOB_SIZE(me.elements()); + + // the entire amorph is replicated into the new buffer. + for (int j = 0; j < me.elements(); j++) { + // the offset of this limb in the packed area is saved in the hold. + blob_array[j].size + = (hold_packed_bits[j]? hold_packed_bits[j]->length() : 0); + blob_array[j].offset = current_offset; + if (hold_packed_bits[j] && hold_packed_bits[j]->length()) { + // the actual data is copied.... + memcpy(to_return.access() + current_offset, + (abyte *)hold_packed_bits[j]->observe(), + hold_packed_bits[j]->length()); + // and the "address" is updated. + current_offset += hold_packed_bits[j]->length(); + } + } + ASSERT_EQUAL(current_offset, len, "offset is incorrect after packing"); + return to_return; +} + +amorph *t_amorph::fake_amorph_unpack(byte_array &packed_amorph) +{ + // snagged from the packable_amorph unpack function! + int max_limbs; + memcpy(&max_limbs, (int *)packed_amorph.access(), sizeof(max_limbs)); + amorph *to_return = new amorph(max_limbs); + + blob_hold *blob_array = new blob_hold[max_limbs]; + memcpy(blob_array, (blob_hold *)(packed_amorph.access() + + sizeof(int)), PACK_BLOB_SIZE(max_limbs)); + for (int i = 0; i < to_return->elements(); i++) + if (blob_array[i].size) { + abyte *source = packed_amorph.access() + blob_array[i].offset; + byte_array packed_byte_array(blob_array[i].size, source); + byte_array *unpacked = new byte_array; + detach(packed_byte_array, *unpacked); + to_return->put(i, unpacked); + } + delete [] blob_array; + return to_return; +} + +int t_amorph::test_byte_array_amorph() +{ + FUNCDEF("test_byte_array_amorph"); + LOG("start of amorph of abyte array test"); + for (int qq = 0; qq < default_test_iterations; qq++) { + LOG(astring(astring::SPRINTF, "index %d", qq)); + { + // some simple creation and stuffing tests.... + amorph fred(20); + amorph gen(10); + for (int i=0; i < 10; i++) { + byte_array *gens = new byte_array(8, (abyte *)"goodbye"); + gen.put(i, gens); + } + for (int j = 0; j < 20; j++) { + byte_array *freds = new byte_array(6, (abyte *)"hello"); + fred.put(j, freds); + } + amorph_assign(gen, fred); + LOG("done with fred & gen"); + } + + LOG("before fred creation"); + chaos randomizer; + amorph fred(MAX_LIMBS - 1); + fred.append(NIL); // add one to make it max limbs big. + LOG("after append nil"); + { + for (int i = 0; i < fred.elements(); i++) { + int size = MIN_CHUBBY + randomizer.inclusive(0, MAX_RANDO); + astring text("bogus burfonium nuggets"); + astring burph(astring::SPRINTF, " ung %d ", i); + text += burph; + abyte *temp = new abyte[size]; + text.stuff((char *)temp, text.length()+1); + byte_array *to_stuff = new byte_array(size, temp); + fred.put(i, to_stuff); + delete [] temp; + } + } + LOG("after first loop"); + { + amorph bungee3; + amorph_assign(bungee3, fred); + amorph burglar2; + amorph_assign(burglar2, bungee3); + amorph trunklid; + amorph_assign(trunklid, burglar2); + ASSERT_INEQUAL(trunklid.elements(), 0, "const constructor test - no elements!"); + } + LOG("after copies performed"); + { + astring text; + text = "hello this is part one."; + LOG(astring(astring::SPRINTF, "len is %d, content is %s", + text.length(), text.observe())); + char *tadr = text.access(); + abyte *badr = (abyte *)tadr; + byte_array *to_stuff = new byte_array(text.length() + 1, badr); + fred.put(183, to_stuff); + text = "wonky tuniea bellowbop"; + byte_array *to_stuff1 = new byte_array(text.length()+1, (abyte *)text.s()); + fred.put(90, to_stuff1); + + text = "frunkwioioio"; + byte_array *to_stuff2 = new byte_array(text.length()+1, (abyte *)text.s()); + fred.put(12, to_stuff2); + + fred.clear(98); fred.clear(122); fred.clear(123); + fred.clear(256); + fred.clear(129); + fred.zap(82, 90); + fred.zap(93, 107); + } + LOG("after second loop"); + { + byte_array packed = fake_pack(fred); + LOG(astring(astring::SPRINTF, "done packing in %s, pack has %d " + "elems.", class_name(), packed.length())); + amorph *new_fred = fake_amorph_unpack(packed); + LOG("done unpacking in test_amorph"); + ASSERT_TRUE(compare(fred, *new_fred), "first pack test, amorphs not the same"); + abyte *cont1 + = (new_fred->get(14)? (*new_fred)[14]->access() : (abyte *)"NIL"); + abyte *cont2 + = (new_fred->get(20)? (*new_fred)[20]->access() : (abyte *)"NIL"); + abyte *cont3 + = (new_fred->get(36)? (*new_fred)[36]->access() : (abyte *)"NIL"); + + if (cont1) LOG(astring(astring::SPRINTF, "14: %s", cont1)); + if (cont2) LOG(astring(astring::SPRINTF, "20: %s", cont2)); + if (cont3) LOG(astring(astring::SPRINTF, "36: %s", cont3)); + LOG("fields all compare identically after pack and unpack"); + byte_array packed_second = fake_pack(*new_fred); + delete new_fred; + amorph *newer_fred = fake_amorph_unpack(packed_second); + ASSERT_TRUE(compare(*newer_fred, fred), "second pack test, amorphs not the same"); + delete newer_fred; + } + + { + amorph fred(randomizer.inclusive(20, 30)); + int size = MIN_CHUBBY + randomizer.inclusive(0, MAX_RANDO); + astring text("bogus burfonium nuggets"); + astring burph(astring::SPRINTF, " ung %d ", 2314); + text += burph; + byte_array intermed(size); + + for (int i = 0; i < fred.elements(); i += 5) { + byte_array *to_stuff = new byte_array(size, intermed.access()); + memcpy(intermed.access(), (abyte *)text.s(), text.length() + 1); + fred.put(i, to_stuff); + } + fred.clear_all(); + for (int j = 0; j < fred.elements(); j += 5) { + byte_array *to_stuff = new byte_array(size, intermed.access()); + memcpy(intermed.access(), (abyte *)text.s(), text.length() + 1); + fred.put(j, to_stuff); + } + text = "frunkwioioio"; + byte_array *to_stuff = new byte_array(text.length()+1, (abyte *)text.s()); + fred.put(12, to_stuff); + fred.clear_all(); + } + LOG("survived the clear_alls"); + { + amorph *ted = new amorph(0); + amorph_assign(*ted, fred); + ASSERT_TRUE(compare(*ted, fred), "ted and fred aren't the same"); + { + amorph *george = new amorph(0); + amorph_assign(*george, fred); + ASSERT_TRUE(compare(*george, fred), "fred and george aren't the same"); + ted->zap(3, 20); + george->zap(3, 10); + george->zap(3, 12); + ASSERT_TRUE(compare(*ted, *george), "after zap, ted and george aren't the same"); + ted->adjust(ted->elements() - 20); + george->adjust(george->elements() - 5); + george->adjust(george->elements() - 5); + george->adjust(george->elements() - 5); + george->adjust(george->elements() - 5); + ASSERT_TRUE(compare(*ted, *george), "after adjust, ted and george aren't the same"); + delete george; + } + delete ted; + } + } + return 0; +} + +int t_amorph::compare(const amorph &one, const amorph &two) +{ + FUNCDEF("compare amorph"); + if (one.elements() != two.elements()) return false; + for (int i = 0; i < one.elements(); i++) { + if (!one.get(i) && !two.get(i)) continue; + ASSERT_FALSE(!one.get(i) || !two.get(i), "both should be non-nil"); + ASSERT_EQUAL(one.get(i)->size(), two.get(i)->size(), "sizes should be equal"); + if (one.get(i)->size() > 0) { + ASSERT_INEQUAL(one.get(i)->held(), two.get(i)->held(), "pointer should not be in use twice"); + ASSERT_FALSE(memcmp(one.get(i)->held(), two.get(i)->held(), one.get(i)->size()), + "contents should be equal"); + } + } + return true; +} + +int t_amorph::test_bogon_amorph() +{ + FUNCDEF("test_bogon_amorph"); + LOG("start of amorph of bogon test"); + for (int qq = 0; qq < default_test_iterations; qq++) { + LOG(astring(astring::SPRINTF, "index %d", qq)); + { + // some simple creation and stuffing tests.... + amorph fred(20); + amorph gen(10); + for (int i = 0; i < 10; i++) { + bogon *gens = new bogon((abyte *)"goodbye"); + gen.put(i, gens); + } + for (int j = 0; j < 20; j++) { + bogon *freds = new bogon((abyte *)"hello"); + fred.put(j, freds); + } + ASSERT_FALSE(compare(fred, gen), "fred and gen ARE the same"); + amorph_assign(gen, fred); + ASSERT_TRUE(compare(fred, gen), "fred and gen aren't the same"); + } + + chaos randomizer; + + amorph fred(MAX_LIMBS); + + LOG("after append nil"); + { + for (int i = 0; i < fred.elements(); i++) { + int size = MIN_CHUBBY + randomizer.inclusive(0, MAX_RANDO); + astring text("bogus burfonium nuggets"); + astring burph(astring::SPRINTF, " ung %d ", i); + text += burph; + abyte *temp = new abyte[size]; + text.stuff((char *)temp, text.length()+1); + bogon *to_stuff = new bogon(temp); + fred.put(i, to_stuff); + delete [] temp; + } + } + + LOG("after first loop"); + { + amorph bungee3; + amorph_assign(bungee3, fred); + amorph burglar2; + amorph_assign(burglar2, bungee3); + amorph_assign(burglar2, bungee3); + amorph trunklid; + amorph_assign(trunklid, burglar2); + ASSERT_TRUE(trunklid.elements(), "const constructor test: no elements!"); + } + { + astring text; + text = "hello this is part one."; + bogon *to_stuff = new bogon((abyte *)text.s()); + fred.put(32, to_stuff); + + text = "wonky tuniea bellowbop"; + bogon *to_stuff1 = new bogon((abyte *)text.s()); + fred.put(84, to_stuff1); + + text = "frunkwioioio"; + bogon *to_stuff2 = new bogon((abyte *)text.s()); + fred.put(27, to_stuff2); + + fred.clear(98); fred.clear(122); fred.clear(123); + fred.clear(256); + fred.clear(129); + fred.zap(82, 90); + fred.zap(93, 107); + } + LOG("after second loop"); + { + amorph fred(randomizer.inclusive(20, 30)); + astring text("bogus burfonium nuggets"); + astring burph(astring::SPRINTF, " ung %d ", 2314); + text += burph; + + for (int i = 0; i < fred.elements(); i += 5) { + bogon *to_stuff = new bogon((abyte *)text.s()); + fred.put(i, to_stuff); + } + fred.clear_all(); + for (int j = 0; j < fred.elements(); j += 5) { + bogon *to_stuff = new bogon((abyte *)text.s()); + fred.put(j, to_stuff); + } + text = "frunkwioioio"; + bogon *to_stuff = new bogon((abyte *)text.s()); + fred.put(6, to_stuff); + fred.clear_all(); + } + LOG("survived the clear_alls"); + { + amorph *ted = new amorph(); + amorph_assign(*ted, fred); + ASSERT_TRUE(compare(*ted, fred), "after assign, ted and fred aren't the same"); + { + amorph *george = new amorph(); + amorph_assign(*george, fred); + ASSERT_TRUE(compare(*george, fred), "pre-zap, george and fred aren't the same"); + ted->zap(3, 20); + george->zap(3, 10); + george->zap(3, 12); + ASSERT_TRUE(compare(*ted, *george), "after zap, ted and george aren't the same"); + ted->adjust(ted->elements()-20); + george->adjust(george->elements()-5); + george->adjust(george->elements()-5); + george->adjust(george->elements()-5); + george->adjust(george->elements()-5); + ASSERT_TRUE(compare(*ted, *george), "after more zaps, ted and george aren't the same"); + delete george; + } + delete ted; + } + } + return 0; +} + +const int MAX_TEST_DURATION = 1 * MINUTE_ms; + // each of the tests calling on the templated tester will take this long. + +const int MAX_SIMULTANEOUS_OBJECTS = 42; // the maximum length tested. + +//hmmm: this test_amorph_of is not completed. + +template +int test_amorph_of(const contents &bogus) +{ + chaos rando; + + // these are the actions we try on the amorph during the test. + // the first and last elements must be identical to the first and last + // tests to perform. + enum actions { first, do_zap = first, do_adjust, do_assign, + + + do_borrow, last = do_borrow}; + + time_stamp exit_time(::MAX_TEST_DURATION); + while (time_stamp() < exit_time) { + int index = rando.inclusive(0, ::MAX_SIMULTANEOUS_OBJECTS - 1); + int choice = rando.inclusive(first, last); + switch (choice) { + case do_zap: { + + break; + } + case do_adjust: { + + break; + } + case do_assign: { + + break; + } + case do_borrow: { + + break; + } + } + } +} + +int t_amorph::execute() +{ + SETUP_COMBO_LOGGER; + int errs = 0; + int retval = test_byte_array_amorph(); + if (retval != 0) errs += retval; + retval = test_bogon_amorph(); + if (retval != 0) errs += retval; + +//incorporate these errors somehow also. + +// if (retval == 0) +// critical_events::alert_message("amorph:: works for those functions tested."); +// else +// critical_events::alert_message("amorph:: there were errors!"); + return final_report(); +} + diff --git a/core/library/tests_structures/test_bit_vector.cpp b/core/library/tests_structures/test_bit_vector.cpp new file mode 100644 index 00000000..068f8a81 --- /dev/null +++ b/core/library/tests_structures/test_bit_vector.cpp @@ -0,0 +1,163 @@ +/*****************************************************************************\ +* * +* Name : test_bit_vector * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), to_print) + +#define MAX_TEST 100 +#define FOOP_MAX 213 + +////////////// + +class test_bit_vector : virtual public unit_base, virtual public application_shell +{ +public: + test_bit_vector() : unit_base() {} + DEFINE_CLASS_NAME("test_bit_vector"); + virtual int execute(); +}; + +HOOPLE_MAIN(test_bit_vector, ); + +////////////// + +struct test_struct { basis::un_int store; int posn; int size; }; + +int test_bit_vector::execute() +{ + FUNCDEF("execute"); + SETUP_COMBO_LOGGER; + + const array unused; + + chaos randomizer; + bit_vector foop(FOOP_MAX); + + for (int i = 0; i < MAX_TEST; i++) { + // sets a random bit and finds that one. + int rando = randomizer.inclusive(0, FOOP_MAX-1); + foop.light(rando); + int found = foop.find_first(true); + ASSERT_EQUAL(found, rando, "find first locates first true"); + foop.clear(rando); + + foop.resize(FOOP_MAX); + ASSERT_EQUAL(0, foop.find_first(0), "locating location of first zero"); + ASSERT_EQUAL(common::NOT_FOUND, foop.find_first(1), "showing there are no one bits"); + for (int i = 0; i < 12; i++) foop.light(i); + ASSERT_EQUAL(12, foop.find_first(0), "finding first on partially set vector"); + + foop.light(FOOP_MAX); // shouldn't work, but shouldn't die. + ASSERT_FALSE(foop.on(FOOP_MAX), "bit_on should not be lit past end of vector"); + + // sets a bunch of random bits. + for (int j = 0; j < 40; j++) { + int rando = randomizer.inclusive(0, FOOP_MAX-1); + foop.light(rando); + } + bit_vector foop2(FOOP_MAX, ((const byte_array &)foop).observe()); + ASSERT_EQUAL(foop, foop2, "after lighting, vectors should be identical"); + + { + // this block tests the subvector and int storage/retrieval routines. + if (foop.bits() < 90) foop.resize(90); // make sure we have room to play. + + array tests; + test_struct t1 = { 27, 15, 5 }; + tests += t1; + test_struct t2 = { 8, 25, 4 }; + tests += t2; + test_struct t3 = { 1485, 34, 16 }; + tests += t3; + test_struct t4 = { 872465, 50, 32 }; + tests += t4; + + for (int i = 0; i < tests.length(); i++) { + ASSERT_TRUE(foop.set(tests[i].posn, tests[i].size, tests[i].store), + "storing int in vector should work"); + +//hmmm: make this a test case! +// bit_vector found = foop.subvector(tests[i].posn, tests[i].posn+tests[i].size-1); +// LOG(astring(astring::SPRINTF, "contents found:\n%s", found.text_form().s())); + + basis::un_int to_check = foop.get(tests[i].posn, tests[i].size); + if (to_check != tests[i].store) + LOG(a_sprintf("int found at %d in vector (%u) is different than what was stored (%u).", + i, to_check, tests[i].store)); + ASSERT_EQUAL((int)to_check, (int)tests[i].store, "should see expected int stored in vector"); + } + } + + { + // tests random resizings and resettings. + int number_of_loops = randomizer.inclusive(50, 150); + for (int i = 0; i < number_of_loops; i++) { + int which_to_do = randomizer.inclusive(1, 3); + switch (which_to_do) { + case 1: { + // resize. + int new_size = randomizer.inclusive(0, 32000); + foop.resize(new_size); + break; + } + case 2: { + // reset. + int new_size = randomizer.inclusive(0, 32000); + foop.reset(new_size); + break; + } + case 3: { + // random sets. + int sets_to_do = randomizer.inclusive(40, 280); + for (int i = 0; i < sets_to_do; i++) { + int rando = randomizer.inclusive(0, foop.bits()); + if (randomizer.inclusive(0, 1)) foop.light(rando); + else foop.clear(rando); + } + break; + } + } + } + } + + foop.reset(FOOP_MAX); // to clear before next loop. + } + + return final_report(); +} + diff --git a/core/library/tests_structures/test_hash_table.cpp b/core/library/tests_structures/test_hash_table.cpp new file mode 100644 index 00000000..0e2e214c --- /dev/null +++ b/core/library/tests_structures/test_hash_table.cpp @@ -0,0 +1,540 @@ +/* +* Name : test_hash_table +* Author : Chris Koeritz +* Purpose: +* Tests out the hash_table abstract data type. +** +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +///using namespace configuration; +using namespace mathematics; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +//#define DEBUG_HASH_TABLE + // uncomment for noisier run. + +const double TEST_DURATION = 0.014 * MINUTE_ms; +//const double TEST_DURATION = 20 * SECOND_ms; + +const int MAX_ELEMENTS = 8; + // we start low since we will rehash occasionally. + +////////////// + +enum test_actions { + FIRST_TEST = 38, // place-holder. + ADD = FIRST_TEST, + // adds an item that is probably new. + ADD_ADD, + // adds an item that is probably new, followed by another item under the + // same key id. this ensures the overwriting gets tested. + ZAP, + // finds an item we know is in the list and whacks it. + ADD_ZAP, + // adds a new item and immediately finds and zaps it. + ZAP_ADD, + // zaps an item that we know about and then adds a new item with the same + // identifier. + FIND, + // locates an item in the list which we know should exist. + ACQUIRE, + // grabs an item out of the list (and tosses it). + FIND_ZAP_ADD, + // finds an item we know should exist, zaps it out of the list, then adds + // a new item with the same id. + ACQUIRE_ADD_ZAP, + // removes an item from the list that we know should be there, adds it back + // in, and then whacks it. + FIND_ADD_FIND, + // find an item with a particular id (one that we know should be in the + // list) and then adds a different item using the same id. the new item + // is then sought. + RESET, + // tosses all data out of the hash table. not done very often. + CHECK_SANITY, + // look for any problems or irregularities; print the contents of the list + // if any are found. + REHASH, + // resizes the hash table. + COPY, + // copies a hash table to another hash table. + LAST_TEST = COPY // place-holder; must equal test just prior. +}; + +////////////// + +// a simple object that is used as the contents of the hash_table. + +class data_shuttle +{ +public: + int food_bar; + astring snacky_string; + bool hungry; + byte_array chunk; + chaos chao; + + data_shuttle() + : snacky_string(string_manipulation::make_random_name()), + chunk(chao.inclusive(100, 10000)) {} +}; + +////////////// + +class test_hash_table : virtual public unit_base, virtual public application_shell +{ +public: + test_hash_table(); + + DEFINE_CLASS_NAME("test_hash_table"); + + int raw_random_id(); //!< returns an unvetted random number. + int unused_random_id(); //!< returns an unused (so far) random number. + + int execute(); + // the main startup for the test. + + bool perform_a_test(test_actions test_type); + // carries out the specifics of the "test_type". + + bool pick_a_test(); + // randomly picks one of the test types and performs it. + + static const char *test_name(test_actions test_type); + + // these functions each perform one type of test, which their names indicate. + bool test_add(); + bool test_add_add(); + bool test_zap(); + bool test_add_zap(); + bool test_zap_add(); + bool test_find(); + bool test_acquire(); + bool test_find_zap_add(); + bool test_acquire_add_zap(); + bool test_find_add_find(); + bool test_reset(bool always_run = false); + bool test_check_sanity(); + bool test_copy(); + bool test_rehash(); + + static bool equivalence_applier(const int &key, data_shuttle &item, void *dlink); + +private: + int_set _keys_in_use; // keys that we think are stored in the table. + hash_table _the_table; // our table under test. + int _hits[LAST_TEST - FIRST_TEST + 1]; // tracks our testing activities. + int _tested; // simple counter of number of test calls. +}; + +////////////// + +typedef hash_table our_hash; // cleans up somewhat. + +////////////// + +test_hash_table::test_hash_table() +: application_shell(), + _the_table(rotating_byte_hasher(), MAX_ELEMENTS), + _tested(0) +{ + for (int i = FIRST_TEST; i <= LAST_TEST; i++) + _hits[i - FIRST_TEST] = 0; +} + +int test_hash_table::raw_random_id() +{ + return randomizer().inclusive(-MAXINT32 / 4, MAXINT32 / 4); +} + +int test_hash_table::unused_random_id() +{ + while (true) { + int checking = raw_random_id(); + if (!_keys_in_use.member(checking)) return checking; // got one. + } // keep going until we find unused id. +} + +int test_hash_table::execute() +{ + time_stamp exit_time((int)TEST_DURATION); + while (time_stamp() < exit_time) { + pick_a_test(); + } + test_reset(true); // force it to run at least once. + +#ifdef DEBUG_HASH_TABLE + log(a_sprintf("did %d tests.\n", _tested)); + log(astring("Test Activity:")); + for (int i = 0; i < LAST_TEST - FIRST_TEST + 1; i++) + log(astring(astring::SPRINTF, "%d (%s): %d hits", i + FIRST_TEST, + test_name(test_actions(i + FIRST_TEST)), _hits[i])); + log(a_sprintf("note that test %d will seldom be executed.", RESET)); +#endif + return final_report(); +} + +const char *test_hash_table::test_name(test_actions test_type) +{ + switch (test_type) { + case ADD: return "ADD"; + case ADD_ADD: return "ADD_ADD"; + case ZAP: return "ZAP"; + case ADD_ZAP: return "ADD_ZAP"; + case ZAP_ADD: return "ZAP_ADD"; + case FIND: return "FIND"; + case ACQUIRE: return "ACQUIRE"; + case FIND_ZAP_ADD: return "FIND_ZAP_ADD"; + case ACQUIRE_ADD_ZAP: return "ACQUIRE_ADD_ZAP"; + case FIND_ADD_FIND: return "FIND_ADD_FIND"; + case RESET: return "RESET"; + case COPY: return "COPY"; + case REHASH: return "REHASH"; + case CHECK_SANITY: return "CHECK_SANITY"; + default: return "UnknownTest"; + } +} + +bool test_hash_table::perform_a_test(test_actions test_type) +{ + FUNCDEF("perform_a_test"); + +// log(astring(test_name(test_type)) + " "); + + switch (test_type) { + case ADD: return test_add(); + case ADD_ADD: return test_add_add(); + case ZAP: return test_zap(); + case ADD_ZAP: return test_add_zap(); + case ZAP_ADD: return test_zap_add(); + case FIND: return test_find(); + case ACQUIRE: return test_acquire(); + case FIND_ZAP_ADD: return test_find_zap_add(); + case ACQUIRE_ADD_ZAP: return test_acquire_add_zap(); + case FIND_ADD_FIND: return test_find_add_find(); + case RESET: return test_reset(); + case COPY: return test_copy(); + case REHASH: return test_rehash(); + case CHECK_SANITY: return test_check_sanity(); + default: + ASSERT_TRUE(false, "should not see any missing cases"); + return false; // never gets here. + } +} + +bool test_hash_table::pick_a_test() +{ + _tested++; + return perform_a_test(test_actions(randomizer().inclusive(FIRST_TEST, + LAST_TEST))); +} + +bool test_hash_table::test_add() +{ + FUNCDEF("test_add"); + _hits[ADD - FIRST_TEST]++; + int random_id = raw_random_id(); + data_shuttle *to_add = new data_shuttle; + to_add->snacky_string = string_manipulation::make_random_name(); + to_add->food_bar = random_id; + outcome expected = common::IS_NEW; + if (_keys_in_use.member(random_id)) common::EXISTING; + ASSERT_EQUAL(_the_table.add(random_id, to_add).value(), expected.value(), + "add should give proper outcome based on expectation"); + if (_keys_in_use.member(random_id)) + return true; // already was there so we replaced. + _keys_in_use.add(random_id); + return true; +} + +////////////// + +hash_table *_hang_on = NIL; + // must be set before calling the apply method. + +#undef UNIT_BASE_THIS_OBJECT +#define UNIT_BASE_THIS_OBJECT (*dynamic_cast(application_shell::single_instance())) + +bool test_hash_table::equivalence_applier(const int &key, data_shuttle &item, void *dlink) +{ + FUNCDEF("equivalence_applier"); + ASSERT_NON_NULL(dlink, "should have been given name"); + if (!dlink) return false; // fail. + astring test_name = (char *)dlink; + +//application_shell::single_instance()->log(astring("after name check")); + + data_shuttle *found = _hang_on->find(key); + ASSERT_NON_NULL(found, test_name + ": should find equivalent entry in second list"); + if (!found) return false; // bail or we'll crash. + +//application_shell::single_instance()->log(astring("after finding")); + + ASSERT_EQUAL(item.food_bar, found->food_bar, test_name + ": food_bar should not differ"); + ASSERT_EQUAL(item.snacky_string, found->snacky_string, test_name + ": snacky_string should not differ"); + ASSERT_EQUAL(item.hungry, found->hungry, test_name + ": hungry should not differ"); + return true; +} + +#undef UNIT_BASE_THIS_OBJECT +#define UNIT_BASE_THIS_OBJECT (*this) + +////////////// + +bool test_hash_table::test_rehash() +{ + FUNCDEF("test_rehash"); + _hang_on = &_the_table; // must happen first. + + // we don't want to rehash too often; it is expensive. + int maybe = randomizer().inclusive(1, 50); + if (maybe < 32) return true; // not this time. + + _hits[REHASH - FIRST_TEST]++; + + hash_table table_copy(rotating_byte_hasher(), + _the_table.estimated_elements()); + +//log("copying table..."); + copy_hash_table(table_copy, _the_table); + // make a copy of the table. + +//log("rehashing table..."); + _the_table.rehash(randomizer().inclusive(1, 20)); +//hmmm: need test of non-existent dehash function that reduces max_bits. + +//log("comparing table..."); + table_copy.apply(equivalence_applier, (void*)func); +//log("done copy and compare."); + + return true; +} + +bool test_hash_table::test_copy() +{ + FUNCDEF("test_copy"); + _hang_on = &_the_table; // must happen first. + + // we don't want to copy too often. it's a heavy operation. + int maybe = randomizer().inclusive(1, 50); + if (maybe > 16) return true; // not this time. + + _hits[COPY - FIRST_TEST]++; + + hash_table table_copy(rotating_byte_hasher(), MAX_ELEMENTS); + +//log("copying table..."); + copy_hash_table(table_copy, _the_table); + // make a copy of the table. + +//log("comparing table..."); + table_copy.apply(equivalence_applier, (void*)func); +//log("done copy and compare."); + + return true; +} + +////////////// + +bool test_hash_table::test_add_add() +{ + FUNCDEF("test_add_add"); + _hits[ADD_ADD - FIRST_TEST]++; + int random_id = unused_random_id(); + data_shuttle *to_add = new data_shuttle; + to_add->snacky_string = string_manipulation::make_random_name(); + to_add->food_bar = random_id; + ASSERT_EQUAL(_the_table.add(random_id, to_add).value(), common::IS_NEW, + "new addition should be seen as such"); + // add the new key if it's really new. + _keys_in_use.add(random_id); + + // second add on same id. + data_shuttle *next_add = new data_shuttle; + next_add->snacky_string = string_manipulation::make_random_name(); + next_add->food_bar = random_id; + ASSERT_EQUAL(_the_table.add(random_id, next_add).value(), our_hash::EXISTING, + "second add should not say first failed"); + + return true; +} + +bool test_hash_table::test_zap() +{ + FUNCDEF("test_zap"); + int maybe = randomizer().inclusive(1, 1000); + if (maybe > 50) return true; + if (!_keys_in_use.elements()) return false; // can't do it yet. + _hits[ZAP - FIRST_TEST]++; + int rand_indy = randomizer().inclusive(0, _keys_in_use.elements() - 1); + int dead_key = _keys_in_use[rand_indy]; + _keys_in_use.remove(dead_key); // remove the record of that key. + ASSERT_TRUE(_the_table.zap(dead_key), "key should be present in table"); + return true; +} + +bool test_hash_table::test_add_zap() +{ + FUNCDEF("test_add_zap"); + // add. + _hits[ADD_ZAP - FIRST_TEST]++; + int random_id = unused_random_id(); + data_shuttle *to_add = new data_shuttle; + to_add->snacky_string = string_manipulation::make_random_name(); + to_add->food_bar = random_id; + ASSERT_EQUAL(_the_table.add(random_id, to_add).value(), common::IS_NEW, + "putting new item in should be seen as new"); + // zap. + ASSERT_TRUE(_the_table.zap(random_id), "key should be present after add"); + return true; +} + +bool test_hash_table::test_zap_add() +{ + FUNCDEF("test_zap_add"); + if (!_keys_in_use.elements()) return false; // can't do it yet. + _hits[ZAP_ADD - FIRST_TEST]++; + int rand_indy = randomizer().inclusive(0, _keys_in_use.elements() - 1); + // in the end, the key list state won't be changed unless the test fails. + int dead_key = _keys_in_use[rand_indy]; + ASSERT_TRUE(_the_table.zap(dead_key), "key should be there when we look"); + + data_shuttle *to_add = new data_shuttle; + to_add->snacky_string = string_manipulation::make_random_name(); + to_add->food_bar = dead_key; + outcome ret = _the_table.add(dead_key, to_add); + ASSERT_EQUAL(ret.value(), our_hash::IS_NEW, "key should not be present already"); + return true; +} + +bool test_hash_table::test_find() +{ + FUNCDEF("test_find"); + if (!_keys_in_use.elements()) return false; // can't do it yet. + _hits[FIND - FIRST_TEST]++; + int rand_indy = randomizer().inclusive(0, _keys_in_use.elements() - 1); + int find_key = _keys_in_use[rand_indy]; + data_shuttle *found = NIL; + ASSERT_TRUE(_the_table.find(find_key, found), "key should be there as expected"); + ASSERT_NON_NULL(found, "contents should not be NIL"); + ASSERT_EQUAL(found->food_bar, find_key, "stored key should be same as real key"); + ASSERT_TRUE(found->snacky_string.length(), "stored string should have length"); + return true; +} + +bool test_hash_table::test_acquire() +{ + FUNCDEF("test_acquire"); + int maybe = randomizer().inclusive(1, 1000); + if (maybe > 150) return true; + if (!_keys_in_use.elements()) return false; // can't do it yet. + _hits[ACQUIRE - FIRST_TEST]++; + int rand_indy = randomizer().inclusive(0, _keys_in_use.elements() - 1); + int find_key = _keys_in_use[rand_indy]; + _keys_in_use.remove(find_key); // remove the record of that key. + data_shuttle *found = _the_table.acquire(find_key); + ASSERT_NON_NULL(found, "key should be present when expected"); + ASSERT_EQUAL(found->food_bar, find_key, "stored key should be same as real key"); + ASSERT_TRUE(found->snacky_string.length(), "stored string should not have zero length"); + WHACK(found); + found = _the_table.acquire(find_key); + ASSERT_NULL(found, "key should not be there after zap"); + return true; +} + +bool test_hash_table::test_find_zap_add() +{ + FUNCDEF("test_find_zap_add"); + // find. + if (!_keys_in_use.elements()) return false; // can't do it yet. + _hits[FIND_ZAP_ADD - FIRST_TEST]++; + int rand_indy = randomizer().inclusive(0, _keys_in_use.elements() - 1); + // this is another key list invariant function, if it works. + int find_key = _keys_in_use[rand_indy]; + data_shuttle *found = NIL; + ASSERT_TRUE(_the_table.find(find_key, found), "key should be locateable"); + ASSERT_NON_NULL(found, "key should not have NIL contents"); + ASSERT_EQUAL(found->food_bar, find_key, "stored key should be equal to real key"); + ASSERT_TRUE(found->snacky_string.length(), "stored string should not have zero length"); + // zap. + ASSERT_TRUE(_the_table.zap(find_key), "should be able to zap the item we had found"); + // add. + data_shuttle *to_add = new data_shuttle; + to_add->snacky_string = string_manipulation::make_random_name(); + to_add->food_bar = find_key; + ASSERT_EQUAL(_the_table.add(find_key, to_add).value(), our_hash::IS_NEW, + "the item we zapped should be gone"); + return true; +} + +bool test_hash_table::test_reset(bool always_run) +{ + FUNCDEF("test_reset"); + if (!always_run) { + int maybe = randomizer().inclusive(1, 1000); + // this is hardly ever hit, but it loses all contents too often otherwise. + if ( (maybe > 372) || (maybe < 368) ) return true; + } + + // we hit the big time; we will reset now. + _hits[RESET - FIRST_TEST]++; + _the_table.reset(); + for (int i = _keys_in_use.elements() - 1; i >= 0; i--) { + int dead_key = _keys_in_use[i]; + ASSERT_FALSE(_the_table.acquire(dead_key), "after reset, we should not find item"); + _keys_in_use.remove(dead_key); + } + return true; +} + +//hmmm: implement these tests! + +bool test_hash_table::test_acquire_add_zap() +{ + _hits[ACQUIRE_ADD_ZAP - FIRST_TEST]++; +return false; +} + +bool test_hash_table::test_find_add_find() +{ + _hits[FIND_ADD_FIND - FIRST_TEST]++; +return false; +} + +bool test_hash_table::test_check_sanity() +{ + _hits[CHECK_SANITY - FIRST_TEST]++; +return false; +} + +////////////// + +HOOPLE_MAIN(test_hash_table, ) + diff --git a/core/library/tests_structures/test_int_hash.cpp b/core/library/tests_structures/test_int_hash.cpp new file mode 100644 index 00000000..1c888daa --- /dev/null +++ b/core/library/tests_structures/test_int_hash.cpp @@ -0,0 +1,553 @@ +/* +* Name : test_int_hash +* Author : Chris Koeritz +* Purpose: +* Tests out hash_table specialization for integers. +** +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace mathematics; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +//#define DEBUG_INT_HASH + // uncomment for noisier run. + +#define EXTREME_CHECKING + // causes extra checks in the library code. + +const double TEST_DURATION = 0.5 * SECOND_ms; + +const int MAX_DEFAULT_BITS = 2; + // we start low since we will rehash occasionally. + +////////////// + +enum test_actions { + FIRST_TEST = 38, // place-holder. + ADD = FIRST_TEST, + // adds an item that is probably new. + ADD_ADD, + // adds an item that is probably new, followed by another item under the + // same key id. this ensures the overwriting gets tested. + ZAP, + // finds an item we know is in the list and whacks it. + ADD_ZAP, + // adds a new item and immediately finds and zaps it. + ZAP_ADD, + // zaps an item that we know about and then adds a new item with the same + // identifier. + FIND, + // locates an item in the list which we know should exist. + ACQUIRE, + // grabs an item out of the list (and tosses it). + FIND_ZAP_ADD, + // finds an item we know should exist, zaps it out of the list, then adds + // a new item with the same id. + ACQUIRE_ADD_ZAP, + // removes an item from the list that we know should be there, adds it back + // in, and then whacks it. + FIND_ADD_FIND, + // find an item with a particular id (one that we know should be in the + // list) and then adds a different item using the same id. the new item + // is then sought. + RESET, + // tosses all data out of the hash table. not done very often. + CHECK_SANITY, + // look for any problems or irregularities; print the contents of the list + // if any are found. + REHASH, + // resizes the hash table. + COPY, + // copies a hash table to another hash table. + LAST_TEST = COPY // place-holder; must equal test just prior. +}; + +////////////// + +// a simple object that is used as the contents of the hash_table. + +class data_shuttle +{ +public: + int food_bar; + astring snacky_string; + bool hungry; + byte_array chunk; + chaos chao; + + data_shuttle() + : snacky_string(string_manipulation::make_random_name()), + chunk(chao.inclusive(100, 10000)) {} +}; + +////////////// + +class test_int_hash : public virtual unit_base, virtual public application_shell +{ +public: + test_int_hash(); + + int execute(); + //!< the main startup for the test. + + bool perform_a_test(test_actions test_type); + //!< carries out the specifics of the "test_type". + + bool pick_a_test(); + //!< randomly picks one of the test types and performs it. + + DEFINE_CLASS_NAME("test_int_hash"); + + static bool equivalence_applier(const int &key, data_shuttle &item, void *dlink); + + static const char *test_name(test_actions test_type); + + int raw_random_id(); //!< returns an unvetted random number. + int unused_random_id(); //!< returns an unused (so far) random number. + + // these functions each perform one type of test, which their names indicate. + bool test_add(); + bool test_add_add(); + bool test_zap(); + bool test_add_zap(); + bool test_zap_add(); + bool test_find(); + bool test_acquire(); + bool test_find_zap_add(); + bool test_acquire_add_zap(); + bool test_find_add_find(); + bool test_reset(bool always_do_it = false); + bool test_check_sanity(); + bool test_copy(); + bool test_rehash(); + +private: + int_set _keys_in_use; // keys that we think are stored in the table. + int_hash _the_table; // our table under test. + int _hits[LAST_TEST - FIRST_TEST + 1]; // tracks our testing activities. + int _tested; // simple counter of number of test calls. +}; + +////////////// + +typedef int_hash our_hash; // cleans up somewhat. + +////////////// + +test_int_hash::test_int_hash() +: application_shell(), + _the_table(MAX_DEFAULT_BITS), + _tested(0) +{ + for (int i = FIRST_TEST; i <= LAST_TEST; i++) + _hits[i - FIRST_TEST] = 0; +} + +int test_int_hash::execute() +{ + time_stamp exit_time((int)TEST_DURATION); +//log(astring("before starting tests")); + while (time_stamp() < exit_time) { + pick_a_test(); + } + test_reset(true); // make sure we do this at least once. +#ifdef DEBUG_INT_HASH + log(a_sprintf("did %d tests.\n", _tested)); + log(astring("Test Activity:")); + for (int i = 0; i < LAST_TEST - FIRST_TEST + 1; i++) + log(astring(astring::SPRINTF, "%d (%s): %d hits", i + FIRST_TEST, + test_name(test_actions(i + FIRST_TEST)), _hits[i])); + log(a_sprintf("note that test %d will seldom be executed.", RESET)); +#endif + return final_report(); +} + +const char *test_int_hash::test_name(test_actions test_type) +{ + switch (test_type) { + case ADD: return "ADD"; + case ADD_ADD: return "ADD_ADD"; + case ZAP: return "ZAP"; + case ADD_ZAP: return "ADD_ZAP"; + case ZAP_ADD: return "ZAP_ADD"; + case FIND: return "FIND"; + case ACQUIRE: return "ACQUIRE"; + case FIND_ZAP_ADD: return "FIND_ZAP_ADD"; + case ACQUIRE_ADD_ZAP: return "ACQUIRE_ADD_ZAP"; + case FIND_ADD_FIND: return "FIND_ADD_FIND"; + case RESET: return "RESET"; + case COPY: return "COPY"; + case REHASH: return "REHASH"; + case CHECK_SANITY: return "CHECK_SANITY"; + default: return "UnknownTest"; + } +} + +bool test_int_hash::perform_a_test(test_actions test_type) +{ + FUNCDEF("perform_a_test"); + +// log(astring(test_name(test_type)) + " "); + + switch (test_type) { + case ADD: return test_add(); + case ADD_ADD: return test_add_add(); + case ZAP: return test_zap(); + case ADD_ZAP: return test_add_zap(); + case ZAP_ADD: return test_zap_add(); + case FIND: return test_find(); + case ACQUIRE: return test_acquire(); + case FIND_ZAP_ADD: return test_find_zap_add(); + case ACQUIRE_ADD_ZAP: return test_acquire_add_zap(); + case FIND_ADD_FIND: return test_find_add_find(); + case RESET: return test_reset(); + case COPY: return test_copy(); + case REHASH: return test_rehash(); + case CHECK_SANITY: return test_check_sanity(); + default: + ASSERT_TRUE(false, "there should be no missing case seen!"); + return false; // never gets here. + } +} + +bool test_int_hash::pick_a_test() +{ + _tested++; + return perform_a_test(test_actions(randomizer().inclusive(FIRST_TEST, LAST_TEST))); +} + +int test_int_hash::raw_random_id() +{ + return randomizer().inclusive(-MAXINT32 / 4, MAXINT32 / 4); +} + +int test_int_hash::unused_random_id() +{ + while (true) { + int checking = raw_random_id(); + if (!_keys_in_use.member(checking)) return checking; // got one. + } // keep going until we find unused id. +} + +bool test_int_hash::test_add() +{ + FUNCDEF("test_add"); + _hits[ADD - FIRST_TEST]++; + int random_id = raw_random_id(); + data_shuttle *to_add = new data_shuttle; + to_add->snacky_string = string_manipulation::make_random_name(); + to_add->food_bar = random_id; + outcome wanting = common::IS_NEW; + if (_keys_in_use.member(random_id)) wanting = common::EXISTING; + ASSERT_EQUAL(_the_table.add(random_id, to_add).value(), wanting.value(), + "adding key should work with right expectation"); + if (_keys_in_use.member(random_id)) + return true; // already was there so we replaced. + _keys_in_use.add(random_id); + return true; +} + +////////////// + +#undef UNIT_BASE_THIS_OBJECT +#define UNIT_BASE_THIS_OBJECT (*dynamic_cast(application_shell::single_instance())) + +int_hash *_hang_on = NIL; + // must be set before calling the apply method. + +bool test_int_hash::equivalence_applier(const int &key, data_shuttle &item, void *dlink) +{ + FUNCDEF("equivalence_applier"); + ASSERT_TRUE(dlink, "should have been given name"); + if (!dlink) return false; // fail. + astring test_name = (char *)dlink; + +//application_shell::single_instance()->log(astring("after name check")); + + data_shuttle *found = _hang_on->find(key); + ASSERT_TRUE(found, test_name + ": should find equivalent entry in second list"); + if (!found) return false; // bail or we'll crash. + +//application_shell::single_instance()->log(astring("after finding")); + + ASSERT_EQUAL(item.food_bar, found->food_bar, test_name + ": food_bar should not differ"); + ASSERT_EQUAL(item.snacky_string, found->snacky_string, test_name + ": snacky_string should not differ"); + ASSERT_EQUAL(item.hungry, found->hungry, test_name + ": hungry should not differ"); + return true; +} + +#undef UNIT_BASE_THIS_OBJECT +#define UNIT_BASE_THIS_OBJECT (*this) + +////////////// + +bool test_int_hash::test_rehash() +{ + FUNCDEF("test_rehash"); + // we don't want to rehash too often; it is expensive. +// int maybe = randomizer().inclusive(1, 50); +// if (maybe < 32) return true; // not this time. + + _hang_on = &_the_table; // must do this first. + + _hits[REHASH - FIRST_TEST]++; + + int_hash table_copy(_the_table.estimated_elements()); + + _the_table.apply(equivalence_applier, (void *)func); + astring second_test_name(func); + second_test_name += " try 2"; + table_copy.apply(equivalence_applier, (void *)second_test_name.s()); + + if (!_the_table.elements()) return true; // nothing to do right now for comparisons. + +//log("copying table..."); + copy_hash_table(table_copy, _the_table); + // make a copy of the table. + + ASSERT_INEQUAL(0, table_copy.elements(), "copy shouldn't have unexpected absence of contents"); + +//log("rehashing table..."); + _the_table.rehash(randomizer().inclusive(1, 20)); + +//hmmm: need test of dehash function that reduces elements estimated. + +//log("comparing table..."); + astring third_test_name(func); + third_test_name += " try 3"; + table_copy.apply(equivalence_applier, (void *)third_test_name.s()); +//log("done copy and compare."); + + return true; +} + +bool test_int_hash::test_copy() +{ + FUNCDEF("test_copy"); + // we don't want to copy too often. it's a heavy operation. +// int maybe = randomizer().inclusive(1, 50); +// if (maybe > 16) return true; // not this time. + + _hang_on = &_the_table; // must do this first. + + _hits[COPY - FIRST_TEST]++; + + int_hash table_copy(MAX_DEFAULT_BITS); + +//log("copying table..."); + copy_hash_table(table_copy, _the_table); + // make a copy of the table. + +//log("comparing table..."); + table_copy.apply(equivalence_applier, (void *)func); +//log("done copy and compare."); + + return true; +} + +////////////// + +bool test_int_hash::test_add_add() +{ + FUNCDEF("test_add_add"); + _hits[ADD_ADD - FIRST_TEST]++; + int random_id = unused_random_id(); + data_shuttle *to_add = new data_shuttle; + to_add->snacky_string = string_manipulation::make_random_name(); + to_add->food_bar = random_id; + ASSERT_EQUAL(_the_table.add(random_id, to_add).value(), common::IS_NEW, "key should be new"); + _keys_in_use.add(random_id); + + // second add on same id. + data_shuttle *next_add = new data_shuttle; + next_add->snacky_string = string_manipulation::make_random_name(); + next_add->food_bar = random_id; + ASSERT_EQUAL(_the_table.add(random_id, next_add).value(), our_hash::EXISTING, + "second add should not say first failed"); + + return true; +} + +bool test_int_hash::test_zap() +{ + FUNCDEF("test_zap"); + int maybe = randomizer().inclusive(1, 1000); + if (maybe > 500) return true; + if (!_keys_in_use.elements()) return false; // can't do it yet. + _hits[ZAP - FIRST_TEST]++; + int rand_indy = randomizer().inclusive(0, _keys_in_use.elements() - 1); + int dead_key = _keys_in_use[rand_indy]; + _keys_in_use.remove(dead_key); // remove the record of that key. + ASSERT_TRUE(_the_table.zap(dead_key), "zap should work on key"); + return true; +} + +bool test_int_hash::test_add_zap() +{ + FUNCDEF("test_add_zap"); + // add. + _hits[ADD_ZAP - FIRST_TEST]++; + int random_id = unused_random_id(); + data_shuttle *to_add = new data_shuttle; + to_add->snacky_string = string_manipulation::make_random_name(); + to_add->food_bar = random_id; + ASSERT_EQUAL(_the_table.add(random_id, to_add).value(), common::IS_NEW, "key is new before zap"); + // zap. + ASSERT_TRUE(_the_table.zap(random_id), "add then zap should remove the key"); + return true; +} + +bool test_int_hash::test_zap_add() +{ + FUNCDEF("test_zap_add"); + if (!_keys_in_use.elements()) return false; // can't do it yet. + _hits[ZAP_ADD - FIRST_TEST]++; + int rand_indy = randomizer().inclusive(0, _keys_in_use.elements() - 1); + // in the end, the key list state won't be changed unless the test fails. + int dead_key = _keys_in_use[rand_indy]; + ASSERT_TRUE(_the_table.zap(dead_key), "key should be there for zapping"); + + data_shuttle *to_add = new data_shuttle; + to_add->snacky_string = string_manipulation::make_random_name(); + to_add->food_bar = dead_key; + outcome ret = _the_table.add(dead_key, to_add); + ASSERT_EQUAL(ret.value(), our_hash::IS_NEW, "key should not already be present somehow"); + return true; +} + +int functional_return(int to_pass) { + return to_pass; +} + +bool test_int_hash::test_find() +{ + FUNCDEF("test_find"); + if (!_keys_in_use.elements()) return false; // can't do it yet. + _hits[FIND - FIRST_TEST]++; + int rand_indy = randomizer().inclusive(0, _keys_in_use.elements() - 1); + int find_key = _keys_in_use[rand_indy]; + data_shuttle *found = NIL; + ASSERT_TRUE(_the_table.find(functional_return(find_key), found), + "key should be there when we look"); + ASSERT_TRUE(_the_table.find(functional_return(find_key)), "find2: key be there when checked"); + ASSERT_TRUE(found, "when key is found contents should not be NIL"); + ASSERT_EQUAL(found->food_bar, find_key, "stored key should be same as real key"); + ASSERT_TRUE(found->snacky_string.length(), "stored string should have some length"); + return true; +} + +bool test_int_hash::test_acquire() +{ + FUNCDEF("test_acquire"); + int maybe = randomizer().inclusive(1, 1000); + if (maybe > 750) return true; + if (!_keys_in_use.elements()) return false; // can't do it yet. + _hits[ACQUIRE - FIRST_TEST]++; + int rand_indy = randomizer().inclusive(0, _keys_in_use.elements() - 1); + int find_key = _keys_in_use[rand_indy]; + _keys_in_use.remove(find_key); // remove the record of that key. + data_shuttle *found = _the_table.acquire(find_key); + ASSERT_TRUE(found, "key should be there like clockwork"); + ASSERT_EQUAL(found->food_bar, find_key, "stored key should be same as real key"); + ASSERT_TRUE(found->snacky_string.length(), "stored string should have some length"); + WHACK(found); + found = _the_table.acquire(find_key); + ASSERT_FALSE(found, "key should be missing after zap"); + return true; +} + +bool test_int_hash::test_find_zap_add() +{ + FUNCDEF("test_find_zap_add"); + // find. + if (!_keys_in_use.elements()) return false; // can't do it yet. + _hits[FIND_ZAP_ADD - FIRST_TEST]++; + int rand_indy = randomizer().inclusive(0, _keys_in_use.elements() - 1); + // this is another key list invariant function, if it works. + int find_key = _keys_in_use[rand_indy]; + data_shuttle *found = NIL; + ASSERT_TRUE(_the_table.find(find_key, found), "key should be there when sought"); + ASSERT_TRUE(_the_table.find(find_key), "find2: key should be there for us to find"); + ASSERT_TRUE(found, "found key should have non-NIL contents"); + ASSERT_EQUAL(found->food_bar, find_key, "stored key should have no differences from real key"); + ASSERT_TRUE(found->snacky_string.length(), "stored string should have non-zero length"); + // zap. + ASSERT_TRUE(_the_table.zap(find_key), "should be able to zap the item we had found"); + // add. + data_shuttle *to_add = new data_shuttle; + to_add->snacky_string = string_manipulation::make_random_name(); + to_add->food_bar = find_key; + ASSERT_EQUAL(_the_table.add(find_key, to_add).value(), our_hash::IS_NEW, + "the item we zapped should not still be there"); + return true; +} + +bool test_int_hash::test_reset(bool always_do_it) +{ + FUNCDEF("test_reset"); + if (!always_do_it) { + int maybe = randomizer().inclusive(1, 1000); + // this is hardly ever hit, but it loses all contents too often otherwise. + if ( (maybe > 372) || (maybe < 368) ) return true; + } + + // we hit the big time; we will reset now. + _hits[RESET - FIRST_TEST]++; + _the_table.reset(); + for (int i = _keys_in_use.elements() - 1; i >= 0; i--) { + int dead_key = _keys_in_use[i]; + ASSERT_FALSE(_the_table.acquire(dead_key), "after reset, we should not find an item"); + _keys_in_use.remove(dead_key); + } + return true; +} + +//hmmm: implement these tests! + +bool test_int_hash::test_acquire_add_zap() +{ + _hits[ACQUIRE_ADD_ZAP - FIRST_TEST]++; +return false; +} + +bool test_int_hash::test_find_add_find() +{ + _hits[FIND_ADD_FIND - FIRST_TEST]++; +return false; +} + +bool test_int_hash::test_check_sanity() +{ + _hits[CHECK_SANITY - FIRST_TEST]++; +return false; +} + +////////////// + +HOOPLE_MAIN(test_int_hash, ) + diff --git a/core/library/tests_structures/test_matrix.cpp b/core/library/tests_structures/test_matrix.cpp new file mode 100644 index 00000000..bb87e1e0 --- /dev/null +++ b/core/library/tests_structures/test_matrix.cpp @@ -0,0 +1,401 @@ +/* +* Name : test_matrix +* Author : Chris Koeritz +** +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace mathematics; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +//#define DEBUG_MATRIX + // uncomment for noisier version. + +const int DIM_ROWS = 10; +const int DIM_COLS = 10; + +// fills the matrix "to_stuff" with the pure version of the exemplar. +#define STUFF_MATRIX(to_stuff, max_row, max_col) \ + to_stuff.reset(max_row, max_col); \ + for (int r = 0; r < max_row; r++) \ + for (int c = 0; c < max_col; c++) \ + to_stuff.put(r, c, test_pure.get(r, c)) + +////////////// + +// forward. +class my_int_matrix; + +////////////// + +// this class exhibits an old bug where the matrix was zeroing out its +// contents for a same size resize. the zeroing allowed hell to spew forth. +class diggulite +{ +public: + diggulite() {} + virtual ~diggulite() {} +}; + +////////////// + +class test_matrix : virtual public unit_base, virtual public application_shell +{ +public: + test_matrix(); + + DEFINE_CLASS_NAME("test_matrix"); + + void log(const astring &to_print) { application_shell::log(to_print); } + // override to avoid redoing all the logs here. + + int execute(); + // performs main body of test. + + static astring dump_matrix(const my_int_matrix &to_print); + // creates a nice form of the matrix "to_print". + + void print_matrix(const my_int_matrix &to_print); + // dumps "to_print" to the diagnostic output. + + void test_out_submatrix(const my_int_matrix &source); + //!< runs some tests on the submatrix() function. + + void test_out_redimension(); + //!< checks out the redimension() method for resizing the array. + + void test_out_resizing_virtual_objects(); + //!< checks that a matrix of non-simple objects doesn't have bad problems. + + void test_out_zapping(const my_int_matrix &test_pure); + //!< tries out zap operations. + + void test_out_inserting(const my_int_matrix &test_pure); + //!< checks the insert row and column methods. +}; + +////////////// + +class my_int_matrix : public int_matrix, virtual public hoople_standard +{ +public: + my_int_matrix(int r = 0, int c = 0) : int_matrix(r, c) {} + my_int_matrix(const int_matrix &init) : int_matrix(init) {} + + DEFINE_CLASS_NAME("my_int_matrix"); + + virtual bool equal_to(const equalizable &s2) const { + const my_int_matrix *sec = dynamic_cast(&s2); + if (!sec) return false; + if (rows() != sec->rows()) return false; + if (columns() != sec->columns()) return false; + for (int r = 0; r < this->rows(); r++) + for (int c = 0; c < this->columns(); c++) + if ((*this)[r][c] != (*sec)[r][c]) return false; + return true; + } + + virtual void text_form(base_string &state_fill) const { + state_fill.assign(test_matrix::dump_matrix(*this)); + } +}; + +////////////// + +test_matrix::test_matrix() : application_shell() {} + +astring test_matrix::dump_matrix(const my_int_matrix &to_print) +{ + astring text; + for (int t = 0; t < to_print.rows(); t++) { + text += astring(astring::SPRINTF, "[%d] ", t); + for (int c = 0; c < to_print.columns(); c++) + text += astring(astring::SPRINTF, "%03d ", int(to_print[t][c])); + text += parser_bits::platform_eol_to_chars(); + } + return text; +} + +void test_matrix::print_matrix(const my_int_matrix &to_print) +{ log(astring("\n") + dump_matrix(to_print)); } + +void test_matrix::test_out_submatrix(const my_int_matrix &source) +{ + FUNCDEF("test_out_submatrix") + my_int_matrix test2(source); + + for (int s = 0; s < DIM_ROWS; s++) + for (int c = 0; c < DIM_COLS; c++) + ASSERT_EQUAL(source[s][c], test2[s][c], "computed matrices should be same after copy"); + +#ifdef DEBUG_MATRIX + log("before submatrix:"); + print_matrix(test2); +#endif + my_int_matrix chunk(test2.submatrix(2, 3, 3, 2)); + my_int_matrix chunk_comparator(3, 2); + for (int r = 0; r < 3; r++) + for (int c = 0; c < 2; c++) + chunk_comparator[r][c] = test2[r+2][c+3]; + ASSERT_EQUAL(chunk, chunk_comparator, "submatrix should grab proper contents"); +#ifdef DEBUG_MATRIX + log("after submatrix, chunk of the matrix has:"); + print_matrix(chunk); +#endif +} + +void test_matrix::test_out_redimension() +{ + FUNCDEF("test_out_redimension") + my_int_matrix computed(7, 14); + for (int x1 = 0; x1 < 7; x1++) { + for (int y1 = 0; y1 < 14; y1++) { + if ( (x1 * y1) % 2) computed[x1][y1] = 1 + x1 * 100 + y1; + else computed.put(x1, y1, 1 + x1 * 100 + y1); + } + } + + for (int x2 = 6; x2 >= 0; x2--) { + for (int y2 = 13; y2 >= 0; y2--) { + ASSERT_EQUAL(computed[x2][y2], 1 + x2 * 100 + y2, + "computed matrix should have proper computed values"); + } + } + + computed.redimension(3, 5); + ASSERT_FALSE( (computed.rows() != 3) || (computed.columns() != 5), + "redimension should not get size wrong"); + for (int x3 = 2; x3 >= 0; x3--) { + for (int y3 = 4; y3 >= 0; y3--) { + ASSERT_EQUAL(computed[x3][y3], 1 + x3 * 100 + y3, + "computed matrix should still have right values"); + } + } + + computed.redimension(0, 0); + ASSERT_FALSE(computed.rows() || computed.columns(), + "redimension to zero should see matrix as empty"); + + computed.reset(12, 20); + ASSERT_FALSE( (computed.rows() != 12) || (computed.columns() != 20), + "resize should compute proper size"); +} + +void test_matrix::test_out_resizing_virtual_objects() +{ + FUNCDEF("test_out_resizing_virtual_objects") + // this test block ensures that the matrix doesn't blow up from certain + // resizing operations performed on a templated type that has a virtual + // destructor. + matrix grids; + grids.reset(); + grids.redimension ( 0, 1 ); + grids.redimension ( 1, 1 ); + grids.reset(1, 1); + ASSERT_TRUE(true, "no explosions should occur due to virtual contents"); +} + +void test_matrix::test_out_zapping(const my_int_matrix &test_pure) +{ + FUNCDEF("test_out_zapping") + // this block tests the zapping ops. + my_int_matrix test_zap; + STUFF_MATRIX(test_zap, DIM_ROWS, DIM_COLS); + +#ifdef DEBUG_MATRIX + log("matrix before zappage:"); + print_matrix(test_zap); +#endif + + my_int_matrix compare_1 = test_zap; + ASSERT_EQUAL(compare_1, test_zap, "assignment works right"); + test_zap.zap_row(5); + // make same changes but with different ops so we can compare. + for (int r = 6; r < DIM_ROWS; r++) + for (int c = 0; c < DIM_COLS; c++) + compare_1[r - 1][c] = compare_1[r][c]; + compare_1.zap_row(DIM_ROWS - 1); // lose the last row now. + ASSERT_EQUAL(compare_1, test_zap, "zapping should work regardless of path"); + +#ifdef DEBUG_MATRIX + log("matrix after zappage of row 5:"); + print_matrix(test_zap); +#endif + + // reset the array again. + STUFF_MATRIX(test_zap, DIM_ROWS, DIM_COLS); + my_int_matrix compare_2 = test_zap; + test_zap.zap_column(3); + // now make those same changes in our compare array. + for (int r = 0; r < DIM_ROWS; r++) + for (int c = 4; c < DIM_COLS; c++) + compare_2[r][c - 1] = compare_2[r][c]; + compare_2.zap_column(DIM_COLS - 1); // lose the last row now. + ASSERT_EQUAL(compare_2, test_zap, "second zapping should work regardless of path"); + +#ifdef DEBUG_MATRIX + log("matrix after zappage of column 3:"); + print_matrix(test_zap); +#endif + + // reset test_zap again. + STUFF_MATRIX(test_zap, DIM_ROWS, DIM_COLS); + my_int_matrix compare_3(test_zap.submatrix(1, 1, DIM_ROWS - 2, DIM_COLS - 2)); + test_zap.zap_column(0); + test_zap.zap_row(0); + test_zap.zap_row(test_zap.rows() - 1); + test_zap.zap_column(test_zap.columns() - 1); + ASSERT_EQUAL(test_zap, compare_3, "zapping and submatrix should compute same result"); + +#ifdef DEBUG_MATRIX + log("matrix after zap of row 0, col 0, last row, last col"); + print_matrix(test_zap); +#endif +} + +void test_matrix::test_out_inserting(const my_int_matrix &test_pure) +{ + FUNCDEF("test_out_inserting") + // this block tests the inserting ops. + my_int_matrix test_insert; + STUFF_MATRIX(test_insert, 4, 4); + +#ifdef DEBUG_MATRIX + log("matrix before inserting:"); + print_matrix(test_insert); +#endif + + my_int_matrix compare_1(test_insert); + test_insert.insert_row(2); + compare_1.insert_row(4); + for (int r = 3; r >= 2; r--) + for (int c = 0; c < 4; c++) + compare_1[r + 1][c] = compare_1[r][c]; + for (int c = 0; c < 4; c++) + compare_1[2][c] = 0; + ASSERT_EQUAL(test_insert, compare_1, "inserting row should create expected array"); + +#ifdef DEBUG_MATRIX + log("matrix after insert of row 2:"); + print_matrix(test_insert); +#endif + + // reset test_insert again. + STUFF_MATRIX(test_insert, 5, 6); + +#ifdef DEBUG_MATRIX + log("reset matrix before inserting:"); + print_matrix(test_insert); +#endif + + my_int_matrix compare_2(test_insert); + test_insert.insert_column(3); + compare_2.insert_column(6); + for (int r = 0; r < 5; r++) + for (int c = 5; c >= 3; c--) + compare_2[r][c + 1] = compare_2[r][c]; + for (int r = 0; r < 5; r++) + compare_2[r][3] = 0; + ASSERT_EQUAL(test_insert, compare_2, "inserting column should create expected array"); + +#ifdef DEBUG_MATRIX + log("matrix after insert of column 3:"); + print_matrix(test_insert); +#endif + + // reset test_insert again. + STUFF_MATRIX(test_insert, 3, 3); + my_int_matrix compare_3(5, 5); + for (int r = 0; r < 3; r++) + for (int c = 0; c < 3; c++) + compare_3[r + 1][c + 1] = test_insert[r][c]; + for (int r = 0; r < 5; r++) { compare_3[r][0] = 0; compare_3[r][4] = 0; } + for (int c = 0; c < 5; c++) { compare_3[0][c] = 0; compare_3[4][c] = 0; } + +#ifdef DEBUG_MATRIX + log("matrix before inserting:"); + print_matrix(test_insert); +#endif + + test_insert.insert_column(0); + +#ifdef DEBUG_MATRIX + log("insert col at 0"); + print_matrix(test_insert); +#endif + + test_insert.insert_row(test_insert.rows()); + +#ifdef DEBUG_MATRIX + log("insert row at rows()"); + print_matrix(test_insert); +#endif + + test_insert.insert_column(test_insert.columns()); + +#ifdef DEBUG_MATRIX + log("insert col at cols()"); + print_matrix(test_insert); + log("insert row at 0..."); +#endif + + test_insert.insert_row(0); + + ASSERT_EQUAL(test_insert, compare_3, + "inserting some rows and columns should create expected array"); + +#ifdef DEBUG_MATRIX + log(astring("matrix after insert of col 0, last row, last col, row 0")); + print_matrix(test_insert); +#endif +} + +int test_matrix::execute() +{ + FUNCDEF("execute"); + + my_int_matrix test_pure(DIM_ROWS, DIM_COLS); // kept without modification. + for (int r = 0; r < DIM_ROWS; r++) + for (int c = 0; c < DIM_COLS; c++) + test_pure[r][c] = r * DIM_COLS + c; + + my_int_matrix test1 = test_pure; // first copy to work with. + + test1.reset(); + ASSERT_FALSE(test1.rows() || test1.columns(), "after reset matrix should be empty"); + + test_out_submatrix(test_pure); + + test_out_redimension(); + + test_out_resizing_virtual_objects(); + + test_out_zapping(test_pure); + + test_out_inserting(test_pure); + + return final_report(); +} + +HOOPLE_MAIN(test_matrix, ) + diff --git a/core/library/tests_structures/test_memory_limiter.cpp b/core/library/tests_structures/test_memory_limiter.cpp new file mode 100644 index 00000000..e0fec5eb --- /dev/null +++ b/core/library/tests_structures/test_memory_limiter.cpp @@ -0,0 +1,154 @@ +/*****************************************************************************\ +* * +* Name : test_memory_limiter * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Tests that the memory_limiter is keeping track of the memory users * +* accurately. * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//#define DEBUG_MEMORY_LIMITER + // uncomment for debugging version. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG_MEMORY_LIMITER + #include +#endif + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +const int MAXIMUM_MEM_OVERALL = 1 * MEGABYTE; +const int MAXIMUM_MEM_PER_OWNER = 100 * KILOBYTE; + +const int RUN_TIME = .8 * SECOND_ms; + +////////////// + +class test_memory_limiter : virtual public unit_base, virtual public application_shell +{ +public: + test_memory_limiter() {} + DEFINE_CLASS_NAME("test_memory_limiter"); + virtual int execute(); +}; + +////////////// + +struct mem_record { + int parent; + int allocated; + + mem_record(int parent_in = 0, int allocated_in = 0) + : parent(parent_in), allocated(allocated_in) {} +}; + +struct memorial : array {}; + +////////////// + +int test_memory_limiter::execute() +{ + FUNCDEF("execute"); + time_stamp when_to_leave(RUN_TIME); + time_stamp start; + memorial wtc; + memory_limiter to_test(MAXIMUM_MEM_OVERALL, MAXIMUM_MEM_PER_OWNER); + int allocations = 0; + int deletions = 0; + basis::un_int total_allocated = 0; + basis::un_int total_deleted = 0; + while (time_stamp() < when_to_leave) { + int to_do = randomizer().inclusive(1, 100); + if (to_do < 50) { + // add a new record. + int alloc = randomizer().inclusive(1, 1 * MEGABYTE); +//isolate min max alloc + int parent = randomizer().inclusive(1, 120); +//isolate min max parents + + if (!to_test.okay_allocation(parent, alloc)) + continue; // no space right now. + wtc += mem_record(parent, alloc); + allocations++; + total_allocated += alloc; + } else if (to_do < 88) { + // remove an existing record. + if (!wtc.length()) continue; // nothing to remove. + int indy = randomizer().inclusive(0, wtc.length() - 1); + mem_record to_gone = wtc[indy]; + wtc.zap(indy, indy); + ASSERT_TRUE(to_test.record_deletion(to_gone.parent, to_gone.allocated), + "first case failed to record deletion!"); + deletions++; + total_deleted += to_gone.allocated; + } else { +//do something funky, like allocate part of one into another... + } + } + + // now clear everything left in our list. + for (int i = 0; i < wtc.length(); i++) { + mem_record to_gone = wtc[i]; + ASSERT_TRUE(to_test.record_deletion(to_gone.parent, to_gone.allocated), + "second case failed to record deletion!"); + deletions++; + total_deleted += to_gone.allocated; + } + + // now check that the memory limiter has returned to camber. + + ASSERT_FALSE(to_test.overall_usage(), "final checks: there is still memory in use!"); + + ASSERT_EQUAL(to_test.overall_space_left(), MAXIMUM_MEM_OVERALL, + "final checks: the free space is not correct!"); + + int_set remaining = to_test.individuals_listed(); + ASSERT_FALSE(remaining.elements(), "final checks: there were still uncleared individuals!"); + + time_stamp end; + + LOG("stats for this run:"); + LOG(astring(astring::SPRINTF, "\trun time %f ms", + end.value() - start.value())); + LOG(astring(astring::SPRINTF, "\tallocations %d, total memory allocated %d", + allocations, total_allocated)); + LOG(astring(astring::SPRINTF, "\tdeletions %d, total memory deleted %d", + deletions, total_deleted)); + + return final_report(); +} + +////////////// + +HOOPLE_MAIN(test_memory_limiter, ); + diff --git a/core/library/tests_structures/test_packing.cpp b/core/library/tests_structures/test_packing.cpp new file mode 100644 index 00000000..13e369d6 --- /dev/null +++ b/core/library/tests_structures/test_packing.cpp @@ -0,0 +1,248 @@ +/* +* Name : test_object_packing +* Author : Chris Koeritz +** +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace application; +using namespace basis; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace unit_test; + +#define GENERATE_TEST_NAME(group, message) \ + (astring(group) + " test group: " + message) + +#define TRY_ON(type, value, group) { \ + byte_array temp_array; \ + attach(temp_array, type(value)); \ + type output; \ +/*log(astring(astring::SPRINTF, "parms are: type=%s value=%s group=%s", #type, #value, #group));*/ \ + ASSERT_TRUE(detach(temp_array, output), \ + GENERATE_TEST_NAME(group, "should unpack " #type " okay")); \ + ASSERT_TRUE(output == type(value), \ + GENERATE_TEST_NAME(group, #type " value should match")); \ + ASSERT_FALSE(temp_array.length(), \ + GENERATE_TEST_NAME(group, #type " detached should be empty")); \ +} + +#define TRY_ON_OBSCURE(type, value, group) { \ + byte_array temp_array; \ + obscure_attach(temp_array, type(value)); \ + type output; \ +/*log(astring(astring::SPRINTF, "parms are: type=%s value=%s group=%s", #type, #value, #group));*/ \ + ASSERT_TRUE(obscure_detach(temp_array, output), \ + GENERATE_TEST_NAME(group, "should obscure unpack " #type " okay")); \ + ASSERT_TRUE(output == type(value), \ + GENERATE_TEST_NAME(group, #type " value should obscure match")); \ + ASSERT_FALSE(temp_array.length(), \ + GENERATE_TEST_NAME(group, #type " obscure detached should be empty")); \ +} + +#define TRY_ON_F(type, value, group) { \ + byte_array temp_array; \ + attach(temp_array, type(value)); \ + type output; \ +/*log(astring(astring::SPRINTF, "parms are: type=%s value=%s group=%s", #type, #value, #group));*/ \ + ASSERT_TRUE(detach(temp_array, output), \ + GENERATE_TEST_NAME(group, "should unpack " #type " fine")); \ +/* double_plus a(output); \ + double_plus b(value); */ \ + /*double diff = maximum(output, value) - minimum(output, value);*/ \ + int exponent_1, exponent_2; \ + double mantissa_1 = frexp(output, &exponent_1); \ + double mantissa_2 = frexp(output, &exponent_2); \ + ASSERT_FALSE( (mantissa_1 != mantissa_2) || (exponent_1 != exponent_2), \ + GENERATE_TEST_NAME(group, #type " value should match just so")); \ + ASSERT_FALSE(temp_array.length(), \ + GENERATE_TEST_NAME(group, #type " detached should have no data left")); \ +} + +class test_object_packing : virtual public unit_base, virtual public application_shell +{ +public: + test_object_packing() : application_shell() {} + ~test_object_packing() {} + + DEFINE_CLASS_NAME("test_object_packing"); + + int execute(); +}; + +////////////// + +int test_object_packing::execute() +{ + FUNCDEF("execute"); + { + #define TEST "first" + TRY_ON(int, 2383, TEST); + TRY_ON(int, -18281, TEST); +// TRY_ON(long, 337628, TEST); +// TRY_ON(long, -987887, TEST); + TRY_ON(short, 12983, TEST); + TRY_ON(short, -32700, TEST); + TRY_ON(int, 2988384, TEST); + TRY_ON(int, 92982984, TEST); +// TRY_ON(un_long, 388745, TEST); +// TRY_ON(un_long, 993787, TEST); + TRY_ON(basis::un_short, 12983, TEST); + TRY_ON(basis::un_short, 48377, TEST); + TRY_ON_OBSCURE(un_int, -23948377, TEST); + TRY_ON_OBSCURE(un_int, 28938, TEST); + #undef TEST + } + { + #define TEST "second" + TRY_ON(int, 0, TEST); + TRY_ON(int, MAXINT32, TEST); + TRY_ON(int, MININT32, TEST); + TRY_ON(abyte, 0, TEST); + TRY_ON(abyte, MAXBYTE, TEST); + TRY_ON(abyte, MINBYTE, TEST); + TRY_ON(char, 0, TEST); + TRY_ON(char, MAXCHAR, TEST); + TRY_ON(char, MINCHAR, TEST); +// TRY_ON(long, 0, TEST); +// TRY_ON(long, MAXLONG, TEST); +// TRY_ON(long, MINLONG, TEST); + TRY_ON(short, 0, TEST); + TRY_ON(short, MAXINT16, TEST); + TRY_ON(short, MININT16, TEST); + TRY_ON(basis::un_int, 0, TEST); + un_int max_u_int = MAXINT32 | MININT32; + TRY_ON(basis::un_int, max_u_int, TEST); + TRY_ON(basis::un_int, max_u_int - 1, TEST); + TRY_ON(basis::un_int, max_u_int - 2, TEST); + TRY_ON(basis::un_int, max_u_int - 3, TEST); +// un_long max_u_long = MAXLONG | MINLONG; +// TRY_ON(un_long, 0, TEST); +// TRY_ON(un_long, max_u_long, TEST); +// TRY_ON(un_long, max_u_long - 1, TEST); +// TRY_ON(un_long, max_u_long - 2, TEST); +// TRY_ON(un_long, max_u_long - 3, TEST); + basis::un_short max_u_short = MAXINT16 | MININT16; + TRY_ON(basis::un_short, 0, TEST); + TRY_ON(basis::un_short, max_u_short, TEST); + TRY_ON(basis::un_short, max_u_short - 1, TEST); + TRY_ON(basis::un_short, max_u_short - 2, TEST); + TRY_ON(basis::un_short, max_u_short - 3, TEST); + #undef TEST + } + { + #define TEST "third" + // new bit for floating point packing. + TRY_ON_F(double, 0.0, TEST); + TRY_ON_F(double, 1.0, TEST); + TRY_ON_F(double, -1.0, TEST); + TRY_ON_F(double, 1.1, TEST); + TRY_ON_F(double, -1.1, TEST); + TRY_ON_F(double, 1983.293, TEST); + TRY_ON_F(double, -1983.293, TEST); + TRY_ON_F(double, 984.293e20, TEST); + TRY_ON_F(double, -984.293e31, TEST); + + const int MAX_FLOAT_ITERS = 100; + int iters = 0; + while (iters++ < MAX_FLOAT_ITERS) { + double dividend = randomizer().inclusive(1, MAXINT32 / 2); + double divisor = randomizer().inclusive(1, MAXINT32 / 2); + double multiplier = randomizer().inclusive(1, MAXINT32 / 2); + double rand_float = (dividend / divisor) * multiplier; + if (randomizer().inclusive(0, 1) == 1) + rand_float = -1.0 * rand_float; + TRY_ON_F(double, rand_float, "third--loop"); +//log(a_sprintf("%f", rand_float)); + } + #undef TEST + } + + { + #define TEST "fourth" + // new test for char * packing. + const char *tunnel_vision = "plants can make good friends."; + const char *fresnel_lense = "chimney sweeps carry some soot."; + const char *snoopy = "small white dog with black spots."; + byte_array stored; + int fregose = 38861; + double perky_doodle = 3799.283e10; + const char *emptyish = ""; + int jumboat = 998; + // now stuff the array with some things. + attach(stored, fregose); + attach(stored, tunnel_vision); + attach(stored, snoopy); + attach(stored, perky_doodle); + attach(stored, fresnel_lense); + attach(stored, emptyish); + attach(stored, jumboat); + // time to restore those contents. + astring tunnel_copy; + astring fresnel_copy; + astring snoopy_copy; + int freg_copy; + double perk_copy; + astring emp_copy; + int jum_copy; + ASSERT_TRUE(detach(stored, freg_copy), + GENERATE_TEST_NAME(TEST, "first int failed to unpack")); + ASSERT_TRUE(detach(stored, tunnel_copy), + GENERATE_TEST_NAME(TEST, "first string failed to unpack")); + ASSERT_TRUE(detach(stored, snoopy_copy), + GENERATE_TEST_NAME(TEST, "second string failed to unpack")); + ASSERT_TRUE(detach(stored, perk_copy), + GENERATE_TEST_NAME(TEST, "first double failed to unpack")); + ASSERT_TRUE(detach(stored, fresnel_copy), + GENERATE_TEST_NAME(TEST, "third string failed to unpack")); + ASSERT_TRUE(detach(stored, emp_copy), + GENERATE_TEST_NAME(TEST, "fourth string failed to unpack")); + ASSERT_TRUE(detach(stored, jum_copy), + GENERATE_TEST_NAME(TEST, "second int failed to unpack")); + // now test contents. + ASSERT_EQUAL(freg_copy, fregose, + GENERATE_TEST_NAME(TEST, "first int had wrong contents")); + ASSERT_EQUAL(tunnel_copy, astring(tunnel_vision), + GENERATE_TEST_NAME(TEST, "first string had wrong contents")); + ASSERT_EQUAL(snoopy_copy, astring(snoopy), + GENERATE_TEST_NAME(TEST, "second string had wrong contents")); + ASSERT_EQUAL(perk_copy, perky_doodle, + GENERATE_TEST_NAME(TEST, "first double had wrong contents")); + ASSERT_EQUAL(fresnel_copy, astring(fresnel_lense), + GENERATE_TEST_NAME(TEST, "third string had wrong contents")); + ASSERT_EQUAL(emp_copy, astring(emptyish), + GENERATE_TEST_NAME(TEST, "fourth string had wrong contents")); + ASSERT_EQUAL(jum_copy, jumboat, + GENERATE_TEST_NAME(TEST, "second int had wrong contents")); + ASSERT_FALSE(stored.length(), + GENERATE_TEST_NAME(TEST, "array still had contents after detaching")); + #undef TEST + } + +// critical_events::alert_message("packable:: works for those functions tested."); + return final_report(); +} + +////////////// + +HOOPLE_MAIN(test_object_packing, ); + diff --git a/core/library/tests_structures/test_set.cpp b/core/library/tests_structures/test_set.cpp new file mode 100644 index 00000000..06175059 --- /dev/null +++ b/core/library/tests_structures/test_set.cpp @@ -0,0 +1,74 @@ +/*****************************************************************************\ +* * +* Name : test_set * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1995-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +class test_set : virtual public unit_base, virtual public application_shell +{ +public: + test_set() {} + DEFINE_CLASS_NAME("test_set"); + virtual int execute(); +}; + +int test_set::execute() +{ + FUNCDEF("execute"); + int_set fred; + ASSERT_TRUE(fred.empty(), "first empty check should work"); + ASSERT_TRUE(fred.add(23), "fred 1st add should go in"); + ASSERT_TRUE(fred.add(90123), "fred 2nd add should be okay"); + ASSERT_FALSE(fred.add(23), "fred 3rd add works fine"); + ASSERT_FALSE(fred.add(90123), "fred 4th add is good"); + ASSERT_FALSE(fred.empty(), "second empty check should work"); + ASSERT_TRUE(fred.non_empty(), "non_empty check should be right"); + + int_set gen; + ASSERT_TRUE(gen.add(13), "gen 1st add is okay"); + ASSERT_TRUE(gen.add(23), "gen 2nd add should be fine"); + ASSERT_TRUE(gen.add(8012), "gen 3rd add was good"); + + int_set intersect(gen.intersection(fred)); + ASSERT_EQUAL(intersect.elements(), 1, "intersection elements should be one"); + ASSERT_TRUE(intersect.member(23), "element should be present as 23"); + + int_set uni(gen.set_union(fred)); + ASSERT_EQUAL(uni.elements(), 4, "union elements should be correct"); + ASSERT_TRUE(uni.member(23), "first element we seek should be present"); + ASSERT_TRUE(uni.member(90123), "second element we seek should be present"); + ASSERT_TRUE(uni.member(13), "third element we seek should be present"); + ASSERT_TRUE(uni.member(8012), "fourth element we seek should be present"); + + return final_report(); +} + +////////////// + +HOOPLE_MAIN(test_set, ) + diff --git a/core/library/tests_structures/test_stack.cpp b/core/library/tests_structures/test_stack.cpp new file mode 100644 index 00000000..231931ec --- /dev/null +++ b/core/library/tests_structures/test_stack.cpp @@ -0,0 +1,413 @@ +/* +* Name : t_stack +* Author : Chris Koeritz +* Purpose: +* Tests out the stack object with both flat objects (byte_array) but also +* deep objects (pointer to byte_array). Both bounded and unbounded stacks +* are tested for each. +** +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG_STACK + #define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), to_print) +#endif + +#include +#include + +using namespace application; +using namespace basis; +///using namespace configuration; +using namespace mathematics; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +//HOOPLE_STARTUP_CODE; + +//#define DEBUG_STACK + // uncomment for a noisier version. + +const int test_iterations = 40; + + +class test_stack : public virtual unit_base, public virtual application_shell +{ +public: + test_stack() {} + DEFINE_CLASS_NAME("test_stack"); + int execute(); + + void test_stack_with_objects(); + void test_stack_with_pointers(); + + void CHECK_STACK_RESULT(outcome retval, const astring &place); + byte_array generate_flat(const char *to_store); + byte_array *generate_deep(const char *to_store); + astring text_form(stack &s); + astring text_form(stack &s); +}; + +// CHECK_STACK_RESULT: a function that takes care of error testing for a +// stack command. if executing the command ends in full or empty, then +// that is reported. +void test_stack::CHECK_STACK_RESULT(outcome retval, const astring &place) +{ +#ifdef DEBUG_STACK + if (retval == common::IS_FULL) + LOG(astring(astring::SPRINTF, "returned IS_FULL at %s", place.s())) + else if (retval == common::IS_EMPTY) + LOG(astring(astring::SPRINTF, "returned IS_EMPTY at %s", place.s())); +#else + if (retval.value() || !place) {} +#endif +} + +byte_array test_stack::generate_flat(const char *to_store) +{ + byte_array to_return = byte_array(int(strlen(to_store) + 1), (abyte *)to_store); + return to_return; +} + +byte_array *test_stack::generate_deep(const char *to_store) +{ + byte_array *to_return = new byte_array(int(strlen(to_store) + 1), + (abyte *)to_store); + return to_return; +} + +astring test_stack::text_form(stack &s) +{ + astring to_return; + for (int i = 0; i < s.elements(); i++) { + to_return += astring(astring::SPRINTF, "#%d: %s\n", i, s[i]->observe()); + } + return to_return; +} + +astring test_stack::text_form(stack &s) +{ + astring to_return; + for (int i = 0; i < s.elements(); i++) { + to_return += astring(astring::SPRINTF, "#%d: %s\n", i, s[i].observe()); + } + return to_return; +} + +void test_stack::test_stack_with_objects() +{ + FUNCDEF("test_stack_with_objects"); + for (int qq = 0; qq < test_iterations; qq++) { +#ifdef DEBUG_STACK + LOG(astring(astring::SPRINTF, "index %d", qq)); +#endif + stack bounded_stack(3); + stack unlimited_stack(0); + chaos randomizer; + + { +#ifdef DEBUG_STACK + LOG("testing the bounded stack first:"); +#endif + CHECK_STACK_RESULT(bounded_stack.push(generate_flat("the first line")), "first push"); + CHECK_STACK_RESULT(bounded_stack.push(generate_flat("the second line")), "second push"); + CHECK_STACK_RESULT(bounded_stack.push(generate_flat("the final and third")), "third push"); + byte_array gend = generate_flat("this shouldn't work"); + ASSERT_EQUAL(bounded_stack.push(gend).value(), common::IS_FULL, + "the bounded stack push should catch IS_FULL"); +#ifdef DEBUG_STACK + LOG("pushing worked successfully..."); + LOG("printing the stack in element order"); +#endif + for (int i = 0; i < bounded_stack.size(); i++) { +#ifdef DEBUG_STACK + LOG(astring(astring::SPRINTF, "depth %d has %s.", i, + bounded_stack[i].observe())); +#endif + } +#ifdef DEBUG_STACK + LOG("now popping the stack all the way back."); +#endif + int full_size = bounded_stack.size(); + for (int j = 0; j < full_size; j++) { +#ifdef DEBUG_STACK + LOG(astring(astring::SPRINTF, "pop %d, stack size is %d, has %s", j+1, + bounded_stack.size(), bounded_stack.top().observe())); +#endif + byte_array found; + bounded_stack.acquire_pop(found); + astring got((char *)found.observe()); + switch (j) { + case 0: + ASSERT_EQUAL(got, astring("the final and third"), + "acquire_pop should have right contents at 2"); + break; + case 1: + ASSERT_EQUAL(got, astring("the second line"), + "acquire_pop should have right contents at 1"); + break; + case 2: + ASSERT_EQUAL(got, astring("the first line"), + "acquire_pop should have right contents at 0"); + break; + } + } + ASSERT_EQUAL(bounded_stack.pop().value(), common::IS_EMPTY, + "bounded pop should have right outcome"); + } + + { +#ifdef DEBUG_STACK + LOG("testing the unbounded stack now:"); +#endif + for (int j = 0; j < 24; j++) { + astring line(astring::SPRINTF, "{posn %d here}", j); + CHECK_STACK_RESULT(unlimited_stack.push(generate_flat(line.s())), "unbound push"); + } +#ifdef DEBUG_STACK + LOG("unbounded stack in element order:"); +#endif + for (int k = 0; k < unlimited_stack.size(); k++) { +#ifdef DEBUG_STACK + LOG(astring(astring::SPRINTF, "#%d is %s", k, unlimited_stack[k].observe())); +#endif + } +#ifdef DEBUG_STACK + LOG("now popping fresh order:"); +#endif + while (unlimited_stack.size()) { +#ifdef DEBUG_STACK + LOG(astring(astring::SPRINTF, "size %d, content %s", + unlimited_stack.size(), unlimited_stack.top().observe())); +#endif + unlimited_stack.pop(); + } +#ifdef DEBUG_STACK + LOG(""); +#endif + ASSERT_EQUAL(unlimited_stack.pop().value(), common::IS_EMPTY, + "unlimited pop should return empty as expected"); +#ifdef DEBUG_STACK + LOG("\ +----------------------------------------------\n\ +both types of stack exercises were successful.\n\ +----------------------------------------------"); +#endif + } + +#ifdef DEBUG_STACK + LOG("now setting up some simple stacks..."); +#endif + + { +#ifdef DEBUG_STACK + LOG("bounded first..."); +#endif + stack bounder(randomizer.inclusive(30, 80)); +#ifdef DEBUG_STACK + LOG(astring(astring::SPRINTF, "length of bounder is max %d, curr %d", bounder.elements(), bounder.size())); +#endif + int max = bounder.elements(); + for (int i = 0; i < max; i++) { + ASSERT_EQUAL(bounder.size(), i, "the bounder size should be in step with loop"); + int byte_array_size = randomizer.inclusive(259, 287); + byte_array to_stuff(byte_array_size); + astring visible(astring::SPRINTF, "entry %d...", i); + visible.stuff((char *)to_stuff.access(), visible.length() + 1); + for (int j = visible.length() + 1; j < to_stuff.length(); j++) + *(to_stuff.access() + j) = '\0'; +#ifdef DEBUG_STACK + LOG(astring(astring::SPRINTF, "pushing index %d", i)); +#endif + outcome ret = bounder.push(to_stuff); + ASSERT_EQUAL(ret.value(), common::OKAY, "pushing should not fail in simple test"); + } + ASSERT_EQUAL(bounder.elements(), bounder.size(), "bounder set should see size and max same"); +#ifdef DEBUG_STACK + LOG("inverting:"); +#endif + bounder.invert(); +#ifdef DEBUG_STACK + LOG(astring(astring::SPRINTF, "inverted is:\n%s", text_form(bounder).s())); +#endif + while (bounder.size()) bounder.pop(); + } + } +} + +void test_stack::test_stack_with_pointers() +{ + FUNCDEF("test_stack_with_pointers") + for (int qq = 0; qq < test_iterations; qq++) { +#ifdef DEBUG_STACK + LOG(astring(astring::SPRINTF, "index %d", qq)); +#endif + stack bounded_stack(3); + stack unlimited_stack(0); + chaos randomizer; + + { +#ifdef DEBUG_STACK + LOG("testing the bounded stack first:"); +#endif + CHECK_STACK_RESULT(bounded_stack.push(generate_deep("the first line")), "first push"); + CHECK_STACK_RESULT(bounded_stack.push(generate_deep("the second line")), "second push"); + CHECK_STACK_RESULT(bounded_stack.push(generate_deep("the final and third")), "third push"); + byte_array *gend = generate_deep("this shouldn't work"); + ASSERT_EQUAL(bounded_stack.push(gend).value(), common::IS_FULL, + "the bounded stack push should catch IS_FULL"); + delete gend; +#ifdef DEBUG_STACK + LOG("pushing worked successfully..."); + LOG("printing the stack in element order"); +#endif + for (int i = 0; i < bounded_stack.size(); i++) { +#ifdef DEBUG_STACK + LOG(astring(astring::SPRINTF, "depth %d has: %s", i, bounded_stack[i]->observe())); +#endif + } +#ifdef DEBUG_STACK + LOG("now popping the stack all the way back."); +#endif + int full_size = bounded_stack.size(); + for (int j = 0; j < full_size; j++) { +#ifdef DEBUG_STACK + LOG(astring(astring::SPRINTF, "pop %d, stack size is %d, has %s", j+1, bounded_stack.size(), bounded_stack.top()->observe())); +#endif + byte_array *found; + bounded_stack.acquire_pop(found); + ASSERT_TRUE(found, "acquire_pop test should not return nil"); + ASSERT_TRUE(found->observe(), "acquire_pop test should have non-nil observe"); + astring got((char *)found->observe()); + switch (j) { + case 0: + ASSERT_EQUAL(got, astring("the final and third"), + "popping should have right contents at 2"); + break; + case 1: + ASSERT_EQUAL(got, astring("the second line"), + "popping should have right contents at 1"); + break; + case 2: + ASSERT_EQUAL(got, astring("the first line"), + "popping should have right contents at 0"); + break; + } + delete found; + } + ASSERT_EQUAL(bounded_stack.pop().value(), common::IS_EMPTY, + "bounded pop failure in result"); + } + + { +#ifdef DEBUG_STACK + LOG("testing the unbounded stack now:"); +#endif + for (int j = 0; j < 24; j++) { + astring line(astring::SPRINTF, "{posn %d here}", j); + CHECK_STACK_RESULT(unlimited_stack.push(generate_deep(line.s())), "unbound push"); + } +#ifdef DEBUG_STACK + LOG("unbounded stack in element order:"); +#endif + for (int k = 0; k < unlimited_stack.size(); k++) { +#ifdef DEBUG_STACK + LOG(astring(astring::SPRINTF, "#%d is %s", k, unlimited_stack[k]->observe())); +#endif + } +#ifdef DEBUG_STACK + LOG("\nnow popping order:"); +#endif + while (unlimited_stack.size()) { +#ifdef DEBUG_STACK + LOG(astring(astring::SPRINTF, "size %d, content %s", unlimited_stack.size(), unlimited_stack.top()->observe())); +#endif + byte_array *to_zap = unlimited_stack.top(); + unlimited_stack.pop(); + delete to_zap; + // okay because the pointer was copied, and the reference from top + // is not being relied on. + } +#ifdef DEBUG_STACK + LOG(""); +#endif + ASSERT_EQUAL(unlimited_stack.pop().value(), common::IS_EMPTY, + "unlimited pop should return empty as expected"); +#ifdef DEBUG_STACK + LOG("\n\ +----------------------------------------------\n\ +both types of stack exercises were successful.\n\ +----------------------------------------------"); +#endif + } + +#ifdef DEBUG_STACK + LOG("now setting up some simple stacks..."); +#endif + + { +#ifdef DEBUG_STACK + LOG("bounded first..."); +#endif + stack bounder(randomizer.inclusive(30, 80)); +#ifdef DEBUG_STACK + LOG(astring(astring::SPRINTF, "length of bounder is max %d, curr %d", + bounder.elements(), bounder.size())); +#endif + int max = bounder.elements(); + for (int i = 0; i < max; i++) { + ASSERT_EQUAL(bounder.size(), i, "bounder size should remain in step with loop"); + int byte_array_size = randomizer.inclusive(259, 287); + byte_array *to_stuff = new byte_array(byte_array(byte_array_size)); + astring visible(astring::SPRINTF, "entry %d...", i); + visible.stuff((char *)to_stuff->observe(), visible.length() + 1); + for (int j = visible.length() + 1; j < to_stuff->length(); j++) + *(to_stuff->access() + j) = '\0'; + ASSERT_EQUAL(bounder.push(to_stuff).value(), common::OKAY, + "pushing should not fail in bound simple test"); + } + ASSERT_EQUAL(bounder.elements(), bounder.size(), "bounder set must have size and max agree"); +#ifdef DEBUG_STACK + LOG("inverting:"); +#endif + bounder.invert(); +#ifdef DEBUG_STACK + LOG(astring(astring::SPRINTF, "inverted is:\n%s", text_form(bounder).s())); +#endif + while (bounder.size()) { + byte_array *to_zap = bounder.top(); + bounder.pop(); + delete to_zap; + } + } + } +} + +int test_stack::execute() +{ + test_stack_with_objects(); + test_stack_with_pointers(); + return final_report(); +} + +HOOPLE_MAIN(test_stack, ) + diff --git a/core/library/tests_structures/test_string_table.cpp b/core/library/tests_structures/test_string_table.cpp new file mode 100644 index 00000000..8eadeda2 --- /dev/null +++ b/core/library/tests_structures/test_string_table.cpp @@ -0,0 +1,314 @@ +/*****************************************************************************\ +* * +* Name : test_string_table * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1994-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace application; +using namespace basis; +///using namespace configuration; +using namespace mathematics; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +//#define DEBUG_SYMBOL_TABLE + // uncomment to get lots of noise out of base class. + +//#define DEBUG_STRING_TABLE + // uncomment for noisy version. + +//#define TEST_SIZE_TABLE + // uncomment for testing the size of a string_table. + +//#define OLD_TEST + // uncomment for the older version of symbol table. + +const int test_iterations = 25; + +const int FIND_ITERATIONS = 20; +const int MAXIMUM_RANDOM_ADDS = 50; + +const int TEST_SIZE_TABLE_COUNT = 10000; + // the number of string_table elements we create for checking how + // big one of them is. + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), astring(to_print)) + +double time_in_add = 0; +double time_in_find = 0; +double time_in_pack = 0; +double time_in_unpack = 0; +basis::un_int operations = 0; + +class test_string_table : public virtual application_shell, public virtual unit_base +{ +public: + test_string_table() {} + DEFINE_CLASS_NAME("test_string_table"); + + void ADD(string_table &syms, const astring &name, const astring &to_add); + void FIND(const string_table &syms, const astring &name, const astring &to_find); + + void test_1(); + + virtual int execute(); +}; + +////////////// + +void test_string_table::ADD(string_table &syms, const astring &name, const astring &to_add) +{ + FUNCDEF("ADD") +//LOG(astring("add of ") + name + " => " + to_add); + time_stamp start; + outcome added = syms.add(name, to_add); + operations++; + ASSERT_INEQUAL(added.value(), common::EXISTING, "should not already be in table"); + time_stamp end; + time_in_add += end.value() - start.value(); +} + +void test_string_table::FIND(const string_table &syms, const astring &name, const astring &to_find) +{ + FUNCDEF("FIND") + for (int i = 0; i < FIND_ITERATIONS; i++) { + time_stamp start; + astring *found = syms.find(name); + operations++; + time_stamp end; + time_in_find += end.value() - start.value(); + ASSERT_TRUE(found, "should be in table"); + ASSERT_EQUAL(*found, to_find, "string in table should be correct"); + } +} + +////////////// + +void test_string_table::test_1() +{ + FUNCDEF("test_string_table"); +#ifdef TEST_SIZE_TABLE + { + array symso(TEST_SIZE_TABLE_COUNT); + operations++; + guards::alert_message(astring(astring::SPRINTF, "we should effectively " + "have swamped out the size of other code in the program now. the size " + "should represent %d string_table instantiations. take the current " + "memory size (minus maybe 2 megs) and divide by %d and you will have " + "a fairly accurate cost for instantiating a string table. hit a key.", + TEST_SIZE_TABLE_COUNT, TEST_SIZE_TABLE_COUNT)); + #ifdef _CONSOLE + getchar(); + #elif defined(__UNIX__) + getchar(); + #endif + } +#endif + + string_table syms; + string_table new_syms; + string_table newer_syms; + operations += 3; + for (int qq = 0; qq < test_iterations; qq++) { + syms.reset(); // still could be costly. + operations++; +#ifdef DEBUG_STRING_TABLE + LOG(astring(astring::SPRINTF, "index %d", qq)); +#endif + astring freudname("blurgh"); + astring freud("Sigmund Freud was a very freaked dude."); + ADD(syms, freudname, freud); + astring borgname("borg"); + astring borg("You will be assimilated."); + ADD(syms, borgname, borg); + astring xname("X-Men"); + astring x("The great unknown superhero cartoon."); + ADD(syms, xname, x); + astring aname("fleeny-brickle"); + astring a("lallax menick publum."); + ADD(syms, aname, a); + astring axname("ax"); + astring ax("Lizzy Borden has a very large hatchet."); + ADD(syms, axname, ax); + astring bloinkname("urg."); + astring bloink("this is a short and stupid string"); + ADD(syms, bloinkname, bloink); + astring faxname("fax"); + astring fax("alligators in my teacup."); + ADD(syms, faxname, fax); + astring zname("eagle ovaries"); + astring z("malfeasors beware"); + ADD(syms, zname, z); + + FIND(syms, freudname, freud); + FIND(syms, borgname, borg); + FIND(syms, xname, x); + FIND(syms, aname, a); + FIND(syms, axname, ax); + FIND(syms, bloinkname, bloink); + FIND(syms, faxname, fax); + FIND(syms, zname, z); + + astring name; + astring content; + for (int y = 0; y < MAXIMUM_RANDOM_ADDS; y++) { + name = string_manipulation::make_random_name(40, 108); + content = string_manipulation::make_random_name(300, 1000); + ADD(syms, name, content); + FIND(syms, name, content); + } + + // test copying of the string tables. + string_table chronos = syms; + operations++; + { + string_table mary = syms; + operations++; + string_table june = mary; + operations++; + ASSERT_TRUE(mary == syms, "copy test should compare properly"); + operations++; + } + ASSERT_TRUE(syms == chronos, "copy test original should not be harmed"); + operations++; + + { + // test the bug we think we found in the operator =. + string_table fred; + fred.add("urp", "rarp"); + fred.add("hyurgh", "ralph"); + string_table med; + med.add("urp", "rarp"); + med.add("hyurgh", "ralph"); + fred = med; // the deadly assignment. + fred = med; // the deadly assignment. + fred = med; // the deadly assignment. + fred = med; // the deadly assignment. + fred.add("urp", "rarp"); + fred.add("gurp", "flaarp"); // a new entry. + astring *urp = fred.find("urp"); + astring *hyurgh = fred.find("hyurgh"); + ASSERT_TRUE(urp, "urp should not go missing"); + ASSERT_TRUE(hyurgh, "hyurgh should not go missing"); +#ifdef DEBUG_STRING_TABLE + LOG(astring("got urp as ") + (urp? *urp : "empty!!!!")); + LOG(astring("got hyurgh as ") + (hyurgh? *hyurgh : "empty!!!!")); +#endif + astring urp_val = fred[0]; + // AH HA! this one finds it. accessing via bracket or other methods + // that use the internal get() method will fail. + // if there is no outright crash, then the bug is gone. +#ifdef DEBUG_STRING_TABLE + LOG(astring("got urp_val as ") + (urp_val.t()? urp_val : "empty!!!!")); +#endif + } + +#ifdef DEBUG_STRING_TABLE +//// LOG(astring(astring::SPRINTF,"This is the symbol table before any manipulation\n%s", syms.text_form())); + LOG("now packing the symbol table..."); +#endif + byte_array packed_form; + time_stamp start; + syms.pack(packed_form); + operations++; + time_stamp end; + time_in_pack += end.value() - start.value(); +#ifdef DEBUG_STRING_TABLE + LOG("now unpacking from packed form"); +#endif + start.reset(); + ASSERT_TRUE(new_syms.unpack(packed_form), "unpack test should succeed in unpacking"); + operations++; + end.reset(); // click, off. + time_in_unpack += end.value() - start.value(); + +#ifdef DEBUG_STRING_TABLE +/// LOG(astring(astring::SPRINTF, "unpacked form has:\n%s", new_syms->text_form().s())); +#endif + ASSERT_FALSE(! (syms == new_syms), "unpacked test symbol tables should be same"); + operations++; + +#ifdef DEBUG_STRING_TABLE + LOG("now deleting old symbol table..."); +#endif + +#ifdef DEBUG_STRING_TABLE +/// LOG(astring(astring::SPRINTF, "got the unpacked form, and dumping it:\n%s", new_syms->text_form().s())); + LOG("packing the symbol table again..."); +#endif + byte_array packed_again(0); + start.reset(); // click, on. + new_syms.pack(packed_again); + operations++; + end.reset(); // click, off. + time_in_pack += end.value() - start.value(); +#ifdef DEBUG_STRING_TABLE + LOG("now unpacking from packed form again..."); +#endif + start = time_stamp(); + newer_syms.unpack(packed_again); + operations++; + end = time_stamp(); + time_in_unpack += end.value() - start.value(); +#ifdef DEBUG_STRING_TABLE + LOG(astring(astring::SPRINTF, "got the unpacked form, and dumping " + "it:\n%s", newer_syms.text_form().s())); +#endif + ASSERT_TRUE(new_syms == newer_syms, "unpacked test should not be different symbol tables"); + operations++; + +#ifdef DEBUG_STRING_TABLE + LOG("now deleting new and newer symbol table..."); +#endif + } +} + +////////////// + +int test_string_table::execute() +{ +#ifdef DEBUG_STRING_TABLE + LOG(astring("starting test: ") + time_stamp::notarize(false)); +#endif + test_1(); +#ifdef DEBUG_STRING_TABLE + LOG(astring("done test: ") + time_stamp::notarize(false)); + LOG(astring(astring::SPRINTF, "time in add=%f", time_in_add)); + LOG(astring(astring::SPRINTF, "time in find=%f", time_in_find)); + LOG(astring(astring::SPRINTF, "time in pack=%f", time_in_pack)); + LOG(astring(astring::SPRINTF, "time in unpack=%f", time_in_unpack)); + LOG(astring(astring::SPRINTF, "total operations=%u", operations)); +#endif + + return final_report(); +} + +HOOPLE_MAIN(test_string_table, ) + diff --git a/core/library/tests_structures/test_symbol_table.cpp b/core/library/tests_structures/test_symbol_table.cpp new file mode 100644 index 00000000..22535a8f --- /dev/null +++ b/core/library/tests_structures/test_symbol_table.cpp @@ -0,0 +1,591 @@ +/* +* Name : test_symbol_table +* Author : Chris Koeritz +** +* Copyright (c) 1994-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace application; +using namespace basis; +using namespace mathematics; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +//#define DEBUG_SYMBOL_TABLE + // uncomment for noisy version. + +const int test_iterations = 4; + +const int FIND_ITERATIONS = 10; +const int MAXIMUM_RANDOM_ADDS = 20; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), astring(to_print)) + +//#define OLD_TEST + // uncomment for the older version of symbol table. + +double time_in_add = 0; +double time_in_dep_find = 0; +double time_in_new_find = 0; +double time_in_pack = 0; +double time_in_unpack = 0; +double time_in_copy = 0; + +////////////// + +class my_table_def : virtual public hoople_standard, virtual public symbol_table +///, virtual public equalizable +{ +public: + DEFINE_CLASS_NAME("my_table_def"); + + virtual void text_form(base_string &state_fill) const { + state_fill.assign(astring(class_name()) + ": uhhh not really implemented"); + } + + virtual bool equal_to(const equalizable &s2) const { + const my_table_def *to_compare = dynamic_cast(&s2); + if (!to_compare) return false; + if (symbols() != to_compare->symbols()) return false; + for (int i = 0; i < symbols(); i++) { + if (name(i) != to_compare->name(i)) return false; + if (operator [](i) != (*to_compare)[i]) return false; + } + return true; + } +}; + +////////////// + +// jethro is a simple object for containment below. +class jethro +{ +public: + astring _truck; + + bool operator ==(const jethro &tc) const { return tc._truck == _truck; } + bool operator !=(const jethro &tc) const { return !(*this == tc); } +}; + +////////////// + +// the test_content object is an object with proper copy constructor +// and assignment operator that also has deep contents. +class test_content : public packable +{ +public: + int _q; + astring _ted; + astring _jed; + int_array _ned; + matrix _med; + + test_content() : _q(9) {} + + test_content(const astring &ted, const astring &jed) : _q(4), _ted(ted), + _jed(jed) {} + +//hmmm: pack and unpack don't do everything. + void pack(byte_array &packed_form) const { + attach(packed_form, _q); + _ted.pack(packed_form); + _jed.pack(packed_form); + } + int packed_size() const { + return sizeof(_q) + _ted.packed_size() + _jed.packed_size(); + } + bool unpack(byte_array &packed_form) { + if (!detach(packed_form, _q)) return false; + if (!_ted.unpack(packed_form)) return false; + if (!_jed.unpack(packed_form)) return false; + return true; + } + + bool operator ==(const test_content &tc) const { + if (tc._q != _q) return false; + if (tc._ted != _ted) return false; + if (tc._jed != _jed) return false; + if (tc._ned.length() != _ned.length()) return false; + for (int i = 0; i < _ned.length(); i++) + if (tc._ned[i] != _ned[i]) return false; + + if (tc._med.rows() != _med.rows()) return false; + if (tc._med.columns() != _med.columns()) return false; + for (int c = 0; c < _med.columns(); c++) + for (int r = 0; r < _med.rows(); r++) + if (tc._med.get(r, c) != _med.get(r, c)) return false; + + return true; + } + bool operator !=(const test_content &tc) const { return !operator ==(tc); } + + operator int() const { return _q; } +}; + +////////////// + +class second_table_def : virtual public hoople_standard, virtual public symbol_table +{ +public: + DEFINE_CLASS_NAME("second_table_def") + + virtual void text_form(base_string &state_fill) const { + state_fill.assign(astring(class_name()) + ": uhhh not really implemented"); + } + + virtual bool equal_to(const equalizable &s2) const { + const second_table_def *to_compare = dynamic_cast(&s2); + if (symbols() != to_compare->symbols()) return false; + for (int i = 0; i < symbols(); i++) { + if ((*this)[i] != (*to_compare)[i]) return false; + } + return true; + } +}; + +////////////// + +class test_symbol_table : public virtual application_shell, public virtual unit_base +{ +public: + test_symbol_table() {} + DEFINE_CLASS_NAME("test_symbol_table"); + + void test_1(); + + virtual int execute(); + + void ADD(my_table_def &syms, const astring &name, const astring &to_add); + void FIND(const my_table_def &syms, const astring &name, const astring &to_find); + + void ADD2(second_table_def &syms, const astring &name, const test_content &to_add); + + void pack(byte_array &packed_form, const my_table_def &to_pack); + bool unpack(byte_array &packed_form, my_table_def &to_unpack); + + void pack(byte_array &packed_form, const second_table_def &to_pack); + bool unpack(byte_array &packed_form, second_table_def &to_unpack); + + void test_byte_table(); + void test_tc_table(); +}; + +////////////// + +void test_symbol_table::ADD(my_table_def &syms, const astring &name, const astring &to_add) +{ + FUNCDEF("ADD") + byte_array to_stuff(to_add.length() + 1, (abyte *)to_add.s()); + time_stamp start; + outcome added = syms.add(name, to_stuff); + ASSERT_EQUAL(added.value(), common::IS_NEW, "should not already be in table"); + time_stamp end; + time_in_add += end.value() - start.value(); + start.reset(); +#ifdef OLD_TEST + int indy = syms.find(name); + ASSERT_FALSE(negative(indy), "should be in table after add"); + end.reset(); + time_in_dep_find += end.value() - start.value(); + const byte_array *found = &syms[indy]; +#else + byte_array *found = syms.find(name); + ASSERT_TRUE(found, "really should be in table after add"); + end.reset(); + time_in_new_find += end.value() - start.value(); +#endif + ASSERT_EQUAL(*found, to_stuff, "value should be right in table after add"); +} + +void test_symbol_table::FIND(const my_table_def &syms, const astring &name, const astring &to_add) +{ + FUNCDEF("FIND") + byte_array to_stuff(to_add.length() + 1, (abyte *)to_add.s()); + for (int i = 0; i < FIND_ITERATIONS; i++) { + time_stamp start; +#ifdef OLD_TEST + // double the calls so we roughly match the other test. + int indy = syms.find(name); + ASSERT_FALSE(negative(indy), "should locate item in table"); + indy = syms.find(name); + ASSERT_FALSE(negative(indy), "second find should locate item in table"); + byte_array *found = &syms[indy]; + time_stamp end; + time_in_dep_find += end.value() - start.value(); +#else + int indy = syms.dep_find(name); + ASSERT_FALSE(negative(indy), "should locate item in table (dep_find)"); + time_stamp end; + time_in_dep_find += end.value() - start.value(); + start.reset(); + byte_array *found = syms.find(name); + ASSERT_TRUE(found, "second find should see item in table (new_find)"); + end.reset(); + time_in_new_find += end.value() - start.value(); +#endif + } +} + +void test_symbol_table::pack(byte_array &packed_form, const my_table_def &to_pack) +{ + attach(packed_form, to_pack.symbols()); + astring name; + byte_array content; + for (int i = 0; i < to_pack.symbols(); i++) { + to_pack.retrieve(i, name, content); + name.pack(packed_form); + attach(packed_form, content); + } +} + +bool test_symbol_table::unpack(byte_array &packed_form, my_table_def &to_unpack) +{ + to_unpack.reset(); + int syms = 0; + if (!detach(packed_form, syms)) return false; + astring name; + byte_array chunk; + for (int i = 0; i < syms; i++) { + if (!name.unpack(packed_form)) return false; + if (!detach(packed_form, chunk)) return false; + ADD(to_unpack, name, (char *)chunk.observe()); + } + return true; +} + +void test_symbol_table::pack(byte_array &packed_form, const second_table_def &to_pack) +{ + attach(packed_form, to_pack.symbols()); + astring name; + test_content content; + for (int i = 0; i < to_pack.symbols(); i++) { + to_pack.retrieve(i, name, content); + name.pack(packed_form); + content.pack(packed_form); + } +} + +bool test_symbol_table::unpack(byte_array &packed_form, second_table_def &to_unpack) +{ + to_unpack.reset(); + int syms = 0; + if (!detach(packed_form, syms)) return false; + astring name; + test_content chunk; + for (int i = 0; i < syms; i++) { + if (!name.unpack(packed_form)) return false; + if (!chunk.unpack(packed_form)) return false; + to_unpack.add(name, chunk); + } + return true; +} + +////////////// + +my_table_def creatapose() +{ + my_table_def to_return; + astring name; + astring content; + for (int y = 0; y < MAXIMUM_RANDOM_ADDS; y++) { + name = string_manipulation::make_random_name(40, 108); + content = string_manipulation::make_random_name(300, 1000); + byte_array to_stuff(content.length() + 1, (abyte *)content.s()); + to_return.add(name, to_stuff); + } + return to_return; +} + +////////////// + +void test_symbol_table::test_byte_table() +{ + FUNCDEF("test_byte_table") + my_table_def syms; + my_table_def new_syms; + my_table_def newer_syms; + for (int qq = 0; qq < test_iterations; qq++) { + syms.reset(); // still could be costly. +#ifdef DEBUG_SYMBOL_TABLE + LOG(astring(astring::SPRINTF, "index %d", qq)); +#endif + astring freudname("blurgh"); + astring freud("Sigmund Freud was a very freaked dude."); + ADD(syms, freudname, freud); + astring borgname("borg"); + astring borg("You will be assimilated."); + ADD(syms, borgname, borg); + astring xname("X-Men"); + astring x("The great unknown superhero cartoon."); + ADD(syms, xname, x); + astring aname("fleeny-brickle"); + astring a("lallax menick publum."); + ADD(syms, aname, a); + astring axname("ax"); + astring ax("Lizzy Borden has a very large hatchet."); + ADD(syms, axname, ax); + astring bloinkname("urg."); + astring bloink("this is a short and stupid string"); + ADD(syms, bloinkname, bloink); + astring faxname("fax"); + astring fax("alligators in my teacup."); + ADD(syms, faxname, fax); + astring zname("eagle ovaries"); + astring z("malfeasors beware"); + ADD(syms, zname, z); + + FIND(syms, freudname, freud); + FIND(syms, borgname, borg); + FIND(syms, xname, x); + FIND(syms, aname, a); + FIND(syms, axname, ax); + FIND(syms, bloinkname, bloink); + FIND(syms, faxname, fax); + FIND(syms, zname, z); + + astring name; + astring content; + for (int y = 0; y < MAXIMUM_RANDOM_ADDS; y++) { + name = string_manipulation::make_random_name(40, 108); + content = string_manipulation::make_random_name(300, 1000); + ADD(syms, name, content); + FIND(syms, name, content); + } + + // test copying the table. + time_stamp start; // click, on. + my_table_def copy1(syms); + { + my_table_def joe(copy1); + my_table_def joe2 = joe; + ASSERT_EQUAL(joe2, joe, "copy test A: symbol tables should be same"); + my_table_def joe3 = creatapose(); // on stack. + my_table_def joe4 = joe3; + my_table_def joe5 = joe4; + ASSERT_EQUAL(joe5, joe3, "copy test A2: symbol tables should be same"); + } + ASSERT_FALSE(! (syms == copy1), "copy test B: symbol tables should be same still"); + time_stamp end; + time_in_copy += end.value() - start.value(); + +#ifdef DEBUG_SYMBOL_TABLE +//// LOG(astring(astring::SPRINTF,"This is the symbol table before any manipulation\n%s", syms.text_form())); + LOG("now packing the symbol table..."); +#endif + +#ifdef DEBUG_SYMBOL_TABLE + LOG("now unpacking from packed form"); +#endif + byte_array packed_form; + pack(packed_form, syms); + ASSERT_TRUE(unpack(packed_form, new_syms), "unpack test should not fail to unpack"); + +#ifdef DEBUG_SYMBOL_TABLE +/// LOG(astring(astring::SPRINTF, "unpacked form has:\n%s", new_syms->text_form().s())); +#endif + ASSERT_FALSE(! (syms == new_syms), "unpacked test symbol tables must be equal"); + +#ifdef DEBUG_SYMBOL_TABLE +/// LOG(astring(astring::SPRINTF, "got the unpacked form, and dumping it:\n%s", new_syms->text_form().s())); + LOG("packing the symbol table again..."); +#endif + byte_array packed_again(0); + start.reset(); // click, on. + pack(packed_again, new_syms); + end.reset(); // click, off. + time_in_pack += end.value() - start.value(); +#ifdef DEBUG_SYMBOL_TABLE + LOG("now unpacking from packed form again..."); +#endif + start = time_stamp(); + ASSERT_TRUE(unpack(packed_again, newer_syms), "newer unpacking should working be"); + end = time_stamp(); + time_in_unpack += end.value() - start.value(); +#ifdef DEBUG_SYMBOL_TABLE +/// LOG(astring(astring::SPRINTF, "got the unpacked form, and dumping it:\n%s", newer_syms->text_form().s())); +#endif + ASSERT_EQUAL(new_syms, newer_syms, + "unpacked test these just aren't getting it but should be same"); + } +} + +////////////// + +void test_symbol_table::ADD2(second_table_def &syms, const astring &name, + const test_content &to_add) +{ + FUNCDEF("ADD2") + time_stamp start; + outcome added = syms.add(name, to_add); + ASSERT_EQUAL(added.value(), common::IS_NEW, "new item should not already be in table"); + time_stamp end; + time_in_add += end.value() - start.value(); + start = time_stamp(); // reset start. +#ifdef OLD_TEST + int indy = syms.find(name); + ASSERT_FALSE(negative(indy), "item should be found after add"); + // refind to balance timing. + indy = syms.find(name); + ASSERT_FALSE(negative(indy), "item should be found after second add"); + end = time_stamp(); // reset end. + time_in_dep_find += end.value() - start.value(); +#else + int indy = syms.dep_find(name); + ASSERT_FALSE(negative(indy), "finding item after add should work"); + end = time_stamp(); // reset end. + time_in_dep_find += end.value() - start.value(); + start = time_stamp(); + test_content *found = syms.find(name); + ASSERT_TRUE(found, "item shouldn't be nil that we found"); + end = time_stamp(); // reset end. + time_in_new_find += end.value() - start.value(); +#endif + astring name_out; + test_content content_out; + if (syms.retrieve(indy, name_out, content_out) != common::OKAY) { + ASSERT_EQUAL(name_out, name, "name should be correct after retrieve"); + ASSERT_EQUAL(content_out, to_add, "content should be correct after retrieve"); + } +} + +////////////// + +void test_symbol_table::test_tc_table() +{ + FUNCDEF("test_tc_table") + second_table_def syms; + second_table_def new_syms; + second_table_def newer_syms; + for (int qq = 0; qq < test_iterations; qq++) { + syms.reset(); +#ifdef DEBUG_SYMBOL_TABLE + LOG(astring(astring::SPRINTF, "index %d", qq)); +#endif + astring freudname("blurgh"); + test_content freud("Sigmund Freud was a very freaked dude.", "flutenorf"); + ADD2(syms, freudname, freud); + astring borgname("borg"); + test_content borg("You will be assimilated.", "alabaster"); + ADD2(syms, borgname, borg); + astring xname("X-Men"); + test_content x("The great unknown superhero cartoon.", "somnambulist"); + ADD2(syms, xname, x); + astring aname("fleeny-brickle"); + test_content a("lallax menick publum.", "aglos bagnort pavlod"); + ADD2(syms, aname, a); + astring axname("ax"); + test_content ax("Lizzy Borden has a very large hatchet.", "chop"); + ADD2(syms, axname, ax); + astring bloinkname("urg."); + test_content bloink("this is a short and stupid string", "not that short"); + ADD2(syms, bloinkname, bloink); + astring faxname("fax"); + test_content fax("alligators in my teacup.", "lake placid"); + ADD2(syms, faxname, fax); + astring zname("eagle ovaries"); + test_content z("malfeasors beware", "endangered"); + ADD2(syms, zname, z); + + // test copying the table. + time_stamp start; + second_table_def copy1(syms); + { + second_table_def joe(copy1); + second_table_def joe2 = joe; + ASSERT_EQUAL(joe2, joe, "copy test C: should have same symbol tables"); + } + ASSERT_FALSE(! (syms == copy1), "copy test D: symbol tables shouldn't be different"); + time_stamp end; + time_in_copy += end.value() - start.value(); + +#ifdef DEBUG_SYMBOL_TABLE + astring texto; + syms.text_form(texto); + LOG(astring("This is the symbol table before any manipulation\n") + texto); + LOG("now packing the symbol table..."); +#endif + +#ifdef DEBUG_SYMBOL_TABLE + LOG("now unpacking from packed form"); +#endif + byte_array packed_form; + pack(packed_form, syms); + ASSERT_TRUE(unpack(packed_form, new_syms), "crikey all these unpacks should work"); +#ifdef DEBUG_SYMBOL_TABLE + new_syms.text_form(texto); + LOG(astring("unpacked form has:\n") + texto); +#endif + ASSERT_FALSE(! (syms == new_syms), "unpacked test symbol tables should be equivalent"); + +#ifdef DEBUG_SYMBOL_TABLE + new_syms.text_form(texto); + LOG(astring("got the unpacked form, and dumping it:\n") + texto); + LOG("packing the symbol table again..."); +#endif + byte_array packed_again(0); + pack(packed_again, new_syms); +#ifdef DEBUG_SYMBOL_TABLE + LOG("now unpacking from packed form again..."); +#endif + ASSERT_TRUE(unpack(packed_again, newer_syms), "unpacking should get back the goods"); +#ifdef DEBUG_SYMBOL_TABLE + newer_syms.text_form(texto); + LOG(astring("got the unpacked form, and dumping it:\n") + texto); +#endif + ASSERT_FALSE(! (new_syms == newer_syms), "unpacked test symbol tables should stay same"); + } +} + +////////////// + +int test_symbol_table::execute() +{ +#ifdef DEBUG_SYMBOL_TABLE + LOG(astring("starting test 1: ") + time_stamp::notarize(false)); +#endif + test_byte_table(); +#ifdef DEBUG_SYMBOL_TABLE + LOG(astring("done test 1: ") + time_stamp::notarize(false)); + LOG(astring("starting test 2: ") + time_stamp::notarize(false)); +#endif + + test_tc_table(); +#ifdef DEBUG_SYMBOL_TABLE + LOG(astring("done test 2: ") + time_stamp::notarize(false)); + LOG(astring(astring::SPRINTF, "time in add=%f", time_in_add)); + LOG(astring(astring::SPRINTF, "time in dep_find=%f", time_in_dep_find)); + LOG(astring(astring::SPRINTF, "time in new_find=%f", time_in_new_find)); + LOG(astring(astring::SPRINTF, "time in pack=%f", time_in_pack)); + LOG(astring(astring::SPRINTF, "time in unpack=%f", time_in_unpack)); +#endif + return final_report(); +} + +////////////// + +HOOPLE_MAIN(test_symbol_table, ) + diff --git a/core/library/tests_structures/test_unique_id.cpp b/core/library/tests_structures/test_unique_id.cpp new file mode 100644 index 00000000..d86f6b77 --- /dev/null +++ b/core/library/tests_structures/test_unique_id.cpp @@ -0,0 +1,82 @@ +/*****************************************************************************\ +* * +* Name : t_unique_id * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG_STACK + #define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), to_print) +#endif + +using namespace application; +using namespace basis; +///using namespace configuration; +using namespace mathematics; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#include +#include + +////////////// + +class test_unique_id : public virtual unit_base, public virtual application_shell +{ +public: + test_unique_id() {} + DEFINE_CLASS_NAME("test_unique_id"); + int execute(); +}; + +HOOPLE_MAIN(test_unique_id, ); + +////////////// + +int test_unique_id::execute() +{ + FUNCDEF("execute"); + unique_int ted(25); + unique_int jed; + + ASSERT_TRUE(ted.raw_id(), "testing non-zero should not claim was zero"); + ASSERT_TRUE(!jed, "testing zero should claim was zero"); + ASSERT_TRUE(!!ted, "testing non-zero doubled should not claim was zero"); + ASSERT_TRUE(!!!jed, "testing zero doubled should claim was zero"); + + unique_int med(25); + unique_int fed(0); + + ASSERT_EQUAL(med.raw_id(), ted.raw_id(), "testing equality 1 should have correct result"); + ASSERT_EQUAL(fed.raw_id(), jed.raw_id(), "testing equality 2 should have correct result"); + ASSERT_INEQUAL(med.raw_id(), jed.raw_id(), "testing equality 3 should have correct result"); + ASSERT_INEQUAL(fed.raw_id(), ted.raw_id(), "testing equality 4 should have correct result"); + + ASSERT_FALSE(med != ted, "equality operator 1 should have correct result"); + ASSERT_FALSE(fed != jed, "equality operator 2 should have correct result"); + ASSERT_FALSE(med == jed, "equality operator 3 should have correct result"); + ASSERT_FALSE(fed == ted, "equality operator 4 should have correct result"); + + return final_report(); +} + diff --git a/core/library/tests_structures/test_version.cpp b/core/library/tests_structures/test_version.cpp new file mode 100644 index 00000000..8a68ffab --- /dev/null +++ b/core/library/tests_structures/test_version.cpp @@ -0,0 +1,86 @@ +/* +* Name : test_version +* Author : Chris Koeritz +** +* Copyright (c) 2009-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +class test_version : public virtual unit_base, virtual public application_shell +{ +public: + test_version() : application_shell() {} + DEFINE_CLASS_NAME("test_version"); + virtual int execute(); +}; + +int test_version::execute() +{ + FUNCDEF("execute"); + + version v1(5, 6, 138); + version v2(5, 7, 108); + ASSERT_TRUE((v1 < v2), "compare v1 < v2 should work properly"); + ASSERT_FALSE(v1 > v2, "compare v1 > v2 should work properly"); + ASSERT_FALSE(v1 == v2, "compare v1 == v2 should work properly"); + ASSERT_TRUE((v1 != v2), "compare v1 != v2 should work properly"); + + version v3(4, 6, 180); + ASSERT_TRUE((v3 < v1), "compare v3 < v1 should work properly"); + ASSERT_FALSE(v3 > v1, "compare v3 > v1 should work properly"); + ASSERT_FALSE(v3 == v1, "compare v3 == v1 should work properly"); + ASSERT_TRUE((v3 != v1), "compare v3 != v1 should work properly"); + ASSERT_TRUE((v3 < v2), "compare v3 < v2 should work properly"); + ASSERT_FALSE(v3 > v2, "compare v3 > v2 should work properly"); + ASSERT_FALSE(v3 == v2, "compare v3 == v2 should work properly"); + ASSERT_TRUE((v3 != v2), "compare v3 != v2 should work properly"); + ASSERT_FALSE(v1 < v3, "compare v1 < v3 should work properly"); + ASSERT_TRUE((v1 > v3), "compare v1 > v3 should work properly"); + ASSERT_FALSE(v1 == v3, "compare v1 == v3 should work properly"); + ASSERT_TRUE((v1 != v3), "compare v1 != v3 should work properly"); + ASSERT_FALSE(v2 < v3, "compare v2 < v3 should work properly"); + ASSERT_TRUE((v2 > v3), "compare v2 > v3 should work properly"); + ASSERT_FALSE(v2 == v3, "compare v2 == v3 should work properly"); + ASSERT_TRUE((v2 != v3), "compare v2 != v3 should work properly"); + + version v4(4, 6, 180); + ASSERT_FALSE(v3 < v4, "compare v3 < v4 should work properly"); + ASSERT_FALSE(v3 > v4, "compare v3 > v4 should work properly"); + ASSERT_TRUE((v3 == v4), "compare v3 == v4 should work properly"); + ASSERT_FALSE(v3 != v4, "compare v3 != v4 should work properly"); + ASSERT_FALSE(v4 < v3, "compare v4 < v3 should work properly"); + ASSERT_FALSE(v4 > v3, "compare v4 > v3 should work properly"); + ASSERT_TRUE((v4 == v3), "compare v4 == v3 should work properly"); + ASSERT_FALSE(v4 != v3, "compare v4 != v3 should work properly"); + + return final_report(); +} + +HOOPLE_MAIN(test_version, ) + diff --git a/core/library/tests_textual/df_1.csv b/core/library/tests_textual/df_1.csv new file mode 100644 index 00000000..9aa58dfc --- /dev/null +++ b/core/library/tests_textual/df_1.csv @@ -0,0 +1,112 @@ +"1", "OK", "STRING", "Row0", +"2", "OK", "INT16", "722", +"3", "OK", "INT16", "363", +"4", "OK", "INT16", "992", +"5", "OK", "INT16", "665", +"6", "OK", "INT16", "454", +"7", "OK", "INT16", "431", +"8", "OK", "INT16", "460", +"101", "OK", "STRING", "Row1", +"102", "OK", "INT16", "805", +"103", "OK", "INT16", "50", +"104", "OK", "INT16", "715", +"105", "OK", "INT16", "992", +"106", "OK", "INT16", "449", +"107", "OK", "INT16", "430", +"108", "OK", "INT16", "463", +"201", "OK", "STRING", "Row2", +"202", "OK", "INT16", "84", +"203", "OK", "INT16", "957", +"204", "OK", "INT16", "666", +"205", "OK", "INT16", "379", +"206", "OK", "INT16", "616", +"207", "OK", "INT16", "305", +"208", "OK", "INT16", "214", +"301", "OK", "STRING", "Row3", +"302", "OK", "INT16", "479", +"303", "OK", "INT16", "620", +"304", "OK", "INT16", "621", +"305", "OK", "INT16", "146", +"306", "OK", "INT16", "699", +"307", "OK", "INT16", "440", +"308", "OK", "INT16", "577", +"401", "OK", "STRING", "Row4", +"402", "OK", "INT16", "150", +"403", "OK", "INT16", "287", +"404", "OK", "INT16", "108", +"405", "OK", "INT16", "69", +"406", "OK", "INT16", "298", +"407", "OK", "INT16", "947", +"408", "OK", "INT16", "504", +"501", "OK", "STRING", "Row5", +"502", "OK", "INT16", "305", +"503", "OK", "INT16", "46", +"504", "OK", "INT16", "695", +"505", "OK", "INT16", "892", +"506", "OK", "INT16", "645", +"507", "OK", "INT16", "698", +"508", "OK", "INT16", "467", +"601", "OK", "STRING", "Row6", +"602", "OK", "INT16", "384", +"603", "OK", "INT16", "65", +"604", "OK", "INT16", "710", +"605", "OK", "INT16", "279", +"606", "OK", "INT16", "12", +"607", "OK", "INT16", "157", +"608", "OK", "INT16", "386", +"701", "OK", "STRING", "Row7", +"702", "OK", "INT16", "299", +"703", "OK", "INT16", "656", +"704", "OK", "INT16", "665", +"705", "OK", "INT16", "950", +"706", "OK", "INT16", "79", +"707", "OK", "INT16", "44", +"708", "OK", "INT16", "413", +"801", "OK", "STRING", "Row8", +"802", "OK", "INT16", "938", +"803", "OK", "INT16", "19", +"804", "OK", "INT16", "592", +"805", "OK", "INT16", "145", +"806", "OK", "INT16", "574", +"807", "OK", "INT16", "671", +"808", "OK", "INT16", "564", +"901", "OK", "STRING", "Row9", +"902", "OK", "INT16", "221", +"903", "OK", "INT16", "162", +"904", "OK", "INT16", "203", +"905", "OK", "INT16", "304", +"906", "OK", "INT16", "617", +"907", "OK", "INT16", "158", +"908", "OK", "INT16", "631", +"1001", "OK", "STRING", "Row10", +"1002", "OK", "INT16", "844", +"1003", "OK", "INT16", "157", +"1004", "OK", "INT16", "634", +"1005", "OK", "INT16", "123", +"1006", "OK", "INT16", "64", +"1007", "OK", "INT16", "585", +"1008", "OK", "INT16", "510", +"1101", "OK", "STRING", "Row11", +"1102", "OK", "INT16", "999", +"1103", "OK", "INT16", "228", +"1104", "OK", "INT16", "261", +"1105", "OK", "INT16", "394", +"1106", "OK", "INT16", "123", +"1107", "OK", "INT16", "216", +"1108", "OK", "INT16", "161", +"1201", "OK", "STRING", "Row12", +"1202", "OK", "INT16", "206", +"1203", "OK", "INT16", "583", +"1204", "OK", "INT16", "524", +"1205", "OK", "INT16", "877", +"1206", "OK", "INT16", "18", +"1207", "OK", "INT16", "11", +"1208", "OK", "INT16", "800", +"1301", "OK", "STRING", "Row13", +"1302", "OK", "INT16", "385", +"1303", "OK", "INT16", "286", +"1304", "OK", "INT16", "727", +"1305", "OK", "INT16", "540", +"1306", "OK", "INT16", "277", +"1307", "OK", "INT16", "314", +"1308", "OK", "INT16", "363", diff --git a/core/library/tests_textual/makefile b/core/library/tests_textual/makefile new file mode 100644 index 00000000..0fcaf2d4 --- /dev/null +++ b/core/library/tests_textual/makefile @@ -0,0 +1,15 @@ +include cpp/variables.def + +PROJECT = tests_textual +TYPE = test +LAST_TARGETS = copy_datafile +TARGETS = test_byte_format.exe test_parse_csv.exe test_splitter.exe test_xml_generator.exe +LOCAL_LIBS_USED = unit_test application loggers configuration textual timely filesystem \ + structures basis +RUN_TARGETS = $(ACTUAL_TARGETS) + +include cpp/rules.def + +copy_datafile: + $(HIDER)cp -f df_1.csv $(EXECUTABLE_DIR) + diff --git a/core/library/tests_textual/test_byte_format.cpp b/core/library/tests_textual/test_byte_format.cpp new file mode 100644 index 00000000..0895cc44 --- /dev/null +++ b/core/library/tests_textual/test_byte_format.cpp @@ -0,0 +1,171 @@ +/* +* Name : test_byte_format +* Author : Chris Koeritz +* Purpose: Puts the byte formatting utilities through their paces. +** +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), astring(to_print)) + +class test_byte_format : virtual public unit_base, virtual public application_shell +{ +public: + test_byte_format() {} + DEFINE_CLASS_NAME("test_byte_format"); + int execute(); +}; + +int test_byte_format::execute() +{ + FUNCDEF("execute"); + { + // test out the byte shifting functions on a simple known array. + byte_array source; + source += 0x23; source += 0x5f; source += 0xa8; source += 0x2d; + source += 0xe2; source += 0x61; source += 0x90; source += 0x2d; + source += 0xf2; source += 0x38; + astring shifty; + byte_formatter::bytes_to_shifted_string(source, shifty); +// LOG(a_sprintf("A: shifted our %d bytes into: ", source.length()) +// + shifty); + byte_array source_copy; + byte_formatter::shifted_string_to_bytes(shifty, source_copy); + ASSERT_EQUAL(source_copy, source, "A: shifting corrupted the bytes."); + } + + { + // test out the byte shifting functions on a random array. + for (int run = 0; run < 100; run++) { + byte_array source; + int size = randomizer().inclusive(1, 20923); + for (int i = 0; i < size; i++) { + source += abyte(randomizer().inclusive(0, 255)); + } + astring shifty; + byte_formatter::bytes_to_shifted_string(source, shifty); +// LOG(a_sprintf("B: shifted our %d bytes into: ", source.length()) +// + shifty); + byte_array source_copy; + byte_formatter::shifted_string_to_bytes(shifty, source_copy); + ASSERT_EQUAL(source_copy, source, "B: shifting corrupted the bytes."); + } + } + + { + astring burf("alia bodalia petunia"); + astring dump(byte_formatter::text_dump((abyte *)burf.s(), burf.length() + 1)); +//doofus! make this compare the output with expectations. + LOG("dumped form is:"); + LOG(""); + LOG(dump); + } + { + abyte fodder[] = { 0x83, 0x0d, 0x93, 0x21, 0x82, 0xfe, 0xef, 0xdc, 0xb9, + 0xa9, 0x21, 0x54, 0x83, 0x38, 0x65, 0x59, 0x99, 0xff, 0x00, 0xa0, + 0x29, 0x03 }; + byte_array fred(sizeof(fodder), fodder); + astring as_text1; + byte_formatter::bytes_to_string(fred, as_text1, true); + LOG(astring("got string #1 of: ") + as_text1); + astring as_text2; + byte_formatter::bytes_to_string(fred, as_text2, false); + LOG(astring("got string #2 of: ") + as_text2); + byte_array convert1; + byte_formatter::string_to_bytes(as_text1, convert1); + byte_array convert2; + byte_formatter::string_to_bytes(as_text2, convert2); + ASSERT_EQUAL(fred, convert1, "first abyte conversion: failed due to inequality"); + ASSERT_TRUE(fred == convert2, "second abyte conversion: failed due to inequality"); + // now a harder test. + astring as_text3("muggulo x83d x93, x21, x82, xfe, xef, xdc, xb9, " + "xa9, x21, x54, x83, x38, x65, x59, x99, xff, x00a0, x293"); + byte_array harder_convert1; + byte_formatter::string_to_bytes(as_text3, harder_convert1); +astring back3; +byte_formatter::bytes_to_string(harder_convert1, back3); +LOG(astring("got third: ") + back3); + + astring as_text4("muggulo x83d x93, x21, x82, xfe, xef, xdc, xb9, " + "xa9, x21, x54, x83, x38, x65, x59, x99, xff, x00a0, x293gikkor"); + byte_array harder_convert2; + byte_formatter::string_to_bytes(as_text4, harder_convert2); +astring back4; +byte_formatter::bytes_to_string(harder_convert2, back4); +LOG(astring("got fourth: ") + back4); + ASSERT_EQUAL(fred, harder_convert1, "third abyte conversion: failed due to inequality"); + ASSERT_EQUAL(fred, harder_convert2, "fourth abyte conversion: failed due to inequality"); + + abyte fodder2[] = { +0x04, 0x00, 0x06, 0x00, 0x0a, 0x02, 0x03, 0x00, 0x06, 0x00, 0x48, 0x01, 0x1c, 0x00, 0x2c, 0x00, 0x04, 0x00, 0x09, 0x00, 0x17, 0x00, 0xff, 0xff, 0x00, 0x00, +0x00, 0x00, 0x09, 0x00 }; + fred = byte_array(sizeof(fodder2), fodder2); + astring as_text5("040006000a020300060048011c002c00040009001700ffff000000000900"); + byte_array harder_convert3; + byte_formatter::string_to_bytes(as_text5, harder_convert3); +astring back5; +byte_formatter::bytes_to_string(harder_convert3, back5); +LOG(astring("got fifth: ") + back5); + ASSERT_EQUAL(fred, harder_convert3, "fifth abyte conversion: failed due to inequality"); + +#ifndef EMBEDDED_BUILD + // now a test of parse_dump. + astring fred_dump; + byte_formatter::text_dump(fred_dump, fred, 0x993834); +LOG("fred dump..."); +LOG(fred_dump); + byte_array fred_like; + byte_formatter::parse_dump(fred_dump, fred_like); + ASSERT_EQUAL(fred, fred_like, "parse_dump test: failed due to inequality"); +#endif + } + { + // an endian tester. + basis::un_short test1 = 0x3c5f; + LOG("0x3c5f in intel:"); + LOG(byte_formatter::text_dump((abyte *)&test1, 2)); + basis::un_int test2 = 0x9eaad0cb; + LOG("0x9eaad0cb in intel:"); + LOG(byte_formatter::text_dump((abyte *)&test2, 4)); + + // now see what they look like as packables. + byte_array testa; + structures::attach(testa, test1); + LOG("0x3c5f in package:"); + LOG(byte_formatter::text_dump(testa)); + byte_array testb; + structures::attach(testb, test2); + LOG("0x9eaad0cb in package:"); + LOG(byte_formatter::text_dump(testb)); + } + + return final_report(); +} + +HOOPLE_MAIN(test_byte_format, ); + diff --git a/core/library/tests_textual/test_parse_csv.cpp b/core/library/tests_textual/test_parse_csv.cpp new file mode 100644 index 00000000..99c7b5b8 --- /dev/null +++ b/core/library/tests_textual/test_parse_csv.cpp @@ -0,0 +1,176 @@ +/* +* Name : test parsing of csv +* Author : Chris Koeritz +* Purpose: Checks that the CSV parsing function handles a few common scenarios. +** +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace configuration; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(s) EMERGENCY_LOG(program_wide_logger::get(), s) + +// the number of times we scan our data file for performance test. +const int MAX_DATA_FILE_ITERS = 4000; + +class test_parsing_csv : public virtual unit_base, public virtual application_shell +{ +public: + test_parsing_csv() {} + DEFINE_CLASS_NAME("test_parsing_csv"); + int execute(); +}; + +//hmmm: too congratulatory? +#define COMPLAIN_FIELD(list, index, value) \ + ASSERT_EQUAL(list[index], astring(value), \ + a_sprintf("comparison test should have field %d correct in %s", index, #list)) + +int test_parsing_csv::execute() +{ + FUNCDEF("execute"); + astring line1 = "\"fupe\",\"snoorp\",\"lutem\",\"fipe\""; + string_array fields1; + bool works1 = list_parsing::parse_csv_line(line1, fields1); + ASSERT_TRUE(works1, "first test should not fail to parse"); +//LOG(a_sprintf("fields len now %d", fields1.length())); + ASSERT_EQUAL(fields1.length(), 4, "first test should have right count of strings found"); + COMPLAIN_FIELD(fields1, 0, "fupe"); + COMPLAIN_FIELD(fields1, 1, "snoorp"); + COMPLAIN_FIELD(fields1, 2, "lutem"); + COMPLAIN_FIELD(fields1, 3, "fipe"); + + astring line2 = "fupe,\"snoorp\",lutem,\"fipe\""; + string_array fields2; + bool works2 = list_parsing::parse_csv_line(line2, fields2); + ASSERT_TRUE(works2, "second test should not fail to parse"); + ASSERT_EQUAL(fields2.length(), 4, "second test should have right count of strings found"); + COMPLAIN_FIELD(fields2, 0, "fupe"); + COMPLAIN_FIELD(fields2, 1, "snoorp"); + COMPLAIN_FIELD(fields2, 2, "lutem"); + COMPLAIN_FIELD(fields2, 3, "fipe"); + + astring line3 = "\"lowenburger\",\"wazizzle\",morphel"; + string_array fields3; + bool works3 = list_parsing::parse_csv_line(line3, fields3); + ASSERT_TRUE(works3, "third test should not fail to parse"); + ASSERT_EQUAL(fields3.length(), 3, "third test should have right count of strings found"); + COMPLAIN_FIELD(fields3, 0, "lowenburger"); + COMPLAIN_FIELD(fields3, 1, "wazizzle"); + COMPLAIN_FIELD(fields3, 2, "morphel"); + + astring line4 = "\"lowenburger\",\"wazizzle\",morphel,"; + string_array fields4; + bool works4 = list_parsing::parse_csv_line(line4, fields4); + ASSERT_TRUE(works4, "fourth test should not fail to parse"); + ASSERT_EQUAL(fields4.length(), 4, "fourth test should not have wrong count of strings found"); + COMPLAIN_FIELD(fields4, 0, "lowenburger"); + COMPLAIN_FIELD(fields4, 1, "wazizzle"); + COMPLAIN_FIELD(fields4, 2, "morphel"); + COMPLAIN_FIELD(fields4, 3, ""); + + astring line5 = "\"lowenburger\",,"; + string_array fields5; + bool works5 = list_parsing::parse_csv_line(line5, fields5); + ASSERT_TRUE(works5, "fifth test should not fail to parse"); + ASSERT_EQUAL(fields5.length(), 3, "fifth test should have right count of strings found"); + COMPLAIN_FIELD(fields5, 0, "lowenburger"); + COMPLAIN_FIELD(fields5, 1, ""); + COMPLAIN_FIELD(fields5, 2, ""); + + astring line6 = ",,,\"rasputy\",,\"spunk\",ralph"; + string_array fields6; + bool works6 = list_parsing::parse_csv_line(line6, fields6); + ASSERT_TRUE(works6, "sixth test should not fail to parse"); + ASSERT_EQUAL(fields6.length(), 7, "sixth test should have right count of strings found"); + COMPLAIN_FIELD(fields6, 0, ""); + COMPLAIN_FIELD(fields6, 1, ""); + COMPLAIN_FIELD(fields6, 2, ""); + COMPLAIN_FIELD(fields6, 3, "rasputy"); + COMPLAIN_FIELD(fields6, 4, ""); + COMPLAIN_FIELD(fields6, 5, "spunk"); + COMPLAIN_FIELD(fields6, 6, "ralph"); + + astring line7 = "\"SRV0001337CHN0000001DSP0000001SRV0001337LAY0003108,16,0,8,192\",\"\\\"row_3\\\" on 12.5.55.159\",3"; + string_array fields7; + bool works7 = list_parsing::parse_csv_line(line7, fields7); + ASSERT_TRUE(works7, "seventh test should not fail to parse"); + ASSERT_EQUAL(fields7.length(), 3, "seventh test should have right count of strings found"); + COMPLAIN_FIELD(fields7, 0, "SRV0001337CHN0000001DSP0000001SRV0001337LAY0003108,16,0,8,192"); + COMPLAIN_FIELD(fields7, 1, "\"row_3\" on 12.5.55.159"); + COMPLAIN_FIELD(fields7, 2, "3"); + + // test 8... use data file. + filename df_dir = filename(application_configuration::application_name()).dirname(); + byte_filer test_data(df_dir.raw() + "/df_1.csv", "rb"); + string_array parsed; + string_array lines; + astring curr_line; + int read_result; + while ( (read_result = test_data.getline(curr_line, 1024)) > 0 ) + lines += curr_line; + if (lines.length()) { + // now we have the data file loaded. + stopwatch clicker; + clicker.start(); + for (int iterations = 0; iterations < MAX_DATA_FILE_ITERS; iterations++) { + for (int line = 0; line < lines.length(); line++) { + const astring ¤t = lines[line]; + list_parsing::parse_csv_line(current, parsed); + } + } + clicker.stop(); + log(a_sprintf("%d csv lines with %d iterations took %d ms (or %d s).", + lines.length(), MAX_DATA_FILE_ITERS, clicker.elapsed(), + clicker.elapsed() / 1000)); + } + + // test 9: process results of create_csv_line. + string_array fields9; + fields9 += "ACk\"boozort"; + fields9 += "sme\"ra\"\"foop"; + fields9 += "\"gumby\""; + astring line9 = "\"ACk\\\"boozort\",\"sme\\\"ra\\\"\\\"foop\",\"\\\"gumby\\\"\""; + astring gen_line_9; + list_parsing::create_csv_line(fields9, gen_line_9); +//log(astring(" got gen line: ") + gen_line_9); +//log(astring("expected line: ") + line9); + ASSERT_EQUAL(gen_line_9, line9, "ninth test should not fail to create expected text"); + string_array fields9_b; + bool works9 = list_parsing::parse_csv_line(gen_line_9, fields9_b); + ASSERT_TRUE(works9, "ninth test should not fail to parse"); + ASSERT_TRUE(fields9_b == fields9, "ninth test should match original fields"); + + return final_report(); +} + +////////////// + +HOOPLE_MAIN(test_parsing_csv, ) + diff --git a/core/library/tests_textual/test_splitter.cpp b/core/library/tests_textual/test_splitter.cpp new file mode 100644 index 00000000..be07b62b --- /dev/null +++ b/core/library/tests_textual/test_splitter.cpp @@ -0,0 +1,96 @@ +/* +* Name : test_splitter +* Author : Chris Koeritz +* Purpose: Checks out the line splitting support to make sure it is working. +** +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +class test_splitter : public virtual unit_base, virtual public application_shell +{ +public: + test_splitter() {} + DEFINE_CLASS_NAME("test_splitter"); + int execute(); +}; + +astring show_limits(int min_col, int max_col) +{ + astring to_return; + for (int i = 0; i <= max_col; i++) { + if (i < min_col) to_return += " "; + else to_return += "+"; + } + return to_return; +} + +#define SHOW_SPLIT(str, low, high) \ + string_manipulation::split_lines(str, output, low, high); \ + LOG(""); formal(temp)\ + LOG(show_limits(low, high)); \ + LOG(output + "<<<<"); \ + LOG(show_limits(low, high)) + +int test_splitter::execute() +{ + FUNCDEF("execute"); + + astring split_1 = "This is a fairly long paragraph that will be split a few different ways to check the splitting logic. It will be interesting to see how things like spaces, and punctuation, are handled."; + + astring output; + + SHOW_SPLIT(split_1, 0, 1); + SHOW_SPLIT(split_1, 0, 10); + SHOW_SPLIT(split_1, 10, 30); + SHOW_SPLIT(split_1, 20, 35); + SHOW_SPLIT(split_1, 4, 50); + SHOW_SPLIT(split_1, 8, 12); + + astring split_2 = "Here's another string.\r\nThere are embedded carriage returns after every sentence.\r\nSo, this should be a good test of how those things are handled when they're seen in the body of the text.\r\nThe next one is, hey, guess what?\r\nIt's a simple LF instead of CRLF; here it comes:\nHow did that look compared the others?\nThe previous was another bare one.\r\nWhen can I stop writing this stupid paragraph?\r\nSoon hopefully."; + + SHOW_SPLIT(split_2, 5, 20); + SHOW_SPLIT(split_2, 0, 30); + SHOW_SPLIT(split_2, 8, 11); + SHOW_SPLIT(split_2, 28, 41); + SHOW_SPLIT(split_2, 58, 79); + + astring split_3 = "This string exists for just one purpose; it will be showing how the thing handles a really long word at the end. And that word is... califragilisticexpialadociuosberriesinatreearerottingnowsomamacasscanyoupleasehelpme"; + + SHOW_SPLIT(split_3, 0, 5); + SHOW_SPLIT(split_3, 10, 30); + + astring split_4 = "This string\n\n\nhas multiple CRs gwan on.\r\n\r\nDoes this cause problems?\n\n\n\n"; + + SHOW_SPLIT(split_4, 3, 10); + SHOW_SPLIT(split_4, 8, 20); + + return final_report(); +} + +HOOPLE_MAIN(test_splitter, ); + diff --git a/core/library/tests_textual/test_xml_generator.cpp b/core/library/tests_textual/test_xml_generator.cpp new file mode 100644 index 00000000..923e438c --- /dev/null +++ b/core/library/tests_textual/test_xml_generator.cpp @@ -0,0 +1,122 @@ +/* +* Name : test_xml_generator +* Author : Chris Koeritz +* Purpose: Checks out whether the XML writer seems to be functional. +** +* Copyright (c) 2007-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +//using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(s) EMERGENCY_LOG(program_wide_logger::get(), s) + +class test_xml_generator : public virtual unit_base, virtual public application_shell +{ +public: + test_xml_generator() {} + DEFINE_CLASS_NAME("test_xml_generator"); + int execute(); +}; + +#define OPERATE_XML(func, args, test_name) { \ + outcome ret = ted.func args; \ + ASSERT_EQUAL(ret.value(), xml_generator::OKAY, \ + astring(test_name) + astring(": failed to ") + #func); \ +} + +int test_xml_generator::execute() +{ + FUNCDEF("execute"); + xml_generator ted; + #define TEST "boilerplate" + + string_table attribs; + attribs.add("bluebird", "petunia chowder"); + OPERATE_XML(add_header, ("glommage", attribs), TEST); + + OPERATE_XML(open_tag, ("Recipe"), TEST); + + OPERATE_XML(open_tag, ("Name"), TEST); + OPERATE_XML(add_content, ("Lime Jello Marshmallow Cottage Cheese Surprise"), + TEST); + OPERATE_XML(close_tag, ("Name"), TEST); + + OPERATE_XML(open_tag, ("Description"), TEST); + OPERATE_XML(add_content, ("My grandma's favorite (may she rest in peace)."), + TEST); + OPERATE_XML(close_tag, ("Description"), TEST); + + #undef TEST + #define TEST "stirring ingredients" + OPERATE_XML(open_tag, ("Ingredients"), TEST); + + ////////////// + + OPERATE_XML(open_tag, ("Ingredient"), TEST); + + attribs.reset(); + attribs.add("unit", "box"); + OPERATE_XML(open_tag, ("Qty", attribs), TEST); + OPERATE_XML(add_content, ("1"), TEST); + OPERATE_XML(close_tag, ("Qty"), TEST); + + OPERATE_XML(open_tag, ("Item"), TEST); + OPERATE_XML(add_content, ("lime gelatin"), TEST); + OPERATE_XML(close_tag, ("Item"), TEST); + + OPERATE_XML(close_tag, ("Ingredient"), TEST); + + ////////////// + + OPERATE_XML(open_tag, ("Ingredient"), TEST); + + attribs.reset(); + attribs.add("unit", "g"); + OPERATE_XML(open_tag, ("Qty", attribs), TEST); + OPERATE_XML(add_content, ("500"), TEST); + OPERATE_XML(close_tag, ("Qty"), TEST); + + OPERATE_XML(open_tag, ("Item"), TEST); + OPERATE_XML(add_content, ("multicolored tiny marshmallows"), TEST); + OPERATE_XML(close_tag, ("Item"), TEST); + + OPERATE_XML(close_tag, ("Ingredient"), TEST); + + ////////////// + + #undef TEST + #define TEST "closing the bowl" + + OPERATE_XML(close_tag, ("Ingredients"), TEST); + + astring generated = ted.generate(); + LOG(astring("XML generated is as follows:")); + LOG(generated); + + return final_report(); +} + +HOOPLE_MAIN(test_xml_generator, ) + diff --git a/core/library/tests_timely/makefile b/core/library/tests_timely/makefile new file mode 100644 index 00000000..3b91fe7b --- /dev/null +++ b/core/library/tests_timely/makefile @@ -0,0 +1,12 @@ +include cpp/variables.def + +PROJECT = tests_timely +TYPE = test +TARGETS = test_earth_time.exe +DEFINITIONS += USE_HOOPLE_DLLS +LOCAL_LIBS_USED = unit_test application processes loggers configuration mathematics nodes \ + structures textual timely filesystem structures basis +RUN_TARGETS = $(ACTUAL_TARGETS) + +include cpp/rules.def + diff --git a/core/library/tests_timely/test_earth_time.cpp b/core/library/tests_timely/test_earth_time.cpp new file mode 100644 index 00000000..716a14b1 --- /dev/null +++ b/core/library/tests_timely/test_earth_time.cpp @@ -0,0 +1,136 @@ +/* +* Name : test_earth_time * +* Author : Chris Koeritz * +** +* Copyright (c) 2007-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#define DEBUG_EARTH_TIME + // set this to enable debugging features of the string class. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace mathematics; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) +#undef BASE_LOG +#define BASE_LOG(s) STAMPED_EMERGENCY_LOG(program_wide_logger::get(), s) + +const int TIME_FORMAT = clock_time::MERIDIAN | clock_time::SECONDS + | clock_time::MILLISECONDS; + // the way we like to see our seconds get printed out. + +////////////// + +class test_earth_time : virtual public unit_base, virtual public application_shell +{ +public: + test_earth_time() {} + DEFINE_CLASS_NAME("test_earth_time"); + + virtual int execute(); + + void run_test_01(); + void run_test_02(); +}; + +////////////// + +void test_earth_time::run_test_01() +{ + FUNCDEF("run_test_01"); + // this test makes sure that clock_time's normalize is working as expected. + + time_locus checker_1(clock_time(12, 0, 60), day_in_year(), 2007); + clock_time::normalize(checker_1); + time_locus compare_1(clock_time(12, 1, 0), day_in_year(), 2007); +//BASE_LOG(astring("a=") + checker_1.text_form(TIME_FORMAT)); +//BASE_LOG(astring("b=") + compare_1.text_form(TIME_FORMAT)); + ASSERT_EQUAL(checker_1, compare_1, "normalize should not fail test 1"); + + time_locus checker_2(clock_time(12, 0, -1), day_in_year(), 2007); + clock_time::normalize(checker_2); + time_locus compare_2(clock_time(11, 59, 59), day_in_year(), 2007); + ASSERT_EQUAL(checker_2, compare_2, "normalize should not fail test 2"); + + time_locus checker_3(clock_time(11, 59, 61), day_in_year(), 2007); + clock_time::normalize(checker_3); + time_locus compare_3(clock_time(12, 00, 01), day_in_year(), 2007); + ASSERT_EQUAL(checker_3, compare_3, "normalize should not fail test 3"); + + time_locus checker_4(clock_time(12, 54, -61), day_in_year(), 2007); + clock_time::normalize(checker_4); + time_locus compare_4(clock_time(12, 52, 59), day_in_year(), 2007); + ASSERT_EQUAL(checker_4, compare_4, "normalize should not fail test 4"); + + time_locus checker_5(clock_time(12, -32, -62), day_in_year(), 2007); + clock_time::normalize(checker_5); + time_locus compare_5(clock_time(11, 26, 58), day_in_year(), 2007); + ASSERT_EQUAL(checker_5, compare_5, "normalize should not fail test 5"); +} + +void test_earth_time::run_test_02() +{ + FUNCDEF("run_test_02"); + // this test makes sure that day_in_year's normalize is working as expected. + + time_locus checker_1(clock_time(0, 0, -1), day_in_year(JANUARY, 1), 2007); + time_locus::normalize(checker_1); + time_locus compare_1(clock_time(23, 59, 59), day_in_year(DECEMBER, 31), 2006); +//BASE_LOG(astring("a=") + checker_1.text_form(TIME_FORMAT)); +//BASE_LOG(astring("b=") + compare_1.text_form(TIME_FORMAT)); + ASSERT_EQUAL(checker_1, compare_1, "normalize should not fail test 1"); + + time_locus checker_2(clock_time(23, 59, 60), day_in_year(DECEMBER, 31), 2007); + time_locus::normalize(checker_2); + time_locus compare_2(clock_time(0, 0, 0), day_in_year(JANUARY, 1), 2008); + ASSERT_EQUAL(checker_2, compare_2, "normalize should not fail test 2"); + + +//add more cases! +// test leap years +// test lotso things. + +} + +int test_earth_time::execute() +{ + FUNCDEF("execute"); + + run_test_01(); + run_test_02(); + + return final_report(); +} + +////////////// + +HOOPLE_MAIN(test_earth_time, ) + diff --git a/core/library/textual/byte_formatter.cpp b/core/library/textual/byte_formatter.cpp new file mode 100644 index 00000000..0aa73932 --- /dev/null +++ b/core/library/textual/byte_formatter.cpp @@ -0,0 +1,324 @@ +/*****************************************************************************\ +* * +* Name : byte_formatter * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "byte_formatter.h" +#include "parser_bits.h" +#include "string_manipulation.h" + +#include +#include +#include + +//#define DEBUG_BYTE_FORMAT + // uncomment for noisier version. + +#undef LOG +#ifdef DEBUG_BYTE_FORMAT + #define LOG(s) printf("%s\n", astring(s).s()) +#else + #define LOG(s) {} +#endif + +#define LINE_SIZE 80 + +using namespace basis; +using namespace structures; + +namespace textual { + +void byte_formatter::print_char(abyte to_print, astring &out, char replace) +{ + int temp = to_print % 128; + if (!parser_bits::is_printable_ascii(to_print)) out += replace; + else out += char(temp); +} + +void byte_formatter::print_chars(const abyte *to_print, int len, astring &out, char replace) +{ + for (int i = 0; i < len; i++) + print_char(to_print[i], out, replace); +} + +void byte_formatter::make_eight(basis::un_int num, astring &out) +{ + basis::un_int thresh = 0x10000000; + while (thresh >= 0x10) { + if (num < thresh) + out += '0'; + thresh >>= 4; // zap a nibble. + } +} + +astring byte_formatter::text_dump(const abyte *location, basis::un_int length, basis::un_int label, + const char *eol) +{ + astring to_return; + text_dump(to_return, location, length, label, eol); + return to_return; +} + +void byte_formatter::text_dump(astring &output, const byte_array &to_dump, basis::un_int label, + const char *eol) +{ + text_dump(output, to_dump.observe(), to_dump.length(), label, eol); +} + +astring byte_formatter::text_dump(const byte_array &to_dump, basis::un_int label, const char *eol) +{ + astring output; + text_dump(output, to_dump.observe(), to_dump.length(), label, eol); + return output; +} + +// this is the real version of text_dump. all the others use it. +void byte_formatter::text_dump(astring &to_return, const abyte *location, basis::un_int length, + basis::un_int label, const char *eol) +{ + to_return = ""; + int entry_size = 4; + int preamble = 14; + + basis::un_int entries_per_line = (LINE_SIZE - preamble) / entry_size; + + for (basis::un_int i = 0; i < length; i += entries_per_line) { + make_eight(i + label, to_return); + to_return += astring(astring::SPRINTF, "%x", i + label) + astring(" | "); + for (basis::un_int j = 0; j < entries_per_line; j++) { + if (i + j >= length) { + // if at the end of the loop, just print spaces. + to_return += " "; + } else { + int ord_of_current_char = *(location + i + j) & 0xFF; + if (ord_of_current_char < 0x10) to_return += '0'; + to_return += astring(astring::SPRINTF, "%x", int(ord_of_current_char)); + to_return += ' '; + } + } + + to_return += "| "; + for (basis::un_int k = i; k < i + entries_per_line; k++) { + if (k >= length) to_return += ' '; + // if past the end of the block, just add spaces. + else print_char(*(location + k), to_return); + } + to_return += astring(" |") + eol; + } +} + +void byte_formatter::parse_dump(const astring &dumped_form, byte_array &bytes_found) +{ + bytes_found.reset(); + string_array lines_found; + // iterate over the string and break it up into lines. + for (int i = 0; i < dumped_form.length(); i++) { + int indy = dumped_form.find('\n', i); +//hmmm: not platform invariant. what about '\r' if we see it? + + if (negative(indy)) { + // no more lines found. + if (i < dumped_form.length() - 1) { + // grab the last bit as a line. + lines_found += dumped_form.substring(i, dumped_form.length() - 1); + } + break; + } + // found a normal line ending, so drop everything from the current + // position up to the ending into the list of strings. + lines_found += dumped_form.substring(i, indy - 1); + i = indy + 1; // jump to next potential line. + } + // now process the lines that we've found. + for (int j = 0; j < lines_found.length(); j++) { + // first step is to find the pipe character that brackets the actual + // data. we ignore the "address" located before the pipe. + astring &s = lines_found[j]; + int bar_one = s.find('|', 0); + if (negative(bar_one)) continue; // skip this one; it's malformed. + // now we look for the second pipe that comes before the text form of + // the data. we don't care about the text or anything after. + int bar_two = s.find('|', bar_one + 1); + if (negative(bar_two)) continue; // skip for same reason. + astring s2 = s.substring(bar_one + 1, bar_two - 1); + byte_array this_part; + string_to_bytes(s2, this_part); + bytes_found += this_part; + } +} + +////////////// + +void byte_formatter::bytes_to_string(const abyte *to_convert, int length, astring &as_string, + bool space_delimited) +{ + if (!to_convert || !length) return; // nothing to do. + if (negative(length)) return; // bunk. + as_string = ""; // reset the output parameter. + + // the pattern is used for printing the bytes and considering the delimiter. + astring pattern("%02x"); + if (space_delimited) pattern += " "; + + // now zip through the array and dump it into the string. + for (int i = 0; i < length; i++) + as_string += astring(astring::SPRINTF, pattern.s(), to_convert[i]); +} + +// returns true if the character is within the valid ranges of hexadecimal +// nibbles (as text). +bool byte_formatter::in_hex_range(char to_check) +//hmmm: move this to parser bits. +{ + return ( (to_check <= '9') && (to_check >= '0') ) + || ( (to_check <= 'f') && (to_check >= 'a') ) + || ( (to_check <= 'F') && (to_check >= 'A') ); +} + +void byte_formatter::string_to_bytes(const char *to_convert, byte_array &as_array) +{ + as_array.reset(); // clear the array. + const int len = int(strlen(to_convert)); + + // the parser uses a simple state machine for processing the string. + enum states { FINDING_HEX, IGNORING_JUNK }; + states state = IGNORING_JUNK; + + int digits = 0; // the number of digits we've currently found. + int accumulator = 0; // the current hex duo. + + // loop through the string. + for (int i = 0; i < len; i++) { + switch (state) { + case IGNORING_JUNK: { + if (in_hex_range(to_convert[i])) { + i--; // skip back to where we were before now. + state = FINDING_HEX; + continue; // jump to the other state. + } + // otherwise, we could care less what the character is. + break; + } + case FINDING_HEX: { + if (digits >= 2) { + // we have finished a hex byte. + as_array += abyte(accumulator); + accumulator = 0; + digits = 0; + i--; // skip back for the byte we haven't eaten yet. + state = IGNORING_JUNK; // jump to other state for a new item. + continue; + } + // we really think this is a digit here and we're not through with + // accumulating them. + accumulator <<= 4; + digits++; + accumulator += string_manipulation::char_to_hex(to_convert[i]); + + // now we sneakily check the next character. + if (!in_hex_range(to_convert[i+1])) { + // we now know we should not be in this state for long. + if (digits) { + // there's still some undigested stuff. + digits = 2; // fake a finished byte. + continue; // keep going, but eat the character we were at. + } + // well, there's nothing lost if we just jump to that state. + state = IGNORING_JUNK; + continue; + } + break; + } + } + } + if (digits) { + // snag the last unfinished bit. + as_array += abyte(accumulator); + } +} + +void byte_formatter::bytes_to_string(const byte_array &to_convert, astring &as_string, + bool space_delimited) +{ + bytes_to_string(to_convert.observe(), to_convert.length(), as_string, + space_delimited); +} + +void byte_formatter::string_to_bytes(const astring &to_convert, byte_array &as_array) +{ string_to_bytes(to_convert.s(), as_array); } + +void byte_formatter::bytes_to_shifted_string(const byte_array &to_convert, astring &as_string) +{ +#ifdef DEBUG_BYTE_FORMAT + FUNCDEF("bytes_to_shifted_string"); +#endif + bit_vector splitter(8 * to_convert.length(), to_convert.observe()); + int i; // track our current position. + for (i = 0; i < splitter.bits(); i += 7) { + abyte curr = 1; // start with a bit set already. + for (int j = i; j < i + 7; j++) { + curr <<= 1; // move to the left. + if (j < splitter.bits()) + curr |= abyte(splitter.on(j)); // or in the current position. + } + as_string += char(curr); + } +#ifdef DEBUG_BYTE_FORMAT + LOG(a_sprintf("%d bytes comes out as %d char string.", + to_convert.length(), as_string.length()).s()); +#endif +} + +void byte_formatter::shifted_string_to_bytes(const astring &to_convert, byte_array &as_array) +{ +#ifdef DEBUG_BYTE_FORMAT + FUNCDEF("shifted_string_to_bytes"); +#endif + bit_vector accumulator; + + for (int i = 0; i < to_convert.length(); i++) { + abyte current = abyte(to_convert[i]) & 0x7F; + // get the current bits but remove the faux sign bit. + accumulator.resize(accumulator.bits() + 7); + // now shift off the individual pieces. + for (int j = 0; j < 7; j++) { + // get current bit's value. + current <<= 1; // shift it up. + abyte set_here = current & 0x80; // test the highest order bit. + // now flip that bit on or off based on what we saw. + accumulator.set_bit(i * 7 + j, bool(set_here)); + } + } + + int remainder = accumulator.bits() % 8; + accumulator.resize(accumulator.bits() - remainder); + // chop off any extraneous bits that are due to our shifting. + +#ifdef DEBUG_BYTE_FORMAT + // there should be no remainder. and the number of bits should be a multiple + // of eight now. + if (accumulator.bits() % 8) + deadly_error("byte_formatter", func, "number of bits is erroneous."); +#endif + + const byte_array &accumref = accumulator; + for (int q = 0; q < accumulator.bits() / 8; q++) + as_array += accumref[q]; + +#ifdef DEBUG_BYTE_FORMAT + LOG(a_sprintf("%d chars comes out as %d bytes.", + to_convert.length(), as_array.length()).s()); +#endif +} + +} // namespace + diff --git a/core/library/textual/byte_formatter.h b/core/library/textual/byte_formatter.h new file mode 100644 index 00000000..b67f997e --- /dev/null +++ b/core/library/textual/byte_formatter.h @@ -0,0 +1,122 @@ +#ifndef BYTE_FORMATTER_CLASS +#define BYTE_FORMATTER_CLASS + +/*****************************************************************************\ +* * +* Name : byte_formatter * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +namespace textual { + +//! Provides functions for manipulating arrays of bytes. + +class byte_formatter : public virtual basis::root_object +{ +public: + virtual ~byte_formatter() {} + + DEFINE_CLASS_NAME("byte_formatter"); + + static void print_char(basis::abyte to_print, basis::astring &out, + char replace = '_'); + //!< prints the byte "to_print" into "out" as long as "to_print" is readable. + /*!< if it's not readable, then the "replace" is printed. */ + + static void print_chars(const basis::abyte *to_print, int length, + basis::astring &out, char replace = '_'); + //!< sends the bytes in "to_print" of "length" bytes into the string "out". + + static void text_dump(basis::astring &output, const basis::abyte *location, + basis::un_int length, basis::un_int label = 0, const char *eol = "\n"); + //!< prints out a block of memory in a human readable form. + /*!< it is stored in the "output" string. the "location" is where to dump, + the "length" is the number of bytes to dump, and the "label" is where to + start numbering the location label on the first line. the "eol" supplies + the line ending sequence to be used for the output file. this should be + "\r\n" for win32. */ + + static basis::astring text_dump(const basis::abyte *location, basis::un_int length, + basis::un_int label = 0, const char *eol = "\n"); + //!< this is a less efficient version of text_dump that returns a string. + /*!< it's easier to use when combining astrings. */ + + static void text_dump(basis::astring &output, + const basis::byte_array &to_dump, basis::un_int label = 0, const char *eol = "\n"); + //!< a version that operates on a byte_array and stores into a string. + static basis::astring text_dump(const basis::byte_array &to_dump, + basis::un_int label = 0, const char *eol = "\n"); + //!< a version that operates on a byte_array and returns a string. + + //! this operation performs the inverse of a text_dump. + static void parse_dump(const basis::astring &dumped_form, + basis::byte_array &bytes_found); + +////////////// + + static void bytes_to_string(const basis::byte_array &to_convert, + basis::astring &as_string, bool space_delimited = true); + //!< converts a byte_array into a string. + /*!< takes an array of bytes "to_convert" and spits out the equivalent form + "as_string". if "space_delimited" is true, then the bytes are separated + by spaces. */ + + static void string_to_bytes(const basis::astring &to_convert, + basis::byte_array &as_array); + //!< wrangles the string "to_convert" into an equivalent byte form "as_array". + /*!< this is a fairly forgiving conversion; it will accept any string and + strip out the hexadecimal bytes. spacing is optional, but every two + hex nibbles together will be taken as a byte. if there are an odd number + of nibbles, then the odd one will be taken as the least significant half + of a byte. */ + + static void bytes_to_string(const basis::abyte *to_convert, int length, + basis::astring &as_string, bool space_delimited = true); + //!< a version that accepts a byte pointer and length, rather than byte_array. + + static void string_to_bytes(const char *to_convert, + basis::byte_array &as_array); + //!< a version that works with the char pointer rather than an astring. + +////////////// + + static void bytes_to_shifted_string + (const basis::byte_array &to_convert, basis::astring &as_string); + //!< this is a special purpose converter from bytes to character strings. + /*!< it merely ensures that the "as_string" version has no zero bytes + besides the end of string null byte. this packs 7 bits of data into each + character, resulting in an 87.5% efficient string packing of the array. + the resulting string is not readable. the "as_string" parameter is not + reset; any data will be appended to it. */ + + static void shifted_string_to_bytes(const basis::astring &to_convert, + basis::byte_array &as_array); + //!< unshifts a string "to_convert" back into a byte_array. + /*!< converts a string "to_convert" created by bytes_to_shifted_string() into + the original array of bytes and stores it in "as_array". the "as_array" + parameter is not reset; any data will be appended to it. */ + +////////////// + + // utility methods to help building the formatted strings. + + static void make_eight(basis::un_int num, basis::astring &out); + + static bool in_hex_range(char to_check); +}; + +} //namespace. + +#endif + diff --git a/core/library/textual/list_parsing.cpp b/core/library/textual/list_parsing.cpp new file mode 100644 index 00000000..9cac4a8f --- /dev/null +++ b/core/library/textual/list_parsing.cpp @@ -0,0 +1,279 @@ +/*****************************************************************************\ +* * +* Name : list_parsing * +* Author : Chris Koeritz * +* Author : Gary Hardley * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "list_parsing.h" +#include "parser_bits.h" + +#include +#include +#include + +#include +#include + +using namespace basis; +using namespace structures; + +namespace textual { + +#undef LOG +#define LOG(to_print) printf("%s::%s: %s\n", static_class_name(), func, astring(to_print).s()) + +list_parsing::~list_parsing() {} // needed since we use the class_name macro. + +// by Gary Hardley. +bool list_parsing::get_ids_from_string(const astring &to_parse, int_set &identifiers) +{ + identifiers.clear(); // clear existing ids, if any. + int_array found; + bool ret = get_ids_from_string(to_parse, found); + if (!ret) return false; + for (int i = 0; i < found.length(); i++) identifiers.add(found[i]); + return true; +} + +// by Gary Hardley. +bool list_parsing::get_ids_from_string(const astring &to_parse, + int_array &identifiers) +{ + identifiers.reset(); // clear existing ids, if any. + if (!to_parse) return false; + // if an empty string is passed, return an empty set. + + int last_id = -1; + int tmp_id; + bool done = false; + char last_separator = ' '; + + int index = 0; + while (!done && (index < to_parse.length())) { + tmp_id = 0; + bool got_digit = false; + while ( (to_parse[index] != ',') && (to_parse[index] != '-') + && (to_parse[index] != ' ') && (index < to_parse.length()) ) { + if (!isdigit(to_parse[index])) return false; + tmp_id *= 10; + tmp_id += int(to_parse[index++]) - 0x30; + got_digit = true; + } + + if (got_digit) { + if (tmp_id > MAXINT32) return false; + + if (last_id == -1) { + last_id = tmp_id; + identifiers += last_id; + } else { + // if the last separator was a dash, this is a range + if (last_separator == '-') { + if (tmp_id >= last_id) { + for (int i = last_id + 1; i <= tmp_id; i++) + identifiers += i; + } + else { + for (int i = tmp_id; i < last_id; i++) + identifiers += i; + } + last_id = 0; + last_separator = ' '; + } else { + last_id = tmp_id; + identifiers += last_id; + } + } + } else { + // did not read an address, to_parse[index] must be a non-digit. + if ( (to_parse[index] != ' ') && (to_parse[index] != '-') + && (to_parse[index] != ',') ) return false; + last_separator = to_parse[index++]; + } + } + return true; +} + +//by chris koeritz. +astring list_parsing::put_ids_in_string(const int_set &ids, char separator) +{ + astring to_return; + for (int i = 0; i < ids.length(); i++) { + to_return += a_sprintf("%d", ids[i]); + if (i < ids.length() - 1) { + to_return += separator; + to_return += " "; + } + } + return to_return; +} + +//by chris koeritz. +astring list_parsing::put_ids_in_string(const int_array &ids, char separator) +{ + astring to_return; + for (int i = 0; i < ids.length(); i++) { + to_return += a_sprintf("%d", ids[i]); + if (i < ids.length() - 1) { + to_return += separator; + to_return += " "; + } + } + return to_return; +} + +// ensures that quotes inside the string "to_emit" are escaped. +astring list_parsing::emit_quoted_chunk(const astring &to_emit) +{ + astring to_return('\0', 256); // avoid reallocations with large pre-alloc. + to_return = ""; // reset to get blank string but keep pre-alloc. + for (int i = 0; i < to_emit.length(); i++) { + char next_char = to_emit[i]; + if ( (next_char == '"') || (next_char == '\\') ) + to_return += "\\"; // add the escape before quote or backslash. + to_return += astring(next_char, 1); + } + return to_return; +} + +void list_parsing::create_csv_line(const string_table &to_csv, astring &target) +{ + target = astring::empty_string(); + for (int i = 0; i < to_csv.symbols(); i++) { + target += astring("\"") + emit_quoted_chunk(to_csv.name(i)) + + "=" + emit_quoted_chunk(to_csv[i]) + "\""; + if (i < to_csv.symbols() - 1) target += ","; + } +} + +void list_parsing::create_csv_line(const string_array &to_csv, astring &target) +{ + target = astring::empty_string(); + for (int i = 0; i < to_csv.length(); i++) { + target += astring("\"") + emit_quoted_chunk(to_csv[i]) + "\""; + if (i < to_csv.length() - 1) target += ","; + } +} + +// we do handle escaped quotes for csv parsing, so check for backslash. +// and since we handle escaped quotes, we also have to handle escaping the +// backslash (otherwise a quoted item with a backslash as the last character +// cannot be handled appropriately, because it will be interpreted as an +// escaped quote instead). no other escapes are implemented right now. +#define handle_escapes \ + if (to_parse[i] == '\\') { \ + if ( (to_parse[i + 1] == '"') || (to_parse[i + 1] == '\\') ) { \ + i++; \ + accumulator += to_parse[i]; \ + continue; /* skip normal handling in sequel. */ \ + } \ + } + +const int ARRAY_PREFILL_AMOUNT = 7; + // a random default for pre-filling. + +#define ADD_LINE_TO_FIELDS(new_line) { \ + storage_slot++; /* move to next place to store item. */ \ + /* make sure we have enough space for the next slot and then some. */ \ +/*LOG(a_sprintf("fields curr=%d stowslot=%d", fields.length(), storage_slot));*/ \ + if (fields.length() < storage_slot + 2) \ + fields.insert(fields.length(), ARRAY_PREFILL_AMOUNT); \ +/*LOG(a_sprintf("now fields=%d stowslot=%d", fields.length(), storage_slot));*/ \ + fields[storage_slot] = new_line; \ +} + +//hmmm: parameterize what is meant by a quote. maybe comma too. +//by chris koeritz. +bool list_parsing::parse_csv_line(const astring &to_parse, string_array &fields) +{ + FUNCDEF("parse_csv_line"); + // the current field we're chowing. we puff it out to start with to + // avoid paying for expanding its memory later. + astring accumulator(' ', 256); + accumulator = astring::empty_string(); + + // the state machine goes through these states until the entire string + // is consumed. + enum states { seeking_quote, eating_string, seeking_comma }; + states state = seeking_quote; + + bool no_second_quote = false; // true if we started without a quote. + bool just_saw_comma = false; // true if seeking comma was the last state. + + int storage_slot = -1; + + for (int i = 0; i < to_parse.length(); i++) { + switch (state) { + case seeking_quote: + if (parser_bits::white_space(to_parse[i])) continue; + if (to_parse[i] == ',') { + // a missing quoted string counts as an empty string. + ADD_LINE_TO_FIELDS(astring::empty_string()); + just_saw_comma = true; + continue; + } + just_saw_comma = false; // cancel that state. + if (to_parse[i] != '"') { + // short circuit the need for a quote. + accumulator += to_parse[i]; + no_second_quote = true; + } + state = eating_string; + break; + case eating_string: + just_saw_comma = false; // no longer true. + if (no_second_quote && (to_parse[i] != ',') ) { + handle_escapes; + accumulator += to_parse[i]; + } else if (!no_second_quote && (to_parse[i] != '"') ) { + handle_escapes; + accumulator += to_parse[i]; + } else { + // we found the closing quote (or comma). add the string. + if (no_second_quote) { + state = seeking_quote; + just_saw_comma = true; + } else state = seeking_comma; + ADD_LINE_TO_FIELDS(accumulator) + accumulator = astring::empty_string(); + no_second_quote = false; + } + break; + case seeking_comma: + if (parser_bits::white_space(to_parse[i])) continue; + if (to_parse[i] == ',') { + // we got what we wanted. + state = seeking_quote; + just_saw_comma = true; + continue; + } + // well, there was no comma. that's an error. + return false; + break; + default: { + LOG("erroneous state reached during csv parsing"); + break; + } + } + } + if ( (state == eating_string) && (accumulator.length()) ) + ADD_LINE_TO_FIELDS(accumulator) + else if (just_saw_comma) + ADD_LINE_TO_FIELDS(astring::empty_string()) + if (fields.length() > storage_slot + 1) + fields.zap(storage_slot + 1, fields.last()); + return true; +} + +} //namespace. + + diff --git a/core/library/textual/list_parsing.h b/core/library/textual/list_parsing.h new file mode 100644 index 00000000..0a2c9292 --- /dev/null +++ b/core/library/textual/list_parsing.h @@ -0,0 +1,72 @@ +#ifndef LIST_PARSING_CLASS +#define LIST_PARSING_CLASS + +/*****************************************************************************\ +* * +* Name : list_parsing * +* Author : Chris Koeritz * +* Author : Gary Hardley * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include + +namespace textual { + +//! A set of functions that help out with parsing lists of things. + +class list_parsing +{ +public: + virtual ~list_parsing(); + DEFINE_CLASS_NAME("list_parsing"); + + static bool get_ids_from_string(const basis::astring &string, structures::int_set &ids); + //!< returns true if a set of unique ids can be extracted from "string". + /*!< valid separators are spaces, commas, hyphens. note that this + returns an int_set although the integers will all be non-negative. + e.g. "1-4,5 6 7,30-25" is a valid string. */ + + static bool get_ids_from_string(const basis::astring &string, basis::int_array &ids); + //!< same as above except result is an array -- to preserve order. + /*!< this also retains duplicates. */ + + static basis::astring put_ids_in_string(const structures::int_set &ids, char separator = ','); + //!< returns a string containing a "separator" separated list of ids. + /*!< spaces are also used between entries for readability. */ + static basis::astring put_ids_in_string(const basis::int_array &ids, char separator = ','); + //!< operates on an array instead of a set. + + static basis::astring emit_quoted_chunk(const basis::astring &to_emit); + //!< ensures that quotes inside the string "to_emit" are escaped. + + static bool parse_csv_line(const basis::astring &to_parse, structures::string_array &fields); + //!< examines the string "to_parse" which should be in csv format. + /*!< the "fields" list is set to the entries found on the line. true is + returned if the line parsed without any errors. this method will accept + a backslash as an escape character if it is immediately followed by a + quote character or another backslash character. no other escapes are + currently supported; backslashes will be taken literally otherwise. */ + + static void create_csv_line(const structures::string_array &to_csv, basis::astring &target); + static void create_csv_line(const structures::string_table &to_csv, basis::astring &target); + //!< writes a CSV line of text into the "target" from the items in "to_csv". + /*!< the "target" is reset before the line is stored there; thus, this is + not cumulative. further, the end of line character is not appended. this + will escape quote and backslash characters with a prepended backslash. */ +}; + +} //namespace. + +#endif + diff --git a/core/library/textual/makefile b/core/library/textual/makefile new file mode 100644 index 00000000..3b7d9199 --- /dev/null +++ b/core/library/textual/makefile @@ -0,0 +1,10 @@ +include cpp/variables.def + +PROJECT = textual +TYPE = library +SOURCE = byte_formatter.cpp list_parsing.cpp parser_bits.cpp string_manipulation.cpp \ + xml_generator.cpp xml_parser.cpp +TARGETS = textual.lib + +include cpp/rules.def + diff --git a/core/library/textual/parser_bits.cpp b/core/library/textual/parser_bits.cpp new file mode 100644 index 00000000..0bf33179 --- /dev/null +++ b/core/library/textual/parser_bits.cpp @@ -0,0 +1,223 @@ +/*****************************************************************************\ +* * +* Name : parser_bits * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "parser_bits.h" + +#include +#include +#include + +#include +#include + +using namespace basis; + +#undef LOG +#define LOG(prf) printf("%s\n", basis::astring(prf).s()) + +namespace textual { + +parser_bits::line_ending parser_bits::platform_eol() +{ +#ifdef __UNIX__ + // obviously a unix OS, unless someone's playing games with us. + return LF_AT_END; +#elif defined(__WIN32__) + // smells like DOS. + return CRLF_AT_END; +#else + // pick the unix default if we can't tell. + return LF_AT_END; +#endif +} + +const char *parser_bits::eol_to_chars(line_ending end) +{ + static const char *CRLF_AT_END_STRING = "\r\n"; + static const char *LF_AT_END_STRING = "\n"; + static const char *NO_ENDING_STRING = ""; + + switch (end) { + case CRLF_AT_END: return CRLF_AT_END_STRING; + case NO_ENDING: return NO_ENDING_STRING; + case LF_AT_END: // fall-through to default. + default: return LF_AT_END_STRING; + } +} + +const char *parser_bits::platform_eol_to_chars() +{ return eol_to_chars(platform_eol()); } + +bool parser_bits::is_printable_ascii(char to_check) +{ return (to_check >= 32) && (to_check <= 126); } + +bool parser_bits::white_space_no_cr(char to_check) +{ return (to_check == ' ') || (to_check == '\t'); } + +bool parser_bits::is_eol(char to_check) +{ return (to_check == '\n') || (to_check == '\r'); } + +bool parser_bits::white_space(char to_check) +{ return white_space_no_cr(to_check) || is_eol(to_check); } + +void parser_bits::translate_CR_for_platform(astring &to_translate) +{ + line_ending plat_eol = platform_eol(); + bool last_was_lf = false; + for (int i = 0; i <= to_translate.end(); i++) { + if (to_translate[i] == '\r') { + if (last_was_lf) continue; // ignore two in a row. + last_was_lf = true; + } else if (to_translate[i] == '\n') { + if (last_was_lf) { + if (plat_eol != CRLF_AT_END) { + // fix it, since there was not supposed to be an LF. + to_translate.zap(i - 1, i - 1); + i--; + } + } else { + if (plat_eol == CRLF_AT_END) { + // fix it, since we're missing an LF that we want. + to_translate.insert(i, "\r"); + i++; + } + } + last_was_lf = false; + } else { + // not the two power characters. + last_was_lf = false; + } + } +} + +bool parser_bits::is_hexadecimal(char look_at) +{ + return range_check(look_at, 'a', 'f') + || range_check(look_at, 'A', 'F') + || range_check(look_at, '0', '9'); +} + +bool parser_bits::is_hexadecimal(const char *look_at, int len) +{ + for (int i = 0; i < len; i++) + if (!is_hexadecimal(look_at[i])) return false; + return true; +} + +bool parser_bits::is_hexadecimal(const astring &look_at, int len) +{ return is_hexadecimal(look_at.observe(), len); } + +bool parser_bits::is_alphanumeric(char look_at) +{ + return range_check(look_at, 'a', 'z') + || range_check(look_at, 'A', 'Z') + || range_check(look_at, '0', '9'); +} + +bool parser_bits::is_alphanumeric(const char *look_at, int len) +{ + for (int i = 0; i < len; i++) + if (!is_alphanumeric(look_at[i])) return false; + return true; +} + +bool parser_bits::is_alphanumeric(const astring &look_at, int len) +{ return is_alphanumeric(look_at.observe(), len); } + +bool parser_bits::is_identifier(char look_at) +{ + return range_check(look_at, 'a', 'z') + || range_check(look_at, 'A', 'Z') + || range_check(look_at, '0', '9') + || (look_at == '_'); +} + +bool parser_bits::is_identifier(const char *look_at, int len) +{ + if (is_numeric(look_at[0])) return false; + for (int i = 0; i < len; i++) + if (!is_identifier(look_at[i])) return false; + return true; +} + +bool parser_bits::is_identifier(const astring &look_at, int len) +{ return is_identifier(look_at.observe(), len); } + +bool parser_bits::is_numeric(char look_at) +{ + return range_check(look_at, '0', '9') || (look_at == '-'); +} + +bool parser_bits::is_numeric(const char *look_at, int len) +{ + for (int i = 0; i < len; i++) { + if (!is_numeric(look_at[i])) return false; + if ( (i > 0) && (look_at[i] == '-') ) return false; + } + return true; +} + +bool parser_bits::is_numeric(const astring &look_at, int len) +{ return is_numeric(look_at.observe(), len); } + +astring parser_bits::substitute_env_vars(const astring &to_process, + bool leave_unknown) +{ + astring editing = to_process; + +//LOG(astring("input to subst env: ") + to_process); + + int indy; // index of the dollar sign in the string. + while (true) { + indy = editing.find('$'); + if (negative(indy)) break; // all done. + int q; + for (q = indy + 1; q < editing.length(); q++) { + if (!parser_bits::is_identifier(editing[q])) + break; // done getting variable name. + } + if (q != indy + 1) { + // we caught something in our environment variable trap... + astring var_name = editing.substring(indy + 1, q - 1); +//LOG(astring("var name ") + var_name); + astring value_found = environment::get(var_name); +//LOG(astring("val found ") + value_found); + if (value_found.t()) { + editing.zap(indy, q - 1); + editing.insert(indy, value_found); + } else { + if (leave_unknown) { + // that lookup failed. let's mark it. + editing[indy] = '?'; + // simple replacement, shows variables that failed. + } else { + // replace it with blankness. + editing.zap(indy, q - 1); + } + } + } else { + // well, we didn't see a valid variable name, but we don't want to leave + // the dollar sign in there. + editing[indy] = '!'; // simple replacement, marks where syntax is bad. + } + + } + +//LOG(astring("output from subst env: ") + editing); + + return editing; +} + +} //namespace. + diff --git a/core/library/textual/parser_bits.h b/core/library/textual/parser_bits.h new file mode 100644 index 00000000..6ca2a21d --- /dev/null +++ b/core/library/textual/parser_bits.h @@ -0,0 +1,124 @@ +#ifndef PARSER_BITS_CLASS +#define PARSER_BITS_CLASS + +/*****************************************************************************\ +* * +* Name : parser_bits * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace textual { + +//! Warehouses some functions that are often useful during text parsing. + +class parser_bits +{ +public: + //! Line endings is an enumeration of the separator character(s) used for text files. + /*! on unix, every line in a text file has a line feed (LF) character appended to the line. + on ms-dos and ms-windows, each line has a carriage return (CR) and line feed (LF) appended + instead. a synonym for the line_ending is "eol" which stands for "end of line". */ + enum line_ending { + LF_AT_END = -15, //!< Unix standard is LF_AT_END ("\n"). + CRLF_AT_END, //!< DOS standard is CRLF_AT_END ("\r\n"). + NO_ENDING //!< No additional characters added as line endings. + }; + + static const char *eol_to_chars(line_ending ending); + //!< returns the C string form for the "ending" value. + + static line_ending platform_eol(); + //!< provides the appropriate ending on the current OS platform. + + static const char *platform_eol_to_chars(); + //!< provides the characters that make up this platform's line ending. + + static void translate_CR_for_platform(basis::astring &to_translate); + //!< flips embedded EOL characters for this platform's needs. + /*!< runs through the string "to_translate" and changes any CR or CRLF + combinations into the EOL (end-of-line) character that's appropriate + for this operating system. */ + + static basis::astring substitute_env_vars(const basis::astring &text, + bool leave_unknown = true); + //!< resolves embedded environment variables in "text". + /*!< replaces the names of any environment variables in "text" with the + variable's value and returns the resulting string. the variable names + are marked by a single dollar before an alphanumeric identifier + (underscores are valid), for example: $PATH if the "leave_unknown" flag + is true, then any unmatched variables are left in the text with a question + mark instead of a dollar sign. if it's false, then they are simply + replaced with nothing at all. */ + + static bool is_printable_ascii(char to_check); + //!< returns true if "to_check" is a normally visible ASCII character. + /*!< this is defined very simply by it being within the range of 32 to + 126. that entire range should be printable in ASCII. before 32 we have + control characters. after 126 we have potentially freakish looking + characters. this is obviously not appropriate for utf-8 or unicode. */ + + static bool white_space_no_cr(char to_check); + //!< reports if "to_check" is white space but not a carriage return. + /*!< returns true if the character "to_check" is considered a white space, + but is not part of an end of line combo (both '\n' and '\r' are + disallowed). the allowed set includes tab ('\t') and space (' ') only. */ + + static bool is_eol(char to_check); + //!< returns true if "to_check" is part of an end-of-line sequence. + /*!< this returns true for both the '\r' and '\n' characters. */ + + static bool white_space(char to_check); + //!< returns true if the character "to_check" is considered a white space. + /*!< this set includes tab ('\t'), space (' '), carriage return ('\n'), + and line feed ('\r'). */ + + static bool is_alphanumeric(char look_at); + //!< returns true if "look_at" is one of the alphanumeric characters. + /*!< This includes a to z in either case and 0 to 9. */ + static bool is_alphanumeric(const char *look_at, int len); + //!< returns true if the char ptr "look_at" is all alphanumeric characters. + static bool is_alphanumeric(const basis::astring &look_at, int len); + //!< returns true if the string "look_at" is all alphanumeric characters. + + static bool is_numeric(char look_at); + //!< returns true if "look_at" is a valid numerical character. + /*! this allows the '-' character for negative numbers also (but only for + first character if the char* or astring versions are used). does not + support floating point numbers or exponential notation yet. */ + static bool is_numeric(const char *look_at, int len); + //!< returns true if "look_at" is all valid numerical characters. + static bool is_numeric(const basis::astring &look_at, int len); + //!< returns true if the "look_at" string has only valid numerical chars. + + static bool is_hexadecimal(char look_at); + //!< returns true if "look_at" is one of the hexadecimal characters. + /*!< This includes a to f in either case and 0 to 9. */ + static bool is_hexadecimal(const char *look_at, int len); + //!< returns true if "look_at" is all hexadecimal characters. + static bool is_hexadecimal(const basis::astring &look_at, int len); + //!< returns true if the string "look_at" is all hexadecimal characters. + + static bool is_identifier(char look_at); + //!< returns true if "look_at" is a valid identifier character. + /*!< this just allows alphanumeric characters and underscore. */ + static bool is_identifier(const char *look_at, int len); + //!< returns true if "look_at" is composed of valid identifier character. + /*!< additionally, identifiers cannot start with a number. */ + static bool is_identifier(const basis::astring &look_at, int len); + //!< like is_identifier() above but operates on a string. +}; + +} //namespace. + +#endif + diff --git a/core/library/textual/string_convert.h b/core/library/textual/string_convert.h new file mode 100644 index 00000000..697439e7 --- /dev/null +++ b/core/library/textual/string_convert.h @@ -0,0 +1,70 @@ +#ifndef STRING_CONVERSION_GROUP +#define STRING_CONVERSION_GROUP + +/*****************************************************************************\ +* * +* Name : string_convert * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2007-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +#ifdef __WIN32__ + #ifndef _MANAGED + #ifndef __MINGW32__ + #define _WINSOCKAPI_ // the dance of the windows headers. + #include + #include + #endif + #endif +#endif + +// forward. +class _bstr_t; // ATL (Active Template Library) string type. + +//! A collection of conversions between popular string types. + +namespace string_convert +{ + +#ifdef _AFXDLL + //! conversion from MFC CString to astring. + inline astring to_astring(const CString &original) + { return astring(from_unicode_temp(original)); } + + //! conversion from astring to MFC CString. + inline CString to_CString(const astring &original) + { return CString(to_unicode_temp(original)); } +#endif + +#ifdef WIN32 + #ifndef _MANAGED + #ifndef __MINGW32__ + //! conversion from ATL's _bstr_t object to astring. + inline basis::astring to_astring(const _bstr_t &original) { + return basis::astring(basis::astring::UNTERMINATED, (const char *)original, + original.length()); + } + + //! conversion from astring to the ATL _bstr_t object. + inline _bstr_t to_bstr_t(const basis::astring &original) + { return _bstr_t(original.s()); } + #endif + #endif +#endif + +//other conversions. + +} //namespace + +#endif + diff --git a/core/library/textual/string_manipulation.cpp b/core/library/textual/string_manipulation.cpp new file mode 100644 index 00000000..6e21691a --- /dev/null +++ b/core/library/textual/string_manipulation.cpp @@ -0,0 +1,351 @@ +/*****************************************************************************\ +* * +* Name : string_manipulation * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "parser_bits.h" +#include "string_manipulation.h" + +#include +#include +#include +#include + +using namespace basis; +using namespace mathematics; + +namespace textual { + +//SAFE_STATIC_CONST(astring_object, string_manipulation::splitter_finding_set, +// ("\t\r\n -,;?!.:")) +const char *splitter_finding_set = "\t\r\n -,;?!.:"; + // any of these characters make a valid place to break a line. + +astring string_manipulation::make_random_name(int min, int max) +{ + chaos rando; + int length = rando.inclusive(min, max); + // pick a size for the string. + astring to_return; + for (int i = 0; i < length; i++) { + int chah = rando.inclusive(0, 26); + // use a range one larger than alphabet size. + char to_add = 'a' + chah; + if (chah == 26) to_add = '_'; + // patch the extra value to be a separator. + to_return += to_add; + } + return to_return; +} + +astring string_manipulation::long_line(char line_item, int repeat) +{ return astring(line_item, repeat); } + +astring string_manipulation::indentation(int spaces) +{ + astring s; + for (int i = 0; i < spaces; i++) s += ' '; + return s; +} + +void string_manipulation::carriage_returns_to_spaces(astring &to_strip) +{ + for (int j = 0; j < to_strip.length(); j++) { + int original_j = j; // track where we started looking. + if (!parser_bits::is_eol(to_strip[j])) continue; + // we have found at least one CR. let's see what else there is. + if ( (to_strip[j] == '\r') && (to_strip[j + 1] == '\n') ) { + // this is looking like a DOS CR. let's skip that now. + j++; + } + j++; // skip the one we know is a CR. + if (parser_bits::is_eol(to_strip[j])) { + // we are seeing more than one carriage return in a row. let's + // truncate that down to just one. + j++; + while (parser_bits::is_eol(to_strip[j]) && (j < to_strip.length())) + j++; // skip to next one that might not be CR. + // now we think we know where there's this huge line of CRs. we will + // turn them all into spaces except the first. + to_strip[original_j] = '\n'; + for (int k = original_j + 1; k < j; k++) to_strip[k] = ' '; + // put the index back so we'll start looking at the non-CR char. + j--; + continue; // now skip back out to the main loop. + } else { + // we see only one carriage return, which we will drop in favor of + // joining those lines together. we iterate here since we might have + // seen a DOS CR taking up two spaces. + for (int k = original_j; k < j; k++) to_strip[k] = ' '; + } + } + +} + +void string_manipulation::split_lines(const astring &input_in, astring &output, + int min_column, int max_column) +{ + output = ""; + if (max_column - min_column + 1 < 2) return; // what's the point? + + astring input = input_in; // make a copy to work on. + carriage_returns_to_spaces(input); + + int col = min_column; + astring indent_add = indentation(min_column); + output = indent_add; // start with the extra space. + + bool just_had_break = false; + // set true if we just handled a line break in the previous loop. + bool put_accum_before_break = false; // true if we must postpone CR. + astring accumulated; + // holds stuff to print on next go-round. + + // now we parse across the list counting up our line size and making sure + // we don't go over it. + for (int j = 0; j < input.length(); j++) { + +//char to_print = input[j]; +//if (parser_bits::is_eol(to_print)) to_print = '_'; +//printf("[%d: val=%d, '%c', col=%d]\n", j, to_print, to_print, col); +//fflush(0); + + // handle the carriage return if it was ordered. + if (just_had_break) { + if (put_accum_before_break) { + output += accumulated; + // strip off any spaces from the end of the line. + output.strip_spaces(astring::FROM_END); + output += parser_bits::platform_eol_to_chars(); + accumulated = ""; + j++; // skip the CR that we think is there. + } + // strip off any spaces from the end of the line. + output.strip_spaces(astring::FROM_END); + output += parser_bits::platform_eol_to_chars(); + col = min_column; + output += indent_add; + just_had_break = false; + if (accumulated.length()) { + output += accumulated; + col += accumulated.length(); + accumulated = ""; + } + j--; + continue; + } + + put_accum_before_break = false; + + // skip any spaces we've got at the current position. + while ( (input[j] == ' ') || (input[j] == '\t') ) { + j++; + if (j >= input.length()) break; // break out of subloop if past it. + } + + if (j >= input.length()) break; // we're past the end. + + // handle carriage returns when they're at the current position. + char current_char = input[j]; + if (parser_bits::is_eol(current_char)) { + just_had_break = true; // set the state. + put_accum_before_break = true; + continue; + } + +//hmmm: the portion below could be called a find word break function. + + bool add_dash = false; // true if we need to break a word and add hyphen. + bool break_line = false; // true if we need to go to the next line. + bool invisible = false; // true if invisible characters were seen. + bool end_sentence = false; // true if there was a sentence terminator. + bool punctuate = false; // true if there was normal punctuation. + bool keep_on_line = false; // true if we want add current then break line. + char prior_break = '\0'; // set for real below. + char prior_break_plus_1 = '\0'; // ditto. + + // find where our next normal word break is, if possible. + int next_break = input.find_any(splitter_finding_set, j); + // if we didn't find a separator, just use the end of the string. + if (negative(next_break)) + next_break = input.length() - 1; + + // now we know where we're supposed to break, but we don't know if it + // will all fit. + prior_break = input[next_break]; + // hang onto the value before we change next_break. + prior_break_plus_1 = input[next_break + 1]; + // should still be safe since we're stopping before the last zero. + switch (prior_break) { + case '\r': case '\n': + break_line = true; + just_had_break = true; + put_accum_before_break = true; + // intentional fall-through. + case '\t': case ' ': + invisible = true; + next_break--; // don't include it in what's printed. + break; + case '?': case '!': case '.': + end_sentence = true; + // if we see multiples of these, we count them as just one. + while ( (input[next_break + 1] == '?') + || (input[next_break + 1] == '!') + || (input[next_break + 1] == '.') ) { + next_break++; + } + // make sure that there's a blank area after the supposed punctuation. + if (!parser_bits::white_space(input[next_break + 1])) + end_sentence = false; + break; + case ',': case ';': case ':': + punctuate = true; + // make sure that there's a blank area after the supposed punctuation. + if (!parser_bits::white_space(input[next_break + 1])) + punctuate = false; + break; + } + + // we'll need to add some spaces for certain punctuation. + int punct_adder = 0; + if (punctuate || invisible) punct_adder = 1; + if (end_sentence) punct_adder = 2; + + // check that we're still in bounds. + int chars_added = next_break - j + 1; + if (col + chars_added + punct_adder > max_column + 1) { + // we need to break before the next breakable character. + break_line = true; + just_had_break = true; + if (col + chars_added <= max_column + 1) { + // it will fit without the punctuation spaces, which is fine since + // it should be the end of the line. + invisible = false; + punctuate = false; + end_sentence = false; + punct_adder = 0; + keep_on_line = true; + } else if (min_column + chars_added > max_column + 1) { + // this word won't ever fit unless we break it. + int chars_left = max_column - col + 1; + // remember to take out room for the dash also. + if (chars_left < 2) { + j--; // stay where we are. + continue; + } else { + next_break = j + chars_left - 2; + chars_added = next_break - j + 1; + if (next_break >= input.length()) + next_break = input.length() - 1; + else if (next_break < j) + next_break = j; + add_dash = true; + } + } + } + + astring adding_chunk = input.substring(j, next_break); + // this is what we've decided the next word chunk to be added will be. + // we still haven't completely decided where it goes. + + if (break_line) { + col = min_column; + if (add_dash || keep_on_line) { + // include the previous stuff on the same line. + output += adding_chunk; + if (add_dash) output += "-"; + j = next_break; + continue; // done with this case. + } + + // don't include the previous stuff; make it go to the next line. + accumulated = adding_chunk; + if (punctuate || invisible) { + accumulated += " "; + } else if (end_sentence) { + accumulated += " "; + } + j = next_break; + continue; + } + + // add the line normally since it should fit. + output += adding_chunk; + col += chars_added + punct_adder; // add the characters added. + j = next_break; + just_had_break = false; // reset the state. + + // handle when we processed an invisible or punctuation character. + if (punctuate || invisible) { + output += " "; + } else if (end_sentence) { + output += " "; + } + } + // make sure we handle any leftovers. + if (accumulated.length()) { + output.strip_spaces(astring::FROM_END); + output += parser_bits::platform_eol_to_chars(); + output += indent_add; + output += accumulated; + } + output.strip_spaces(astring::FROM_END); + output += parser_bits::platform_eol_to_chars(); +} + +char string_manipulation::hex_to_char(abyte to_convert) +{ + if (to_convert <= 9) return char('0' + to_convert); + else if ( (to_convert >= 10) && (to_convert <= 15) ) + return char('A' - 10 + to_convert); + else return '?'; +} + +abyte string_manipulation::char_to_hex(char to_convert) +{ + if ( (to_convert >= '0') && (to_convert <= '9') ) + return char(to_convert - '0'); + else if ( (to_convert >= 'a') && (to_convert <= 'f') ) + return char(to_convert - 'a' + 10); + else if ( (to_convert >= 'A') && (to_convert <= 'F') ) + return char(to_convert - 'A' + 10); + else return 0; +} + +byte_array string_manipulation::string_to_hex(const astring &to_convert) +{ + byte_array to_return(0, NIL); + for (int i = 0; i < to_convert.length() / 2; i++) { + int str_index = i * 2; + abyte first_byte = char_to_hex(to_convert.get(str_index)); + abyte second_byte = char_to_hex(to_convert.get(str_index + 1)); + abyte to_stuff = abyte(first_byte * 16 + second_byte); + to_return.concatenate(to_stuff); + } + return to_return; +} + +astring string_manipulation::hex_to_string(const byte_array &to_convert) +{ + astring to_return; + for (int i = 0; i < to_convert.length() * 2; i += 2) { + int str_index = i / 2; + char first_char = hex_to_char(char(to_convert.get(str_index) / 16)); + char second_char = hex_to_char(char(to_convert.get(str_index) % 16)); + to_return += astring(first_char, 1); + to_return += astring(second_char, 1); + } + return to_return; +} + +} //namespace. + diff --git a/core/library/textual/string_manipulation.h b/core/library/textual/string_manipulation.h new file mode 100644 index 00000000..c240f0fb --- /dev/null +++ b/core/library/textual/string_manipulation.h @@ -0,0 +1,88 @@ +#ifndef STRING_MANIPULATION_CLASS +#define STRING_MANIPULATION_CLASS + +/*****************************************************************************\ +* * +* Name : string_manipulation * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace textual { + +//! Provides various functions for massaging strings. + +class string_manipulation +{ +public: + +////////////// + + static basis::astring make_random_name(int min = 1, int max = 64); + //!< creates a random name, where the letters are between 'a' and 'z'. + /*!< the underscore will also be used occasionally. the size is random, + but the minimum size is "min" while the maximum is "max". */ + +////////////// + + static basis::astring long_line(char line_item = '/', int repeat = 76); + //!< produces a long line of "line_item" characters. + /*!< returns a string of text that is somewhat long compared to an 80 column + output window and which consists of a single character repeated. the + character used and the repeat count are both variable. */ + + static basis::astring indentation(int spaces); + //!< Returns a string made of white space that is "spaces" long. + +////////////// + + static void carriage_returns_to_spaces(basis::astring &to_strip); + //!< converts carriage returns in "to_strip" into spaces. + /*!< processes the string "to_strip" by replacing all single carriage + returns with spaces and by turning two or more carriage returns into a + single CR plus spaces. */ + +////////////// + + static void split_lines(const basis::astring &input, basis::astring &output, + int min_column = 0, int max_column = 79); + //!< formats blocks of text for a maximum width. + /*!< processes the "input" text by splitting any lines that are longer + than the "max_column". the "min_column" is used to specify how much + indentation should be included. */ + +////////////// + + // numerical manipulation functions: + + static basis::abyte char_to_hex(char to_convert); + //!< Converts a single character into the corresponding hex nibble. + /*!< If the character is not valid, an arbitrary value is returned. */ + static char hex_to_char(basis::abyte to_convert); + //!< Converts a byte between 0 and 15 into a corresponding hexadecimal character. + + static basis::byte_array string_to_hex(const basis::astring &character_form); + //!< Turns a string form of a set of hex numbers into an array of bytes. + /*!< This functions takes a string in "character_form" and returns an array + of bytes that is half as long and which contains the hexadecimal + interpretation of the string. This is currently geared to even length + strings... */ + static basis::astring hex_to_string(const basis::byte_array &byte_form); + //!< The inverse of string_to_hex prints "byte_form" as text. + /*!< This function takes an array of bytes and converts them into their + equivalent hexadecimal character representation. */ +}; + +} //namespace. + +#endif + diff --git a/core/library/textual/xml_generator.cpp b/core/library/textual/xml_generator.cpp new file mode 100644 index 00000000..ec7645cf --- /dev/null +++ b/core/library/textual/xml_generator.cpp @@ -0,0 +1,261 @@ +/*****************************************************************************\ +* * +* Name : xml_generator * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2007-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "parser_bits.h" +#include "string_manipulation.h" +#include "xml_generator.h" + +#include +#include +#include + +using namespace basis; +using namespace structures; + +namespace textual { + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s); + +////////////// + +class tag_info +{ +public: + astring _tag_name; + string_table _attribs; + + tag_info() {} + + tag_info(const astring &tag_name, const string_table &attribs) + : _tag_name(tag_name), _attribs(attribs) {} +}; + +////////////// + +class tag_stack : public stack +{ +public: + tag_stack() : stack(0) {} +}; + +////////////// + +xml_generator::xml_generator(int mods) +: _tags(new tag_stack), + _accumulator(new astring), + _human_read(mods & HUMAN_READABLE), + _clean_chars(mods & CLEAN_ILLEGAL_CHARS), + _indentation(2) +{ +} + +xml_generator::~xml_generator() +{ + WHACK(_tags); + WHACK(_accumulator); +} + +const char *xml_generator::outcome_name(const outcome &to_name) +{ + switch (to_name.value()) { + case ERRONEOUS_TAG: return "ERRONEOUS_TAG"; + default: return common::outcome_name(to_name); + } +} + +void xml_generator::set_indentation(int to_indent) +{ + if (to_indent <= 0) to_indent = 1; + _indentation = to_indent; +} + +void xml_generator::reset() +{ + _accumulator->reset(); +// we need a reset on stack. + while (_tags->pop() == common::OKAY) {} +} + +astring xml_generator::generate() +{ + astring to_return; + generate(to_return); + return to_return; +} + +void xml_generator::generate(astring &generated) +{ + close_all_tags(); + generated = ""; // first string is the version. + if (_human_read) generated += parser_bits::platform_eol_to_chars(); + generated += *_accumulator; +} + +outcome xml_generator::open_tag(const astring &tag_name) +{ + string_table junk; + return open_tag(tag_name, junk); +} + +outcome xml_generator::add_header(const astring &tag_name, + const string_table &attributes) +{ + tag_info new_item(tag_name, attributes); + print_open_tag(new_item, HEADER_TAG); + return OKAY; +} + +outcome xml_generator::open_tag(const astring &tag_name, const string_table &attributes) +{ + tag_info new_item(tag_name, attributes); + print_open_tag(new_item); + _tags->push(new_item); + return OKAY; +} + +outcome xml_generator::close_tag(const astring &tag_name) +{ + if (_tags->elements() < 1) return NOT_FOUND; + // check to see that it's the right one to close. + if (_tags->top()._tag_name != tag_name) return ERRONEOUS_TAG; + print_close_tag(tag_name); + _tags->pop(); + return OKAY; +} + +void xml_generator::close_all_tags() +{ + while (_tags->elements()) { + close_tag(_tags->top()._tag_name); + } +} + +outcome xml_generator::add_content(const astring &content) +{ + if (_human_read) { + astring indentata = string_manipulation::indentation(_indentation); + int num_indents = _tags->elements(); + for (int i = 0; i < num_indents; i++) + *_accumulator += indentata; + } + if (_clean_chars) + *_accumulator += clean_reserved(content); + else + *_accumulator += content; + if (_human_read) *_accumulator += parser_bits::platform_eol_to_chars(); + return OKAY; +} + +void xml_generator::print_open_tag(const tag_info &to_print, int type) +{ + bool is_header = false; + if (type == HEADER_TAG) is_header = true; + + if (_human_read) { +//hmmm: starting to look like a nice macro for this stuff, param is num levs. + astring indentata = string_manipulation::indentation(_indentation); + int num_indents = _tags->elements(); + for (int i = 0; i < num_indents; i++) + *_accumulator += indentata; + } + + if (is_header) + *_accumulator += ""; + else + *_accumulator += ">"; + if (_human_read) *_accumulator += parser_bits::platform_eol_to_chars(); +} + +void xml_generator::print_close_tag(const astring &tag_name) +{ + if (_human_read) { + astring indentata = string_manipulation::indentation(_indentation); + int num_indents = _tags->elements() - 1; + for (int i = 0; i < num_indents; i++) + *_accumulator += indentata; + } + *_accumulator += ""; + if (_human_read) *_accumulator += parser_bits::platform_eol_to_chars(); +} + +#define PLUGIN_REPLACEMENT(posn, repl_string) { \ + to_modify.zap(posn, posn); \ + to_modify.insert(posn, repl_string); \ + posn += int(strlen(repl_string)) - 1; \ +} + +void xml_generator::clean_reserved_mod(astring &to_modify, + bool replace_spaces) +{ +//could this set live somewhere? + const char *quot = """; + const char *amp = "&"; + const char *lt = "<"; + const char *gt = ">"; + const char *apos = "'"; + const char *space = "_"; + // was going to use %20 but that still won't parse in an attribute name. + + for (int i = 0; i < to_modify.length(); i++) { + switch (to_modify[i]) { + case '"': PLUGIN_REPLACEMENT(i, quot); break; + case '&': PLUGIN_REPLACEMENT(i, amp); break; + case '<': PLUGIN_REPLACEMENT(i, lt); break; + case '>': PLUGIN_REPLACEMENT(i, gt); break; + case '\'': PLUGIN_REPLACEMENT(i, apos); break; + case ' ': if (replace_spaces) PLUGIN_REPLACEMENT(i, space); break; + default: continue; + } + } +} + +astring xml_generator::clean_reserved(const astring &to_modify, + bool replace_spaces) +{ + astring to_return = to_modify; + clean_reserved_mod(to_return, replace_spaces); + return to_return; +} + +} //namespace. + + diff --git a/core/library/textual/xml_generator.h b/core/library/textual/xml_generator.h new file mode 100644 index 00000000..0b3901a6 --- /dev/null +++ b/core/library/textual/xml_generator.h @@ -0,0 +1,129 @@ +#ifndef XML_GENERATOR_CLASS +#define XML_GENERATOR_CLASS + +/*****************************************************************************\ +* * +* Name : xml_generator * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2007-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace textual { + +class tag_info; +class tag_stack; + +//! Supports simple XML output with consistency checking. + +class xml_generator +{ +public: + enum behavioral_mods { + HUMAN_READABLE = 0x1, + CLEAN_ILLEGAL_CHARS = 0x2 + }; + + xml_generator(int modifiers = HUMAN_READABLE | CLEAN_ILLEGAL_CHARS); + //!< creates an xml generator with the specified behavior. + + virtual ~xml_generator(); + + DEFINE_CLASS_NAME("xml_generator"); + + //! the possible ways that operations here can complete. + enum outcomes { + OKAY = basis::common::OKAY, + NOT_FOUND = basis::common::NOT_FOUND, + ERRONEOUS_TAG = basis::common::INVALID //temporary until we can shed a compatibility concern. +// DEF INE_OUTCOME(ERRONEOUS_TAG, -75, "The most recently opened tag must be " +// "closed before a new tag can be opened and before any other tag can " +// "be closed"), + }; + + static const char *outcome_name(const basis::outcome &to_name); + //!< reports the string version of "to_name". + + void reset(); //!< throws out all accumulated information. + + basis::astring generate(); + //!< writes the current state into a string and returns it. + /*!< if there was an error during generation, the string will be empty. + note that unclosed tags are not considered an error; they will simply be + closed. note that the accumulated string is not cleared after the + generate() invocation. use reset() to clear out all prior state. */ + + void generate(basis::astring &generated); + //!< synonym method, writes the current state into "generated". + + basis::outcome add_header(const basis::astring &tag_name, const structures::string_table &attributes); + //!< adds an xml style header with the "tag_name" and "attributes". + /*!< headers can be located anywhere in the file. */ + + basis::outcome open_tag(const basis::astring &tag_name, const structures::string_table &attributes); + //!< adds a tag with "tag_name" and the "attributes", if any. + /*!< this adds an item into the output string in the form: @code + @endcode + it is required that you close the tag later on, after the tag's contents + have been added. */ + + basis::outcome open_tag(const basis::astring &tag_name); + //!< adds a tag with "tag_name" without any attributes. + + basis::outcome close_tag(const basis::astring &tag_name); + //!< closes a previously added "tag_name". + /*!< this will generate xml code like so: @code + @endcode + note that it is an error to try to close any tag but the most recently + opened one. */ + + void close_all_tags(); + //!< a wide-bore method that closes all outstanding tags. + + basis::outcome add_content(const basis::astring &content); + //!< stores content into the currently opened tag. + /*!< it is an error to add content when no tag is open. */ + + void set_indentation(int to_indent); + //!< sets the number of spaces to indent for the human readable form. + + static basis::astring clean_reserved(const basis::astring &to_modify, + bool replace_spaces = false); + //!< returns a cleaned version of "to_modify" to make it XML appropriate. + /*!< if "replace_spaces" is true, then any spaces will be turned into + their html code equivalent; this helps in attribute names. */ + + static void clean_reserved_mod(basis::astring &to_modify, + bool replace_spaces = false); + //!< ensures that "to_modify" contains only characters valid for XML. + /*!< this is only different from the other clean method because this + one modifies the string in place. */ + +private: + tag_stack *_tags; //!< the already opened tags. + basis::astring *_accumulator; //!< stores our output. + bool _human_read; //!< true if the output should be human readable. + bool _clean_chars; //!< true if strings should be validated and repaired. + int _indentation; //!< number of spaces per level of xml. + + enum open_types { NORMAL_TAG, HEADER_TAG }; + void print_open_tag(const tag_info &to_print, int type = NORMAL_TAG); + //!< opens the tag for to_print by showing the tag name and attributes. + void print_close_tag(const basis::astring &tag_name); + //!< closes the tag for "tag_name" in the output string. +}; + +} //namespace. + +#endif + diff --git a/core/library/textual/xml_parser.cpp b/core/library/textual/xml_parser.cpp new file mode 100644 index 00000000..f20806db --- /dev/null +++ b/core/library/textual/xml_parser.cpp @@ -0,0 +1,127 @@ +/*****************************************************************************\ +* * +* Name : xml_parser * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2007-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "xml_parser.h" + +#include +#include + +using namespace basis; +using namespace structures; + +namespace textual { + +xml_parser::xml_parser(const astring &to_parse) +{ + if (!to_parse) {} +} + +xml_parser::~xml_parser() +{ +} + +const char *xml_parser::outcome_name(const outcome &to_name) +{ + return common::outcome_name(to_name); +} + +void xml_parser::reset(const astring &to_parse) +{ + if (!to_parse) {} +} + +outcome xml_parser::header_callback(astring &header_name, + string_table &attributes) +{ + if (!header_name || !attributes.symbols()) {} + return common::OKAY; +} + + +outcome xml_parser::tag_open_callback(astring &tag_name, + string_table &attributes) +{ + if (!tag_name || !attributes.symbols()) {} + return OKAY; +} + +outcome xml_parser::tag_close_callback(astring &tag_name) +{ + if (!tag_name) {} + return OKAY; +} + +outcome xml_parser::content_callback(astring &content) +{ + if (!content) {} + return OKAY; +} + +outcome xml_parser::parse() +{ + +//phases: we are initially always seeking a bracket bounded tag of some sort. + +// the first few constructs must be headers, especially the xml header. + +// is it true that headers are the only valid thing to see before real tags +// start, or can there be raw content embedded in there? +// yes, it seems to be true in mozilla. you can't have bare content in +// between the headers and the real tags. + +// actually, if we allow the file to not start with the xml header and +// version, then that's a bug. + +// need function to accumulate the tag based on structure. do headers +// have to have a ? as part of the inner and outer structure? + +// test against mozilla to ensure we are seeing the same things; get +// together some tasty sample files. + +// count lines and chars so we can report where it tanked. + +// back to phases, (not a precise grammar below) +// white_space ::= [ ' ' '\n' '\r' '\t' ] * +// ws ::= white_space +// text_phrase ::= not_white_space_nor_reserved not_reserved* +// name ::= text_phrase +// value ::= not_reserved * +// lt_char ::= '<' +// quote ::= '"' + +// xml_file ::= header+ ws tagged_unit+ ws +// header ::= '<' '?' name ws attrib_list ws '?' '>' ws +// tagged_unit ::= open_tag content* close_tag ws +// content ::= [ tagged_unit | text_phrase ] + ws +// open_tag ::= '<' name ws attrib_list ws '>' ws +// attrib_list ::= ( attribute ws ) * ws +// attribute ::= name ws '=' ws quoted_string ws +// quoted_string ::= '"' not_lt_char_nor_quote '"' ws +// close_tag :: '<' '/' name ws '>' ws + +//write a recursive descent parser on this grammar and spit out the +// productions as callbacks, at least for the major ones already listed. + +return common::NOT_IMPLEMENTED; +} + +/* callbacks to invoke. +outcome header_callback(astring &header_name, string_table &attributes) +outcome tag_open_callback(astring &tag_name, string_table &attributes) +outcome tag_close_callback(astring &tag_name) +outcome content_callback(astring &content) +*/ + +} //namespace. + diff --git a/core/library/textual/xml_parser.h b/core/library/textual/xml_parser.h new file mode 100644 index 00000000..7b2011d8 --- /dev/null +++ b/core/library/textual/xml_parser.h @@ -0,0 +1,82 @@ +#ifndef XML_PARSER_CLASS +#define XML_PARSER_CLASS + +/*****************************************************************************\ +* * +* Name : xml_parser * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2007-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +// forward. +#include +#include + +namespace textual { + +//! Parses XML input and invokes a callback for the different syntactic pieces. + +// hmmm, could this be the first class ever named this? perhaps it should be +// in a textual namespace. -->after current sprint. + +class xml_parser +{ +public: + xml_parser(const basis::astring &to_parse); + virtual ~xml_parser(); + + DEFINE_CLASS_NAME("xml_parser"); + + //! the possible ways that operations here can complete. + enum outcomes { + OKAY = basis::common::OKAY +//uhhh... + }; + + static const char *outcome_name(const basis::outcome &to_name); + //!< reports the string version of "to_name". + + void reset(const basis::astring &to_parse); + //!< throws out any accumulated information and uses "to_parse" instead. + + basis::outcome parse(); + //!< starts the parsing process on the current string. + /*!< this will cause callbacks to be invoked for each of the xml syntactic + elements. */ + + virtual basis::outcome header_callback(basis::astring &header_name, + structures::string_table &attributes); + //!< invoked when a well-formed xml header is seen in the input stream. + /*!< the following applies to all of the callbacks: the derived method must + return an outcome, which will be used by the parser. if the outcome is + OKAY, then parsing will continue. any other outcome will cause parsing + to stop and will become the return value of the parse() method. */ + + virtual basis::outcome tag_open_callback(basis::astring &tag_name, + structures::string_table &attributes); + //!< an xml tag has been opened in the input stream. + + virtual basis::outcome tag_close_callback(basis::astring &tag_name); + //!< an xml tag was closed in the input stream. + + virtual basis::outcome content_callback(basis::astring &content); + //!< invoked when plain text content is found inside an opened tag. + +private: + basis::astring *_xml_stream; // the stringful of xml information. + +}; + +} //namespace. + +#endif + diff --git a/core/library/timely/earth_time.cpp b/core/library/timely/earth_time.cpp new file mode 100644 index 00000000..ed7c4583 --- /dev/null +++ b/core/library/timely/earth_time.cpp @@ -0,0 +1,406 @@ +/*****************************************************************************\ +* * +* Name : earth_time * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1999-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "earth_time.h" + +#include +#include + +#include +#if defined(__WIN32__) || defined(__UNIX__) + #include +#endif + +using namespace basis; +using namespace structures; + +namespace timely { + +const int days_in_month[12] + = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +const int leap_days_in_month[12] + = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +const int julian_days_in_month[12] + = { 31, 29, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30 }; +//hmmm: is this right? + +const int julian_leap_days_in_month[12] + = { 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30 }; + +////////////// + +void clock_time::pack(byte_array &packed_form) const +{ + attach(packed_form, hour); + attach(packed_form, minute); + attach(packed_form, second); + attach(packed_form, millisecond); + attach(packed_form, microsecond); +} + +bool clock_time::unpack(byte_array &packed_form) +{ + if (!detach(packed_form, hour)) return false; + if (!detach(packed_form, minute)) return false; + if (!detach(packed_form, second)) return false; + if (!detach(packed_form, millisecond)) return false; + if (!detach(packed_form, microsecond)) return false; + return true; +} + +#define EASY_LT(x, y) \ + if (x < y) return true; \ + if (x > y) return false + +bool clock_time::operator < (const clock_time &to_compare) const +{ + EASY_LT(hour, to_compare.hour); + EASY_LT(minute, to_compare.minute); + EASY_LT(second, to_compare.second); + EASY_LT(millisecond, to_compare.millisecond); + EASY_LT(microsecond, to_compare.microsecond); + return false; +} + +bool clock_time::operator == (const clock_time &to_compare) const +{ + return (hour == to_compare.hour) + && (minute == to_compare.minute) + && (second == to_compare.second) + && (millisecond == to_compare.millisecond) + && (microsecond == to_compare.microsecond); +} + +astring clock_time::text_form(int how) const +{ + astring to_return; + text_form(to_return, how); + return to_return; +} + +void clock_time::text_form(astring &to_return, int how) const +{ + if (!how) return; // enforce use of the default. + if (how & MILITARY) + to_return += a_sprintf("%02d:%02d", hour, minute); + else { + int uhr = hour; + if (uhr > 12) uhr -= 12; + to_return += a_sprintf("%2d:%02d", uhr, minute); + } + if ( (how & SECONDS) || (how & MILLISECONDS) ) + to_return += a_sprintf(":%02d", second); + if (how & MILLISECONDS) + to_return += a_sprintf(":%03d", millisecond); + if (how & MERIDIAN) { + if (hour >= 12) to_return += "PM"; + else to_return += "AM"; + } +} + +// makes sure that "val" is not larger than "max". if it is, then max is +// used as a divisor and stored in "rolls". +#define limit_value(val, max) \ + if (val < 0) { \ + rolls = val / max; \ + rolls--; /* subtract an extra one since we definitely roll before -max */ \ + val += max * -rolls; \ + } else if (val >= max) { \ + rolls = val / max; \ + val -= max * rolls; \ + } else { rolls = 0; } + +int clock_time::normalize(clock_time &to_fix) +{ + int rolls = 0; // rollover counter. + limit_value(to_fix.microsecond, 1000); + to_fix.millisecond += rolls; + limit_value(to_fix.millisecond, 1000); + to_fix.second += rolls; + limit_value(to_fix.second, 60); + to_fix.minute += rolls; + limit_value(to_fix.minute, 60); + to_fix.hour += rolls; + limit_value(to_fix.hour, 24); + return rolls; +} + +////////////// + +void day_in_year::pack(byte_array &packed_form) const +{ + attach(packed_form, day_of_year); + attach(packed_form, abyte(day_of_week)); + attach(packed_form, abyte(month)); + attach(packed_form, day_in_month); + attach(packed_form, abyte(1)); + // still packing dst chunk; must for backward compatibility. +} + +bool day_in_year::unpack(byte_array &packed_form) +{ + if (!detach(packed_form, day_of_year)) return false; + abyte temp; + if (!detach(packed_form, temp)) return false; + day_of_week = days(temp); + if (!detach(packed_form, temp)) return false; + month = months(temp); + if (!detach(packed_form, day_in_month)) return false; + if (!detach(packed_form, temp)) return false; // dst chunk--backward compat. + return true; +} + +bool day_in_year::operator < (const day_in_year &to_compare) const +{ + EASY_LT(month, to_compare.month); + EASY_LT(day_in_month, to_compare.day_in_month); + return false; +} + +bool day_in_year::operator == (const day_in_year &to_compare) const +{ + return (month == to_compare.month) + && (day_in_month == to_compare.day_in_month); +} + +astring day_in_year::text_form(int how) const +{ + astring to_return; + text_form(to_return, how); + return to_return; +} + +void day_in_year::text_form(astring &to_stuff, int how) const +{ + if (!how) return; // enforce use of the default. + if (how & INCLUDE_DAY) to_stuff += astring(day_name(day_of_week)) + " "; + const char *monat = short_month_name(month); + if (how & LONG_MONTH) + monat = month_name(month); +//hmmm: more formatting, like euro? + to_stuff += monat; + to_stuff += a_sprintf(" %02d", day_in_month); +} + +// note: this only works when adjusting across one month, not multiples. +int limit_day_of_month(int &day, int days_in_month, int days_in_prev_month) +{ + if (day > days_in_month) { + day -= days_in_month; + return 1; // forward rollover. + } else if (day < 1) { + day += days_in_prev_month; + return -1; + } + return 0; // no rolling. +} + +int day_in_year::normalize(day_in_year &to_fix, bool leap_year) +{ + int rolls = 0; // rollover counter. + int daysinm = leap_year? + leap_days_in_month[to_fix.month] : days_in_month[to_fix.month]; + int prev_month = to_fix.month - 1; + if (prev_month < 0) prev_month = 11; + int daysinpm = leap_year? + leap_days_in_month[prev_month] : days_in_month[prev_month]; + rolls = limit_day_of_month(to_fix.day_in_month, daysinm, daysinpm); + int monat = to_fix.month + rolls; + limit_value(monat, 12); // months are zero based. + to_fix.month = months(monat); + return rolls; +} + +////////////// + +void time_locus::pack(byte_array &packed_form) const +{ + attach(packed_form, year); + clock_time::pack(packed_form); + day_in_year::pack(packed_form); +} + +bool time_locus::unpack(byte_array &packed_form) +{ + if (!detach(packed_form, year)) return false; + if (!clock_time::unpack(packed_form)) return false; + if (!day_in_year::unpack(packed_form)) return false; + return true; +} + +astring time_locus::text_form_long(int t, int d, int y) const +{ + astring to_return; + text_form_long(to_return, t, d, y); + return to_return; +} + +bool time_locus::equal_to(const equalizable &s2) const { + const time_locus *s2_cast = dynamic_cast(&s2); + if (!s2_cast) throw "error: time_locus::==: unknown type"; + return (year == s2_cast->year) + && ( (const day_in_year &) *this == *s2_cast) + && ( (const clock_time &) *this == *s2_cast); +} + +bool time_locus::less_than(const orderable &s2) const { + const time_locus *s2_cast = dynamic_cast(&s2); + if (!s2_cast) throw "error: time_locus::<: unknown type"; + EASY_LT(year, s2_cast->year); + if (day_in_year::operator < (*s2_cast)) return true; + if (!(day_in_year::operator == (*s2_cast))) return false; + if (clock_time::operator < (*s2_cast)) return true; + return false; +} + +void time_locus::text_form_long(astring &to_stuff, int t, int d, int y) const +{ +//hmmm: more formatting desired, like european. + if (!y) { + text_form_long(to_stuff, t, d); // enforce use of the default. + return; + } + // add the day. + day_in_year::text_form(to_stuff, d); + to_stuff += " "; + // add the year. + if (y & SHORT_YEAR) + to_stuff += a_sprintf("%2d", year % 100); + else + to_stuff += a_sprintf("%4d", year); + // add the time. + to_stuff += " "; + clock_time::text_form(to_stuff, t); +} + +int time_locus::normalize(time_locus &to_fix) +{ + int rolls = clock_time::normalize(to_fix); + to_fix.day_in_month += rolls; + +//hmmm: this little gem should be abstracted to a method. + bool leaping = !(to_fix.year % 4); + if (!(to_fix.year % 100)) leaping = false; + if (!(to_fix.year % 400)) leaping = true; + + rolls = day_in_year::normalize(to_fix, leaping); + to_fix.year += rolls; + return 0; + // is that always right? not for underflow. +//hmmm: resolve the issue of rollovers here. +} + +////////////// + +time_locus convert(const tm &to_convert, int ms) +{ + time_locus r; + + // we lack the resolution for this, currently. + r.microsecond = 0; + + r.second = to_convert.tm_sec; + r.minute = to_convert.tm_min; + r.hour = to_convert.tm_hour; + r.day_in_month = to_convert.tm_mday; + r.month = months(to_convert.tm_mon); + r.year = to_convert.tm_year + 1900; + r.day_of_week = days(to_convert.tm_wday); + r.day_of_year = to_convert.tm_yday; + r.millisecond = ms; + return r; +} + +time_locus now() +{ + timeb current; + ftime(¤t); + tm split_time(*localtime(¤t.time)); + return convert(split_time, current.millitm); +} + +time_locus greenwich_now() +{ + timeb current; + ftime(¤t); + tm split_time(*gmtime(¤t.time)); + return convert(split_time, current.millitm); +} + +clock_time time_now() { return now(); } + +days day_now() { return now().day_of_week; } + +months month_now() { return now().month; } + +int year_now() { return now().year; } + +day_in_year date_now() { return now(); } + +const char *day_name(days to_name) +{ + switch (to_name) { + case SUNDAY: return "Sunday"; + case MONDAY: return "Monday"; + case TUESDAY: return "Tuesday"; + case WEDNESDAY: return "Wednesday"; + case THURSDAY: return "Thursday"; + case FRIDAY: return "Friday"; + case SATURDAY: return "Saturday"; + default: return "Not_a_day"; + } +} + +const char *month_name(months to_name) +{ + switch (to_name) { + case JANUARY: return "January"; + case FEBRUARY: return "February"; + case MARCH: return "March"; + case APRIL: return "April"; + case MAY: return "May"; + case JUNE: return "June"; + case JULY: return "July"; + case AUGUST: return "August"; + case SEPTEMBER: return "September"; + case OCTOBER: return "October"; + case NOVEMBER: return "November"; + case DECEMBER: return "December"; + default: return "Not_a_month"; + } +} + +const char *short_month_name(months to_name) +{ + switch (to_name) { + case JANUARY: return "Jan"; + case FEBRUARY: return "Feb"; + case MARCH: return "Mar"; + case APRIL: return "Apr"; + case MAY: return "May"; + case JUNE: return "Jun"; + case JULY: return "Jul"; + case AUGUST: return "Aug"; + case SEPTEMBER: return "Sep"; + case OCTOBER: return "Oct"; + case NOVEMBER: return "Nov"; + case DECEMBER: return "Dec"; + default: return "Not"; + } +} + +} // namespace. + diff --git a/core/library/timely/earth_time.h b/core/library/timely/earth_time.h new file mode 100644 index 00000000..3b249413 --- /dev/null +++ b/core/library/timely/earth_time.h @@ -0,0 +1,241 @@ +#ifndef EARTH_TIME_GROUP +#define EARTH_TIME_GROUP + +// Name : earth_time +// Author : Chris Koeritz +/****************************************************************************** +* Copyright (c) 1999-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include + +//! A set of methods for rendering calendrical and clock times. +/*! + It is based on the Gregorian calendar currently in use by the USA and other + countries. +*/ + +namespace timely { + + enum days { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY }; + //!< The names of the days of the week. + + days day_now(); //!< Returns the current local day. + + const char *day_name(days to_name); + //!< Returns the name of the day "to_name". + + enum months { JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, + SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER }; + //!< The names of the months in our calendar. + + months month_now(); //!< returns the local month. + + const char *month_name(months to_name); + //!< Returns the name of the month "to_name". + const char *short_month_name(months to_name); + //!< Returns a shorter, constant-length (3 characters) month name. + + extern const int days_in_month[12]; + //!< The number of days in each month in the standard year. + extern const int leap_days_in_month[12]; + //!< The number of days in each month in a leap year. + + extern const int julian_days_in_month[12]; + //!< Number of days in each month based on the julian calendar. + extern const int julian_leap_days_in_month[12]; + //!< Number of days in each month of a leap year in the julian calendar. + + const int SECONDS_IN_MINUTE = 60; //!< Number of seconds in one minute. + const int MINUTES_IN_HOUR = 60; //!< Number of minutes in an hour. + const int HOURS_IN_DAY = 24; //!< Number of hours in a day. + const int DAYS_IN_YEAR = 365; //!< Number of days in a standard year. + const int LEAP_DAYS_IN_YEAR = 366; //!< Number of days in a leap year. + const double APPROX_DAYS_IN_YEAR = 365.2424; + //!< A more accurate measure of the number of days in a year. + /*!< This is the more accurate mean length of time in 24 hour days between + vernal equinoxes. it's about 11 minutes shy of 365.25 days. */ + + //! An enumeration of time zones, both relative and absolute. + enum time_zones { + LOCAL_ZONE, //!< The time zone this computer is configured to report. + GREENWICH_ZONE //!< The time zone of Greenwich Mean Time. + }; + + // now some structures for representing time... + + //! A specific point in time as represented by a 24 hour clock. + class clock_time : public virtual basis::packable + { + public: + int hour; //!< The hour represented in military time: 0 through 23. + int minute; //!< The number of minutes after the hour. + int second; //!< The number of seconds after the current minute. + int millisecond; //!< The number of milliseconds elapsed in this second. + int microsecond; //!< Number of microseconds elapsed in this millisecond. + + //! Constructs a clock_time object given all the parts. + clock_time(int h = 0, int m = 0, int s = 0, int ms = 0, int us = 0) + : hour(h), minute(m), second(s), millisecond(ms), + microsecond(us) {} + ~clock_time() {} + + int packed_size() const { return 5 * structures::PACKED_SIZE_INT32; } + + virtual void pack(basis::byte_array &packed_form) const; + //!< Packs a clock time into an array of bytes. + virtual bool unpack(basis::byte_array &packed_form); + //!< Unpacks a clock time from an array of bytes. + + bool operator < (const clock_time &to_compare) const; + //!< Returns true if this clock_time is earlier than "to_compare" + bool operator == (const clock_time &to_compare) const; + //!< Returns true if this clock_time is equal to "to_compare" + + //! An enumeration of time formatting modes used when printing the time. + enum time_formats { + MERIDIAN = 0x1, //!< default: uses 12 hour with AM/PM and no seconds. + MILITARY = 0x2, //!< use military 24 hour time. + NO_AM_PM = 0x4, //!< use 12 hour time but don't include AM/PM. + SECONDS = 0x8, //!< include the number of seconds as a third field. + MILLISECONDS = 0x10 //!< milliseconds are fourth field (after secs). + }; + + basis::astring text_form(int how = MERIDIAN) const; + //!< Prints the clock_time according to "how". + /*!< "how" is a combination of time_formats. */ + void text_form(basis::astring &to_stuff, int how = MERIDIAN) const; + //!< Prints the time into "to_stuff" given "how". + /*!< note that "to_stuff" will be appended to; the existing contents + are retained. */ + + static int normalize(clock_time &to_fix); + // ensures that the units in each field are in the proper range by + // promoting them upwards. if the clock_time goes above the maximum hour, + // then it rolls around. zero is returned for no rollover or a positive + // integer is returned for the number of rollovers that occurred. if + // there are any negative fields, they are rolled backwards. + // the returned rollovers are measured in days. + }; + + //! An object that represents a particular day in a year. + class day_in_year : public virtual basis::packable + { + public: + months month; //!< The current month. + int day_in_month; //!< The day number within the month (starting at one). + days day_of_week; //!< The day of the week. + int day_of_year; //!< Numerical day, where January 1st is equal to zero. + + int packed_size() const { return 4 * structures::PACKED_SIZE_INT32; } + + //! Constructs a representation of the day specified. + day_in_year(months m = JANUARY, int dim = 1, days dow = SUNDAY, + int day_o_year = 1) : month(m), day_in_month(dim), + day_of_week(dow), day_of_year(day_o_year) {} + + virtual void pack(basis::byte_array &packed_form) const; + //!< Packs a day object into an array of bytes. + virtual bool unpack(basis::byte_array &packed_form); + //!< Unpacks a day object from an array of bytes. + + bool operator < (const day_in_year &to_compare) const; + //!< Returns true if this day is earlier than "to_compare" + /*!< Note that this only compares the month and day in the month. */ + bool operator == (const day_in_year &to_compare) const; + //!< Returns true if this day is equal to "to_compare" + /*!< Note that this only compares the month and day in the month. */ + + //! An enumeration of ways to print out the current date. + enum date_formats { + // note: these classes may need to be revised in the year 9999. + SHORT_MONTH = 0x1, //!< default: three letter month. + LONG_MONTH = 0x2, //!< uses full month name. + INCLUDE_DAY = 0x4 //!< adds the name of the day. + }; + + basis::astring text_form(int how = SHORT_MONTH) const; + //!< Prints the day according to "how". + void text_form(basis::astring &to_stuff, int how = SHORT_MONTH) const; + //!< Prints the day according to "how" and stores it in "to_stuff". + + static int normalize(day_in_year &to_fix, bool leap_year = false); + //!< normalizes the day as needed and returns the adjustment in years. + /*!< note that this only adjusts the day_in_month and month members + currently. the other counters are not changed. */ + }; + + //! An object that represents a particular point in time. + /*! It contains both a time of day and the day in the year. */ + class time_locus : public clock_time, public day_in_year, + public virtual basis::hoople_standard + { + public: + int year; //!< The year, using the gregorian calendar. + + time_locus() : clock_time(), day_in_year(), year() {} + + DEFINE_CLASS_NAME("time_locus"); + + //! Constructs a location in time given its components. + time_locus(const clock_time &ct, const day_in_year &ytd, int year_in) + : clock_time(ct), day_in_year(ytd), year(year_in) {} + + int packed_size() const { return clock_time::packed_size() + + day_in_year::packed_size() + structures::PACKED_SIZE_INT32; } + + virtual void pack(basis::byte_array &packed_form) const; + //!< Packs a time_locus object into an array of bytes. + virtual bool unpack(basis::byte_array &packed_form); + //!< Unpacks a time_locus object from an array of bytes. + + // these implement the orderable and equalizable interfaces. + virtual bool equal_to(const basis::equalizable &s2) const; + virtual bool less_than(const basis::orderable &s2) const; +//old bool operator < (const time_locus &to_compare) const; + //!< Returns true if this time_locus is earlier than "to_compare" +//old bool operator == (const time_locus &to_compare) const; + //!< Returns true if this time_locus is equal to "to_compare" + + //! Enumerates the ways to show the year. + enum locus_formats { + LONG_YEAR = 0x1, //!< default: full four digit year (problems in 9999). + SHORT_YEAR = 0x2 //!< use only last two digits of year. ugh--Y2K danger. + }; + + // fulfills obligation for text_formable. + virtual void text_form(basis::base_string &state_fill) const { + state_fill.assign(text_form_long(clock_time::MERIDIAN, day_in_year::SHORT_MONTH, LONG_YEAR)); + } + + basis::astring text_form_long(int t = clock_time::MERIDIAN, + int d = day_in_year::SHORT_MONTH, int y = LONG_YEAR) const; + //! Prints out the time_locus given the way to print each component. + /*< "t" is a combination of time_formats, "d" is a combination of + date_formats and "y" is a combination of locus_formats. */ + void text_form_long(basis::astring &to_stuff, int t = clock_time::MERIDIAN, + int d = day_in_year::SHORT_MONTH, int y = LONG_YEAR) const; + //! Same as text_form() above, but stores into "to_stuff". + + static int normalize(time_locus &to_fix); + //!< normalizes the time_locus for its clock time and date. +//hmmm: what are rollovers measured in? + }; + + int year_now(); //!< what year is it? + clock_time time_now(); //!< what time is it? + day_in_year date_now(); //!< what day on the calendar is it? + time_locus now(); //!< returns our current locus in the time continuum. + time_locus greenwich_now(); //!< returns Greenwich Mean Time (their now). +} // namespace. + +#endif + diff --git a/core/library/timely/makefile b/core/library/timely/makefile new file mode 100644 index 00000000..c297d0f5 --- /dev/null +++ b/core/library/timely/makefile @@ -0,0 +1,9 @@ +include cpp/variables.def + +PROJECT = timely +TYPE = library +SOURCE = earth_time.cpp stopwatch.cpp time_control.cpp time_stamp.cpp timer_driver.cpp +TARGETS = timely.lib + +include cpp/rules.def + diff --git a/core/library/timely/stopwatch.cpp b/core/library/timely/stopwatch.cpp new file mode 100644 index 00000000..d5661cbc --- /dev/null +++ b/core/library/timely/stopwatch.cpp @@ -0,0 +1,103 @@ +/*********************** +* * +* Name : stopwatch +* Author : Chris Koeritz +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "time_stamp.h" +#include "stopwatch.h" + +#include +#include +#include + +using namespace basis; + +namespace timely { + +stopwatch::stopwatch() +: _status(UNSTARTED), + _start_time(new time_stamp()), + _stop_time(new time_stamp()), + _total_so_far(0) +{} + +stopwatch::stopwatch(const stopwatch &to_copy) +: _status(UNSTARTED), + _start_time(new time_stamp()), + _stop_time(new time_stamp()), + _total_so_far(0) +{ *this = to_copy; } + +stopwatch::~stopwatch() +{ + _status = UNSTARTED; + WHACK(_start_time); + WHACK(_stop_time); +} + +stopwatch &stopwatch::operator =(const stopwatch &to_copy) +{ + if (this == &to_copy) return *this; + *_start_time = *to_copy._start_time; + *_stop_time = *to_copy._stop_time; + _status = to_copy._status; + _total_so_far = to_copy._total_so_far; + return *this; +} + +void stopwatch::reset() { _status = UNSTARTED; _total_so_far = 0; } + +int stopwatch::milliseconds() { return common_measure(); } + +void stopwatch::start() +{ + if (_status == RUNNING) return; + *_start_time = time_stamp(); + _status = RUNNING; +} + +int stopwatch::compute_diff(const time_stamp &t1, const time_stamp &t2) +{ return int(t2.value() - t1.value()); } + +void stopwatch::halt() +{ + if (_status == STOPPED) return; + else if (_status == UNSTARTED) return; + + *_stop_time = time_stamp(); + _total_so_far += compute_diff(*_start_time, *_stop_time); + + _status = STOPPED; +} + +int stopwatch::common_measure() +{ + bool restart = false; + int to_return = 0; + switch (_status) { + case UNSTARTED: break; + case RUNNING: + // stop stopwatch, restart afterwards. + halt(); + restart = true; + // intentional fall through to default. + default: + // set the return value to the accumulated time. + to_return = _total_so_far; + break; + } + if (restart) start(); // crank the stopwatch back up if we were supposed to. + return to_return; +} + +} //namespace. + diff --git a/core/library/timely/stopwatch.h b/core/library/timely/stopwatch.h new file mode 100644 index 00000000..964debf7 --- /dev/null +++ b/core/library/timely/stopwatch.h @@ -0,0 +1,94 @@ +#ifndef STOPWATCH_CLASS +#define STOPWATCH_CLASS + +/*** +* +* Name : stopwatch +* Author : Chris Koeritz +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "time_stamp.h" + +namespace timely { + +//! A class for measuring event durations in real time. +/*! + Once the stopwatch is constructed, it can then be repeatedly started and + halted, and then started again. The number of milliseconds or + microseconds elapsed can be requested while the stopwatch is running, but + that can disrupt fine-grained measurements. +*/ + +class stopwatch : public virtual basis::root_object +{ +public: + stopwatch(); + stopwatch(const stopwatch &to_copy); + + virtual ~stopwatch(); + + stopwatch &operator =(const stopwatch &to_copy); + + void start(); + //!< Begins the timing. + /*!< If the stopwatch is already timing, then "start" does nothing. */ + + void halt(); + //!< Stops the timing. + /*!< start() may be called again to resume timing after the halt. If the + stopwatch is already stopped, or never was started, then halt does nothing. */ + void stop() { halt(); } + //!< a synonym for halt(). + + void reset(); + //!< Stops the stopwatch and clears it to zero time elapsed. + + int milliseconds(); + //!< Returns the elapsed number of milliseconds on the stopwatch, overall. + int elapsed() { return milliseconds(); } + //!< a synonym for milliseconds(). + +private: + enum stopwatch_kinds { UNSTARTED, RUNNING, STOPPED }; //!< states for the stopwatch. + stopwatch_kinds _status; //!< our current state. + time_stamp *_start_time; //!< last time we got started. + time_stamp *_stop_time; //!< last time we got stopped. + int _total_so_far; //!< total amount of time run for so far. + + int common_measure(); + //!< returns the current time used to this point, in milliseconds. + + int compute_diff(const time_stamp &t1, const time_stamp &t2); + //!< the difference in milliseconds between the times "t1" and "t2". +}; + +////////////// + +//! Logs a warning when an operation takes longer than expected. +/*! + Place TIME_CHECK_BEGIN before the code that you want to check, then place + TIME_CHECK_END afterwards. The two calls must be in the same scope. + "logger" should be a reference to a log_base object. [ by Brit Minor ] +*/ +#define TIME_CHECK_BEGIN \ + stopwatch t; \ + t.start(); +#define TIME_CHECK_END(logger, who, msec_limit, what, filter) { \ + t.halt(); \ + if (t.milliseconds() > msec_limit) { \ + (logger).log( a_sprintf("TIME_CHECK: %s: %d ms wait for %s.", \ + (who), t.milliseconds(), (what)), filter); \ + } \ +} + +} //namespace. + +#endif + diff --git a/core/library/timely/time_control.cpp b/core/library/timely/time_control.cpp new file mode 100644 index 00000000..17e71ddb --- /dev/null +++ b/core/library/timely/time_control.cpp @@ -0,0 +1,98 @@ +// Name : time_control +// Author : Chris Koeritz +/****************************************************************************** +* Copyright (c) 1994-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "time_control.h" + +#include +#include + +#include +#if defined(__WIN32__) || defined(__UNIX__) + #include +#endif +#ifdef __UNIX__ + #include +#endif + +using namespace basis; +using namespace structures; + +namespace timely { + +void time_control::sleep_ms(basis::un_int msec) +{ +#ifdef __UNIX__ + usleep(msec * 1000); +#endif +#ifdef __WIN32__ + Sleep(msec); +#endif +} + +bool time_control::set_time(const time_locus &new_time) +{ +#ifdef __WIN32__ + SYSTEMTIME os_time; + os_time.wYear = WORD(new_time.year); + os_time.wMonth = new_time.month; + os_time.wDayOfWeek = new_time.day_of_week; + os_time.wDay = new_time.day_of_year; + os_time.wHour = new_time.hour; + os_time.wMinute = new_time.minute; + os_time.wSecond = new_time.second; + os_time.wMilliseconds = 0; // currently unused. + + // get our process token for manipulation. + HANDLE petoken; + OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES + | TOKEN_QUERY, &petoken); + // get our +//something or other +// identifier so we can adjust our privileges. + LUID our_id; + LookupPrivilegeValue(NULL, to_unicode_temp("SeSystemTimePrivilege"), &our_id); + // make up a privilege structure for the adjustment. + TOKEN_PRIVILEGES privs; + privs.PrivilegeCount = 1; + privs.Privileges[0].Luid = our_id; + privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + // enable system-time privileges. + AdjustTokenPrivileges(petoken, false, &privs, sizeof(TOKEN_PRIVILEGES), + NULL, NULL); + + SetLocalTime(&os_time); // actually set the time. + + // disable the time adjustment privileges again. + AdjustTokenPrivileges(petoken, true, &privs, sizeof(TOKEN_PRIVILEGES), + NULL, NULL); + + // let all the main windows know that the time got adjusted. +//do we need to do this ourselves? + ::PostMessage(HWND_BROADCAST, WM_TIMECHANGE, 0, 0); + +//hmmm: make sure this seems right. + CloseHandle(petoken); + + return true; +#elif defined(__UNIX__) +//no implem yet. + +//temp to shut up warnings +time_locus ted = new_time; +return ted.year ? 0:1; + +#else + return false; +#endif +} + +} // namespace. + diff --git a/core/library/timely/time_control.h b/core/library/timely/time_control.h new file mode 100644 index 00000000..d2391b41 --- /dev/null +++ b/core/library/timely/time_control.h @@ -0,0 +1,48 @@ +#ifndef TIME_CONTROL_CLASS +#define TIME_CONTROL_CLASS + +/*****************************************************************************\ +* * +* Name : time_control +* Author : Chris Koeritz +* * +******************************************************************************* +* Copyright (c) 1995-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "earth_time.h" + +///#include +///#include +///#include + +namespace timely { + +//! Provides some functions that affect time, or ones perception of time. + +class time_control : public virtual basis::nameable +{ +public: + ~time_control() {} + + static void sleep_ms(basis::un_int msec); + //!< a system independent name for a forced snooze measured in milliseconds. + /*!< the application will do nothing on the current thread for the amount of + time specified. on some systems, if "msec" is zero, then the sleep will + yield the current thread's remaining timeslice back to other threads. */ + + bool set_time(const time_locus &new_time); + //!< makes the current time equal to "new_time". + /*!< This will only work if the operation is supported on this OS and if + the current user has the proper privileges. */ +}; + +} //namespace. + +#endif + diff --git a/core/library/timely/time_stamp.cpp b/core/library/timely/time_stamp.cpp new file mode 100644 index 00000000..d015a342 --- /dev/null +++ b/core/library/timely/time_stamp.cpp @@ -0,0 +1,160 @@ +/*****************************************************************************\ +* * +* Name : time_stamp * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1995-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "earth_time.h" +#include "time_stamp.h" + +#include +#include + +#include +#ifdef __WIN32__ + #define _WINSOCKAPI_ // make windows.h happy about winsock. +// #include + #include // timeval. +#endif + +using namespace basis; + +namespace timely { + +static mutex &__uptime_synchronizer() { + static mutex uptiming_syncher; + return uptiming_syncher; +} + +basis::astring time_stamp::notarize(bool add_space) +{ + const time_locus the_time = now(); + astring to_return; + the_time.text_form_long(to_return, clock_time::MILITARY | clock_time::MILLISECONDS); + if (add_space) to_return += " "; + return to_return; +} + +time_stamp::time_stamp() : c_stamp(0) { fill_in_time(); } + +time_stamp::time_stamp(time_representation offset) +: c_stamp(0) { reset(offset); } + +void time_stamp::reset() { fill_in_time(); } + +astring time_stamp::text_form(stamp_display_style style) const +{ + time_representation stump = c_stamp; + bool past = false; + if (style == STAMP_RELATIVE) { + // adjust the returned time by subtracting the current time. + stump -= get_time_now(); + if (negative(stump)) { + // if we're negative, just note that the stamp is in the past. + past = true; + stump = absolute_value(stump); + } + } + time_representation divisor = 3600 * SECOND_ms; + basis::un_int hours = basis::un_int(stump / divisor); + stump -= divisor * time_representation(hours); + divisor /= 60; + basis::un_int minutes = basis::un_int(stump / divisor); + stump -= divisor * time_representation(minutes); + divisor /= 60; + basis::un_int seconds = basis::un_int(stump / divisor); + stump -= divisor * time_representation(seconds); + basis::un_int milliseconds = basis::un_int(stump); + // make absolutely sure we are between 0 and 999. + milliseconds %= 1000; + + astring to_return; + bool did_hours = false; + if (hours) { + to_return += astring(astring::SPRINTF, "%uh:", hours); + did_hours = true; + } + if (minutes || did_hours) + to_return += astring(astring::SPRINTF, "%02um:", minutes); + to_return += astring(astring::SPRINTF, "%02us.%03u", seconds, milliseconds); + if (style == STAMP_RELATIVE) { + if (past) to_return += " ago"; + else to_return += " from now"; + } + return to_return; +} + +void time_stamp::fill_in_time() +{ + time_representation current = get_time_now(); + c_stamp = current; // reset our own time now. +} + +void time_stamp::reset(time_representation offset) +{ + fill_in_time(); + c_stamp += offset; +} + +time_stamp::time_representation time_stamp::get_time_now() +{ return rolling_uptime(); } + +const double __rollover_point = 2.0 * MAXINT32; + // this number is our rollover point for 32 bit integers. + +double time_stamp::rolling_uptime() +{ + auto_synchronizer l(__uptime_synchronizer()); + // protect our rollover records. + + static basis::un_int __last_ticks = 0; + static int __rollovers = 0; + + basis::un_int ticks_up = environment::system_uptime(); + // acquire the current uptime as a 32 bit unsigned int. + + if (ticks_up < __last_ticks) { + // rollover happened. increment our tracker. + __rollovers++; + } + __last_ticks = ticks_up; + + return double(__rollovers) * __rollover_point + double(ticks_up); +} + +timeval time_stamp::fill_timeval_ms(int duration) +{ + timeval time_out; // timeval has tv_sec=seconds, tv_usec=microseconds. + if (!duration) { + // duration is immediate for the check; just a quick poll. + time_out.tv_sec = 0; + time_out.tv_usec = 0; +#ifdef DEBUG_PORTABLE +// LOG("no duration specified"); +#endif + } else { + // a non-zero duration means we need to compute secs and usecs. + time_out.tv_sec = duration / 1000; + // set the number of seconds from the input in milliseconds. + duration -= time_out.tv_sec * 1000; + // now take out the chunk we've already recorded as seconds. + time_out.tv_usec = duration * 1000; + // set the number of microseconds from the remaining milliseconds. +#ifdef DEBUG_PORTABLE +// LOG(isprintf("duration of %d ms went to %d sec and %d usec.", duration, +// time_out.tv_sec, time_out.tv_usec)); +#endif + } + return time_out; +} + +} //namespace. + diff --git a/core/library/timely/time_stamp.h b/core/library/timely/time_stamp.h new file mode 100644 index 00000000..691660d8 --- /dev/null +++ b/core/library/timely/time_stamp.h @@ -0,0 +1,113 @@ +#ifndef TIME_STAMP_CLASS +#define TIME_STAMP_CLASS + +/*****************************************************************************\ +* * +* Name : time_stamp * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1995-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +// forward. +class rollover_record; +struct timeval; + +namespace timely { + +//! Represents a point in time relative to the operating system startup time. +/*! + This duration is measured in milliseconds. This class provides a handy way + of measuring relative durations at the millisecond time scale. + Unfortunately, operating systems that reckon their millisecond uptime in + 32 bit integers will suffer from a rollover problem every 49 days or so, + but this class corrects this issue. +*/ + +class time_stamp : public virtual basis::orderable +{ +public: + typedef double time_representation; + //!< the representation of time for this universe, measured in milliseconds. + + time_stamp(); + //!< creates a time_stamp containing the current time. + + time_stamp(time_representation offset); + //!< creates a stamp after the current time by "offset" milliseconds. + /*!< negative offsets are allowed, in which case the stamp will be + prior to the current time. */ + + static double rolling_uptime(); + //!< give the OS uptime in a more durable form that handles rollovers. + + void reset(); + //!< sets the stamp time back to now. + void reset(time_representation offset); + //!< sets the stamp forward by "offset" similar to the second constructor. + + time_representation value() const { return c_stamp; } + //!< returns the time_stamp in terms of the lower level type. + + enum stamp_display_style { STAMP_RELATIVE, STAMP_ABSOLUTE }; + + basis::astring text_form(stamp_display_style style = STAMP_RELATIVE) const; + //!< returns a simple textual representation of the time_stamp. + /*!< if the "style" is ABSOLUTE, then the stamp is shown in H:M:S.ms + form based on the system uptime. if the "style" is RELATIVE, then the + stamp is shown as an offset from now. */ + + static basis::astring notarize(bool add_space = true); + //!< a useful method for getting a textual version of the time "right now". + /*!< this was formerly known as a very useful method called 'utility::timestamp'. + that naming was fairly abusive to keep straight from the time_stamp class, so the + functionality has been absorbed. */ + + // standard operators: keep in mind that time is represented by an ever + // increasing number. so, if a value A is less than a value B, that means + // that A is older than B, since it occurred at an earlier time. + virtual bool less_than(const basis::orderable &that) const { + const time_stamp *cast = dynamic_cast(&that); + if (!cast) return false; + return c_stamp < cast->c_stamp; + } + + virtual bool equal_to(const basis::equalizable &that) const { + const time_stamp *cast = dynamic_cast(&that); + if (!cast) return false; + return c_stamp == cast->c_stamp; + } + + // helper functions for using the OS's time support. + + static timeval fill_timeval_ms(int milliseconds); + //!< returns a timeval system object that represents the "milliseconds". + /*!< if "milliseconds" is zero, then the returned timeval will + represent zero time passing (rather than infinite duration as some + functions assume). */ + +private: + time_representation c_stamp; //!< the low-level time stamp is held here. + + void fill_in_time(); //!< stuffs the current time into the held value. + + static time_representation get_time_now(); + //!< platform specific function that gets uptime. + + static rollover_record &rollover_rover(); +}; + +} //namespace. + +#endif + diff --git a/core/library/timely/timer_driver.cpp b/core/library/timely/timer_driver.cpp new file mode 100644 index 00000000..9be8aba0 --- /dev/null +++ b/core/library/timely/timer_driver.cpp @@ -0,0 +1,453 @@ +/* +* Name : timer_driver +* Author : Chris Koeritz + +* Copyright (c) 2003-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "timer_driver.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef __UNIX__ + #include +#endif + +using namespace basis; +using namespace processes; +using namespace structures; +using namespace timely; + +//#define DEBUG_TIMER_DRIVER + // uncomment for noisy code. + +#undef LOG +#define LOG(tpr) printf( (time_stamp::notarize() + "timer_driver::" + func + tpr).s() ) + +namespace timely { + +const int INITIAL_TIMER_GRANULARITY = 14; + // the timer will support durations of this length or greater initially. + // later durations will be computed based on the timers waiting. + +const int MAX_TIMER_PREDICTION = 140; + // this is the maximum predictive delay before we wake up again to see if + // any new timed items have arrived. this helps us to not wait too long + // when something's scheduled in between snoozes. + +const int PAUSE_TIME = 200; + // we will pause this many milliseconds if the timer is already occurring + // when we're trying to get the lock on our list. + +const int LONG_TIME = 1 * HOUR_ms; + // the hook can be postponed a really long time with this when necessary. + +////////////// + +SAFE_STATIC(timer_driver, timer_driver::global_timer_driver, ) + +////////////// + +#ifdef __UNIX__ +const int OUR_SIGNAL = SIGUSR2; + +class signalling_thread : public ethread +{ +public: + signalling_thread(int initial_interval) : ethread(initial_interval) {} + + void perform_activity(void *formal(ptr)) { + raise(OUR_SIGNAL); + } + +private: +}; +#endif + +#ifdef __UNIX__ +void timer_driver_private_handler(int signal_seen) +#elif defined(__WIN32__) +void __stdcall timer_driver_private_handler(window_handle hwnd, basis::un_int msg, + UINT_PTR id, un_long time) +#else + #error No timer method known for this OS. +#endif +{ +#ifdef DEBUG_TIMER_DRIVER + #undef static_class_name + #define static_class_name() "timer_driver" + FUNCDEF("timer_driver_private_handler"); +#endif +#ifdef __UNIX__ + int seen = signal_seen; + if (seen != OUR_SIGNAL) { +#elif defined(__WIN32__) + basis::un_int *seen = (basis::un_int *)id; + if (seen != program_wide_timer().real_timer_id()) { +#else + if (true) { // unknown OS. +#endif +#ifdef DEBUG_TIMER_DRIVER + LOG(a_sprintf("unknown signal/message %x caught.", (void *)seen)); +#endif + return; + } + program_wide_timer().handle_system_timer(); + #undef static_class_name +} + +////////////// + +class driven_object_record +{ +public: + int _duration; // the interval for timer hits on this object. + timeable *_to_invoke; // the object that will be called back. + time_stamp _next_hit; // next time the timer should hit for this object. + bool _okay_to_invoke; // true if this object is okay to call timers on. + bool _handling_timer; // true if we're handling this object right now. + + driven_object_record(int duration, timeable *to_invoke) + : _duration(duration), _to_invoke(to_invoke), _next_hit(duration), + _okay_to_invoke(true), _handling_timer(false) {} +}; + +class driven_objects_list +: public amorph, + public virtual root_object +{ +public: + DEFINE_CLASS_NAME("driven_objects_list"); + + int find_obj(timeable *obj) { + for (int i = 0; i < elements(); i++) { + if (borrow(i) && (borrow(i)->_to_invoke == obj)) + return i; + } + return common::NOT_FOUND; + } +}; + +////////////// + +timer_driver::timer_driver() +: _timers(new driven_objects_list), + _lock(new mutex), +#ifdef __UNIX__ + _prompter(new signalling_thread(INITIAL_TIMER_GRANULARITY)), +#endif +#ifdef __WIN32__ + _real_timer_id(NIL), +#endif + _in_timer(false) +{ + hookup_OS_timer(INITIAL_TIMER_GRANULARITY); + +#ifdef __UNIX__ + // register for the our personal signal. + signal(OUR_SIGNAL, &timer_driver_private_handler); + _prompter->start(NIL); +#endif +} + +timer_driver::~timer_driver() +{ +#ifdef DEBUG_TIMER_DRIVER + FUNCDEF("destructor"); +#endif +#ifdef __UNIX__ + _prompter->stop(); + + struct sigaction action; + action.sa_handler = SIG_DFL; + action.sa_sigaction = NIL; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; +#ifndef __APPLE__ + action.sa_restorer = NIL; +#endif + int ret = sigaction(OUR_SIGNAL, &action, NIL); + if (ret) { +///uhhh + } +#endif + unhook_OS_timer(); + + // make sure we aren't still in a timer handler when we reset our list. + while (true) { + _lock->lock(); + if (_in_timer) { + _lock->unlock(); +#ifdef DEBUG_TIMER_DRIVER + LOG("waiting to acquire timer_driver lock."); +#endif + time_control::sleep_ms(PAUSE_TIME); + } else { + break; + } + } + + _timers->reset(); // clear out the registered functions. + _lock->unlock(); + + WHACK(_timers); + WHACK(_lock); +#ifdef __UNIX__ + WHACK(_prompter); +#endif + +#ifdef DEBUG_TIMER_DRIVER + LOG("timer_driver is closing down."); +#endif +} + +#ifdef __WIN32__ +basis::un_int *timer_driver::real_timer_id() { return _real_timer_id; } +#endif + +bool timer_driver::zap_timer(timeable *to_remove) +{ +#ifdef DEBUG_TIMER_DRIVER + FUNCDEF("zap_timer"); +#endif +#ifdef DEBUG_TIMER_DRIVER + if (_in_timer) { + LOG("hmmm: zapping timer while handling previous timer...!"); + } +#endif + auto_synchronizer l(*_lock); + int indy = _timers->find_obj(to_remove); + if (negative(indy)) return false; // unknown. +#ifdef DEBUG_TIMER_DRIVER + LOG(a_sprintf("zapping timer %x.", to_remove)); +#endif + driven_object_record *reco = _timers->borrow(indy); + reco->_okay_to_invoke = false; + if (reco->_handling_timer) { + // results are not guaranteed if we see this situation. +#ifdef DEBUG_TIMER_DRIVER + LOG(a_sprintf("Logic Error: timer %x being zapped WHILE BEING HANDLED!", + to_remove)); +#endif + } + return true; +} + +bool timer_driver::set_timer(int duration, timeable *to_invoke) +{ +#ifdef DEBUG_TIMER_DRIVER + FUNCDEF("set_timer"); + if (_in_timer) { + LOG("hmmm: setting timer while handling previous timer...!"); + } +#endif +#ifdef DEBUG_TIMER_DRIVER + LOG(a_sprintf("setting timer %x to %d ms.", to_invoke, duration)); +#endif + auto_synchronizer l(*_lock); + // find any existing record. + int indy = _timers->find_obj(to_invoke); + if (negative(indy)) { + // add a new record to list. + _timers->append(new driven_object_record(duration, to_invoke)); + } else { + // change the existing record. + driven_object_record *reco = _timers->borrow(indy); + reco->_duration = duration; + reco->_okay_to_invoke = true; // just in case. + } + return true; +} + +void timer_driver::handle_system_timer() +{ +#ifdef DEBUG_TIMER_DRIVER + FUNCDEF("handle_system_timer"); +#endif + if (_in_timer) { +#ifdef DEBUG_TIMER_DRIVER + LOG("terrible error: invoked system timer while handling previous timer."); +#endif + return; + } + unhook_OS_timer(); + +#ifdef DEBUG_TIMER_DRIVER + LOG("into handling OS timer..."); +#endif + + array to_invoke_now; + + { + // lock the list for a short time, just to put in a stake for the timer + // flag; no one is allowed to change the list while this is set to true. + auto_synchronizer l(*_lock); + _in_timer = true; + + // zip across our list and find out which of the timer functions should be + // invoked. + for (int i = 0; i < _timers->elements(); i++) { + driven_object_record *funky = _timers->borrow(i); + if (!funky) { + const char *msg = "error: timer_driver's timer list logic is broken."; +#ifdef DEBUG_TIMER_DRIVER + LOG(msg); +#endif +#ifdef CATCH_ERRORS + throw msg; +#endif + _timers->zap(i, i); + i--; // skip back over dud record. + continue; + } + if (funky->_next_hit <= time_stamp()) { + // this one needs to be jangled. + to_invoke_now += funky; + } + } + } + +#ifdef DEBUG_TIMER_DRIVER + astring pointer_dump; + for (int i = 0; i < to_invoke_now.length(); i++) { + driven_object_record *funky = to_invoke_now[i]; + pointer_dump += a_sprintf("%x ", funky->_to_invoke); + } + if (pointer_dump.t()) + LOG(astring("activating ") + pointer_dump); +#endif + + // now that we have a list of timer functions, let's call on them. + for (int i = 0; i < to_invoke_now.length(); i++) { + driven_object_record *funky = to_invoke_now[i]; + { + auto_synchronizer l(*_lock); + if (!funky->_okay_to_invoke) continue; // skip this guy. + funky->_handling_timer = true; + } + // call the timer function. + funky->_to_invoke->handle_timer_callback(); + { + auto_synchronizer l(*_lock); + funky->_handling_timer = false; + } + // reset the time for the next hit. + funky->_next_hit.reset(funky->_duration); + } + + // compute the smallest duration before the next guy should fire. + int next_timer_duration = MAX_TIMER_PREDICTION; + time_stamp now; // pick a point in time as reference for all timers. + for (int i = 0; i < _timers->elements(); i++) { + driven_object_record *funky = _timers->borrow(i); + int funky_time = int(funky->_next_hit.value() - now.value()); + // we limit the granularity of timing since we don't want to be raging + // on the CPU with too small a duration. + if (funky_time < INITIAL_TIMER_GRANULARITY) + funky_time = INITIAL_TIMER_GRANULARITY; + if (funky_time < next_timer_duration) + next_timer_duration = funky_time; + } + + { + // release the timer flag again and do any cleanups that are necessary. + auto_synchronizer l(*_lock); + _in_timer = false; + for (int i = 0; i < _timers->elements(); i++) { + driven_object_record *funky = _timers->borrow(i); + if (!funky->_okay_to_invoke) { + // clean up something that was unhooked. + _timers->zap(i, i); + i--; + } + } + } + +#ifdef DEBUG_TIMER_DRIVER + LOG("done handling OS timer."); +#endif + + // set the next expiration time to the smallest next guy. + reset_OS_timer(next_timer_duration); +} + +// the following OS_timer methods do not need to lock the mutex, since they +// are not actually touching the list of timers. + +void timer_driver::hookup_OS_timer(int duration) +{ + FUNCDEF("hookup_OS_timer"); + if (negative(duration)) { +#ifdef DEBUG_TIMER_DRIVER + LOG("seeing negative duration for timer!"); +#endif + duration = 1; + } else if (!duration) { +#ifdef DEBUG_TIMER_DRIVER + LOG("patching zero duration for timer."); +#endif + duration = 1; + } +#ifdef DEBUG_TIMER_DRIVER + LOG(a_sprintf("hooking next OS timer in %d ms.", duration)); +#endif +#ifdef __UNIX__ + // just make our thread hit after the duration specified. + _prompter->reschedule(duration); +#elif defined(__WIN32__) + int max_tries_left = 100; + while (max_tries_left-- >= 0) { + _real_timer_id = (basis::un_int *)SetTimer(NIL, 0, duration, + timer_driver_private_handler); + if (!_real_timer_id) { + // failure to set the timer. + LOG("could not set the interval timer."); + time_control::sleep_ms(50); // snooze for a bit to see if we can get right. + continue; + } else + break; // success hooking timer. + } +#endif +} + +void timer_driver::unhook_OS_timer() +{ +#ifdef DEBUG_TIMER_DRIVER + FUNCDEF("unhook_OS_timer"); +#endif +#ifdef __UNIX__ + // postpone the thread for quite a while so we can take care of business. + _prompter->reschedule(LONG_TIME); +#elif defined(__WIN32__) + if (_real_timer_id) KillTimer(NIL, (UINT_PTR)_real_timer_id); +#endif +#ifdef DEBUG_TIMER_DRIVER + LOG("unhooked OS timer."); +#endif +} + +void timer_driver::reset_OS_timer(int next_hit) +{ +#ifdef DEBUG_TIMER_DRIVER + FUNCDEF("reset_OS_timer"); +#endif + unhook_OS_timer(); // stop the timer from running. + hookup_OS_timer(next_hit); // restart the timer with the new interval. +} + +} //namespace. + diff --git a/core/library/timely/timer_driver.h b/core/library/timely/timer_driver.h new file mode 100644 index 00000000..ae2b9735 --- /dev/null +++ b/core/library/timely/timer_driver.h @@ -0,0 +1,115 @@ +#ifndef TIMER_DRIVER_CLASS +#define TIMER_DRIVER_CLASS + +/*****************************************************************************\ +* * +* Name : timer_driver * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2003-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace timely { + +// forward. +class driven_objects_list; +class signalling_thread; + +////////////// + +//! timeable is the base for objects that can be hooked into timer events. + +class timeable : public virtual basis::root_object +{ +public: +//// virtual ~timeable() {} + virtual void handle_timer_callback() = 0; + //!< this method is invoked when the timer period elapses for this object. +}; + +////////////// + +//! Provides platform-independent timer support. +/*! + Multiple objects can be hooked to the timer to be called when their interval + elapses. The driver allows new timeables to be added as needed. + + NOTE: Only one of the timer_driver objects is allowed per program. +*/ + +class timer_driver : public virtual basis::root_object +{ +public: + timer_driver(); + virtual ~timer_driver(); + + DEFINE_CLASS_NAME("timer_driver"); + + // main methods for controlling timeables. + + bool set_timer(int duration, timeable *to_invoke); + //!< sets a timer to call "to_invoke" every "duration" milliseconds. + /*!< if the object "to_invoke" already exists, then its duration is + changed. */ + + bool zap_timer(timeable *to_drop); + //!< removes the timer that was established for "to_drop". + /*!< do not zap a timer from its own callback! that could cause + synchronization problems. */ + + // internal methods. + +#ifdef __WIN32__ + basis::un_int *real_timer_id(); + //!< provides the timer id for comparison on windows platforms. +#endif + + void handle_system_timer(); + //!< invoked by the OS timer support and must be called by main thread. + + static timer_driver &global_timer_driver(); + //!< the first time this is invoked, it creates a program-wide timer driver. + +private: + driven_objects_list *_timers; //!< timer hooked objects. + basis::mutex *_lock; //!< protects list of timers. +#ifdef __UNIX__ + signalling_thread *_prompter; //!< drives our timers. +#endif +#ifdef __WIN32__ + basis::un_int *_real_timer_id; //!< used for storing window timer handle. +#endif + bool _in_timer; //!< true if we're handling the timer right now. + + // these do the low-level system magic required to get a function hooked + // to a timer. + void hookup_OS_timer(int duration); + //!< hooks us into the timer events at the "duration" specified. + void reset_OS_timer(int next_hit); + //!< changes the root interval to "next_hit". + /*!< only that many milliseconds will elapse before the next timer hit. */ + void unhook_OS_timer(); + //!< disconnects us from the timer events. +}; + +////////////// + +#define program_wide_timer() timer_driver::global_timer_driver() + //!< provides access to the singleton timer_driver. + /*!< no other timer_driver objects should ever be created, since this + single one will service all timer needs within the program. */ + +} //namespace. + +#endif + diff --git a/core/library/unit_test/makefile b/core/library/unit_test/makefile new file mode 100644 index 00000000..7529795f --- /dev/null +++ b/core/library/unit_test/makefile @@ -0,0 +1,9 @@ +include cpp/variables.def + +PROJECT = unit_test +TYPE = library +SOURCE = unit_base.cpp +TARGETS = unit_test.lib + +include cpp/rules.def + diff --git a/core/library/unit_test/unit_base.cpp b/core/library/unit_test/unit_base.cpp new file mode 100644 index 00000000..439cc697 --- /dev/null +++ b/core/library/unit_test/unit_base.cpp @@ -0,0 +1,395 @@ +/* +* Name : unit test tools +* Author : Chris Koeritz +** +* Copyright (c) 2009-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include "unit_base.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace configuration; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; + +#define BASE_LOG(s) EMERGENCY_LOG(program_wide_logger::get(), s) + +namespace unit_test { + +const int EXPECTED_MAXIMUM_TESTS = 10008; //!< maximum number of tests expected. + +const char *name_for_bools(bool bv) +{ if (bv) return "true"; else return "false"; } + +unit_base::unit_base() +: c_lock(), + c_total_tests(0), + c_passed_tests(0), + c_successful(EXPECTED_MAXIMUM_TESTS), + c_failed(EXPECTED_MAXIMUM_TESTS) +{ +} + +unit_base::~unit_base() {} + +int unit_base::total_tests() const { return c_total_tests; } + +int unit_base::passed_tests() const { return c_passed_tests; } + +int unit_base::failed_tests() const +{ return c_total_tests - c_passed_tests; } + +void unit_base::count_successful_test(const basis::astring &class_name, const basis::astring &test_name) +{ + auto_synchronizer synch(c_lock); + outcome ret = c_successful.add(class_name + " -- " + test_name, ""); + if (ret == common::IS_NEW) { + c_total_tests++; + c_passed_tests++; + } +} + +void unit_base::record_pass(const basis::astring &class_name, const astring &test_name, + const astring &diag) +{ + auto_synchronizer synch(c_lock); + count_successful_test(class_name, test_name); +//hmmm: kind of lame bailout on printing this. +// it gets very very wordy if it's left in. +#ifdef DEBUG + astring message = astring("OKAY: ") + class_name + " in test [" + test_name + "]"; + BASE_LOG(message); +#endif +} + +void unit_base::count_failed_test(const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &diag) +{ + auto_synchronizer synch(c_lock); + outcome ret = c_failed.add(class_name + " -- " + test_name, diag); + if (ret == common::IS_NEW) { + c_total_tests++; + } +} + +void unit_base::record_fail(const basis::astring &class_name, const astring &test_name, const astring &diag) +{ + count_failed_test(class_name, test_name, diag); + astring message = astring("\nFAIL: ") + class_name + " in test [" + test_name + "]\n" + diag + "\n"; + BASE_LOG(message); +} + +void unit_base::record_successful_assertion(const basis::astring &class_name, const astring &test_name, + const astring &assertion_name) +{ + record_pass(class_name, test_name, + astring("no problem with: ") + assertion_name); +} + +void unit_base::record_failed_object_compare(const hoople_standard &a, const hoople_standard &b, + const basis::astring &class_name, const astring &test_name, const astring &assertion_name) +{ + astring a_state, b_state; + a.text_form(a_state); + b.text_form(b_state); + record_fail(class_name, test_name, + astring("Error in assertion ") + assertion_name + ":\n" + + "==============\n" + + a_state + "\n" + + "=== versus ===\n" + + b_state + "\n" + + "=============="); +} + +void unit_base::record_failed_int_compare(int a, int b, + const basis::astring &class_name, const astring &test_name, const astring &assertion_name) +{ + record_fail(class_name, test_name, + astring("Error in assertion ") + assertion_name + + a_sprintf(": inappropriate values: %d & %d", a, b)); +} + +void unit_base::record_failed_double_compare(double a, double b, + const basis::astring &class_name, const astring &test_name, const astring &assertion_name) +{ + record_fail(class_name, test_name, + astring("Error in assertion ") + assertion_name + + a_sprintf(": inappropriate values: %f & %f", a, b)); +} + +void unit_base::record_failed_pointer_compare(const void *a, const void *b, + const basis::astring &class_name, const astring &test_name, const astring &assertion_name) +{ + record_fail(class_name, test_name, + astring("Error in assertion ") + assertion_name + + a_sprintf(": inappropriate values: %p & %p", a, b)); +} + +void unit_base::record_failed_tf_assertion(bool result, + bool expected_result, const basis::astring &class_name, const astring &test_name, + const astring &assertion_name) +{ + record_fail(class_name, test_name, astring("Error in assertion ") + assertion_name + + ": expected " + name_for_bools(expected_result) + + " but found " + name_for_bools(result)); +} + +void unit_base::assert_equal(const hoople_standard &a, const hoople_standard &b, + const basis::astring &class_name, const astring &test_name, const astring &diag) +{ + FUNCDEF("assert_equal"); + bool are_equal = (a == b); + if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag); + else record_failed_object_compare(a, b, class_name, test_name, func); +} + +void unit_base::assert_not_equal(const hoople_standard &a, const hoople_standard &b, + const basis::astring &class_name, const astring &test_name, const astring &diag) +{ + FUNCDEF("assert_not_equal"); + bool are_equal = (a == b); + if (!are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag); + else record_failed_object_compare(a, b, class_name, test_name, func); +} + +const char *byte_array_phrase = "byte_array[%d]"; // for printing a text form of byte_array. + +void unit_base::assert_equal(const basis::byte_array &a, const basis::byte_array &b, + const basis::astring &class_name, const basis::astring &test_name, const astring &diag) +{ + FUNCDEF("assert_equal"); + bool are_equal = (a == b); + if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag); + else { + astring a_s = a_sprintf(byte_array_phrase, a.length()); + astring b_s = a_sprintf(byte_array_phrase, b.length()); + record_failed_object_compare(a_s, b_s, class_name, test_name, func); + } +} + +void unit_base::assert_not_equal(const basis::byte_array &a, const basis::byte_array &b, + const basis::astring &class_name, const basis::astring &test_name, const astring &diag) +{ + FUNCDEF("assert_not_equal"); + bool are_equal = (a == b); + if (!are_equal) record_successful_assertion(class_name, test_name, func); + else { + astring a_s = a_sprintf(byte_array_phrase, a.length()); + astring b_s = a_sprintf(byte_array_phrase, b.length()); + record_failed_object_compare(a_s, b_s, class_name, test_name, func); + } +} + +void unit_base::assert_equal(int a, int b, const basis::astring &class_name, const astring &test_name, const astring &diag) +{ + FUNCDEF("assert_equal integer"); + bool are_equal = a == b; + if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag); + else record_failed_int_compare(a, b, class_name, test_name, astring(func) + ": " + diag); +} + +void unit_base::assert_not_equal(int a, int b, const basis::astring &class_name, const astring &test_name, const astring &diag) +{ + FUNCDEF("assert_not_equal integer"); + bool are_inequal = a != b; + if (are_inequal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag); + else record_failed_int_compare(a, b, class_name, test_name, astring(func) + ": " + diag); +} + +void unit_base::assert_equal(double a, double b, const basis::astring &class_name, const astring &test_name, const astring &diag) +{ + FUNCDEF("assert_equal double"); + bool are_equal = a == b; + if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag); + else record_failed_double_compare(a, b, class_name, test_name, astring(func) + ": " + diag); +} + +void unit_base::assert_not_equal(double a, double b, const basis::astring &class_name, const astring &test_name, const astring &diag) +{ + FUNCDEF("assert_not_equal double"); + bool are_inequal = a != b; + if (are_inequal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag); + else record_failed_double_compare(a, b, class_name, test_name, astring(func) + ": " + diag); +} + + +void unit_base::assert_equal(const void *a, const void *b, + const basis::astring &class_name, const astring &test_name, const astring &diag) +{ + FUNCDEF("assert_equal void pointer"); + bool are_equal = a == b; + if (are_equal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag); + else record_failed_pointer_compare(a, b, class_name, test_name, astring(func) + ": " + diag); +} + +void unit_base::assert_not_equal(const void *a, const void *b, + const basis::astring &class_name, const astring &test_name, const astring &diag) +{ + FUNCDEF("assert_not_equal void pointer"); + bool are_inequal = a != b; + if (are_inequal) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag); + else record_failed_pointer_compare(a, b, class_name, test_name, astring(func) + ": " + diag); +} + +void unit_base::assert_true(bool result, const basis::astring &class_name, const astring &test_name, const astring &diag) +{ + FUNCDEF("assert_true"); + if (result) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag); + else record_failed_tf_assertion(result, true, class_name, test_name, astring(func) + ": " + diag); +} + +void unit_base::assert_false(bool result, const basis::astring &class_name, const astring &test_name, const astring &diag) +{ + FUNCDEF("assert_false"); + if (!result) record_successful_assertion(class_name, test_name, astring(func) + ": " + diag); + else record_failed_tf_assertion(result, false, class_name, test_name, astring(func) + ": " + diag); +} + +int unit_base::final_report() +{ + auto_synchronizer synch(c_lock); + int to_return = 0; // return success until we know otherwise. + + astring keyword = "FAILURE"; // but be pessimistic about overall result at first..? + + // check whether we really did succeed or not. + if (c_total_tests == c_passed_tests) keyword = "SUCCESS"; // success! + else to_return = 12; // a failure return. + + if (!c_total_tests) keyword = "LAMENESS (no tests!)"; // boring! + +// astring message = keyword + " for " +// + application_configuration::application_name() +// + a_sprintf(": %d of %d atomic tests passed.", +// c_passed_tests, c_total_tests); +// BASE_LOG(message); + + astring message = keyword + " for " + + filename(application_configuration::application_name()).basename().raw() + + a_sprintf(": %d of %d unit tests passed.", + c_successful.symbols(), c_successful.symbols() + c_failed.symbols()); + BASE_LOG(message); + + // send an xml file out for the build engine to analyze. + write_cppunit_xml(); + + return to_return; +} + +void unit_base::write_cppunit_xml() +{ + auto_synchronizer synch(c_lock); + astring logs_dir = environment::get("LOGS_DIR"); + if (logs_dir == astring::empty_string()) logs_dir = "logs"; // uhhh. + astring outfile = logs_dir + "/" + + filename(application_configuration::application_name()).basename().raw() + + ".xml"; + +//BASE_LOG(astring("outfile is ") + outfile); + + int id = 1; + + xml_generator report; + string_table attribs; + + // we are emulating a cppunit xml output. + + report.open_tag("TestRun"); + + report.open_tag("FailedTests"); + for (int i = 0; i < c_failed.symbols(); i++) { + attribs.reset(); + attribs.add("id", a_sprintf("%d", id++)); + report.open_tag("FailedTest", attribs); + attribs.reset(); +//hmmm: does open_tag eat the attribs? we could stop worrying about resetting. + report.open_tag("Name"); + report.add_content(c_failed.name(i)); + report.close_tag("Name"); + + report.open_tag("FailureType", attribs); + report.add_content("Assertion"); + report.close_tag("FailureType"); + + report.open_tag("Location", attribs); + + report.open_tag("File", attribs); + report.add_content(application_configuration::application_name()); + report.close_tag("File"); + + report.open_tag("Line", attribs); + report.add_content("0"); + report.close_tag("Line"); + + report.close_tag("Location"); + + report.open_tag("Message"); + report.add_content(c_failed[i]); + report.close_tag("Message"); + + report.close_tag("FailedTest"); + } + report.close_tag("FailedTests"); + + report.open_tag("SuccessfulTests"); + for (int i = 0; i < c_successful.symbols(); i++) { + attribs.reset(); + attribs.add("id", a_sprintf("%d", id++)); + attribs.reset(); + report.open_tag("Test", attribs); + report.open_tag("Name"); + report.add_content(c_successful.name(i)); + report.close_tag("Name"); + report.close_tag("Test"); + } + report.close_tag("SuccessfulTests"); + + report.open_tag("Statistics"); + report.open_tag("Tests"); + report.add_content(a_sprintf("%d", c_failed.symbols() + c_successful.symbols())); + report.close_tag("Tests"); + + report.open_tag("FailuresTotal"); + report.add_content(a_sprintf("%d", c_failed.symbols())); + report.close_tag("FailuresTotal"); + + report.open_tag("Errors"); + report.add_content("0"); + report.close_tag("Errors"); + + report.open_tag("Failures"); + report.add_content(a_sprintf("%d", c_failed.symbols())); + report.close_tag("Failures"); + + report.close_tag("Statistics"); + + report.close_tag("TestRun"); + + astring text_report = report.generate(); +// BASE_LOG(astring("got report\n") + text_report); + + byte_filer xml_out(outfile, "wb"); + xml_out.write(text_report); + xml_out.close(); +} + +} //namespace. + diff --git a/core/library/unit_test/unit_base.h b/core/library/unit_test/unit_base.h new file mode 100644 index 00000000..d5185dec --- /dev/null +++ b/core/library/unit_test/unit_base.h @@ -0,0 +1,186 @@ +#ifndef UNIT_BASE_GROUP +#define UNIT_BASE_GROUP + +/* +* Name : unit_base tools for unit testing +* Author : Chris Koeritz +** +* Copyright (c) 2009-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +//! Useful support functions for unit testing, especially within hoople. +/*! + Provides a simple framework for unit testing the hoople classes. +*/ + +#include +#include +#include +#include +#include + +namespace unit_test { + +// these macros can be used to put more information into the test name. +// using these is preferable to calling the class methods directly, since +// those cannot print out the original version of the formal parameters a +// and b for the test; the macros show the expressions that failed rather +// than just the values. +// note that these require the calling object to be derived from unit_base. +#define UNIT_BASE_THIS_OBJECT (*this) + // the macro for UNIT_BASE_THIS_OBJECT allows tests to use a single unit_base object by + // changing the value of UNIT_BASE_THIS_OBJECT to refer to the proper object's name. +#define ASSERT_EQUAL(a, b, test_name) { \ + BASE_FUNCTION(func); \ + UNIT_BASE_THIS_OBJECT.assert_equal(a, b, function_name, test_name, basis::astring(#a) + " must be equal to " + #b); \ +} +#define ASSERT_INEQUAL(a, b, test_name) { \ + BASE_FUNCTION(func); \ + UNIT_BASE_THIS_OBJECT.assert_not_equal(a, b, function_name, test_name, basis::astring(#a) + " must be inequal to " + #b); \ +} +#define ASSERT_TRUE(a, test_name) { \ + BASE_FUNCTION(func); \ + UNIT_BASE_THIS_OBJECT.assert_true(a, function_name, test_name, basis::astring(#a) + " must be true"); \ +} +#define ASSERT_FALSE(a, test_name) { \ + BASE_FUNCTION(func); \ + UNIT_BASE_THIS_OBJECT.assert_false(a, function_name, test_name, basis::astring(#a) + " must be false"); \ +} +// pointer versions for nicer syntax. +#define ASSERT_NULL(x, y) ASSERT_FALSE(x, y) +#define ASSERT_NON_NULL(x, y) ASSERT_TRUE(x, y) + +class unit_base : public virtual basis::nameable +{ +public: + unit_base(); + virtual ~unit_base(); + + DEFINE_CLASS_NAME("unit_base"); + + int total_tests() const; //!< the total count of tests that have been run. + int passed_tests() const; //!< count of successful tests run. + int failed_tests() const; //!< count of number of failed tests. + + void assert_equal(const basis::hoople_standard &a, const basis::hoople_standard &b, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &diagnostic_info); + //!< tests that the objects a and b are equal. + void assert_not_equal(const basis::hoople_standard &a, const basis::hoople_standard &b, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &diagnostic_info); + //!< tests that objects a and b are NOT equal. + + void assert_equal(const basis::byte_array &a, const basis::byte_array &b, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &diagnostic_info); + //!< tests that the byte_arrays a and b are equal. + void assert_not_equal(const basis::byte_array &a, const basis::byte_array &b, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &diagnostic_info); + //!< tests that the byte_arrays a and b are not equal. + + void assert_equal(int a, int b, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &diagnostic_info); + //!< tests that integers a and b are equal. + void assert_not_equal(int a, int b, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &diagnostic_info); + //!< tests that integers a and b are NOT equal. + + void assert_equal(double a, double b, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &diagnostic_info); + //!< tests that doubles a and b are equal. + void assert_not_equal(double a, double b, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &diagnostic_info); + //!< tests that doubles a and b are NOT equal. + + void assert_equal(const void *a, const void *b, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &diagnostic_info); + //!< tests that void pointers a and b are equal. + /*!< note that you can use this to compare any two pointers as long as + you cast them to (void *) first. this reports whether the two have the + same address in memory, but nothing about their contents. */ + void assert_not_equal(const void *a, const void *b, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &diagnostic_info); + //!< tests that void pointers a and b are NOT equal. + + void assert_true(bool result, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &diagnostic_info); + //!< tests that the "result" is a true boolean. + void assert_false(bool result, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &diagnostic_info); + //!< tests that the "result" is a false boolean. + + // these two methods can be used in an ad hoc manner when using the above + // assert methods is not helpful. the "test_name" should be provided + // like above, but the "diagnostic_info" can be phrased in any way needed + // to describe the pass or fail event. + void record_pass(const basis::astring &class_name, + const basis::astring &test_name, + const basis::astring &diagnostic_info); + //!< very general recording of a successful test; better to use asserts. + void record_fail(const basis::astring &class_name, + const basis::astring &test_name, + const basis::astring &diagnostic_info); + //!< very general recording of a failed test; better to use asserts. + + int final_report(); + //!< generates a report of the total number of tests that succeeded. + /*!< the return value of this can be used as the exit value of the overall test. if there + are any failures, then a failure result is returned (non-zero). otherwise the successful + exit status of zero is returned. */ + +private: + basis::mutex c_lock; //!< protects our objects for concurrent access. + int c_total_tests; //!< how many tests have been run? + int c_passed_tests; //!< how many of those passed? + structures::string_table c_successful; //!< successful test names. + structures::string_table c_failed; //!< failing test names. + + void write_cppunit_xml(); + //!< outputs a report file in cppunit format so CI engines can see results. + + void count_successful_test(const basis::astring &class_name, const basis::astring &test_name); + //!< records one successful test. + + void count_failed_test(const basis::astring &class_name, const basis::astring &test_name, const basis::astring &diag); + //!< records one failed test. + + void record_successful_assertion(const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &assertion_name); + //!< used by happy tests to record their success. + + void record_failed_object_compare(const basis::hoople_standard &a, + const basis::hoople_standard &b, const basis::astring &class_name, + const basis::astring &test_name, const basis::astring &assertion_name); + void record_failed_int_compare(int a, int b, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &assertion_name); + void record_failed_double_compare(double a, double b, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &assertion_name); + void record_failed_tf_assertion(bool result, bool expected_result, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &assertion_name); + void record_failed_pointer_compare(const void *a, const void *b, + const basis::astring &class_name, const basis::astring &test_name, + const basis::astring &assertion_name); +}; + +} //namespace. + +#endif + diff --git a/core/library/versions/makefile b/core/library/versions/makefile new file mode 100644 index 00000000..0eea688c --- /dev/null +++ b/core/library/versions/makefile @@ -0,0 +1,9 @@ +include cpp/variables.def + +PROJECT = versions +TYPE = library +SOURCE = version_checker.cpp version_ini.cpp +TARGETS = versions.lib + +include cpp/rules.def + diff --git a/core/library/versions/version.ini.example b/core/library/versions/version.ini.example new file mode 100644 index 00000000..3fc5e56a --- /dev/null +++ b/core/library/versions/version.ini.example @@ -0,0 +1,19 @@ +# This is an example "version.ini" file for a library or application. +# Using this file and the "main_ver.ini" (see "proto_main_ver.ini" for more +# information), the version support creates the version record in a resource +# file and outputs the header for version checking (libraries only). + +[version] + ; currently, the only section name used is "version". +description=Gurp Library: assorted multi-purpose frotzing components + ; longish text summary of the library's purpose. +name=Gurp_Library + ; the name that the library goes by. how it thinks of itself. +root=gurp + ; the root name for the project's dll or exe file. don't include the + ; extension; that's specified below. +extension=dll + ; type of file created by the project. the default is "dll", which indicates + ; that a version checking header should be created (called "version.h"). + ; the other option is "exe", which doesn't bother with the version header. + diff --git a/core/library/versions/version_checker.cpp b/core/library/versions/version_checker.cpp new file mode 100644 index 00000000..c5009341 --- /dev/null +++ b/core/library/versions/version_checker.cpp @@ -0,0 +1,371 @@ +/*****************************************************************************\ +* * +* Name : check_version * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "version_checker.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace basis; +using namespace configuration; +using namespace loggers; +using namespace structures; + +#ifndef BOOT_STRAPPING + // pull in the version specified for this build. +///hmmm: on hold! #include <__build_version.h> +#else + // plug in a fake version for our bootstrapping process. + #define __build_FILE_VERSION "108.420.1024.10008" +#endif + +#ifdef _MSC_VER + #include + #include +#endif + +#ifdef __WIN32__ + // ensures that we handle the data properly regardless of unicode settings. + #ifdef UNICODE + #define render_ptr(ptr) from_unicode_temp( (UTF16 *) ptr) + #else + #define render_ptr(ptr) ( (char *) ptr) + #endif +#endif + +////////////// + +namespace versions { + +version_checker::version_checker(const astring &library_file_name, + const version &expected_version, const astring &version_complaint) +: _library_file_name(new astring(library_file_name)), + _expected_version(new version(expected_version)), + _version_complaint(new astring(version_complaint)) +{} + +version_checker::~version_checker() +{ + WHACK(_library_file_name); + WHACK(_expected_version); + WHACK(_version_complaint); +} + +astring version_checker::text_form() const +{ + return astring(class_name()) + ": library_file_name=" + *_library_file_name + + ", expected_version=" + _expected_version->text_form() + + ", complaint_message=" + *_version_complaint; +} + +bool version_checker::good_version() const +{ + astring version_disabler = environment::get("TMP"); + version_disabler += "/no_version_check.txt"; + FILE *always_okay = fopen(version_disabler.s(), "r"); + if (always_okay) { + fclose(always_okay); + return true; + } + + version version_found = retrieve_version(*_library_file_name); + if (version_found.compatible(*_expected_version)) return true; // success. + complain_wrong_version(*_library_file_name, *_expected_version, + version_found); + return false; +} + +bool version_checker::loaded(const astring &library_file_name) +{ +#ifdef __WIN32__ + return bool(get_handle(library_file_name) != 0); +#else +//temp code. + return true || library_file_name.t(); +#endif +} + +void *version_checker::get_handle(const astring &library_file_name) +{ +#ifdef __WIN32__ + return GetModuleHandle(to_unicode_temp(library_file_name)); +#else +//hmmm: there really isn't this concept on OSes that i'm aware of. + return 0 && library_file_name.t(); +#endif +} + +astring version_checker::module_name(const void *module_handle) +{ +#ifdef __UNIX__ +//hmmm: implement module name for linux if that makes sense. + if (module_handle) {} + return application_configuration::application_name(); +#elif defined(__WIN32__) + flexichar low_buff[MAX_ABS_PATH + 1]; + GetModuleFileName((HMODULE)module_handle, low_buff, MAX_ABS_PATH - 1); + astring buff = from_unicode_temp(low_buff); + buff.to_lower(); + return buff; +#else + #pragma message("module_name unknown for this operating system.") + return application_name(); +#endif +} + +astring version_checker::get_name(const void *to_find) +{ return module_name(to_find); } + +bool version_checker::retrieve_version_info(const astring &filename, + byte_array &to_fill) +{ + to_fill.reset(); // clear the buffer. + + // determine the required size of the version info buffer. + int required_size; +#ifdef __WIN32__ + un_long module_handle; // filled with the dll or exe handle. + required_size = GetFileVersionInfoSize(to_unicode_temp(filename), &module_handle); +#else + required_size = 0 && filename.t(); +#endif + if (!required_size) return false; + to_fill.reset(required_size); // resize the buffer. + + // read the version info into our buffer. + bool success = false; +#ifdef __WIN32__ + success = GetFileVersionInfo(to_unicode_temp(filename), module_handle, + required_size, to_fill.access()); +#else + success = false; +#endif + return success; +} + +bool version_checker::get_language(byte_array &version_chunk, + basis::un_short &high, basis::un_short &low) +{ + high = 0; + low = 0; +#ifdef __WIN32__ + // determine the language that the version's written in. + basis::un_int data_size; + void *pointer_to_language_structure; + // query the information from the version blob. + if (!VerQueryValue(version_chunk.access(), + to_unicode_temp("\\VarFileInfo\\Translation"), + &pointer_to_language_structure, &data_size)) + return false; + // get the low & high shorts of the structure. + high = LOWORD(*(unsigned int *)pointer_to_language_structure); + low = HIWORD(*(unsigned int *)pointer_to_language_structure); +#else + high = 0 && version_chunk.length(); + low = 0; +#endif + + return true; +} + +version version_checker::retrieve_version(const astring &filename) +{ +#ifdef UNIX + + // totally bogus stand-in; this just returns the version we were built with + // rather than the version that's actually tagged on the file. + +//hmmm: fix this! get the version header back. +// return version(__build_FILE_VERSION); + return version(); + +#endif + + byte_array version_info_found(0, NIL); + if (!retrieve_version_info(filename, version_info_found)) + return version(0, 0, 0, 0); + + basis::un_short high, low; // holds the language of the version data. + if (!get_language(version_info_found, high, low)) + return version(0, 0, 0, 0); + + // retrieve the file version from version info using the appropriate + // language. + astring root_key(astring::SPRINTF, "\\StringFileInfo\\%04x%04x", high, low); + astring file_version_key(root_key + astring("\\FileVersion")); + + astring version_string; +#ifdef __WIN32__ + abyte *file_version_pointer; + basis::un_int data_size; + if (!VerQueryValue(version_info_found.access(), + to_unicode_temp(file_version_key), + (LPVOID *)&file_version_pointer, &data_size)) + return version(0, 0, 0, 0); + version_string = render_ptr(file_version_pointer); + // clean any spaces out of the string; people sometimes format these + // very badly. + for (int i = 0; i < version_string.length(); i++) { + if (version_string[i] == ' ') { + version_string.zap(i, i); + i--; // skip back a beat. + } + } +#else + return version(0, 0, 0, 0); +//tmp. +#endif + return version::from_text(version_string); +} + +bool version_checker::get_record(const astring &filename, + version_record &to_fill) +{ + to_fill = version_record(); + byte_array version_info_found(0, NIL); + if (!retrieve_version_info(filename, version_info_found)) + return false; + + basis::un_short high, low; // holds the language of the version data. + if (!get_language(version_info_found, high, low)) + return false; + + // set the root key for all accesses of the version chunk. + astring root_key(astring::SPRINTF, "\\StringFileInfo\\%04x%04x", high, low); + + // reports whether all lookups succeeded or not. + bool total_success = true; + + // the various version pieces are retrieved... + +#ifdef __WIN32__ + basis::un_int data_size; + void *data_pointer; + + // file version. + if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key + + astring("\\FileVersion")), &data_pointer, &data_size)) + total_success = false; + else + to_fill.file_version = version::from_text(render_ptr(data_pointer)); + + // company name. + if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key + + astring("\\CompanyName")), &data_pointer, &data_size)) + total_success = false; + else + to_fill.company_name = render_ptr(data_pointer); + + // file description. + if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key + + astring("\\FileDescription")), &data_pointer, &data_size)) + total_success = false; + else + to_fill.description = render_ptr(data_pointer); + + // internal name. + if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key + + astring("\\InternalName")), &data_pointer, &data_size)) + total_success = false; + else + to_fill.internal_name = render_ptr(data_pointer); + + // copyright info. + if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key + + astring("\\LegalCopyright")), &data_pointer, &data_size)) + total_success = false; + else + to_fill.copyright = render_ptr(data_pointer); + + // trademark info. + if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key + + astring("\\LegalTrademarks")), &data_pointer, &data_size)) + total_success = false; + else + to_fill.trademarks = render_ptr(data_pointer); + + // original file name. + if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key + + astring("\\OriginalFilename")), &data_pointer, &data_size)) + total_success = false; + else + to_fill.original_name = render_ptr(data_pointer); + + // product name. + if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key + + astring("\\ProductName")), &data_pointer, &data_size)) + total_success = false; + else + to_fill.product_name = render_ptr(data_pointer); + + // product version. + if (!VerQueryValue(version_info_found.access(), to_unicode_temp(root_key + + astring("\\ProductVersion")), &data_pointer, &data_size)) + total_success = false; + else + to_fill.product_version = version::from_text(render_ptr(data_pointer)); +#else + // hmmm: chunks missing in version check. +#endif + + return total_success; +} + +void version_checker::complain_wrong_version(const astring &library_file_name, + const version &expected_version, const version &version_found) const +{ + astring to_show("There has been a Version Mismatch: The module \""); + // use embedded module handle to retrieve name of dll or exe. + astring module_name = get_name(version::__global_module_handle()); + if (!module_name) module_name = "Unknown"; + to_show += module_name; + to_show += astring("\" cannot load. This is because the file \""); + to_show += library_file_name; + to_show += astring("\" was expected to have a version of ["); + + to_show += expected_version.flex_text_form(version::DOTS); + + to_show += astring("] but it instead had a version of ["); + to_show += version_found.flex_text_form(version::DOTS); + + to_show += astring("]. "); + to_show += *_version_complaint; +#ifdef __UNIX__ + continuable_error("version checking", "failure", to_show.s()); +#elif defined(__WIN32__) + MessageBox(0, to_unicode_temp(to_show), + to_unicode_temp("version_checking::failure"), MB_OK); +#endif +} + +void version_checker::complain_cannot_load(const astring &lib_file) const +{ + astring to_show("There has been a failure in Version Checking: The file \""); + to_show += lib_file; + to_show += astring("\" could not be loaded or found. "); + to_show += *_version_complaint; + continuable_error("version checking", "loading dll", to_show.s()); +} + +} //namespace. + + diff --git a/core/library/versions/version_checker.h b/core/library/versions/version_checker.h new file mode 100644 index 00000000..9e810155 --- /dev/null +++ b/core/library/versions/version_checker.h @@ -0,0 +1,117 @@ +#ifndef VERSION_CHECKER_CLASS +#define VERSION_CHECKER_CLASS + +/*****************************************************************************\ +* * +* Name : check_version * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace versions { + +//! Provides version checking for shared libraries. + +class version_checker : public virtual basis::root_object +{ +public: + version_checker(const basis::astring &library_file_name, const structures::version &expected, + const basis::astring &version_complaint); + //!< Constructs a checking object and ensures the version is appropriate. + /*!< the version checking (a call to the good_version() function) will + succeed if the library with the "library_file_name" (such as + "basis32.dll") has the "expected" version. the simplest way to check + if the version is correct is probably similar to: @code + if (!version_checker("my.dll", version(1.2.3.4)).good_version()) { + ...program exit or version failure management... + } @endcode + the "version_complaint" is the message that will be displayed on a + failure in version checking (with noisy mode enabled). it should + describe that a version failure occurred and include contact information + for the customer to get the most recent versions. for example: @code + astring version_grievance = "Please contact Floobert Corporation for " + "the latest DLL and Executable files (http://www.floobert.com)."; + @endcode */ + + virtual ~version_checker(); //!< Destructor releases any resources. + + bool good_version() const; + //!< Performs the actual version check. + /*!< If the version check is unsuccessful, then a message that + describes the problem is shown to the user and false is returned. + NOTE: the version check will also fail if the version information + structure cannot be found for that library. */ + + static basis::astring module_name(const void *module_handle); + //!< returns the module name where this object resides; only sensible on win32. + + // base requirements. + DEFINE_CLASS_NAME("version_checker"); + basis::astring text_form() const; + + static bool loaded(const basis::astring &library_file_name); + //!< returns true if the "library_file_name" is currently loaded. + static void *get_handle(const basis::astring &library_file_name); + //!< retrieves the module handle for the "library_file_name". + /*!< This returns zero if the library cannot be located. the returned + pointer wraps a win32 HMODULE currently, or it is meaningless. */ + static basis::astring get_name(const void *to_find); + //!< returns the name of the HMODULE specified by "to_find". + /*!< If that handle cannot be located, then an empty string is returned. */ + + static structures::version retrieve_version(const basis::astring &pathname); + //!< Returns the version given a "pathname" to the DLL or EXE file. + /*!< If the directory component is not included, then the search path + will be used. */ + + static bool get_record(const basis::astring &pathname, structures::version_record &to_fill); + //!< Retrieves a version record for the file at "pathname". + /*!< Returns the full version record found for a given "pathname" to + the DLL or EXE file in the record "to_fill". if the directory component + of the path is not included, then the search path will be used. false is + returned if some piece of information could not be located; this does not + necessarily indicate a total failure of the retrieval. */ + + static bool retrieve_version_info(const basis::astring &filename, + basis::byte_array &to_fill); + //!< Retrieves the version info for the "filename" into the array "to_fill". + + static bool get_language(basis::byte_array &version_chunk, basis::un_short &high, + basis::un_short &low); + //!< Gets the language identifier out of the "version_chunk". + /*!< Returns true if the language identifier for the dll's version chunk + could be stored in "high" and "low". This is a win32-only method. */ + + void complain_wrong_version(const basis::astring &library_file_name, + const structures::version &expected_version, + const structures::version &version_found) const; + //!< Reports that the file has the wrong version. + + void complain_cannot_load(const basis::astring &library_file_name) const; + //!< Reports that the dll could not be loaded. + +private: + basis::astring *_library_file_name; + structures::version *_expected_version; + basis::astring *_version_complaint; + + // forbidden. + version_checker(const version_checker &); + version_checker &operator =(const version_checker &); +}; + +} //namespace. + +#endif + diff --git a/core/library/versions/version_ini.cpp b/core/library/versions/version_ini.cpp new file mode 100644 index 00000000..7edb6f1e --- /dev/null +++ b/core/library/versions/version_ini.cpp @@ -0,0 +1,599 @@ +/*****************************************************************************\ +* * +* Name : version_ini editing support * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1995-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "version_ini.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef __WIN32__ + #include +#endif + +using namespace basis; +using namespace configuration; +using namespace filesystem; +using namespace loggers; +using namespace structures; + +namespace versions { + +// the following are all strings that are sought in the version.ini file. +const char *version_ini::VERSION_SECTION = "version"; + // the section that version entries are stored under in the INI file. +const char *version_ini::COMPANY_KEY="company"; +const char *version_ini::COPYRIGHT_KEY="copyright"; +const char *version_ini::LEGAL_INFO_KEY="legal_info"; +const char *version_ini::PRODUCT_KEY="product_name"; +const char *version_ini::WEB_SITE_KEY="web_site"; + +// not used anymore; now matched with the file version's first two digits. +//const version PRODUCT_VERSION(2, 0, 0, 0); + // the current version of the entire product. + +// these are field names in the INI file. +const char *version_ini::MAJOR = "major"; +const char *version_ini::MINOR = "minor"; +const char *version_ini::REVISION = "revision"; +const char *version_ini::BUILD = "build"; +const char *version_ini::DESCRIPTION = "description"; +const char *version_ini::ROOT = "root"; +const char *version_ini::NAME = "name"; +const char *version_ini::EXTENSION = "extension"; +const char *version_ini::OLE_AUTO = "ole_auto"; + +// this is the default version INI file name, if no other is specified. +const char *VERSION_INI_FILE = "/version.ini"; + +#undef LOG +#define LOG(t) CLASS_EMERGENCY_LOG(program_wide_logger::get(), t) + +version_ini::version_ini(const astring &path_name) +: _loaded(false), + _path_name(new filename(path_name)), + _ini(new ini_configurator("", ini_configurator::RETURN_ONLY)), + _held_record(new version_record) +{ + check_name(*_path_name); + _ini->name(*_path_name); +} + +version_ini::~version_ini() +{ + WHACK(_ini); + WHACK(_path_name); + WHACK(_held_record); +} + +bool version_ini::ole_auto_registering() +{ + astring extension = _ini->load(VERSION_SECTION, OLE_AUTO, ""); + return (extension.lower().t()); +} + +bool version_ini::executable() +{ + astring extension = _ini->load(VERSION_SECTION, EXTENSION, ""); + if (extension.lower() == astring("exe")) return true; + return false; +} + +bool version_ini::library() { return !executable(); } + +bool version_ini::writable() { return _path_name->is_writable(); } + +void version_ini::check_name(filename &to_examine) +{ + // if it's just a directory name, add the file name. + if (to_examine.is_directory()) { + to_examine = astring(to_examine) + VERSION_INI_FILE; + to_examine.canonicalize(); + } + + // add the directory explicitly (if it's not there already) or the ini + // writer will get it from the windows directory. + if ( (to_examine.raw()[0] != '.') && (to_examine.dirname().raw().equal_to(".")) ) { + to_examine = astring("./") + to_examine; + to_examine.canonicalize(); + } +} + +bool version_ini::executable(const astring &path_name_in) +{ + filename path_name(path_name_in); + check_name(path_name); + ini_configurator temp_ini(path_name, ini_configurator::RETURN_ONLY); + astring extension = temp_ini.load(VERSION_SECTION, EXTENSION, ""); + extension.to_lower(); + if (extension == astring("exe")) return true; + return false; +} + +bool version_ini::library(const astring &path_name) +{ return !executable(path_name); } + +version version_ini::get_version() +{ + if (_loaded) return _held_record->file_version; + get_record(); + return _held_record->file_version; +} + +void version_ini::set_version(const version &to_write, bool write_ini) +{ + _held_record->file_version = to_write; // copy the version we're given. + + // set the product version appropriately to the file version. + _held_record->product_version = to_write; + _held_record->product_version.set_component(version::REVISION, "0"); + _held_record->product_version.set_component(version::BUILD, "0"); + + if (!write_ini) return; // they don't want a change to the file. + _ini->store(VERSION_SECTION, MAJOR, to_write.get_component(version::MAJOR)); + _ini->store(VERSION_SECTION, MINOR, to_write.get_component(version::MINOR)); + _ini->store(VERSION_SECTION, REVISION, to_write.get_component(version::REVISION)); + _ini->store(VERSION_SECTION, BUILD, to_write.get_component(version::BUILD)); +} + +version version_ini::read_version_from_ini() +{ + string_array parts; + parts += _ini->load(VERSION_SECTION, MAJOR, "0"); + parts += _ini->load(VERSION_SECTION, MINOR, "0"); + parts += _ini->load(VERSION_SECTION, REVISION, "0"); + parts += _ini->load(VERSION_SECTION, BUILD, "0"); + return version(parts); +} + +version_record &version_ini::access_record() { return *_held_record; } + +version_record version_ini::get_record() +{ +// FUNCDEF("get_record"); + if (_loaded) return *_held_record; + version_record to_return; + to_return.description = _ini->load(VERSION_SECTION, DESCRIPTION, ""); + to_return.file_version = read_version_from_ini(); + to_return.internal_name = _ini->load(VERSION_SECTION, NAME, ""); + to_return.original_name = _ini->load(VERSION_SECTION, ROOT, ""); + to_return.original_name += "."; + + // the dll type of extension is a hard default. anything besides the exe + // ending gets mapped to dll. + astring extension = _ini->load(VERSION_SECTION, EXTENSION, ""); + extension.to_lower(); + if (extension.equal_to("dll")) {} + else if (extension.equal_to("exe")) {} + else extension.equal_to("dll"); + to_return.original_name += extension; + + to_return.product_version = to_return.file_version; + to_return.product_version.set_component(version::REVISION, "0"); + to_return.product_version.set_component(version::BUILD, "0"); + + to_return.product_name = _ini->load(VERSION_SECTION, PRODUCT_KEY, ""); + to_return.company_name = _ini->load(VERSION_SECTION, COMPANY_KEY, ""); + to_return.copyright = _ini->load(VERSION_SECTION, COPYRIGHT_KEY, ""); + to_return.trademarks = _ini->load(VERSION_SECTION, LEGAL_INFO_KEY, ""); + to_return.web_address = _ini->load(VERSION_SECTION, WEB_SITE_KEY, ""); + + *_held_record = to_return; + _loaded = true; + return to_return; +} + +void version_ini::set_record(const version_record &to_write, bool write_ini) +{ + *_held_record = to_write; + if (write_ini) { + _ini->store(VERSION_SECTION, DESCRIPTION, to_write.description); + set_version(to_write.file_version, write_ini); + _ini->store(VERSION_SECTION, ROOT, to_write.original_name); + _ini->store(VERSION_SECTION, NAME, to_write.internal_name); + } + _loaded = true; // we consider this to be the real version now. +} + +////////////// + +const astring version_rc_template = "\ +#ifndef NO_VERSION\n\ +#include \n\ +#include <__build_version.h>\n\ +#include <__build_configuration.h>\n\ +#define BI_PLAT_WIN32\n\ + // force 32 bit compile.\n\ +1 VERSIONINFO LOADONCALL MOVEABLE\n\ +FILEVERSION __build_FILE_VERSION_COMMAS\n\ +PRODUCTVERSION __build_PRODUCT_VERSION_COMMAS\n\ +FILEFLAGSMASK 0\n\ +FILEFLAGS VS_FFI_FILEFLAGSMASK\n\ +#if defined(BI_PLAT_WIN32)\n\ + FILEOS VOS__WINDOWS32\n\ +#else\n\ + FILEOS VOS__WINDOWS16\n\ +#endif\n\ +FILETYPE VFT_APP\n\ +BEGIN\n\ + BLOCK \"StringFileInfo\"\n\ + BEGIN\n\ + // Language type = U.S. English(0x0409) and Character Set = Windows, Multilingual(0x04b0)\n\ + BLOCK \"040904b0\" // Matches VarFileInfo Translation hex value.\n\ + BEGIN\n\ + VALUE \"CompanyName\", __build_company \"\\000\"\n\ +#ifndef _DEBUG\n\ + VALUE \"FileDescription\", \"$file_desc\\000\"\n\ +#else\n\ + VALUE \"FileDescription\", \"$file_desc (DEBUG)\\000\"\n\ +#endif\n\ + VALUE \"FileVersion\", __build_FILE_VERSION \"\\000\" \n\ + VALUE \"ProductVersion\", __build_PRODUCT_VERSION \"\\000\" \n\ + VALUE \"InternalName\", \"$internal\\000\"\n\ + VALUE \"LegalCopyright\", __build_copyright \"\\000\"\n\ + VALUE \"LegalTrademarks\", __build_legal_info \"\\000\"\n\ + VALUE \"OriginalFilename\", \"$original_name\\000\"\n\ + VALUE \"ProductName\", __build_product_name \"\\000\"\n\ + $special_ole_flag\n\ + END\n\ + END\n\ +\n\ + BLOCK \"VarFileInfo\"\n\ + BEGIN\n\ + VALUE \"Translation\", 0x0409, 0x04b0 // US English (0x0409) and win32 multilingual (0x04b0)\n\ + END\n\ +END\n\ +#endif\n"; + +////////////// + +// replaces every occurrence of the keyword in "tag" with the "replacement". +#define REPLACE(tag, replacement) \ + new_version_entry.replace_all(tag, replacement); \ + +bool version_ini::write_rc(const version_record &to_write) +{ + astring new_version_entry(version_rc_template); + + // $file_ver -> w, x, y, z for version of the file. + REPLACE("$file_ver", to_write.file_version.flex_text_form(version::COMMAS)); + + // $prod_ver -> w, x, y, z for version of the product. + REPLACE("$prod_ver", to_write.product_version.flex_text_form + (version::COMMAS)); + + // $company -> name of company. + REPLACE("$company", to_write.company_name); + + // $file_desc -> description of file. + astring description_release = to_write.description; + REPLACE("$file_desc", description_release); + astring description_debug = to_write.description + + astring(" -- Debug Version"); + REPLACE("$file_desc", description_debug); + + // $file_txt_ver -> file version in form w.x.y.z. + REPLACE("$file_txt_ver", to_write.file_version.flex_text_form(version::DOTS)); + + // $internal -> internal name of the library, without extensions? + REPLACE("$internal", to_write.internal_name); + + // $copyright -> copyright held by us. + REPLACE("$copyright", to_write.copyright); + + // $legal_tm -> the legal trademarks that must be included, e.g. windows? + REPLACE("$legal_tm", to_write.trademarks); + + // $original_name -> the file's name before possible renamings. + REPLACE("$original_name", to_write.original_name); + + // $prod_name -> the name of the product that this belongs to. + REPLACE("$prod_name", to_write.product_name); + + // $prod_txt_ver -> product version in form w.x.y.z. + REPLACE("$prod_txt_ver", to_write.product_version + .flex_text_form(version::DOTS, version::MINOR)); + + astring special_filler; // nothing by default. + if (ole_auto_registering()) + special_filler = "VALUE \"OLESelfRegister\", \"\\0\""; + REPLACE("$special_ole_flag", special_filler); + + astring root_part = "/"; + root_part += _ini->load(VERSION_SECTION, ROOT, ""); + + astring rc_filename(astring(_path_name->dirname()) + root_part + + "_version.rc"); + + filename(rc_filename).chmod(filename::ALLOW_BOTH, filename::USER_RIGHTS); + // make sure we can write to the file. + + byte_filer rc_file(rc_filename, "w"); + if (!rc_file.good()) return false; + rc_file.write((abyte *)new_version_entry.s(), new_version_entry.length()); + rc_file.close(); + return true; +} + +////////////// + +const astring version_header_template = "\ +#ifndef $lib_prefix_VERSION_HEADER\n\ +#define $lib_prefix_VERSION_HEADER\n\ +\n\ +/*****************************************************************************\\\n\ +* *\n\ +* Name : Version header for $lib_name\n\ +* Author : Automatically generated by version_stamper *\n\ +* *\n\ +\\*****************************************************************************/\n\ +\n\ +#include <__build_version.h>\n\ +#include <__build_configuration.h>\n\ +#include \n\ +#include \n\ +\n\ +#ifdef __WIN32__\n\ +\n\ +// this macro can be used to check that the current version of the\n\ +// $lib_name library is the same version as expected. to use it, check\n\ +// whether it returns true or false. if false, the version is incorrect.\n\ +#define CHECK_$lib_prefix() (version_checker(astring(\"$lib_prefix\")\\\n\ + + astring(\".dll\"), version(__build_SYSTEM_VERSION),\\\n\ + astring(\"Please contact $company_name for the latest DLL and \"\\\n\ + \"Executable files ($web_address).\")).good_version())\n\ +\n\ +#else\n\ +\n\ +// null checking for embedded or other platforms without versions.\n\ +\n\ +#define CHECK_$lib_prefix() 1\n\ +\n\ +#endif //__WIN32__\n\ +\n\ +#endif\n\ +\n"; + +////////////// + +bool version_ini::write_code(const version_record &to_write) +{ + astring root_part = _ini->load(VERSION_SECTION, ROOT, ""); + astring root = root_part.upper(); // make upper case for naming sake. + astring name = _ini->load(VERSION_SECTION, NAME, ""); + // replace the macros here also. + name.replace_all("$product_name", to_write.product_name); + + astring new_version_entry(version_header_template); + +//some of the replacements are no longer needed. + + // $lib_prefix -> the first part of the library's name. + REPLACE("$lib_prefix", root); + REPLACE("$lib_prefix", root); + REPLACE("$lib_prefix", root); + REPLACE("$lib_prefix", root); + REPLACE("$lib_prefix", root); + REPLACE("$lib_prefix", root); + REPLACE("$lib_prefix", root); + + // $lib_name -> the name of the library, as it thinks of itself. + REPLACE("$lib_name", name); + REPLACE("$lib_name", name); + REPLACE("$lib_name", name); + + // $lib_version -> the current version for this library. + REPLACE("$lib_version", to_write.file_version.flex_text_form(version::COMMAS)); + + // $company_name -> the company that produces the library. + REPLACE("$company_name", to_write.company_name); + + // $web_address -> the web site for the company. not actually stored. + REPLACE("$web_address", to_write.web_address); + + astring header_filename(_path_name->dirname().raw() + "/" + root_part + + astring("_version.h")); + + filename(header_filename).chmod(filename::ALLOW_BOTH, filename::USER_RIGHTS); + // make sure we can write to the file. + + byte_filer header(header_filename, "w"); + if (!header.good()) return false; + header.write((abyte *)new_version_entry.s(), new_version_entry.length()); + header.close(); + return true; +} + + +// returns true if manipulated the full_string to replace its version +bool replace_version_entry(astring &full_string, const astring &look_for, + const astring &new_ver) +{ + bool to_return = false; + int posn = 0; // where are we looking for this? + + while (posn < full_string.length()) { + int ver_posn = full_string.find(look_for, posn); + if (ver_posn < 0) break; // nothing to modify. + int quote_posn = full_string.find("\"", ver_posn); + if (quote_posn < 0) break; // malformed assembly we will not touch. + int second_quote_posn = full_string.find("\"", quote_posn + 1); + if (second_quote_posn < 0) break; // more malformage. + full_string.zap(quote_posn + 1, second_quote_posn - 1); + full_string.insert(quote_posn + 1, new_ver); + to_return = true; // found a match. + // skip to the next place. + posn = quote_posn + new_ver.length(); + } + + return to_return; +} + +bool version_ini::write_assembly(const version_record &to_write, + bool do_logging) +{ +// FUNCDEF("write_assembly"); + filename just_dir = _path_name->dirname(); +//LOG(astring("dir is set to: ") + just_dir); + directory dir(just_dir); + filename to_patch; +//LOG(astring("dir has: ") + dir.files().text_form()); + if (non_negative(dir.files().find("AssemblyInfo.cpp"))) + to_patch = just_dir.raw() + "/AssemblyInfo.cpp"; + else if (non_negative(dir.files().find("AssemblyInfo.cs"))) + to_patch = just_dir.raw() + "/AssemblyInfo.cs"; + if (!to_patch.raw()) { + // no assembly file yet. see if there's one in a properties subdirectory. + directory dir2(just_dir + "/Properties"); + if (non_negative(dir2.files().find("AssemblyInfo.cpp"))) + to_patch = just_dir.raw() + "/Properties/AssemblyInfo.cpp"; + else if (non_negative(dir2.files().find("AssemblyInfo.cs"))) + to_patch = just_dir.raw() + "/Properties/AssemblyInfo.cs"; + } + + if (to_patch.raw().t()) { + // we have a filename to work on. + filename(to_patch).chmod(filename::ALLOW_BOTH, filename::USER_RIGHTS); + // make sure we can write to the file. + byte_filer modfile(to_patch, "r+b"); + astring contents; + modfile.read(contents, 1000000); // read any file size up to that. + while (contents[contents.end()] == '\032') { + // erase any stray eof characters that may happen to be present. + contents.zap(contents.end(), contents.end()); + } +//LOG(astring("file contents are: \n") + contents); + +//here's where to fixit. + + astring ver_string = to_write.file_version.flex_text_form(version::DOTS); + bool did_replace = replace_version_entry(contents, "AssemblyVersionAttribute", ver_string); + if (!did_replace) { + did_replace = replace_version_entry(contents, "AssemblyVersion", ver_string); + } + if (!did_replace) return true; // nothing to modify? + did_replace = replace_version_entry(contents, "AssemblyFileVersion", ver_string); + if (!did_replace) { + did_replace = replace_version_entry(contents, "AssemblyFileVersionAttribute", ver_string); + } + // if we got to here, we at least replaced something... + +/* + int ver_posn = contents.find("AssemblyVersionAttribute", 0); + // try again if that seek failed. + if (ver_posn < 0) + ver_posn = contents.find("AssemblyVersion", 0); + if (ver_posn < 0) return true; // nothing to modify. +//LOG(astring("found assembly version: ") + to_patch); + int quote_posn = contents.find("\"", ver_posn); + if (quote_posn < 0) return true; // malformed assembly we will not touch. +//LOG(astring("found quote: ") + to_patch); + int second_quote_posn = contents.find("\"", quote_posn + 1); + if (second_quote_posn < 0) return true; // more malformage. +//LOG(astring("found quote: ") + to_patch); + contents.zap(quote_posn + 1, second_quote_posn - 1); + contents.insert(quote_posn + 1, ver_string); +*/ + +//LOG(astring("writing new output file: ") + to_patch); + modfile.seek(0); + modfile.write(contents); + modfile.truncate(); // chop off anything left from previous versions. + if (do_logging) { + // let the people know about this... + filename dirbase = filename(modfile.filename()).dirname().basename(); + filename just_base = filename(modfile.filename()).basename(); + program_wide_logger::get().log(astring(" patching: ") + dirbase + + "/" + just_base, basis::ALWAYS_PRINT); + } + } + + return true; +} + +bool version_ini::one_stop_version_stamp(const astring &path, + const astring &source_version, bool do_logging) +{ + astring path_name = path; + if (path_name.equal_to(".")) + path_name = application_configuration::current_directory(); + + // load the version record in from the ini file and cache it. + version_ini source(path_name); + source.get_record(); + + if (source_version.t()) { + // get the version structure from the passed file. + version_ini main_version(source_version); + version version_to_use = main_version.get_version(); + + // stuff the version from the main source into this particular file. + source.set_version(version_to_use, false); + + // stuff the other volatile records from the main version. + version_record main = main_version.get_record(); + source.access_record().company_name = main.company_name; + source.access_record().web_address = main.web_address; + source.access_record().copyright = main.copyright; + source.access_record().trademarks = main.trademarks; + source.access_record().product_name = main.product_name; + + source.access_record().internal_name.replace("$product_name", + source.get_record().product_name); + } + + if (do_logging) { + // report the current state. + program_wide_logger::get().log(source.get_record().internal_name + " version " + + source.get_version().text_form() + ".", ALWAYS_PRINT); + } + + version_ini verini(path_name); + verini.set_record(source.get_record(), false); + +// LOG(a_sprintf("The file \"%s\" contains this version information:", +// path_name.s())); +// LOG(verini.get_record().text_form()); + + if (!verini.write_rc(verini.get_record())) { + critical_events::alert_message(a_sprintf("Could not write the RC file in \"%s\".", + filename(path_name).basename().raw().s())); + return false; + } + + if (verini.library() && !verini.write_code(verini.get_record())) { + critical_events::alert_message(astring("Could not write the C++ header file for " + "the directory \"") + + filename(path_name).basename() + astring("\".\n")); + return false; + } + + if (!verini.write_assembly(verini.get_record(), do_logging)) { + critical_events::alert_message(astring("Could not write the Assembly info file for " + "the directory \"") + + filename(path_name).basename() + astring("\".\n")); + return false; + } + + return true; +} + +} //namespace. diff --git a/core/library/versions/version_ini.h b/core/library/versions/version_ini.h new file mode 100644 index 00000000..6838adee --- /dev/null +++ b/core/library/versions/version_ini.h @@ -0,0 +1,143 @@ +#ifndef VERSION_INI_CLASS +#define VERSION_INI_CLASS + +/*****************************************************************************\ +* * +* Name : version_ini editing support * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1995-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include + +namespace versions { + +//! This provides support for writing windows version files. +/*! + The class loads version information out of an initialization file and writes + it into a standard windows VERSION_INFO RC file entry. It also creates the + headers we use with our version control support. +*/ + +class version_ini : public virtual basis::root_object +{ +public: + version_ini(const basis::astring &path_name); + //!< the "path_name" is where the version INI file is located. + /*!< this file will be read for the key fields that will be used to name + the version RC file and generate information in the resource. */ + + ~version_ini(); + + DEFINE_CLASS_NAME("version_ini"); + + bool writable(); + //!< returns true if the INI file specified in the constructor is writable. + + structures::version get_version(); + //!< observes or modifies the version number structure held here. + /*!< if the record had not been previously loaded, it is loaded. */ + + void set_version(const structures::version &to_write, bool write_to_ini); + //!< sets the version held here. + /*!< if "write_ini" is true, then the ini file is written to also. note + that if get_record() had not previously been done and "write_ini" is not + true, then the next get_record() or get_version() will wipe out the + version that is set in the interim. */ + + structures::version_record get_record(); + //!< observes the entire version record. + /*!< The information is assembled from any cached record, the ini file and + other sources. if the record has been loaded before, the last loaded + version is returned plus any changes that have been made to the held + record since then. otherwise, the record is read from the ini file. */ + + structures::version_record &access_record(); + //!< provides access to change the version_record held here. + /*!< this does not read from the INI file, unlike get_record(). */ + + void set_record(const structures::version_record &to_write, bool write_to_ini); + //!< modifies the entire version record. + /*!< if "write_to_ini" is true, then the new information is actually + written to our configuration file. otherwise the information just + replaces the source record here. */ + + bool executable(); + //!< returns true if this version file is for an executable. + bool library(); + //!< returns true if this version file is for a dynamic library. + + bool ole_auto_registering(); + //!< returns true if this version file specifies ole auto registration. + + bool write_rc(const structures::version_record &to_write); + //!< writes out the file 'X_version.rc' for the X library or application. + /*!< the contents will include the information specified in "to_write", + where X is the library name from that record. */ + + bool write_code(const structures::version_record &to_write); + //!< writes out the header ('X_version.h') with the version information. + /*!< this file is needed for other libraries or application to know this + project's version number. the users can make sure that the header info + agrees with the actual version seen on the file. */ + + bool write_assembly(const structures::version_record &to_write, bool do_logging); + //!< fixes any assemblies with the info in "to_write". + + static bool executable(const basis::astring &path_name); + //!< returns true if "path_name" is for an executable. + static bool library(const basis::astring &path_name); + //!< returns true if "path_name" is for a dynamic library. + + structures::version read_version_from_ini(); + //!< specialized version ignores cache and gets version directly from file. + + static bool one_stop_version_stamp(const basis::astring &path, + const basis::astring &source_version, bool do_logging); + //!< performs version stamping using the ini file in "path". + /*!< "source_version" supplies the name of the main version file where + we retrieve the current version numbers. if that is not specified, then + only the version header and RC file are created. if "do_logging" is + true, then version info will be sent to the program-wide logger. */ + + // constants for strings found in the version INI file. + static const char *VERSION_SECTION; + static const char *COMPANY_KEY; + static const char *COPYRIGHT_KEY; + static const char *LEGAL_INFO_KEY; + static const char *PRODUCT_KEY; + static const char *WEB_SITE_KEY; + static const char *MAJOR; + static const char *MINOR; + static const char *REVISION; + static const char *BUILD; + static const char *DESCRIPTION; + static const char *ROOT; + static const char *NAME; + static const char *EXTENSION; + static const char *OLE_AUTO; + +private: + bool _loaded; //!< did we grab the data from the ini file yet? + filesystem::filename *_path_name; //!< where to find the ini file. + configuration::ini_configurator *_ini; //!< accesses the ini file. + structures::version_record *_held_record; //!< the data we cache for the ini file. + + static void check_name(filesystem::filename &to_examine); + //!< adds the default file name if "to_examine" turns out to be a directory. +}; + +} //namespace. + +#endif + diff --git a/core/makefile b/core/makefile new file mode 100644 index 00000000..2334be05 --- /dev/null +++ b/core/makefile @@ -0,0 +1,7 @@ +include variables.def + +PROJECT = core_modules +BUILD_BEFORE = library applications tools + +include rules.def + diff --git a/core/tools/clam_tools/clamtools_version.rc b/core/tools/clam_tools/clamtools_version.rc new file mode 100644 index 00000000..98db017c --- /dev/null +++ b/core/tools/clam_tools/clamtools_version.rc @@ -0,0 +1,46 @@ +#ifndef NO_VERSION +#include +#include <__build_version.h> +#include <__build_configuration.h> +#define BI_PLAT_WIN32 + // force 32 bit compile. +1 VERSIONINFO LOADONCALL MOVEABLE +FILEVERSION __build_FILE_VERSION_COMMAS +PRODUCTVERSION __build_PRODUCT_VERSION_COMMAS +FILEFLAGSMASK 0 +FILEFLAGS VS_FFI_FILEFLAGSMASK +#if defined(BI_PLAT_WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + // Language type = U.S. English(0x0409) and Character Set = Windows, Multilingual(0x04b0) + BLOCK "040904b0" // Matches VarFileInfo Translation hex value. + BEGIN + VALUE "CompanyName", __build_company "\000" +#ifndef _DEBUG + VALUE "FileDescription", "CLAM Tools for Building\000" +#else + VALUE "FileDescription", "CLAM Tools for Building (DEBUG)\000" +#endif + VALUE "FileVersion", __build_FILE_VERSION "\000" + VALUE "ProductVersion", __build_PRODUCT_VERSION "\000" + VALUE "InternalName", "CLAM Tools\000" + VALUE "LegalCopyright", __build_copyright "\000" + VALUE "LegalTrademarks", __build_legal_info "\000" + VALUE "OriginalFilename", "clamtools.exe\000" + VALUE "ProductName", __build_product_name "\000" + + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04b0 // US English (0x0409) and win32 multilingual (0x04b0) + END +END +#endif diff --git a/core/tools/clam_tools/makefile b/core/tools/clam_tools/makefile new file mode 100644 index 00000000..85ff86a9 --- /dev/null +++ b/core/tools/clam_tools/makefile @@ -0,0 +1,15 @@ +CONSOLE_MODE = true + +include cpp/variables.def + +PROJECT = clam_tools +TYPE = application +DEFINITIONS += __BUILD_STATIC_APPLICATION__ +LIBS_USED += pthread +ifeq "$(OMIT_VERSIONS)" "" + SOURCE += clamtools_version.rc +endif +TARGETS = value_tagger.exe version_stamper.exe vsts_version_fixer.exe write_build_config.exe + +include cpp/rules.def + diff --git a/core/tools/clam_tools/value_tagger.cpp b/core/tools/clam_tools/value_tagger.cpp new file mode 100644 index 00000000..df1724a4 --- /dev/null +++ b/core/tools/clam_tools/value_tagger.cpp @@ -0,0 +1,1003 @@ +/*****************************************************************************\ +* * +* Name : value_tagger * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Scoots through the entire known code base and builds a list of all the * +* outcome (and filter) values for that tree. A manifest of the names is * +* produced. Most of the behavior is driven by the ini file whose name is * +* passed on the command line. * +* Note that the set of items that can be searched for can be specified * +* in the ini file, although they must follow the format of: * +* pattern(name, value, description) * +* where the "pattern" is the search term and the other three items specify * +* the enumerated value to be marked. * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef __WIN32__ + #include +#endif + +#undef LOG +#define LOG(s) EMERGENCY_LOG(program_wide_logger::get(), astring(s)) + +using namespace algorithms; +using namespace application; +using namespace basis; +using namespace configuration; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; + +const int LONGEST_SEPARATION = 128; + // the longest we expect a single line of text to be in definition blocks. + // if the definition of an outcome or whatever is farther away than this + // many characters from a comment start, we will no longer consider the + // line to be commented out. this pretty much will never happen unless it's + // intentionally done to break this case. + +const char *SKIP_VALUE_PHRASE = "SKIP_TO_VALUE"; + // the special phrase we use to indicate that values should jump to + // a specific number. + +//////////////////////////////////////////////////////////////////////////// + +// this object records all the data that we gather for the defined items. +class item_record +{ +public: + astring _name; + int _value; + astring _description; + astring _path; + astring _extra_tag; //!< records special info for links. + + item_record(const astring &name = astring::empty_string(), int value = 999, + const astring &description = astring::empty_string(), + const astring &path = astring::empty_string(), + const astring &extra_tag = astring::empty_string()) + : _name(name), _value(value), _description(description), _path(path), + _extra_tag(extra_tag) {} +}; + +//////////////////////////////////////////////////////////////////////////// + +class search_record +{ +public: + search_record(const astring &search = astring::empty_string(), + bool is_link = false, search_record *link = NIL) + : _search(search), _no_modify(false), _is_link(is_link), _our_link(link), + _current_value(0), _value_increment(1) {} + + // these properties are available for both real or linked records. + astring _search; // our term to search for in the files. + bool _no_modify; // true if values should not be automatically incremented. + astring _tag; // extra information attached to this type. + + bool is_link() const { return _is_link; } + // returns true if this object is leeching off another object for data. + + search_record *our_link() const { return _our_link; } + // returns the object that this object is a mere shadow of. + + symbol_table &definitions() { + if (is_link()) return _our_link->_definitions; + else return _definitions; + } + + int ¤t_value() { + if (is_link()) return _our_link->_current_value; + else return _current_value; + } + + int &value_increment() { + if (is_link()) return _our_link->_value_increment; + else return _value_increment; + } + + int_set &out_of_band() { + if (is_link()) return _our_link->_out_of_band; + else return _out_of_band; + } + +private: + bool _is_link; // true if this object links to another. + search_record *_our_link; // the search we share for our values. + symbol_table _definitions; + // the definitions that we found in the code. + int _current_value; // the next value to use for our term. + int _value_increment; + // how much to add for each new value, if this is an incrementing search. + int_set _out_of_band; + // values we've seen that were premature. we always want to honor this + // set, if it exists, but there will be nothing in it if the search has + // completely standard non-incrementing type. this could be varied by + // a non-incrementer linking to a standard incrementer. +}; + +//! a table of terms that we will search for in the code. +class active_searches : public symbol_table +{}; + +//////////////////////////////////////////////////////////////////////////// + +// this class provides us a way to easily sort our items based on value. + +class simple_sorter { +public: + int _index; + int _value; + simple_sorter(int index = 0, int value = 0) : _index(index), _value(value) {} + bool operator < (const simple_sorter &to_compare) const + { return _value < to_compare._value; } + bool operator == (const simple_sorter &to_compare) const + { return _value == to_compare._value; } +}; + +class sorting_array : public array {}; + +//////////////////////////////////////////////////////////////////////////// + +class value_tagger : public application_shell +{ +public: + value_tagger(); + virtual ~value_tagger(); + DEFINE_CLASS_NAME("value_tagger"); + int execute(); + int print_instructions_and_exit(); + + bool process_tree(const astring &path); + // called on each directory hierarchy that we need to process. + + bool process_file(const astring &path); + // examines the file specified to see if it matches our needs. + + bool parse_define(const astring &scanning, int indy, astring &name, + int &value, astring &description, int &num_start, int &num_end); + // processes the string in "scanning" to find parentheses surrounding + // the "name", "value" and "description". the "description" field may + // occupy multiple lines, so all are gathered together to form one + // unbroken string. the "num_start" and "num_end" specify where the + // numeric value was found, in case it needs to be patched. + +private: + ini_configurator *_ini; // the configuration for what we'll scan. + string_table _dirs; // the list of directories. + string_table _dirs_seen; // full list of already processed directories. + astring _manifest_filename; // the name of the manifest we'll create. + byte_filer _manifest; // the actual file we're building. + active_searches _search_list; // tracks our progress in scanning files. + int_array _search_ordering; + // lists the terms in the order they should be applied. initially this + // carries the first pass items, but later will be reset for second pass. + int_array _postponed_searches; + // lists the searches that must wait until the main search is done. + string_table _modified_files; // the list of files that we touched. +}; + +//////////////////////////////////////////////////////////////////////////// + +value_tagger::value_tagger() +: application_shell(), + _ini(NIL), + _dirs_seen(10) +{ +} + +value_tagger::~value_tagger() +{ + WHACK(_ini); +} + +int value_tagger::print_instructions_and_exit() +{ + LOG(a_sprintf("%s usage:", filename(_global_argv[0]).basename().raw().s())); + LOG(""); + LOG("\ +This utility scans a code base for outcome and filter definitions. It will\n\ +only scan the header files (*.h) found in the directories specified. The\n\ +single parameter is expected to be an INI filename that contains the scanning\n\ +configuration. The INI file should be formatted like this (where the $HOME\n\ +can be any variable substitution from the environment):"); + LOG(""); + LOG("\ +[manifest]\n\ +output=$HOME/manifest.txt\n\ +\n\ +[searches]\n\ +DEFINE_OUTCOME=1\n\ +DEFINE_FILTER=1\n\ +\n\ +[directories]\n\ +$HOME/source/lib_src/library/basis\n\ +$HOME/source/lib_src/library\n\ +$HOME/source/lib_src/communication/sockets\n\ +$HOME/source/lib_src/communication\n\ +$HOME/source/lib_src\n\ +$HOME/source/app_src\n\ +$HOME/source/test_src\n\ +\n\ +[DEFINE_OUTCOME]\n\ +first=0\n\ +increment=-1\n\ +\n\ +[DEFINE_FILTER]\n\ +first=-1\n\ +increment=1\n\ +no_modify=1\n\ +\n\ +[DEFINE_API_OUTCOME]\n\ +no_modify=1\n\ +link=DEFINE_OUTCOME\n\ +tag=API\n\ +\n\ + The \"first\" field defines the starting value that should be assigned to\n\ +items.\n\ + The \"increment\" field specifies what to add to a value for the next item.\n\ + The optional \"no_modify\" flag means that the values should not be auto-\n\ +incremented; their current value will be used.\n\ + The optional \"link\" field defines this type of item as using the current\n\ +values for another type of item. In this case, API_OUTCOME will use the\n\ +values for OUTCOME to share its integer space, but API_OUTCOME is not auto-\n\ +incremented even though OUTCOME is. This causes the values for OUTCOME and\n\ +API_OUTCOME to be checked for uniqueness together, but only OUTCOME will be\n\ +auto-incremented. Note that only one level of linking is supported currently.\n\ + The optional \"tag\" can be used to distinguish the entries for a particular\n\ +search type if needed. This is most helpful for links, so that they can be\n\ +distinguished from their base type.\n\ +\n\ +"); + + return 23; +} + +astring header_string(const astring &build_number) +{ + return a_sprintf("\ +#ifndef GENERATED_VALUES_MANIFEST\n\ +#define GENERATED_VALUES_MANIFEST\n\ +\n\ +// This file contains all outcomes and filters for this build.\n\ +\n\ +// Generated for build %s on %s\n\ +\n\ +", build_number.s(), time_stamp::notarize(true).s()); +} + +astring footer_string(const byte_array &full_config_file) +{ + return a_sprintf("\n\ +// End of definitions.\n\ +\n\ +\n\ +// The following is the full configuration for this build:\n\ +\n\ +/*\n\ +\n\ +%s\n\ +*/\n\ +\n\ +\n\ +#endif // outer guard.\n\ +", (char *)full_config_file.observe()); +} + +int value_tagger::execute() +{ + FUNCDEF("execute"); + if (_global_argc < 2) { + return print_instructions_and_exit(); + } + + log(time_stamp::notarize(true) + "value_tagger started.", basis::ALWAYS_PRINT); + + astring test_repository = environment::get("REPOSITORY_DIR"); + if (!test_repository) { + astring msg = "\ +There is a problem with a required build precondition. The following\r\n\ +variables must be set before the build is run:\r\n\ +\r\n\ + REPOSITORY_DIR This should point at the root of the build tree.\r\n\ +\r\n\ +There are also a few variables only required for CLAM-based compilation:\r\n\ +\r\n\ + MAKEFLAGS This should be set to \"-I $REPOSITORY_DIR/clam\".\r\n\ +\r\n\ +Note that on Win32 platforms, these should be set in the System or User\r\n\ +variables before running a build.\r\n"; +#ifdef __WIN32__ + ::MessageBox(0, to_unicode_temp(msg), + to_unicode_temp("Missing Precondition"), MB_ICONWARNING|MB_OK); +#endif + non_continuable_error(class_name(), func, msg); + } + + astring ini_file = _global_argv[1]; // the name of our ini file. + _ini = new ini_configurator(ini_file, ini_configurator::RETURN_ONLY); + + // read the name of the manifest file to create. + _manifest_filename = _ini->load("manifest", "output", ""); + if (!_manifest_filename) { + non_continuable_error(class_name(), ini_file, "The 'output' file entry is missing"); + } + _manifest_filename = parser_bits::substitute_env_vars(_manifest_filename); + + LOG(astring("Sending Manifest to ") + _manifest_filename); + LOG(""); + + filename(_manifest_filename).unlink(); + // clean out the manifest ahead of time. + + // read the list of directories to scan for code. + string_table temp_dirs; + bool read_dirs = _ini->get_section("directories", temp_dirs); + if (!read_dirs || !temp_dirs.symbols()) { + non_continuable_error(class_name(), ini_file, + "The 'directories' section is missing"); + } + for (int i = 0; i < temp_dirs.symbols(); i++) { +//log(astring("curr is ") + current); + astring current = parser_bits::substitute_env_vars(temp_dirs.name(i)); + _dirs.add(current, ""); + } + + LOG(astring("Directories to scan...")); + LOG(_dirs.text_form()); + + astring rdir = environment::get("REPOSITORY_DIR"); + astring fname; + astring parmfile = environment::get("BUILD_PARAMETER_FILE"); + if (parmfile.t()) fname = parmfile; + else fname = rdir + "/build.ini"; + + // read the list of search patterns. + string_table searches; + bool read_searches = _ini->get_section("searches", searches); + if (!read_searches || !searches.symbols()) { + non_continuable_error(class_name(), ini_file, + "The 'searches' section is missing"); + } + + LOG("Searching for..."); + LOG(searches.text_form()); + + // now make sure that we get the configuration for each type of value. + for (int i = 0; i < searches.symbols(); i++) { + const astring &curr_name = searches.name(i); + + search_record *check_search = _search_list.find(curr_name); + if (check_search) { + non_continuable_error(class_name(), ini_file, + astring("section ") + curr_name + " is being defined twice"); + } + + { + // check for whether this section is linked to another or not. + astring linked = _ini->load(curr_name, "link", ""); + search_record *our_link_found = NIL; + if (linked.t()) { + // we found that this should be linked to another item. + our_link_found = _search_list.find(linked); + if (!our_link_found) { + non_continuable_error(class_name(), ini_file, + astring("linked section ") + curr_name + " is linked to missing " + "section " + linked); + } + search_record new_guy(curr_name, true, our_link_found); + _search_list.add(curr_name, new_guy); + } else { + // this section is a stand-alone section. + search_record new_guy(curr_name); + _search_list.add(curr_name, new_guy); + } + } + + // find our new search cabinet again so we can use it. + search_record *curr_search = _search_list.find(curr_name); + if (!curr_search) { + non_continuable_error(class_name(), ini_file, + astring("section ") + curr_name + " is missing from table " + "after addition; logic error"); + } + + // specify some defaults first. + int start = 0; + int increm = 1; + if (!curr_search->is_link()) { + // a linked object doesn't get to specify starting value or increment. + start = _ini->load(curr_name, "first", start); + curr_search->current_value() = start; + increm = _ini->load(curr_name, "increment", increm); + curr_search->value_increment() = increm; + } else { + start = curr_search->our_link()->current_value(); + increm = curr_search->our_link()->value_increment(); + } + + int no_modify = _ini->load(curr_name, "no_modify", 0); + if (no_modify) { + curr_search->_no_modify = true; + } + + astring tag = _ini->load(curr_name, "tag", ""); + if (tag.t()) { + curr_search->_tag = tag; + } + + a_sprintf to_show("%s: no_modify=%s", curr_name.s(), + no_modify? "true" : "false"); + + if (curr_search->is_link()) { + // links show who they're hooked to. + to_show += astring(" link=") + curr_search->our_link()->_search; + } else { + // non-links get to show off their start value and increment. + to_show += a_sprintf(" start=%d increment=%d", start, increm); + } + if (tag.t()) { + to_show += astring(" tag=") + curr_search->_tag; + } + LOG(to_show); + } + LOG(""); + + // now gather some info about the build that we can plug into the manifest. + + byte_filer build_file(fname, "r"); + if (!build_file.good()) { + non_continuable_error(class_name(), build_file.filename(), + "Could not find the build configuration; is REPOSITORY_DIR set?"); + } + byte_array full_config; + build_file.read(full_config, 100000); // a good chance to be big enough. + build_file.close(); + +//log("got config info:"); +//log((char *)full_config.observe()); + + astring build_number; + ini_configurator temp_ini(fname, configurator::RETURN_ONLY); + build_number += temp_ini.load("version", "major", ""); + build_number += "."; + build_number += temp_ini.load("version", "minor", ""); + build_number += "."; + build_number += temp_ini.load("version", "revision", ""); + build_number += "."; + build_number += temp_ini.load("version", "build", ""); + if (build_number.equal_to("...")) { + non_continuable_error(class_name(), build_file.filename(), + "Could not read the build number; is build parameter file malformed?"); + } + +//log(astring("got build num: ") + build_number); + + // now that we know what file to create, write the header blob for it. + _manifest.open(_manifest_filename, "wb"); + if (!_manifest.good()) { + non_continuable_error(class_name(), _manifest_filename, + "Could not write to the manifest file!"); + } + _manifest.write(header_string(build_number)); + + // make sure we have the right ordering for our terms. items that are + // non-modify types must come before the modifying types. + for (int i = 0; i < _search_list.symbols(); i++) { + search_record &curr_reco = _search_list[i]; + if (curr_reco._no_modify) + _search_ordering += i; + else + _postponed_searches += i; + } + + // scan across each directory specified for our first pass. + LOG("First pass..."); + for (int i = 0; i < _dirs.symbols(); i++) { + if (_dirs.name(i).begins("#") || _dirs.name(i).begins(";")) continue; // skip comment. + LOG(astring(" Processing: ") + _dirs.name(i)); + bool ret = process_tree(_dirs.name(i)); + if (!ret) { + LOG(astring("Problem encountered in directory ") + _dirs.name(i)); + } + } + LOG(""); + + // second pass now. + LOG("Second pass..."); + _search_ordering = _postponed_searches; // recharge the list for 2nd pass. + _dirs_seen.reset(); // drop any directories we saw before. + for (int i = 0; i < _dirs.symbols(); i++) { + if (_dirs.name(i).begins("#") || _dirs.name(i).begins(";")) continue; // skip comment. + LOG(astring(" Processing: ") + _dirs.name(i)); + bool ret = process_tree(_dirs.name(i)); + if (!ret) { + LOG(astring("Problem encountered in directory ") + _dirs.name(i)); + } + } + LOG(""); + + const astring quote = "\""; + const astring comma = ","; + + // scoot across all the completed searches and dump results. + for (int i = 0; i < _search_list.symbols(); i++) { + search_record &curr_reco = _search_list[i]; + const astring &pattern = curr_reco._search; + + _manifest.write(astring("/* START ") + pattern + "\n"); + _manifest.write(astring("[") + pattern + "]\n"); + + if (!curr_reco.is_link()) { + // scoot across all definitions and print them out. + + // do the print out in order, as dictated by the sign of the increment. + sorting_array sortie; + for (int j = 0; j < curr_reco.definitions().symbols(); j++) { + const item_record &rec = curr_reco.definitions().get(j); + sortie += simple_sorter(j, rec._value); + } + shell_sort(sortie.access(), sortie.length(), + negative(curr_reco.value_increment())); + + for (int j = 0; j < sortie.length(); j++) { + int indy = sortie[j]._index; + const item_record &rec = curr_reco.definitions().get(indy); + astring to_write = " "; + if (rec._extra_tag.t()) { + to_write += astring("(") + rec._extra_tag + ") "; + } + to_write += quote + rec._name + quote + comma + " "; + to_write += quote + a_sprintf("%d", rec._value) + quote + comma + " "; + to_write += quote + rec._description + quote + comma + " "; + to_write += quote + rec._path + quote; + to_write += "\n"; + _manifest.write(to_write); + } + } else { + // this is just a link. + astring to_write = " Linked to search item "; + to_write += curr_reco.our_link()->_search; + to_write += "\n"; + _manifest.write(to_write); + } + + _manifest.write(astring("END ") + pattern + " */\n\n"); + } + + _manifest.write(footer_string(full_config)); + + // show all the modified files. + if (_modified_files.symbols()) { + const int syms = _modified_files.symbols(); + LOG("Modified Files:"); + LOG("==============="); + for (int i = 0; i < syms; i++) { + LOG(_modified_files.name(i)); + } + } else { + LOG("No files needed modification for generated values."); + } + LOG(""); + + log(time_stamp::notarize(true) + "value_tagger finished.", ALWAYS_PRINT); + + return 0; +} + +#define INBO (indy < scanning.length()) + // a macro that makes length checking less verbose. + +// make sure we drop any spaces in between important bits. +#define SKIP_SPACES \ + while (INBO && parser_bits::white_space(scanning[indy])) indy++; + +// return with a failure but say why it happened. +#define FAIL_PARSE(why) { \ + log(astring("failed to parse the string because ") + why + ".", ALWAYS_PRINT); \ + return false; \ +} + +bool value_tagger::parse_define(const astring &scanning, int indy, + astring &name, int &value, astring &description, int &num_start, + int &num_end) +{ + // prepare our result objects. + name = ""; value = -1; description = ""; num_start = -1; num_end = -1; + + SKIP_SPACES; + + // look for starting parenthesis. + if (!INBO || (scanning[indy] != '(') ) + FAIL_PARSE("the first parenthesis is missing"); + + indy++; // skip paren. + SKIP_SPACES; + + // find the name of the item being defined. + while (INBO && (scanning[indy] != ',') ) { + name += scanning[indy]; + indy++; + } + + indy++; // skip the comma. + SKIP_SPACES; + + astring num_string; + num_start = indy; + while (INBO && parser_bits::is_numeric(scanning[indy])) { + num_string += scanning[indy]; + indy++; + } + num_end = indy - 1; + value = num_string.convert(0); + + SKIP_SPACES; + + if (!INBO || (scanning[indy] != ',') ) + FAIL_PARSE("the post-value comma is missing"); + + indy++; + SKIP_SPACES; + + if (!INBO || (scanning[indy] != '"') ) + FAIL_PARSE("the opening quote for the description is missing"); + + indy++; // now we should be at raw text. + + // scan through the full description, taking into account that it might + // be broken across multiple lines as several quoted bits. + bool in_quote = true; // we're inside a quote now. + while (INBO && (scanning[indy] != ')') ) { + const char curr = scanning[indy]; +//hmmm: escaped quotes are not currently handled. + if (curr == '"') in_quote = !in_quote; // switch quoting state. + else if (in_quote) description += curr; + indy++; + } + + return scanning[indy] == ')'; +} + +bool value_tagger::process_file(const astring &path) +{ + byte_filer examining(path, "rb"); + if (!examining.good()) { + log(astring("Error reading file: ") + path, ALWAYS_PRINT); + return false; + } + examining.seek(0, byte_filer::FROM_END); + int fsize = int(examining.tell()); + examining.seek(0, byte_filer::FROM_START); + + astring contents('\0', fsize + 20); + int bytes_read = examining.read((abyte *)contents.access(), fsize); + // read the file directly into a big astring. + examining.close(); + contents[bytes_read] = '\0'; + contents.shrink(); // drop any extra stuff at end. + + bool modified = false; // set to true if we need to write the file back. + + // check if the file matches our phrases of interest. + bool matched = false; + for (int q = 0; q < _search_list.symbols(); q++) { + search_record &curr_reco = _search_list[q]; + if (contents.contains(curr_reco._search)) { +//_manifest.write(astring("MATCH-") + curr_pattern + ": " + path + "\n" ); //temp + matched = true; + break; + } + } + + if (!matched) return true; + + // now we have verified that there's something interesting in this file. + // go through to find the interesting bits. + + // we do this in the search ordering that we established earlier, so we + // will tag the values in the proper order. + for (int x = 0; x < _search_ordering.length(); x++) { + int q = _search_ordering[x]; // get our real index. + search_record &curr_reco = _search_list[q]; + const astring &curr_pattern = curr_reco._search; +///log(astring("now seeking ") + curr_pattern); + int start_from = 0; // where searches will start from. + + while (true) { + // search forward for next match. + int indy = contents.find(curr_pattern, start_from); + if (negative(indy)) break; // no more matches. + start_from = indy + 5; // ensure we'll skip past the last match. + + // make sure our deadly pattern isn't in front; we don't want to + // process the actual definition of the macro in question. +//log(a_sprintf("indy=%d [indy-1]=%c [indy-2]=%c", indy, contents[indy-1], contents[indy-2])); + if ( (indy > 3) && (contents[indy-1] == ' ') + && (contents[indy-2] == 'e') ) { + int def_indy = contents.find("#define", indy, true); +//log(astring("checking ") + curr_pattern + a_sprintf(": defindy %d, ", def_indy) + path + "\n" ); + + if (non_negative(def_indy) && (absolute_value(indy - def_indy) < 12) ) { + // they're close enough that we probably need to skip this + // occurrence of our search term. +//_manifest.write(astring("DEMATCH-") + curr_pattern + ": had the #define! " + path + "\n" ); + continue; + } + } + + // make sure we don't include commented lines in consideration. + int comm_indy = contents.find("//", indy, true); + if (non_negative(comm_indy)) { +//log("found a comment marker"); + // we found a comment before the definition, but we're not sure how + // far before. + if (absolute_value(comm_indy - indy) < LONGEST_SEPARATION) { +//log("comment is close enough..."); + // they could be on the same line... unless lines are longer than + // our constant. + bool found_cr = false; + for (int q = comm_indy; q < indy; q++) { + if (parser_bits::is_eol(contents[q])) { + found_cr = true; + break; + } + } + if (!found_cr) { + // if there's a comment before the definition and no carriage + // returns in between, then this is just a comment. +//log(astring("DEMATCH-") + curr_pattern + ": had the comment! " + path + "\n" ); + continue; + } + } + } + + // now we are pretty sure this is a righteous definition of an outcome, + // and not the definition of the macro itself. + int value, num_start, num_end; + astring name, description; + bool found_it = parse_define(contents, indy + curr_pattern.length(), + name, value, description, num_start, num_end); + if (!found_it) { + log(astring("there was a problem parsing ") + curr_pattern + " in " + path, ALWAYS_PRINT); + continue; + } + + // handle the special keyword for changing the value. this is useful + // if you want a set of outcomes to start at a specific range. + if (name.equal_to(SKIP_VALUE_PHRASE)) { + LOG(astring("\tSkipping value for ") + curr_pattern + + a_sprintf(" to %d because of request in\n\t", value) + path); + curr_reco.current_value() = value; + } + while (true) { + // make sure that the current value is not already in use. + if (!curr_reco.out_of_band().member(curr_reco.current_value())) + break; + // if we had a match above, we need to adjust the current value. + curr_reco.current_value() += curr_reco.value_increment(); + } + if (name.equal_to(SKIP_VALUE_PHRASE)) { + continue; // keep going now that we vetted the current value. + } + +//must catch some conditions here for values: +// for incrementing types, we can always just try to use the next value +// once we know it wasn't already defined out of band? +// for non-incrementing types, we need to ensure we haven't already seen +// the thing. do we just always add a value seen to out of band? +// for mixed types, the incrementing side needs to not reuse out of band +// values. + + astring other_place; // the other place it was defined. + if (curr_reco.out_of_band().member(value) && curr_reco._no_modify) { + // this is bad; we have already seen this value elsewhere... + for (int x = 0; x < curr_reco.definitions().symbols(); x++) { + // see if we can find the previous definition in our list. + if (value == curr_reco.definitions()[x]._value) + other_place = curr_reco.definitions()[x]._path; + } + non_continuable_error(class_name(), path, + a_sprintf("There is a duplicate value here for %s=%d ! " + "Also defined in %s.", name.s(), value, other_place.s())); + } + + // we care sometimes that this value is different than the next + // sequential one we'd assign. if it's a non-modifying type of + // search, then we can't change the assigned value anyway--we can + // only report the error in re-using a value (above). + if (!curr_reco._no_modify) { + // check that the defined value matches the next one we'd assign. + if (value != curr_reco.current_value()) { + // patch the value with the appropriate one we've been tracking. + modified = true; + value = curr_reco.current_value(); + contents.zap(num_start, num_end); // remove old fusty value. + contents.insert(num_start, a_sprintf("%d", value)); + _modified_files.add(path, ""); + } + // move the current value up (or down). + curr_reco.current_value() += curr_reco.value_increment(); + } else { + // non-modifying type of value here. +//anything to do? + } + + curr_reco.out_of_band() += value; + // we've vetted the value, and now we're definitely using it. + + // make sure they aren't trying to reuse the name for this item. + item_record rec; + bool found_name = false; // we don't want to find name already there. + if (curr_reco.definitions().find(name)) { + rec = *curr_reco.definitions().find(name); + found_name = true; + } + if (found_name) { + // this is bad. this means we are not unique. remove the manifest + // file due to this error. + _manifest.close(); // close the file since we want to whack it. + filename(_manifest_filename).unlink(); + non_continuable_error(class_name(), path, + a_sprintf("There is a duplicate name here (%s)! " + "Also defined in %s.", name.s(), rec._path.s())); + } + + // record the definition in the appropriate table. + curr_reco.definitions().add(name, item_record(name, value, + description, path, curr_reco._tag)); + +//log(curr_pattern + a_sprintf(": name=%s value=%d desc=[%s]\n", name.s(), value, description.s())); + + } + } + + if (modified) { + // rewrite the file, since we modified its contents. + bool chmod_result = filename(path).chmod(filename::ALLOW_BOTH, + filename::USER_RIGHTS); +/* + int chmod_value; +#ifdef __UNIX__ + chmod_value = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; +#elif defined(__WIN32__) + chmod_value = _S_IREAD | _S_IWRITE; +#else + //unknown. let's try unix... + chmod_value = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; +#endif + int chmod_result = chmod(path.s(), chmod_value); +*/ + if (!chmod_result) { + log(astring("there was a problem changing permissions on ") + path + + "; writing the new version might fail.", ALWAYS_PRINT); + } + + byte_filer rewriting(path, "wb"); + rewriting.write(contents); + rewriting.close(); + } + + return true; +} + +bool value_tagger::process_tree(const astring &path) +{ + directory_tree dir(path, "*.h"); + if (!dir.good()) return false; + + dir_tree_iterator *ted = dir.start(directory_tree::prefix); + // create our iterator to perform a prefix traversal. + + filename curr_dir; // the current path the iterator is at. + string_array files; // the filenames held at the iterator. + + while (directory_tree::current(*ted, curr_dir, files)) { + // we have a good directory to process. + + // omit any subdirectories that exactly match directories we've already + // scanned. necessary to avoid redoing whole areas. + if (!_dirs_seen.find(curr_dir)) { + // deal with each matching header file we've found. + for (int i = 0; i < files.length(); i++) { + bool file_ret = process_file(filename(curr_dir.raw(), files[i])); + if (!file_ret) { + log(astring("There was an error while processing ") + files[i], ALWAYS_PRINT); + } + } + + _dirs_seen.add(curr_dir, ""); + } + + // go to the next place. + directory_tree::next(*ted); + } + + directory_tree::throw_out(ted); + return true; +} + +HOOPLE_MAIN(value_tagger, ) + +#ifdef __BUILD_STATIC_APPLICATION__ + // static dependencies found by buildor_gen_deps.sh: + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif // __BUILD_STATIC_APPLICATION__ + diff --git a/core/tools/clam_tools/version.ini b/core/tools/clam_tools/version.ini new file mode 100644 index 00000000..1ae20aa5 --- /dev/null +++ b/core/tools/clam_tools/version.ini @@ -0,0 +1,6 @@ +[version] +description = CLAM Tools for Building +root = clamtools +name = CLAM Tools +extension = exe + diff --git a/core/tools/clam_tools/version_stamper.cpp b/core/tools/clam_tools/version_stamper.cpp new file mode 100644 index 00000000..a668a9f0 --- /dev/null +++ b/core/tools/clam_tools/version_stamper.cpp @@ -0,0 +1,131 @@ +/*****************************************************************************\ +* * +* Name : version_stamper * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1995-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef LOG +#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger(), to_print) + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace versions; + +//! This class creates resource information for applications and libraries to be version stamped. +/*! + This creates a resource (.rc) file and a C++ header (.h) file when given the directory where + a version information file (version.ini) resides. It creates the files in that directory. +*/ + +class version_stamper : public application_shell +{ +public: + version_stamper(); + ~version_stamper(); + + DEFINE_CLASS_NAME("version_stamper"); + + int execute(); + // performs the main action of creating resource and code files. +}; + +////////////// + +version_stamper::version_stamper() : application_shell() +{ +} + +version_stamper::~version_stamper() {} + +int version_stamper::execute() +{ +/// FUNCDEF("execute"); + SETUP_CONSOLE_LOGGER; // override the file_logger from app_shell. + if (application::_global_argc < 2) { + log(astring("The directory where the 'version.ini' file is located\n" + "must be specified as the first parameter of this program. Another\n" + "version file may optionally be specified as the second parameter of\n" + "the program; the version contained in this file will be used to set\n" + "the version of the file specified in the first parameter.\n" + "Additionally, if the environment variable 'DEBUG' exists, then the\n" + "generated RC file will be marked as a debug build. Otherwise it is\n" + "marked as a release build. Note that the CLAM system automatically\n" + "sets this for you.\n\n"), ALWAYS_PRINT); + return 1; + } + + astring path_name = application::_global_argv[1]; + astring source_version_file; // blank by default. + if (application::_global_argc > 2) + source_version_file = application::_global_argv[2]; + bool ret = version_ini::one_stop_version_stamp(path_name, source_version_file, true); + if (!ret) return 1; // failure. + return 0; // success. +} + +HOOPLE_MAIN(version_stamper, ) + +#ifdef __BUILD_STATIC_APPLICATION__ + // static dependencies found by buildor_gen_deps.sh: + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif // __BUILD_STATIC_APPLICATION__ + diff --git a/core/tools/clam_tools/vsts_version_fixer.cpp b/core/tools/clam_tools/vsts_version_fixer.cpp new file mode 100644 index 00000000..8fc8d3cd --- /dev/null +++ b/core/tools/clam_tools/vsts_version_fixer.cpp @@ -0,0 +1,363 @@ +/*****************************************************************************\ +* * +* Name : vsts_version_fixer * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2008-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) +#undef BASE_LOG +#define BASE_LOG(s) program_wide_logger::get().log(s, ALWAYS_PRINT) + +using namespace application; +//using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace processes; +using namespace structures; +using namespace timely; +using namespace versions; + +//#define DEBUG_VSTS_VERSION_FIXER + // uncomment for noisy version. + +//////////////////////////////////////////////////////////////////////////// + +class vsts_version_fixer : public application::application_shell +{ +public: + vsts_version_fixer() : application_shell() {} + virtual ~vsts_version_fixer() {} + + virtual int execute(); + + DEFINE_CLASS_NAME("vsts_version_fixer"); + + void remove_confusing_files(); + //!< tosses out the generated files that confuse ms devstudio. + +//move these + typedef bool spider_method(const directory ¤t); + //!< prototype for functions that are called during directory spidering. + /*!< this function should do whatever work is needed on the items in + that "current" directory. true should be returned by this method when + the traversal of the directory is still desired. if there is a reason + to stop traversing the directory hierarchy, then it should return false. */ + +//hmmm: support postfix and in order also. +//hmmm: support reporting where the spidering stopped. + bool spider_directory(directory start, spider_method to_invoke); + //!< traverses hierarchy "start" in prefix order while calling "to_invoke". + /*!< true is returned if all invoked spider methods returned true. + otherwise, false is returned. */ +//move those + + bool perform_version_stamping(const filename &start_name); + //!< finds all version ini files and applies stamps using them. + + void whack_in_subdirs(const directory &start, + const string_array &file_whacks, const string_array &dir_whacks); + //!< recursively cleans all items found in "file_whacks" and "dir_whacks". + /*!< "file_whacks" is a list of file suffixes to whack. for example, to + remove all files matching a pattern *.exe, pass in just ".exe" in the + "file_whacks". the "dir_whacks" list is a list of directories to + completely obliterate where found. */ +}; + +HOOPLE_MAIN(vsts_version_fixer, ) + +//////////////////////////////////////////////////////////////////////////// + +//hmmm: move to a useful place; maybe even in directory class? +bool vsts_version_fixer::spider_directory(directory start, + spider_method to_invoke) +{ +// FUNCDEF("spider_directory"); + + using namespace basis; + +//LOG(astring("spider_directory: ") + start.path()); + // call our method on this directory first. this ensures that we have + // dealt with it before we spider off elsewhere. + bool ret = to_invoke(start); + if (!ret) return false; // bail. + + // now let's look at the subdirectories. we'll recurse on all of them in + // the order listed. + const string_array &dirs = start.directories(); +//LOG(astring("dirs found to spider: ") + dirs.text_form()); + for (int dir_indy = 0; dir_indy < dirs.length(); dir_indy++) { + const astring ¤t_dir = dirs[dir_indy]; +//LOG(astring("currdir into ") + current_dir); + if (current_dir.equal_to(".svn")) continue; // skip this. + if (current_dir.equal_to("CVS")) continue; // skip this also. + directory new_dir(start.path() + "/" + current_dir, start.pattern().observe()); + bool ret = spider_directory(new_dir, to_invoke); + if (!ret) return false; // bail from subdir issue. + } + // if we made it to here, everything was groovy. + return true; +} + +//////////////////////////////////////////////////////////////////////////// + +#define static_class_name() "vsts_version_fixer" + +// global variables used to communicate with whacking_spider. +string_array global_file_whacks; +string_array global_dir_whacks; + +bool whacking_spider(const directory ¤t) +{ +// FUNCDEF("whacking_spider"); + using namespace basis; +//LOG(astring("whacking_spider: ") + current.path()); + // iterate across the files in the directory and check for evil ones. + const string_array &files = current.files(); + for (int file_indy = 0; file_indy < files.length(); file_indy++) { + const astring ¤t_file = files[file_indy]; +//LOG(astring("currfile ") + current_file); + // now iterate across our pattern list to see if this thing is + // one of the offending files. + for (int pat_indy = 0; pat_indy < global_file_whacks.length(); pat_indy++) { +//LOG(astring("currpat ") + global_file_whacks[pat_indy]); + if (current_file.iends(global_file_whacks[pat_indy])) { + filename goner(current.path() + "/" + current_file); + BASE_LOG(astring("whack file: ") + goner.raw()); + goner.unlink(); + break; // stop looking at the pattern list for matches. + } + } + } + + // okay, now that we've cleaned out those files, let's look at the + // subdirectories. + const string_array &dirs = current.directories(); + for (int dir_indy = 0; dir_indy < dirs.length(); dir_indy++) { + const astring ¤t_dir = dirs[dir_indy]; +//LOG(astring("currdir ") + current_dir); + for (int pat_indy = 0; pat_indy < global_dir_whacks.length(); pat_indy++) { + if (current_dir.iequals(global_dir_whacks[pat_indy])) { + filename goner(current.path() + "/" + current_dir); + BASE_LOG(astring("whack dir: ") + goner.raw()); +//hmmm: plug in recursive delete here instead. +basis::un_int kid; +launch_process::run("rm", astring("-rf ") + goner.raw(), launch_process::AWAIT_APP_EXIT, kid); + break; // skip remainder of patterns for this dir. + } + } + } + return true; +} + +#undef static_class_name + +//////////////////////////////////////////////////////////////////////////// + +void vsts_version_fixer::whack_in_subdirs(const directory &start, + const string_array &file_whacks, const string_array &dir_whacks) +{ + FUNCDEF("whack_in_subdirs"); + using namespace basis; + + // save the lists so the spider method can see them. + // note that this approach with a global variable would be bad if there + // were concurrent invocations of the spidering, but we're not doing + // that here. + global_file_whacks = file_whacks; + global_dir_whacks = dir_whacks; + + bool worked = spider_directory(start, whacking_spider); + if (!worked) { + LOG(astring("spidering of ") + start.path() + " failed for some reason."); + } +} + +//////////////////////////////////////////////////////////////////////////// + +#define static_class_name() "vsts_version_fixer" + +basis::astring global_build_ini; + +bool stamping_spider(const directory ¤t) +{ +// FUNCDEF("stamping_spider"); + using namespace basis; +//LOG(astring("stamping_spider: ") + current.path()); + + const string_array &files = current.files(); + for (int file_indy = 0; file_indy < files.length(); file_indy++) { + const astring ¤t_file = files[file_indy]; +//LOG(astring("currfile ") + current_file); + // we won't process the "core_version.ini" file, which is a special + // case that is somewhat well known as not being a file used (by us) + // for dlls. + if (current_file.ends("version.ini") + && !current_file.iequals("core_version.ini") ) { +//LOG(astring("found ver file: ") + current.path() + "/" + current_file); + version_ini::one_stop_version_stamp(current.path() + "/" + current_file, + global_build_ini, true); + } + } + return true; +} + +#undef static_class_name + +//////////////////////////////////////////////////////////////////////////// + +bool vsts_version_fixer::perform_version_stamping(const filename &start_name) +{ +// FUNCDEF("perform_version_stamping"); + directory start(start_name); + return spider_directory(start, stamping_spider); +} + +//////////////////////////////////////////////////////////////////////////// + +void vsts_version_fixer::remove_confusing_files() +{ + using namespace basis; + // clean out a few directories that show up in the source tree from c# + // projects compilation. c# projects always rebuild every time anyways, + // so this doesn't lose us any compilation time. the only thing c# + // projects don't ever seem to rebuild is their version resource, unless + // they're forced to totally recompile like we cause below. + string_array source_file_whacks; // none right now. + string_array source_dir_whacks; + source_dir_whacks += "obj"; + source_dir_whacks += "Debug"; + source_dir_whacks += "Release"; + source_dir_whacks += "bin"; + source_dir_whacks += "temp_build"; + directory repo_source(environment::get("REPOSITORY_DIR") + "/source"); + whack_in_subdirs(repo_source, source_file_whacks, source_dir_whacks); + directory libra_src(environment::get("REPOSITORY_DIR") + "/libraries"); + whack_in_subdirs(libra_src, source_file_whacks, source_dir_whacks); + directory produ_src(environment::get("REPOSITORY_DIR") + "/products"); + whack_in_subdirs(produ_src, source_file_whacks, source_dir_whacks); + +/* this never helped. + // clean out a variety of bad files in the objects hierarchy. + // currently this is just the generated RES files which we have seen cause + // vsts to think apps and dlls are up to date when they are actually not. + directory repo_objects(environment::get("REPOSITORY_DIR")); + string_array objects_file_whacks; + objects_file_whacks += ".res"; + string_array objects_dir_whacks; // none right now. + whack_in_subdirs(repo_objects, objects_file_whacks, objects_dir_whacks); +*/ +} + +int vsts_version_fixer::execute() +{ + FUNCDEF("execute"); + using namespace basis; + log(time_stamp::notarize(true) + "vsts_version_fixer started.", ALWAYS_PRINT); + + remove_confusing_files(); + + astring repo_dir = environment::get("REPOSITORY_DIR"); + + // figure out which build parameter file to use. + global_build_ini = ""; + astring parmfile = environment::get("BUILD_PARAMETER_FILE"); + if (parmfile.t()) { + global_build_ini = parmfile; +LOG(astring("found parm variable ") + parmfile); + } else { + // they didn't specify the file. argh. + global_build_ini = repo_dir + "/production/feisty_meow_config.ini"; + if (!filename(global_build_ini).exists()) { +LOG(astring("guess not found: ") + global_build_ini); + LOG("cannot locate the build configuration file."); + return 3; + } + } + + // now stamp versions on everything we can find. + filename repo_source = repo_dir + "/../../libraries"; + if (!repo_source.exists()) { + repo_source = repo_dir + "/source"; + if (!repo_source.exists()) { + LOG("cannot locate the main library source location."); + return 3; + } + } +LOG(astring("chose source dir as ") + repo_source); + perform_version_stamping(repo_source); + + filename repo_apps = repo_dir + "/../../products"; + if (repo_apps.exists()) { + perform_version_stamping(repo_apps); + } + log(time_stamp::notarize(true) + "vsts_version_fixer finished.", ALWAYS_PRINT); + return 0; +} + +#ifdef __BUILD_STATIC_APPLICATION__ + // static dependencies found by buildor_gen_deps.sh: + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif // __BUILD_STATIC_APPLICATION__ + diff --git a/core/tools/clam_tools/write_build_config.cpp b/core/tools/clam_tools/write_build_config.cpp new file mode 100644 index 00000000..53cfa0a4 --- /dev/null +++ b/core/tools/clam_tools/write_build_config.cpp @@ -0,0 +1,434 @@ +/*****************************************************************************\ +* * +* Name : write_build_config * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1995-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "write_build_config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace application; +using namespace basis; +using namespace configuration; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace versions; + +const int MAX_LINE_SIZE = 2048; + //!< we should never see an ini line longer than this. + +const int MAX_HEADER_FILE = 128 * KILOBYTE; + //!< an excessively long allowance for the maximum generated header size. + +const char *DEFINITIONS_STATEMENT = "DEFINITIONS"; + //!< the tag we see in the config file for directly compatible macros. + +const char *EXPORT_STATEMENT = "export "; + //!< a tag we see on variables to be inherited by subshells + +// make conditionals that we will eat. +const char *IFEQ_STATEMENT = "ifeq"; +const char *IFNEQ_STATEMENT = "ifneq"; +const char *ENDIF_STATEMENT = "endif"; + +#undef LOG +#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print) + +write_build_config::write_build_config() +: application_shell(), + _end_matter(new astring), + _ver(new version), + _nesting(0) +{} + +write_build_config::~write_build_config() +{ + WHACK(_end_matter); + WHACK(_ver); +} + +const string_set &write_build_config::exclusions() +{ + static string_set _hidden; + static bool _initted = false; + if (!_initted) { + _hidden += "DEBUG"; + _hidden += "OPTIMIZE"; + _hidden += "STRICT_WARNINGS"; + } + return _hidden; +} + +// adds some more material to dump at the end of the file. +#define ADD_COMMENT_RETURN(sym, val) { \ + *_end_matter += astring(" ") + sym + " = " + val + "\n"; \ + return common::OKAY; \ +} + +outcome write_build_config::output_macro(const astring &symbol, + const astring &value, astring &accumulator) +{ + // drop any excluded items to avoid interfering with devstu solution. + if (exclusions().member(symbol)) + ADD_COMMENT_RETURN(symbol, value); + // drop any malformed symbols or values. + if (symbol.contains("\"") || value.contains("\"")) + ADD_COMMENT_RETURN(symbol, value); + accumulator += " #ifndef "; + accumulator += symbol; + accumulator += "\n"; + accumulator += " #define "; + accumulator += symbol; + accumulator += " \""; + accumulator += value; + accumulator += "\"\n"; + accumulator += " #endif\n"; + return common::OKAY; +} + +bool write_build_config::process_version_parts(const astring &symbol, + const astring &value) +{ + if (symbol.equal_to("major")) + { _ver->set_component(version::MAJOR, value); return true; } + if (symbol.equal_to("minor")) + { _ver->set_component(version::MINOR, value); return true; } + if (symbol.equal_to("revision")) + { _ver->set_component(version::REVISION, value); return true; } + if (symbol.equal_to("build")) + { _ver->set_component(version::BUILD, value); return true; } + return false; +} + +bool write_build_config::check_nesting(const astring &to_check) +{ + if (to_check.compare(IFEQ_STATEMENT, 0, 0, int(strlen(IFEQ_STATEMENT)), true) + || to_check.compare(IFNEQ_STATEMENT, 0, 0, int(strlen(IFNEQ_STATEMENT)), true)) { + _nesting++; + return true; + } + if (to_check.compare(ENDIF_STATEMENT, 0, 0, int(strlen(ENDIF_STATEMENT)), true)) { + _nesting--; + return true; + } + return false; +} + +outcome write_build_config::output_decorated_macro(const astring &symbol_in, + const astring &value, astring &cfg_accumulator, astring &ver_accumulator) +{ + // make sure we catch any conditionals. + if (check_nesting(symbol_in)) + ADD_COMMENT_RETURN(symbol_in, value); + // toss out any exclusions. + if (exclusions().member(symbol_in)) + ADD_COMMENT_RETURN(symbol_in, value); + if (symbol_in.contains("\"") || value.contains("\"")) + ADD_COMMENT_RETURN(symbol_in, value); + if (symbol_in[0] == '[') + ADD_COMMENT_RETURN(symbol_in, value); + if (_nesting) + ADD_COMMENT_RETURN(symbol_in, value); + // switch the output stream based on whether its a version component or not. + astring *the_accumulator = &cfg_accumulator; + if (process_version_parts(symbol_in, value)) { + the_accumulator = &ver_accumulator; + } + // add a special tag so that we won't be colliding with other names. + astring symbol = astring("__build_") + symbol_in; + return output_macro(symbol, value, *the_accumulator); +} + +outcome write_build_config::output_definition_macro + (const astring &embedded_value, astring &accumulator) +{ +// FUNCDEF("output_definition_macro"); +//LOG(astring("into output def with: ") + embedded_value); + variable_tokenizer t; + t.parse(embedded_value); + if (!t.symbols()) + ADD_COMMENT_RETURN("bad definition", embedded_value); + if (exclusions().member(t.table().name(0))) + ADD_COMMENT_RETURN(t.table().name(0), t.table()[0]); + if (_nesting) + ADD_COMMENT_RETURN(t.table().name(0), t.table()[0]); + return output_macro(t.table().name(0), t.table()[0], accumulator); +} + +bool write_build_config::write_output_file(const astring &filename, + const astring &new_contents) +{ + FUNCDEF("write_output_file"); + // now read the soon-to-be output file so we can check its current state. + bool write_header = true; + byte_filer check_header(filename, "rb"); + if (check_header.good()) { + byte_array file_contents; + int read = check_header.read(file_contents, MAX_HEADER_FILE); +if (read < 1) LOG("why is existing header contentless?"); + if (read > 0) { + astring found(astring::UNTERMINATED, (char *)file_contents.observe(), + read); +//LOG(astring("got existing content:\n-----\n") + found + "\n-----\n"); +//LOG(astring("new_content has:\n-----\n") + new_contents + "\n-----\n"); + if (found == new_contents) { + write_header = false; + } + } + } + // writing only occurs when we know that the build configurations have + // changed. if the file is the same, we definitely don't want to write + // it because pretty much all the other files depend on it. + if (write_header) { + // we actually want to blast out a new file. + byte_filer build_header(filename, "wb"); + if (!build_header.good()) + non_continuable_error(static_class_name(), func, astring("failed to create " + "build header file in ") + build_header.filename()); + build_header.write(new_contents); + LOG(astring(static_class_name()) + ": wrote config to " + + build_header.filename()); + } else { + // nothing has changed. +// LOG(astring(static_class_name()) + ": config already up to date in " +// + filename); + } + return true; +} + +int write_build_config::execute() +{ + FUNCDEF("execute"); + SETUP_CONSOLE_LOGGER; // override the file_logger from app_shell. + + // find our build ini file. + astring repodir = environment::get("REPOSITORY_DIR"); + + // the below code should never be needed for a properly configured build. +#ifdef __WIN32__ + if (!repodir) repodir = "l:"; +#else // unix and other locations. + if (!repodir) + repodir = environment::get("HOME") + "/hoople"; +#endif + + astring fname; + // where we seek out our build settings. + astring parmfile = environment::get("BUILD_PARAMETER_FILE"); + if (parmfile.t()) fname = parmfile; + else fname = repodir + "/build.ini"; + + // find our storage area for the build headers. we know a couple build + // configurations by now, but this should really be coming out of a config + // file instead. + astring library_directory = repodir + "/source/lib_src/library"; + if (!filename(library_directory).good()) { + library_directory = repodir + "/source/core/library"; + if (!filename(library_directory).good()) { + library_directory = repodir + "/libraries/library"; + if (!filename(library_directory).good()) { + library_directory = repodir + "/../../libraries/library"; + if (!filename(library_directory).good()) { + non_continuable_error(static_class_name(), func, + astring("failed to locate the library folder storing the generated files.")); + } + } + } + } + + // these are very specific paths, but they really are where we expect to + // see the headers. + + astring cfg_header_filename = library_directory + "/" + "__build_configuration.h"; + astring ver_header_filename = library_directory + "/" + "__build_version.h"; + + // open the ini file for reading. + byte_filer ini(fname, "r"); + if (!ini.good()) + non_continuable_error(static_class_name(), func, astring("failed to open " + "build configuration file for reading at ") + ini.filename()); +//hmmm: parameterize the build ini thing above! + + // now we build strings that represents the output files we want to create. + astring cfg_accumulator; + astring ver_accumulator; + + // odd indentation below comes from the strings being c++ code that will be + // written to a file. +//hmmm: another location to fix!!! + cfg_accumulator += "\ +#ifndef BUILD_SYSTEM_CONFIGURATION\n\ +#define BUILD_SYSTEM_CONFIGURATION\n\n\ + // This file provides all of the code flags which were used when this build\n\ + // was generated. Some of the items in the build configuration have been\n\ + // stripped out because they are not used.\n\n"; + ver_accumulator += "\ +#ifndef BUILD_VERSION_CONFIGURATION\n\ +#define BUILD_VERSION_CONFIGURATION\n\n\ + // This file provides the version macros for this particular build.\n\n"; + + // iterate through the entries we read in earlier and generate our header. + astring symbol, value; + astring buffer; + while (!ini.eof()) { + int chars = ini.getline(buffer, MAX_LINE_SIZE); + if (!chars) continue; // hmmm. + + variable_tokenizer t; + t.parse(buffer); + if (!t.symbols()) continue; // not so good. + + // pull out the first pair we found and try to parse it. + symbol = t.table().name(0); + value = t.table()[0]; + symbol.strip_spaces(astring::FROM_BOTH_SIDES); + + // clean out + characters that can come from += declarations. + while ( (symbol[symbol.end()] == '+') || (symbol[symbol.end()] == ':') ) { + symbol.zap(symbol.end(), symbol.end()); + symbol.strip_spaces(astring::FROM_END); + } + + if (symbol[0] == '#') continue; // toss out any comments. + + if (!symbol) continue; // seems like that one didn't work out so well. + + if (symbol.compare(EXPORT_STATEMENT, 0, 0, int(strlen(EXPORT_STATEMENT)), true)) { + // clean out export statements in front of our variables. + symbol.zap(0, int(strlen(EXPORT_STATEMENT) - 1)); + } + + // check for a make-style macro definition. + if (symbol.compare(DEFINITIONS_STATEMENT, 0, 0, int(strlen(DEFINITIONS_STATEMENT)), true)) { + // found a macro definition. write that up after pulling the real + // contents out. + output_definition_macro(value, cfg_accumulator); + } else { + // this one is hopefully a very tasty specialized macro. we will + // show it with added text to make it unique. + output_decorated_macro(symbol, value, cfg_accumulator, ver_accumulator); + } + } + + // write some calculated macros now. + ver_accumulator += "\n"; + ver_accumulator += " // calculated macros are dropped in here.\n\n"; + + // we write our version in a couple forms. hopefully we accumulated it. + + // this one is the same as the file version currently (below), but may go to + // a different version at some point. + ver_accumulator += " #define __build_SYSTEM_VERSION \""; + ver_accumulator += _ver->flex_text_form(); + ver_accumulator += "\"\n\n"; + + // we drop in the version as numbers also, since the version RC wants that. + ver_accumulator += " #define __build_FILE_VERSION_COMMAS "; + ver_accumulator += _ver->flex_text_form(version::COMMAS); + ver_accumulator += "\n"; + // another form of the file version for dotted notation. + ver_accumulator += " #define __build_FILE_VERSION \""; + ver_accumulator += _ver->flex_text_form(); + ver_accumulator += "\"\n"; + + // product version is just the first two parts. + _ver->set_component(version::REVISION, "0"); + _ver->set_component(version::BUILD, "0"); + // product version as a list of numbers. + ver_accumulator += " #define __build_PRODUCT_VERSION_COMMAS "; + ver_accumulator += _ver->flex_text_form(version::COMMAS); + ver_accumulator += "\n"; + // another form of the product version for use as a string. + ver_accumulator += " #define __build_PRODUCT_VERSION \""; + ver_accumulator += _ver->flex_text_form(version::DOTS, version::MINOR); + ver_accumulator += "\"\n"; + + // write a blob of comments at the end with what we didn't use. + cfg_accumulator += "\n"; + cfg_accumulator += "/*\n"; + cfg_accumulator += "These settings were not used:\n"; + cfg_accumulator += *_end_matter; + cfg_accumulator += "*/\n"; + cfg_accumulator += "\n"; + cfg_accumulator += "#endif /* outer guard */\n\n"; + + // finish up the version file also. + ver_accumulator += "\n"; + ver_accumulator += "#endif /* outer guard */\n\n"; + + if (!write_output_file(cfg_header_filename, cfg_accumulator)) { + LOG(astring("failed writing output file ") + cfg_header_filename); + } + if (!write_output_file(ver_header_filename, ver_accumulator)) { + LOG(astring("failed writing output file ") + ver_header_filename); + } + + return 0; +} + +HOOPLE_MAIN(write_build_config, ) + +#ifdef __BUILD_STATIC_APPLICATION__ + // static dependencies found by buildor_gen_deps.sh: + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif // __BUILD_STATIC_APPLICATION__ + diff --git a/core/tools/clam_tools/write_build_config.h b/core/tools/clam_tools/write_build_config.h new file mode 100644 index 00000000..3efa4b99 --- /dev/null +++ b/core/tools/clam_tools/write_build_config.h @@ -0,0 +1,82 @@ +#ifndef BUILD_DEFAULTS_CLASS +#define BUILD_DEFAULTS_CLASS + +/*****************************************************************************\ +* * +* Name : write_build_config * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* This class creates a header file that will provide macros that govern * +* how the build is created under win32 using visual studio project files. * +* This file is not on other platforms, nor with the clam makefile system. * +* * +******************************************************************************* +* Copyright (c) 1995-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +class write_build_config : public application::application_shell +{ +public: + write_build_config(); + ~write_build_config(); + + DEFINE_CLASS_NAME("write_build_config"); + + int execute(); + //!< performs the main action of creating our build configuration header. + + const structures::string_set &exclusions(); + //!< returns the set of symbols that we will not include in the header. + + basis::outcome output_macro(const basis::astring &symbol, const basis::astring &value, + basis::astring &accumulator); + //!< sends a macro definition for "symbol" with "value" to "accumulator". + + basis::outcome output_decorated_macro(const basis::astring &symbol, const basis::astring &value, + basis::astring &cfg_accumulator, basis::astring &ver_accumulator); + //!< produces a new macro by adding a uniquifying string to "symbol". + /*!< if the item is a version component, it will be output to the + "ver_accumulator" but otherwise it goes to the "cfg_accumulator". */ + + basis::outcome output_definition_macro(const basis::astring &embedded_value, + basis::astring &accumulator); + //!< parses a 'name=value' pair out of "embedded_value" and writes a macro. + + bool process_version_parts(const basis::astring &symbol, const basis::astring &value); + //!< checks on "symbol" to see if it's a version component. stores if so. + /*!< if the string was a version component, then true is returned. */ + + bool check_nesting(const basis::astring &to_check); + //!< if "to_check" is a make conditional, the nesting level is adjusted. + /*!< also if it's a conditional, true is returned, which means the line + can be dropped. */ + + bool write_output_file(const basis::astring &filename, const basis::astring &contents); + //!< writes "contents" to "filename" if it differs from current contents. + +private: + basis::astring *_end_matter; // stuff that isn't part of the real file. + structures::version *_ver; // accumulated when we see the right parts. + int _nesting; // how many levels of conditionals do we see? + + // not provided. + write_build_config(const write_build_config &); + write_build_config &operator =(const write_build_config &); +}; + +#endif + diff --git a/core/tools/dependency_tool/Xfuncproto.h b/core/tools/dependency_tool/Xfuncproto.h new file mode 100644 index 00000000..ffbbe905 --- /dev/null +++ b/core/tools/dependency_tool/Xfuncproto.h @@ -0,0 +1,88 @@ +/* $XConsortium: Xfuncproto.h,v 1.8 94/04/17 20:10:49 rws Exp $ */ +/* + * +Copyright (c) 1989, 1991 X Consortium + +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 +X CONSORTIUM 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. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + * + */ + +/* Definitions to make function prototypes manageable */ + +#ifndef _XFUNCPROTO_H_ +#define _XFUNCPROTO_H_ + +#ifndef NeedFunctionPrototypes +#if defined(FUNCPROTO) || __STDC__ || defined(__cplusplus) || defined(c_plusplus) +#define NeedFunctionPrototypes 1 +#else +#define NeedFunctionPrototypes 0 +#endif +#endif /* NeedFunctionPrototypes */ + +#ifndef NeedVarargsPrototypes +#if __STDC__ || defined(__cplusplus) || defined(c_plusplus) || (FUNCPROTO&2) +#define NeedVarargsPrototypes 1 +#else +#define NeedVarargsPrototypes 0 +#endif +#endif /* NeedVarargsPrototypes */ + +#if NeedFunctionPrototypes + +#ifndef NeedNestedPrototypes +#if __STDC__ || defined(__cplusplus) || defined(c_plusplus) || (FUNCPROTO&8) +#define NeedNestedPrototypes 1 +#else +#define NeedNestedPrototypes 0 +#endif +#endif /* NeedNestedPrototypes */ + +#ifndef _Xconst +#if __STDC__ || defined(__cplusplus) || defined(c_plusplus) || (FUNCPROTO&4) +#define _Xconst const +#else +#define _Xconst +#endif +#endif /* _Xconst */ + +#ifndef NeedWidePrototypes +#ifdef NARROWPROTO +#define NeedWidePrototypes 0 +#else +#define NeedWidePrototypes 1 /* default to make interropt. easier */ +#endif +#endif /* NeedWidePrototypes */ + +#endif /* NeedFunctionPrototypes */ + +#ifndef _XFUNCPROTOBEGIN +#ifdef __cplusplus /* for C++ V2.0 */ +#define _XFUNCPROTOBEGIN extern "C" { /* do not leave open across includes */ +#define _XFUNCPROTOEND } +#else +#define _XFUNCPROTOBEGIN +#define _XFUNCPROTOEND +#endif +#endif /* _XFUNCPROTOBEGIN */ + +#endif /* _XFUNCPROTO_H_ */ diff --git a/core/tools/dependency_tool/Xos2defs.h b/core/tools/dependency_tool/Xos2defs.h new file mode 100644 index 00000000..6e4b1324 --- /dev/null +++ b/core/tools/dependency_tool/Xos2defs.h @@ -0,0 +1,74 @@ +/* $XConsortium: Xw32defs.h,v 1.4 94/02/25 18:50:11 rws Exp $ */ + +#error + +#ifndef _XW32DEFS_H +#define _XW32DEFS_H + +typedef char *caddr_t; + +#define access _access +#define chdir _chdir +#define chmod _chmod +#define close _close +#define creat _creat +#define dup _dup +#define dup2 _dup2 +#define environ _environ +#define execl _execl +#define execle _execle +#define execlp _execlp +#define execlpe _execlpe +#define execv _execv +#define execve _execve +#define execvp _execvp +#define execvpe _execvpe +#define fdopen _fdopen +#define fileno _fileno +#define fstat _fstat +#define getcwd _getcwd +#define getpid _getpid +#define hypot _hypot +#define isatty _isatty +#define lseek _lseek +#define mkdir _mkdir +#define mktemp _mktemp +#define open _open +#define putenv _putenv +#define read _read +#define rmdir _rmdir +#define sleep(x) _sleep((x) * 1000) +#define stat _stat +#define sys_errlist _sys_errlist +#define sys_nerr _sys_nerr +#define umask _umask +#define unlink _unlink +#define write _write + +/* +#define O_RDONLY _O_RDONLY +#define O_WRONLY _O_WRONLY +#define O_RDWR _O_RDWR +#define O_APPEND _O_APPEND +#define O_CREAT _O_CREAT +#define O_TRUNC _O_TRUNC +#define O_EXCL _O_EXCL +#define O_TEXT _O_TEXT +#define O_BINARY _O_BINARY +#define O_RAW _O_BINARY + +#define S_IFMT _S_IFMT +#define S_IFDIR _S_IFDIR +#define S_IFCHR _S_IFCHR +#define S_IFREG _S_IFREG +#define S_IREAD _S_IREAD +#define S_IWRITE _S_IWRITE +#define S_IEXEC _S_IEXEC + +*/ +#define F_OK 0 +#define X_OK 1 +#define W_OK 2 +#define R_OK 4 + +#endif diff --git a/core/tools/dependency_tool/Xosdefs.h b/core/tools/dependency_tool/Xosdefs.h new file mode 100644 index 00000000..4513bd90 --- /dev/null +++ b/core/tools/dependency_tool/Xosdefs.h @@ -0,0 +1,126 @@ +/* + * O/S-dependent (mis)feature macro definitions + * + * $XConsortium: Xosdefs.h,v 1.14 94/11/30 20:48:05 kaleb Exp $ + * $XFree86: xc/include/Xosdefs.h,v 3.7 1995/01/28 15:42:05 dawes Exp $ + * +Copyright (c) 1991 X Consortium + +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 +X CONSORTIUM 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. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + */ + +#ifndef _XOSDEFS_H_ +#define _XOSDEFS_H_ + +/* + * X_NOT_STDC_ENV means does not have ANSI C header files. Lack of this + * symbol does NOT mean that the system has stdarg.h. + * + * X_NOT_POSIX means does not have POSIX header files. Lack of this + * symbol does NOT mean that the POSIX environment is the default. + * You may still have to define _POSIX_SOURCE to get it. + */ + +#ifdef NOSTDHDRS +#define X_NOT_POSIX +#define X_NOT_STDC_ENV +#endif + +#ifdef sony +#if !defined(SYSTYPE_SYSV) && !defined(_SYSTYPE_SYSV) +#define X_NOT_POSIX +#endif +#endif + +#ifdef UTEK +#define X_NOT_POSIX +#define X_NOT_STDC_ENV +#endif + +#ifdef vax +#ifndef ultrix /* assume vanilla BSD */ +#define X_NOT_POSIX +#define X_NOT_STDC_ENV +#endif +#endif + +#ifdef luna +#define X_NOT_POSIX +#define X_NOT_STDC_ENV +#endif + +#ifdef Mips +#define X_NOT_POSIX +#define X_NOT_STDC_ENV +#endif + +#ifdef USL +#ifdef SYSV /* (release 3.2) */ +#define X_NOT_POSIX +#define X_NOT_STDC_ENV +#endif +#endif + +#ifdef i386 +#ifdef SYSV +#ifndef SCO +#define X_NOT_POSIX +#endif +#define X_NOT_STDC_ENV +#endif +#endif + +#ifdef MOTOROLA +#ifdef SYSV +#define X_NOT_STDC_ENV +#endif +#endif + +#ifdef sun +#ifdef SVR4 +/* define this to whatever it needs to be */ +#define X_POSIX_C_SOURCE 199300L +#endif +#endif + +#ifdef WIN32 +#ifndef _POSIX_ +#define X_NOT_POSIX +#endif +#endif + +#ifdef __OS2__ +#ifndef _POSIX_ +#define X_NOT_POSIX +#endif +#endif + +#if defined(nec_ews_svr2) || defined(SX) || defined(PC_UX) +#define X_NOT_POSIX +#define X_NOT_STDC_ENV +#endif + +#ifdef __EMX__ +#define USGISH +#endif + +#endif /* _XOSDEFS_H_ */ diff --git a/core/tools/dependency_tool/Xw32defs.h b/core/tools/dependency_tool/Xw32defs.h new file mode 100644 index 00000000..58c2187c --- /dev/null +++ b/core/tools/dependency_tool/Xw32defs.h @@ -0,0 +1,72 @@ +/* $XConsortium: Xw32defs.h,v 1.4 94/02/25 18:50:11 rws Exp $ */ + +#ifndef _XW32DEFS_H +#define _XW32DEFS_H + +typedef char *caddr_t; + +#define access _access +#define alloca _alloca +#define chdir _chdir +#define chmod _chmod +#define close _close +#define creat _creat +#define dup _dup +#define dup2 _dup2 +#define environ _environ +#define execl _execl +#define execle _execle +#define execlp _execlp +#define execlpe _execlpe +#define execv _execv +#define execve _execve +#define execvp _execvp +#define execvpe _execvpe +#define fdopen _fdopen +#define fileno _fileno +#define fstat _fstat +#define getcwd _getcwd +#define getpid _getpid +#define hypot _hypot +#define isascii __isascii +#define isatty _isatty +#define lseek _lseek +#define mkdir _mkdir +#define mktemp _mktemp +#define open _open +#define putenv _putenv +#define read _read +#define rmdir _rmdir +#define sleep(x) _sleep((x) * 1000) +#define stat _stat +#define sys_errlist _sys_errlist +#define sys_nerr _sys_nerr +#define umask _umask +#define unlink _unlink +#define write _write + +#define O_RDONLY _O_RDONLY +#define O_WRONLY _O_WRONLY +#define O_RDWR _O_RDWR +#define O_APPEND _O_APPEND +#define O_CREAT _O_CREAT +#define O_TRUNC _O_TRUNC +#define O_EXCL _O_EXCL +#define O_TEXT _O_TEXT +#define O_BINARY _O_BINARY +#define O_RAW _O_BINARY + +#define S_IFMT _S_IFMT +#define S_IFDIR _S_IFDIR +#define S_IFCHR _S_IFCHR +#define S_IFREG _S_IFREG +#define S_IREAD _S_IREAD +#define S_IWRITE _S_IWRITE +#define S_IEXEC _S_IEXEC + +#define F_OK 0 +#define X_OK 1 +#define W_OK 2 +#define R_OK 4 + +#endif diff --git a/core/tools/dependency_tool/cppsetup.cpp b/core/tools/dependency_tool/cppsetup.cpp new file mode 100644 index 00000000..dc404113 --- /dev/null +++ b/core/tools/dependency_tool/cppsetup.cpp @@ -0,0 +1,230 @@ +/* $XConsortium: cppsetup.c,v 1.13 94/04/17 20:10:32 gildea Exp $ */ +/* + +Copyright (c) 1993, 1994 X Consortium + +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 +X CONSORTIUM 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. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + +*/ + +#ifdef __WIN32__ + #pragma warning(disable : 4996) +#endif + +#include "def.h" + +#ifdef CPP + +#include "ifparser.h" + +/* + * This file is strictly for the sake of cpy.y and yylex.c (if + * you indeed have the source for cpp). + */ +#define IB 1 +#define SB 2 +#define NB 4 +#define CB 8 +#define QB 16 +#define WB 32 +#define SALT '#' +#if pdp11 | vax | ns16000 | mc68000 | ibm032 +#define COFF 128 +#else +#define COFF 0 +#endif +/* + * These variables used by cpy.y and yylex.c + */ +extern char *outp, *inp, *newp, *pend; +extern char *ptrtab; +extern char fastab[]; +extern char slotab[]; + +/* + * cppsetup + */ +struct filepointer *currentfile; +inclist *currentinc; + +int cppsetup(register char *line, register struct filepointer *filep, + register inclist *inc) +{ + register char *p, savec; + static bool setupdone = false; + bool value; + + if (!setupdone) { + cpp_varsetup(); + setupdone = true; + } + + currentfile = filep; + currentinc = inc; + inp = newp = line; + for (p=newp; *p; p++) + ; + + /* + * put a newline back on the end, and set up pend, etc. + */ + *p++ = '\n'; + savec = *p; + *p = '\0'; + pend = p; + + ptrtab = slotab+COFF; + *--inp = SALT; + outp=inp; + value = yyparse(); + *p = savec; + return(value); +} + +struct symtab *lookup(char *symbol) +{ + static struct symtab undefined; + struct symtab *sp; + + sp = isdefined(symbol, currentinc, NULL); + if (sp == NULL) { + sp = &undefined; + sp->s_value = NULL; + } + return (sp); +} + +int pperror(int tag, int x0, int x1, int x2, int x3, int x4) +{ + warning("\"%s\", line %d: ", currentinc->i_file, currentfile->f_line); + warning(x0,x1,x2,x3,x4); +} + + +int yyerror(register char *s) +{ + fatalerr("Fatal error: %s\n", s); +} + +#else /* not CPP */ + +#include "ifparser.h" + +#include + +struct _parse_data { + struct filepointer *filep; + inclist *inc; + const char *line; +}; + +static const char *_my_if_errors(IfParser *ip, const char *cp, + const char *expecting) +{ + struct _parse_data *pd = (struct _parse_data *) ip->data; + int lineno = pd->filep->f_line; + char *filename = pd->inc->i_file; + char prefix[300]; + int prefixlen; + int i; + + sprintf (prefix, "\"%s\":%d", filename, lineno); + prefixlen = int(strlen(prefix)); + fprintf (stderr, "%s: %s", prefix, pd->line); + i = int(cp - pd->line); + if (i > 0 && pd->line[i-1] != '\n') { + putc ('\n', stderr); + } + for (i += prefixlen + 3; i > 0; i--) { + putc (' ', stderr); + } + fprintf (stderr, "^--- expecting %s\n", expecting); + return NULL; +} + + +#define MAXNAMELEN 256 + +static struct symtab *_lookup_variable(IfParser *ip, const char *var, int len) +{ + char tmpbuf[MAXNAMELEN + 1]; + struct _parse_data *pd = (struct _parse_data *) ip->data; + + if (len > MAXNAMELEN) + return 0; + + strncpy (tmpbuf, var, len); + tmpbuf[len] = '\0'; + return isdefined (tmpbuf, pd->inc, NULL); +} + + +static int _my_eval_defined(IfParser *ip, const char *var, int len) +{ + if (_lookup_variable (ip, var, len)) return 1; + else return 0; +} + +#define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_') + +static int _my_eval_variable(IfParser *ip, const char *var, int len) +{ + struct symtab *s; + + s = _lookup_variable (ip, var, len); + if (!s) + return 0; + do { + var = s->s_value; + if (!isvarfirstletter(*var)) + break; + s = _lookup_variable (ip, var, int(strlen(var))); + } while (s); + + return atoi(var); +} + + +int cppsetup(register char *line, register struct filepointer *filep, + register inclist *inc) +{ + IfParser ip; + struct _parse_data pd; + int val = 0; + + pd.filep = filep; + pd.inc = inc; + pd.line = line; + ip.funcs.handle_error = _my_if_errors; + ip.funcs.eval_defined = _my_eval_defined; + ip.funcs.eval_variable = _my_eval_variable; + ip.data = (char *) &pd; + + (void) ParseIfExpression (&ip, line, &val); + if (val) + return IF; + else + return IFFALSE; +} + +#endif /* CPP */ + diff --git a/core/tools/dependency_tool/def.h b/core/tools/dependency_tool/def.h new file mode 100644 index 00000000..de4e4189 --- /dev/null +++ b/core/tools/dependency_tool/def.h @@ -0,0 +1,195 @@ +/* $XConsortium: def.h,v 1.25 94/04/17 20:10:33 gildea Exp $ */ +/* + +Copyright (c) 1993, 1994 X Consortium + +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 +X CONSORTIUM 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. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + +*/ + +#include +#include "Xosdefs.h" +#ifdef WIN32 +#endif +#ifdef __OS2__ +#define __STDC__ 1 +#include "Xos2defs.h" +#endif +#include "Xfuncproto.h" +#include +#ifndef X_NOT_POSIX +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE +#endif +#endif +#include +#include +#include + +#define MAXDEFINES 512 +#define MAXFILES 4000 +#define MAXDIRS 640 +#define SYMTABINC 10 /* must be > 1 for define() to work right */ + +/* the following must match the directives table in main.c */ +#define IF 0 +#define IFDEF 1 +#define IFNDEF 2 +#define ELSE 3 +#define ENDIF 4 +#define DEFINE 5 +#define UNDEF 6 +#define INCLUDE 7 +#define LINE 8 +#define PRAGMA 9 +#define ERROR 10 +#define IDENT 11 +#define SCCS 12 +#define ELIF 13 +#define EJECT 14 +#define IMPORT 15 + +#define IFFALSE 16 /* pseudo value --- never matched */ +#define ELIFFALSE 17 /* pseudo value --- never matched */ +#define INCLUDEDOT 18 /* pseudo value --- never matched */ +#define IFGUESSFALSE 19 /* pseudo value --- never matched */ +#define ELIFGUESSFALSE 20 /* pseudo value --- never matched */ + +///#define DEBUG + // uncomment this for debugging version. + +#ifdef DEBUG +extern int _debugmask; +/* + * debug levels are: + * + * 0 show ifn*(def)*,endif + * 1 trace defined/!defined + * 2 show #include + * 3 show #include SYMBOL + * 4-6 unused + */ +#define debug(level,arg) { if (_debugmask & (1 << level)) warning arg; } +#else +#define debug(level,arg) {} +#endif /* DEBUG */ + +struct symtab { + char *s_name; + char *s_value; +}; + +struct inclist { + char *i_incstring; /* string from #include line */ + char *i_file; /* path name of the include file */ + inclist **i_list; /* list of files it itself includes */ + int i_listlen; /* length of i_list */ + symtab *i_defs; /* symbol table for this file */ + int i_ndefs; /* current # defines */ + int i_deflen; /* amount of space in table */ + bool i_defchecked; /* whether defines have been checked */ + bool i_notified; /* whether we have revealed includes */ + bool i_marked; /* whether it's in the makefile */ + bool i_searched; /* whether we have read this */ + bool i_included_sym; /* whether #include SYMBOL was found */ + /* Can't use i_list if true */ +}; + +struct filepointer { + char *f_p; + char *f_base; + char *f_end; + long f_len; + long f_line; + char *f_name; +}; + +#ifndef X_NOT_STDC_ENV +#include +#if defined(macII) && !defined(__STDC__) /* stdlib.h fails to define these */ +char *malloc(), *realloc(); +#endif /* macII */ +#else +char *malloc(); +char *realloc(); +#endif + +/* +char *getline(); +symtab *slookup(); +symtab *isdefined(); +symtab *fdefined(); +filepointer *getfile(); +inclist *newinclude(register char *, register char *); +inclist *inc_path(); +*/ + +// cppsetup.cpp: +int cppsetup(register char *line, register filepointer *filep, + register inclist *inc); + +// include.cpp +inclist *newinclude(register char *newfile, register char *incstring); +void inc_clean(); +inclist *inc_path(register char *file, register char *include, bool dot, + bool &failure_okay); +void included_by(register inclist *ip, register inclist *newfile); + +// main.cpp: +char *base_name(register char *file); +char *copy(register char *str); +filepointer *getfile(char *file); +void freefile(filepointer *fp); +char *getline(register filepointer *filep); +int match(register const char *str, register const char **list); +void redirect(char *line, char *makefile); +#if NeedVarargsPrototypes + void fatalerr(const char *, ...); + void warning(const char *, ...); + void warning1(const char *, ...); +#endif + +// parse.cpp: +void define(char *def, inclist *file); +void define2(char *name, char *val, inclist *file); +int deftype(register char *line, register filepointer *filep, + register inclist *file_red, register inclist *file, + int parse_it); +symtab *fdefined(register char *symbol, inclist *file, inclist **srcfile); +int find_includes(filepointer *filep, inclist *file, + inclist *file_red, int recursion, bool failOK); +int gobble(register filepointer *filep, inclist *file, + inclist *file_red); +symtab *isdefined(register char *symbol, inclist *file, + inclist **srcfile); +symtab *slookup(register char *symbol, register inclist *file); +void undefine(char *symbol, register inclist *file); +int zero_value(register char *exp, register filepointer *filep, + register inclist *file_red); + +// pr.cpp: +void add_include(filepointer *filep, inclist *file, + inclist *file_red, char *include, bool dot, bool failOK); +void pr(register inclist *ip, char *file, char *base, bool rc_file); +void recursive_pr_include(register inclist *head, register char *file, + register char *base); + diff --git a/core/tools/dependency_tool/ifparser.cpp b/core/tools/dependency_tool/ifparser.cpp new file mode 100644 index 00000000..b4de6824 --- /dev/null +++ b/core/tools/dependency_tool/ifparser.cpp @@ -0,0 +1,401 @@ +/* + * $XConsortium: ifparser.c,v 1.7 94/01/18 21:30:50 rws Exp $ + * + * Copyright 1992 Network Computing Devices, Inc. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Network Computing Devices may not be + * used in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Network Computing Devices makes + * no representations about the suitability of this software for any purpose. + * It is provided ``as is'' without express or implied warranty. + * + * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL, + * 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. + * + * Author: Jim Fulton + * Network Computing Devices, Inc. + * + * Simple if statement processor + * + * This module can be used to evaluate string representations of C language + * if constructs. It accepts the following grammar: + * + * EXPRESSION := VALUE + * | VALUE BINOP EXPRESSION + * + * VALUE := '(' EXPRESSION ')' + * | '!' VALUE + * | '-' VALUE + * | 'defined' '(' variable ')' + * | 'defined' variable + * | # variable '(' variable-list ')' + * | variable + * | number + * + * BINOP := '*' | '/' | '%' + * | '+' | '-' + * | '<<' | '>>' + * | '<' | '>' | '<=' | '>=' + * | '==' | '!=' + * | '&' | '|' + * | '&&' | '||' + * + * The normal C order of precidence is supported. + * + * + * External Entry Points: + * + * ParseIfExpression parse a string for #if + */ + +#ifdef __WIN32__ + #pragma warning(disable : 4996) +#endif + +#include "ifparser.h" + +#include +#include +#include + +/**************************************************************************** + Internal Macros and Utilities for Parser + ****************************************************************************/ + +#define DO(val) if (!(val)) return NULL +#define CALLFUNC(ggg,fff) (*((ggg)->funcs.fff)) +#define SKIPSPACE(ccc) while (isspace(*ccc)) ccc++ +#define isvarfirstletter(ccc) (isalpha(ccc) || (ccc) == '_') + + +static const char *parse_variable(IfParser *g, const char *cp, + const char **varp) +{ + SKIPSPACE (cp); + + if (!isvarfirstletter (*cp)) + return CALLFUNC(g, handle_error) (g, cp, "variable name"); + + *varp = cp; + /* EMPTY */ + for (cp++; isalnum(*cp) || *cp == '_'; cp++) ; + return cp; +} + + +static const char *parse_number(IfParser *g, const char *cp, int *valp) +{ + SKIPSPACE (cp); + + if (!isdigit(*cp)) + return CALLFUNC(g, handle_error) (g, cp, "number"); + +#ifdef WIN32 + char *hold_result; + *valp = strtol(cp, &hold_result, 0); + cp = hold_result; +#else + *valp = atoi (cp); + /* EMPTY */ + for (cp++; isdigit(*cp); cp++) ; +#endif + return cp; +} + + +static const char *parse_value(IfParser *g, const char *cp, int *valp) +{ + const char *var; + + *valp = 0; + + SKIPSPACE (cp); + if (!*cp) + return cp; + + switch (*cp) { + case '(': + DO (cp = ParseIfExpression (g, cp + 1, valp)); + SKIPSPACE (cp); + if (*cp != ')') + return CALLFUNC(g, handle_error) (g, cp, ")"); + + return cp + 1; /* skip the right paren */ + + case '!': + DO (cp = parse_value (g, cp + 1, valp)); + *valp = !(*valp); + return cp; + + case '-': + DO (cp = parse_value (g, cp + 1, valp)); + *valp = -(*valp); + return cp; + + case '#': + DO (cp = parse_variable (g, cp + 1, &var)); + SKIPSPACE (cp); + if (*cp != '(') + return CALLFUNC(g, handle_error) (g, cp, "("); + do { + DO (cp = parse_variable (g, cp + 1, &var)); + SKIPSPACE (cp); + } while (*cp && *cp != ')'); + if (*cp != ')') + return CALLFUNC(g, handle_error) (g, cp, ")"); + *valp = 1; /* XXX */ + return cp + 1; + + case 'd': + if (strncmp (cp, "defined", 7) == 0 && !isalnum(cp[7])) { + int paren = 0; + cp += 7; + SKIPSPACE (cp); + if (*cp == '(') { + paren = 1; + cp++; + } + DO (cp = parse_variable (g, cp, &var)); + SKIPSPACE (cp); + if (paren && *cp != ')') + return CALLFUNC(g, handle_error) (g, cp, ")"); + *valp = (*(g->funcs.eval_defined)) (g, var, int(cp - var)); + return cp + paren; /* skip the right paren */ + } + /* fall out */ + } + + if (isdigit(*cp)) { + DO (cp = parse_number (g, cp, valp)); + } else if (!isvarfirstletter(*cp)) + return CALLFUNC(g, handle_error) (g, cp, "variable or number"); + else { + DO (cp = parse_variable (g, cp, &var)); + *valp = (*(g->funcs.eval_variable)) (g, var, int(cp - var)); + } + + return cp; +} + +static const char *parse_product(IfParser *g, const char *cp, int *valp) +{ + int rightval; + + DO (cp = parse_value (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '*': + DO (cp = parse_product (g, cp + 1, &rightval)); + *valp = (*valp * rightval); + break; + + case '/': + DO (cp = parse_product (g, cp + 1, &rightval)); + *valp = (*valp / rightval); + break; + + case '%': + DO (cp = parse_product (g, cp + 1, &rightval)); + *valp = (*valp % rightval); + break; + } + return cp; +} + +static const char *parse_sum(IfParser *g, const char *cp, int *valp) +{ + int rightval; + + DO (cp = parse_product (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '+': + DO (cp = parse_sum (g, cp + 1, &rightval)); + *valp = (*valp + rightval); + break; + + case '-': + DO (cp = parse_sum (g, cp + 1, &rightval)); + *valp = (*valp - rightval); + break; + } + return cp; +} + + +static const char *parse_shift(IfParser *g, const char *cp, int *valp) +{ + int rightval; + + DO (cp = parse_sum (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '<': + if (cp[1] == '<') { + DO (cp = parse_shift (g, cp + 2, &rightval)); + *valp = (*valp << rightval); + } + break; + + case '>': + if (cp[1] == '>') { + DO (cp = parse_shift (g, cp + 2, &rightval)); + *valp = (*valp >> rightval); + } + break; + } + return cp; +} + +static const char *parse_inequality(IfParser *g, const char *cp, int *valp) +{ + int rightval; + + DO (cp = parse_shift (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '<': + if (cp[1] == '=') { + DO (cp = parse_inequality (g, cp + 2, &rightval)); + *valp = (*valp <= rightval); + } else { + DO (cp = parse_inequality (g, cp + 1, &rightval)); + *valp = (*valp < rightval); + } + break; + + case '>': + if (cp[1] == '=') { + DO (cp = parse_inequality (g, cp + 2, &rightval)); + *valp = (*valp >= rightval); + } else { + DO (cp = parse_inequality (g, cp + 1, &rightval)); + *valp = (*valp > rightval); + } + break; + } + return cp; +} + +static const char *parse_equality(IfParser *g, const char *cp, int *valp) +{ + int rightval; + + DO (cp = parse_inequality (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '=': + if (cp[1] == '=') + cp++; + DO (cp = parse_equality (g, cp + 1, &rightval)); + *valp = (*valp == rightval); + break; + + case '!': + if (cp[1] != '=') + break; + DO (cp = parse_equality (g, cp + 2, &rightval)); + *valp = (*valp != rightval); + break; + } + return cp; +} + +static const char *parse_band(IfParser *g, const char *cp, int *valp) +{ + int rightval; + + DO (cp = parse_equality (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '&': + if (cp[1] != '&') { + DO (cp = parse_band (g, cp + 1, &rightval)); + *valp = (*valp & rightval); + } + break; + } + return cp; +} + + +static const char *parse_bor(IfParser *g, const char *cp, int *valp) +{ + int rightval; + + DO (cp = parse_band (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '|': + if (cp[1] != '|') { + DO (cp = parse_bor (g, cp + 1, &rightval)); + *valp = (*valp | rightval); + } + break; + } + return cp; +} + +static const char *parse_land(IfParser *g, const char *cp, int *valp) +{ + int rightval; + + DO (cp = parse_bor (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '&': + if (cp[1] != '&') + return CALLFUNC(g, handle_error) (g, cp, "&&"); + DO (cp = parse_land (g, cp + 2, &rightval)); + *valp = (*valp && rightval); + break; + } + return cp; +} + +static const char *parse_lor(IfParser *g, const char *cp, int *valp) +{ + int rightval; + + DO (cp = parse_land (g, cp, valp)); + SKIPSPACE (cp); + + switch (*cp) { + case '|': + if (cp[1] != '|') + return CALLFUNC(g, handle_error) (g, cp, "||"); + DO (cp = parse_lor (g, cp + 2, &rightval)); + *valp = (*valp || rightval); + break; + } + return cp; +} + + +/**************************************************************************** + External Entry Points + ****************************************************************************/ + +const char *ParseIfExpression(IfParser *g, const char *cp, int *valp) +{ + return parse_lor (g, cp, valp); +} + + diff --git a/core/tools/dependency_tool/ifparser.h b/core/tools/dependency_tool/ifparser.h new file mode 100644 index 00000000..72fb6a47 --- /dev/null +++ b/core/tools/dependency_tool/ifparser.h @@ -0,0 +1,70 @@ +/* + * $XConsortium: ifparser.h,v 1.1 92/08/22 13:05:39 rws Exp $ + * + * Copyright 1992 Network Computing Devices, Inc. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Network Computing Devices may not be + * used in advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. Network Computing Devices makes + * no representations about the suitability of this software for any purpose. + * It is provided ``as is'' without express or implied warranty. + * + * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL, + * 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. + * + * Author: Jim Fulton + * Network Computing Devices, Inc. + * + * Simple if statement processor + * + * This module can be used to evaluate string representations of C language + * if constructs. It accepts the following grammar: + * + * EXPRESSION := VALUE + * | VALUE BINOP EXPRESSION + * + * VALUE := '(' EXPRESSION ')' + * | '!' VALUE + * | '-' VALUE + * | 'defined' '(' variable ')' + * | variable + * | number + * + * BINOP := '*' | '/' | '%' + * | '+' | '-' + * | '<<' | '>>' + * | '<' | '>' | '<=' | '>=' + * | '==' | '!=' + * | '&' | '|' + * | '&&' | '||' + * + * The normal C order of precidence is supported. + * + * + * External Entry Points: + * + * ParseIfExpression parse a string for #if + */ + +#include + +struct IfParser { + struct { /* functions */ + const char *(*handle_error) (IfParser *, const char *, const char *); + int (*eval_variable) (IfParser *, const char *, int); + int (*eval_defined) (IfParser *, const char *, int); + } funcs; + char *data; +}; + +const char *ParseIfExpression(IfParser *, const char *, int *); + diff --git a/core/tools/dependency_tool/imakemdep.h b/core/tools/dependency_tool/imakemdep.h new file mode 100644 index 00000000..d9d2c277 --- /dev/null +++ b/core/tools/dependency_tool/imakemdep.h @@ -0,0 +1,718 @@ +/* $XConsortium: imakemdep.h,v 1.82 95/01/12 16:27:01 kaleb Exp $ */ +/* $XFree86: xc/config/imake/imakemdep.h,v 3.8 1995/01/28 15:40:59 dawes Exp $ */ +/* + +Copyright (c) 1993, 1994 X Consortium + +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 +X CONSORTIUM 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. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + +*/ + + +/* + * This file contains machine-dependent constants for the imake utility. + * When porting imake, read each of the steps below and add in any necessary + * definitions. In general you should *not* edit ccimake.c or imake.c! + */ + +#ifdef CCIMAKE +/* + * Step 1: imake_ccflags + * Define any special flags that will be needed to get imake.c to compile. + * These will be passed to the compile along with the contents of the + * make variable BOOTSTRAPCFLAGS. + */ +#ifdef hpux +#ifdef hp9000s800 +#define imake_ccflags "-DSYSV" +#else +#define imake_ccflags "-Wc,-Nd4000,-Ns3000 -DSYSV" +#endif +#endif + +#if defined(macII) || defined(_AUX_SOURCE) +#define imake_ccflags "-DmacII -DSYSV" +#endif + +#ifdef stellar +#define imake_ccflags "-DSYSV" +#endif + +#if defined(USL) || defined(Oki) || defined(NCR) +#define imake_ccflags "-Xc -DSVR4" +#endif + +#ifdef sony +#if defined(SYSTYPE_SYSV) || defined(_SYSTYPE_SYSV) +#define imake_ccflags "-DSVR4" +#else +#include +#if NEWSOS < 41 +#define imake_ccflags "-Dbsd43 -DNOSTDHDRS" +#else +#if NEWSOS < 42 +#define imake_ccflags "-Dbsd43" +#endif +#endif +#endif +#endif + +#ifdef _CRAY +#define imake_ccflags "-DSYSV -DUSG" +#endif + +#if defined(_IBMR2) || defined(aix) +#define imake_ccflags "-Daix -DSYSV" +#endif + +#ifdef Mips +# if defined(SYSTYPE_BSD) || defined(BSD) || defined(BSD43) +# define imake_ccflags "-DBSD43" +# else +# define imake_ccflags "-DSYSV" +# endif +#endif + +#ifdef is68k +#define imake_ccflags "-Dluna -Duniosb" +#endif + +#ifdef SYSV386 +# ifdef SVR4 +# define imake_ccflags "-Xc -DSVR4" +# else +# define imake_ccflags "-DSYSV" +# endif +#endif + +#ifdef SVR4 +# ifdef i386 +# define imake_ccflags "-Xc -DSVR4" +# endif +#endif + +#ifdef SYSV +# ifdef i386 +# define imake_ccflags "-DSYSV" +# endif +#endif + +#ifdef __convex__ +#define imake_ccflags "-fn -tm c1" +#endif + +#ifdef apollo +#define imake_ccflags "-DX_NOT_POSIX" +#endif + +#ifdef WIN32 +#define imake_ccflags "-nologo -batch -D__STDC__" +#endif + +#ifdef __uxp__ +#define imake_ccflags "-DSVR4 -DANSICPP" +#endif + +#ifdef __sxg__ +#define imake_ccflags "-DSYSV -DUSG -DNOSTDHDRS" +#endif + +#ifdef sequent +#define imake_ccflags "-DX_NOT_STDC_ENV -DX_NOT_POSIX" +#endif + +#ifdef _SEQUENT_ +#define imake_ccflags "-DSYSV -DUSG" +#endif + +#if defined(SX) || defined(PC_UX) +#define imake_ccflags "-DSYSV" +#endif + +#ifdef nec_ews_svr2 +#define imake_ccflags "-DUSG" +#endif + +#if defined(nec_ews_svr4) || defined(_nec_ews_svr4) || defined(_nec_up) || defined(_nec_ft) +#define imake_ccflags "-DSVR4" +#endif + +#ifdef MACH +#define imake_ccflags "-DNOSTDHDRS" +#endif + +/* this is for OS/2 under EMX. This won't work with DOS */ +#if defined(__EMX__) +#define imake_ccflags "-DBSD43" +#endif + +#else /* not CCIMAKE */ +#ifndef MAKEDEPEND +/* + * Step 2: dup2 + * If your OS doesn't have a dup2() system call to duplicate one file + * descriptor onto another, define such a mechanism here (if you don't + * already fall under the existing category(ies). + */ +#if defined(SYSV) && !defined(_CRAY) && !defined(Mips) && !defined(_SEQUENT_) +#define dup2(fd1,fd2) ((fd1 == fd2) ? fd1 : (close(fd2), \ + fcntl(fd1, F_DUPFD, fd2))) +#endif + + +/* + * Step 3: FIXUP_CPP_WHITESPACE + * If your cpp collapses tabs macro expansions into a single space and + * replaces escaped newlines with a space, define this symbol. This will + * cause imake to attempt to patch up the generated Makefile by looking + * for lines that have colons in them (this is why the rules file escapes + * all colons). One way to tell if you need this is to see whether or not + * your Makefiles have no tabs in them and lots of @@ strings. + */ +#if defined(sun) || defined(SYSV) || defined(SVR4) || defined(hcx) || defined(WIN32) || (defined(AMOEBA) && defined(CROSS_COMPILE)) +#define FIXUP_CPP_WHITESPACE +#endif +#ifdef WIN32 +#define REMOVE_CPP_LEADSPACE +#define INLINE_SYNTAX +#define MAGIC_MAKE_VARS +#endif +#ifdef __minix_vmd +#define FIXUP_CPP_WHITESPACE +#endif + +/* + * Step 4: USE_CC_E, DEFAULT_CC, DEFAULT_CPP + * If you want to use cc -E instead of cpp, define USE_CC_E. + * If use cc -E but want a different compiler, define DEFAULT_CC. + * If the cpp you need is not in /lib/cpp, define DEFAULT_CPP. + */ +#ifdef hpux +#define USE_CC_E +#endif +#ifdef WIN32 +#define USE_CC_E +#define DEFAULT_CC "cl" +#endif +#ifdef apollo +#define DEFAULT_CPP "/usr/lib/cpp" +#endif +#if defined(_IBMR2) && !defined(DEFAULT_CPP) +#define DEFAULT_CPP "/usr/lpp/X11/Xamples/util/cpp/cpp" +#endif +#if defined(sun) && defined(SVR4) +#define DEFAULT_CPP "/usr/ccs/lib/cpp" +#endif +#ifdef __bsdi__ +#define DEFAULT_CPP "/usr/bin/cpp" +#endif +#ifdef __uxp__ +#define DEFAULT_CPP "/usr/ccs/lib/cpp" +#endif +#ifdef __sxg__ +#define DEFAULT_CPP "/usr/lib/cpp" +#endif +#ifdef _CRAY +#define DEFAULT_CPP "/lib/pcpp" +#endif +#if defined(__386BSD__) || defined(__NetBSD__) || defined(__FreeBSD__) +#define DEFAULT_CPP "/usr/libexec/cpp" +#endif +#ifdef MACH +#define USE_CC_E +#endif +#ifdef __minix_vmd +#define DEFAULT_CPP "/usr/lib/cpp" +#endif +#if defined(__EMX__) +/* expects cpp in PATH */ +#define DEFAULT_CPP "cpp" +#endif + +/* + * Step 5: cpp_argv + * The following table contains the flags that should be passed + * whenever a Makefile is being generated. If your preprocessor + * doesn't predefine any unique symbols, choose one and add it to the + * end of this table. Then, do the following: + * + * a. Use this symbol in Imake.tmpl when setting MacroFile. + * b. Put this symbol in the definition of BootstrapCFlags in your + * .cf file. + * c. When doing a make World, always add "BOOTSTRAPCFLAGS=-Dsymbol" + * to the end of the command line. + * + * Note that you may define more than one symbol (useful for platforms + * that support multiple operating systems). + */ + +#define ARGUMENTS 50 /* number of arguments in various arrays */ +char *cpp_argv[ARGUMENTS] = { + "cc", /* replaced by the actual program to exec */ + "-I.", /* add current directory to include path */ +#ifdef unix + "-Uunix", /* remove unix symbol so that filename unix.c okay */ +#endif +#if defined(__386BSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(MACH) +# ifdef __i386__ + "-D__i386__", +# endif +# ifdef __GNUC__ + "-traditional" +# endif +#endif +#ifdef M4330 + "-DM4330", /* Tektronix */ +#endif +#ifdef M4310 + "-DM4310", /* Tektronix */ +#endif +#if defined(macII) || defined(_AUX_SOURCE) + "-DmacII", /* Apple A/UX */ +#endif +#ifdef USL + "-DUSL", /* USL */ +#endif +#ifdef sony + "-Dsony", /* Sony */ +#if !defined(SYSTYPE_SYSV) && !defined(_SYSTYPE_SYSV) && NEWSOS < 42 + "-Dbsd43", +#endif +#endif +#ifdef _IBMR2 + "-D_IBMR2", /* IBM RS-6000 (we ensured that aix is defined above */ +#ifndef aix +#define aix /* allow BOOTSTRAPCFLAGS="-D_IBMR2" */ +#endif +#endif /* _IBMR2 */ +#ifdef aix + "-Daix", /* AIX instead of AOS */ +#ifndef ibm +#define ibm /* allow BOOTSTRAPCFLAGS="-Daix" */ +#endif +#endif /* aix */ +#ifdef ibm + "-Dibm", /* IBM PS/2 and RT under both AOS and AIX */ +#endif +#ifdef luna + "-Dluna", /* OMRON luna 68K and 88K */ +#ifdef luna1 + "-Dluna1", +#endif +#ifdef luna88k /* need not on UniOS-Mach Vers. 1.13 */ + "-traditional", /* for some older version */ +#endif /* instead of "-DXCOMM=\\#" */ +#ifdef uniosb + "-Duniosb", +#endif +#ifdef uniosu + "-Duniosu", +#endif +#endif /* luna */ +#ifdef _CRAY /* Cray */ + "-Ucray", +#endif +#ifdef Mips + "-DMips", /* Define and use Mips for Mips Co. OS/mach. */ +# if defined(SYSTYPE_BSD) || defined(BSD) || defined(BSD43) + "-DBSD43", /* Mips RISCOS supports two environments */ +# else + "-DSYSV", /* System V environment is the default */ +# endif +#endif /* Mips */ +#ifdef MOTOROLA + "-DMOTOROLA", /* Motorola Delta Systems */ +# ifdef SYSV + "-DSYSV", +# endif +# ifdef SVR4 + "-DSVR4", +# endif +#endif /* MOTOROLA */ +#ifdef i386 +# ifdef SVR4 + "-Di386", + "-DSVR4", +# endif +# ifdef SYSV + "-Di386", + "-DSYSV", +# ifdef ISC + "-DISC", +# ifdef ISC40 + "-DISC40", /* ISC 4.0 */ +# else +# ifdef ISC202 + "-DISC202", /* ISC 2.0.2 */ +# else +# ifdef ISC30 + "-DISC30", /* ISC 3.0 */ +# else + "-DISC22", /* ISC 2.2.1 */ +# endif +# endif +# endif +# endif +# ifdef SCO + "-DSCO", +# ifdef SCO324 + "-DSCO324", +# endif +# endif +# endif +# ifdef ESIX + "-Di386", + "-DESIX", +# endif +# ifdef ATT + "-Di386", + "-DATT", +# endif +# ifdef DELL + "-Di386", + "-DDELL", +# endif +#endif +#ifdef SYSV386 /* System V/386 folks, obsolete */ + "-Di386", +# ifdef SVR4 + "-DSVR4", +# endif +# ifdef ISC + "-DISC", +# ifdef ISC40 + "-DISC40", /* ISC 4.0 */ +# else +# ifdef ISC202 + "-DISC202", /* ISC 2.0.2 */ +# else +# ifdef ISC30 + "-DISC30", /* ISC 3.0 */ +# else + "-DISC22", /* ISC 2.2.1 */ +# endif +# endif +# endif +# endif +# ifdef SCO + "-DSCO", +# ifdef SCO324 + "-DSCO324", +# endif +# endif +# ifdef ESIX + "-DESIX", +# endif +# ifdef ATT + "-DATT", +# endif +# ifdef DELL + "-DDELL", +# endif +#endif +#ifdef __osf__ + "-D__osf__", +# ifdef __mips__ + "-D__mips__", +# endif +# ifdef __alpha + "-D__alpha", +# endif +# ifdef __i386__ + "-D__i386__", +# endif +# ifdef __GNUC__ + "-traditional" +# endif +#endif +#ifdef Oki + "-DOki", +#endif +#ifdef sun +#ifdef SVR4 + "-DSVR4", +#endif +#endif +#ifdef WIN32 + "-DWIN32", + "-nologo", + "-batch", + "-D__STDC__", +#endif +#ifdef NCR + "-DNCR", /* NCR */ +#endif +#ifdef linux + "-traditional", + "-Dlinux", +#endif +#ifdef __uxp__ + "-D__uxp__", +#endif +#ifdef __sxg__ + "-D__sxg__", +#endif +#ifdef nec_ews_svr2 + "-Dnec_ews_svr2", +#endif +#ifdef AMOEBA + "-DAMOEBA", +# ifdef CROSS_COMPILE + "-DCROSS_COMPILE", +# ifdef CROSS_i80386 + "-Di80386", +# endif +# ifdef CROSS_sparc + "-Dsparc", +# endif +# ifdef CROSS_mc68000 + "-Dmc68000", +# endif +# else +# ifdef i80386 + "-Di80386", +# endif +# ifdef sparc + "-Dsparc", +# endif +# ifdef mc68000 + "-Dmc68000", +# endif +# endif +#endif +#ifdef __minix_vmd + "-Dminix", +#endif + +#if defined(__EMX__) + "-traditional", + "-Demxos2", +#endif + +}; +#else /* else MAKEDEPEND */ +/* + * Step 6: predefs + * If your compiler and/or preprocessor define any specific symbols, add + * them to the the following table. The definition of struct symtab is + * in util/makedepend/def.h. + */ +struct symtab predefs[] = { +#ifdef apollo + {"apollo", "1"}, +#endif +#ifdef ibm032 + {"ibm032", "1"}, +#endif +#ifdef ibm + {"ibm", "1"}, +#endif +#ifdef aix + {"aix", "1"}, +#endif +#ifdef sun + {"sun", "1"}, +#endif +#ifdef sun2 + {"sun2", "1"}, +#endif +#ifdef sun3 + {"sun3", "1"}, +#endif +#ifdef sun4 + {"sun4", "1"}, +#endif +#ifdef sparc + {"sparc", "1"}, +#endif +#ifdef __sparc__ + {"__sparc__", "1"}, +#endif +#ifdef hpux + {"hpux", "1"}, +#endif +#ifdef __hpux + {"__hpux", "1"}, +#endif +#ifdef __hp9000s800 + {"__hp9000s800", "1"}, +#endif +#ifdef __hp9000s700 + {"__hp9000s700", "1"}, +#endif +#ifdef vax + {"vax", "1"}, +#endif +#ifdef VMS + {"VMS", "1"}, +#endif +#ifdef cray + {"cray", "1"}, +#endif +#ifdef CRAY + {"CRAY", "1"}, +#endif +#ifdef _CRAY + {"_CRAY", "1"}, +#endif +#ifdef att + {"att", "1"}, +#endif +#ifdef mips + {"mips", "1"}, +#endif +#ifdef __mips__ + {"__mips__", "1"}, +#endif +#ifdef ultrix + {"ultrix", "1"}, +#endif +#ifdef stellar + {"stellar", "1"}, +#endif +#ifdef mc68000 + {"mc68000", "1"}, +#endif +#ifdef mc68020 + {"mc68020", "1"}, +#endif +#ifdef __GNUC__ + {(char *)"__GNUC__", (char *)"1"}, +#endif +#if __STDC__ + {(char *)"__STDC__", (char *)"1"}, +#endif +#ifdef __HIGHC__ + {"__HIGHC__", "1"}, +#endif +#ifdef CMU + {"CMU", "1"}, +#endif +#ifdef luna + {"luna", "1"}, +#ifdef luna1 + {"luna1", "1"}, +#endif +#ifdef luna2 + {"luna2", "1"}, +#endif +#ifdef luna88k + {"luna88k", "1"}, +#endif +#ifdef uniosb + {"uniosb", "1"}, +#endif +#ifdef uniosu + {"uniosu", "1"}, +#endif +#endif +#ifdef ieeep754 + {"ieeep754", "1"}, +#endif +#ifdef is68k + {"is68k", "1"}, +#endif +#ifdef m68k + {"m68k", "1"}, +#endif +#ifdef m88k + {"m88k", "1"}, +#endif +#ifdef __m88k__ + {"__m88k__", "1"}, +#endif +#ifdef bsd43 + {"bsd43", "1"}, +#endif +#ifdef hcx + {"hcx", "1"}, +#endif +#ifdef sony + {"sony", "1"}, +#ifdef SYSTYPE_SYSV + {"SYSTYPE_SYSV", "1"}, +#endif +#ifdef _SYSTYPE_SYSV + {"_SYSTYPE_SYSV", "1"}, +#endif +#endif +#ifdef __OSF__ + {"__OSF__", "1"}, +#endif +#ifdef __osf__ + {"__osf__", "1"}, +#endif +#ifdef __alpha + {"__alpha", "1"}, +#endif +#ifdef __DECC + {"__DECC", "1"}, +#endif +#ifdef __decc + {"__decc", "1"}, +#endif +#ifdef __uxp__ + {"__uxp__", "1"}, +#endif +#ifdef __sxg__ + {"__sxg__", "1"}, +#endif +#ifdef _SEQUENT_ + {"_SEQUENT_", "1"}, + {"__STDC__", "1"}, +#endif +#ifdef __bsdi__ + {"__bsdi__", "1"}, +#endif +#ifdef nec_ews_svr2 + {"nec_ews_svr2", "1"}, +#endif +#ifdef nec_ews_svr4 + {"nec_ews_svr4", "1"}, +#endif +#ifdef _nec_ews_svr4 + {"_nec_ews_svr4", "1"}, +#endif +#ifdef _nec_up + {"_nec_up", "1"}, +#endif +#ifdef SX + {"SX", "1"}, +#endif +#ifdef nec + {"nec", "1"}, +#endif +#ifdef _nec_ft + {"_nec_ft", "1"}, +#endif +#ifdef PC_UX + {(char *)"PC_UX", (char *)"1"}, +#endif +#ifdef __EMX__ + {"__EMX__", "1"}, +#endif + /* add any additional symbols before this line */ + {NULL, NULL} +}; + +#endif /* MAKEDEPEND */ +#endif /* CCIMAKE */ diff --git a/core/tools/dependency_tool/include.cpp b/core/tools/dependency_tool/include.cpp new file mode 100644 index 00000000..08ade608 --- /dev/null +++ b/core/tools/dependency_tool/include.cpp @@ -0,0 +1,348 @@ +/* $XConsortium: include.c,v 1.17 94/12/05 19:33:08 gildea Exp $ */ +/* + +Copyright (c) 1993, 1994 X Consortium + +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 +X CONSORTIUM 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. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + +*/ + +#ifdef __WIN32__ + #pragma warning(disable : 4996) +#endif + +#include "def.h" + +#include + +#ifdef _MSC_VER + #undef strcasecmp + #undef strncasecmp + #define strcasecmp strcmpi + #define strncasecmp strnicmp +#endif + +extern inclist inc_list[MAXFILES], *inclistp; +extern char *includedirs[ ]; +extern char *excludedirs[ ]; +extern char *notdotdot[ ]; +extern bool show_where_not; +extern bool warn_multiple; + +// forward. +void remove_dotdot(char *path); +int isdot(register char *p); +int isdotdot(register char *p); +int issymbolic(register char *dir, register char *component); +void included_by(register inclist *ip, register inclist *newfile); + +inclist *inc_path(register char *file, register char *include, bool dot, + bool &failure_okay) +{ + static char path[ BUFSIZ ]; + register char **pp, *p; + register inclist *ip; + struct stat st; + bool found = false; + +//fprintf(stderr, "file=%s include=%s\n", file, include); + const size_t inclen = strlen(include); + if (inclen >= 4) { + register char *cpp_point = include + inclen - 4; + if (!strcasecmp(".cpp", cpp_point)) { + // this is a CPP file include, which we skip. +//fprintf(stderr, "!found match at point: %s\n", cpp_point); +//hold failure_okay = true; +//hold return NULL; + } + } + +////////fprintf(stderr, "incpath entry\n"); + + /* + * Check all previously found include files for a path that + * has already been expanded. + */ + for (ip = inc_list; ip->i_file; ip++) + if ((strcmp(ip->i_incstring, include) == 0) && !ip->i_included_sym) { + found = true; + break; + } + + /* + * If the path was surrounded by "" or is an absolute path, + * then check the exact path provided. + */ + if (!found && (dot || *include == '/' || *include == '\\')) { + if (stat(include, &st) == 0) { + ip = newinclude(include, include); + found = true; + } + else if (show_where_not) + warning1("\tnot in %s\n", include); + } + + /* + * See if this include file is in the directory of the + * file being compiled. + */ + if (!found) { + for (p=file+strlen(file); p>file; p--) + if (*p == '/' || *p == '\\') + break; + if (p == file) + strcpy(path, include); + else { + strncpy(path, file, (p-file) + 1); + path[ (p-file) + 1 ] = '\0'; + strcpy(path + (p-file) + 1, include); + } + remove_dotdot(path); + if (stat(path, &st) == 0) { + ip = newinclude(path, include); + found = true; + } + else if (show_where_not) + warning1("\tnot in %s\n", path); + } + + /* + * Check the include directories specified. (standard include dir + * should be at the end.) + */ + if (!found) + for (pp = includedirs; *pp; pp++) { + sprintf(path, "%s/%s", *pp, include); + remove_dotdot(path); + if (stat(path, &st) == 0) { + register char **pp2; + bool exclude_it = false; + for (pp2 = excludedirs; *pp2; pp2++) { +////////fprintf(stderr, "comparing %s with %s\n", *pp, *pp2); + if (!strncmp(*pp, *pp2, strlen(*pp2))) { + // this file is supposed to be excluded. + exclude_it = true; + break; + } + } +////////if (exclude_it) fprintf(stderr, "excluding path %s\n", path); + if (exclude_it) { + failure_okay = true; + found = false; + } else { + ip = newinclude(path, include); + found = true; + } + break; + } + else if (show_where_not) + warning1("\tnot in %s\n", path); + } + + if (!found) + ip = NULL; + return(ip); +} + +/* + * Occasionally, pathnames are created that look like .../x/../y + * Any of the 'x/..' sequences within the name can be eliminated. + * (but only if 'x' is not a symbolic link!!) + */ +void remove_dotdot(char *path) +{ + register char *end, *from, *to, **cp; + char *components[ MAXFILES ], newpath[ BUFSIZ ]; + bool component_copied; + + /* + * slice path up into components. + */ + to = newpath; + if (*path == '/' || *path == '\\') + *to++ = '/'; + *to = '\0'; + cp = components; + for (from=end=path; *end; end++) + if (*end == '/' || *end == '\\') { + while (*end == '/' || *end == '\\') + *end++ = '\0'; + if (*from) + *cp++ = from; + from = end; + } + *cp++ = from; + *cp = NULL; + + /* + * Recursively remove all 'x/..' component pairs. + */ + cp = components; + while(*cp) { + if (!isdot(*cp) && !isdotdot(*cp) && isdotdot(*(cp+1)) + && !issymbolic(newpath, *cp)) + { + char **fp = cp + 2; + char **tp = cp; + + do + *tp++ = *fp; /* move all the pointers down */ + while (*fp++); + if (cp != components) + cp--; /* go back and check for nested ".." */ + } else { + cp++; + } + } + /* + * Concatenate the remaining path elements. + */ + cp = components; + component_copied = false; + while(*cp) { + if (component_copied) + *to++ = '/'; + component_copied = true; + for (from = *cp; *from; ) + *to++ = *from++; + *to = '\0'; + cp++; + } + *to++ = '\0'; + + /* + * copy the reconstituted path back to our pointer. + */ + strcpy(path, newpath); +} + +int isdot(register char *p) +{ + if(p && *p++ == '.' && *p++ == '\0') + return(true); + return(false); +} + +int isdotdot(register char *p) +{ + if(p && *p++ == '.' && *p++ == '.' && *p++ == '\0') + return(true); + return(false); +} + +int issymbolic(register char *dir, register char *component) +{ +#ifdef S_IFLNK + struct stat st; + char buf[ BUFSIZ ], **pp; + + sprintf(buf, "%s%s%s", dir, *dir ? "/" : "", component); + for (pp=notdotdot; *pp; pp++) + if (strcmp(*pp, buf) == 0) + return (true); + if (lstat(buf, &st) == 0 + && (st.st_mode & S_IFMT) == S_IFLNK) { + *pp++ = copy(buf); + if (pp >= ¬dotdot[ MAXDIRS ]) + fatalerr("out of .. dirs, increase MAXDIRS\n"); + return(true); + } +#endif + return(false); +} + +/* + * Add an include file to the list of those included by 'file'. + */ +inclist *newinclude(register char *newfile, register char *incstring) +{ + register inclist *ip; + + /* + * First, put this file on the global list of include files. + */ + ip = inclistp++; + if (inclistp == inc_list + MAXFILES - 1) + fatalerr("out of space: increase MAXFILES\n"); + ip->i_file = copy(newfile); + ip->i_included_sym = false; + if (incstring == NULL) + ip->i_incstring = ip->i_file; + else + ip->i_incstring = copy(incstring); + + return(ip); +} + +void included_by(register inclist *ip, register inclist *newfile) +{ + register int i; + + if (ip == NULL) + return; + /* + * Put this include file (newfile) on the list of files included + * by 'file'. If 'file' is NULL, then it is not an include + * file itself (i.e. was probably mentioned on the command line). + * If it is already on the list, don't stick it on again. + */ + if (ip->i_list == NULL) + ip->i_list = (inclist **) + malloc(sizeof(inclist *) * ++ip->i_listlen); + else { + for (i=0; ii_listlen; i++) + if (ip->i_list[ i ] == newfile) { + i = int(strlen(newfile->i_file)); + if (!ip->i_included_sym && + !(i > 2 && + newfile->i_file[i-1] == 'c' && + newfile->i_file[i-2] == '.')) + { + /* only complain if ip has */ + /* no #include SYMBOL lines */ + /* and is not a .c file */ + if (warn_multiple) + { + warning("%s includes %s more than once!\n", + ip->i_file, newfile->i_file); + warning1("Already have\n"); + for (i=0; ii_listlen; i++) + warning1("\t%s\n", ip->i_list[i]->i_file); + } + } + return; + } + ip->i_list = (inclist **) realloc(ip->i_list, + sizeof(inclist *) * ++ip->i_listlen); + } + ip->i_list[ ip->i_listlen-1 ] = newfile; +} + +void inc_clean() +{ + register inclist *ip; + + for (ip = inc_list; ip < inclistp; ip++) { + ip->i_marked = false; + } +} + diff --git a/core/tools/dependency_tool/makedep.cpp b/core/tools/dependency_tool/makedep.cpp new file mode 100644 index 00000000..83df3bf1 --- /dev/null +++ b/core/tools/dependency_tool/makedep.cpp @@ -0,0 +1,785 @@ +/* $XConsortium: main.c,v 1.84 94/11/30 16:10:44 kaleb Exp $ */ +/* $XFree86: xc/config/makedepend/main.c,v 3.3 1995/01/28 15:41:03 dawes Exp $ */ +/* + +Copyright (c) 1993, 1994 X Consortium + +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 +X CONSORTIUM 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. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + +*/ + +#ifdef __WIN32__ + #pragma warning(disable : 4996) +#endif + +#include "def.h" + +#ifdef _MSC_VER +#include +#else +#include +#endif +#include +#include + +#ifdef hpux +#define sigvec sigvector +#endif /* hpux */ + +#ifdef X_POSIX_C_SOURCE +#define _POSIX_C_SOURCE X_POSIX_C_SOURCE +#include +#undef _POSIX_C_SOURCE +#else +#if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE) +#include +#else +#define _POSIX_SOURCE +#include +#undef _POSIX_SOURCE +#endif +#endif + +#if NeedVarargsPrototypes +#include +#endif + +#ifdef MINIX +#define USE_CHMOD 1 +#endif + +#ifdef DEBUG +int _debugmask; +#endif + +char *ProgramName; + +const char *directives[] = { + "if", + "ifdef", + "ifndef", + "else", + "endif", + "define", + "undef", + "include", + "line", + "pragma", + "error", + "ident", + "sccs", + "elif", + "eject", + "import", + NULL +}; + +#define MAKEDEPEND +#include "imakemdep.h" /* from config sources */ +#undef MAKEDEPEND + +struct inclist inc_list[ MAXFILES ], *inclistp = inc_list, maininclist; + +char *filelist[ MAXFILES ]; +char *includedirs[ MAXDIRS + 1 ]; +char *excludedirs[ MAXDIRS + 1 ]; +char *notdotdot[ MAXDIRS ]; +char *objprefix = (char *)""; +char *objsuffix = (char *)".obj"; /* OBJSUFFIX; */ +char *startat = (char *)"# DO NOT DELETE"; +int width = 78; +bool append = false; +bool printed = false; +bool verbose = false; +bool show_where_not = false; +bool warn_multiple = false; /* Warn on multiple includes of same file */ + +static +#ifdef SIGNALRETURNSINT +int +#else +void +#endif +c_catch(int sig) +{ + fflush (stdout); + fatalerr ((char *)"got signal %d\n", sig); +} + +#if defined(USG) || (defined(i386) && defined(SYSV)) || defined(WIN32) || defined(__EMX__) || defined(__OS2__) +#define USGISH +#endif + +#ifndef USGISH +#ifndef _POSIX_SOURCE +#define sigaction sigvec +#define sa_handler sv_handler +#define sa_mask sv_mask +#define sa_flags sv_flags +#endif +struct sigaction sig_act; +#endif /* USGISH */ + +/* fatty boombalatty, and wrong idea here. + +// adds any subdirectories under dirname into the list of +// include directories, so we can get a whole hierarchies set of +// include files. +void add_subdirs(char *dirname, char ** &incp) +{ + directory dir(dirname); + string_array subdirs = dir.directories(); + for (int i = 0; i < subdirs.length(); i++) { + istring curr = istring(dirname) + "/" + subdirs[i]; + // add the current subdirectory. + if (incp >= includedirs + MAXDIRS) + fatalerr("Too many -I flags.\n"); + *incp++ = strdup(curr.s()); +printf((istring("added ") + curr + "\n").s()); + add_subdirs(curr.s(), incp); + } +} +*/ + +int main(int argc, char **argv) +{ + register char **fp = filelist; + register char **incp = includedirs; + register char **excp = excludedirs; + register char *p; + register struct inclist *ip; + char *makefile = NULL; + struct filepointer *filecontent; + struct symtab *psymp = predefs; + char *endmarker = NULL; + char *defincdir = NULL; + + ProgramName = argv[0]; + + while (psymp->s_name) + { + define2(psymp->s_name, psymp->s_value, &maininclist); + psymp++; + } + if (argc == 2 && argv[1][0] == '@') { + struct stat ast; + int afd; + char *args; + char **nargv; + int nargc; + char quotechar = '\0'; + + nargc = 1; + if ((afd = open(argv[1]+1, O_RDONLY)) < 0) + fatalerr("cannot open \"%s\"\n", argv[1]+1); + fstat(afd, &ast); + args = (char *)malloc(ast.st_size + 2); + if ((ast.st_size = read(afd, args, ast.st_size)) < 0) + fatalerr("failed to read %s\n", argv[1]+1); + args[ast.st_size] = '\n'; + args[ast.st_size + 1] = '\0'; + close(afd); + for (p = args; *p; p++) { + if (quotechar) { + if (quotechar == '\\' || + (*p == quotechar && p[-1] != '\\')) + quotechar = '\0'; + continue; + } + switch (*p) { + case '\\': + case '"': + case '\'': + quotechar = *p; + break; + case ' ': + case '\n': + *p = '\0'; + if (p > args && p[-1]) + nargc++; + break; + } + } + if (p[-1]) + nargc++; + nargv = (char **)malloc(nargc * sizeof(char *)); + nargv[0] = argv[0]; + argc = 1; + for (p = args; argc < nargc; p += strlen(p) + 1) + if (*p) nargv[argc++] = p; + argv = nargv; + } + for(argc--, argv++; argc; argc--, argv++) { + /* if looking for endmarker then check before parsing */ + if (endmarker && strcmp (endmarker, *argv) == 0) { + endmarker = NULL; + continue; + } + if (**argv != '-') { + /* treat +thing as an option for C++ */ + if (endmarker && **argv == '+') + continue; + *fp++ = argv[0]; + continue; + } + switch(argv[0][1]) { + case '-': + endmarker = &argv[0][2]; + if (endmarker[0] == '\0') endmarker = (char *)"--"; + break; + case 'D': + if (argv[0][2] == '\0') { + argv++; + argc--; + } + for (p=argv[0] + 2; *p ; p++) + if (*p == '=') { + *p = ' '; + break; + } + define(argv[0] + 2, &maininclist); + break; + case 'i': + { + char* delim; + char* envinclude; + char* prevdir; + if (endmarker) break; + if (argv[0][2] == '\0') { + argv++; + argc--; + envinclude = getenv(argv[0]); + } else envinclude = getenv(argv[0]+2); + if (!envinclude) break; + prevdir = envinclude; + delim = (char*)strchr(envinclude, ';'); + while(delim) + { + if (incp >= includedirs + MAXDIRS) fatalerr("Too many Include directories.\n"); + *delim = '\0'; + delim++; + *incp++ = prevdir; + prevdir = delim; + delim = (char*)strchr(delim, ';'); + } + } + break; + case 'X': +//fprintf(stderr, "adding Xclude %s\n", argv[0]+2); + // add a directory to the exclusions list. + if (excp >= excludedirs + MAXDIRS) + fatalerr("Too many -X flags.\n"); + *excp++ = argv[0]+2; + if (**(excp-1) == '\0') { + // fix the prior entry, but don't incremement yet; we'll do that + // on the include list instead. + *(excp-1) = *(argv + 1); + } + if (incp >= includedirs + MAXDIRS) + fatalerr("Too many -I flags via -X.\n"); + *incp++ = argv[0]+2; + if (**(incp-1) == '\0') { + *(incp-1) = *(++argv); + argc--; + } + break; + case 'I': + if (incp >= includedirs + MAXDIRS) + fatalerr("Too many -I flags.\n"); + *incp++ = argv[0]+2; + if (**(incp-1) == '\0') { + *(incp-1) = *(++argv); + argc--; + } +/// add_subdirs(*(incp-1), incp); + break; + case 'Y': + defincdir = argv[0]+2; + break; + /* do not use if endmarker processing */ + case 'a': + if (endmarker) break; + append = true; + break; + case 'w': + if (endmarker) break; + if (argv[0][2] == '\0') { + argv++; + argc--; + width = atoi(argv[0]); + } else + width = atoi(argv[0]+2); + break; + case 'o': + if (endmarker) break; + if (argv[0][2] == '\0') { + argv++; + argc--; + objsuffix = argv[0]; + } else + objsuffix = argv[0]+2; + break; + case 'p': + if (endmarker) break; + if (argv[0][2] == '\0') { + argv++; + argc--; + objprefix = argv[0]; + } else + objprefix = argv[0]+2; + break; + case 'v': + if (endmarker) break; + verbose = true; +#ifdef DEBUG + if (argv[0][2]) + _debugmask = atoi(argv[0]+2); +#endif + break; + case 's': + if (endmarker) break; + startat = argv[0]+2; + if (*startat == '\0') { + startat = *(++argv); + argc--; + } + if (*startat != '#') + fatalerr("-s flag's value should start %s\n", + "with '#'."); + break; + case 'f': + if (endmarker) break; + makefile = argv[0]+2; + if (*makefile == '\0') { + makefile = *(++argv); + argc--; + } + break; + case 'm': + warn_multiple = true; + break; + + /* Ignore -O, -g so we can just pass ${CFLAGS} to + makedepend + */ + case 'O': + case 'g': + break; + default: + if (endmarker) break; + /* fatalerr("unknown opt = %s\n", argv[0]); */ + warning("ignoring option %s\n", argv[0]); + } + } + + if (!defincdir) { +#ifdef PREINCDIR + if (incp >= includedirs + MAXDIRS) + fatalerr("Too many -I flags.\n"); + *incp++ = PREINCDIR; +#endif +#ifdef INCLUDEDIR + if (incp >= includedirs + MAXDIRS) + fatalerr("Too many -I flags.\n"); + *incp++ = INCLUDEDIR; +#endif +#ifdef POSTINCDIR + if (incp >= includedirs + MAXDIRS) + fatalerr("Too many -I flags.\n"); + *incp++ = POSTINCDIR; +#endif + } else if (*defincdir) { + if (incp >= includedirs + MAXDIRS) + fatalerr("Too many -I flags.\n"); + *incp++ = defincdir; + } + + redirect(startat, makefile); + + /* + * c_catch signals. + */ +#ifdef USGISH +/* should really reset SIGINT to SIG_IGN if it was. */ +#ifdef SIGHUP + signal (SIGHUP, c_catch); +#endif + signal (SIGINT, c_catch); +#ifdef SIGQUIT + signal (SIGQUIT, c_catch); +#endif + signal (SIGILL, c_catch); +#ifdef SIGBUS + signal (SIGBUS, c_catch); +#endif + signal (SIGSEGV, c_catch); +#ifdef SIGSYS + signal (SIGSYS, c_catch); +#endif +#else + sig_act.sa_handler = c_catch; +#ifdef _POSIX_SOURCE + sigemptyset(&sig_act.sa_mask); + sigaddset(&sig_act.sa_mask, SIGINT); + sigaddset(&sig_act.sa_mask, SIGQUIT); +#ifdef SIGBUS + sigaddset(&sig_act.sa_mask, SIGBUS); +#endif + sigaddset(&sig_act.sa_mask, SIGILL); + sigaddset(&sig_act.sa_mask, SIGSEGV); + sigaddset(&sig_act.sa_mask, SIGHUP); + sigaddset(&sig_act.sa_mask, SIGPIPE); +#ifdef SIGSYS + sigaddset(&sig_act.sa_mask, SIGSYS); +#endif +#else + sig_act.sa_mask = ((1<<(SIGINT -1)) + |(1<<(SIGQUIT-1)) +#ifdef SIGBUS + |(1<<(SIGBUS-1)) +#endif + |(1<<(SIGILL-1)) + |(1<<(SIGSEGV-1)) + |(1<<(SIGHUP-1)) + |(1<<(SIGPIPE-1)) +#ifdef SIGSYS + |(1<<(SIGSYS-1)) +#endif + ); +#endif /* _POSIX_SOURCE */ + sig_act.sa_flags = 0; + sigaction(SIGHUP, &sig_act, (struct sigaction *)0); + sigaction(SIGINT, &sig_act, (struct sigaction *)0); + sigaction(SIGQUIT, &sig_act, (struct sigaction *)0); + sigaction(SIGILL, &sig_act, (struct sigaction *)0); +#ifdef SIGBUS + sigaction(SIGBUS, &sig_act, (struct sigaction *)0); +#endif + sigaction(SIGSEGV, &sig_act, (struct sigaction *)0); +#ifdef SIGSYS + sigaction(SIGSYS, &sig_act, (struct sigaction *)0); +#endif +#endif /* USGISH */ + + /* + * now peruse through the list of files. + */ + for(fp=filelist; *fp; fp++) { + filecontent = getfile(*fp); + ip = newinclude(*fp, (char *)NULL); + + find_includes(filecontent, ip, ip, 0, false); + freefile(filecontent); + recursive_pr_include(ip, ip->i_file, base_name(*fp)); + inc_clean(); + } + if (printed) + printf("\n"); + + return 0; +} + +struct filepointer *getfile(char *file) +{ + register int fd; + struct filepointer *content; + struct stat st; + + content = (struct filepointer *)malloc(sizeof(struct filepointer)); + content->f_name = strdup(file); + if ((fd = open(file, O_RDONLY)) < 0) { + warning("cannot open \"%s\"\n", file); + content->f_p = content->f_base = content->f_end = (char *)malloc(1); + *content->f_p = '\0'; + return(content); + } + fstat(fd, &st); + content->f_base = (char *)malloc(st.st_size+1); + if (content->f_base == NULL) + fatalerr("cannot allocate mem\n"); + if ((st.st_size = read(fd, content->f_base, st.st_size)) < 0) + fatalerr("failed to read %s\n", file); + close(fd); + content->f_len = st.st_size+1; + content->f_p = content->f_base; + content->f_end = content->f_base + st.st_size; + *content->f_end = '\0'; + content->f_line = 0; + return(content); +} + +void freefile(struct filepointer *fp) +{ + free(fp->f_name); + free(fp->f_base); + free(fp); +} + +char *copy(register char *str) +{ + register char *p = (char *)malloc(strlen(str) + 1); + + strcpy(p, str); + return(p); +} + +int match(register const char *str, register const char **list) +{ + register int i; + + for (i=0; *list; i++, list++) + if (strcmp(str, *list) == 0) + return i; + return -1; +} + +/* + * Get the next line. We only return lines beginning with '#' since that + * is all this program is ever interested in. + */ +char *getline(register struct filepointer *filep) +{ + register char *p, /* walking pointer */ + *eof, /* end of file pointer */ + *bol; /* beginning of line pointer */ + register int lineno; /* line number */ + + eof = filep->f_end; +//// if (p >= eof) return NULL; + lineno = filep->f_line; + bol = filep->f_p; + + // p is always pointing at the "beginning of a line" when we start this loop. + // this means that we must start considering the stuff on that line as + // being a useful thing to look at. + for (p = filep->f_p; p < eof; p++) { +if (bol > p) fatalerr("somehow bol got ahead of p."); + if (*p == '/' && *(p+1) == '*') { + /* consume C-style comments */ + *p++ = ' '; *p++ = ' '; // skip the two we've already seen. + while (p < eof) { + if (*p == '*' && *(p+1) == '/') { + *p++ = ' '; *p = ' '; + // skip one of these last two, let the loop skip the next. + break; + } else if (*p == '\n') lineno++; + p++; // skip the current character. + } + continue; + } else if (*p == '/' && *(p+1) == '/') { + /* consume C++-style comments */ + *p++ = ' '; *p++ = ' '; // skip the comment characters. + while (p < eof && (*p != '\n') ) *p++ = ' '; + // we scan until we get to the end of line. +///no count, since we'll see it again. lineno++; + p--; // skip back to just before \n. + continue; // let the loop skip past the eol that we saw. + } else if (*p == '\\') { + // handle an escape character. + if (*(p+1) == '\n') { + // we modify the stream so we consider the line correctly. + *p = ' '; + *(p+1) = ' '; + lineno++; + } + } else if (*p == '\n') { + // we've finally reached the end of the line. + lineno++; + *p = '\0'; // set the end of line to be a end of string now. + if (bol < p) { + // it's not at the same position as the end of line, so it's worth + // considering. + while ( (bol < p) && ((*bol == ' ') || (*bol == '\t')) ) bol++; +////fprintf(stderr, "%s: %s\n", filep->f_name, bol); +////fflush(stderr); + if (*bol == '#') { + register char *cp; + /* punt lines with just # (yacc generated) */ + for (cp = bol+1; *cp && (*cp == ' ' || *cp == '\t'); cp++) {} + if (*cp) { p++; goto done; } + } + } + // this is a failure now. we reset the beginning of line. + bol = p+1; + } + } + if (*bol != '#') bol = NULL; +done: + filep->f_p = p; + filep->f_line = lineno; + return bol; +} + +/* + * Strip the file name down to what we want to see in the Makefile. + * It will have objprefix and objsuffix around it. + */ +char *base_name(register char *file) +{ + register char *p; + + file = copy(file); + for(p=file+strlen(file); p>file && *p != '.'; p--) ; + + if (*p == '.') + *p = '\0'; + return(file); +} + +#if defined(USG) && !defined(CRAY) && !defined(SVR4) && !defined(__EMX__) +int rename(char *from, char *to) +{ + (void) unlink (to); + if (link (from, to) == 0) { + unlink (from); + return 0; + } else { + return -1; + } +} +#endif /* USGISH */ + +void redirect(char *line, char *makefile) +{ + struct stat st; + FILE *fdin, *fdout; + char backup[ BUFSIZ ], + buf[ BUFSIZ ]; + bool found = false; + int len; + + /* + * if makefile is "-" then let it pour onto stdout. + */ + if (makefile && *makefile == '-' && *(makefile+1) == '\0') + return; + + /* + * use a default makefile is not specified. + */ + if (!makefile) { + if (stat("Makefile", &st) == 0) + makefile = (char *)"Makefile"; + else if (stat("makefile", &st) == 0) + makefile = (char *)"makefile"; + else + fatalerr("[mM]akefile is not present\n"); + } + else + stat(makefile, &st); + if ((fdin = fopen(makefile, "r")) == NULL) + fatalerr("cannot open \"%s\"\n", makefile); + sprintf(backup, "%s.bak", makefile); + unlink(backup); +#if defined(WIN32) || defined(__EMX__) || defined(__OS2__) + fclose(fdin); +#endif + if (rename(makefile, backup) < 0) + fatalerr("cannot rename %s to %s\n", makefile, backup); +#if defined(WIN32) || defined(__EMX__) || defined(__OS2__) + if ((fdin = fopen(backup, "r")) == NULL) + fatalerr("cannot open \"%s\"\n", backup); +#endif + if ((fdout = freopen(makefile, "w", stdout)) == NULL) + fatalerr("cannot open \"%s\"\n", backup); + len = int(strlen(line)); + while (!found && fgets(buf, BUFSIZ, fdin)) { + if (*buf == '#' && strncmp(line, buf, len) == 0) + found = true; + fputs(buf, fdout); + } + if (!found) { + if (verbose) + warning("Adding new delimiting line \"%s\" and dependencies...\n", + line); + puts(line); /* same as fputs(fdout); but with newline */ + } else if (append) { + while (fgets(buf, BUFSIZ, fdin)) { + fputs(buf, fdout); + } + } + fflush(fdout); +#if defined(USGISH) || defined(_SEQUENT_) || defined(USE_CHMOD) + chmod(makefile, st.st_mode); +#else + fchmod(fileno(fdout), st.st_mode); +#endif /* USGISH */ +} + +#if NeedVarargsPrototypes +void fatalerr(const char *msg, ...) +#else +/*VARARGS*/ +void fatalerr(char *msg,x1,x2,x3,x4,x5,x6,x7,x8,x9) +#endif +{ +#if NeedVarargsPrototypes + va_list args; +#endif + fprintf(stderr, "%s: error: ", ProgramName); +#if NeedVarargsPrototypes + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); +#else + fprintf(stderr, msg,x1,x2,x3,x4,x5,x6,x7,x8,x9); +#endif + exit (1); +} + +#if NeedVarargsPrototypes +void warning(const char *msg, ...) +#else +/*VARARGS0*/ +void warning(const char *msg,x1,x2,x3,x4,x5,x6,x7,x8,x9) +#endif +{ +#if NeedVarargsPrototypes + va_list args; +#endif + fprintf(stderr, "%s: warning: ", ProgramName); +#if NeedVarargsPrototypes + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); +#else + fprintf(stderr, msg,x1,x2,x3,x4,x5,x6,x7,x8,x9); +#endif +} + +#if NeedVarargsPrototypes +void warning1(const char *msg, ...) +#else +/*VARARGS0*/ +void warning1(const char *msg,x1,x2,x3,x4,x5,x6,x7,x8,x9) +#endif +{ +#if NeedVarargsPrototypes + va_list args; + va_start(args, msg); + vfprintf(stderr, msg, args); + va_end(args); +#else + fprintf(stderr, msg,x1,x2,x3,x4,x5,x6,x7,x8,x9); +#endif +} + diff --git a/core/tools/dependency_tool/makedep_version.rc b/core/tools/dependency_tool/makedep_version.rc new file mode 100644 index 00000000..a32fcb38 --- /dev/null +++ b/core/tools/dependency_tool/makedep_version.rc @@ -0,0 +1,46 @@ +#ifndef NO_VERSION +#include +#include <__build_version.h> +#include <__build_configuration.h> +#define BI_PLAT_WIN32 + // force 32 bit compile. +1 VERSIONINFO LOADONCALL MOVEABLE +FILEVERSION __build_FILE_VERSION_COMMAS +PRODUCTVERSION __build_PRODUCT_VERSION_COMMAS +FILEFLAGSMASK 0 +FILEFLAGS VS_FFI_FILEFLAGSMASK +#if defined(BI_PLAT_WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + // Language type = U.S. English(0x0409) and Character Set = Windows, Multilingual(0x04b0) + BLOCK "040904b0" // Matches VarFileInfo Translation hex value. + BEGIN + VALUE "CompanyName", __build_company "\000" +#ifndef _DEBUG + VALUE "FileDescription", "Makefile Dependency Generator\000" +#else + VALUE "FileDescription", "Makefile Dependency Generator (DEBUG)\000" +#endif + VALUE "FileVersion", __build_FILE_VERSION "\000" + VALUE "ProductVersion", __build_PRODUCT_VERSION "\000" + VALUE "InternalName", "Dependency_Maker\000" + VALUE "LegalCopyright", __build_copyright "\000" + VALUE "LegalTrademarks", __build_legal_info "\000" + VALUE "OriginalFilename", "makedep.exe\000" + VALUE "ProductName", __build_product_name "\000" + + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04b0 // US English (0x0409) and win32 multilingual (0x04b0) + END +END +#endif diff --git a/core/tools/dependency_tool/makedepend.man b/core/tools/dependency_tool/makedepend.man new file mode 100644 index 00000000..9c3cdccd --- /dev/null +++ b/core/tools/dependency_tool/makedepend.man @@ -0,0 +1,368 @@ +.\" $XConsortium: mkdepend.man,v 1.15 94/04/17 20:10:37 gildea Exp $ +.\" Copyright (c) 1993, 1994 X Consortium +.\" +.\" 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 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 X CONSORTIUM 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. +.\" +.\" Except as contained in this notice, the name of the X Consortium shall not +.\" be used in advertising or otherwise to promote the sale, use or other +.\" dealing in this Software without prior written authorization from the +.\" X Consortium. +.TH MAKEDEPEND 1 "Release 6" "X Version 11" +.UC 4 +.SH NAME +makedepend \- create dependencies in makefiles +.SH SYNOPSIS +.B makedepend +[ +.B \-Dname=def +] [ +.B \-Dname +] [ +.B \-Iincludedir +] [ +.B \-Yincludedir +] [ +.B \-a +] [ +.B \-fmakefile +] [ +.B \-oobjsuffix +] [ +.B \-pobjprefix +] [ +.B \-sstring +] [ +.B \-wwidth +] [ +.B \-v +] [ +.B \-m +] [ +\-\^\- +.B otheroptions +\-\^\- +] +sourcefile .\|.\|. +.br +.SH DESCRIPTION +.B Makedepend +reads each +.I sourcefile +in sequence and parses it like a C-preprocessor, +processing all +.I #include, +.I #define, +.I #undef, +.I #ifdef, +.I #ifndef, +.I #endif, +.I #if +and +.I #else +directives so that it can correctly tell which +.I #include, +directives would be used in a compilation. +Any +.I #include, +directives can reference files having other +.I #include +directives, and parsing will occur in these files as well. +.PP +Every file that a +.I sourcefile +includes, +directly or indirectly, +is what +.B makedepend +calls a "dependency". +These dependencies are then written to a +.I makefile +in such a way that +.B make(1) +will know which object files must be recompiled when a dependency has changed. +.PP +By default, +.B makedepend +places its output in the file named +.I makefile +if it exists, otherwise +.I Makefile. +An alternate makefile may be specified with the +.B \-f +option. +It first searches the makefile for +the line +.sp + # DO NOT DELETE THIS LINE \-\^\- make depend depends on it. +.sp +or one provided with the +.B \-s +option, +as a delimiter for the dependency output. +If it finds it, it will delete everything +following this to the end of the makefile +and put the output after this line. +If it doesn't find it, the program +will append the string to the end of the makefile +and place the output following that. +For each +.I sourcefile +appearing on the command line, +.B makedepend +puts lines in the makefile of the form +.sp + sourcefile.o:\0dfile .\|.\|. +.sp +Where "sourcefile.o" is the name from the command +line with its suffix replaced with ".o", +and "dfile" is a dependency discovered in a +.I #include +directive while parsing +.I sourcefile +or one of the files it included. +.SH EXAMPLE +Normally, +.B makedepend +will be used in a makefile target so that typing "make depend" will +bring the dependencies up to date for the makefile. +For example, +.nf + SRCS\0=\0file1.c\0file2.c\0.\|.\|. + CFLAGS\0=\0\-O\0\-DHACK\0\-I\^.\^.\^/foobar\0\-xyz + depend: + makedepend\0\-\^\-\0$(CFLAGS)\0\-\^\-\0$(SRCS) +.fi +.SH OPTIONS +.B Makedepend +will ignore any option that it does not understand so that you may use +the same arguments that you would for +.B cc(1). +.TP 5 +.B \-Dname=def or \-Dname +Define. +This places a definition for +.I name +in +.B makedepend's +symbol table. +Without +.I =def +the symbol becomes defined as "1". +.TP 5 +.B \-Iincludedir +Include directory. +This option tells +.B makedepend +to prepend +.I includedir +to its list of directories to search when it encounters +a +.I #include +directive. +By default, +.B makedepend +only searches the standard include directories (usually /usr/include +and possibly a compiler-dependent directory). +.TP 5 +.B \-Yincludedir +Replace all of the standard include directories with the single specified +include directory; you can omit the +.I includedir +to simply prevent searching the standard include directories. +.TP 5 +.B \-a +Append the dependencies to the end of the file instead of replacing them. +.TP 5 +.B \-fmakefile +Filename. +This allows you to specify an alternate makefile in which +.B makedepend +can place its output. +.TP 5 +.B \-oobjsuffix +Object file suffix. +Some systems may have object files whose suffix is something other +than ".o". +This option allows you to specify another suffix, such as +".b" with +.I -o.b +or ":obj" +with +.I -o:obj +and so forth. +.TP 5 +.B \-pobjprefix +Object file prefix. +The prefix is prepended to the name of the object file. This is +usually used to designate a different directory for the object file. +The default is the empty string. +.TP 5 +.B \-sstring +Starting string delimiter. +This option permits you to specify +a different string for +.B makedepend +to look for in the makefile. +.TP 5 +.B \-wwidth +Line width. +Normally, +.B makedepend +will ensure that every output line that it writes will be no wider than +78 characters for the sake of readability. +This option enables you to change this width. +.TP 5 +.B \-v +Verbose operation. +This option causes +.B makedepend +to emit the list of files included by each input file on standard output. +.TP 5 +.B \-m +Warn about multiple inclusion. +This option causes +.B makedepend +to produce a warning if any input file includes another file more than +once. In previous versions of +.B makedepend +this was the default behavior; the default has been changed to better +match the behavior of the C compiler, which does not consider multiple +inclusion to be an error. This option is provided for backward +compatibility, and to aid in debugging problems related to multiple +inclusion. +.TP 5 +.B "\-\^\- options \-\^\-" +If +.B makedepend +encounters a double hyphen (\-\^\-) in the argument list, +then any unrecognized argument following it +will be silently ignored; a second double hyphen terminates this +special treatment. +In this way, +.B makedepend +can be made to safely ignore esoteric compiler arguments that might +normally be found in a CFLAGS +.B make +macro (see the +.B EXAMPLE +section above). +All options that +.B makedepend +recognizes and appear between the pair of double hyphens +are processed normally. +.SH ALGORITHM +The approach used in this program enables it to run an order of magnitude +faster than any other "dependency generator" I have ever seen. +Central to this performance are two assumptions: +that all files compiled by a single +makefile will be compiled with roughly the same +.I -I +and +.I -D +options; +and that most files in a single directory will include largely the +same files. +.PP +Given these assumptions, +.B makedepend +expects to be called once for each makefile, with +all source files that are maintained by the +makefile appearing on the command line. +It parses each source and include +file exactly once, maintaining an internal symbol table +for each. +Thus, the first file on the command line will take an amount of time +proportional to the amount of time that a normal C preprocessor takes. +But on subsequent files, if it encounter's an include file +that it has already parsed, it does not parse it again. +.PP +For example, +imagine you are compiling two files, +.I file1.c +and +.I file2.c, +they each include the header file +.I header.h, +and the file +.I header.h +in turn includes the files +.I def1.h +and +.I def2.h. +When you run the command +.sp + makedepend\0file1.c\0file2.c +.sp +.B makedepend +will parse +.I file1.c +and consequently, +.I header.h +and then +.I def1.h +and +.I def2.h. +It then decides that the dependencies for this file are +.sp + file1.o:\0header.h\0def1.h\0def2.h +.sp +But when the program parses +.I file2.c +and discovers that it, too, includes +.I header.h, +it does not parse the file, +but simply adds +.I header.h, +.I def1.h +and +.I def2.h +to the list of dependencies for +.I file2.o. +.SH "SEE ALSO" +cc(1), make(1) +.SH BUGS +.B makedepend +parses, but does not currently evaluate, the SVR4 +#predicate(token-list) preprocessor expression; +such expressions are simply assumed to be true. +This may cause the wrong +.I #include +directives to be evaluated. +.PP +Imagine you are parsing two files, +say +.I file1.c +and +.I file2.c, +each includes the file +.I def.h. +The list of files that +.I def.h +includes might truly be different when +.I def.h +is included by +.I file1.c +than when it is included by +.I file2.c. +But once +.B makedepend +arrives at a list of dependencies for a file, +it is cast in concrete. +.SH AUTHOR +Todd Brunhoff, Tektronix, Inc. and MIT Project Athena diff --git a/core/tools/dependency_tool/makedepend.txt b/core/tools/dependency_tool/makedepend.txt new file mode 100644 index 00000000..e8a90f76 --- /dev/null +++ b/core/tools/dependency_tool/makedepend.txt @@ -0,0 +1,264 @@ + + + +User Commands MAKEDEPEND(1) + + + +NNAAMMEE + makedepend - create dependencies in makefiles + +SSYYNNOOPPSSIISS + mmaakkeeddeeppeenndd [ --DDnnaammee==ddeeff ] [ --DDnnaammee ] [ --IIiinncclluuddeeddiirr ] [ + --YYiinncclluuddeeddiirr ] [ --aa ] [ --ffmmaakkeeffiillee ] [ --oooobbjjssuuffffiixx ] [ --ppoobb-- + jjpprreeffiixx ] [ --ssssttrriinngg ] [ --wwwwiiddtthh ] [ --vv ] [ --mm ] [ -- ootthh-- + eerrooppttiioonnss -- ] sourcefile ... + +DDEESSCCRRIIPPTTIIOONN + MMaakkeeddeeppeenndd reads each _s_o_u_r_c_e_f_i_l_e in sequence and parses it + like a C-preprocessor, processing all _#_i_n_c_l_u_d_e_, _#_d_e_f_i_n_e_, + _#_u_n_d_e_f_, _#_i_f_d_e_f_, _#_i_f_n_d_e_f_, _#_e_n_d_i_f_, _#_i_f and _#_e_l_s_e directives so + that it can correctly tell which _#_i_n_c_l_u_d_e_, directives would + be used in a compilation. Any _#_i_n_c_l_u_d_e_, directives can ref- + erence files having other _#_i_n_c_l_u_d_e directives, and parsing + will occur in these files as well. + + Every file that a _s_o_u_r_c_e_f_i_l_e includes, directly or indi- + rectly, is what mmaakkeeddeeppeenndd calls a "dependency". These + dependencies are then written to a _m_a_k_e_f_i_l_e in such a way + that mmaakkee((11)) will know which object files must be recompiled + when a dependency has changed. + + By default, mmaakkeeddeeppeenndd places its output in the file named + _m_a_k_e_f_i_l_e if it exists, otherwise _M_a_k_e_f_i_l_e_. An alternate + makefile may be specified with the --ff option. It first + searches the makefile for the line + + # DO NOT DELETE THIS LINE -- make depend depends on it. + + or one provided with the --ss option, as a delimiter for the + dependency output. If it finds it, it will delete every- + thing following this to the end of the makefile and put the + output after this line. If it doesn't find it, the program + will append the string to the end of the makefile and place + the output following that. For each _s_o_u_r_c_e_f_i_l_e appearing on + the command line, mmaakkeeddeeppeenndd puts lines in the makefile of + the form + + sourcefile.o: dfile ... + + Where "sourcefile.o" is the name from the command line with + its suffix replaced with ".o", and "dfile" is a dependency + discovered in a _#_i_n_c_l_u_d_e directive while parsing _s_o_u_r_c_e_f_i_l_e + or one of the files it included. + +EEXXAAMMPPLLEE + Normally, mmaakkeeddeeppeenndd will be used in a makefile target so + that typing "make depend" will bring the dependencies up to + date for the makefile. For example, + SRCS = file1.c file2.c ... + + + +X Version 11 Last change: Release 6 1 + + + + + + +User Commands MAKEDEPEND(1) + + + + CFLAGS = -O -DHACK -I../foobar -xyz + depend: + makedepend -- $(CFLAGS) -- $(SRCS) + +OOPPTTIIOONNSS + MMaakkeeddeeppeenndd will ignore any option that it does not under- + stand so that you may use the same arguments that you would + for cccc((11)).. + + --DDnnaammee==ddeeff oorr --DDnnaammee + Define. This places a definition for _n_a_m_e in mmaakkeeddee-- + ppeenndd''ss symbol table. Without _=_d_e_f the symbol becomes + defined as "1". + + --IIiinncclluuddeeddiirr + Include directory. This option tells mmaakkeeddeeppeenndd to + prepend _i_n_c_l_u_d_e_d_i_r to its list of directories to search + when it encounters a _#_i_n_c_l_u_d_e directive. By default, + mmaakkeeddeeppeenndd only searches the standard include directo- + ries (usually /usr/include and possibly a compiler- + dependent directory). + + --YYiinncclluuddeeddiirr + Replace all of the standard include directories with + the single specified include directory; you can omit + the _i_n_c_l_u_d_e_d_i_r to simply prevent searching the standard + include directories. + + --aa Append the dependencies to the end of the file instead + of replacing them. + + --ffmmaakkeeffiillee + Filename. This allows you to specify an alternate + makefile in which mmaakkeeddeeppeenndd can place its output. + + --oooobbjjssuuffffiixx + Object file suffix. Some systems may have object files + whose suffix is something other than ".o". This option + allows you to specify another suffix, such as ".b" with + _-_o_._b or ":obj" with _-_o_:_o_b_j and so forth. + + --ppoobbjjpprreeffiixx + Object file prefix. The prefix is prepended to the + name of the object file. This is usually used to desig- + nate a different directory for the object file. The + default is the empty string. + + --ssssttrriinngg + Starting string delimiter. This option permits you to + specify a different string for mmaakkeeddeeppeenndd to look for + in the makefile. + + + + +X Version 11 Last change: Release 6 2 + + + + + + +User Commands MAKEDEPEND(1) + + + + --wwwwiiddtthh + Line width. Normally, mmaakkeeddeeppeenndd will ensure that + every output line that it writes will be no wider than + 78 characters for the sake of readability. This option + enables you to change this width. + + --vv Verbose operation. This option causes mmaakkeeddeeppeenndd to + emit the list of files included by each input file on + standard output. + + --mm Warn about multiple inclusion. This option causes + mmaakkeeddeeppeenndd to produce a warning if any input file + includes another file more than once. In previous ver- + sions of mmaakkeeddeeppeenndd this was the default behavior; the + default has been changed to better match the behavior + of the C compiler, which does not consider multiple + inclusion to be an error. This option is provided for + backward compatibility, and to aid in debugging prob- + lems related to multiple inclusion. + + ---- ooppttiioonnss ---- + If mmaakkeeddeeppeenndd encounters a double hyphen (--) in the + argument list, then any unrecognized argument following + it will be silently ignored; a second double hyphen + terminates this special treatment. In this way, + mmaakkeeddeeppeenndd can be made to safely ignore esoteric com- + piler arguments that might normally be found in a + CFLAGS mmaakkee macro (see the EEXXAAMMPPLLEE section above). All + options that mmaakkeeddeeppeenndd recognizes and appear between + the pair of double hyphens are processed normally. + +AALLGGOORRIITTHHMM + The approach used in this program enables it to run an order + of magnitude faster than any other "dependency generator" I + have ever seen. Central to this performance are two assump- + tions: that all files compiled by a single makefile will be + compiled with roughly the same _-_I and _-_D options; and that + most files in a single directory will include largely the + same files. + + Given these assumptions, mmaakkeeddeeppeenndd expects to be called + once for each makefile, with all source files that are main- + tained by the makefile appearing on the command line. It + parses each source and include file exactly once, maintain- + ing an internal symbol table for each. Thus, the first file + on the command line will take an amount of time proportional + to the amount of time that a normal C preprocessor takes. + But on subsequent files, if it encounter's an include file + that it has already parsed, it does not parse it again. + + For example, imagine you are compiling two files, _f_i_l_e_1_._c + and _f_i_l_e_2_._c_, they each include the header file _h_e_a_d_e_r_._h_, and + + + +X Version 11 Last change: Release 6 3 + + + + + + +User Commands MAKEDEPEND(1) + + + + the file _h_e_a_d_e_r_._h in turn includes the files _d_e_f_1_._h and + _d_e_f_2_._h_. When you run the command + + makedepend file1.c file2.c + + mmaakkeeddeeppeenndd will parse _f_i_l_e_1_._c and consequently, _h_e_a_d_e_r_._h and + then _d_e_f_1_._h and _d_e_f_2_._h_. It then decides that the dependen- + cies for this file are + + file1.o: header.h def1.h def2.h + + But when the program parses _f_i_l_e_2_._c and discovers that it, + too, includes _h_e_a_d_e_r_._h_, it does not parse the file, but sim- + ply adds _h_e_a_d_e_r_._h_, _d_e_f_1_._h and _d_e_f_2_._h to the list of depen- + dencies for _f_i_l_e_2_._o_. + +SSEEEE AALLSSOO + cc(1), make(1) + +BBUUGGSS + mmaakkeeddeeppeenndd parses, but does not currently evaluate, the SVR4 + #predicate(token-list) preprocessor expression; such expres- + sions are simply assumed to be true. This may cause the + wrong _#_i_n_c_l_u_d_e directives to be evaluated. + + Imagine you are parsing two files, say _f_i_l_e_1_._c and _f_i_l_e_2_._c_, + each includes the file _d_e_f_._h_. The list of files that _d_e_f_._h + includes might truly be different when _d_e_f_._h is included by + _f_i_l_e_1_._c than when it is included by _f_i_l_e_2_._c_. But once + mmaakkeeddeeppeenndd arrives at a list of dependencies for a file, it + is cast in concrete. + +AAUUTTHHOORR + Todd Brunhoff, Tektronix, Inc. and MIT Project Athena + + + + + + + + + + + + + + + + + + + + + +X Version 11 Last change: Release 6 4 + + + diff --git a/core/tools/dependency_tool/makefile b/core/tools/dependency_tool/makefile new file mode 100644 index 00000000..14967053 --- /dev/null +++ b/core/tools/dependency_tool/makefile @@ -0,0 +1,17 @@ +CONSOLE_MODE = true + +include cpp/variables.def + +STRICT_WARNINGS = + +PROJECT = dependency_tool +TYPE = application +SOURCE = cppsetup.cpp ifparser.cpp include.cpp parse.cpp pr.cpp +#DEFINITIONS += __BUILD_STATIC_APPLICATION__ +ifeq "$(OP_SYSTEM)" "WIN32" + SOURCE += makedep_version.rc +endif +TARGETS = makedep.exe + +include cpp/rules.def + diff --git a/core/tools/dependency_tool/parse.cpp b/core/tools/dependency_tool/parse.cpp new file mode 100644 index 00000000..6bb46237 --- /dev/null +++ b/core/tools/dependency_tool/parse.cpp @@ -0,0 +1,554 @@ +/* $XConsortium: parse.c,v 1.30 94/04/17 20:10:38 gildea Exp $ */ +/* + +Copyright (c) 1993, 1994 X Consortium + +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 +X CONSORTIUM 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. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + +*/ + +#ifdef __WIN32__ + #pragma warning(disable : 4996) +#endif + +#include "def.h" + +#include + +extern const char *directives[]; +extern inclist maininclist; + +int find_includes(struct filepointer *filep, inclist *file, inclist *file_red, int recursion, bool failOK) +{ + register char *line; + register int type; + bool recfailOK; + + while ((line = getline(filep))) { + switch(type = deftype(line, filep, file_red, file, true)) { + case IF: + doif: + type = find_includes(filep, file, + file_red, recursion+1, failOK); + while ((type == ELIF) || (type == ELIFFALSE) || + (type == ELIFGUESSFALSE)) + type = gobble(filep, file, file_red); + if (type == ELSE) + gobble(filep, file, file_red); + break; + case IFFALSE: + case IFGUESSFALSE: + doiffalse: + if (type == IFGUESSFALSE || type == ELIFGUESSFALSE) + recfailOK = true; + else + recfailOK = failOK; + type = gobble(filep, file, file_red); + if (type == ELSE) + find_includes(filep, file, + file_red, recursion+1, recfailOK); + else + if (type == ELIF) + goto doif; + else + if ((type == ELIFFALSE) || (type == ELIFGUESSFALSE)) + goto doiffalse; + break; + case IFDEF: + case IFNDEF: + if ((type == IFDEF && isdefined(line, file_red, NULL)) + || (type == IFNDEF && !isdefined(line, file_red, NULL))) { + debug(1,(type == IFNDEF ? + "line %d: %s !def'd in %s via %s%s\n" : "", + filep->f_line, line, + file->i_file, file_red->i_file, ": doit")); + type = find_includes(filep, file, + file_red, recursion+1, failOK); + while (type == ELIF || type == ELIFFALSE || type == ELIFGUESSFALSE) + type = gobble(filep, file, file_red); + if (type == ELSE) + gobble(filep, file, file_red); + } + else { + debug(1,(type == IFDEF ? + "line %d: %s !def'd in %s via %s%s\n" : "", + filep->f_line, line, + file->i_file, file_red->i_file, ": gobble")); + type = gobble(filep, file, file_red); + if (type == ELSE) + find_includes(filep, file, + file_red, recursion+1, failOK); + else if (type == ELIF) + goto doif; + else if (type == ELIFFALSE || type == ELIFGUESSFALSE) + goto doiffalse; + } + break; + case ELSE: + case ELIFFALSE: + case ELIFGUESSFALSE: + case ELIF: + if (!recursion) + gobble(filep, file, file_red); + case ENDIF: + if (recursion) + return(type); + case DEFINE: + define(line, file); + break; + case UNDEF: + if (!*line) { + warning("%s, line %d: incomplete undef == \"%s\"\n", + file_red->i_file, filep->f_line, line); + break; + } + undefine(line, file_red); + break; + case INCLUDE: + add_include(filep, file, file_red, line, false, failOK); + break; + case INCLUDEDOT: + add_include(filep, file, file_red, line, true, failOK); + break; + case ERROR: + warning("%s: %d: %s\n", file_red->i_file, + filep->f_line, line); + break; + + case PRAGMA: + case IDENT: + case SCCS: + case EJECT: + case IMPORT: + break; + case -1: + warning("%s", file_red->i_file); + if (file_red != file) + warning1(" (reading %s)", file->i_file); + warning1(", line %d: unknown directive == \"%s\"\n", + filep->f_line, line); + break; + case -2: + warning("%s", file_red->i_file); + if (file_red != file) + warning1(" (reading %s)", file->i_file); + warning1(", line %d: incomplete include == \"%s\"\n", + filep->f_line, line); + break; + } + } + return(-1); +} + +int gobble(register struct filepointer *filep, inclist *file, + inclist *file_red) +{ + register char *line; + register int type; + + while ((line = getline(filep))) { + switch(type = deftype(line, filep, file_red, file, false)) { + case IF: + case IFFALSE: + case IFGUESSFALSE: + case IFDEF: + case IFNDEF: + type = gobble(filep, file, file_red); + while ((type == ELIF) || (type == ELIFFALSE) || + (type == ELIFGUESSFALSE)) + type = gobble(filep, file, file_red); + if (type == ELSE) + (void)gobble(filep, file, file_red); + break; + case ELSE: + case ENDIF: + debug(0,("%s, line %d: #%s\n", + file->i_file, filep->f_line, + directives[type])); + return(type); + case DEFINE: + case UNDEF: + case INCLUDE: + case INCLUDEDOT: + case PRAGMA: + case ERROR: + case IDENT: + case SCCS: + case EJECT: + case IMPORT: + break; + case ELIF: + case ELIFFALSE: + case ELIFGUESSFALSE: + return(type); + case -1: + warning("%s, line %d: unknown directive == \"%s\"\n", + file_red->i_file, filep->f_line, line); + break; + } + } + return(-1); +} + +/* + * Decide what type of # directive this line is. + */ +int deftype(register char *line, register struct filepointer *filep, + register inclist *file_red, register inclist *file, + int parse_it) +{ + register char *p; + char *directive, savechar; + register int ret; + + /* + * Parse the directive... + */ + directive=line+1; + while (*directive == ' ' || *directive == '\t') + directive++; + + p = directive; + while (*p >= 'a' && *p <= 'z') + p++; + savechar = *p; + *p = '\0'; + ret = match(directive, directives); + *p = savechar; + + /* If we don't recognize this compiler directive or we happen to just + * be gobbling up text while waiting for an #endif or #elif or #else + * in the case of an #elif we must check the zero_value and return an + * ELIF or an ELIFFALSE. + */ + + if (ret == ELIF && !parse_it) + { + while (*p == ' ' || *p == '\t') + p++; + /* + * parse an expression. + */ + debug(0,("%s, line %d: #elif %s ", + file->i_file, filep->f_line, p)); + ret = zero_value(p, filep, file_red); + if (ret != IF) + { + debug(0,("false...\n")); + if (ret == IFFALSE) + return(ELIFFALSE); + else + return(ELIFGUESSFALSE); + } + else + { + debug(0,("true...\n")); + return(ELIF); + } + } + + if (ret < 0 || ! parse_it) + return(ret); + + /* + * now decide how to parse the directive, and do it. + */ + while (*p == ' ' || *p == '\t') + p++; + switch (ret) { + case IF: + /* + * parse an expression. + */ + ret = zero_value(p, filep, file_red); + debug(0,("%s, line %d: %s #if %s\n", + file->i_file, filep->f_line, ret?"false":"true", p)); + break; + case IFDEF: + case IFNDEF: + debug(0,("%s, line %d: #%s %s\n", + file->i_file, filep->f_line, directives[ret], p)); + case UNDEF: + /* + * separate the name of a single symbol. + */ + while (isalnum(*p) || *p == '_') + *line++ = *p++; + *line = '\0'; + break; + case INCLUDE: + debug(2,("%s, line %d: #include %s\n", + file->i_file, filep->f_line, p)); + + /* Support ANSI macro substitution */ + { + struct symtab *sym = isdefined(p, file_red, NULL); + while (sym) { + p = sym->s_value; + debug(3,("%s : #includes SYMBOL %s = %s\n", + file->i_incstring, + sym -> s_name, + sym -> s_value)); + /* mark file as having included a 'soft include' */ + file->i_included_sym = true; + sym = isdefined(p, file_red, NULL); + } + } + + /* + * Separate the name of the include file. + */ + while (*p && *p != '"' && *p != '<') + p++; + if (! *p) + return(-2); + if (*p++ == '"') { + ret = INCLUDEDOT; + while (*p && *p != '"') + *line++ = *p++; + } else + while (*p && *p != '>') + *line++ = *p++; + *line = '\0'; + break; + case DEFINE: + /* + * copy the definition back to the beginning of the line. + */ + strcpy (line, p); + break; + case ELSE: + case ENDIF: + case ELIF: + case PRAGMA: + case ERROR: + case IDENT: + case SCCS: + case EJECT: + case IMPORT: + debug(0,("%s, line %d: #%s\n", + file->i_file, filep->f_line, directives[ret])); + /* + * nothing to do. + */ + break; + } + return(ret); +} + +symtab *isdefined(register char *symbol, inclist *file, + inclist **srcfile) +{ + register struct symtab *val; + + if ((val = slookup(symbol, &maininclist))) { + debug(1,("%s defined on command line\n", symbol)); + if (srcfile != NULL) *srcfile = &maininclist; + return(val); + } + if ((val = fdefined(symbol, file, srcfile))) + return(val); + debug(1,("%s not defined in %s\n", symbol, file->i_file)); + return(NULL); +} + +struct symtab *fdefined(register char *symbol, inclist *file, inclist **srcfile) +{ + register inclist **ip; + register struct symtab *val; + register int i; + static int recurse_lvl = 0; + + if (file->i_defchecked) + return(NULL); + file->i_defchecked = true; + if ((val = slookup(symbol, file))) + debug(1,("%s defined in %s as %s\n", symbol, file->i_file, val->s_value)); + if (val == NULL && file->i_list) + { + for (ip = file->i_list, i=0; i < file->i_listlen; i++, ip++) + if ((val = fdefined(symbol, *ip, srcfile))) { + break; + } + } + else if (val != NULL && srcfile != NULL) *srcfile = file; + recurse_lvl--; + file->i_defchecked = false; + + return(val); +} + +/* + * Return type based on if the #if expression evaluates to 0 + */ +int zero_value(register char *exp, register struct filepointer *filep, + register inclist *file_red) +{ + if (cppsetup(exp, filep, file_red)) + return(IFFALSE); + else + return(IF); +} + +void define(char *def, inclist *file) +{ + char *val; + + /* Separate symbol name and its value */ + val = def; + while (isalnum(*val) || *val == '_') + val++; + if (*val) + *val++ = '\0'; + while (*val == ' ' || *val == '\t') + val++; + + if (!*val) + val = (char *)"1"; + define2(def, val, file); +} + +void define2(char *name, char *val, inclist *file) +{ + int first, last, below; + register struct symtab *sp = NULL, *dest; + + /* Make space if it's needed */ + if (file->i_defs == NULL) + { + file->i_defs = (struct symtab *) + malloc(sizeof (struct symtab) * SYMTABINC); + file->i_deflen = SYMTABINC; + file->i_ndefs = 0; + } + else if (file->i_ndefs == file->i_deflen) + file->i_defs = (struct symtab *) + realloc(file->i_defs, + sizeof(struct symtab)*(file->i_deflen+=SYMTABINC)); + + if (file->i_defs == NULL) + fatalerr("malloc()/realloc() failure in insert_defn()\n"); + + below = first = 0; + last = file->i_ndefs - 1; + while (last >= first) + { + /* Fast inline binary search */ + register char *s1; + register char *s2; + register int middle = (first + last) / 2; + + /* Fast inline strchr() */ + s1 = name; + s2 = file->i_defs[middle].s_name; + while (*s1++ == *s2++) + if (s2[-1] == '\0') break; + + /* If exact match, set sp and break */ + if (*--s1 == *--s2) + { + sp = file->i_defs + middle; + break; + } + + /* If name > i_defs[middle] ... */ + if (*s1 > *s2) + { + below = first; + first = middle + 1; + } + /* else ... */ + else + { + below = last = middle - 1; + } + } + + /* Search is done. If we found an exact match to the symbol name, + just replace its s_value */ + if (sp != NULL) + { + free(sp->s_value); + sp->s_value = copy(val); + return; + } + + sp = file->i_defs + file->i_ndefs++; + dest = file->i_defs + below + 1; + while (sp > dest) + { + *sp = sp[-1]; + sp--; + } + sp->s_name = copy(name); + sp->s_value = copy(val); +} + +struct symtab *slookup(register char *symbol, register inclist *file) +{ + register int first = 0; + register int last = file->i_ndefs - 1; + + if (file) while (last >= first) + { + /* Fast inline binary search */ + register char *s1; + register char *s2; + register int middle = (first + last) / 2; + + /* Fast inline strchr() */ + s1 = symbol; + s2 = file->i_defs[middle].s_name; + while (*s1++ == *s2++) + if (s2[-1] == '\0') break; + + /* If exact match, we're done */ + if (*--s1 == *--s2) + { + return file->i_defs + middle; + } + + /* If symbol > i_defs[middle] ... */ + if (*s1 > *s2) + { + first = middle + 1; + } + /* else ... */ + else + { + last = middle - 1; + } + } + return(NULL); +} + +void undefine(char *symbol, register inclist *file) +{ + register struct symtab *ptr; + inclist *srcfile; + while ((ptr = isdefined(symbol, file, &srcfile)) != NULL) + { + srcfile->i_ndefs--; + for (; ptr < srcfile->i_defs + srcfile->i_ndefs; ptr++) + *ptr = ptr[1]; + } +} diff --git a/core/tools/dependency_tool/pr.cpp b/core/tools/dependency_tool/pr.cpp new file mode 100644 index 00000000..e1bdf7b3 --- /dev/null +++ b/core/tools/dependency_tool/pr.cpp @@ -0,0 +1,134 @@ +/* + +Copyright (c) 1993, 1994 X Consortium + +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 +X CONSORTIUM 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. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + +*/ + +#ifdef __WIN32__ + #pragma warning(disable : 4996) +#endif + +#include "def.h" + +#include + +extern struct inclist inc_list[ MAXFILES ], *inclistp; +extern char *objprefix; +extern char *objsuffix; +extern int width; +extern bool printed; +extern bool verbose; +extern bool show_where_not; + +void add_include(filepointer *filep, inclist *file, + inclist *file_red, char *include, bool dot, bool failOK) +{ + register struct inclist *newfile; + register struct filepointer *content; + + /* + * First decide what the pathname of this include file really is. + */ + newfile = inc_path(file->i_file, include, dot, failOK); + if (newfile == NULL) { + if (failOK) + return; + if (file != file_red) + warning("%s (reading %s, line %d): ", + file_red->i_file, file->i_file, filep->f_line); + else + warning("%s, line %d: ", file->i_file, filep->f_line); + warning1("cannot find include file \"%s\"\n", include); +fatalerr("cannot find include file \"%s\"\n", include); + show_where_not = true; + newfile = inc_path(file->i_file, include, dot, failOK); + show_where_not = false; + } + + if (newfile) { + included_by(file, newfile); + if (!newfile->i_searched) { + newfile->i_searched = true; + content = getfile(newfile->i_file); + find_includes(content, newfile, file_red, 0, failOK); + freefile(content); + } + } +} + +void recursive_pr_include(register struct inclist *head, register char *file, + register char *base) +{ + register int i; + + if (head->i_marked) + return; + head->i_marked = true; + if (head->i_file != file) { + bool rc_file = false; + if ((strlen(file) >= 3) && !strcmp(file + strlen(file) - 3, ".rc")) + rc_file = true; + pr(head, file, base, rc_file); + } + for (i=0; ii_listlen; i++) + recursive_pr_include(head->i_list[ i ], file, base); +} + +void pr(register struct inclist *ip, char *file, char *base, bool rc_file) +{ + static char *lastfile; + static int current_len; + register int len, i; + char buf[ BUFSIZ ]; + + printed = true; + len = int(strlen(ip->i_file)+1); + if (current_len + len > width || file != lastfile) { + lastfile = file; + char *temp_suff = objsuffix; + if (rc_file) temp_suff = (char *)".res"; + sprintf(buf, "\n%s%s%s: %s ", objprefix, base, temp_suff, ip->i_file); + len = current_len = int(strlen(buf)); + } + else { + strcpy(buf, ip->i_file); +//printf("ip->file=%s\n", ip->i_file); + char tmp[2] = { ' ', '\0' }; +//printf("tmp=%s\n", tmp); + strcat(buf, tmp); +//printf("buf=%s\n", buf); + current_len += len; + } + fwrite(buf, len, 1, stdout); + + // If verbose is set, then print out what this file includes. + if (! verbose || ip->i_list == NULL || ip->i_notified) + return; + ip->i_notified = true; + lastfile = NULL; + printf("\n# %s includes:", ip->i_file); + for (i=0; ii_listlen; i++) + printf("\n#\t%s", ip->i_list[ i ]->i_incstring); +} + diff --git a/core/tools/dependency_tool/readme.txt b/core/tools/dependency_tool/readme.txt new file mode 100644 index 00000000..e9f660e2 --- /dev/null +++ b/core/tools/dependency_tool/readme.txt @@ -0,0 +1,55 @@ + +this port of makedepend is now called makedep, since it's a shorter name, +and therefore better...? or at least shorter. +the code has been ported to visual c++ 5.x-7.x as well as remaining compatible +with unix and linux. it has been made to comply with c++ prototype rules. +also, support for excluding directories from dependency checking has been +added (with a -X flag that takes the directory name). + +Chris Koeritz +fred@gruntose.com +(original 3/4/1999) +(updated 9/7/2000) +(updated 3/18/2004) + +============================================================================ + +makedepend +---------- + +This is a quick and rude port of the X11 R6 makedepend to OS/2 using icc. + +I have taken the code from FreeBSD 2.0.5, which I happened to have handy. + +I have added a feature I wanted: the switch -iENVIRONMENTVARIABLE will add +all semicolon delimited directories in ENVIRONMENTVARIABLE to the list of +include directories. + +One obvious use is: makedepend -i INCLUDE a.c + +If you do not want the system header files in you dependencies, you might use: + +set MYINCLUDE=\mytree\include;\mytree\subproj\include + +makedepend -i MYINCLUDE a.c + +but + +makedepend -I \mytree\include -I \mytree\subproj\include a.c + +which is the 'normal' way of doing things, is also possible. + +Sources +------- + +Todd Brunhoff wrote this program. Thanks. + +To build makedepend, I use icc and GNU make. nmake will barf over the makefile. + +And yes, I use long filenames. If you don't like that, edit the makefile. + +I have compiled these sources under NT with VC++ 4.2 without any hassles (different +makefile, though). + +Lars Immisch +lars@ibp.de diff --git a/core/tools/dependency_tool/version.ini b/core/tools/dependency_tool/version.ini new file mode 100644 index 00000000..8444aa59 --- /dev/null +++ b/core/tools/dependency_tool/version.ini @@ -0,0 +1,5 @@ +[version] +description=Makefile Dependency Generator +root=makedep +name=Dependency_Maker +extension=exe diff --git a/core/tools/makefile b/core/tools/makefile new file mode 100644 index 00000000..f3781eeb --- /dev/null +++ b/core/tools/makefile @@ -0,0 +1,7 @@ +include variables.def + +PROJECT = tools_hierarchy +BUILD_BEFORE = clam_tools dependency_tool simple_utilities solution_solvers + +include rules.def + diff --git a/core/tools/simple_utilities/create_guid.cpp b/core/tools/simple_utilities/create_guid.cpp new file mode 100644 index 00000000..104bde72 --- /dev/null +++ b/core/tools/simple_utilities/create_guid.cpp @@ -0,0 +1,138 @@ +/*****************************************************************************\ +* * +* Name : create_guid * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* This program generates a globally unique identifier using the operating * +* system's support. The resulting id can be used to tag items that must be * +* uniquely named. * +* * +******************************************************************************* +* Copyright (c) 2006-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __WIN32__ + #include +#endif + +using namespace application; +using namespace basis; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace textual; + +#define BASE_LOG(to_print) program_wide_logger::get().log(to_print, ALWAYS_PRINT) + +// this is an example GUID in the DCE format: +// +// {12345678-1234-1234-1234-123456789012} +// +// each position can be a hexadecimal digit, ranging from 0 to F. +// the full size is measured as 32 nibbles or 16 bytes or 128 bits. + +class create_guid : public application_shell +{ +public: + create_guid() : application_shell() {} + DEFINE_CLASS_NAME("create_guid"); + int execute(); +}; + +int create_guid::execute() +{ +// FUNCDEF("execute"); + SETUP_CONSOLE_LOGGER; +#ifdef __UNIX__ + +// this is completely bogus for the time being. it just produces a random +// number rather than a guid. + #define add_random \ + faux_guid += astring(string_manipulation::hex_to_char \ + (randomizer().inclusive(0, 0xf)), 1) + + astring faux_guid("{"); + for (int i = 0; i < 8; i++) add_random; + faux_guid += "-"; + for (int j = 0; j < 3; j++) { + for (int i = 0; i < 4; i++) add_random; + faux_guid += "-"; + } + for (int i = 0; i < 8; i++) add_random; + faux_guid += "}"; + BASE_LOG(faux_guid.lower()); +#elif defined (__WIN32__) + GUID guid; + CoCreateGuid(&guid); + const int BUFFER_SIZE = 1024; + LPOLESTR wide_buffer = new WCHAR[BUFFER_SIZE + 4]; + StringFromGUID2(guid, wide_buffer, BUFFER_SIZE); + const int BYTE_BUFFER_SIZE = BUFFER_SIZE * 2 + 4; + char buffer[BYTE_BUFFER_SIZE]; + WideCharToMultiByte(CP_UTF8, 0, wide_buffer, -1, buffer, BYTE_BUFFER_SIZE, + NULL, NULL); + astring guid_text = buffer; + delete [] wide_buffer; + BASE_LOG(guid_text); +#else + #error unknown operating system; no support for guids. +#endif + + return 0; +} + +HOOPLE_MAIN(create_guid, ) + +#ifdef __BUILD_STATIC_APPLICATION__ + // static dependencies found by buildor_gen_deps.sh: + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif // __BUILD_STATIC_APPLICATION__ + diff --git a/core/tools/simple_utilities/makefile b/core/tools/simple_utilities/makefile new file mode 100644 index 00000000..ee683578 --- /dev/null +++ b/core/tools/simple_utilities/makefile @@ -0,0 +1,27 @@ +CONSOLE_MODE = t + +include cpp/variables.def + +PROJECT = simplistic_utils +TYPE = application +TARGETS = create_guid.exe playsound.exe short_path.exe sleep_ms.exe \ + zap_process.exe +ifeq "$(OMIT_VERSIONS)" "" + SOURCE += simple_utils_version.rc +endif +DEFINITIONS += __BUILD_STATIC_APPLICATION__ +UNDEFINITIONS += ENABLE_MEMORY_HOOK ENABLE_CALLSTACK_TRACKING +ifeq "$(OP_SYSTEM)" "WIN32" + # static C runtime support... +#hmmm: resurrect this as a particular build type, so makefiles don't need to know this... + COMPILER_FLAGS += -MT + LIBS_USED += netapi32.lib + ifeq "$(DEBUG)" "" + LIBS_USED += libcmt.lib + else + LIBS_USED += libcmtd.lib + endif +endif + +include cpp/rules.def + diff --git a/core/tools/simple_utilities/playsound.cpp b/core/tools/simple_utilities/playsound.cpp new file mode 100644 index 00000000..c1ddf4b2 --- /dev/null +++ b/core/tools/simple_utilities/playsound.cpp @@ -0,0 +1,73 @@ +/*****************************************************************************\ +* * +* Name : playsound * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* A program intended to be as simple as possible and to play only WAV * +* files on the win32 platform. It was decided that this was needed because * +* windows media player suddenly became political and started complaining * +* when other programs were registered to play different sound types. * +* All this does is play WAV files. That's it. * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include + +#ifdef __WIN32__ + #include +#endif + +using namespace basis; +using namespace loggers; +using namespace structures; + +///HOOPLE_STARTUP_CODE; +//hmmm: this needs to arise from obscurity too? + +int main(int argc, char *argv[]) +{ + console_logger out; + if (argc < 2) { + out.log(astring(argv[0]) + " usage:", ALWAYS_PRINT); + out.log(astring("This program takes one or more parameters which are interpreted"), ALWAYS_PRINT); + out.log(astring("as the names of sound files."), ALWAYS_PRINT); + return 12; + } + for (int i = 1; i < argc; i++) { +// out.log(astring(astring::SPRINTF, "soundfile %d: %s", i, argv[i])); +#ifdef __WIN32__ + if (!PlaySound(to_unicode_temp(argv[i]), NIL, SND_FILENAME)) + out.log(astring("failed to play ") + argv[i], ALWAYS_PRINT); +#else + out.log(astring("this program is a NO-OP, ignoring ") + argv[i], ALWAYS_PRINT); +#endif + } + return 0; +} + +#ifdef __BUILD_STATIC_APPLICATION__ + // static dependencies found by buildor_gen_deps.sh: + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif // __BUILD_STATIC_APPLICATION__ + diff --git a/core/tools/simple_utilities/short_path.cpp b/core/tools/simple_utilities/short_path.cpp new file mode 100644 index 00000000..b9582492 --- /dev/null +++ b/core/tools/simple_utilities/short_path.cpp @@ -0,0 +1,58 @@ +/*****************************************************************************\ +* * +* Name : short_path * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* This program converts a pathname to its 8.3 name. Only for windows. * +* * +******************************************************************************* +* Copyright (c) 2007-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +#include +#include +#ifdef __WIN32__ + #include +#endif + +using namespace basis; +using namespace structures; + +///HOOPLE_STARTUP_CODE; + +int main(int argc, char *argv[]) +{ + astring shorty('\0', 2048); + if (argc < 2) { + printf("This program needs a path to convert to its short form.\n"); + return 23; + } +#ifdef __WIN32__ + GetShortPathNameA(argv[1], shorty.s(), 2045); +#else + strcpy(shorty.s(), argv[1]); +#endif + shorty.replace_all('\\', '/'); + printf("%s", shorty.s()); + return 0; +} + +#ifdef __BUILD_STATIC_APPLICATION__ + // static dependencies found by buildor_gen_deps.sh: + #include + #include + #include + #include +#endif // __BUILD_STATIC_APPLICATION__ + diff --git a/core/tools/simple_utilities/simple_utils_version.rc b/core/tools/simple_utilities/simple_utils_version.rc new file mode 100644 index 00000000..bc11407b --- /dev/null +++ b/core/tools/simple_utilities/simple_utils_version.rc @@ -0,0 +1,46 @@ +#ifndef NO_VERSION +#include +#include <__build_version.h> +#include <__build_configuration.h> +#define BI_PLAT_WIN32 + // force 32 bit compile. +1 VERSIONINFO LOADONCALL MOVEABLE +FILEVERSION __build_FILE_VERSION_COMMAS +PRODUCTVERSION __build_PRODUCT_VERSION_COMMAS +FILEFLAGSMASK 0 +FILEFLAGS VS_FFI_FILEFLAGSMASK +#if defined(BI_PLAT_WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + // Language type = U.S. English(0x0409) and Character Set = Windows, Multilingual(0x04b0) + BLOCK "040904b0" // Matches VarFileInfo Translation hex value. + BEGIN + VALUE "CompanyName", __build_company "\000" +#ifndef _DEBUG + VALUE "FileDescription", "Simple Utility Collection\000" +#else + VALUE "FileDescription", "Simple Utility Collection (DEBUG)\000" +#endif + VALUE "FileVersion", __build_FILE_VERSION "\000" + VALUE "ProductVersion", __build_PRODUCT_VERSION "\000" + VALUE "InternalName", "Simple Utilities\000" + VALUE "LegalCopyright", __build_copyright "\000" + VALUE "LegalTrademarks", __build_legal_info "\000" + VALUE "OriginalFilename", "simple_utils.exe\000" + VALUE "ProductName", __build_product_name "\000" + + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04b0 // US English (0x0409) and win32 multilingual (0x04b0) + END +END +#endif diff --git a/core/tools/simple_utilities/sleep_ms.cpp b/core/tools/simple_utilities/sleep_ms.cpp new file mode 100644 index 00000000..3b3d87ed --- /dev/null +++ b/core/tools/simple_utilities/sleep_ms.cpp @@ -0,0 +1,77 @@ +/*****************************************************************************\ +* * +* Name : sleep_ms * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Takes a number from the command line and sleeps for that many milli- * +* seconds. * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include + +#include + +using namespace application; +using namespace basis; +using namespace timely; + +DEFINE_ARGC_AND_ARGV; +///DEFINE_INSTANCE_HANDLE; + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + printf("%s usage:\nThe first parameter is taken as the number of " + "milliseconds to sleep.\n", argv[0]); + return 1; + } + + int snooze_ms; + sscanf(argv[1], "%d", &snooze_ms); + time_control::sleep_ms(snooze_ms); + return 0; +} + +#ifdef __BUILD_STATIC_APPLICATION__ + // static dependencies found by buildor_gen_deps.sh: + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif // __BUILD_STATIC_APPLICATION__ + diff --git a/core/tools/simple_utilities/version.ini b/core/tools/simple_utilities/version.ini new file mode 100644 index 00000000..c1e1b1d8 --- /dev/null +++ b/core/tools/simple_utilities/version.ini @@ -0,0 +1,6 @@ +[version] +description = Simple Utility Collection +root = simple_utils +name = Simple Utilities +extension = exe + diff --git a/core/tools/simple_utilities/zap_process.cpp b/core/tools/simple_utilities/zap_process.cpp new file mode 100644 index 00000000..70473765 --- /dev/null +++ b/core/tools/simple_utilities/zap_process.cpp @@ -0,0 +1,123 @@ +/*****************************************************************************\ +* * +* Name : zap_process * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Whacks a process named on the command line, if possible. * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//HOOPLE_STARTUP_CODE; +//hmmm: missing + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace processes; +using namespace structures; + +int main(int argc, char *argv[]) +{ + console_logger out; + command_line cmds(argc, argv); + if ( (cmds.entries() < 1) + || (cmds.get(0).type() != command_parameter::VALUE) ) { + out.log(cmds.program_name().basename().raw() + " usage:\n" + "this takes a single parameter, which is the name of a program\n" + "to hunt down and eradicate. this will zap the program without\n" + "any warning or any chance for it to save its state.", ALWAYS_PRINT); + return 1; + } + astring program_name = cmds.get(0).text(); + program_name.to_lower(); + process_entry_array processes; + process_control proc_con; + if (!proc_con.query_processes(processes)) + non_continuable_error("procto", "main", "failed to query processes!"); + bool found = false; + bool success = true; + for (int i = 0; i < processes.length(); i++) { + filename path = processes[i].path(); +// out.log(astring("got process path: ") + path.raw(), ALWAYS_PRINT); + astring base = path.basename().raw().lower(); + if (base == program_name) { + found = true; +// out.log("would whack this entry:"); +// out.log(processes[i].text_form()); + bool ret = proc_con.zap_process(processes[i]._process_id); + if (ret) + out.log(a_sprintf("Killed process %d [", processes[i]._process_id) + + program_name + astring("]"), ALWAYS_PRINT); + else { + out.log(astring(astring::SPRINTF, "Failed to zap process %d [", + processes[i]._process_id) + program_name + astring("]"), ALWAYS_PRINT); + success = false; + } + } + } + if (!found) + out.log(astring("Could not find the program named ") + program_name, ALWAYS_PRINT); + if (!success) return 123; + else return 0; +} + +#ifdef __BUILD_STATIC_APPLICATION__ + // static dependencies found by buildor_gen_deps.sh: + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif // __BUILD_STATIC_APPLICATION__ + diff --git a/core/tools/solution_solvers/check_resource_ids.sh b/core/tools/solution_solvers/check_resource_ids.sh new file mode 100644 index 00000000..d4b9dab9 --- /dev/null +++ b/core/tools/solution_solvers/check_resource_ids.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +# finds all the resource ids in resource headers (only those named the +# canonical resource.h name) and discovers any duplicates. the duplicates +# are shown with their symbolic names and file locations. + +TEMP_RESOURCE_HEADERS=/tmp/resrc_headers.txt + +sp='[ ]' # space and tab. + +# find all the resource headers so we can look at their contents. +find "$BUILD_TOP" -type f -iname "resource.h" | \ + grep -vi 3rdparty | \ + grep -vi admin_items | \ + grep -v app >"$TEMP_RESOURCE_HEADERS" +#hmmm: above ignores *anything* with app in the name. +# grep -v app_src >"$TEMP_RESOURCE_HEADERS" + +FULLDEFS=/tmp/full_definition_list.txt +# clean up prior versions. +rm -f "$FULLDEFS" + +# iterate through all the resource headers we found. +while read line; do +#echo "file=$line" + # find any lines that define a resource id. remove any that are part of + # visual studio's tracking system for next id to assign (_APS_NEXT crud). + chop_line="$(echo $line | sed -e 's/[\\\/]/+/g')" + grep "^$sp*#define$sp*[_A-Za-z0-9][_A-Za-z0-9]*$sp*[0-9][0-9]*$sp*$" <"$line" | \ + grep -v "_APS_NEXT" | \ + grep -v "_APS_3D" | \ + grep -v "$sp*\/\/.*" | \ + sed -e "s/^$sp*#define$sp*\([_A-Za-z0-9][_A-Za-z0-9]*\)$sp*\([0-9][0-9]*\)$sp*$/\1=\2#$chop_line/" >>"$FULLDEFS" +done <"$TEMP_RESOURCE_HEADERS" + +# our accumulated lists of names and ids (in order per list). +declare -a resource_names=() +declare -a resource_ids=() +declare -a resource_files=() + +# iterate through the definitions list and compile the set of known ids. +while read line; do + name=$(echo $line | sed -e 's/\([^=]*\)=[^#]*#.*/\1/') + id=$(echo $line | sed -e 's/[^=]*=\([^#]*\)#.*/\1/') + file=$(echo $line | sed -e 's/[^=]*=[^#]*#\(.*\)/\1/') + +#echo got name $name +#echo got id $id +#echo got file $file + next_index=${#resource_names[*]} +#echo next ind is $next_index + resource_names[${next_index}]=$name + resource_ids[${next_index}]=$id + resource_files[${next_index}]=$id + +done <"$FULLDEFS" + +echo done reading all definitions. + +JUST_IDS=/tmp/ids_list.txt +rm -f "$JUST_IDS" + +i=0 +while [[ i -le $next_index ]]; do + echo ${resource_ids[$i]} >>"$JUST_IDS" + ((i++)) +done + +echo done accumulating list of integer ids. + +id_size=$(wc "$JUST_IDS") + +JUST_IDS_TEMP=/tmp/ids_list_temp.txt + +sort "$JUST_IDS" | uniq >"$JUST_IDS_TEMP" +id_temp_size=$(wc "$JUST_IDS_TEMP") +if [ "$id_size" == "$id_temp_size" ]; then + echo "Your IDs are all unique! Ending analysis." + exit 0 +fi + +echo "Your ids are *NOT* all unique; the repeated ones are:" +sort "$JUST_IDS" | uniq -d >"$JUST_IDS_TEMP" + +while read line; do + id="$line" + echo "=== identifier $id ===" + grep "=$line#" "$FULLDEFS" | sed -e 's/\+/\//g' | sed -e 's/#/\ +/' +done <"$JUST_IDS_TEMP" + + diff --git a/core/tools/solution_solvers/clean_vcxproj.sh b/core/tools/solution_solvers/clean_vcxproj.sh new file mode 100644 index 00000000..8f74ba4a --- /dev/null +++ b/core/tools/solution_solvers/clean_vcxproj.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# updates a vcxproj that had been converted from prior visual studio 2005 +# to the newer 2010. + +parms=($*) + +outdir="$HOME/fixed_proj" + +if [ ! -d "$outdir" ]; then mkdir -p $outdir; fi + +for i in ${parms[*]}; do + curr_parm="$i" + base=$(basename "$curr_parm") + echo fixing $base + + cat "$curr_parm" | + sed -e 's/v80<\/PlatformToolset>/v100<\/PlatformToolset>/g' | + sed -e 's/ide_files/build/g' | + sed -e 's/release_[de][lx][el]/release/g' | + sed -e 's/debug_[de][lx][el]/debug/g' | + sed -e 's///g' | + sed -e 's///g' | + sed -e 's///g' | + sed -e 's/v2.0<\/TargetFrameworkVersion>/v4.0<\/TargetFrameworkVersion>/g' | + sed -e 's/\.\.\\\.\.\\\.\.\\build/\.\.\\\.\.\\\.\.\\\.\.\\\.\.\\build/g' | + sed -e 's/\.\.\\\.\.\\lib_src/\.\.\\\.\.\\\.\.\\\.\.\\\.\.\\libraries/g' | + cat >"$outdir/$base" + +done + diff --git a/core/tools/solution_solvers/extract_projects.sh b/core/tools/solution_solvers/extract_projects.sh new file mode 100644 index 00000000..c36525c0 --- /dev/null +++ b/core/tools/solution_solvers/extract_projects.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# this is a simple script that finds the project files listed in a solution file. + +solution_name="$1"; shift +if [ -z "$solution_name" ]; then + echo This script needs a solution or project file name. It will locate all the + echo projects listed in that file. + exit 3 +fi + +grep -i proj "$solution_name" | sed -n -e 's/.*"\([^"]*proj\)".*/\1/p' | sed -e 's/.*[\\\/]\([^\\\/]*\)/\1/' | tr A-Z a-z | sort | uniq + diff --git a/core/tools/solution_solvers/find_multilisted_projects_in_solutions.sh b/core/tools/solution_solvers/find_multilisted_projects_in_solutions.sh new file mode 100644 index 00000000..75ee4a31 --- /dev/null +++ b/core/tools/solution_solvers/find_multilisted_projects_in_solutions.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# this script checks all the known solution files to ensure that no project file is +# listed more than once in the whole set. + +export errors_seen=0 + +# where we'll send our generated files. +export OUT_DIR="$TMP" + +export CORE_PROJ="$OUT_DIR/core_projects.txt" +export SHARED_PROJ="$OUT_DIR/shared_projects.txt" +export MIDDLEWARE_PROJ="$OUT_DIR/middleware_projects.txt" + +#hmmm: fix references! +# extract all the project names from the solution files. +bash $BUILD_TOP/build/tool_source/solution_solvers/extract_projects.sh "$BUILD_TOP/libraries/solutions/lightlink_core.sln" >"$CORE_PROJ" +bash $BUILD_TOP/build/tool_source/solution_solvers/extract_projects.sh "$BUILD_TOP/libraries/solutions/lightlink_shared.sln" >"$SHARED_PROJ" +bash $BUILD_TOP/build/tool_source/solution_solvers/extract_projects.sh "$BUILD_TOP/products/Middleware/middleware.sln" >"$MIDDLEWARE_PROJ" + +export TEMP_COMPARE_FILE="$OUT_DIR/commonlines.txt" + +# outer loop is all but the most dependent project. +for i in "$CORE_PROJ" "$SHARED_PROJ"; do + # inner loop is all but the most depended on project. + for j in "$SHARED_PROJ" "$MIDDLEWARE_PROJ"; do + if [ "$i" == "$j" ]; then continue; fi +#echo comparing $i and $j + comm -1 -2 "$i" "$j" >"$TEMP_COMPARE_FILE" + if [ -s "$TEMP_COMPARE_FILE" ]; then + echo "ERROR: the two files $(basename $i) and $(basename $j) share common projects." + ((errors_seen++)) + fi + done +done + +if [ $errors_seen -gt 0 ]; then + echo "ERROR: There were errors detected during the checks!" + exit 3 +else + echo "No problems were seen in any checks." +fi + diff --git a/core/tools/solution_solvers/find_output_pathers.sh b/core/tools/solution_solvers/find_output_pathers.sh new file mode 100644 index 00000000..204fd327 --- /dev/null +++ b/core/tools/solution_solvers/find_output_pathers.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# this script locates any project files that mention the output path setting (OutputPath). +# these are extra suspicious due to the problems caused for our solutions when files have the +# OutputPath specified rather than allowing the parent's settings to be inherited. + +# only works when repo dir is at the top of the full builds area. +# we need like a top dir of some sort. +find $REPOSITORY_DIR -iname "*proj" -exec grep -l OutputPath {} ';' >~/outputpath_mentioners.txt diff --git a/core/tools/solution_solvers/verify_project_file.sh b/core/tools/solution_solvers/verify_project_file.sh new file mode 100644 index 00000000..841bdf4d --- /dev/null +++ b/core/tools/solution_solvers/verify_project_file.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +# this script locates the solution that a project file belongs in and decides whether the +# project file contains any bad references to projects that are outside of its solution. + +proj_file="$1"; shift + +if [ -z "$proj_file" ]; then + echo This script needs one parameter that is a project file to be verified. + echo The file will be located in a solution file, and then checked for all project + echo references being in the same solution file. + exit 3 +fi + +############## + +# look for the solution that a project belongs to. +function find_solution_membership() +{ + local proj="$1"; shift + found_solution= + for i in ${SOLUTIONS[*]}; do + # secret sauce--don't match on any old reference to the file; we need + # it to be coming from the real project definition. + grep -i "$proj\"," "$i" &>/dev/null + if [ $? -eq 0 ]; then +#echo "$proj found in solution $i" + found_solution="$i" + break + fi + done +} + +function complain_about_project() +{ + filename="$1"; shift + echo "!!" + echo "Project $proj_base is in error (at $proj_file)" + echo "it references project $filename which is external to the solution." + echo "!!" + ((errors_seen++)) +} + +############## + +proj_base="$(basename $proj_file)" + +CHECKERS="$TMP/checking_refs.txt" + +errors_seen=0 + +#hmmm fix this for big time +export SOLUTIONS=("$BUILD_TOP/libraries/solutions/"*.sln "$BUILD_TOP/products/"*/*.sln) + +find_solution_membership "$proj_base" + +#echo found sol is $found_solution + +if [ -z "$found_solution" ]; then + echo error: could not find the solution containing $proj_base + exit 3 +fi + +# get all the project references from the project file being tested. +#hmmm: fix this path to extract! +bash "$BUILD_TOP/build/tool_source/solution_solvers/extract_projects.sh" "$proj_file" >"$CHECKERS" + +# iterate over all references in the project file. +while read line; do + grep -i "$line" "$found_solution" &>/dev/null + if [ $? -ne 0 ]; then + complain_about_project "$line" + fi +done <"$CHECKERS" + +if [ $errors_seen -eq 0 ]; then + echo "project $proj_base is clean." +else + echo "ERROR: there were $errors_seen problems in $proj_base; see above logging." + exit 3 +fi + + diff --git a/database/ansi/globe.ansi b/database/ansi/globe.ansi new file mode 100644 index 00000000..5efd8225 --- /dev/null +++ b/database/ansi/globe.ansi @@ -0,0 +1,690 @@ + _-o#&&*''''?d:>b\_ + _o/"`'' '',, dMF9MMMMMHo_ + .o&#' `"MbHMMMMMMMMMMMHo. + .o"" ' vodM*$&&HMMMMMMMMMM?. + ,' $M&ood,~'`(&##MMMMMMH\ + / ,MMMMMMM#b?#bobMMMMHMMML + & ?MMMMMMMMMMMMMMMMM7MMM$R*Hk + ?$. :MMMMMMMMMMMMMMMMMMM/HMMM|`*L +| |MMMMMMMMMMMMMMMMMMMMbMH' T, +$H#: `*MMMMMMMMMMMMMMMMMMMMb#}' `? +]MMH# ""*""""*#MMMMMMMMMMMMM' - +MMMMMb_ |MMMMMMMMMMMP' : +HMMMMMMMHo `MMMMMMMMMT . +?MMMMMMMMP 9MMMMMMMM} - +-?MMMMMMM |MMMMMMMMM?,d- ' + :|MMMMMM- `MMMMMMMT .M|. : + .9MMM[ &MMMMM*' `' . + :9MMk `MMM#" - + &M} ` .- + `&. . + `~, . ./ + . _ .- + '`--._,dd###pp=""' + _v->#H#P? "':o<>\_ + .,dP` `'' "'-o.+H6&MMMHo_ + oHMH9' `?&bHMHMMMMMMHo. + oMP"' ' ooMP*#&HMMMMMMM?. + ,M* - `*MSdob//`^&##MMMH\ + d*' .,MMMMMMH#o>#ooMMMMMb + HM- :HMMMMMMMMMMMMMMM&HM[R\ + d"Z\. 9MMMMMMMMMMMMMMMMM[HMM|: +-H - MMMMMMMMMMMMMMMMMMMbMP' : +:??Mb# `9MMMMMMMMMMMMMMMMMMH#! . +: MMMMH#, "*""""`#HMMMMMMMMMMH - +||MMMMMM6\. {MMMMMMMMMH' : +:|MMMMMMMMMMHo `9MMMMMMMM' . +. HMMMMMMMMMMP' !MMMMMMMM ` +- `#MMMMMMMMM HMMMMMMM*,/ : + : ?MMMMMMMF HMMMMMM',P' : + . HMMMMR' {MMMMP' ^' - + : `HMMMT iMMH' .' + -.`HMH . + -:*H . ' + -`\,, . .- + ' . _ .-` + '`~\.__,obb#q==~''' + .ovr:HMM#?:`' >b\_ + .,:&Hi' `' "' \\|&bSMHo_ + oHMMM#*} `?&dMMMMMMHo. + .dMMMH"'''' ,oHH*&&9MMMM?. + ,MMM*' `*M\bd<|"*&#MH\ + dHH?' :MMMMMM#bd#odMML + H' |\ `dMMMMMMMMMMMMMM9Mk + JL/"7+,. `MMMMMMMMMMMMMMMH9ML +-`Hp ' |MMMMMMMMMMMMMMMMHH|: +: \\#M#d? `HMMMMMMMMMMMMMMMMH. +. JMMMMM##, ``*""'"*#MMMMMMMMH +-. ,MMMMMMMM6o_ |MMMMMMMM': +: |MMMMMMMMMMMMMb\ TMMMMMMT : +. ?MMMMMMMMMMMMM' :MMMMMM|.` +- ?HMMMMMMMMMM: HMMMMMM\|: + : 9MMMMMMMMH' `MMMMMP.P. + . `MMMMMMT'' HMMM*''- + - TMMMMM' MM*' - + '. HMM# - + -. `9M: .' + -. `b,, . . ' + '-\ ., .-` + '-:b~\\_,oddq==--" + _oo##'9MMHb':'-,o_ + .oH":HH$' ""' "' -\7*R&o_ + .oHMMMHMH#9: "\bMMMMHo. + dMMMMMM*""'`' .oHM"H9MM?. + ,MMMMMM' "HLbd<|?&H\ + JMMH#H' |MMMMM#b>bHb + :MH ."\ `|MMMMMMMMMMMM& + .:M:d-"|:b.. 9MMMMMMMMMMMMM+ +: "*H| - &MMMMMMMMMMMMMH: +. `LvdHH#d? `?MMMMMMMMMMMMMb +: iMMMMMMH#b `"*"'"#HMMMMMM +. . ,MMMMMMMMMMb\. {MMMMMH +- |MMMMMMMMMMMMMMHb, `MMMMM| +: |MMMMMMMMMMMMMMH' &MMMM, +- `#MMMMMMMMMMMM |MMMM6- + : `MMMMMMMMMM+ ]MMMT/ + . `MMMMMMMP" HMM*` + - |MMMMMH' ,M#'- + '. :MMMH| .- + . |MM - + ` . `#?.. . ..' + -. _. .- + '-|.#qo__,,ob=~~-'' + _ooppH[`MMMD::--\_ + _oHMR":&M&. ""' "' /&\\_ + oHMMMMMHMMH#9, `"#&H6?*MMH:-.._ + .oHMMMR:"&MZ\ `"' " |$-_ + ..dMMMMMMMMdMMM#9\ `'HHo. + . ,dMMMMMMMMMMM"`' ` ?MP?. + . |MMMMMMMMMMM' `"$b&\ + - |MMMMHH##M' HMMH? + - TTMM| >.. \MMMMMH + : |MM\,#-""$~b\. `MMMMMM+ +. ``"H&# - &MMMMMM| +: *\v,#MHddc. `9MMMMMb +. MMMMMMMM##\ `"":HM +- . .HMMMMMMMMMMRo_. |M +: |MMMMMMMMMMMMMMMM#\ :M +- `HMMMMMMMMMMMMMMM' |T +: `*HMMMMMMMMMMMM' H' + : MMMMMMMMMMM| |T + . MMMMMMMM?' ./ + `. MMMMMMH' ./ + -. |MMMH#' . + . `MM* . ' + -. #M: . . .- + ` . ., .- + '-.-~ooHH__,,v~--` + _ood>H&H&Z?#M#b-\. + .\HMMMMMR?`\M6b."`' ''``v. + .. .MMMMMMMMMMHMMM#&. ``~o. + . ,HMMMMMMMMMMMM*"'-` &b. + . .MMMMMMMMMMMMH' `"&\ + - RMMMMM#H##R' 4Mb + - |7MMM' ?:: `|MMb + / HMM__#|`"\>?v.. `MMML +. `"'#Hd| ` 9MMM: +- |\,\?HH#bbL `9MMb +: !MMMMMMMH#b, `""T +. . ,MMMMMMMMMMMbo. | +: 4MMMMMMMMMMMMMMMHo | +: ?MMMMMMMMMMMMMMM? : +-. `#MMMMMMMMMMMM: .- + : |MMMMMMMMMM? . + - JMMMMMMMT' : + `. MMMMMMH' - + -. |MMM#*` - + . HMH' . ' + -. #H:. .- + ` . .\ .- + '-..-+oodHL_,--/-` + _,\?dZkMHF&$*q#b.. + .//9MMMMMMM?:'HM\\"`-''`.. + ..` :MMMMMMMMMMHMMMMH?_ `-\ + . .dMMMMMMMMMMMMMM'"'" `\. + . |MMMMMMMMMMMMMR \\ + - T9MMMMMHH##M" `? + : (9MMM' !':. &k + .: HMM\_?p "":-b\. `ML +- "'"H&#, : |M| +: ?\,\dMH#b#. 9b +: |MMMMMMM##, `* +: . +MMMMMMMMMMMo_ - +: HMMMMMMMMMMMMMM#, : +: 9MMMMMMMMMMMMMH' . +: . *HMMMMMMMMMMP .' + : MMMMMMMMMH' . + - :MMMMMMM'` . + `. 9MMMMM*' - + -. {MMM#' : + - |MM" .' + `. &M'.. . ..' + ' . ._ .- + '-. -voboo#&:,-.-` + _oo:\bk99M[<$$+b\. + .$*"MMMMMMMM[:"\Mb\?^" . + . '` HMMMMMMMMMMHMMMM+?. `. + . .HMMMMMMMMMMMMMMP"'' . + . `MMMMMMMMMMMMMM| -`. + - `&MMMMMMHH##H: : + : `(*MMM} `|\ | + : `- ?MMb__#|""`|+v.. \ +. `''*H#b - :| +: `*\v,#M#b#, \ +. 9MMMMMMHb. : +: . #MMMMMMMMMb\ - +- .HMMMMMMMMMMMMb : +: `MMMMMMMMMMMMH . +-: . `#MMMMMMMMMP ' + : ]MMMMMMMH' : + - ,MMMMMM?' . + `: HMMMMH" - + -. .HMM#* .- + `. .HH*' . + `-. &R". .- + -. ._ .- + '-. .voodoodc?..-` + _\oo\?ddk9MRbS>v\_ + ..:>*""MMMMMMMMM:?|H?$?-. + ..- - "HMMMMMMMMMMHMMMH\_-. + . dMMMMMMMMMMMMMMT" . + . TMMMMMMMMMMMMMM `. + - `&HMMMMMM#H#H: . + - `\7HMMH |\. . + : ` HMM\_?c`""+?\.. : +- "``#&#| . - +: `?,\#MHdb. . +: |MMMMMH#. : +: . ,HMMMMMMMb, - +: ' 4MMMMMMMMMMH` +: . 9MMMMMMMMMT- +:.` `#MMMMMMMH ' + : ' HMMMMMH': + - |MMMMH" - + `: |MMMH*' .' + '? dMM#' . + \. .dH" .' + -. ,M'- ..' + ` . .. ..-` + '-. .\ooooboo<^.-` + _o,:o?\?dM&MHcc~,. + ..^':&#""HMMMMMMMM$:?&&?. + .` -` 'HMMMMMMMMMHMMMp\. + . ' |MMMMMMMMMMMMMM"' . + . `9MMMMMMMMMMMMM -. + - `*9MMMMMHH##[ . + - `\Z9MMM `~\ . + : '| ?MMb_?p""-?v.. : +- `"'*&#, - . +: `?,oHH#? . +-- |MMMMH,: +: . |MMMMMM6, +: - |MMMMMMMM +? HMMMMMMP +-- . ' |HMMMMM' + :.` . ' JMMMM+ + \ ,MMMP: + : |MMH?: + -:\. dM#" . + \ ,H*' .' + -. d':..' + ` . .,.- + '-.. .\oooodov~^-` + _o\:,??\??MR9#cb\_ + .v/''':&#""#HMMMMMMM$?*d\. + ..~' - -` `"#MMMMMMMMMMMHv. + .-' HMMMMMMMMMMMR!. + : `9MMMMMMMMMMM| -. + . `*9MMMMMH##| . + - `(#MMH `:, . + : '| `HMb_>/"|\,.: +.' `"'#&b - . +: ?\oHH?. +: !MMM& +: . . HMMMM +/. - -MMMMM +\`. 9MMMP +:. . . - |MMM' + \... ' .MMT + &. .dMP + \, .HM* + \. `\. ,H&' + `- `| - ,&': + `. ,/\ ' + '-.. _.- + "---.._\o,oov+--'" + _,d?,:?o?:?HM>#b\_ + ..H*"''`'H#*"**MMMMMM6$$v_ + v//" - `` `'#MMMMMMMMHo. + /"` |MMMMMMMMMM:. + ,> `HMMMMMMMMH:. + : `#HMMMMHH\ - + ' `Z#MM, `,: + : '\ ?HH_>:`\, +: "'*&| `: +. <\Hb +: MM +: . iMM +Mb\. {MM +::.`- - !MP +`&. . . - :M' + 9H, \ ' |T + HM? ,P + *ML ?? + :&. `o .d' + ': |T /" + -. .<'' + `... ..- + "`-=.,_,,,oov-~.-` + _,oc>?_:b?o?HH#b\_ + .v/99*""" '*H#""*HMMMMMZ,_ + oH* /" - ' "`#MMMMM#o. + ./*>- `MMMMMMMb + ,b/' `#MMMMMMM\ + :' ``HMMMMb: + /- `|&MH `\ + / `-. |Hb??\ +,- ' "`&,. +1 \} +!. T +$,. . 1 +?`M??. M +?.::| '\ - ? + M?&. . . - ,' + 9MMH\ .. ' ` . + HMMM#. :' + 9#MMb .. + -:"# `b. .- + . ` {! / + - ,-' + ' . .- + ```^==\_.,,,ov--\-` + _\o##??,:io??$#b\_ + .oH#"H9*""" "`#H*"*#MMMHo_ + oHMM- -' - '' ``*HMMHo. + dM#S>-` ?MMMM?. + ,&&,/' "#MMMH\ + d?-" `*HMMb + H? "ZHb: + /: \ H?L +|:| . `*: +:?: \ +>" : +M|\,_ | +!|":HH?-'. : +:^'_:?"\ `-- - . +- |ML?b . .. - - + :HMMMMH\ \ ` : + >MMMMMM#. . + ^M*HMMM| - + `. `"#+ `?v .` + . `- +?' - + ` . ..' + - . .- + "`\b=p?.._\\vv---` + _,o#bH\??::?o?cbo_ + .o#MH#**SH""' "`*H#"*#MHo_ + oHMMMH^ ^" - ` '*HHo. + .dMMM#">>- `HM?. + ,MH:R_o/ `*MH\ + dMM' ' "ML + HMR! ' `#k + d&'. -. `L +:M :: ` `- +/| !| - +k.$-" : +}9R:!,,_. . +\::\':`*M#\-'. - +: "''..:"!`\ '- - ` +- ,HMb.H| . _ - .' + : ,MMMMMMMb. .. . + .`HMMMMMMMM? . + `.`9M#*HMMMM : + -.' "##* `b, . + . ` ,/' .' + ` . ..' + - . ..- + "`*#d##c.._\v----` + _,o#&oHb?\o::d?>\_ + .oHHMMM#**$M""` "`*HH"#&o_ + oHMMMMMMD' .'' - ' ``bo. + .dMMMMMH*'/|- `\b. + ,MMMM?T|_o/ `\\ + dMMMMP '' `| + HMMMH& - `\ + /MH7' : -- : +-:MM {. . . +:i?' .!& . +:{, o| ' : +-T?9M\:-'o,_ . +: \?::``"`?9MHo./.. - +. '"`'^ _.`"!"^. `- - ` +- ,bMM?.M\ . . - . .' + : .oMMMMMMMMb. .. ` . + . `HMMMMMMMMMMb - + - 9MH*#HMMMMH .' + '. ' `"*##' `b. : + . ` .d'' .' + -. . ' + -. .-` + "`*##H###:._\--.-` + _oo#H&d#b?\b:_>>\_ + .oHMMMMMMH*"*9R"'-``*#P\-_ + oHMMMMMMMMM$ ." ' `^- + .dMMMMMMMMH*",?- '\. + ,MMMMMMM:?}.,d' `. + dMMMMMMMH /'' : + HMMMMMMM&' - - + dPTMMP>' : -. : +|? -MM} .\ . +J' ::*' -$L . +: ?b .,H- ' : +- |6.&MP:: !.,_. - +: `\:: "' "`:"MM#,-^, - : +- ````:' _.:"?``\ `- . +: .?bMML.]# - _ ` . .' + - .o#MMMMMMMMH\ \. . . + - `HMMMMMMMMMMMH : + `. `HMM#*#MMMMMH' - + -. ' ``##*' i+ : + - `' v/' .' + `- ..' + ' . .- + "`*##HMH##:__,-.-` + _oo##Mbb&bo??o_>\_ + .oHMMMMMMMMM**#?M*' "?*&.. + oHMMMMMMMMMMMM4 `" - `. + .dMMMMMMMMMMMM#"\?.- . + ,MMMMMMMMMM}"9:_,d' -. + dMMMMMMMMMMM| ^'' . + &MMMMMMMMMMH\ - . + :{M*"MMMPT"' : `-. : +.'M' 'MMM. -T, . . +- k i:?'' -|& . +: ` -o& .,H- " : +- `M:`HMP|:'!.o._. . +: "<:::'<^ '"``9MH#,-^ . - +- '''``''._.`"?`^| ^ - : +: ?#dMM_.M? . . - ..' + : ,ddMMMMMMMMMb. .. ' . + . TMMMMMMMMMMMMM, : + - ?MMH**#MMMMMH' : + '. ' "`##*' &. : + -. `' ,~" .' + -. ..' + ` . .- + ```*##HMMMH#<:,..-` + _,dd#HMb&dHo?\?:\_ + .oHMMMMMMMMMMMH***9P'`"\v. + oHMMMMMMMMMMMMMMM> `' -. + .dMMMMMMMMMMMMMMMH*'|~-' . + ,MMMMMMMMMMMMM6>`H._,& -. + dMMMMMMMMMMMMMMM| `" . + H*MMMMMMMMMMMMMH&. - . + d' HMM""&MMMPT'' :. `.- +,' MP `TMMM, |: . - +| #: ? *" : &L : +! `' /?H ,#r `' : +. ?M: HMM^<~->,o._ : +: `9:::'`*-``':`9MHb,|-, ' : +. `"''':' :_ ""!"^. `| : +`. _dbHM6_|H. . . ' .' + \ _odHMMMMMMMMH, .. ` : + `- |MMMMMMMMMMMMM| : + `. 9MMH**#MMMMMH' : + -. ' "?##" d : + . ' ,/" .' + `.. ..' + ` . .- + '`"#HHMMMMM#<>..-` + _oo##bHMb&d#bd,>\_ + .oHMMMMMMMMMMMMMM***9R"-.. + oHMMMMMMMMMMMMMMMMMH\ ? `-. + .dMMMMMMMMMMMMMMMMMMM#".}-' . + ,MMMMMMMMMMMMMMMMM6/`H _o} -. + dMMMMMMMMMMMMMMMMMMML `'' . + HbP*HMMMMMMMMMMMMMMM*: - , + dMH' `MMMP'"HMMMR'T" : : +|H' -MR' `?MMMb P, . . +1& *| |.`*" .-`&| . +M' " |\&| .,#~ "' : +T :HL.|HMH\c~`|v,\_ : +| `"|:::':`-`` '"MM#\-'. -: +% ``'``'`' :_ '?'`| ``. : +||, ,#dMM?.M? . .` - + ?\ .,odMMMMMMMMM? \ ` : + / |MMMMMMMMMMMMM: .' + `. TMMH#*9MMMMM* : + -. ` "*#*' ,: . + . ` .v'' .' + `. ..' + '- . .- + "`\+HHMMMMMMHr~.-` + _,,>#b&HMHd&&bb>\_ + _oHMMMMMMMMMMMMMMMMH**H:. + oHMMMMMMMMMMMMMMMMMMMM#v`? `. + .dMMMMMMMMMMMMMMMMMMMMMMH*`+| . + ,MMMMMMMMMMMMMMMMMMMMMb|?+.,H -. + ddHMMMMMMMMMMMMMMMMMMMMMb `' . + HMMkZ**HMMMMMMMMMMMMMMMMH\ - . : + dTMMM* `9MMMP'"*MMMMPT"` .. : +|M6H'' 4MP' `"HMMM| !|. . . +1MHp' #L $ *"' .-:&. . +MMM' " q:H. .o#-``' : +MM' ?H?.|MMH::::-o,_. - +M[ `*?:::'|` `"`:9MH\~-. ` +&M. ""'`'^'.:.`?'`. '| -: +`M|d, .dbHM[.1? .. : + 9||| . _obMMMMMMMMH, . : + H.^ MMMMMMMMMMMM} - + \ |MMH#*HMMMMH' .' + . ` `#*' ,:- + ` '' .-'. + `. .- + '- . .-` + '`\bqHMMMMMMHHb--` + .,:,#&6dHHHb&##o\_ + .oHHMMMMMMMMMMMMMMMMMH*\,. + oHMMMMMMMMMMMMMMMMMMMMMMHb:'-. + .dMMMMMMMMMMMMMMMMMMMMMMMMMH|\/' . + ,&HMMMMMMMMMMMMMMMMMMMMMMM/"&.,d. -. + dboMMHMMMMMMMMMMMMMMMMMMMMMML `' . + HMHMMM$Z***MMMMMMMMMMMMMMMMMM|.- . + dMM}MMMM#' `9MMMH?"`MMMMR'T' _ : +|MMMbM#'' |MM" ``MMMH. <_ . +dMMMM#& *&. .?`*" .'&: . +MMMMMH- `' -v/H .dD "' ' : +MMMM* `*M: 4MM*::-!v,_ : +MMMM `*?::" "'``"?9Mb::. : +&MMM, `"'"'|"._ "?`| - : +`MMM}.H ,#dM[_H ..: + 9MMi`M: . .ooHMMMMMMM, .. + 9Mb `- 1MMMMMMMMMM| : + ?M |MM#*#MMMM* . + -. ` |#"' ,' + . -" v` + -. .- + - . . ` + '-*#d#HHMMMMHH#"-' + _,<_:&S6dHHHb&bb\_ + .odHMMMMMMMMMMMMMMMMMMM}-_ + .oHMMMMMMMMMMMMMMMMMMMMMMMM#d:. + ?9MMMMMMMMMMMMMMMMMMMMMMMMMMMH-$ . + ,::dHMMMMMMMMMMMMMMMMMMMMMMMMH:\.?? -. + dMdboHMMHMMMMMMMMMMMMMMMMMMMMMMH, ' . + HMMMM7MMMb$R***MMMMMMMMMMMMMMMMMH\ - . + dMMMMM/MMMMM* `$MMMM*'"*MMMM?&' . : +|MMMMMMb1H*' HMP' '9MMM| &. . . +dMMMMMMM##~` `#\ |.`*" .-9. : +9MMMMMMMM* ` |v7? .,H `' ` : +SMMMMMMH' '9M_-MMH::-\v_ : +:HMMMMM `\_:"'|'`':9Mv\. +-|MMMMM, ""`'`':.`?\ \ +`:MMMMM}.d} .?bM6,| | + :?MMM6 M| . .,oHMMMMM| / + .?MMM- `' &MMMMMMMM|. + -`HM- HMH#*MMM?: + '. ' `#*:` + - -'/ + ` . . ' + ` . . ` + '--##HH#HMMMHH#""` + _o,d_?dZdoHHHb#b\_ + .vdMMMMMMMMMMMMMMMMMMMMH\. + .,HHMMMMMMMMMMMMMMMMMMMMMMMMH&,. + /?RMMMMMMMMMMMMMMMMMMMMMMMMMMMMH|.. + ,\?>`T#RMMMMMMMMMMMMMMMMMMMMMMMM6`\|/ + dMMbd#ooHMMMHMMMMMMMMMMMMMMMMMMMMMH,`' ' + HMMMMMMMTMMMMb$ZP**HMMMMMMMMMMMMMMMM|. : + dMMMMMMMM}$MMMMMH' `HMMMH?"`MMMM?T' . : +|MMMMMMMMMMoMH*'' `MM? ``MMM| +\ . +1MMMMMMMMMMMb#/ ?#? |`#" -T: : +*'HMMMMMMMMMM*' " ~?& .?} ' ' . +- 4MMMMMMMMP" `M? HMTc:\\.: +: `MMMMMMM[ "#:::`>`"?M{ +. |MMMMMMH. ``'``'_`:- +- |MMMMMMM|.dD ,#Mb\' + : *MMMMM: iM| . _oHMMMM: + . ?MMMM' "' ,MMMMMMP + : `HMH JM#*MMT + -. ' ` #' + . / + -. - .' + -. . ` + '--=&&MH##HMHH#""" + .-:?,Z?:&$dHH##b\_ + ,:bqRMMMMMMMMMMMMMMMMMHo. + .?HHHMMMMMMMMMMMMMMMMMMMMMMMHo. + -o/*M9MMMMMMMMMMMMMMMMMMMMMMMMMMMv + .:H\b\'|?#HHMMMMMMMMMMMMMMMMMMMMMM6?Z\ + .?MMMHbdbbodMMMMHMMMMMMMMMMMMMMMMMMMM\': + :MMMMMMMMMMM7MMMMb?6P**#MMMMMMMMMMMMMMM_ : + \MMMMMMMMMMMMb^MMMMMM? `*MMMM*"`MMMR<' . - +.1MMMMMMMMMMMMMb]M#"" 9MR' `?MMb \. : +-MMMMMMMMMMMMMMMH##|` *&. |`*' .\ . +-?""*MMMMMMMMMMMMM' ' |?b ,}" : +: MMMMMMMMMMH' `M_|M}r\? +. `MMMMMMMMM' `$_:`'"H +- TMMMMMMMM, '"``:: +: {MMMMMMMM| oH| .#M- + : `9MMMMMM' .MP . ,oMMT + . HMMMMP' `' ,MMMP + - `MMH' HH9* + '. ` ` .' + - . ' + ` . - .- + ` . .- + ' -==pHMMH##HH#""" + _..-:b&::&?&&##bo_ + ...?-#&9MMMMMMMMMMMMMMMHo_ + .. .1&#MMHMMMMMMMMMMMMMMMMMMMHo. + . .o/##R9MMMMMMMMMMMMMMMMMMMMMMMM?. + .- |MSd?|'`$?#HMMMMMMMMMMMMMMMMMMMMMH\ + - dMMMMHbd##oodMMMM#MMMMMMMMMMMMMMMMMH: + - JMMMMMMMMMMMMM7HMMMH$SR***MMMMMMMMMMMMb> + : {MMMMMMMMMMMMMMM`9MMMMMH' ``HMMM?"*MM[| : +- |MMMMMMMMMMMMMMMMM - 27 July 2004 +# Department of Physics and Astronomy, Vassar College, Poughkeepsie NY +# Eric Myers +# Spy Hill Research, Poughkeepsie, New York +# @(#) $Id: boinc,v 1.8 2007/09/02 01:05:35 myers Exp $ +######################################################################## + +## +# Defaults, which can be overridden in /etc/sysconfig/boinc (Red Hat) +# or /etc/default/boinc (Debian) + +BOINCUSER=boinc +BOINCDIR=/home/boinc +BUILD_ARCH=i686-pc-linux-gnu +BOINCEXE=/usr/local/bin/boinc_client + + +# Log and error files (you should rotate these occasionally) +LOGFILE=boinc.log +ERRORLOG=error.log + +# Additional BOINC options: +# Be wary of -allow_remote_gui_rpc, as it can open your machine up +# to the world if you don't set a password, or if you set a poor one. +# Should be okay if you are behind a NAT firewall. + +#BOINCOPTS="-allow_remote_gui_rpc" # opens up your machine to the world! + + +# Mandrake 10.1 really wants a subsys lock file ... +if [ -d /var/lock/subsys ]; then + LOCKDIR=/var/lock/subsys +elif [ -d /var/lock ]; then + LOCKDIR=/var/lock +fi + +# su on Linux seems to need this to be set to work properly in a script +export TERM dumb + + +## +# Init script function library. This stuff is Red Hat specific, +# but if the functions are not found we create our own simple replacements. +# (The idea for replacing the functions comes from OpenAFS. Thanks guys!) + +if [ -f /etc/rc.d/init.d/functions ] ; then + . /etc/rc.d/init.d/functions +else + export PATH=/sbin:/bin:/usr/sbin:/usr/bin + function echo_success () { echo -n " [ OK ] " ; } + function echo_failure () { echo -n " [FAILED] " ; } + function echo_warning () { echo -n " [WARNING] " ; } + function killproc() { + PID=$(pidof -s -x -o $$ -o $PPID -o %PPID $1) + [ $PID ] && kill $PID ; } +fi + + +## Look for any local configuration settings which override all above + +if [ -f /etc/sysconfig/boinc ]; then + . /etc/sysconfig/boinc +elif [ -f /etc/default/boinc ]; then + . /etc/default/boinc +fi + + +## Verify the working directory exists: + +if [ ! -d $BOINCDIR ]; then + echo -n "Cannot find BOINC directory $BOINCDIR " + echo_failure + echo + exit 7 +fi + + +# Some additional places to look for the client executable +# (Should do this after init.d/functions and sysconfig/boinc, which sets PATH) + +export PATH=$BOINCDIR:/usr/local/bin:$PATH + + +## Locate the executable, either boinc_client, boinc, +## or boinc_M.mm_.... with highest version number +## We only do this if BOINCEXE set above isn't found and executable. + +if [ ! -x $BOINCEXE ]; then + BOINCEXE=$(/usr/bin/which boinc_client 2>/dev/null) + if [ ! -x "$BOINCEXE" ]; then + BOINCEXE=$(/usr/bin/which boinc 2>/dev/null) + if [ ! -x "$BOINCEXE" ]; then + BOINCEXE=$(/bin/ls -1 $BOINCDIR/boinc_*_$BUILD_ARCH 2>/dev/null | tail -1 ) + fi + fi +fi + +if [ ! -x "$BOINCEXE" ]; then + echo -n "Cannot find an executable for the BOINC client." + echo_failure + echo + exit 2 +fi + + + +## Functions: $1 is one of start|stop|status|restart + +case "$1" in + start) + cd $BOINCDIR + + if [ -f lockfile ] ; then + echo -n "Another instance of BOINC is running (lockfile exists)." + echo_failure + echo + exit 4 + fi + + if [ ! -d projects ] ; then + echo -n "The BOINC client requires initialization." + echo_warning + echo + fi + + echo -n "Starting BOINC client as a daemon: " + su $BOINCUSER -c "$BOINCEXE $BOINCOPTS" >>$LOGFILE 2>>$ERRORLOG & + sleep 1 + PID=$(pidof -s -x -o $$ -o $PPID -o %PPID $BOINCEXE) + if [ $PID ]; then + [ -d $LOCKDIR ] && touch $LOCKDIR/boinc + echo_success + else + echo_failure + fi + echo + ;; + + stop) + cd $BOINCDIR + if [ ! -f lockfile -a ! -f $LOCKDIR/boinc ] ; then + echo -n "BOINC is not running (no lockfiles found)." + echo_success + else + echo -n "Stopping BOINC client daemon: " + killproc $BOINCEXE && echo_success || echo_failure + # clean up in any case + rm -f $BOINCDIR/lockfile + rm -f $LOCKDIR/boinc + fi + echo + ;; + + restart) + $0 stop + $0 start + ;; + + status) + PID=$(pidof -x -o $$ -o $PPID -o %PPID boinc_client) + if [ "$PID" == "" ]; then + PID=$(pidof -x -o $$ -o $PPID -o %PPID $BOINCEXE) + fi + if [ "$PID" != "" ]; then + echo "BOINC client is running (pid $PID)." + else + if [ -f $BOINCDIR/lockfile -o -f $LOCKDIR/boinc ]; then + echo "BOINC is stopped but lockfile(s) exist." + else + echo "BOINC client is stopped." + fi + fi + ;; + + *) + echo "Usage: boinc {start|stop|restart|status}" + exit 1 +esac + +exit + +#EOF# diff --git a/database/configuration/boinc/etc/sysconfig/boinc b/database/configuration/boinc/etc/sysconfig/boinc new file mode 100644 index 00000000..ad9da427 --- /dev/null +++ b/database/configuration/boinc/etc/sysconfig/boinc @@ -0,0 +1,16 @@ + +# BOINC daemon configuration settings. The examples below are the defaults +# you'd get if you left them out, so you only need the lines that are different. +# +BOINCUSER=fred +BOINCDIR=/home/fred/BOINC +#BUILD_ARCH=i686-pc-linux-gnu +BOINCEXE=/home/fred/BOINC/boinc +#LOGFILE=boinc.log +#ERRORLOG=error.log + +#BOINCOPTS="-allow_remote_gui_rpc" +# opens up your machine to the world! + +#LOCKDIR=/var/lock/subsys + diff --git a/database/configuration/boinc/readme.txt b/database/configuration/boinc/readme.txt new file mode 100644 index 00000000..18295eaf --- /dev/null +++ b/database/configuration/boinc/readme.txt @@ -0,0 +1,15 @@ + + +the files in etc/sysconfig and etc/init.d should be copied into the +real /etc directory. + +the file /etc/init.d/boinc starts the boinc client on system startup +and the init process will ensure that it gets restarted if it crashes. + +the file /etc/sysconfig/boinc has configuration information for the +boinc process. the user and path will need to be changed to make them +appropriate for your installation. + + + + diff --git a/database/configuration/boot/chos.conf b/database/configuration/boot/chos.conf new file mode 100644 index 00000000..3b18e759 --- /dev/null +++ b/database/configuration/boot/chos.conf @@ -0,0 +1,62 @@ +# +# Choose-OS 0.81 example configuration file. +# + +# +# Traditional (version <= 0.41) lookalike interface. +# +############################################################################# + +# Do we wanna use a cooler color instead of light gray ? yeah. +# +color=lightcyan,brown + +# Wanna have another selection color ? yeah. +# +selection=green,black + +# Set a cooler banner? yeah. +# +banner=Booting the OS of your choice today, Sir! + +# Wanna see copyright 'n keyhelp ?? (Set this to off if U don't.) +# +infoline=on + +# +# General stuff +# +############################################################################# + +# wait 60 secs before bootin' the first image +# +delay=14 + +# Install the boot sector on /dev/hda (makes it MBR). +# +install=/dev/hda + +# Want emergency boot sector ? +# +#emergency=/dev/hda1 + +# +# Next are the boot images... +# +############################################################################# + +# a Linux kernel image +# +linux "Linux" { + image=/boot/vmlinuz + cmdline=vga=EXTENDED root=/dev/hda5 read-only + color=green + initrd=/boot/initrd-2.2.5-15.img +} +# a DOS bootsector +# +bootsect "DOS" { + image=/dev/hda1 + color=red +} + diff --git a/database/configuration/boot/lilo.conf b/database/configuration/boot/lilo.conf new file mode 100644 index 00000000..3ff44a0c --- /dev/null +++ b/database/configuration/boot/lilo.conf @@ -0,0 +1,13 @@ +boot=/dev/hda +map=/boot/map +install=/boot/boot.b +prompt +timeout=50 +image=/boot/vmlinuz-2.2.5-15 + label=linux + root=/dev/hda5 + initrd=/boot/initrd-2.2.5-15.img + read-only +other=/dev/hda1 + label=dos + table=/dev/hda diff --git a/database/configuration/cron/bookmarks_builder.crontab b/database/configuration/cron/bookmarks_builder.crontab new file mode 100644 index 00000000..a68d61fc --- /dev/null +++ b/database/configuration/cron/bookmarks_builder.crontab @@ -0,0 +1,9 @@ + +SHELL=/bin/bash + +# m h dom mon dow command + +# regenerate the bookmarks pages. +24 10 * * * (source $HOME/yeti/scripts/core/profile.sh; bash $HOME/yeti/scripts/bookmarks/create_marks.sh ) &>/tmp/zz_bookmarkingering_$USER.log + + diff --git a/database/configuration/cron/chkrootkit.crontab b/database/configuration/cron/chkrootkit.crontab new file mode 100644 index 00000000..c73f4f4d --- /dev/null +++ b/database/configuration/cron/chkrootkit.crontab @@ -0,0 +1,12 @@ + +SHELL=/bin/bash + +# m h dom mon dow command + +# this runs chkrootkit to demonstrate a bug in the vm kernel currently +# provided by...? +# who's the vendor? + +3 * * * * (/usr/sbin/chkrootkit -q &>/var/www/rootkit_report_quiet.txt ) +4 * * * * (/usr/sbin/chkrootkit -x &>/var/www/rootkit_report_expert.txt ) + diff --git a/database/configuration/cron/doxygen.crontab b/database/configuration/cron/doxygen.crontab new file mode 100644 index 00000000..e6b08edd --- /dev/null +++ b/database/configuration/cron/doxygen.crontab @@ -0,0 +1,6 @@ + +SHELL=/bin/bash + +# m h dom mon dow command + +20 4 * * * (source $HOME/yeti/scripts/core/profile.sh; cd $HOME/hoople/docs; make) &>/tmp/doxy_make.log diff --git a/database/configuration/cron/linux_config_snarf.crontab b/database/configuration/cron/linux_config_snarf.crontab new file mode 100644 index 00000000..1747ade5 --- /dev/null +++ b/database/configuration/cron/linux_config_snarf.crontab @@ -0,0 +1,9 @@ + +SHELL=/bin/bash + +# m h dom mon dow command + +# do a backup of the config files once a week. +28 7 * * 4 (source $HOME/yeti/scripts/core/profile.sh; cd /z/stuffing/archives; perl $HOME/yeti/scripts/archival/snarf_linux_config.pl) &>/dev/null + + diff --git a/database/configuration/cron/mysql_db_backup.crontab b/database/configuration/cron/mysql_db_backup.crontab new file mode 100644 index 00000000..9aea4fe8 --- /dev/null +++ b/database/configuration/cron/mysql_db_backup.crontab @@ -0,0 +1,7 @@ + +SHELL=/bin/bash + +# m h dom mon dow command + +14 7 * * * bash $HOME/yeti/scripts/database/backup_mysql_dbs.sh + diff --git a/database/configuration/cron/nechung_fortune.crontab b/database/configuration/cron/nechung_fortune.crontab new file mode 100644 index 00000000..0d3ae16f --- /dev/null +++ b/database/configuration/cron/nechung_fortune.crontab @@ -0,0 +1,6 @@ + +SHELL=/bin/bash + +# m h dom mon dow command + +* * * * * (export LIGHTWEIGHT_INIT=true; source $HOME/yeti/scripts/core/profile.sh; perl $HOME/yeti/scripts/text/new_sig.pl) &>/dev/null diff --git a/database/configuration/cron/overload_check.crontab b/database/configuration/cron/overload_check.crontab new file mode 100644 index 00000000..d81f31f3 --- /dev/null +++ b/database/configuration/cron/overload_check.crontab @@ -0,0 +1,9 @@ + +SHELL=/bin/bash + +# m h dom mon dow command + +# make a report of the current state of information overload in our notes files. +24 22 * * * (source $HOME/yeti/scripts/core/profile.sh; bash $HOME/yeti/scripts/notes/info_overload_report.sh ) &>/tmp/zz_info_overload_$USER.log + + diff --git a/database/configuration/cron/random_sound.crontab b/database/configuration/cron/random_sound.crontab new file mode 100644 index 00000000..18dbbcf5 --- /dev/null +++ b/database/configuration/cron/random_sound.crontab @@ -0,0 +1,6 @@ + +SHELL=/bin/bash + +# m h dom mon dow command + +* * * * * (export LIGHTWEIGHT_INIT=true; source $HOME/yeti/scripts/core/profile.sh; bash $HOME/yeti/scripts/multimedia/randomly_play.sh) &>/dev/null diff --git a/database/configuration/cron/time_synch.crontab b/database/configuration/cron/time_synch.crontab new file mode 100644 index 00000000..8510ee82 --- /dev/null +++ b/database/configuration/cron/time_synch.crontab @@ -0,0 +1,15 @@ + +SHELL=/bin/bash + +# m h dom mon dow command + +# standard to synch to local machine. +0,4,8,12,16,20,24,28,32,36,40,44,48,52,56 * * * * /usr/sbin/sntp -P no -r equanimity +or +0,4,8,12,16,20,24,28,32,36,40,44,48,52,56 * * * * /usr/bin/rdate -s -n equanimity + +# one machine on the network uses this version, to synch externally. +0 0,4,8,12,16,20 * * * /usr/sbin/sntp -P no -r time.nist.gov +or +0 0,4,8,12,16,20 * * * /usr/bin/rdate -s -n time.nist.gov + diff --git a/database/configuration/cron/uptime_report.crontab b/database/configuration/cron/uptime_report.crontab new file mode 100644 index 00000000..9352e97e --- /dev/null +++ b/database/configuration/cron/uptime_report.crontab @@ -0,0 +1,8 @@ + +SHELL=/bin/bash + +# m h dom mon dow command + +# the script writes the current time and current uptime to the log file. +0 * * * * (source $HOME/yeti/scripts/core/profile.sh; bash $HOME/yeti/scripts/system/write_uptime_report.sh) &>/dev/null + diff --git a/database/configuration/cron/zooty_archive_packer.crontab b/database/configuration/cron/zooty_archive_packer.crontab new file mode 100644 index 00000000..50bc3116 --- /dev/null +++ b/database/configuration/cron/zooty_archive_packer.crontab @@ -0,0 +1,9 @@ + +SHELL=/bin/bash + +# m h dom mon dow command + +# this packs up our archives once a day. +32 6 0 * * (source $HOME/yeti/scripts/core/profile.sh; bash $HOME/yeti/scripts/archival/pack_yeti.sh ; bash $HOME/yeti/scripts/archival/pack_hoople.sh ) &>/tmp/packing_cron.log + + diff --git a/database/configuration/database/sql/find_prims_created_by.sql b/database/configuration/database/sql/find_prims_created_by.sql new file mode 100644 index 00000000..0e44611e --- /dev/null +++ b/database/configuration/database/sql/find_prims_created_by.sql @@ -0,0 +1,13 @@ + +# opensim select statement to find items in the prims table (items that still +# exist in some sim or at least exist in the database) that are owned by +# a particular avatar. the {uuid} below should be replaced with the UUID +# that you care about showing prim ownership for. + +select * from prims where creatorid = '{uuid}' + +# this is a very similar select that shows the items which are NOT owned by +# the avatar in question. + +select * from prims where creatorid != '{uuid}' + diff --git a/database/configuration/database/sql/replace_creatorid_fields.sql b/database/configuration/database/sql/replace_creatorid_fields.sql new file mode 100644 index 00000000..6f3162c1 --- /dev/null +++ b/database/configuration/database/sql/replace_creatorid_fields.sql @@ -0,0 +1,14 @@ + +# this sql statement fixes ownership for items that may have come in with +# erroneous owner UUIDs due to second inventory and the vagaries of crossing +# between grids. +# this particular example is pretty brutal. it finds any item that is owned +# by the old avatar ID {old_ID} and swaps out the owner with the {new_ID} +# avatar. This task is made more difficult by the lack of a wildcard in +# the SQL replace function, so we only stomp out one bogus ID at a time. + +UPDATE inventoryitems +SET creatorid = replace(creatorid, '{old_ID}', '{new_ID}') +WHERE creatorid == '{old_ID}'; + + diff --git a/database/configuration/devices/usb_ram_disk/fstab_with_usb_drive.txt b/database/configuration/devices/usb_ram_disk/fstab_with_usb_drive.txt new file mode 100644 index 00000000..c1ee5832 --- /dev/null +++ b/database/configuration/devices/usb_ram_disk/fstab_with_usb_drive.txt @@ -0,0 +1,5 @@ + +# usb ram drive support on fairly common scsi interface mapping. +/dev/sda1 /mnt/usb auto noauto,owner,user 0 0 + + diff --git a/database/configuration/devices/zip_disk/fstab_with_zip_disk b/database/configuration/devices/zip_disk/fstab_with_zip_disk new file mode 100644 index 00000000..72a95368 --- /dev/null +++ b/database/configuration/devices/zip_disk/fstab_with_zip_disk @@ -0,0 +1,6 @@ + +# mount for zip disk on scsi device where it happens to show up on my machine. +# look for a message in /var/messages to determine which adapter is right. +/dev/sda4 /mnt/zip vfat noauto,rw,user,nosuid,sync,mode=0777 + + diff --git a/database/configuration/dhcp/dhcpd.conf b/database/configuration/dhcp/dhcpd.conf new file mode 100644 index 00000000..02f4794f --- /dev/null +++ b/database/configuration/dhcp/dhcpd.conf @@ -0,0 +1,19 @@ +# example dhcp configuration. + +default-lease-time 600; +max-lease-time 1200; +option subnet-mask 255.255.255.0; +option broadcast-address 192.168.2.255; +option routers 192.168.2.1; +option domain-name-servers 14.28.42.1, 14.28.42.2; +option domain-name "fredulocorp.com"; + +subnet 192.168.2.0 netmask 255.255.255.0 { + range 192.168.2.200 192.168.2.253; +} + +subnet 10.0.0.1 netmask 255.255.255.0 { +} + + + diff --git a/database/configuration/firewall/ipchains/firewalling.conf b/database/configuration/firewall/ipchains/firewalling.conf new file mode 100644 index 00000000..cff83685 --- /dev/null +++ b/database/configuration/firewall/ipchains/firewalling.conf @@ -0,0 +1,30 @@ +#!/bin/sh + +# this could be invoked from rc.local to set up firewall filtering. + +# new firewalling setup. +###ipchains -P forward DENY +# this is too permissive. what are we hoping to block? +###ipchains -A forward -j ACCEPT -b -s 14.28.42.0/24 -d 0.0.0.0/0 +###ipchains -A forward -j ACCEPT -b -s 14.28.42.128/24 -d 0.0.0.0/0 +###ipchains -A forward -j ACCEPT -p tcp -s 0.0.0.0/0 -d 14.28.42.3 25 +###ipchains -A forward -j ACCEPT -p tcp -s 0.0.0.0/0 -d 14.28.42.2 80 + +# input/output rules to dictate valid sides. this can't be used +# when there's a shunt around the gateway; that blocks the packets +# from eth1 that really are from addresses besides 14.28.42.*. +#ipchains -P input DENY +#ipchains -A input -i eth0 -s 14.28.42.1 -j ACCEPT +#ipchains -A input -i eth0 -s 14.28.42.71 -j ACCEPT +#ipchains -A input -i eth0 -s ! 14.28.42.0/24 -j ACCEPT +#ipchains -A input -i eth1 -s 14.28.42.0/24 -j ACCEPT + +# masquerade setup. +#/sbin/depmod -a +#/sbin/modprobe ip_masq_ftp +#/sbin/modprobe ip_masq_raudio +#/sbin/modprobe ip_masq_irc +#ipchains -P forward DENY +#ipchains -A forward -j MASQ -s 14.28.42.0/24 -d 0.0.0.0/0 +#ipchains -A forward -j MASQ -s 14.28.42.128/24 -d 0.0.0.0/0 + diff --git a/database/configuration/firewall/iptables/iptables b/database/configuration/firewall/iptables/iptables new file mode 100644 index 00000000..80480c37 --- /dev/null +++ b/database/configuration/firewall/iptables/iptables @@ -0,0 +1,46 @@ +# Firewall configuration written by lokkit +# Manual customization of this file is not recommended. +# Note: ifup-post will punch the current nameservers through the +# firewall; such entries will *not* be listed here. +*filter +:INPUT ACCEPT [0:0] +:FORWARD ACCEPT [0:0] +:OUTPUT ACCEPT [0:0] +:RH-Lokkit-0-50-INPUT - [0:0] +-A INPUT -j RH-Lokkit-0-50-INPUT + +# allow smtp. +-A RH-Lokkit-0-50-INPUT -p tcp -m tcp --dport 25 --syn -j ACCEPT +# allow ssh. +-A RH-Lokkit-0-50-INPUT -p tcp -m tcp --dport 22 --syn -j ACCEPT + +# allow nfs. +-A RH-Lokkit-0-50-INPUT -p tcp -m tcp --dport 111 --syn -j ACCEPT +-A RH-Lokkit-0-50-INPUT -p udp -m udp --dport 111 -j ACCEPT +-A RH-Lokkit-0-50-INPUT -p tcp -m tcp --dport 369 --syn -j ACCEPT +-A RH-Lokkit-0-50-INPUT -p udp -m udp --dport 369 -j ACCEPT +-A RH-Lokkit-0-50-INPUT -p tcp -m tcp --dport 530 --syn -j ACCEPT +-A RH-Lokkit-0-50-INPUT -p udp -m udp --dport 530 -j ACCEPT +-A RH-Lokkit-0-50-INPUT -p tcp -m tcp --dport 2049 --syn -j ACCEPT +-A RH-Lokkit-0-50-INPUT -p udp -m udp --dport 2049 -j ACCEPT + +# allow SMB (netbios). +-A RH-Lokkit-0-50-INPUT -p tcp -m tcp --dport 137 --syn -j ACCEPT +-A RH-Lokkit-0-50-INPUT -p udp -m udp --dport 137 -j ACCEPT +-A RH-Lokkit-0-50-INPUT -p tcp -m tcp --dport 138 --syn -j ACCEPT +-A RH-Lokkit-0-50-INPUT -p udp -m udp --dport 138 -j ACCEPT +-A RH-Lokkit-0-50-INPUT -p tcp -m tcp --dport 139 --syn -j ACCEPT +-A RH-Lokkit-0-50-INPUT -p udp -m udp --dport 139 -j ACCEPT + +# accept anything from loopback adapter on our own host. +-A RH-Lokkit-0-50-INPUT -i lo -j ACCEPT + +# accept dns info going to the server. +-A RH-Lokkit-0-50-INPUT -p udp -m udp -s 14.28.42.28 --sport 53 -d 0/0 -j ACCEPT + +# otherwise, reject all input. +-A RH-Lokkit-0-50-INPUT -p tcp -m tcp --syn -j REJECT +-A RH-Lokkit-0-50-INPUT -p udp -m udp -j REJECT + +COMMIT + diff --git a/database/configuration/init.d/team_city_startup_example b/database/configuration/init.d/team_city_startup_example new file mode 100644 index 00000000..70471886 --- /dev/null +++ b/database/configuration/init.d/team_city_startup_example @@ -0,0 +1,66 @@ +#!/bin/bash +# +# team_city_startup by Chris Koeritz +# +# /etc/init.d/team_city_startup +# +#uhhh... chkconfig: 2345 01 99 +# description: starts the Team City Agent as our buildor user. +# +### BEGIN INIT INFO +# Provides: team_city_startup +# Required-Start: +# Required-Stop: +# Default-Start: 3 4 5 +# Default-Stop: 0 1 2 6 +# Short-Description: Team City Agent initialization +# Description: team city rc file. This rc script runs the agent as the +# buildor user. +### END INIT INFO + +usage() { + echo "Usage: $0 {start|stop|status|kill}" +} + +function teamcity_action() +{ + export JAVA_HOME=/usr/lib/jvm/java-6-sun + export HOME=/home/buildor + export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games + + # should become the buildor user and tell team city the proper action. + su -m -c "/home/buildor/teamcity_agent/bin/agent.sh $*" buildor +} + +test -x ${PARSER} || exit 0 # by debian policy + +case "$1" in + start) + teamcity_action start + rc=$? + ;; + stop) + teamcity_action stop + rc=$? + ;; + kill) + teamcity_action stop kill + rc=$? + ;; + status) + procs_found=$(ps wuax | grep teamcity | grep -v grep) + if [ -z "$procs_found" ]; then + echo "Team City Agent is not running." + else + echo "Team City Agent is running." + fi + rc=$? + ;; + *) + usage + exit 1 + ;; + esac +exit $rc + + diff --git a/database/configuration/init.d/trac_startup b/database/configuration/init.d/trac_startup new file mode 100644 index 00000000..6e2b0142 --- /dev/null +++ b/database/configuration/init.d/trac_startup @@ -0,0 +1,69 @@ +#!/bin/bash +# +# tracd startup script by Chris Koeritz +# +# Note that this has been superceded by the (IMHO) much better approach of +# running tracd from apache using mod_python. +# +# /etc/init.d/trac_startup +# +#uhhh... chkconfig: 2345 04 11 +# description: starts the trac daemon as the www-data user. +# +### BEGIN INIT INFO +# Provides: trac_startup +# Required-Start: +# Required-Stop: +# Default-Start: 3 4 5 +# Default-Stop: 0 1 2 6 +# Short-Description: trac server initialization +# Description: Keeps the trac server running at our chosen port. +### END INIT INFO + +usage() { + echo "Usage: $0 {start|stop|status|kill}" +} + +function trac_action() +{ +# export JAVA_HOME=/usr/lib/jvm/java-6-sun +# export HOME=/home/buildor + export TRAC_PORT=24108 + export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + + # should become www-data and start trac on our port. + su -m -c "tracd -d -b universalbuilds.com --port $TRAC_PORT /home/trac/ubuilds_project $*" www-data +} + +test -x ${PARSER} || exit 0 # by debian policy + +case "$1" in + start) + trac_action start + rc=$? + ;; + stop) + trac_action stop + rc=$? + ;; + kill) + trac_action stop kill + rc=$? + ;; + status) + procs_found=$(ps wuax | grep tracd | grep -v grep) + if [ -z "$procs_found" ]; then + echo "tracd is not running." + else + echo "tracd is running." + fi + rc=$? + ;; + *) + usage + exit 1 + ;; + esac +exit $rc + + diff --git a/database/configuration/inputrc/symlink_completion.txt b/database/configuration/inputrc/symlink_completion.txt new file mode 100644 index 00000000..55399607 --- /dev/null +++ b/database/configuration/inputrc/symlink_completion.txt @@ -0,0 +1,8 @@ + +>>> goes into /etc/inputrc: + +# Completed names which are symbolic links to +# directories have a slash appended. +set mark-symlinked-directories On + + diff --git a/database/configuration/logging/syslog.conf b/database/configuration/logging/syslog.conf new file mode 100644 index 00000000..ad4258e3 --- /dev/null +++ b/database/configuration/logging/syslog.conf @@ -0,0 +1,55 @@ +# Log all kernel messages to the console. +# Logging much else clutters up the screen. +kern.* /dev/console +kern.* /var/log/kernel.log + +# Log anything (except mail) of level info or higher. +# Don't log private authentication messages! +# Also don't log daemon messages (which go to daemon) or ppp messages (which +# are somehow coming on local2?). +*.info;kern.none;local2.none;daemon.none;mail.none;news.none;authpriv.none /var/log/messages + +# The authpriv file has restricted access. +authpriv.* /var/log/secure + +# Log all the mail messages in one place. +mail.* /var/log/mail.log + +# Everybody gets emergency messages, plus log them on another machine. +*.emerg * + +# Save mail and news errors of level err and higher in a +# special file. +uucp,news.crit /var/log/spooler + +# logging for ppp. +local2.*;daemon.notice /var/log/ppp +daemon.* /var/log/daemon + +# example of breaking up a log facility into its components. +#daemon.=debug /var/log/d.debug +#daemon.=info /var/log/d.info +#daemon.=notice /var/log/d.notice +#daemon.=warning /var/log/d.warning +#daemon.=err /var/log/d.err +#daemon.=crit /var/log/d.crit +#daemon.=alert /var/log/d.alert +#daemon.=emerg /var/log/d.emerg + +# testing to find out what these are. +local1.* /var/log/local1 +local3.* /var/log/local3 +local4.* /var/log/local4 +local5.* /var/log/local5 +local6.* /var/log/local6 + +# Save boot messages also to boot.log +local7.* /var/log/boot.log + +# +# INN +# +news.=crit /var/log/news/news.crit +news.=err /var/log/news/news.err +news.notice /var/log/news/news.notice + diff --git a/database/configuration/nfs_mount/exports b/database/configuration/nfs_mount/exports new file mode 100644 index 00000000..0f8ad639 --- /dev/null +++ b/database/configuration/nfs_mount/exports @@ -0,0 +1,9 @@ +# personal storage areas. + +/fatty/walrus *.lachrymose.net(ro,no_root_squash,sync) +/fatty/stuffing *.lachrymose.net(rw,no_root_squash,sync) +/fatty/opsystems *.lachrymose.net(rw,no_root_squash,sync) + +# allow cd drive to be used remotely for installs, etc. +/media/cdrom *(ro,sync) + diff --git a/database/configuration/nfs_mount/fstab b/database/configuration/nfs_mount/fstab new file mode 100644 index 00000000..a8d837a4 --- /dev/null +++ b/database/configuration/nfs_mount/fstab @@ -0,0 +1,8 @@ + +# nfs mounts to our server. + +pretzel:/fatty/opsystems /mnt/opsystems nfs exec,dev,suid,rw,user 0 0 +pretzel:/fatty/stuffing /stuffing nfs exec,dev,suid,rw,user 0 0 +pretzel:/fatty/walrus /walrus nfs exec,dev,suid,ro,user 0 0 + + diff --git a/database/configuration/opensim/resources/official_saul_panzer_blank.oar b/database/configuration/opensim/resources/official_saul_panzer_blank.oar new file mode 100644 index 0000000000000000000000000000000000000000..6152e0548b369d5c0719617503c919f910185cee GIT binary patch literal 642259 zcmV(;K-<3`iwFP!000000|Ed50RR8gxKofWJk++?wvFAkZQI^$+qP}nwr$(C?cH|w z)7|s__02z3GiP%&HP@Zg3My+Q2P>(Hp|i88i!*~U3oEk;I~yk*2NMf39V;V~AsrVJ z6C<4wiwT>lDYKE0u@Q&9i>Zf;tCK0cl>;--{~H+pgM)+Pe=Ixe|IIQovi =ret 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
    R0jsNghlB^NPVIYwh}%NBradsJVQx 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�MJe2QL}YfPI4`sBt~DG2Fw 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±qeYP!yW9?l907eq=UDTL6BM5#h7 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%0AfBfCz z`GnCjbr1HFpkW99EiqVA87|Ul!+x`WH|xWGTmCpRN4Di@^ckbbr1`=gAEXN$^=WiE zw?N^s@|uH7s6Om4yhrjbs0nOT(0hzXeT8nYxA!&tSKas0E7}mMqriB1Y}DzG*y%`* z4=g1T-brPr#a%vqmk=y+S;Gp<5>KIVSp;fWY7Y0iVnmNe7%*b7gLO}wIoSz1v&5jF zvnrz|HRIJY79sx8Imf(?z&g`j0yIW2(}aNC@WaFqZpZ?W6+HnBl|i3$`QuY)l8U(G z)2ccA<3HuHRUxD*FJO_?zNYYy1_zW05*)GinWV7-eEmb4+CCK%yi{#P+siiABk-hw zA2t6?5TMvAXbMi(B)|*Ukfzj{0JE7;Davw@USz}LbtrFUj!Zg8r!Ku22AmZ z|5fBt=AZSi{&eWt7V&yug*W{;r>MfXrGHRXZ3NDubZryCa7rz!PCZWy5?=OF_E2gV zM&36rf!!?B6i>|9_;!UCW?edK>gTijid93)pOYxmZBexozr$(VY~@J*GwiokUHG5* zYBjduh7^NY=v}Xm*qUG-9g>vgZR`yvg$dOW8cpx-+H7ap@Yc6}u?cjDJ1W&9m`7SQ zG)*=;DCwJM!iHvWG(%~(Zhah-#F(eGpVlrkhhw#?p`A5ixhppaB`|Wcvr>cY5fBFzJ4PV6l>(zB_%A`!c{&Hk=U- z^{vIfhEtJ7&os1iosmikT1Vj_3$^vJ)YmcY*l=zgB?_7-0rIvSdojOZ+7)zoCf3J9 zB@=egO!5iv7M}}nh+$_P$XR%&dCNJ4lWSzT*Le+Fu~QTZNW}{*j8d;8OmsJNh!#4` zv?=vSwmgHC*Z5ozrCUkqRC50s^rBpGuEQ6b37bUDD~i*)C5{t$5@-Q7dD>5ve=U`l zI6)d97k4ozHM`K@JHXT%rS*fR+WMN_f11z8Vh#8vT1n==1MFz424lekZOmT63b5$d z8`S2T;f>#v60aRhZ6?L-NVd{cj*nco_Vl<)N|#{X9nf@1YYn#1w|L#>!*C0{-l!K4 zAM0vZITc1XFIz+Cux?krw@LD6AU0mCD%C#G|8a=q+t)hFrnJSEKy3K(az(&)itHmx zNyEN)S6SZ1Jul%Vby5Xc^z&KkjszwH82LDlV~4I{`7SPUe)-DW zqh$X4Eysl5F8yN^_&Y2?I^NsTh}{cph^)84M_wi9Nq;PJUhXi1dp>9W7+ zOF@8<%UJ{Nc8V}n)6^%&^>&o7W_ln$I-OKf_YV}jW2Oi*RHGgtq4ERYpw1(Jz9i^> ztRQ5#P^Pt6p=F)_WMH!W#tk=0rD~)p`l-e)Pu1eSqm)gnKZg|9p{<$F(U&5OdZdAH z(tz=}66Jm~r~S%JOSu&nseT@%Ugt4B$sdky1j;iEpNLO$<0q0PBJgIto%})0_d^Ye z0IcM`H@NiFG6SslFERcu!#LuJuCJBkilt&QT46Kt;edzlp*Cy!ge|Hq#}H9ppynDJ zM2_XCs{`8YY@hhX2bArqp)Z5}`b8BR)f>oY-7!-gb_%CEwq^rIN$^TDIgs7a@o7ry z#dB>N(^0rTf(XzZ-JmYH0c306u89$p3L<;dtf-Q9tEAT2#d}n+)ME*|4LPS7Z0m;wuB!r zLjsE#$}TWwR4^vSc~2i#hj~x~7D^W?jl@6->jP5W(119N8YzQ65R8)SUm%1}OxXjZ zxohQXXLD5o!;K`X8BbsO076hJdJ}f{q z4&_bnXw+^?4Z!;~Z@q1l;`?(506Rd$zwn4CF;)LMYS6nNG?8Q!!Q=RR-sL~LGsoq! zPIEG9VN4kX>_2L2YW9nBqzcwbGK1=()8riTh&T6pv0M*Us7PYrO}AvnDdM)tfX&%B zvl$HPM10>cr$AO)w>wUfk`KO8fq)c-D~Lic8JAK)l<5U4z}%fRW+vUI)Q^J^Y_=T9 z6Z4|EcPUdJZ<{LiWyU_%bl#>p{V2+YCdt~=+Uys+e<44yi?FJE85)yVKhqV}qJz|` z!d@XFI8UYut#2(u`Z1Jl=Jmd|Fn29$F9ZDvrNi%}lT@P_E0}ZH+-X$2{$-RBLgA}G z{nO;s9fsbpgv*eWnL+O}T2CKtkg*}n>qC(F7^f{`iw9j?WU8O9TAv*!NI!2n zl`S8iQy;2r=6oktD>zgHCMNB6Ai_Y+|`kSqOR#icSX~(KyRx(gHW|2C1O`d<3KGf-jEX@m#~VK?T#Jr`(C6Q|`Of z0MvDfZd6??6x}sl7EhX4!`8g*zW(b6N|HmWD4xoe+qH3@f&(uLD);QFQdNghjO!_u zD;i6}MvKBfy$HsM@ZnP|T%xr8z_=6WZVsx~o4n^dcj0 zt`G8~W4XqYuN=Mu%YEDdR`?Te(zddO_d~cx%#2)C+TP(OI4P5MU3HpaB|cFdB%xU? zB@Ci>R=EM|l>LuT!PB^rYKvE)ILpkr-or|dxL3a>9O7)w?Wc8BjtdCbc+gn_hW7Av zXo^rz`vanM846agd1PgJJg5IsRq`I<&3#gLXG&rEQ{(WQAssx~{1-7@{q{{1YM#NupB(*Jbm?#P}|?ZGK`knpyuHq8rw1Z4vv) zJ}Pnq_fLzqxDF28R6@%n&Re_%=y~^H6*aJlek6R_?oFq$Spu{(LEnOw)TB8AS3!RK z1;D&}P7bKdwx-K9Ir^%d4z;tdySRS$8&koFHuNZRhT@4zXP`P^hUn`*VKg#hZ$ebn z?%Lv7moYSj;rkAreF_-^6XIPm@NQ&+tsuJeN5t2Lp{fOJuK<&2L=L>q%G*HPDCqac zscgqG^QC~D7KpBRGJm`BCPvpAQ!7GfU4`fPZ*WtG-Kc1(Un*6uB&}~+eTQDn|%DUe1f530!0z5 zqDIBG~cWW$*cHUw<6v{>`~vG!-(s;q8Y(q8P)b97nX2m;naC1G$IEcH<>@%o}z?XS=v&NNANC06Kmx5$A1sEM|>i zUN%&`Xidi8mHgaDA6jZ0T;+qaLb8E3-7TpbMe;IOkUZUHG<0Q{8gL-qt6Wd1-s!kD z*@YoWy`27G<9LubzUFpNV&hO)ogPM0ESOww;HI-S+h`9$(16DN`8m{>UEe=9{Ik88 zwuL5!P(p$Nl6<)cJlKQ7WXXfZE2Vh&h$e97vbGjKKq^WXp94mlzNW0)%}8E`MEHmg zEbr_WIHqG#$OSF9`Ts#Hidi=)s5{?+tqXm_vC5k9?C<(hL5Eovw@?YXsT0OqRtIR+ z2K2~;!(iK%JO}ld=fF|SuWT?PcX!uGR5<T88zmgLbw%Sy3A1cKF>2J;i5H7TkdK$TofB$}Ai=X?PqSp^?sbcsSa=M+Nd zH+AR}as%YHxseG$QLaMYCAX!1@QOAEfq-mm(<@j3nnuMkd8<>xp}V)LTGiSpfwl7N zc+hPifOh$Ry{%UOzCLuflW{xAd#MMYqRb3+`T`7v$ywh$|1FN{kl@SJ1mElzS)B*L zM)bULJE>oR15CMwqWkOo8kQ3*&|8p4yB5nS^oag>>b5UA%4Xb z0Fq17-?jE-gg}A0K6)QR?qA)VoBH|IUKj5MsUxfq=E#GlMoNp_!u>!|m6609Ag}OzscMevBVwWu-OVT+_Cbn7C-Av} z+5HlM$KNLVIXR3CexPcAtypX~tT!p(XmsW~k`aSsTB>G~_a9s#K?_;HDsFBi5(~`^gF%mdh*Du&5%c3N|rNZX9nf7uYw_7BAehKa~@dF223;8=JN;?)S+iWiTwF4y- zE+AVc-(&5wsFnwCd0Q6?6?S+`UgUkj$^;KU-53`qZWtbI^gkdwOAM1bGaArF)v$% z^)F%S*OG1Y-f$kfPjYoeyoFT63+q70%o*3Fc@WzoSY|Xd zzj9Zt2DXR*g$gk2-JM;3W+sqCijno7dIO+0?`oiNfPXWKQzD>f3 z5SH-9s}zjF$a&_nIF@ueXLER)CeL&f2+nu<%Fs;^154 zhdI|lJBgINUQoehn}Q2d$LTfubeen|lzRf9cOLYD4wg?p?J4mx-Wc-y%!qcNAxuFR zW)tW03(+x4iE_BxezctHQo7O@X54v(ERD~Lo1_8P(P}*;3Dmnlaip)F68rM~k%G3E zsP8OfNwAX=iuULkxNGk{m>jBwUwI-Sls;!uv8lWxVr;uT(DoY@_h4sz-`kfCa#bWw zW?dvY5TJgxh7|bX;k+wIq|_4!!CvFtu3^W%ZW78|DSH!2MnF`@49qI#Un`7%fZa5# zP}u;(nJH1X^{3m)dsU)G&AVt)?i-{6JjT<3u~}ST)9+L7bJAeBvBax~R1zyWEj(>U z0qowb-&{`awcrxC)WRS)9tx#M8v937$dGd=Lo&NhL;T3ffrj8woO{c8*!vK;9@OxCE z*+nrGCFLi}&gg%>vps3##j3;^`?0BFB@~LGX33UtKudXs00m`mUk@wH)#xZj8V=p5 zcl#1Q#bwX(n-Rd>WM}Vs9>YFrk{wd&qGxG|!K+z2IdaDIZI`j z)gJLL>Uv{5Ao?5JHvH?o-5;b~22;+YZZ3F?1|1X@aDwyxqC z|0t9=v6WKdJrLC~fug_vH{;78O33E?gLVu42W}v2PeMZ!yrVWD{x0EEyV~;EOmE?D zH%cG9wAlnY>AVE-k_||cOpQyCakEagPC`&PTU-y$^{(YfshN1ue5k42wKP04c7tr@E%4rKq&VcctcL5zaxs0*r0?8c+BDCozRRy*)J(s)c7W<0g z+gyn%^JT;@O3kc=l!l6sbhPpCQ-C59cKCyWnZ^td$g7yWtIluhI4rx>iE|s#I%{I} zN)wQMZ$^}fR|b7xA~{YH5ZMGmo^54s^1s_H`yc86$Y_nm!wq|AFj`|D=72i{ zpkrZIc73LuyG7IQPK+~9E-TnmKnoMnsnz}K1Tv-Q*;0G<{;o>l>bxA(dSNJv3xh<* z_UKrqz{09|@VZ{>MGnH2I!YXPPOJnMyMBTcXOqrLvsB-Vqryicc#8g31|thC|3o6p z^WEK&%SnT-G3eq{$stGy#Vp{x0LLe4LJaCcM?!lHH(57B;xVR^L(y?8B=R9=8`(Y> z9i}>%0Q(N%C5|~Xa#YEn?)h{rSAh~dxFjFUa(z4=zzaw-D>y3hXHBsZH3o?i_Pc-8 z+Pd-(b&!r~SCf#4%(_g#deMpbX>h<*M_XikQ$I5bzhVWx)8yTafG-gprCb^~chRLd zR2=@7N36U>t%UzL_^!EQHp!J^gV796pY7VH4@-#&pn#)r zhspGquc2MNT8Xg>NKu^jCU4suY&nBB8rP6jCLu^Y6BPJuQv`zHH#;|r;Y@J`cJ@!& zhDf~(1nj?iV(_#lP>1N%IUlYTq6i2rn)?Zbe`qKGrVW3gX=0;ScbgqQ3!v4Ry+?)C z9PafKX2k@vNN^wf+KA-*Bqepe0xARS@Du$GUY-LjvFXB~D2-?L%zbTix;Kef^Qq*T z(8pZyJqbN&27|EtqGj3N7V7FO^r-g67|?T2V|xc?8fy{|CM}~!dd6W)BO1L!Tkgc? zA%BZNzw#!o_HnBDW&=&`mwDbFIUT?WqXkOc$AMF;o;b3TH)S&VHXH-iWT# zCm1wiKO8&BYI0p+v;}AzP$G`hKHGbze$<)b{8$5K#u1`z=(t&L^isu61)&#ql_h8p5>qqN*dO1n`}tBE|MHKZeH3W z?uk8oN6~sF8r_Vlb#FmZv%BUonj;5f$qyJFM0$Z(uNfv{GvbFqPQV$9g$3AZWN+EF zq>CXIMG$O(xv4ayPB1F4Iz;iHhmQuFy#l5m+{^GwfC*vN9MoQz9Lc}R!1yB+oHRG~& zVCvT^Vg^O`Ul>L+EXiWM1FG1v?Y99t#)k?+k@@kW+KEw$3QNPqpJrLKa@e^kcHZDKb z3iGTurS^E|J}zaK_m%t=*c-4HpN(@DuiDhp?^9a5 z_W^!L?#W^-AVU*5hBHZuGS*-1eFEV7cFjTyV!`)y)V0djvx`xnI4P~&>9J@3U_w36 z!M4ovjq+YYGcfn5ZFa%8pp$Yl2NPPJrcc=b>lc3?sRhLGt`;ol{Vsx!7@5kERwV(< zcdzrrt52@TO#g(erp85J;syZv&N6B1)lQoSuGQcOh8FQ#^jse5VVnudYTi}&xFJO0 zFEpgNPEQ1lbO*ixVmE2seymWkJLt;aZDK|jp{=p$Nh7>wNwpe6x?fO}P-IJ`jxAxP ze*c1mP|yWvJ>@>Gjo>@_0p}j=tL9Np(WaS4jT*Yp0<;)}tK{(lRS$5?xX!8KUCXbL z-by6mte<`yd{>ZhCm16s6&DqSE4o2Mn~}+!(937`mGjocp0|)W7R78*K$+6T#CP1= zSHIkG-#9X=ClRPz4g%0qK2uU2Uk9)hiiNCQDsjIipg^vCm*oqQ@CbpS>unSg66tXl z4(_#!u4y&VVvcc^S5V~ie-F<|*$*P^U%{Whi%IP7THq%#_t=?tOGaLv<@SE1l8~dMj2>Ff+4jOn<*t^RPkaeh~)t7++t_O zU8U1~mwP!%bT$+KrL!BVpYWFd9pqF{so+>c81aO+?wjyztg_(VwLHEgK67C;(ucKW z7lW$96-=;vf04KHNo&{Udn|>?3)t<^iaBE|aZ#!-LApSohCZuix=;RM)e#d%mT5MI zui*XImP7>DixjS}uWUStp-x5jS9Id2whZ=Si$d)lGHO6IP$pi%osWKHA0du)$w|F`oJ@AOPI$02w!@Lb8ncN)$G?8EGAU&$!c-ah7jJHj zI3v%XljYM`S4g0bAePkLEUSdRdZv>sHi9;Qvn0QVe`1Rx^wUVXe70<1qb!?RDdIaO z4O9!!a=%K%jCDm)W0eBpej-DW=0h~Zd`I6L%?l&BSn>xXEs`<-Z|Wrp$A;b_WevXLG{AMO?Fn)ecY#Bwr*7 zQIbku-(WkQrikD*i-Gc6hImU*m4YN#lJB_OT3%Qm_6re3(>n`O3{!Ghv1eDm@Vj{3 zI!>gk$d!XPH3P*RU-d*^lqbZ#kmw(4pVw+hQXY@#i1gcquf9OKEu#B9-zmQeUR_|E zDqKQhNlSkZ1kfIM1aaxjh3r&qCzGui!IstP{hE*KrQu-Ms*fq$RmcBO@yynppGf{? z9CoD4kIF7agihb#tSiSMbo)t%*Uw3Ag-X^RdBf}^*S?K&9)SA+l56$iQAZFQU9^kk zvg=0kgYmv&73r%z{lsUv_&{kQMd-QMsj}9lt`W&iomn^d#I`B!shAt})ex$9N!?zI zv?9|8$-*>XGa)OSbdZPBvcSJ{GD!^w!(=7d{UFx4hM8>_`bkX2kDLhK&L zBrvGNCC1~QDZhIO23RB12$9ce@3v=uu%ahT2`?`*)!4AcTbDa9@STx`FHyDpn-`ZJ zOIfKkp7#ir*SQDIIu~9_k|? z2upqMgajJJLlySg5m}KFdKN3RWdk|-W-ZeAa#b;6sj1~ zL5ws�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

    PEy9>%!m=}fd^TulMLc}1O>4*q8YEzxt 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

    2|wTM~IgEL_Hm>6aja6efXm7Z#63k;USP#7iim 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

    k&%^Rtz(RBxVmbLSN6n!oG32 zaYQmBVT@euZvHFryq%{6a#_}0S@&n-eoeJ3k*mKY_Oi*Jpwbfc&Up;lWs8j_3u(V+ z@NUL!Gs2r)b&<6uvtG5$6`Z${A6sBCxp1)E4S}15g?YN_%{Kj#U;^i@>?M@B7mL@y#3eCF1n0- z!vmwFdD0Ef6EDIy{pFu{HT1PQy&-huOo(WviB)zyar@i9suc<3gt~N}U&5~^4O(zn zv|xk6eN;u7F5;K&W!(9a+NfAFlOI@_|ec=79X@mbT;Z9j$qyT52ZUtjOQ>%#LL8ZS@v5xRPaU zv~_!Jo+QV^QcK(1zOs6$2$RtgMg8yVPzjx2L#iAgYYexe5OcI=%1Iei)~(R#XUR{Q&nYa62FzY*r2Yxh%de$((u0iEM6r?D-mOzix`uaPKr07&w=L58E~!gPk5ufCsDfrOG*-im(ExbKyo?1re_f^Ifmk(>bQ%PcrM7U&rZsOdl-Gnfg~f z`vQhY+C0=+XQlyMX{=$dNI{cgJ%W~*+8;W;mnw0do^5zuNf2H^1zHNKG?*b4-_Wsl z%Z6PH-4MX4odrQzhGT`LO$ZY=U>ZL)RiQ0YOJj;QQT(Aqs!%bgnNL$LG1g)bjg`Xm zu$|Z3!0MEucs_AZN@Sh)bbxufqWa?N&8!wBbfJ+-(Yy_x#9Li##W{B>@ooNi2db#S zGICvGT-Q!~T_rt!)58rEs7wn2BqEizE^CTql#VWh!A0gc zg0}r=%Uc|B^{LFzzA&XU&h{py@qomUbCuz`+KLP9uVGq2=pPex##>Y+fjIxHP#o}7u0EO7g>RSG!NmKAIh6YI|9iU0Az#j+yf+{V53*ws-2Y3l?<;uw0=R+oke4(O z|HNkewY}>Ow z^=vhB_Peg#l(N92;E2`82+d{{(R_FH%2UW@Qg<>~iqYApcFB4D)uZslB-fQ-Ns3S4 z4P|X|d+2Ltl3%dZV4bozS@`#F?<)>ow4cttY@&U$ZQzWZ!}C;aDj9{XWZ;9EZg(V6 zLO)zgE{eE9fpdowa*TbzE$Y zwgIN@8N&ObIEqda+bk3~>aR6lMkTjtQAZt8C$Mw9n*$KqB_f*P2Fh>pctP@~Tg9{I z(?omt_9-VkcVRZJzg21(A!1mJF8>nEA?k0JXLNF-7`Ke*R0&nZuime7K+cvzR^09u z8IhB1-vC~1VnB~lY?-r_Urnl3UpG9-C|PnoOqe>RL_BJaQ1Dl|q@q_1MOL7_mj$C|Zs_vBx~Z{jM_dfPN!i8Ty9q&Rr;i;nIJ4d_rzU@m zn|HKE>Uj^lkpF}d;V2_clGqFcA_N-gdkTJ$g3IT(7f2@*?VL4d)HHbKRqHiBK_}ZC z?GnKcPg~5})_1|ib^FrWeVj?z;>{U0^3(sM0y=}( z|E<)XfdC5ghA|;qM83;~lIu&=4XiXDF54(9_AEpO{{Ek~>-R}3>6w)K827&G`R)8g zhg3pf^5Mi;D&LGOoBg&ytA_bwT$32t2(wys&EXrCtbb{)&Uso{#+B32QDmYXI;laH zswK+*CY!ke-j4nWyuMdR9OE;c(MPSu8CG>p$~f)}%5AO=gK*aD&E?cde$!%4jdt6y zwZbPU2had2{2V(@?1y=D#UTp}ffY@lggR}tc%3IT1}L4f*m)*(jBa8t2xcxUw@x?A zdG(IVvCko9)~8Hl<%#|0q^*mYC2|SEfSX|u(Osl5l`cL zw0U1?TQQEfthxDAn^D@zHpZaM-*D>`DK4)XUl4Lu`D~9rjCtILz4BCe>{x{w6A2cY z5F83rIl}iFGq#}mntED>gM4#u^i+x1Jb%KKV#4^;3oaW@o+3}KlCE6zjV+wPvrQ!zNayC{h*CqS6+Z`FoLf056Q1RSKN7L z|6t5u<`9&dZVUekt1gc5dALTXKz1}TYp;^+4>QQ%#nFI{M^Dsv-6$>fQj;0rN{Oe< zQGM?Ch#eDUeO`wLXYguIV37dZaft!56#_9d{ghwS7~%xCh6dPEw5TDY)M>Z(e(`w2 zZW>Uq#{AVglbuQWu6z^rk1ejTo^LA4+q2R_3RU zNP^75$G(yzE=VEgrNCOu3;Q(S+em1a_JY(0n*Y5}p4^WZ51rv}l25ReAAID_5R)9j z=K!9MfE{0tX#5Ld>p=k7Uvyg(!A=ZU6s`@fi-v}7{Kl8M8*f{E_|Cwr9ryxjEuoo2 z`{*Fw_Drt*3ZKTJD~vYbj-FG`Kik^Ksuq{Df5?T|(D~&Ao1(tH+9^>m;=^&S#^1@i z`meM6R^QspEM*@_qq4<%kkwu3cvnm$VQOp!4u|UJwuHy9iP)bM3nOwR>GGKz6x7`7 z*b5sM#2NKm2|N?YofO*3{b?HI1+#uqL;78=1@=%WEI%Lg+CupO_2K4x*`5`yZAzVF z%$7oV4>=ux*KyZ(`8-*^O`hXemF10cIB50tSh;hyu^}iN@}&O}?d?hD2+1g1l@C6~ja5P(u?COf}g8g7^GERZCKC|I_u_j@j&xdH z$Dg=Td-t0$#SwE~IOKNE{-j5>lyigE5mRcrRhwR`|K4CZspb!# z?U@5f?`)a!wUsC+#!ufZL`y01n@01p^?EWn`2xWUKiZ272RF7YO-b+-52W8n?wX0mbqxCH59iUTEmn(x+Bwl^qsllFx zaQ@!EieTUF6K|r8`sYbSv-b0Xi-+mJD%O!p^22kL=u}+OUh^$y3EXFO?2doo_+Ow| z&iSFOhDA+)SEQ_php+DcrJw+y`fA^1%s}H&%^PQX3$erF8J`aTk0HWZuTGo~ z>jp82<<&{8??BBQX}$w+x=Re?6)#H+4(QPci;=#Dq~FA~!OTFkb2FSZ10%nld4d^FN3#d zGl-mw?U_-TdgLT3-I)+?t`QYR$<3V<%Eza&f3#v1kPKGlVEqQ8Fu9al`+c*JSYuKw zD|@)AaERo)Sgc}>SdA|$ARTV9S9HoKgHlMbrIrhrpU#B{1wc%Ni=Qpp= ziIn=sV~ZZreY%ro+2dB!mF*1K{fv0{(59*M`l5wb?8xzRa-OV(46IvWsb(ysR2@kG zpV4OzD_sd8BNU&CaOxSVUHw1@#oW{y;V;-F@~>7`@42lZnd|%8PpI!5lXph4px&Cg z^_7fY;Fe}V#&M|yzp0}-uS*qGt;xl0RU8ZN>xyP5?vsE?6$y*uj*RAT@Y9CUL>0(yrHsV`8amRnFX=vY^T3%#Y2nX1@}h zpSDr#+w|sLg1@+ogfkaGyT;yj*bZDyEnf(zAJ45xT8@=?wGMp~VOgtAHqC-;(3E0pb0kGzfD0)>Htw$X>c9vrLK(e(S^XEru4)bEBB5mRdf4e_Lw#hth2Y$N{YN3T8q( z?q}$>g;B?M0vjSrEjcka7JFGOyb0U}@A5@PgAGEB19BSAg5CnKc38}WJ`Iy*9?UZS z+&MMP&nAw&eyHie`M`T6Z^AP1^6Dg3e2A`n`+4*B{~ri1Y|!C`%AH}g|DR{{zm&GG zGCB!vUz%)SulfM5Caux}rlrR6IL7E67e6O>*n(;$$k(%+a3;x>IqMj% zalvL*G?CMI_w@_p&-*jz#VeDs$#n-kCE`2oz2W*&yVX`i5q0AU=Nf)?3`@d2&825+ z!1`OU7s{7w(kfGPL)$sblq#pJQ7OQnfz@(`?v9gf&wiqV73M&@mCW}PtnDuuI9*I8 zF3~Kw>JyDbEa#8-O(_6m+EOKx<;`9v?r1=$fOLaPWquy2Ze=-b{N2nH#shqtWwoD6 zy`*KMyneDgx0urIh}N+a$N6vMb^1BeT!ORgLqj!Kk*&$IFhQP&r_%{*EG|N-*dF{WH_{( z{#(uevgS74R4{_T)LDl9%*&@tv}wpIThEs^92_-6$;B~`kZJ8wtcPs^bvkyq;t*j4 zOxL})ya3lWtAuO`Q}?L~1BcCa&J5^+J#>El$uY4bqA+SbrPv$3V+35+qmyv_xMRNI z;q3_nFNrWL*|&y$IN}=J+&i$S%>p|Y-pt8_ELMyqL#0_pDq;CWt>@+_?b78w>d0-C zTF(2rW|zR0!cnA8u{U({E^Lzmkx@f?C~FbF>14W5)l|EqhF90hFFnq8VkmvYc%q~T zT?VQ)7CyL9b$puE<)6KGHl<-E(JdIh<%+A`n@}_{VQ5Pnx(=$Sb@3*tP$|0hFb01l zVGxWcE_;cuF+&-^Uf5A>{03dLdpGHH9i znvSbgI6B(Zz=`9RjA?3*Yk{MCL1lzf4jiNxR#*%xB`Nz9dJN%wI@1cv1p7o}D(Ca6 z66ZH;%ok&_86GZ=kmi`KpUC^=!&Jfx%?A70cc)`}u#rD)BjrZ0X61Jh zmT`9}UhYeXI$q8ACb0DZps2f>o8F#MqChj>0@!}DDOVm~N);h(HuMn-o-d2bEO0^N z^41>8;FW3%(ujIZa6L}3;PwvY;33d**}}QI3)81v0M>9~p--(6MpciP8)tU~^NlW< zpf{CSN}}DtT-`}&=i8P>!LJ>++CCFvPyp5c9WBUPfXOF5lrQxT>{(ASc#XNFJRd-> zjIK>s@PB_V!#ukc#iq)e_jGAkmQh>4xz|?I2aa#8t-%LygL@e1Zx1vXA!hSN>0fqb zrB1fW*?8f$m61JZlur{`iY;-Rqt=65^QQK?d-}XS>8%?Dhcnsj0*4EGEg7*Bom}~u#LHw2P?|R98BhJxCk6bUTHKDGVfU9vnWeh=^&}hI3%`?M%KpMJFQYf zhf)tTYrc@g7mh-oq<{!xIThm#QC$X(32`sF#%U)?7gv^h0LjB#ZNNVtR zpYlYPxa9lysY!z3cG3hxaBQSv*?$S1lTV!g_YeW>?{Xz3+V;cQ_hH)i1Dv6@cT(QI z>ewKC5WyKlZTd*LOzA9fu zMLSatAayIg9Spb4!f zHs4W*SGAg}$}GC)FPGG!-ZTe<6w2w$?@~0%`Ol)Y$BGUMp<)5?rP#1l(9gc4)Zw^{ zrx=xZptTH>AJDs$>S=x*;hp~-=ws1(K8Z5*KcCV}l(}p%Ivj47<{%^Q8D)1Fb#gXT zRMKQsglpPqx&#$_Wx!dD|}4lto*E&DsVug+k=C8T`ei7sRB1xa#bUkSSxDgU{{e9}o>m zc6jOj)3u3{Dd2uewe543l3m#n%H95r^6jSCjo%Vh`%^MxK0KYP6#I&QanH^;{53D5 zN*Ub%tg@G4r<}6iaJ^F|ue@q5+^2p+eqn|9w#9LYN$RJi_d}tUn^KiRZU#*HZ^BYwp)n56PdB!MW*itWMt%$Z%BKv~M4( zS!QgeAvvca~0NOJUw zrf{EJ{PAoH-)g)l!KCYTupAT8L<xxcOVI=$wJPbY)NJ%W~69kK99pN zMrHS1*$zSZ%C!tOd@#rtKNPD5Xel>MgyUw;6x*$}bGCVy^>>vR(aR{{B=cgyy*0hm zoiw3)H0NV?u-JPzl$?+(mcZtfN%UMvWl^bh?xe#Ts1p3gavygnog@Oczjgcxdh5*6 z&$D{Vl*=F3EnLJ}0Jmdm^PYPnJ#Qm6>(h1<8pX$$hqxLtWvF^k$FS+Og!h;#V@Yt6 z#$*&mdgSH(z^(6w>sV+p%U$@eBWcE)FS1QMg|keU)a}fkz-lr@nD)WEk=+jz6ezZ< zesq$Xq6of6lVhKy8ZjDu2BR{gM(ce6D?zxcmQJ{4G`@O3_ts?dtn=hCls?~rdUsCg-KfkLH8*y5j83v^;I57hEG%P3|Nqt>^|4Dl)FZKKU ziStIYkETJQIROjT*q!*@KtpH)v81)uSl=HJOr;;iqdwS5>~rSjP1gw?z}3del_N-h z78Iw4B*iAUr)Oaq&feG zujm$dwUPJ4pPYu6qIDK}u8t}Q7<-WN19bny(ID?=4AUlD=zh4eW4P2LY%m3lhzMkt z28jDnR<|HDJB$fsqVL&EPA$LwN|fD&0lqJ?E0K>Bu~S99$3rpqP8<-2L!=n;<#ib4 z67$*5Er{Cb=9_h{f?=eFrKC#yfA|WJIX?gA&%jH-rT^p%tiu2W4HR7uHZ~0-SCk8W zZak1d3<@4%$i+W;=)bZ&0_~xN{H^YgwZ|C0UI2>ER2q6e@qYF!Z7a27*2_+13A5HA za;mK}Qo7AN_|yE`DRsxv^Tg~OaQ9bb4xF*jz-(e5a6?3+_?S7bA#3(ma8u_`Kki@i znvTxRwWoUilr2Sn^fARhnK1U`AW|b*smj%cFACzfynH@-yBCj_v?oxIx0?Z*77rEL zAbG=uzsrj?7sOauRyt}u>Q_edb8o=J%sE9!V_~W-ciJM{AqODAm?|GibVJoml{|r4 z9RV&M%|Z)elg9YvOD!Yw@!d5RPUWF3ywdU>hJ@IGhLytx&g^w{T79cxe)pF%Jt-et zEVXrJEP~0B=DkSX>=#Jx7n8@#&JCSfePR4_o`oOAu1Rt&Vf`&}Q&_GMgnTVJPZ)-` zOx#LR2tT-I(GnlYq#5eFU}9^Pse4>d@y5YH0Wc13)l(NOC>NF9emFfO5T9O-$>sAp z4TjM;^EADU+JorwA}W)j~p?`LBnTgKYAQSdtCVkjX`NZo9NLpKo<{j@-*@1Mt!-XU9N5?IJ(K zD~Da#*fSVYD_*pd@C4?2NF$GIF^Cy6QP3WsIawXM@^CoRU}Wh6IcS)O5*NFim6|qj zr0!|@0~>|gjAiWX^**%VbmdCkWb-F4lr9hNoGmrHdR^lALk8UJlJCoqi<+274^tzq zC3uRwC(b@uEgFi!>Io&b5X~D6JasEDJgaRk!zy&6X=g|APsISL2{kRcr%350q*7EZ z>{f8Xu~&gcfh#nf6UGt)-f0t9VM!pn$f8Cr_JJrYF-s{QBs}vIi)3*45c?mVga7x{ z(A*89FMOY_{R?k+g`}r*pm{3Z)Nim(>+?-o9eBq*_&dj?zr6$J9mWd!o5cM-eO0qv zhKZ+>H@2>NP!NBkA01Gb>U#N`pZmQ(HthqgMT(EI(O-^!7>21n-#aLE%JQFeggSfI&4Pckn9OW(g8Xx? zMq@`4oo!CiJ^S&DIxqN6V&r)8Aw>NuXIIOIXYLm)wVs&H`iXLaY0Z~)U9znwvH`4A zq*x60adh=sc_$C!s@&k{JA5WsV?$+lZ=Y_P6^z-YVsg*o=HZoY-8vFeQx^m&>MgzZ zYpCH^JRnZ$1+<(^4l=34T3cIbpFZrLk-Vm!o?iW;r}T2Qy+B%gKl2ND=aeTt+J!HC zSXCER;H9{5Z0$a$1getVIUso-7VfJ)$|Eg|k3E`k6Ja{3<1uDxNW>Js`cB}&>V{*? zdq%j%8Ic)DVJ9tIcqU~UdutO9D;ivCg#}TlIM3a(0&F6)bM=ANNipA$m8RlD2W3l< z+GoTY=7*lWrG?^-y$w$D0Xy;Lg4inS=mcSlKn0d+n_lB$qK`57L2Xj{TKa&U&!-yS zaN6zZmSCSVEm-Od5sY&2zlGmWht)LZ+@K!x$@i%lDAkU2$t&#jgW=#{>OGdsiMTCKO8Xj0mt3Pr6d6rtiK5 zk&EN`i=NJ6TPoB`oka@j%foFWrj*)RAOnXiT5FX`+ue^drlQ}+*<+sC61rVm8g=`} z5-Ul=3AFj5>GxAPkrJ}P_B0p7?VSpz)MtyJ6km_HB)^8kP&cs&>1T@z{AdvwRHWc9 zRf{gXOHdk}^}D}?2~n%ZEZc*uf9KUDBf|eZt^Qw)5WElI9NrRm&5MrsC-9&Ba1-`t zM89W_=NiD{66CXStF$Rc8RH~1>e>&$XXebLHE{z%ind-)2LQObPrzHdB;{YTR$$o zOHzjj=5P`Z3CyZT`}BO*PHr$6%J7TjHRR90gPvEyt3-!9g!slPYwIK~-nM)zcHs-I zJ090FZ$T>}54_}BC->AycVcV$Av>F+~JXQ&yv?`k?dku`}K{cI&QBKs6dGbH>N9`Q0M@GN=l}Na7XK2SENx1oQX*Ym4DbOeH>IRzd zK}4M}V69!0w=uimaACqYzNq7}o8#AJqB_~QuuDmv7d#coyI3{b3KhTYt0gUYu;?-y z*>uXE{CnfpEpInHf+BgjQhC^_HovmUxxc1RuvU=Tvq#jhA_JK*UElJ47<<&EXRv_V zuOuiDr5;V^XwT4t|!E5f88 zs%!M8ff<>*2f)bdr@ab6*Z~ax) z_q88?i?Qv)6F=7aHIhwh0?)lkI0ZF=wS&kfOt*OUwch+E(}2XMSVqQY2MMmIAu|nT zz{&3<-Ho|{*)8!d*VqX=ly!+-xJF)jF=%C7$&tL_RG;UyYz|7x@*#~5L-2GKjUT+2 zA8@nAbF!g6I43x2?^6P}cjQ|(i=3f7Kc6S+ zBAD_YYUMje`yAK7C!SXaMyPreUVC;hq>c?j6B27A^~Sor_wAb+R)c2Z+b2&pXiD4- zLGimsFKFwX-*85gmjYi_@1`d-zFD4loVcgV{fJT&+hAFIy32=qvO*z;F!^)Wv^Le)f+m&C4l0DXIq!cO8alLf36^{TjGlufyQ#$NPM>===X{NwX_jt-bzl^5LHf|95MrjB@ho7KO(Z z$QR5bh3np5qkdcKWBheqX%v67mfkXD%KGqpOz4XcIR7V%wsJg5C(7-tvaw2fubo{u zN9)zJBrsL`!CBZ?O{M9600DO7=bL$wvnX%2JE!8I=*g;=Jg26);RNi@~S7!Kj3sh^jO zGQ=;2O7boF%r!AuOHUFyW@*R$lPI3h#7(|+*G@z^$L911Dv-pN(z05N&u=uZPZU3I zAUJ-lf~#+2NTBl61t|!NbmoZ#zMh*k?8H;p!PlJTQtj1Q4nEoZnWU3!YsL@ra(%AJI`RK}x_JrSBKu-hU?>iYZ?i(MN_a)4n4TeJr7=my)EIKF5|`!n$xdg)oLHZL*Gw*6h6?3;tr}SS zSK*Lo43eYoez!;Ia~Xw|=ANCH^#Kt1tjozKF&oz?zAxx*AFAKs7`T`T^)((`+amkH za4C3?Jxm+acITPBSGquIRd;C6s1xZb8R-)Gym?Sf_8P0_f#THfO`eyxt)(O+l2asx z+D>n5O{Dan8A1gRFGs!^LMQ_m9os*CPIx6?>)ed?`sVAttI_UGZv`2Jw7o6#agJKX4`&pR zCl%^j!W+P686Q?yGeu}A*59v>6|zPxW5kAO{Iqzvb_NzzXvT{?{lDcmiOl4@k!D`0 z`fZ$Tvrr%!uNsUy;7@K`@)c3(c(Vn&=tsGTxn17-m#fdC-0}4-3Zp*6)iAee)?jC; z#)N{E6-=k464~uRV_lj*mDuciF#wQ#8>n#FXDg-(WP_#uz#UZ%GY%+%L^)P*s}@)z zF~mST?#yVtw%&CX#`WLP`#0Q`x&IyR^ZVq|#vO0*Td^d}@);ufpV981AeqvXRjp!0 zP!006jB1kdBC@*D_=R*fSr#t#%RO^DTR=a!P`Q%4{aKMj&nmlrvu-Y-J?|)qZ@h1I^>fM z?~E;N8EgRT{jw}7wq0hnCL`<8_KtNxTn@ewa*k4+e53-dXZ0m3&{c7P^KtzYbQ90Q zyoLX~C~q8xxX0bZ;MvtN2w?avS?_wAh)b63-2FRH`+G2Axx{J)>14L~heM-w9K+1P zPwARkdZ0rQ+R8h0&v7-xR9plWlJ`f>*+V#RswYy!{#= zL&zMEoHZRBwSdjMnFUgz!AdqoiJHudBcxCHQ_=m&B@fHOvAf#3eO~ISM&wW7=M`z+ zNxH=g`$t9I1_lCxf`Zo08&xaQ(+#*vONykSTJd`ari^w6MF#1$H`CE3Yl;2cEWjN+Ne{f{)gM$@eK2KN>~QivOf+_(?0>N_zoK&(WQI zb>92mW1!wae-EktpSH|vRZss2b=ILG;fA!V^EUbScPf->bKKg%D6UU3Tdd~kwrkAt$5e%ZwxdbxVc>=tYqYL$LA zy55UdYer9_`=A$D{|88m4=%;Ywo~|t*r9m6FtN>8-DFZ6mvk&yiAj5?qHaoK^nZIyIuZ zT)x=!kg)$rBaOfza2HP>PbokD5mK}w`-r@h7rsD;K9!0 zi>YYHasHH{#NcLK752k>Hl@z47}4l@!T^1qMI$qP@TzEhyL^QSggx`L@|;p$Bd7ZZDq;-?8c0f9F#F>p#NQemB1brB-W| zBNB%Jhu1IUeGIrHC4(K5IHZ;MEY-?;N)lxMr{lOToJpa4X0M_|y>WMo%HtX&0F#;f zd_xYgdzf5(Uo7Y`aImwm(y4;M7k zS#wVeBoTUyMlDPyk!Qg3_Zf_Eb+OUTY~yQ=G(}>-n8v(Ox@9v1+teM9IoGcHvvYvT zJ$vuBV83HM5g*Myd&Vkyx&ZmG)p9Dii-AWoHRQ^SK(U0FQ_{O`A~iZZc%;hUB797q zx1Sutt{z@xiom0Rxs}vBY_AYVnG-@>kC|Jv-)ZM+f}2waqm=4or^(LtQ)ZN0=x&hI zw~nX|N$eA2NYA(x9#H+=-ag<2wY@C6?68pf#C~ICA#t>q8+A5A?KUh*)0>!>j5 zsbs%gxN-DzoMH#Eh~n7so8JkxvUnd(KGk%6uzgm+GWyZB zzt!ik-C_y-vBq#O3D9y*rf@ZlQ^ z1CEP~i<~ za!9!He{I9~59h@$82R|A?iWCi#t2@FJ`TO`Yc^^vZXI~s9K#& zfn{uoKMmpxTci$XwH-w#nYK`l?NlWz{t$FscZp5+k8~9;=X_9 ztMuy7brNfpjM5)j9ZH?t^^))~-+sq)J|*q+sVeG|cxoSu2h#4m$=vJ6l?gghRR6Lu zqq|WOyRHRE6Epm@kRVO?bBA2I2!QU`aQ%2yQI+rNdYe|zx$9Y+eg~(B;zzm)hDwvh z-l1^D0d$P-#j>|T5s8ekbRCJBY}<60t^c{;grbmL>n(uLVC@+7Qa_x3-!!AhX4d{8 zP@^N}PmR9%n<9!+q$Z@=+kdyxv+46L={OXa1nW3uE_@g1yIV$nz%)rZ4DVxK4-4`X zU668mEJVBlVcA`IZR|PfA@8R<>J^b9rZ%?qwmH#bdm(9jEMY60F&3Me`wCmp(nG=5 zeYNE6c|=1#_+uLNNB{c}Sv?N23U4T}ELo5mS>RK<%81eudg$3kU(Ev&sKx3N(hft7y0P9u9R5{HF-y3L!vU^g5WHu<^aWtdbLl;-H&NK& z5sdy_22~D-L6PkI4$<(KI2+gEs(HGnysj#ela7sf`$^JRRMM)l{ zIK15a8aPOIc(wVB|KLC1%D+XW`0oH^%#b|#vg@~hn20;K{uu>b+UoAV_)79Y56aZJ zOkxt7tXpre_SBpcYeC!A*lC*gXXKk*e6N+GPEZAZ))hWj%%4c-mAwpLgGR@~O9K;I z2Fq0UrA-z6kb5|WCk{%KE*~)lEk#(dmK`o3GNI^ zCwSuL#&!*Gy0reH^=3-HA<6&F4?%z7bm@UJO=Xa_nbx_$F09QxAG1XpYM4#nubbvcK%VC zE@f~>VVt|)-LEDa`Owik{!`7GH^T_6YNnw2*!5kPbeqIQua#sz2=Ld{Hi+?_6XmEU zAq}B0<}FvEFba|B=N!`l^ep>o=3h^qMxt&J47;&B0KFRfv z&}ZEL;Esr?#79G`!ZfJDA;}EJDAoioSH8MfY~;?5ns6bT5Nk@0hUH5na*if9?q|fmLa8I7R&%6G z)Ld%J_bkynVJX>wHFx7vt&Ft1)!zM^`O|f8*EvLf0Wfj6`P2h6j~D>KvyHDgOqOs} zfhrs|v#m~e<`{YAz_9V)oyPwIL;U}piupHr(7!BPIr!`2-wpDM_%q=2a{l8hkW)q% zO*G@yts`N6&Vc4<yuY(wCYM^BSJG6OO!6?< z1?!W>JPgC`JAR1AGL|oXjOydgiD)HVB2o!wxk9;jeM>GAT+RVX)|Yj(4}x6&YJaOG zt6na_&=@EIcHC0Y+S0S%=9TUE)4szXCpYoB4(wp;vHi9~M{BE{Jm9?J4@0i*NDmYu{t~0>q=Hoqvt&y1&d1Dp%hdNuiGj?C#@QC~F%4ZYg7{$mw8|s!c{bju5&nynN zN}ss=e4mTon@EP%iSfJxjLuHdjE2*WHI`7_uAcoz`pwKzZ7bz`TGgeMuUQ&1uq!G3 z3d<&^!wi*s4aYjW88^>X?0F{l#6I=JAp-5ALZ7(9%j%_fa-ZF!Dn@(weYH7QZ`}SE zhRDXeYZL_wg*#}Nh4mRpzGYyJeO_2H!?RQJo-NhH;rYOl6PMU@D9$*fUz7pVw&e2+ zTZH2dF@8p%I{|fF{w@9K=xYVQVY4sGb$FCqF)&p6OgiHJo$*k8F0+;-F}6;e(@}A*eAM+3s1@5^M1%=qY9vGd3_gMw)GO z-NH5c%s^Bd66*6dKS2-DH_uRhGXr2rRk1jQ`Z!goQHAEwXqL$U-N=TkoxqE!rBdEu>s#3bO2b%UG+_?ZdUD;-ee5v*LLc6 z+>~xiGb6EQ81Rd^alWN%(la3YO@QMcumjDBw`TrxVF{B_FyVWu6lp;=exmT*u?(KH zB5G)1r%>*;&2HGDO&xDtE^&EPUZ`?G-mRuW#Ov1z1E>I+fU@>hNwP3jw5}CiEv5&g zzNj=3@l4R$f7%*Wsz2Fq^fTEZiS(UYm<@4wTJ?IVN9|qbYzB9*TOUuQ_Vg3-d)RxE zRAz1_4v|>%O)~qqhj{iu8p=Gd0i+>ZYlEzoWMm$z@Y8~1oN1j)GV|5k;(4}OTpgY< z4c2>L-e@Qjey^NCwb*>w3pP|P)sS}vPD^Dz7`QUF?N?>qxs8sB#hq7e{3@_7Ia&X> z%;l?&aoC3YJWJQKKP9PA2n>Yno;Q~s595CxFzL2e0J$wo@{f&p!e|-Lv6SKL4-)df z8SBP7w`p!z_EvHZblm+ymeY_ciK4emh|#nR7{YesLmTa*`s-vJOt#o-XYaqj4j4Tj zk;IsyybNG@X=NOZb~JfOQp`(!>;$$GY}aKu*88q+$^dCnnQ(ue*r;#R&!p4tt1q;N z(0^=B5@K74O!Q6TfH}`PoPMujvnlX+mlUeRac(>la-EdHEwI12)cx2a@c2kLT6nF- zu@bGMBz+zfo0vaal9)J2C)UAMy)s;%N4vg&`Q%6Y6aXa3 zC>zUYCCV8a%K^YWO2i&5aGw#e4`6PkFlwbtYo(fLrDgt2XY`vf?KjiRZvg8Yg~^SEoT*@wcjLH~lgN4{Pk-OI@sw zzy9F%93QUbD4r{Jd`gs~0ZQI$9hwdbT?)j@byxFP{6WVYI5hgpk%nd~bWz$_2-PMu zpv+URa0-J5;wy0PhGzKkj_bT@rgUAKe55kJ;7yH~?3crKbi9rXbfzv(Xu-94O6Gi_ zI@NEjSp=Pfud+(4716u`=0KgDGiRFoLlfDQt(XFM zf6ijiakX8h;Fz zOEuHWIc?^}4~4`6meJykLf%P5^rGX)1QB7y(Mcg4mQ#TdJ8hdJ?S?h3c-*%tlmj%o za4AHWC}^w$wd9&SlR_1|O6Se5H2*{4pAlW9JmB%614JlqAQ9zG;L%!~ZNmEty1ypl zyc^6nm)=HgN~v%n8}x)!obC$oUF+=!Q}u>YS%uLkXFFK-)-V|MU~UWYy}Da8OaG%( z1lb!;yAiT>EwH`zeWYx<68M{cpJXfyF}0XyrkCteK>{p4^`7gxDkpv4)w4+nqzWyN zYTKeU=cC;}YY)52C*OB{v$L2bherU}_n1HG7vFoxpGMUTiKC()y3V?@?_w$Q^rOeD zU`)6%LW(p0CVDHHZ9LG=-5iEYs;hg`9ek)KG~PZL<+t+^xA?AvjUPwQIRf=O-5(4Ywu2Q;&{JaI&P**%mn9OlCiX{t|7S0Bza}fD*&UK# z|K4?2F;y#{WSdA+8&Lc;?b_Gowk6uvd~4T8KiOQvVsw-4e{>6)-7rewu^OFWvpZ%; zVAJI2@#7P@jtYq`j(w4VFR+_OGTX;6Tu0Fty8MJ~yu@mMI4>%`&+U=V6-zb2c_I@{ zkwL@AqMGg|SuZLlRpwDp#`B_ZS6{am{1buqu{Pe+nYrg~PI@?vrvN+f>FnNZsm;4- zn9Wis@6Ok|{aSh%HunTuqRhc@?-m`}`CCCTwy-erccW}T%o?nDU1{uxx~$MEB5%t@!5a=+(mGq@?5I6B+GHlgi@9gjEBnAfP%U!sE6=n1_oN zXiU9iD&y{Iy;WABrjS;=cRKlv`#Ok=~CgmARBUkBS*6%L?7 z6F;=!3AQ}@{rO7`q6N-iebCsmADlXsn=@YXN5f8!kYzUZjXBhAxM8|*C(yLIldRt^ zTG*AEWA2DaKvm}y85juH=61`CQTDjShxLR)vgESISp{-;WYq;d5Zf|8ZmmREqr8`k z6;P1HzP#^?{7<&L3*X^9-^5a+NR};qeF|U}YwViV^y;!XS=*DWREYC4+gjGwYz~&J z@@)xaV)OWhHy{6=q+rDxy8qNqaf-gGgY14Vw+{W;9Vs+bj@t6K2Kt^FPg~B)prmR$ zB#)BojwA7Di@qz({#zj;n~O}FEFvx*QSN#8Mz`^ZSbcx%5@q7E*f0}e+}=c|k>KKX z1K4SUAUG5;FtY2~g4@K+jEE!P)#tAh32F#UlxMTiusM##xtO4Zpj8cZp4io>=$uQ2 z9wkHJuyw+L82E_+Zor)drP=~c$JR#^y`xrN1Rp$4JSYf0%}+cnr+}1E5zFWxC#BmP@+H@%pNwvTwQtKDq{4kI20rX~qWtG>-A6rm>$&DUwRm=* znE0ZCxR*6m`lDu^#BeQ!&y=_>MuP#oT%~G3%xJm4_^3+<4Z4YNZPebt%WsVQG;lT5 zmYR)f%-I0+IW;@z4;nz{5>H)^vP3_L!_LS^w(^GDM(((m?|kUjcvs`jc~SsWwe-97 zIwcs0iE{er>=4(ZqM30O0%#mhKZNK-kL#K|Qe1{U_86}WFSSO9x$1+*K66i8A^ruc zpr$b9VjGApUHg*|Slwxp!Sz`jD{;~xG`*_@3_G2(?R_;X^%q~O~b(emu} z=sPlh#0Bi%_i+g3wb#Y8uy0d6WJC0C#1{3RzB>670y5v-4CGU{+NE-F+RAAr;eWCc zScgcfqI3X-G*WUW$Dxol2@9!YL7E6`v*Wf8b97-%1=AUZrMo~6Uk`WIpwElL)x8xUt7K#)@60pFp-(OX2(3m@OqEST~T zX+QDQ7*c>uPHf;KY5Wh@CX^X-RI_umtn+jx^NbnuOtbTVzsdTG^xzGjHzI44ESVf` z9caF^^Y#ay(nLbYO{K^%sdRfqlC^TFFFwzAqUEmld%H-&i_J;t19(=q->=#Wb46c%%0!K3PGVIDsOwNo4+V+mMbTKV#oh}Gidb8^nF5P=j z;Z-Rq>JuT!?Aga4^fV9eRtZPdZ6^Ur2f>*t27 z!R}iE>CV^C$8i1y>|0%y$lzw`F6C4blo!n@7T@WTlaPR(pTYudygxP;K{s_6`^8^SK%QTFKTe`}`0HT=HmqH)w z{zdFS3-+||A4S~;s@!_vV$mK`meH4T7uVCr!{72N`n^QJc!;jG>Sm{pVZX`5kBs~RD}QVBPhGHm*#<3m?t-Mt4aCMH*XZV}&Zipgbg9&3Og zFMWep$pBldWf<1BK&ISLf7 zfLtM`(OIA!+=cxsSg*L^}HM83s`aBkic|Y4<%4Hzjj)( zSndZ=3-SIu>?{BNf)y&q>?~DMz-*a0`TnHF><7e$Dwv3@@$~m%+5MY^bEBG3T#cl7 zyhFork%qw!_fGNY#Sk~w&Yv<_v(wR8HqAF&%~sb19J2D7+&>i`)IgEEP?*fKC{bPm zKhRI!Ye)2pu`4}y_-nQH4PuMklf(53=)QIkx2IHhpBuU=DwKb86+3ZHi7qC(X4>#O ze2u8D(3wpI$(I7n!82vbHDvN|zn#X%;vR{mUp=wzLGGu~_uN}H4AfnJ-^06WOFu@G z&=FT}X#hI3=F550w?Gy3qBVzc+rJBhqoVdH;#J)$3|?`+GnVqua(hv)WMnrplZJSB zH(ux4v~k?=4Fz`#InUmx-5k~0o@W~n-oE@njGzyCkBhUn!OL@V9WjHZaJQi1hR?> zb$RBeJjL{Cb=Ik}0hn#e%R0VJc6bn14F14zMY?u|PA%fJHV`|`x|1=t zna2A-gLtk;BpPf^oH#`ro$1sorHs(4%IjRP5%_eskwsfD=J_O*{?eyDE+V z0|+?g0Zp1^b>3x7TD&?R-kkEtl=0{z$B~)H(IO>zj*&diL0%9c2T*u@raJyi=krM2j^&!h%YFRf1B|=` z)Jma1ug@+=?`Y49uY@^EB}Oj4P|B#fpniu{^SiPQw0Ru`wvdjQYsmJY(PB=4gCsk| zc9p@Vr^)gQW;Xe2JdD|-KOOM{ww6IGy^emv_&c7S+Hpuq)w{|n9=AopSAJ;PyC*PV zpN8~<8$tkL6_$wT^MI{HITr+@bY(Kg5A^Kh%ldrWzRaX@ z6-6l7aY2q~5~-qBCmg>9Z>`uoh1aS6==KA5uU%Nv@mwBy%Q6%t!E%~cz)SC)iDFWb zJ+%^fHvR(d7-*_{HD3r?9JyyNwHsS56-qe?WVIOD7?Dw@{Doy&nMx(PUb`RAFFcLKj~&vKs`U`{4Vj8 zw7B)y67T@>K7@lKH?B^=Oi2%96&dPnwUTn*d3cpOrfk{mjMPV>1`8 zklw9t7}=!##w!Y5JdRb>p+;EOIzhXwNYcxtJ8|`!HaOc@1H(bxnW*lkSU+Bl-UM81 zo0F9CO)Pwmi8OVPjdiIZ!KGz+cb( zp(EXFx?S&kL(&qc(KC#Den76#M``(+bfcwR%}caT>vMfiwK89^)$fz3uO`Z!Z4z4- z0x7Tdy}zN^RwP^2X^yRSo`S$gg& zbnZsW95s3Fj`Tb|bpAohzgO?z9iO+lYgFmfoXc!OET=NTNLR$2I}fQ*ftO8zq)`jW|(ysT^u8@4naeP`f}yE`G8k#o*OK#99&YH^mk4uva*Y zMVe!Vd521lSuU*xKVEPO%Ix$aU^XIwx_9=TkitFsAJe>{6xNDUUptg7!Cb|hIT*h2 z1nA>~rCEzgqMSsN-7Qzy1aOGRjgh6Tre@3IqvOvX-gK16kW%z-xTKG!)vKG?6C}() zdFxr^dE3J6WcbE6B`aj%$=DKtr>NVEswnJR#9i8A>02fq55)*EW^s!w;k3+=;nocfpY6a=-Pu*DNeEYRZHcfO9Tzeu_}mFM7<+ z?_=a4u4zJsyXA6E<bp@&WtJzOBpNfh%c>d@H z+9@K}RBKYif0rlb5)S(;eSA56gtYJ%9pAvi$=LA3{$s+2MJ6ju zTAjpcXEA(&#wBW39|CFIE+}10-a$%Fq^R;QI`f@RSoumn+9?@dlfcpNqVVd8g!hCA z;HKxc;0d=ILR=d}EF=zt#5fEQ2&^06yb*dNv1)E>d_L$y&LqwQ-TvD)#(zn|qV~6> zGPw>r(2Hx-m`tFX7w=l!^Olp)A?rvAcDXyom%J(XP!8-paLFfUJ?<_)?GGbSn*iog z(b}w)uxlHpUuzp;Ya*&Rmg70j?RUG}B-I^ib>pMVnL0`Vn05m=o7vo1*1_D)kpOtw z?O^x2Pn0r7b1X!Gx+L=rJNLkg=FwbkK1UvMReSYOqJyUjmW~6go4`0yl`xwc+?}hC z+8sDs-!Z?CQ;htPUVPltIQpIbWD#QA&GAQqu~ssAr*ez3)KG4?zl`;~p7SO4Sd>3o z-({0{z&#!ZgF9xHs207~R4~}N+Ens5n5!47`DeMrqx4{1#~(d6YgW#G4w=sFqTYuq z!qtDTtK_G!o|FjbH))g3H^gUH6ER;oYYkO!<~FDqZHg;@O85T z38QT9pmh;h-~CBIT@n>HE`Y z=K5b<95O05j^Zn8<4BfFjW7|HmG+do&hmXXHc`b={O5hl_ZHPov#JzqVkE`3zOc_q zjfy)SA^8!;Rx92o`>S&3f(d%G&{2v$fKl72+c$m3hJ=ZS1Y<;StL1N^`+a;%Al+6i z=^k+>Cy{uQ)cX2lv2;J8f}wO5NEc}52aU-n1-W?_xXu@8mCVC^B2JWR%~77dn}vV_ z?|qoS$2L`T7cLQcl<0(o+ESknljg8KeP@RMKUo)O!N2}}pZBfOXpq`E(q;D1=H-8+ zDnUDg0CCx9;~K`eHM;by2`5#|(!{BdML`~mr9A)J3n?3Wq5yUWpG{*$?vth;D&8sc zPKoeH2>&gBP|#ub+^M5t6pkPf;`XxjL`SsDWmN^@G_43awW$#R%sa?F_@s|3%w- z_cfib>%ueRs53THL`0f`fOG-rC8L0-G?5xwl-^r_&=N)+rGtX>Km-J&hK`gFmEJ;= zgdPngKmw7Lkc1=;Gpju7?6p@pXRrNNUO?{8{k!g~eKkxn+BR#-Wv#NY7ha-$q+OoP z9_^mLRXUm3*4$krx3SzZxmy;R9R+KdH}I6nh!<%ZQQ59Yka7@|G%W4w=8_m3dKR(D zo;1xZ_2vuw;-hIc|woDSsQc0qSa!ysHSe4ibzH^h<6=by}rSQNC zgt9#YRhq8cdkM(j18w)6=fAI`9(4be^^3XlWO)F0h(7fax`(WMbpf7bPQb)!KW1es zUtcN6PH@t?k9ZyWv#%T`WPEAp3PyI0jf{&y3I_F8MsuqIg0_sgL3oUGf7e1C3|6uw z$|7^muZZXFL3+m0j<9o82R213D!Z==92ouDM@r+$CE9H2H4Ms<9vkb~GcpKspFTRVHk+H$MI$tFmUc4nXrGrw zFRU{PPG`)__zH?%_S?#sR+{H2wF_-P`KTMZI!tuEdimhiTfWa`H!E0UDs6Pw>?(~5MPSad|&#+x3kTby|mviApJeV>OvT$UC=~O^JIagGuIx7a8obo<`K)l4oZK!+T z@O=08a;D@9+H9yR_6y7b>OEuLGYiy=Qe6CYV$FOwj5YVpKY~n(B6^lS6NRk>QvByT zTBn2)!L@*vRVM%(kc2Wv-lYC^+LO%Z|F)JG3Tcd%Tl2!(j7;Vu(<)G=!q&{C@BgQ| z$UguS{lee=KX^-0X(>2vMweWz&7MnCb^rf2`752p<`W^`Hrq9y8Z<%ur zbs%~`_+D6e%i@&(R#}MLG=(BFUJ+l>KK`v^U(4;K+SF3|JDMa~AoFZBlb$`a@A+UG z!XGJnM5#%Y$vC1U$DMYZJZ>Ybq|UGHV6Ip81z+gxb8Mb7A9ZH@Wdr{g z_2Vl@^FuE!MF23S563W>fJ?-rQ3dP$qq zp?A=zz!FroLj?Qys^Bu%P|56->WZQ5jkpc;2RS#6;S+-m3vKTwmr)@Vb&*vUtg$Mu z9fX22Jau{F=Kazwm?n98huy(W`kQ)zMK-eZeXG|)RF9^)tv9jc0BY*{h8vk7t-C*v zpnCzNZLFl#cpLBf$D+r{Qx!0$=IHqQ`~FY&BCL5yqa6pyMbE3_AI2;vRD z764VULo}^qiW%TM6Yo5bFr7uSe= z3i!crMY!ZGM|wU-MuF7X75~yb+1`99_bTy6CT2QvfUQJ*b2i3AMrzBW>urtP@XUps zi6JuulVark$mecAyj!$=<2OC-Bc1K4q_6qaDZz-nxVR!ws*0apd|d5gXr7J|>6<8T z^yiQ$EQ>5(;@~Rh7g#OK=s>gzGYv_c=C|47R9>pAA`4Pqe3=L#N_UQ%S7-CxHFu(fe`e{Upwp?}Wbmp!4NN$1lfVUw+E}a-!p( z=+oa!`~ri26&gLt_(fv%==>YR!;hbaj{ZTc{RK&MsBMaHrO1h(#mJdoMTr9jhQir% zUk?X4t#mth_IRU7)Xe5Dlw>q_S4EN1(%eF^nELGlXM*JhjpmyygF2$ZW$b;_Ak2>M zwq5xce@85O1p6AS(bm^51klTNGySb-L`xyULbCQ+q}{Z%=NtHIX`?yHf>~K0Pi0;k zuZ_0qP;*AxbD$_8!jAKzxG`Q2`yhP#w8dtNAu!EkANaf;5mcF&CDCd^pNME4AI@?7 z(sBt*ZO)pJw-MxOFYnsjN;cSJ5*AH+r$Ys`H*NUp`kEGJz=2yPmg)s-H^Z6q+h6&{ zu$P;vs~Q8pp6~6fLUhwyr}Yi(3QsUB&BCk6qWON+vMVLoLc+E;s)n*`?;iHeH+w9Q zP_bJ`c}?4&MM~&$Tf1(ex1xfPwlo1aQl(4tW*8ZfqC62J#`(Y?<5d4-xcO4vRoaitk?CgxIyb3>XH!g!qyjy=N@{e5FZ1kt z9NvsvYh^SdGio+>rY=iZXc&hTos&0L^|ANyhTWX zm+v+%x2W8kjF`|T2c(=gJy~hisH5;Ql_!yF9}<`u1t^UnE5KfDAdcAya;j*JIf(-<8&+ zrnagfM5gm}NKGi%%-t=#r5-Q2Hxg&mR2Vqv`YP+#c=Lv_f>_jhwdElDQvrAhwx3pK zZRj@yRvYoyD)UaUg-hT0+J@Wj<}!+(X$5#>Ia37z6{`h3R?mDJtMZRr8U9sk-QR@-{B)5dq20ML)qyfVoJ97VS7UNz@!o47TF$71*fVh z2ARH!ej#vytm`f>#T=VTtBZDh8fPq~lXQs(TL!Ff>c~--?y1^!@I;T^*!o5;7BlJS z+mK+)D6K7QK96gb$WPvHRa80@)|QLnx*t2{DXXr`>gFHZ*x718+F7@((&Um*!$mhs zOog(>h8O$`a#03W?vch!RFJ-HWbm_7tHU!E9ZgywU}Q^c`GL^#L!G?`!g~(^p^vyi9}9;%$%i@vU|+akUxi@= zdDtRA?bqY)f8|np!u$S-u-a3J_fO^3o~gcn2GII&oO$%iKfn=x%r7v=>DMtX4Jor* zUXkVNXyfRk@(}wZfA)13b@~Zut^1GMBX^yX^+A?#t_qe3L))YU~PSv;A z*R2(8tx#*Ay}nMIcifc>xXY@nwb5svlk*d5lUmq-i3u(E$Xd%JzU1U(Ndfj4a<=5W zs-WjD%1*Tj^^ROtwbvK$aUZ_x=h5h6Z%}6JEkNNVrtvx4=`%R@5H>CNh=gr=y0_%S z<$W*bp%wLZtrPWW*=_NWhYGoEvGWyD{l-bh2D^|G%j5-PSNntk5#88a838$6ud2#53VvznDtf}^Jw9G>j7qq(G!6QWg9IvFi=B}xD5L%Ij$2Ngf6=JpcxczRL3B9mB=1 z(^}pOEoP2FnW>EFR7TED8pWOn*<6I>FNO~kXbluctrzI57sJROgvg(C$X$-)9@xPL z;e$_rgD%iPPjtX{Tmjz;2mBy^_^471CB?B{mK>gL^$lJeAu(!jrfEg zzWnz-pwco!?|#s6_rYnF?&0|Uu5hfLb4XQve;=1eFz){)i0cxoaIsZ*B0+!zi5K#0 z8w|v)ue*x~l%D7a@i?&T8@4Tvw2}`O56d!QMcvb4qz>0EnLF&^2}J`T(D;Fkd_1B3 z*AZ{3npC%Vd==DOe}03zD=qd)i+{+`HT+89UHEJLrs~@J4SDC^viiGCb>N8!q#oZv@4dZxL}QqggA}< zMy6;6UwHy+%ShH1Utd+wZ`u6T>;joq;Bgj*Bc-1Mm^vLu$Tz(J+!(=a3N@8*! z!?N0Umh#0pGV0g+bq(aCw#QzkJ%z53ja{QJM_R<1OIW3cDLYx&G`nrY)4f+}F7=LQ z7HjKHbamDqqizumm4x#fJ2lnazZ~bb#+7A);b@%^2vBYFaectZ|*Qm)O_FZ24A6kR4=c+6qb0?J6DLo6^a`qIYnzSLNNa zW!D+MRalI5UWOCj=A?C`y^K392tFZbG4Jd)EBBf!6bJoyhsmp}l7w3)_P**=Q3-il!~r zaa{reY3`3d{;=!mv_aP>_f^8esNoa#;d>`kL+bPtK>Ltp&2@GVs)g9-Ce7dF1{p7; zM-DG`V<4H&{X~Z%I6DMBBlaQdM8D}SHnd-9jg3ZI9x#ICxc>GIamYBrVP_|~CcFy}Bh&-%s5xeP*ZQg$-pnOmI3 zUT?n_aASKi&o;<}xki{nE9qh62laPj&Jzfml83iU=f3~ggkEkIx08vo@bnJ-?sRKX zj8>axjV(hG?X#^?Pu}%z)G$M3r?X}rkn9dSgcC9K-OTY>AN{mDWhW)tm+(!)+jnT< zLDSjxQj1B6RxytwEw^r~elGe_YxQNFj-|HlRTbv7cKaLOFYUb*X+aQevy?{0WUY>y zSe;{UJB|MQ)iU_78S-&F!+t&K?xKR;%)!cigXKD>o&{+yNLdoW9orb;lRpB7tk@8} z=xDkx$?|Qby}IoE#2E$cN1adrkI)xnPST8oVp4>dTQBI$^%~d6U)EC1yW1UFcJVl2 z6)LS4)_VOShL&O3T78pP`?(EZFLdgPDoyhouy=UL(^(rkh^}F`v0{eon_dX@{;cHK zW?@5k7_Qf}+uIJatV zM7XFaF~>320N~1chHhMNAe(1u5O-JuEz`J;z|w`oATS*VqOEPxWIkFR|9#G<^S&`~H_Vbw*~}zZmClVP2njP3*w*G|gh@Tjf*R1me!|qU|sGk{H5HA6=$~P@%iE4?BMcD>^Dcfgx4LdE;(HGA!}kn-Y5)}f zBRzEXCO6GeDlA#uKVPXdj>{eS6SG(SSv=T7E#Ya@?$TRvd0B76rBczA-?WsM${(RM zBCV#Idlq;IxVXn+$w@XBvGu?Sr$hNywx@YF(33c-v9&|4z1oq#A8GbH%g)_iXpwk3 z(qTBq&gUh#DtU|gED*TIVXil+BKQzWe!>)UD^SJT*{@zJg2jTgxauOL@+ew)oOW}J zrh*%3_>8k9DbB3c&8$P}^8ApqYu+2f|ErYP-%1m$w*bE@YZXy>>p43|{z8cr4&Jv* z9iF~=SRa%cjUb86tGZ|WOdJ@PKOWIvduw8d{seAGD^EuHv?)CE;gRs+u0Wh^tdL3V z)yrxu)I;tn9wz+X1-&Vb9boGwB~Z#kE5l^j+38XYp9)dyNWDHuAfw-3 zQzxztQ9pgaFiGGvt+%YB0$LmW7Vk@!NBuTAqi;()cveO6ZMCcK&3tG_vkyIZ4;|QQ zTar=uqAwb5OTMt#d#4*@(prAM4cmDClmodBQrI{SH6U5Nkin`WF3mPxe(tl}bX2lp z&}(nlKTTsyJ0V9^!ogE19t$I4G}z| z`a?M@PNX@>!b8Gp%1lINIqR-gv@VSaiu=kM&#LE->#Yua^JcR-J);|NLcIm*TkjZI z>?vK3@Ge~SrG_cyi}pOzx&d4MwD&$Re-k2ilZfHwAE4L6Z}qbkFoguI%b2%U%tD!&*~&-5*}F!%M8tG}socudFWZYzfqKDN{EHb71rmbU)rTlUu8mEn7V` z8=`LcV$N2S#a%zJu!uWJ8@O6Wjg_r?_~dGkL8hnEW~FSoX>Rao-xQWi}UfctM=GtEsak=FEg@D^HTE^}Jt*rF4cwS&C&x8D7i|$T<_&5Vf7u zR7c(P8NPSUYL%c{?p`c|ee*8Isw@436TLJ^(O^}JPdV!OrQHwsfy2`zq|V;1f>^#Q zjz$<3Xs-9M!)DuFo@+`aY9#RTT<)K7c5o#|oE!q))os_cEWTf5IbkK}iZFZ}RL<74 zTKuImY?}^Ys{`9vRI{jqVEDdp8fX~cmZY9WM}CMlsuW`)nX*O|%ojjozZXIG^o^sa zLvZC_{s+BFU}vu5q~=cMUgmS^nTfe1oU=OdJ)jOFkR~j*nAF`$X_4#U1Z%gwfSXXWkDe|X&CCX^pYBc?`Yskdgz zR*)ca576EryNxeO@^s+nVm8i-6MY;iJJmk;Sh5|5+Es$?Ho{sH*{zk|w+;b9rjP73 zKKW-b;eWs%{IiL@?SAMhYjm9W^j9N;bgMr4P;!VVwm3TzZPx7F@N&#}sqlQl^2>L( z>ITx?6!!;t;!#B*; z7(_(n=0V!THUjj^sciN*b>HVOr~ts4VVa9&=GIOk@P73aUXab zc6X2E_1a6bta+nna#}H^Kpqv|b*4P%*Zi207lHxaN0rGZb zd`p)KQK5Ks9@pM&)L}HUSLQb-+-qQN?>bbBG8ODIm2YRNKg`s=58`*#`PT+?2;DTCS1lMr=<aRqplY&~WB=-gcg0+k+pkglsn=`|-`CE83a(z_ z=ezRa4Cbn;<97P@AH97cvyBZH)j@yl9Ms293Q(t|)#=w@KAYR@6LIH<{V3Krsib&{ zbwNqHeO;Z*r2~t%h=zN=$=Unv+~?TKpoGMaQ%R#ex@aljU-Os#8(x? z*XzXB6eUP?5+rZ)p?{2FG3s|b|HR9d3Qs5f+Z`!?n`Lug4XXSpPya7jgE7i{8+f_* zw%K-|(d)y|Uf`w&aJsB2$)@WA>?Ek00=Ou*Y0SQ=lAi15J>i-C&Zab9p!Hpu)`J<{ z7qSCa*!(&meu~%G2u)EQsO(V7ye$_*sd)b#IFDCbi zPi_N#pzv&-4a1@fWmyR-8Dz`R%XN_%HDc*JW|DT6_sQz%$o|XexQ(20HRS{{I56v5 zPK$x%&q3z01M(Ljw)DOqb_NEq+2FV`rFFY`l1pum%PQp^Ow=k0s;S$6*RnLSRWV$M z)sO;Q$Py*qgo!HJ_aU*T3?Jg>QzQu)yd6Q!`IEE!qrSeT^Nzx1RpiG`W>40(O(VZo z_vk`?__ZF~oiM4;#@nTUxrV~3?{YKBn-RGWM;S#Z(?(9R$`O3Ho zm#iCh&IaL*srveC#tpV0(b|$-Q6vbvA*Tl-Rn1O*UxGT^s#%^Rs$7B<(YbFZ*_8;Q zAflYPG_kw)zyTW!#Rs#_nBs8%PG(J$)#v_yt0Vk@11L~q$ff@?Ibcj-Wb=HLKh*ugc6hX`c0EhmoX zeAMj`TY8Pp;7EG;aHRsOdC%FE*sc@$Et89zsc}OgiUX$wL9qh{)!m#;9v5So2y4r{ zD4R-6CBr#$IqcG9nANxvSMO@b(Zgg=Tc5%7<_Toh(7?!YomY_dKy4XRY4&rb@8`U) zM|ZP0pyh|)w>eYZ|N!RSyeUtBiMpFu4(mwl$sZ(nKKQE2wZkep`Ypav9N+x8Q{AUeCI&c^Txm|-i?N*#w-H6K`UGTFnm=WEp&i1H%1hxGB> zvpr2U`;=w_QuN}mCjb1eG)N8%T|x>~HOf@d1?83vW))Kq#k1dljE(@P?A*T*9j5FS zz2EvrMGybL(D=_oK7X3tucV)kCd!SK`V-`90Yj#YOnVV<`n&5Bt2)2>6QaO=Z~V9B z36IHYbmc+Kz~Iivz=BP)kCER(XFYpc<#FAwu=Os;75*h=!}4-!#q`oB?fY>huMQvD z4SSur_jiJkRzOr`?daA=u@o=I@{+pdxxyZIfS=|J;kR$6LSGq-U@mZPy418zZaQ!5 zt54Z>G}c>+;jQ*|4}t^LTN~%z2cgI_?x;kb#ih84ZkL#Yq~#abY!@n%-{-5VQn>)j zAQqiUJD$8?^*}bt3dEY-d!7TAK2t)%rKUc(n3xV>Uy=tXabHZiisZ*pcmCqG~G4M-^PK=(0ly=doGrX!>km9Em*;xWBvz-rf za>Dl?e#{$=fa#)WZMcQ>!{-W{e~G6TSz(-BM!cm99>zdLN^&k8l!`$COCl z?G03&ZBA&tcW62o15iFT-_sP7?$9|g_qjHiOt^G~(pNdY5umbpku(k| zVr6^oZKzO(q$8^QdL|wt!s%VGyW{?9*Bh;N^}Tn%&o_($etKE`Y5uCsu^*1`%^jnC z_wnkFkb41{-(9`taQa8c=}kqoQ`O*gYweMG>H=k&7&T9E+_@@!4?i!43z?i3iw>{R zLB*D*J>1JlH%kI$>2>+}NhCu~HS)_^N|lcd5A4ug9%%0zSi+2W4_OMBVomTZ<(fzL(?!9OJP}vwcj&W5jU=kKnU#K* zBgZHAfXF&1dAB0kP6mgZFTbt3GaIu#ci!8I9Xp1{!?^2Z8e%hF40F*Z(~ic(Dt4n> zCiPBVW4_zB(i37`N!^!7GdfNx4&LSbb01d ze4aZgx;Z+hzs`FqSR0q?O8D@IV;rUCbVs-hZ3Q6Z(++>lL9<1G1Qq%^5RlrOF_*rC z>|D9P1P1}nj^t}!C(N9mP2CUedM|=GO~mcDE$+nQ`8(vN(9TB^1O=LIz+=GTJsoP1 z&Xpr@_eDb@LnH7Qj9lC5|^cW7N4EDz^lBXwIG>0njOnQ-x2Y{o)|E^RG5jrX~qtEn^*eITF zz;DDb1%KhJO0bIYyZ^ja?O&X85)&C?cmCX|7TOkQyvQ4SW^Az%q}gX8tavv#-jOEL z@3YIV1(B#lcYTF(KVL=-Emhy5tahw)xQT?9OkQMvR|UmS`{yH)h66X;fwXbGa5D02 zxcY=o1$5W4@!;T0h3?aW2=;uR7hX0+%D(*#c&{PAR(Ob5$#?Uiy_Tvj*)j0WLC9jN zjpAGM%)CaCd7y7B!*lU#R_|euzDLVO{KSPE=c9XZ51yZ}2++BhOL(X8*rd+wh-I2_ zu4PMuG;3i}qwuMDw8hDw2=)FL8Hi${9T9Fvq3lAVYy};&J{_7K@qG3nm1zXyqai!Y z3+k$IvduVu$?g)26EkkGaDzVLgDR244 z1>S=n)~LlnbcaqJENnr$Ij4m$k-yYF?-n|fdkNF+fS)_%mXa8v2zFh_Oyr|ZY9Cfi zaqQMe8^aN~tS~BZ9@X-Sf#UaB7#ztp8TH<}$8e8l&kF8@-@d)K4=?MTmSH95S0RVT zC|}eg4u&Te1FMB5z9kDkw@f#vdgw)a4Lur=VG~-NUK!<7R0)YJtSy_0)+#tORJ%l2 zr6H1_I83^k7T6qZwrT3U{n6m@vz|n4{L* z)p~6~PyrbmdH4u8o3j6ba6Cr-6!qIwmlJP{?Y;IARRC2KxeGY>e0R{_)Yok34XnAF zCC-bSlgb$aT-YJNTQ!TB68lp&NsN>}*=0&P2>6&gsY1wyFUe-NyZ~XerFJw0*Q>7_E7a?HPwSUm0 zW8{jOv10|_v=;ygU5|}^`1W-Sin(zYy~yIVar70w1i~Lh<;H!8aqjvmf6o zBKyDc?mv;(f2z9w%wYdF>;ICWeEK&fIeg7Me5eA=KTT-+jrQ0-QfRBZ(Z6PjHpLT- zc`reWz`L94u*z<=pG?%v_f6+T+Eq)_9iDn8yyE)dbHzvXjZ~eZ~CMDaz{0zn0&o3V+ibLJ7)X z+YQ|u$TgEW)E?{ksYPOYd;aJ&V5dks4E(eE;}tDMb`&4I+#qxs4Y{p;B`>C2**Ui_ z>TPrT%>(&Y4%c{3F6P+rM7+(kJg$At`V4PC`B7?6j(#J=OV)_ku9InQYd721VRp32 zHifG~cc=NV2hY(yg~`8u?<-5{x<}IbURZg=rwZJHbe<^ z#$oV8H+To_9c<+-469A)<-psUmYS}i1xcpl@xG8JC=CT(d8e zA%x8V;ak#29U*=od8sD-Ydc&?34(7g00NX^R-x$t|^9BaAkW4|>{%1;u;x zjF|#AmQci_CU$lOCm)Gyab`bj*2F%0nz80R7{VtV+#UVf1Kn&x*lt%U^LB3^v{QKhHe}Pc{x;_8un)!1$ za+O!G$UQizV@y3_BunI3dzhsCjWd3eBhnP3Y4PfOwg>O&Z)V}JVisw5 z%&zztxdNq4pj8uy}pRRUK zP{C`yW$KPp*~CT}a($m=eRW;D;hC3g<1>GU+9Sf%Hv%yujYj1scq2tis2h@^)?#AS zLFB^c)3V!i#+``RIAD}9T$oULY$WOIBHUxkm!-aFJ}t8zla;NWPDqeUDb1J)vUbR^mqsn0%g zx`S(J>)cNOXz^Derw<><0{{-!=cgre#xyvh3Np5z7r0<#e>uoLm6&XAU1`uCerqn$ z3B=9GxRllPQS@R4KbJ(oRzk1pMUGmOOHEp@)x}74AGbO1&M`L*T>B<=Y^XRK(b}Z> zaix7h3063baL4DXhu6OYw))VZBLWK};M$HRz33(*e7!xs?KYtPA?PH|Rd2PA5&tW{52w6qv&Nm@)i5k4 z)qI4eCH5!Tw&?c)kTK0x^h@WwM*{Zm(tjz>arlw_tE2D9a-y+a#nnZWB_kYQCvjfI zJUu$k%b&d6HeWagiyTWXt<}Xx4DfUuabX9`x*SIJymQh=C4<4gp5Tw_k$bq4u+cna zx(r}C&M_XCV>D7f%!CybpTMJrXH?b`_xDQ(a@zZrQLOE|0_43J zMuhjT8ZR{!o5I&#e5#K%@F}9Zf9?#Ov~1mFks;ROgZ?|%L74^8axxRRyr5atHMl3X zQD!|*t^r7|(I}OZ&i|ZHAHOH_h~6M4cFYlQ^_ah!FF;bDQY&XUAv?=I)>4`zr%>`( zj86z{7qE$T3xwCwC!Yzl!^{g}|(9U`7QFe;I$kE`J&Y~g;(amL|jUk^Pyk!`>A zPcU39l$}$IqtLheH3m-mw~`vaeCJ#rxU#60#$kmnuG#WGb_3ytkJj3&WN$L)3N+2v zklYoSha9~%v;&7-QOdO*nY}0&& zr%lyzAJ4SO>gsehO?a=?)$;+6zvF+f^ez3Pqfjmo=T}{=4j`UNf(BE^ZA_I)4}Z;?l(iBE?Q5>Gl+~!h7cO!tCniUT9+Q(e8{l;eOJEf~U^M#<`#QZU3M*cLlT`hxF;BgUU z;={S&xRiU*d$0e7A09#EpP`FOoUhVaIf2kktD@5qY#*bmc~Axi)ti+a@_C1FceGJ*8HLf6;U^k%+uK5Iz7lvPNICYp+~q zuT3^oa#e(#T9t^$QhCvoRxzPXpx8_X%K6hynOrk|4*^L6rE;blvd5BZ^v9c`<3bKw z8oCkqb0JFZwUyT4U3qUr6gSGGusbEXMwp_3=81A)-Xfpc5FWM;Ec|vvmW{AFw0&32 z-veV1w?!w?hY9mEwK39G2+D*p(M9)E)UN7Lk69^t*+2M#Yo(2%y!48Z)Oc(rD{6Df z6H=`;weO#{7#HO~6?B%e$6lv1vs?&wT=Gtq80?b0bfLbrm%wPcwVv4e^CY!)OY z@AC+Ja-`KAHkJFk(Rh0E&K84(EQ_!IWky80gB zbCqr!EPu-m+*tAEW2*ajs2LCs>qUa8pKYv_npao@mqcvsW#! zcc;T~5>ni8@$mvFaaqw7yY7x%^CpGxkwSp{!P)$~S+nnuzBm8DjGAhCaQ)4Q7PYPM zvWgJ9xIO3VzW=Wm{qL#UKO43Fu)X|@?7#E-F`wZ-V}+fz9{kIMGIzto?)Z|fn7SgQ zl+FlTZ+pFyp?;Dj!(w~MBxJ^&jh=rmD>Ni5GO|hwnh2HFiyLE$^goX)>|eXV{J2S` ze%xVf;tu$F3*>JI< zXY)*suk*9Uj}p_<73b=0^ZUP&ukYW}HywVgDmXvtZ+^y5W!j^k@NuMa1Fxv*e>)n| z9@z{z<|zEt=cf~)b-Z%LiUJE#*odmKpt;g_YN`szC~lO5N?ETdhMo{&v7FJp)a;k- z^2CVPm8#Y6>a*-zxId3^A`}!6!eXrIyn1$?qR>Gb(rY2VC{7aCUOsGc*7x<>>$_QU;xJA6ylG|d2FrRWOX~P+fjD+CCE;XZQg8E~^d4>7i(43W%O8Tr zJ9gM{^m6wDPe!zXIlZ8iwMbJw&bBbG{JLUw)+Gz+p!cEdrM6y)=k3k=yF*at#Bg~YYV!Q&)8kYOsJbXgYr&bUB0W_%Aj~n`;irGhlwpW=0Q8(c_HPBf{*HtC zKPT3Ansj=*S+^qT_mh6k%x(aF`beVVm@GrTcU3*eS2=qn<=OWAj-#nj7m>J&7dI}$ z&eeKl_yjcNSE8=>tY<~N+7D2yk788D-EFUn{U&_gxv@QGlwBj9z8$XqOSgqaaZrk( zJOWcLCXq6D9qNzgk_(DNuhaTsy%QM7BhGY(_jy)Xi`OxL!Z1_npIH1~zom?QP0 zmo1lCZO*-Zpi=cs$EsNKs)kCxgyFJ>oK$*FQL>J$lY1H4sy(C3CHWb*GfXZBsLf^o z1^I&wH;{SyH)F3~2cTsnDH%qxI(NjA=am(^G!K26&b|8(FYXo5(#LC7A z@`3y2{pIQyj{Wz2q5tZx-(J9b?LJE$^)E`u~5deP>+L>9+37*v1Ab zLzAW;prD}imS6#qF1-__N|(@UR8%^MNRvPmL`r~Aq(h?8YovtGqXY;&1PB2_^1qqc z{hYn`+4r9NnS3F?m3OW6tf!>6+;Mb@Emw7_devUlow4x8usy~0vMzd^s++xIL$lx?w zzI{A$?So2%G7=slV=z*hH7QjOrJ!q@92~R`7jh39FDZY)yXKL;hRxQ!GcqfG?p%8g zw&j}#oevtx`fYhM+ItrD20R=7z&P#GLsA4)3&kzU;mLJY5muv>Zd0}}FyWzCy6jP? zGOTeecI)wN_GSylLHU(cW{LU0JRbIdTRva!N=^Aqg%t{3yp2}p;VkUpZY^opbyw1e zGItx5(^kv0)2toAYM>t0RN%Nyh9VUAaOcUF3-N(hoP+zX?%S1M!qldC5H{`>@+8!F z>h4MaoR36`qWzSLU0YuAg?{(M<1-G(kh}e<#pfC*j0#!@5av(Qe~a=86`2&7OmhI?q!x~JmmxmsO~?+DFSRwUbX zAN+VcVj%tjIH3TP>rGk z`}fs@f7T@CS@`Ql{`C96S|deR{W&klKbgsUOxMJWvsg#|bzvO-^<>LQtF|NdI2t$Z z-B+i81;^*^!SAE2hf8y7L<0^5s6osFh=F`Y5f7l_*O)E8_S9;pt>^SmP(tp`^<&n} z2{|68S4=zXGwYFC>vaA?<3oldT+9WEGxHS1rk^UYdGiue~)4f@a#WniGUIs0Q+Cz5}lIyTj?)N^+Joa}I7wnax9_TtN{cWxbz{pU$=^ARs-q>#Yl63X7O&T14)vvIPcMZJ3SG zXAp7CIOA+_??gfGR6*}d0cuU*pAW3-g85Bc@Ba3xMz1)kjcsFV98XB<_y8G5uRM z*(}`+F%;pz+gOc(=QnELIA~+e`9yN;O98piwj0QLC3s(|c8pt7jh5>t9g zrNZdoAiA*H{}y*4WLDXIsJAN5CcN}l8(R&j9(Bus=I_^#R28Rq8>^fqQ8uI%T5wjs zFh20+Vo1OTrXZ(XP;AK=qF^-Ao0 zaMt%K@19NjOT}Xefd6|ZpKY46L#jvY!3xvuM0ZbJM4$ah%2>9ZPbuM;m!9X#A6cwN z^(lMjBZE8JG)&mKp>4+CH1{HP{G8Rb#|7Q&$z<1az_qH~HUTj({~ob|9oDk*BvvH5 z4XBTmPCl(&^I3O)T(U8NPmIY=?}OY04F#wT27nZqOMRNL5G@1G%~=PGL8KbgDMdWm zmKW~BAv>9vO;4?ic4g@D3pluaH^d6^Mh27v3`*Wi|d%0#SqqIl1jR*&hbFeA!sNS{E`|);pHpJCWa;FyyTD9|Kc79{+f4no{b> zI{ly782uB?)}J^2Fs^ZJ`yAPJ)=HI!U5I{e`XZipI*+jN>O@s*OdeDx-X~83p>I80 zoj5ZH+Q%X&AIz9rf*zrjeX}*Lu0}f~TP{|GnmmM7OuU6YJI`-rzR*Cs+c)iJY4;ZM zd4J}?rwKc;vMtxiXkqMz?a)@oF?uOM-h+glsq)c~twio|BF7hTl@x?T$u=E={3Qrg zG=7U%c0%95blT}kj#M_cjoWL3GZYA^BoMz|{95G0#Pr;gdX=UcPrD!lf3{Ej6izj9 zdwtNx?4I?AHf)q|MP|9F1fHLAKC3)yGqipPoZ(aoX`AP{0iD#ZjA)GR`$Vc4Ypi!j z<5V8FjNQ#y?rc9&qCvcVE8K7X`B*fhGhUnT(AKiro%|xmGj1i-waYdhwYD6?5_8U^VdjFMhNoA?MBRnzhvlx=*m5 zH6d)KEt>$Fs@bC?Haj}pKbxGi`)O63J{9h@I<#TI`b!QyRVaf$YpmKY{RJXBJ6ohl znJgA5rEq<$^{j3@_4*C@4P<8>7of6(t*q`GTOvAv4K$*3Ey=`86*JGsf zn1|W7cW8H$_B08ZzTpt%sx&{Sze0cl<5Ecdf_aF3e;O9boywj!5I?)dR&3@Pm9u$P z0M1{{r>dG>3uQy2h;uf6w-UnwZ}mxYU9L-B7ujN`YY!mgHMc{ zJ438D|)&=o9H}C;<%H&<>TOX z&XAqYYIeB-+<_4kvN>4YsQEG@m~FGVUeWU7AV*ed=f`-wBWM5)r&Xyc)MW)6Og~I< z@rKAf@$Czq@#qN6lA-%s(fzY-G_LYQ^fUh3@rkC|VR!Pr`hTg@7;Zp8CsGwBaj@F`|iL7H{=ZIrqE5sl$swV*-`Cy4vG%S`cMrARwQ{_M&<2t5}WG zkFfb|Tg9>6UxXS4t@jlfc9BI{n&5jA)|Y_hiSx2cpK3OzAD^YpCO)!MD#+Cp_uu4X zeuxv-CcoL-?Y8>X^qNTT(%Q?wToCkXx?nt`e?{@R9Vg2+wudR68*^J=qF5uq0r&Zb zmE(znAJ+u+An)SJ&JJvWt>8n&B}G!IwI%CzcXBFbopYX2@v-yj4sBx+O>NnYnoj83 z;Hp(VANe2mz^>_}!_=^7_(10c=M_@Nx{sGbW_9-UvG zni8N>&ZfAqk$Ewl&DanOYPrb}5|;m1|Jnr}YOH`_;Xc@C9DBcn=+>Y+ey8Ts?y9+HV=|Q7v@ot`|7ze9p*hG)t~kFn#YuPwj|Bi*>+g z2}P~4>)dq%GoYoax_I^&u6Rfu`TdU8fl)wOT$4rL5~e6Y0zaReIiK2Lv)2e*%?L@) zFnPOUOt{-}Um&tMxuu%yw3^fPz>E_5eAg=+viIq$#JHO9O>YmCI{~NZ<)by-54_k{ zFP4Y5h@!#UuhCvJE0uNwZFJ;Drvb)9+^{3bFNrRn1tyeiyeqU!Y3+nFIIqZS^Y=8} zM#b;MxHhS}*$eR^Ae+ef?e44M7X3(!P5ep#B!tl-J>R@cb&eIdrf)kcC)(IMias>g z|Ezpkr~$q~=!kY)Obywbk7*a1q1Q5za;_`Pfg?XUc0Gk!Xylf6jD7MQG4DRIo#9g0 zlpo;5u!BHm`%gze4KtSZ_Z(r73q(l}7KS9ygy!0=miHTuBGo^6e=A-g_JS1}WV;bs zY}Ymy>l>8a0Rctn&uyb~;cyKS(^7%KW3!J1v041|&Gi|$ zDv1Q@w{!*}#GO!Raccr!KEk)N*`Bss^+w8MsVWV<@K4cFKN^qX+rLsw10oVynd_ZS ztRobQvnS13msq>fgxNH|i0J8m9aH`H<+--L*I0RLFe-b?IcM;1A$flm4*rA5@gH~m z&0pU6_}d4M^W}$aCj%#mPkoKTosMJl8@R?3xI6kYjHb-?)a;5dif z;WXjvba9L@Zy@q0XPk;+n@E_)x{gd%n!Q3jHQts--E1MjM>5;&t(1zH&fEdTJ9}bd z>E$Cb?^TPt85sg1C9HdP(8Msl;EZlZ^Cy>Sf~cCfI5*ySyHc79{@}=6!(;(jn>~3* zg}I(YmS*nyhN&prs6PZ(hA6rL&5hZ0o*%bM;UF**h>Ttc-GQ1?ekhQoY|j0>MW>98}~@d1tk$TW63v_V3NYEFpeG zdX=k&8ZRKn%K1|C=Us152N$~O)K`LH4RxDCSBGNFDqSdwyt3b=wbfKTEy{BZJfW_s z>fErPox1Jm4)e!KMi-YHb?42yE|FIIS7;?d#1r#*L? z{;edxe_!A^d-Q+yYWp7`{68{0Vgv_8f9LuC`5;O;v`FIi@;3l`un?w0w>ge?93tkm zGjNrOD*c~$r4zC_B%2m#%lcp{YRUOAlaGDuN$)$ zDE?drPajwlHozwU%; zKMXx!rGId5ozQ<{==1kqe)k?OFMGo$ zen#!f|3WnNH^ehOk{xaMPCd-UKM`F$*K>7cUgAQhy^7N|R_TG-_SRM#`vryJ^w>Wk zNp6!NC6xTN(dK8?^o5ih$<8Yn_buZ+yKp%zJ2J$o=4#5=Te;9O0&U>Rs(rDps-_M# zKt}D}cfF9kdpISyX`0f!xQ!Q|1k`!b8^7!r|Kx66SvgHYDcVpm@Z+1~T-|KPkT3KL zLZJ_uYr@8)Y-;t^)mRC6!JM4JuA`+@q#o>!qO`OxYT|YSZ2zX!N6!!UdxMJ2NF_)i z$I#f!0I%~?R*)`cxQ;qW z4evC0B?OAMXuik$${L(=UgY)er117zhRD&CJvMtp%?M4H{A1JfNML_Z4*~{On!{{ zDBM&TA(jhbu8`5+yPG847#;l>rw~g3OIMysw|%wt_-cIS?Kb+tdlq+Es-Y zIzpnQrI^9Ha}_gzUVe(ad;$YY%1B2{E=YBBt#3L6Ci_x7xQR?>L12~ka^K%cZM~CH zHGstbE9Quysx2R}=Hp+PBaScsGEz1v-<{z3$GB!(JDz&{kF2?WgK@rN_y0RisI>pf zzuH)`%v0iizZIPhNx%QHCD@x)k&|G4kqp;s??}8Y2`nr=A$l9%lWnWicF3jqx;Kw{ zf=Gu6a+!f^UdE>(NLm3@zwykYB40I=-as@9?$qx3jweNU;ev7%Py26*`ONV%Rv(rv zSKrK01zD~2gx=eBr=+q)H}1* zJj;-{>vL|4u*0#^14t;e7`lMe_{y#2e{;_;JUMuw7L`jT)NFaJ_;jA?;!)k!ez+1c zeAy%TqnyJ&lE?QZEQ4*#>J}`hF%o7H))D{JN2YsF0QyL3=#gHvE~@s(e9AsJw4chij+3I0zm4{9TB=u)yQu;9YH)#89KqV!mkd z3oZ=7QN+}ENbX8Dv7BBx+-e@Z6*{mwR_97iqN=ilOT>iv$jD>cHBpTTQH`Zab}n!! z`YUu`*TKJrjmnb;-G~2z8{Pj?*G6SKUh4Y4b7=q9(B_@L1-<<4j}p2rT!@9a`M3C4 zI$HVaXpa9_)uniUXQz~J>V9Fpl-H%!*X$nNFyN6kYB+ht*8%-1BjPurTd;JK_sT56we2DT>(zfyNL0p0odN-F>k-a5DgAZqK&eH-1(B1H9@%3^2D8kqw z)tz&=q#W*myT?w}fP0vgepO00#Irhf8$mTBpPk}VamS%Im%Y{^`8bdy| zJ+6#vbCbkJK3niDVM?*331%;JO(sq&0k>}bc#DzWZ+(GNVzMdK>RPP(PVbPm=$q48EG#0sk zw^0E@IXljr8J2K^dN|SylJqxMQ#^VTvlYL#O^ff;dMI+c?_guwHPr+ipx-md`wQiY zV3n~YM`Um4)O2>djBC&;p*p%#CAUG1?AIYj-g7bXZ7jXh*mtLK_0Imp!>w$fVSw}x zsR6|G8+YICyZwG=FHiQZ|1*L9Hc;ybf3&|ep+x-)gWh+GhgYgZzV^=URk10pA6!bpEGGo-$8%kElK7Z@TN$nxx^{E6)0E8bYg#H2fV?TIWWZ+7U3CKIq4WNum^GtUL{s%bcW_CgUA6LJ0JtP1Z}_8tg0&+X_Mkb(!JU zv;I_zp$wtwulWfGL9M`NheL2pY?SK@+SAHfi$*7drJn`N zzTkUtRBYn5${iH~!P|&QdS<$q!`Qc$5uY393#k6?TqCuw?TI@D{)$`+PF>l0v_Q~E z`UNbr=>0q)yU{dIAlQEDYVg-Z$sr$Z?)ny<72I$E>(lonDsN!gBhOv}PX?Bk zKFOHn!Ou^A-B+!9vrtrJ?}2;GQ7wnFt&vscjp?(p`Q<|BDsRI=miQV-!#41p}(Gp|65 z_BEAz&`_13l}%|^YRYjf}9RP=P0 z)^X{*i0$*b%ty5osn+w62r?ZL%Kj8CdLv-#ZVGA-?7Pqx7Et69Bwy2hDXlqN-0VGY z(r9Z+dXp5C?X+~3&xu}MFyHqIg3CO-1P{fl6km(&ok%`Zt$h64J<`xCV*|vgBk&QS z=So{^P4+~LQTH?HE50sGG2sg$Hm3A`wvbWN7cE@{zp!k?ZOqO~M#-$F3Cc6NhuvS` z`-Ek@imx3V5X|e`=fBZN7rUWq1MlL1+RaVw6vpmp+v5!Tr-Okd>x>}}Fn)(dD=#Mi z{m*W_Z9<3IE9e2B(K#RSpr}Z~BAw)3&b3Vb;k>V^a(ioTVb5(XzD(;wF1{fW238c{ z5K7`XJD1#Q{-K7f8R661EvxIPy@^$4?w^53rN)ok2d!WiZ<&Oy18_eKj?xcWMwf!E%%QP()WyP*E{DY&{v0!g<~CC;{}BL0tU9MFx4pRjNnSx!frE(cSZA1(&F#s_&@71y%Pps zz-S_?WPXuUW)ehc;&MZM8l=}Mii?)s+MRCbCNW95LesVuhDwyL}N9Jg@G&dducCGWqYQK@zr zBRgMFC_*eRW|wHO-l0?}EeH4V=6m#hZAEsIk{u6J0{KW&aVzVWrVwAzVIQ7#$; z!aMO3+0{={uUBFwj2jE!jJ!AeN_x?i8z$epn32ip>}jZ?$2xWNJ9V4YpED3=j~@ zk0Hu>g1>q@ZKBSFRlbSlBdfR8qu(MQHuhD9t}7U!a36bEHyVPv0vv^JYybyd4RDME z`84Wd2ZgD&pq{yC(C*Bjy6-+YpS9V19(SdJ$_a#|0=$0s9^QkTbx=K*w=PJWV8VOJ z3p>J6r}sQKID;Z0D=owekA$qN5r>x>yrhT(gQ>};)(>cN!Y=v@d$jEJ)gK$lBJ4uo z>RoJ8gYp7XAUmv>Qu!l1$I`BC*9r+J8ez`zZe>A$cv|8x!ie7yPd>4VQ_|JTTmy%)0UM5-=tOPrLmzo1DpwPAP&u z?$+G71s1xliTCZ+x%{l_#*hl)(++F&>ox*=+qY^2EJj0cH?p=O=Y2g!%ln|%b+@YF zzNF2EE}|vMz3nkXRM~Tjt?{Sq*+urrekB|P7OX9Cf8o~C2M1S+R z@*`*M*_hA-2jJPVj=9+L=3*e!c)Z9&g;!0na3Qav`TnK*D11p5r}9+2dUf@YMe_KC zemh8Np?vd#W&flGM|H~XTE@%1v~1|LAwo;<_Ib(MGmw_iIT+2oNgvO#7bF$K)1RCV zn@#XqWbIT`S5{eaQ(G*?>PE0Lqs~*ZA*2{Zt%Hz*fpeTNUQ@z zbAf4)5iTIwZc=D=vI$%^(}4zoZ<~uA{g|y-mmit2Bd1&+n@a5H&-R2u8v^~d^ui+@ z{5c9UEOW*tIjZC9LFGQ{29BvwLWm|sZb#41@w(31=&gu>-7%7@H#QWZtWJ*Th?53~;qmTB>K@kacnSV!iKl?i}Ii>W{Q4h1Eu2dhAz{eBLs!v$ zq+S}O9zEeEW?0kQydLMCcruoi;A0lN_c+WfVXYq4trypyTr~ls%^oO2#UK%3g9JNV+6uCu5`@J-CzPkiGVu zZyfFzbv|qAj*WW;RHYxBUn}Y^{`F`;+EYRG?yC)a+2mr`re5S&z*gxkuVa>3J^i2J zkHRaf?Y*2ZaXptysF2G{A{{VFqRe8A^XFn1t#hylAgD2YoB^ECm2@T;WNt5)}q=%21WziVKT>ak;5 z$fsXX)s`OM-QV{kfu}`&p(9&O!lc)KU_9aoZfo93Vao9w(J9W81U$sm`WrIOGU zVoCR|@ELk}?5K{@Y@74562QhfFFj#Y*eVb-X?B2M0I(eZuvi7uL$Ysk?eL06L@tXB zD&M%t%47g?1?OLoXD2q7OXTz7Xja(_08MutKN7Z+W)&CQt1oI2TSFGNvXbTIg1xBY zG{mTLYwgg|(UUgahD|#$Rs9De&pm4e-s6w|C~j>pm_7oxYuYMKj~0u+oxEdZ#o=8m zD6YSzNbbIt#&A<6Jvc7yYX8N-fFP42U^Fp&cI&og)rzi}ZHA{oBD2hFY*Bx?zo*xI zx8myZ=yR*cjDhHQ2$Xy-l|Fq=fQvIFWQ99jH|G)n27v9ovc;?`m_CG?^Hov?+!304 zrKd)m3DFx6^e_(?#)pHwvEh)okZIdR16_LI`Ki?`dpu*ZHcjpkgNKu1id1 zB<~R3;I(_Y&iMOsYx9a+`5%7f%3v$@5aA!Z6!53g%f9)Qr2(zFcR$&=v-_O$WYl^$ z|9X%)F|dhu3dkF_oNNrUSPR?Lr=Fmv1E(#ejqt^JSU=O z^6m76_xlsl8mB{)Y$DF0-7OpqjdmZ1rFc*Cq->g9<29~HS+xsD-e5YH@yLr+>-lX| z6@l|=yXqQqK;Lx{i(Rid+F`Ch(EEq-(*U8JNqi`5w)J|``wDC?e2xk@0T#5O`@F$E zQ$Qg@XvY#50~n3~O&!N1o8m$@JraK!?~qL1&cbxElKt`l)C*{w=`eSU0;hpxLh`9x zl*S(YDRjN_I`7bg#X{sx7c){C$(SsBv4dLUL#m(uSkjad`ib6##Jn49G$vT%-J)j~E?=*URuKo#dSyi1gfd-`0Y{q5&&VH9wbYE#XY zRMv#Oni%YEWcN8NHDceX_Ky@PM2Ivc2>HyY+$T&nB636wC>DF``ojfb4QVL z)Eo&KEiN5s(}3!J;dIz|`=t^??JlW$c}xZ65b3vRo@|0+J8F|dAZ0`uyS^64MV}K| zPBXU`6)S#ni>QHR^J@Rt=wsGK`e1N>Vy;R%+GEyfc%lPy8BhCUFadWp(c%ivi$jc0lP%ZZ;>XzF2EoHG)y}{eq8)|vzIDotfnF!c&hp8OTl{* zs`^)i4f)4>4gel*@y@UqD808QdO3j;5|o8=ZsV-<-8^)5X!1_&s=c+sRX@u&T_d`3 zmO=K<%dD@x#q?a2P-o-TE}wrxKVh2EKyiwA&=Ml&b6dXKJu(hQ&JqFDKQlQ6S+?Fx_&00*B?8lS%za4~`E!tkmj zx#QJbWlAD1Ppdp2Z8sE0He0z&muHxv?;Tjak>HgUERYlrG93`tI#4l9(1k&+Q6lor zgHM|t=jN_(uPgglewxkL&Je-@KDOD_LhOkXi%0L`z{(nH(`QQU?Gq~$?A<;th^u^b zk=I{;P9+AKByyE};UIUG zU#+t=?VWB_Q6kmW?@Vc#@1^VDR#kC=0od9Yo7)Gk6ArEaY7|z~OtgZ8y7G&3Duj8= z=A5C;K`5}8=-alx&E~&h*WQP<0eeah7S$;*W-PD;^EyD#p9ai#yc2{5^vi8xI)LJI ztgeC&eW(qvs#QS9lf%QZLgx=rP`m11Y$QvPvyW3oTu?p3xESH9zQ_ZLzey9H^yte6zO95bRDdq_&Z|JD-!^nJwR(PZ)@y zrE1=sd^-BpbI(CAS6`Z#Ebxhd-t=AfN6ww%<9K+@P|eJ|+*nsI%)0wL31jVbsFnv!;tiQk1SG9VNn(5_w_jIvBR{SMN}O>EEonmC6_J=Y6s z2ORl1Kc!oVMn$y4p9CxibkCMVXsc6A%I}Qk)kg@AHw)%HqD#-(;;X;F~zS`pan5%bk z{p7`)6%W4TeE<2`5v)IPec;6UEv|LJ+fRPFSRb~*In}-WeZBVaGu_Ad8alpurfz3E zAP~p&cgGh4$CfW*WrzJ}0W6jaw0D;7K0v)J3F{p+-?5HgZP#W;j{Qg;dGoWNOs%HC zxX&%w6p4i*fy{$XIWP@tI&1*YMGph=0J-4HF=i^O74E?RSPGy>t(T}GH++HKTuB^4 zTHEYF_F#jGA>}M14lQ8nSQ$c(Yl?eHXi9tzC<9-;DS!Uei2X)riA2?!G_^jaBB;Q_ zd0zDm#3|{rH?}BE8-%H|(JM~Tny~Tck`Od={#MYjNzdHg)S?6m+x2trw9z=d2A$Et zYN0OQtN3YV#QL~_&RfUUUXb%O@;EDn9}nvx`}}O2ua4OEhkm>8Eo@x<#zutI$tzLoDUQ4JTy%JCY`rX1 zbF|2Eg(crY?|*n>BZ80NSMP`;NWP9y5A?4^vDg<(A?PnhUAp&m+Y*kS1)2_kIbB*T zQ}|ql+Z79ik_&j6gm~vRQX)u2UN4sl%mIKQ696W+fMcKZMKGt5U#GH74$-mT1W*E+ zWv{~;wGb*iKpKFcN6|k76_(yGv~bQa%wOrYMspsf$_Yxb9PMf;oV<;-8Q39%*GQF8 zuAp+EbH#VQzTl|1A~yUy|0_yEdnuOX=?u4dCx0`1V(-BbmE+k*l&UAW{h@DQ zs8MdKkKyFi`)5x>i$`Ov^7R9%^@Wd=UVKJ6ojYVS~J|XmM_^dDV6%Boz9MS z;+(PlrVRg>pSpJYt~hM?mw`pWp<;Wvj7{kcvBzINSW?is5m}F83*o9hZ!Y^Sr%{JQ z%1YDa6K%)BjUTk7Uk~#}ijOX%Zv^_0g#+_Ngfx4mW9NjZAm?wHdjN_GE6TV%{if;0 zwYfpQ8lZ`poQh!pMhv0lnu9_n7Pbr=z>I)n0!-E?zexrldn~J+qURuUbwRLRfnz<| zfAW1PWO4>nl;^HpEdp8@Um|2G?y+T+-1Qhok^%=W>?xTTuFQVE?f%>%#_kI;vy^Yf z3O#j3Y!en!rHW}OI&Z(>pDq?x6PozwVvp438kGKte%q~e6iRFaZn%B%iGaIe_^UkYyIrlvxn?l`g*5e9^x6@YL z>vN#%x|B<4$)3w*Vw4Uprjp(U?mt(50c0L`usisXx5QA`oXq>BO8aX_ zhxjJ&fk}9$NCZp(vN{!DqKbfM=C_9}hvivcsSt}zzZmFmei3yy=api)W)5`uYqkB=wBdo~^HRZSosRfsnU~y);lz$$*Cy>ZnA# zzV!H|on_vPOKy2&e{NZG-L;C>{Ym0$+jbh5l_;}&Rd%Ca9iyHD<;o@G8RGJENvgZo zp3}f?#|yZJx~y^oqQDOv=tgd16I$B7Hcz_BW1^eZSYuu`1u;*wF>wcb3=?JWcB0`}4a$8DZ7jP2Odmg&}|SI_v2S4l@0%DC_>w2x$>WxPTNN znJoo&Q2BX8tI!}c4h%UQFkFln5bXInAU{_-`(DPCU<5VZHC<|`Ko&RUc2_s#VruY? zwGTjAe3yeFc$}9%nV@nS*G7B*aA1DG zQD=+!3>m@8T|}+Bb|K5XCa$@mTO&DzEPDX#NPeLdh`Pj^Z;Z;dG+V}-bmj(lhFcZ; z80Lg6PZL*+tDHC^P$RMwa%JlzA1rvGQ{FLpB`m&X^FD`cFH_mWP5HL#QkYYDEV2~? zzP=>m$G*uD9}a}W;j z;3r+WZS;ySi}xTf7G*aUoii3cJ!YuB$j-=vZKpDa{udTQ4V}XIeA}rClSlsuH~hx? z`?VQmYkQIx=6?;8d%3Lh@C)Z@nTBoI)@eFRDjpn((7lUaq)#{B8KI95nN<#sPWBB$ zsG6y%5ag~&bCo^&v@{o!Upn42Q~2a|nvB-Q%x5#gF{Gdt!aDI8J?0RQa6Ey+dnMde z&LRc1sl>=YnIhwkbprs5c*OM>^n*aQXvc_mPR_fMN#-aCb(>2zyC8KMb z25$vvxNq;R+UlOSw||DaYE<$6a3Jos1!4|o!5IbTl$V<9*!UH83zP!nS}gB-R)LU~ zpQc5%73FD~-tx{AcAUqS9d*@H?Z!1%wY}LkE#Jq+cU8ZN+)qT-d=*TaH1-Na7>OSv zn2G8NnpdZns-ID&7SwOgrh?vu`Qpm#f7!!!?^gMJ8J%@lw|Oc`{P3&g*(2&6owB=cjx-RzmDDBkwkOJZbYG?|<+3d0nZ}xUSlqfbiyjVz?cs+%{7Ntl


    ^Op)u+6kKQ% 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

    IEa0YZKKB)7d%G!*erqm;rlqWzb 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 N0{|z=8W8{}3;-H(a-0AF literal 0 HcmV?d00001 diff --git a/database/configuration/quake3/fred_free_for_all.cfg b/database/configuration/quake3/fred_free_for_all.cfg new file mode 100644 index 00000000..86c0e218 --- /dev/null +++ b/database/configuration/quake3/fred_free_for_all.cfg @@ -0,0 +1,171 @@ +// fred server for free for all. +// version 0.18 +// last significant modification: 11/8/2003 + +g_gametype 0 + // g_gametype is 0 for free for all, 1 for team deathmatch, 2 for tournament, + // and 3 for capture the flag. + +fraglimit 42 + // Sets the maximum number of frags before victory. Set this to zero to not + // have a frag limit. + +timelimit 14 + // Set the timelimit for the game (in minutes). The person with the most + // points when the timer expires is the winner. Set this to zero if there + // should not be a time limit. + +sv_maxclients 10 + // Set the maximum number of clients that can connect at once. This includes + // the bots that have been added to the game. + +sv_hostname "fredstonia" + // This is the name of this quake3 server. + +sv_allowDownload 0 + // Allow auto-download since we are on a lan in this case. The client + // needs to have the 'cl_allowdownload' flag set to 1 for it to even try. + +sets "Administrator" "fred" +sets "Email" "fred@gruntose.com" +sets "URL" "http://www.gruntose.com" +sets "Location" "Grunston" +sets "CPU" "AMD Athlon 1Ghz" + +//set requirement for client password. 1=require password, 0=no password required (Default: 0) +sv_privateClients 0 + +//set password for private server "" for no password +sv_privatePassword "" + +//set remote console password +rconpassword "" +//turn on/off pak cheat check. 1=on, 0=off (Default: 1) +sv_pure 1 + +//set max allowable rate for a client. 8000 to 10000 recommended. Max: 25000 (Default: 0) +sv_maxRate 25000 + +//add up to 4 additional master servers to report to (As you can see the ingame configs don't report to id automatically) +sv_master1 "" +sv_master2 "" +sv_master3 "" +sv_master4 "" +sv_master5 "" + +//////////////////////////////////////////////////////////////////////////// +// This is the list of maps that the server will rotate through. To change +// a map, just change the name after 'map'. To add a new map, make sure that +// you update the name after 'nextmap vstr' to point to it and that the last +// map still wraps around to the first map. +set m0001 "map 17+ ; set nextmap vstr m0002" +set m0002 "map addict ; set nextmap vstr m0003" +set m0003 "map adventuredm ; set nextmap vstr m0004" +set m0004 "map alkdm13 ; set nextmap vstr m0005" +set m0005 "map bk_thetruth ; set nextmap vstr m0006" +set m0006 "map catq3dm02 ; set nextmap vstr m0007" +set m0007 "map catq3dm03 ; set nextmap vstr m0008" +set m0008 "map cpm1 ; set nextmap vstr m0009" +set m0009 "map cpm1a ; set nextmap vstr m0010" +set m0010 "map cpm2 ; set nextmap vstr m0011" +set m0011 "map cpm4 ; set nextmap vstr m0012" +set m0012 "map cpm5 ; set nextmap vstr m0013" +set m0013 "map cpm6 ; set nextmap vstr m0014" +set m0014 "map cpm7 ; set nextmap vstr m0015" +set m0015 "map cpm9 ; set nextmap vstr m0016" +set m0016 "map cpm11 ; set nextmap vstr m0017" +set m0017 "map cpm13 ; set nextmap vstr m0018" +set m0018 "map cpm14 ; set nextmap vstr m0019" +set m0019 "map gon ; set nextmap vstr m0020" +set m0020 "map hope ; set nextmap vstr m0021" +set m0021 "map ik3dm2 ; set nextmap vstr m0022" +set m0022 "map ktsdm1 ; set nextmap vstr m0023" +set m0023 "map lae3dm1 ; set nextmap vstr m0024" +set m0024 "map LDAQ3A03DM ; set nextmap vstr m0025" +set m0025 "map LDAQ3A06DM ; set nextmap vstr m0026" +set m0026 "map mvdm04 ; set nextmap vstr m0027" +set m0027 "map obiwanshouse ; set nextmap vstr m0028" +set m0028 "map ospca1 ; set nextmap vstr m0029" +set m0029 "map ospctf1 ; set nextmap vstr m0030" +set m0030 "map ospctf2 ; set nextmap vstr m0031" +set m0031 "map ospdm1 ; set nextmap vstr m0032" +set m0032 "map ospdm2 ; set nextmap vstr m0033" +set m0033 "map ospdm3 ; set nextmap vstr m0034" +set m0034 "map ospdm4 ; set nextmap vstr m0035" +set m0035 "map ospdm5 ; set nextmap vstr m0036" +set m0036 "map ospdm6 ; set nextmap vstr m0037" +set m0037 "map ospdm7 ; set nextmap vstr m0038" +set m0038 "map ospdm8 ; set nextmap vstr m0039" +set m0039 "map ospdm9 ; set nextmap vstr m0040" +set m0040 "map ospdm10 ; set nextmap vstr m0041" +set m0041 "map ospdm11 ; set nextmap vstr m0042" +set m0042 "map ospdm12 ; set nextmap vstr m0043" +set m0043 "map pands ; set nextmap vstr m0044" +set m0044 "map pjw3dm3 ; set nextmap vstr m0045" +set m0045 "map pjw3tourney2 ; set nextmap vstr m0046" +set m0046 "map pjw3tourney2f ; set nextmap vstr m0047" +set m0047 "map polo3dm3 ; set nextmap vstr m0048" +set m0048 "map polo3dm5 ; set nextmap vstr m0049" +set m0049 "map pqarena ; set nextmap vstr m0050" +set m0050 "map pureskillz1 ; set nextmap vstr m0051" +set m0051 "map q3fex5 ; set nextmap vstr m0052" +set m0052 "map q3mbd ; set nextmap vstr m0053" +set m0053 "map q3mexx1 ; set nextmap vstr m0054" +set m0054 "map q3tbdm4 ; set nextmap vstr m0055" +set m0055 "map q3tbds1 ; set nextmap vstr m0056" +set m0056 "map railroad ; set nextmap vstr m0057" +set m0057 "map rdogdm4 ; set nextmap vstr m0058" +set m0058 "map reloader ; set nextmap vstr m0059" +set m0059 "map rqm3arena2 ; set nextmap vstr m0060" +set m0060 "map shad3dm1 ; set nextmap vstr m0061" +set m0061 "map simetrik ; set nextmap vstr m0062" +set m0062 "map spk3dm1 ; set nextmap vstr m0063" +set m0063 "map storm3tourney1 ; set nextmap vstr m0064" +set m0064 "map tep ; set nextmap vstr m0065" +set m0065 "map teqdm1 ; set nextmap vstr m0066" +set m0066 "map teqdm2 ; set nextmap vstr m0067" +set m0067 "map wintergames ; set nextmap vstr m0070" +set m0068 "map yog3dm2 ; set nextmap vstr m0071" +set m0069 "map yog3dm4 ; set nextmap vstr m0072" +set m0070 "map yurch ; set nextmap vstr m0073" +set m0071 "map ztn3dm1 ; set nextmap vstr m0074" +set m0072 "map ztn3dm1-ho ; set nextmap vstr m0075" +set m0073 "map ztn3dm2 ; set nextmap vstr m0001" + +// now start the server on the first map... after this point, the settings +// apply to the game we have started. some settings will only work properly +// after this point. +vstr m0009 + +//////////////////////////////////////////////////////////////////////////// + +g_motd "The Fraggingest Place on Earth" + // the message of the day, played for people connecting. + +//set weapon respawn times for free for all in seconds. May need to decrease for large number of clients. (Default: 5) +g_weaponrespawn 4 + +//set voting. 1=on, 0=off (Default: 1) +g_allowvote 1 + +//set power multiples for quad damage (Default: 3) +g_quadfactor 3 + +//synchronous clients is necessary to allow client demo recording. Setting to 1 allows recording but play is not smooth and is not recommended for normal use. (Default: 0) +g_synchronousClients 0 + +//additions by Dekard- Anything with sets will be seen in the server settings in gamespy and in game for server settings. +//If you are using special maps here would be a good place to add where to download them!! +//sets "mappack" "http://www.myserver.com/mappack.zip" + +//reduce the zoom factor to allow farther zooming. +zoom 10 + +addbot bender +addbot uriel +addbot phobos +addbot klesk +addbot orbb +addbot xaero +addbot sandtrooper + diff --git a/database/configuration/quake3/fred_t_hamster_maps.cfg b/database/configuration/quake3/fred_t_hamster_maps.cfg new file mode 100644 index 00000000..32eaa467 --- /dev/null +++ b/database/configuration/quake3/fred_t_hamster_maps.cfg @@ -0,0 +1,75 @@ +// test config file for dan's server. +//set Gametype to 0 for Free for All Mode +g_gametype 0 +//Free For All Settings Below +//set ffa fraglimit. Set to 0 for no limit. +fraglimit 42 +//set ffa timelimit. Set to 0 for no limit. +timelimit 14 +//set Maximum number of clients +sv_maxclients 8 +//set host name that shows up in server list. Change below to your server name preference and remove the // +sv_hostname "fredstonia" +//set message of the day that players see while connecting to the server. Must be set before level loads. Change below to your message of the day preference and remove the // +g_motd "The Fraggingest Place on Earth" +//set requirement for client password. 1=require password, 0=no password required (Default: 0) +sv_privateClients 0 +//set password for private server "" for no password +sv_privatePassword "" +//set remote console password +rconpassword "" +//turn on/off pak cheat check. 1=on, 0=off (Default: 1) +sv_pure 1 +//set max allowable rate for a client. 8000 to 10000 recommended. Max: 25000 (Default: 0) +sv_maxRate 10000 +//add up to 4 additional master servers to report to (As you can see the ingame configs don't report to id automatically) +sv_master1 "" +sv_master2 "" +sv_master3 "" +sv_master4 "" +sv_master5 "" +//Set map selection, cycle order, and load first map for free for all +set d1 "map metro ; set nextmap vstr d2" +set d2 "map coralctf ; set nextmap vstr d3" +set d3 "map addict ; set nextmap vstr d4" +set d4 "map antilogic ; set nextmap vstr d5" +set d5 "map bk_thetruth ; set nextmap vstr d6" +set d6 "map bluemonday ; set nextmap vstr d7" +set d7 "map falloutbunker ; set nextmap vstr d8" +set d8 "map hydrogen ; set nextmap vstr d9" +set d9 "map ik3dm1 ; set nextmap vstr d10" +set d10 "map ik3dm2 ; set nextmap vstr d11" +set d11 "map 17+ ; set nextmap vstr d12" +set d12 "map yog3dm4 ; set nextmap vstr d13" +set d13 "map lae3dm1 ; set nextmap vstr d14" +set d14 "map q3mbd ; set nextmap vstr d15" +set d15 "map teqdm1 ; set nextmap vstr d16" +set d16 "map wintergames ; set nextmap vstr d17" +set d17 "map obiwanshouse ; set nextmap vstr d18" +set d18 "map she'k ; set nextmap vstr d1" +vstr d1 +//set weapon respawn times for free for all in seconds. May need to decrease for large number of clients. (Default: 5) +g_weaponrespawn 4 +//set voting. 1=on, 0=off (Default: 1) +g_allowvote 1 +//set power multiples for quad damage (Default: 3) +g_quadfactor 3 +//syncronous clients is necessary to allow client demo recording. Setting to 1 allows recording but play is not smooth and is not recommended for normal use. (Default: 0) +g_syncronousClients 0 +//additions by Dekard- Anything with sets will be seen in the server settings in gamespy and in game for server settings. +//If you are using special maps here would be a good place to add where to download them!! +sets "Administrator" "fred" +sets "Email" "fred@gruntose.com" +sets "URL" "http://www.gruntose.com" +sets "Location" "Grunston" +sets "CPU" "AMD Athlon 1Ghz" +//sets "mappack" "http://www.myserver.com/mappack.zip" + +//addbot stormtrooper +addbot legoman +addbot uriel +addbot phobos +addbot klesk +addbot orbb + + diff --git a/database/configuration/quake3/quake3_server_start.txt b/database/configuration/quake3/quake3_server_start.txt new file mode 100644 index 00000000..eec214ce --- /dev/null +++ b/database/configuration/quake3/quake3_server_start.txt @@ -0,0 +1,15 @@ + +this is an example of running quake3 in dedicated server mode and with +no console. the config file can be swapped out for other ones. + +nohup quake3 +set dedicated 1 +exec fred_free_for_all.cfg & + +the fred_...cfg file needs to be in the quake3/baseq3 directory unless +the directory where it is stored is pointed at using q3 command line options +that i don't know but which i have heard of. + +this just automates the copying of the config file before running quake. + +cp ~/yeti/quake3/fred_free_for_all.cfg /usr/local/games/quake3/baseq3/ ; nohup quake3 +set dedicated 1 +exec fred_free_for_all.cfg + + diff --git a/database/configuration/rc.local/example_rc.local_with_zooty_alerter b/database/configuration/rc.local/example_rc.local_with_zooty_alerter new file mode 100644 index 00000000..8e969487 --- /dev/null +++ b/database/configuration/rc.local/example_rc.local_with_zooty_alerter @@ -0,0 +1,21 @@ +#!/bin/sh -e +# +# rc.local +# +# This script is executed at the end of each multiuser runlevel. +# Make sure that the script will "exit 0" on success or any other +# value on error. +# +# In order to enable or disable this script just change the execution +# bits. +# +# By default this script does nothing. + +#### + +# special for equanimity; get zooty informed of our IP so we can make +# the back connection and get home. +bash /home/fred/yeti/scripts/security/tell_zooty_our_ip.sh &>/tmp/zooty_tell_report.txt & + # we put this in the background and hope it doesn't intrude too much. + +exit 0 diff --git a/database/configuration/routing/gated.conf b/database/configuration/routing/gated.conf new file mode 100644 index 00000000..8a7d1951 --- /dev/null +++ b/database/configuration/routing/gated.conf @@ -0,0 +1,24 @@ +# gated example configuration. + +interfaces { + # passive keeps the route from dying if there's no communicating router. + interface eth0 passive ; + interface eth1 passive ; +}; + +rip yes { + interface all noripin noripout; + interface eth0 ripin ripout version 2 broadcast; + interface eth1 ripin ripout version 2 broadcast; +}; + +import proto rip { + all ; + default restrict ; +}; + +export proto rip { + proto direct ; + proto static metric 1; +}; + diff --git a/database/configuration/routing/routing_setup.conf b/database/configuration/routing/routing_setup.conf new file mode 100644 index 00000000..4b1fdc27 --- /dev/null +++ b/database/configuration/routing/routing_setup.conf @@ -0,0 +1,22 @@ +#!/bin/sh + +# this could be invoked by the rc.local to set up routes. + +# add the routes for this machine. +####/sbin/route >/root/route_list_before_add.txt +# rip out bogus routes first. +####/sbin/route del default +####/sbin/route del -net 14.28.42.0 netmask 255.255.255.0 +####/sbin/route del -net 14.28.42.0 netmask 255.255.255.0 +# add in the correct routes. +####/sbin/route add router eth0 +####/sbin/route add 14.28.42.71 eth0 +####/sbin/route add -net 14.28.42.0 netmask 255.255.255.0 eth1 +####/sbin/route add -net 14.28.42.128 netmask 255.255.255.0 gw 14.28.42.5 eth1 +####/sbin/route add default gw 14.28.42.71 +####/sbin/route >/root/route_list_after_add.txt + +# add ipx routes. +###/usr/bin/ipx_interface add eth1 802.2 0x2 +###/usr/bin/ipx_interface add eth1 802.2 0x3 + diff --git a/database/configuration/samba/bartron.smb.conf b/database/configuration/samba/bartron.smb.conf new file mode 100644 index 00000000..45a5b4cb --- /dev/null +++ b/database/configuration/samba/bartron.smb.conf @@ -0,0 +1,297 @@ +# This is the main Samba configuration file. You should read the +# smb.conf(5) manual page in order to understand the options listed +# here. Samba has a huge number of configurable options (perhaps too +# many!) most of which are not shown in this example +# +# Any line which starts with a ; (semi-colon) or a # (hash) +# is a comment and is ignored. In this example we will use a # +# for commentry and a ; for parts of the config file that you +# may wish to enable +# +# NOTE: Whenever you modify this file you should run the command "testparm" +# to check that you have not many any basic syntactic errors. +# +#======================= Global Settings ===================================== +[global] + +# workgroup = NT-Domain-Name or Workgroup-Name + workgroup = TwainNet + +# server string is the equivalent of the NT Description field + server string = Bartron the Gateway + +# This option is important for security. It allows you to restrict +# connections to machines which are on your local network. The +# following example restricts access to two C class networks and +# the "loopback" interface. For more examples of the syntax see +# the smb.conf man page +; hosts allow = 192.168.1. 192.168.2. 127. + hosts allow = 14.28.42. 127. + +# if you want to automatically load your printer list rather +# than setting them up individually then you'll need this + printcap name = /etc/printcap + load printers = yes + +# It should not be necessary to spell out the print system type unless +# yours is non-standard. Currently supported print systems include: +# bsd, sysv, plp, lprng, aix, hpux, qnx +; printing = bsd + +# Uncomment this if you want a guest account, you must add this to /etc/passwd +# otherwise the user "nobody" is used +; guest account = pcguest + +# this tells Samba to use a separate log file for each machine +# that connects + log file = /var/log/samba/log.%m + +# Put a capping on the size of the log files (in Kb). + max log size = 100 + +# Security mode. Most people will want user level security. See +# security_level.txt for details. + security = user +# Use password server option only with security = server +; password server = + +# Password Level allows matching of _n_ characters of the password for +# all combinations of upper and lower case. +; password level = 8 +; username level = 8 + +# You may wish to use password encryption. Please read +# ENCRYPTION.txt, Win95.txt and WinNT.txt in the Samba documentation. +# Do not enable this option unless you have read those documents + encrypt passwords = yes + smb passwd file = /etc/smbpasswd + +# Unix users can map to different SMB User names +; username map = /etc/smbusers + +# Using the following line enables you to customise your configuration +# on a per machine basis. The %m gets replaced with the netbios name +# of the machine that is connecting +; include = /etc/smb.conf.%m + +# Most people will find that this option gives better performance. +# See speed.txt and the manual pages for details + socket options = TCP_NODELAY + +# Configure Samba to use multiple interfaces +# If you have multiple network interfaces then you must list them +# here. See the man page for details. +; interfaces = 192.168.12.2/24 192.168.13.2/24 + +# Configure remote browse list synchronisation here +# request announcement to, or browse list sync from: +# a specific host or from / to a whole subnet (see below) +; remote browse sync = 192.168.3.25 192.168.5.255 +# Cause this host to announce itself to local subnets here +; remote announce = 192.168.1.255 192.168.2.44 + +# Browser Control Options: +# set local master to no if you don't want Samba to become a master +# browser on your network. Otherwise the normal election rules apply + local master = yes + +# OS Level determines the precedence of this server in master browser +# elections. The default value should be reasonable + os level = 34 + +# Domain Master specifies Samba to be the Domain Master Browser. This +# allows Samba to collate browse lists between subnets. Don't use this +# if you already have a Windows NT domain controller doing this job + domain master = yes + +# Preferred Master causes Samba to force a local browser election on startup +# and gives it a slightly higher chance of winning the election + preferred master = yes + +# Use only if you have an NT server on your network that has been +# configured at install time to be a primary domain controller. +; domain controller = + +# Enable this if you want Samba to be a domain logon server for +# Windows95 workstations. + domain logons = yes + +# if you enable domain logons then you may want a per-machine or +# per user logon script +# run a specific logon batch file per workstation (machine) +; logon script = %m.bat +# run a specific logon batch file per username +; logon script = %U.bat + +# Where to store roving profiles (only for Win95 and WinNT) +# %L substitutes for this servers netbios name, %U is username +# You must uncomment the [Profiles] share below +; logon path = \\%L\Profiles\%U + +# All NetBIOS names must be resolved to IP Addresses +# 'Name Resolve Order' allows the named resolution mechanism to be specified +# the default order is "host lmhosts wins bcast". "host" means use the unix +# system gethostbyname() function call that will use either /etc/hosts OR +# DNS or NIS depending on the settings of /etc/host.config, /etc/nsswitch.conf +# and the /etc/resolv.conf file. "host" therefore is system configuration +# dependant. This parameter is most often of use to prevent DNS lookups +# in order to resolve NetBIOS names to IP Addresses. Use with care! +# The example below excludes use of name resolution for machines that are NOT +# on the local network segment +# - OR - are not deliberately to be known via lmhosts or via WINS. +; name resolve order = wins lmhosts bcast + +# Windows Internet Name Serving Support Section: +# WINS Support - Tells the NMBD component of Samba to enable it's WINS Server + wins support = yes + +# WINS Server - Tells the NMBD components of Samba to be a WINS Client +# Note: Samba can be either a WINS Server, or a WINS Client, but NOT both +; wins server = w.x.y.z + +# WINS Proxy - Tells Samba to answer name resolution queries on +# behalf of a non WINS capable client, for this to work there must be +# at least one WINS Server on the network. The default is NO. +; wins proxy = yes + +# DNS Proxy - tells Samba whether or not to try to resolve NetBIOS names +# via DNS nslookups. The built-in default for versions 1.9.17 is yes, +# this has been changed in version 1.9.18 to no. + dns proxy = no + +# Case Preservation can be handy - system default is _no_ +# NOTE: These can be set on a per share basis +; preserve case = no +; short preserve case = no +# Default case is normally upper case for all DOS files +; default case = lower +# Be very careful with case sensitivity - it can break things! +; case sensitive = no + +#============================ Share Definitions ============================== +[homes] + comment = Home Directories + browseable = no + writable = yes + +# Un-comment the following and create the netlogon directory for Domain Logons +[netlogon] + comment = Network Logon Service + path = /home/netlogon + guest ok = yes + writable = no + share modes = no + + +# Un-comment the following to provide a specific roving profile share +# the default is to use the user's home directory +;[Profiles] +; path = /home/profiles +; browseable = no +; guest ok = yes + + +# NOTE: If you have a BSD-style print system there is no need to +# specifically define each individual printer +[printers] + comment = All Printers + path = /var/spool/samba + browseable = no +# Set public = yes to allow user 'guest account' to print + guest ok = no + writable = no + printable = yes + +# This one is useful for people to share files +;[tmp] +; comment = Temporary file space +; path = /tmp +; read only = no +; public = yes + +# A publicly accessible directory, but read only, except for people in +# the "staff" group +;[public] +; comment = Public Stuff +; path = /home/samba +; public = yes +; writable = yes +; printable = no +; write list = @staff + +# Other examples. +# +# A private printer, usable only by fred. Spool data will be placed in fred's +# home directory. Note that fred must have write access to the spool directory, +# wherever it is. +;[fredsprn] +; comment = Fred's Printer +; valid users = fred +; path = /homes/fred +; printer = freds_printer +; public = no +; writable = no +; printable = yes + +# A private directory, usable only by fred. Note that fred requires write +# access to the directory. +;[fredsdir] +; comment = Fred's Service +; path = /usr/somewhere/private +; valid users = fred +; public = no +; writable = yes +; printable = no + +# a service which has a different directory for each machine that connects +# this allows you to tailor configurations to incoming machines. You could +# also use the %u option to tailor it by user name. +# The %m gets replaced with the machine name that is connecting. +;[pchome] +; comment = PC Directories +; path = /usr/pc/%m +; public = no +; writable = yes + +# A publicly accessible directory, read/write to all users. Note that all files +# created in the directory by users will be owned by the default user, so +# any user with access can delete any other user's files. Obviously this +# directory must be writable by the default user. Another user could of course +# be specified, in which case all files would be owned by that user instead. +;[public] +; path = /usr/somewhere/else/public +; public = yes +; only guest = yes +; writable = yes +; printable = no + +# The following two entries demonstrate how to share a directory so that two +# users can place files there that will be owned by the specific users. In this +# setup, the directory should be writable by both users and should have the +# sticky bit set on it to prevent abuse. Obviously this could be extended to +# as many users as required. +;[myshare] +; comment = Mary's and Fred's stuff +; path = /usr/somewhere/shared +; valid users = mary fred +; public = no +; writable = yes +; printable = no +; create mask = 0765 + +[top] + comment = BartronTop + path = / + valid users = fred + public = no + writable = yes + printable = no + +[stuffing] + comment = Stuffings + path = /home/fred/stuffing + public = no + writable = yes + printable = no + valid users = fred + diff --git a/database/configuration/samba/smb_mount_in_fstab.txt b/database/configuration/samba/smb_mount_in_fstab.txt new file mode 100644 index 00000000..388da388 --- /dev/null +++ b/database/configuration/samba/smb_mount_in_fstab.txt @@ -0,0 +1,27 @@ + +//gulliver/build /home/fred/gullybuild cifs uid=1000,gid=1001,noauto,credentials=/etc/gulliver_credentials.txt 0 0 + # in this example, a box named gulliver has an SMB share called "build". + # we are mounting that to a local directory /home/fred/gullybuild. + # the uid/gid items should be set to the actual user id and group id for + # whatever user will need to access and modify those files. the credentials + # file is used to specify authentication parameters for the share. an + # example of the credential file format is below. + +############### + +# example credentials file: +# the information below is all that should be needed to access the share on +# gulliver. note that the user can contain a windows domain. +# this file does not go in /etc/fstab! it should be a separate file in the +# /etc directory or some other safe place. the file permissions should only +# allow root to read/write the file, such as when these three commands +# are used to set the ownership and permissions: +# chown root /etc/gulliver_credentials.txt +# chgrp root /etc/gulliver_credentials.txt +# chmod 600 /etc/gulliver_credentials.txt + +## /etc/gulliver_credentials.txt +username=sluggocorp\dhughley +password=semjendradok89 +workgroup=smezzo_group + diff --git a/database/configuration/samba/thewheel.smb.conf b/database/configuration/samba/thewheel.smb.conf new file mode 100644 index 00000000..fc10bddc --- /dev/null +++ b/database/configuration/samba/thewheel.smb.conf @@ -0,0 +1,315 @@ +# This is the main Samba configuration file. You should read the +# smb.conf(5) manual page in order to understand the options listed +# here. Samba has a huge number of configurable options (perhaps too +# many!) most of which are not shown in this example +# +# Any line which starts with a ; (semi-colon) or a # (hash) +# is a comment and is ignored. In this example we will use a # +# for commentry and a ; for parts of the config file that you +# may wish to enable +# +# NOTE: Whenever you modify this file you should run the command "testparm" +# to check that you have not many any basic syntactic errors. +# +#======================= Global Settings ===================================== +[global] + +# workgroup = NT-Domain-Name or Workgroup-Name + workgroup = TwainNet + +# server string is the equivalent of the NT Description field + server string = TheWheel Code Archiver + +# This option is important for security. It allows you to restrict +# connections to machines which are on your local network. The +# following example restricts access to two C class networks and +# the "loopback" interface. For more examples of the syntax see +# the smb.conf man page +; hosts allow = 192.168.1. 192.168.2. 127. + hosts allow = 14.28.42. 127. + +# if you want to automatically load your printer list rather +# than setting them up individually then you'll need this + printcap name = /etc/printcap + load printers = yes + +# It should not be necessary to spell out the print system type unless +# yours is non-standard. Currently supported print systems include: +# bsd, sysv, plp, lprng, aix, hpux, qnx +; printing = bsd + +# Uncomment this if you want a guest account, you must add this to /etc/passwd +# otherwise the user "nobody" is used +; guest account = pcguest + +# this tells Samba to use a separate log file for each machine +# that connects + log file = /var/log/samba/log.%m + +# Put a capping on the size of the log files (in Kb). + max log size = 50 + +# Security mode. Most people will want user level security. See +# security_level.txt for details. + security = user +# Use password server option only with security = server +; password server = + +# Password Level allows matching of _n_ characters of the password for +# all combinations of upper and lower case. +; password level = 8 +; username level = 8 + +# You may wish to use password encryption. Please read +# ENCRYPTION.txt, Win95.txt and WinNT.txt in the Samba documentation. +# Do not enable this option unless you have read those documents + encrypt passwords = yes + smb passwd file = /etc/smbpasswd + +# The following are needed to allow password changing from Windows to +# update the Linux sytsem password also. +# NOTE: Use these with 'encrypt passwords' and 'smb passwd file' above. +# NOTE2: You do NOT need these to allow workstations to change only +# the encrypted SMB passwords. They allow the Unix password +# to be kept in sync with the SMB password. +; unix password sync = Yes +; passwd program = /usr/bin/passwd %u +; passwd chat = *New*UNIX*password* %n\n *ReType*new*UNIX*password* %n\n *passwd:*all*authentication*tokens*updated*successfully* + +# Unix users can map to different SMB User names +; username map = /etc/smbusers + +# Using the following line enables you to customise your configuration +# on a per machine basis. The %m gets replaced with the netbios name +# of the machine that is connecting +; include = /etc/smb.conf.%m + +# Most people will find that this option gives better performance. +# See speed.txt and the manual pages for details + socket options = TCP_NODELAY + +# Configure Samba to use multiple interfaces +# If you have multiple network interfaces then you must list them +# here. See the man page for details. +; interfaces = 192.168.12.2/24 192.168.13.2/24 + +# Configure remote browse list synchronisation here +# request announcement to, or browse list sync from: +# a specific host or from / to a whole subnet (see below) +; remote browse sync = 192.168.3.25 192.168.5.255 +# Cause this host to announce itself to local subnets here +; remote announce = 192.168.1.255 192.168.2.44 + +# Browser Control Options: +# set local master to no if you don't want Samba to become a master +# browser on your network. Otherwise the normal election rules apply +; local master = no + +# OS Level determines the precedence of this server in master browser +# elections. The default value should be reasonable +; os level = 33 + +# Domain Master specifies Samba to be the Domain Master Browser. This +# allows Samba to collate browse lists between subnets. Don't use this +# if you already have a Windows NT domain controller doing this job +; domain master = yes + +# Preferred Master causes Samba to force a local browser election on startup +# and gives it a slightly higher chance of winning the election +; preferred master = yes + +# Use only if you have an NT server on your network that has been +# configured at install time to be a primary domain controller. +; domain controller = + +# Enable this if you want Samba to be a domain logon server for +# Windows95 workstations. +; domain logons = yes + +# if you enable domain logons then you may want a per-machine or +# per user logon script +# run a specific logon batch file per workstation (machine) +; logon script = %m.bat +# run a specific logon batch file per username +; logon script = %U.bat + +# Where to store roving profiles (only for Win95 and WinNT) +# %L substitutes for this servers netbios name, %U is username +# You must uncomment the [Profiles] share below +; logon path = \\%L\Profiles\%U + +# All NetBIOS names must be resolved to IP Addresses +# 'Name Resolve Order' allows the named resolution mechanism to be specified +# the default order is "host lmhosts wins bcast". "host" means use the unix +# system gethostbyname() function call that will use either /etc/hosts OR +# DNS or NIS depending on the settings of /etc/host.config, /etc/nsswitch.conf +# and the /etc/resolv.conf file. "host" therefore is system configuration +# dependant. This parameter is most often of use to prevent DNS lookups +# in order to resolve NetBIOS names to IP Addresses. Use with care! +# The example below excludes use of name resolution for machines that are NOT +# on the local network segment +# - OR - are not deliberately to be known via lmhosts or via WINS. +; name resolve order = wins lmhosts bcast + +# Windows Internet Name Serving Support Section: +# WINS Support - Tells the NMBD component of Samba to enable it's WINS Server +; wins support = yes + +# WINS Server - Tells the NMBD components of Samba to be a WINS Client +# Note: Samba can be either a WINS Server, or a WINS Client, but NOT both +; wins server = w.x.y.z + +# WINS Proxy - Tells Samba to answer name resolution queries on +# behalf of a non WINS capable client, for this to work there must be +# at least one WINS Server on the network. The default is NO. +; wins proxy = yes + +# DNS Proxy - tells Samba whether or not to try to resolve NetBIOS names +# via DNS nslookups. The built-in default for versions 1.9.17 is yes, +# this has been changed in version 1.9.18 to no. + dns proxy = no + +# Case Preservation can be handy - system default is _no_ +# NOTE: These can be set on a per share basis +; preserve case = no +; short preserve case = no +# Default case is normally upper case for all DOS files +; default case = lower +# Be very careful with case sensitivity - it can break things! +; case sensitive = no + +#============================ Share Definitions ============================== +[homes] + comment = Home Directories + browseable = no + writable = yes + +# Un-comment the following and create the netlogon directory for Domain Logons +; [netlogon] +; comment = Network Logon Service +; path = /home/netlogon +; guest ok = yes +; writable = no +; share modes = no + + +# Un-comment the following to provide a specific roving profile share +# the default is to use the user's home directory +;[Profiles] +; path = /home/profiles +; browseable = no +; guest ok = yes + + +# NOTE: If you have a BSD-style print system there is no need to +# specifically define each individual printer +[printers] + comment = All Printers + path = /var/spool/samba + browseable = no +# Set public = yes to allow user 'guest account' to print + guest ok = no + writable = no + printable = yes + +# This one is useful for people to share files +;[tmp] +; comment = Temporary file space +; path = /tmp +; read only = no +; public = yes + +# A publicly accessible directory, but read only, except for people in +# the "staff" group +;[public] +; comment = Public Stuff +; path = /home/samba +; public = yes +; writable = yes +; printable = no +; write list = @staff + +# Other examples. +# +# A private printer, usable only by fred. Spool data will be placed in fred's +# home directory. Note that fred must have write access to the spool directory, +# wherever it is. +;[fredsprn] +; comment = Fred's Printer +; valid users = fred +; path = /homes/fred +; printer = freds_printer +; public = no +; writable = no +; printable = yes + +# A private directory, usable only by fred. Note that fred requires write +# access to the directory. +;[fredsdir] +; comment = Fred's Service +; path = /usr/somewhere/private +; valid users = fred +; public = no +; writable = yes +; printable = no + +# a service which has a different directory for each machine that connects +# this allows you to tailor configurations to incoming machines. You could +# also use the %u option to tailor it by user name. +# The %m gets replaced with the machine name that is connecting. +;[pchome] +; comment = PC Directories +; path = /usr/pc/%m +; public = no +; writable = yes + +# A publicly accessible directory, read/write to all users. Note that all files +# created in the directory by users will be owned by the default user, so +# any user with access can delete any other user's files. Obviously this +# directory must be writable by the default user. Another user could of course +# be specified, in which case all files would be owned by that user instead. +;[public] +; path = /usr/somewhere/else/public +; public = yes +; only guest = yes +; writable = yes +; printable = no + +# The following two entries demonstrate how to share a directory so that two +# users can place files there that will be owned by the specific users. In this +# setup, the directory should be writable by both users and should have the +# sticky bit set on it to prevent abuse. Obviously this could be extended to +# as many users as required. +;[myshare] +; comment = Mary's and Fred's stuff +; path = /usr/somewhere/shared +; valid users = mary fred +; public = no +; writable = yes +; printable = no +; create mask = 0765 + +[top] + comment = TheWheelTop + path = / + valid users = fred + public = no + writable = yes + printable = no + +[data] + comment = Data + path = /home/fred/data + public = no + writable = yes + printable = no + valid users = fred + +[pinheader] + comment = zip drive + path = /mnt/zip + public = no + writable = yes + printable = no + valid users = fred + diff --git a/database/configuration/ssh/make_chroot_jail.sh b/database/configuration/ssh/make_chroot_jail.sh new file mode 100644 index 00000000..40ae3d89 --- /dev/null +++ b/database/configuration/ssh/make_chroot_jail.sh @@ -0,0 +1,505 @@ +#!/bin/bash +# +# (c) Copyright by Wolfgang Fuschlberger +# +# 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. +# ( http://www.fsf.org/licenses/gpl.txt ) +##################################################################### + +# first Release: 2004-07-30 +RELEASE="2007-10-19" +# +# The latest version of the script is available at +# http://www.fuschlberger.net/programs/ssh-scp-sftp-chroot-jail/ +# +# Feedback is welcome! +# +# Thanks for Bugfixes / Enhancements to +# Michael Prokop , +# Randy K., Randy D., Jonathan Hunter and everybody else. +##################################################################### + +# +# Features: +# - enable scp and sftp in the chroot-jail +# - use one directory (default /home/jail/) as chroot for all users +# - create new accounts +# - move existing accounts to chroot +##################################################################### + +# path to sshd's config file: needed for automatic detection of the locaten of +# the sftp-server binary +SSHD_CONFIG="/etc/ssh/sshd_config" + +# Check if we are called with username or update +if [ -z "$1" ] ; then + echo + echo "ERROR: Parameter missing. Did you forget the username?" + echo "-------------------------------------------------------------" + echo + echo "USAGE:" + echo "Create new chrooted account or" + echo "add existing User to chroot-jail:" + echo "-> $0 username" + echo + echo "or specify \$SHELL and path where the jail should be located:" + echo "-> $0 username [/path/to/chroot-shell [/path/to/jail]]" + echo "Default shell = /bin/chroot-shell" + echo "Default chroot-path = /home/jail" + echo "-------------------------------------------------------------" + echo + echo "Updating files in the chroot-jail:" + echo "-> $0 update [/path/to/chroot-shell [/path/to/jail]]" + echo "-------------------------------------------------------------" + echo + echo "To uninstall:" + echo " # userdel \$USER" + echo " # rm -rf /home/jail" + echo " (this deletes all Users' files!)" + echo " # rm -f /bin/chroot-shell" + echo " manually delete the User's line from /etc/sudoers" + exit +fi + +if [ -z "$PATH" ] ; then + PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin +fi + +echo +echo Release: $RELEASE +echo + +echo "Am I root? " +if [ "$(whoami &2>/dev/null)" != "root" ] && [ "$(id -un &2>/dev/null)" != "root" ] ; then + echo " NO! + +Error: You must be root to run this script." + exit 1 +fi +echo " OK"; + +# Check existence of necessary files +echo "Checking distribution... " +if [ -f /etc/debian_version ]; + then echo " Supported Distribution found" + echo " System is running Debian Linux" + DISTRO=DEBIAN; +elif [ -f /etc/SuSE-release ]; + then echo " Supported Distribution found" + echo " System is running SuSE Linux" + DISTRO=SUSE; +elif [ -f /etc/fedora-release ]; + then echo " Supported Distribution found" + echo " System is running Fedora Linux" + DISTRO=FEDORA; +elif [ -f /etc/redhat-release ]; + then echo " Supported Distribution found" + echo " System is running Red Hat Linux" + DISTRO=REDHAT; +else echo -e " failed...........\nThis script works best on Debian, Red Hat, Fedora and SuSE Linux!\nLet's try it nevertheless....\nIf some program files cannot be found adjust the respective path in line 98\n" +#exit 1 +fi + +# Specify the apps you want to copy to the jail +if [ "$DISTRO" = SUSE ]; then + APPS="/bin/bash /bin/cp /usr/bin/dircolors /bin/ls /bin/mkdir /bin/mv /bin/rm /bin/rmdir /bin/sh /bin/su /usr/bin/groups /usr/bin/id /usr/bin/netcat /usr/bin/rsync /usr/bin/ssh /usr/bin/scp /sbin/unix_chkpwd" +elif [ "$DISTRO" = FEDORA ]; then + APPS="/bin/bash /bin/cp /usr/bin/dircolors /bin/ls /bin/mkdir /bin/mv /bin/rm /bin/rmdir /bin/sh /bin/su /usr/bin/groups /usr/bin/id /usr/bin/nc /usr/bin/rsync /usr/bin/ssh /usr/bin/scp /sbin/unix_chkpwd" +elif [ "$DISTRO" = REDHAT ]; then + APPS="/bin/bash /bin/cp /usr/bin/dircolors /bin/ls /bin/mkdir /bin/mv /bin/rm /bin/rmdir /bin/sh /bin/su /usr/bin/groups /usr/bin/id /usr/bin/nc /usr/bin/rsync /usr/bin/ssh /usr/bin/scp /sbin/unix_chkpwd" +elif [ "$DISTRO" = DEBIAN ]; then + APPS="/bin/bash /bin/cp /usr/bin/dircolors /bin/ls /bin/mkdir /bin/mv /bin/rm /bin/rmdir /bin/sh /bin/su /usr/bin/groups /usr/bin/id /usr/bin/rsync /usr/bin/ssh /usr/bin/scp /sbin/unix_chkpwd" +else + APPS="/bin/bash /bin/cp /usr/bin/dircolors /bin/ls /bin/mkdir /bin/mv /bin/rm /bin/rmdir /bin/sh /bin/su /usr/bin/groups /usr/bin/id /usr/bin/rsync /usr/bin/ssh /usr/bin/scp /usr/sbin/unix_chkpwd" +fi + +# Check existence of necessary files +echo "Checking for which... " +#if [ -f $(which which) ] ; +# not good because if which does not exist I look for an +# empty filename and get OK nevertheless +if ( test -f /usr/bin/which ) || ( test -f /bin/which ) || ( test -f /sbin/which ) || ( test -f /usr/sbin/which ); + then echo " OK"; + else echo " failed + +Please install which-binary! +" +exit 1 +fi + +echo "Checking for chroot..." +if [ $(which chroot) ]; + then echo " OK"; + else echo " failed + +chroot not found! +Please install chroot-package/binary! +" +exit 1 +fi + +echo "Checking for sudo..." +if [ $(which sudo) ]; then + echo " OK"; +else + echo " failed + +sudo not found! +Please install sudo-package/binary! +" +exit 1 +fi + +echo "Checking for dirname..." +if [ $(which dirname) ]; then + echo " OK"; +else + echo " failed + +dirname not found! +Please install dirname-binary (to be found eg in the package coreutils)! +" +exit 1 +fi + +echo "Checking for awk..." +if [ $(which awk) ]; then + echo " OK +"; +else + echo " failed + +awk not found! +Please install (g)awk-package/binary! +" +exit 1 +fi + +# get location of sftp-server binary from /etc/ssh/sshd_config +# check for existence of /etc/ssh/sshd_config and for +# (uncommented) line with sftp-server filename. If neither exists, just skip +# this step and continue without sftp-server +# +#if (test ! -f /etc/ssh/sshd_config &> /dev/null); then +# echo " +#File /etc/ssh/sshd_config not found. +#Not checking for path to sftp-server. +# "; +#else +if [ ! -f ${SSHD_CONFIG} ] +then + echo "File ${SSHD_CONFIG} not found." + echo "Not checking for path to sftp-server." + echo "Please adjust the global \$SSHD_CONFIG variable" +else + if !(grep -v "^#" ${SSHD_CONFIG} | grep -i sftp-server &> /dev/null); then + echo "Obviously no sftp-server is running on this system. +"; + else SFTP_SERVER=$(grep -v "^#" ${SSHD_CONFIG} | grep -i sftp-server | awk '{ print $3}') + fi +fi + +#if !(grep -v "^#" /etc/ssh/sshd_config | grep -i sftp-server /etc/ssh/sshd_config | awk '{ print $3}' &> /dev/null); then +APPS="$APPS $SFTP_SERVER" + +# Get accountname to create / move +CHROOT_USERNAME=$1 + +if ! [ -z "$2" ] ; then + SHELL=$2 +else + SHELL=/bin/chroot-shell +fi + +if ! [ -z "$3" ] ; then + JAILPATH=$3 +else + JAILPATH=/home/jail +fi + +# Exit if user already exists +#id $CHROOT_USERNAME > /dev/null 2>&1 && { echo "User exists."; echo "Exiting."; exit 1; } + +# Check if user already exists and ask for confirmation +# we have to trust that root knows what she is doing when saying 'yes' +if ( id $CHROOT_USERNAME > /dev/null 2>&1 ) ; then { +echo " +----------------------------- +User $CHROOT_USERNAME exists. + +Are you sure you want to modify the users home directory and lock him into the +chroot directory? +Are you REALLY sure? +Say only yes if you absolutely know what you are doing!" + read -p "(yes/no) -> " MODIFYUSER + if [ "$MODIFYUSER" != "yes" ]; then + echo " +Not entered yes. Exiting...." + exit 1 + fi +} +else + CREATEUSER="yes" +fi + +# Create $SHELL (shell for jailed accounts) +if [ -f ${SHELL} ] ; then + echo " +----------------------------- +The file $SHELL exists. +Probably it was created by this script. + +Are you sure you want to overwrite it? +(you want to say yes for example if you are running the script for the second +time when adding more than one account to the jail)" +read -p "(yes/no) -> " OVERWRITE +if [ "$OVERWRITE" != "yes" ]; then + echo " +Not entered yes. Exiting...." + exit 1 +fi +else + echo "Creating $SHELL" + echo '#!/bin/sh' > $SHELL + echo "$(which sudo) $(which chroot) $JAILPATH /bin/su - \$USER" \"\$@\" >> $SHELL + chmod 755 $SHELL +fi + +# make common jail for everybody if inexistent +if [ ! -d ${JAILPATH} ] ; then + mkdir -p ${JAILPATH} + echo "Creating ${JAILPATH}" +fi +cd ${JAILPATH} + +# Create directories in jail that do not exist yet +JAILDIRS="dev etc etc/pam.d bin home sbin usr usr/bin usr/lib" +for directory in $JAILDIRS ; do + if [ ! -d "$JAILPATH/$directory" ] ; then + mkdir $JAILPATH/"$directory" + echo "Creating $JAILPATH/$directory" + fi +done +echo + +# Comment in the following lines if your apache can't read the directories and +# uses the security contexts +# Fix security contexts so Apache can read files +#CHCON=$(which chcon) +#if [ -n "$CHCON" ] && [ -x $CHCON ]; then +# $CHCON -t home_root_t $JAILPATH/home +# $CHCON -t user_home_dir_t $JAILPATH/home/$CHROOT_USERNAME +#fi + +# Creating necessary devices +[ -r $JAILPATH/dev/urandom ] || mknod $JAILPATH/dev/urandom c 1 9 +[ -r $JAILPATH/dev/null ] || mknod -m 666 $JAILPATH/dev/null c 1 3 +[ -r $JAILPATH/dev/zero ] || mknod -m 666 $JAILPATH/dev/zero c 1 5 +[ -r $JAILPATH/dev/tty ] || mknod -m 666 $JAILPATH/dev/tty c 5 0 + +# if we only want to update the files in the jail +# skip the creation of the new account +if [ "$1" != "update" ]; then + +# Modifiy /etc/sudoers to enable chroot-ing for users +# must be removed by hand if account is deleted +echo "Modifying /etc/sudoers" +echo "$CHROOT_USERNAME ALL=NOPASSWD: $(which chroot), /bin/su - $CHROOT_USERNAME" >> /etc/sudoers + +# Define HomeDir for simple referencing +HOMEDIR="$JAILPATH/home/$CHROOT_USERNAME" + +# Create new account, setting $SHELL to the above created script and +# $HOME to $JAILPATH/home/* +if [ "$CREATEUSER" != "yes" ] ; then echo " +Not creating new User account +Modifying User \"$CHROOT_USERNAME\" +Copying files in $CHROOT_USERNAME's \$HOME to \"$HOMEDIR\" +" +usermod -d "$HOMEDIR" -m -s "$SHELL" $CHROOT_USERNAME && chmod 700 "$HOMEDIR" +fi + +if [ "$CREATEUSER" = "yes" ] ; then { +echo "Adding User \"$CHROOT_USERNAME\" to system" +useradd -m -d "$HOMEDIR" -s "$SHELL" $CHROOT_USERNAME && chmod 700 "$HOMEDIR" + +# Enter password for new account +if !(passwd $CHROOT_USERNAME); + then echo "Passwords are probably not the same, try again." + exit 1; +fi +echo +} +fi + +# Create /usr/bin/groups in the jail +echo "#!/bin/bash" > usr/bin/groups +echo "id -Gn" >> usr/bin/groups +chmod 755 usr/bin/groups + +# Add users to etc/passwd +# +# check if file exists (ie we are not called for the first time) +# if yes skip root's entry and do not overwrite the file +if [ ! -f etc/passwd ] ; then + grep /etc/passwd -e "^root" > ${JAILPATH}/etc/passwd +fi +if [ ! -f etc/group ] ; then + grep /etc/group -e "^root" > ${JAILPATH}/etc/group +# add the group for all users to etc/group (otherwise there is a nasty error +# message and probably because of that changing directories doesn't work with +# winSCP) + grep /etc/group -e "^users" >> ${JAILPATH}/etc/group +fi + +# grep the username which was given to us from /etc/passwd and add it +# to ./etc/passwd replacing the $HOME with the directory as it will then +# appear in the jail +echo "Adding User $CHROOT_USERNAME to jail" +grep -e "^$CHROOT_USERNAME:" /etc/passwd | \ + sed -e "s#$JAILPATH##" \ + -e "s#$SHELL#/bin/bash#" >> ${JAILPATH}/etc/passwd + +# if the system uses one account/one group we write the +# account's group to etc/group +grep -e "^$CHROOT_USERNAME:" /etc/group >> ${JAILPATH}/etc/group + +# write the user's line from /etc/shadow to /home/jail/etc/shadow +grep -e "^$CHROOT_USERNAME:" /etc/shadow >> ${JAILPATH}/etc/shadow +chmod 600 ${JAILPATH}/etc/shadow + +# endif for =! update +fi + +# Copy the apps and the related libs +echo "Copying necessary library-files to jail (may take some time)" + +# The original code worked fine on RedHat 7.3, but did not on FC3. +# On FC3, when the 'ldd' is done, there is a 'linux-gate.so.1' that +# points to nothing (or a 90xb.....), and it also does not pick up +# some files that start with a '/'. To fix this, I am doing the ldd +# to a file called ldlist, then going back into the file and pulling +# out the libs that start with '/' +# +# Randy K. +# +# The original code worked fine on 2.4 kernel systems. Kernel 2.6 +# introduced an internal library called 'linux-gate.so.1'. This +# 'phantom' library caused non-critical errors to display during the +# copy since the file does not actually exist on the file system. +# To fix re-direct output of ldd to a file, parse the file and get +# library files that start with / +# + +# create temporary files with mktemp, if that doesn't work for some reason use +# the old method with $HOME/ldlist[2] (so I don't have to check the existence +# of the mktemp package / binary at the beginning +# +TMPFILE1=$(mktemp) &> /dev/null || TMPFILE1="${HOME}/ldlist"; if [ -x ${TMPFILE1} ]; then mv ${TMPFILE1} ${TMPFILE1}.bak;fi +TMPFILE2=$(mktemp) &> /dev/null || TMPFILE2="${HOME}/ldlist2"; if [ -x ${TMPFILE2} ]; then mv ${TMPFILE2} ${TMPFILE2}.bak;fi + +for app in $APPS; do + # First of all, check that this application exists + if [ -x $app ]; then + # Check that the directory exists; create it if not. +# app_path=$(echo $app | sed -e 's#\(.\+\)/[^/]\+#\1#') + app_path=$(dirname $app) + if ! [ -d .$app_path ]; then + mkdir -p .$app_path + fi + + # If the files in the chroot are on the same file system as the + # original files you should be able to use hard links instead of + # copying the files, too. Symbolic links cannot be used, because the + # original files are outside the chroot. + cp -p $app .$app + + # get list of necessary libraries + ldd $app >> ${TMPFILE1} + fi +done + +# Clear out any old temporary file before we start +for libs in $(cat ${TMPFILE1}); do + frst_char="$(echo $libs | cut -c1)" + if [ "$frst_char" = "/" ]; then + echo "$libs" >> ${TMPFILE2} + fi +done +for lib in $(cat ${TMPFILE2}); do + mkdir -p .$(dirname $lib) > /dev/null 2>&1 + + # If the files in the chroot are on the same file system as the original + # files you should be able to use hard links instead of copying the files, + # too. Symbolic links cannot be used, because the original files are + # outside the chroot. + cp $lib .$lib +done + +# +# Now, cleanup the 2 files we created for the library list +# +#/bin/rm -f ${HOME}/ldlist +#/bin/rm -f ${HOME}/ldlist2 +/bin/rm -f ${TMPFILE1} +/bin/rm -f ${TMPFILE2} + +# Necessary files that are not listed by ldd. +# +# There might be errors because of files that do not exist but in the end it +# may work nevertheless (I added new file names at the end without deleting old +# ones for reasons of backward compatibility). +# So please test ssh/scp before reporting a bug. +if [ "$DISTRO" = SUSE ]; then + cp /lib/libnss_compat.so.2 /lib/libnss_files.so.2 /lib/libnss_dns.so.2 /lib/libxcrypt.so.1 ${JAILPATH}/lib/ +elif [ "$DISTRO" = FEDORA ]; then + cp /lib/libnss_compat.so.2 /lib/libnsl.so.1 /lib/libnss_files.so.2 /lib/ld-linux.so.2 /lib/ld-ldb.so.3 /lib/ld-lsb.so.3 /lib/libnss_dns.so.2 /lib/libxcrypt.so.1 ${JAILPATH}/lib/ + cp /lib/*.* ${JAILPATH}/lib/ + cp /usr/lib/libcrack.so.2 ${JAILPATH}/usr/lib/ +elif [ "$DISTRO" = REDHAT ]; then + cp /lib/libnss_compat.so.2 /lib/libnsl.so.1 /lib/libnss_files.so.2 /lib/ld-linux.so.2 /lib/ld-lsb.so.1 /lib/libnss_dns.so.2 /lib/libxcrypt.so.1 ${JAILPATH}/lib/ + # needed for scp on RHEL + echo "export LD_LIBRARY_PATH=/usr/kerberos/lib" >> ${JAILPATH}/etc/profile +elif [ "$DISTRO" = DEBIAN ]; then + cp /lib/libnss_compat.so.2 /lib/libnsl.so.1 /lib/libnss_files.so.2 /lib/libcap.so.1 /lib/libnss_dns.so.2 ${JAILPATH}/lib/ +else + cp /lib/libnss_compat.so.2 /lib/libnsl.so.1 /lib/libnss_files.so.2 /lib/libcap.so.1 /lib/libnss_dns.so.2 ${JAILPATH}/lib/ +fi + +# if you are using a 64 bit system and have strange problems with login comment +# the following lines in, perhaps it works then (motto: if you can't find the +# needed library just copy all of them) +# +#cp /lib/*.* ${JAILPATH}/lib/ +#cp /lib/lib64/*.* ${JAILPATH}/lib/lib64/ + +# if you are using PAM you need stuff from /etc/pam.d/ in the jail, +echo "Copying files from /etc/pam.d/ to jail" +cp /etc/pam.d/* ${JAILPATH}/etc/pam.d/ + +# ...and of course the PAM-modules... +echo "Copying PAM-Modules to jail" +cp -r /lib/security ${JAILPATH}/lib/ + +# ...and something else useful for PAM +cp -r /etc/security ${JAILPATH}/etc/ +cp /etc/login.defs ${JAILPATH}/etc/ + +if [ -f /etc/DIR_COLORS ] ; then + cp /etc/DIR_COLORS ${JAILPATH}/etc/ +fi + +# Don't give more permissions than necessary +chown root.root ${JAILPATH}/bin/su +chmod 700 ${JAILPATH}/bin/su + +exit + diff --git a/database/configuration/subversion/per_svn_repo/passwd b/database/configuration/subversion/per_svn_repo/passwd new file mode 100644 index 00000000..e42a448e --- /dev/null +++ b/database/configuration/subversion/per_svn_repo/passwd @@ -0,0 +1,9 @@ +### This file is an example password file for svnserve. +### Its format is similar to that of svnserve.conf. As shown in the +### example below it contains one section labelled [users]. +### The name and password for each user follow, one account per line. + +[users] +# harry = harryssecret +# sally = sallyssecret +fred = passwdgoeshere diff --git a/database/configuration/subversion/per_svn_repo/svnserve.conf b/database/configuration/subversion/per_svn_repo/svnserve.conf new file mode 100644 index 00000000..2ebab304 --- /dev/null +++ b/database/configuration/subversion/per_svn_repo/svnserve.conf @@ -0,0 +1,47 @@ +### This file controls the configuration of the svnserve daemon, if you +### use it to allow access to this repository. (If you only allow +### access through http: and/or file: URLs, then this file is +### irrelevant.) + +### Visit http://subversion.tigris.org/ for more information. + +[general] +### These options control access to the repository for unauthenticated +### and authenticated users. Valid values are "write", "read", +### and "none". The sample settings below are the defaults. +# anon-access = read +# auth-access = write +### The password-db option controls the location of the password +### database file. Unless you specify a path starting with a /, +### the file's location is relative to the directory containing +### this configuration file. +### If SASL is enabled (see below), this file will NOT be used. +### Uncomment the line below to use the default password file. +password-db = passwd +### The authz-db option controls the location of the authorization +### rules for path-based access control. Unless you specify a path +### starting with a /, the file's location is relative to the the +### directory containing this file. If you don't specify an +### authz-db, no path-based access control is done. +### Uncomment the line below to use the default authorization file. +# authz-db = authz +### This option specifies the authentication realm of the repository. +### If two repositories have the same authentication realm, they should +### have the same password database, and vice versa. The default realm +### is repository's uuid. +# realm = My First Repository + +[sasl] +### This option specifies whether you want to use the Cyrus SASL +### library for authentication. Default is false. +### This section will be ignored if svnserve is not built with Cyrus +### SASL support; to check, run 'svnserve --version' and look for a line +### reading 'Cyrus SASL authentication is available.' +# use-sasl = true +### These options specify the desired strength of the security layer +### that you want SASL to provide. 0 means no encryption, 1 means +### integrity-checking only, values larger than 1 are correlated +### to the effective key length for encryption (e.g. 128 means 128-bit +### encryption). The values below are the defaults. +# min-encryption = 0 +# max-encryption = 256 diff --git a/database/configuration/subversion/subversion_install.txt b/database/configuration/subversion/subversion_install.txt new file mode 100644 index 00000000..67c31558 --- /dev/null +++ b/database/configuration/subversion/subversion_install.txt @@ -0,0 +1,4 @@ + +https://help.ubuntu.com/community/Subversion + + diff --git a/database/configuration/subversion/svnserve b/database/configuration/subversion/svnserve new file mode 100644 index 00000000..eada7ca4 --- /dev/null +++ b/database/configuration/subversion/svnserve @@ -0,0 +1,92 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: svnserve +# Required-Start: $local_fs $syslog $remote_fs +# Required-Stop: $local_fs $syslog $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Start svnserve +### END INIT INFO + +# Author: Michal Wojciechowski + +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="svnserve" +NAME=svnserve +DAEMON=/usr/bin/$NAME +DAEMON_ARGS="-d -r /z/repo" +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME + +[ -x "$DAEMON" ] || exit 0 + +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +. /lib/init/vars.sh + +. /lib/lsb/init-functions + +do_start() +{ + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 +} + +do_stop() +{ + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + rm -f $PIDFILE + return "$RETVAL" +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + restart|force-reload) + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 + exit 3 + ;; +esac + +exit 0 + + diff --git a/database/configuration/sysctl/sysctl.conf.real_time_mods b/database/configuration/sysctl/sysctl.conf.real_time_mods new file mode 100644 index 00000000..3d74f52c --- /dev/null +++ b/database/configuration/sysctl/sysctl.conf.real_time_mods @@ -0,0 +1,4 @@ + +# real time clock config. +dev.rtc.max-user-freq=1024 + diff --git a/database/configuration/visual_studio/good_compare_string.txt b/database/configuration/visual_studio/good_compare_string.txt new file mode 100644 index 00000000..acfe735c --- /dev/null +++ b/database/configuration/visual_studio/good_compare_string.txt @@ -0,0 +1,8 @@ + +this is a kind of useful compare filter for visual studio. it drops checking of a lot +of commonly present files which in general one does not care about. + +note: this pattern will hide changes to AssemblyInfo files. we do that because these often +are updated for versions and we don't want to see every one of those as being modified. + +!AssemblyInfo.cpp;!AssemblyInfo.cs;!logs\;!waste\;!generated\;!binaries\;!objects\;!lib\;!*_version.h;!*_version.rc;!obj\;!dll\;!exe\;!obj\;!pdb\;!install\;!*.user;!*.vspscc;!tests\;!*.fb2;!*.fxb;!*.fn2;!*.res;!html\;!*.pyc diff --git a/database/configuration/web_server/example_web_redirect_htaccess b/database/configuration/web_server/example_web_redirect_htaccess new file mode 100644 index 00000000..e4bccfbc --- /dev/null +++ b/database/configuration/web_server/example_web_redirect_htaccess @@ -0,0 +1,10 @@ + +Redirect permanent /google "http://google.com" + +# so, given this file in a web directory at: http://frenziedogmatics.com/ +# a web browser pointed at: http://frenziedogmatics.com/google +# would actually just show http://google.com instead. + + + + diff --git a/database/configuration/webcam/reloader.html b/database/configuration/webcam/reloader.html new file mode 100644 index 00000000..03f2a9b2 --- /dev/null +++ b/database/configuration/webcam/reloader.html @@ -0,0 +1,21 @@ + + + + + +

    Webcamomatic Yo!

    +

    oof

    + +
    + +

    The horrendous new picture will be up shortly.

    + + + + diff --git a/database/configuration/webcam/webcamrc_example b/database/configuration/webcam/webcamrc_example new file mode 100644 index 00000000..f6b0aaa6 --- /dev/null +++ b/database/configuration/webcam/webcamrc_example @@ -0,0 +1,37 @@ + +[grab] + device = /dev/video0 + text = "webcam %Y-%m-%d %H:%M:%S" +# infofile = filename + fg_red = 255 + fg_green = 255 + fg_blue = 255 + width = 320 + height = 240 + delay = 1 + wait = 0 +# input = composite1 + norm = pal + rotate = 0 + top = 0 + left = 0 + bottom = 239 + right = 319 + quality = 85 + trigger = 100 + once = 0 + +[velma] + host = velma + user = anonymous + pass = fred@ + dir = incoming + file = webcam.jpeg + tmp = uploading.jpeg + passive = 1 + debug = 0 + auto = 0 + local = 0 + ssh = 0 + + diff --git a/database/configuration/x_win/start_fishtank.sh b/database/configuration/x_win/start_fishtank.sh new file mode 100644 index 00000000..b1c407f4 --- /dev/null +++ b/database/configuration/x_win/start_fishtank.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# this is a simple shell that can be dropped in ~/.kde/Autostart to run a +# fishtank on the root X window. +# note: you must enable desktop | behavior | general | allow programs in +# desktop window for the fishtank to show up. +xfishtank -c deepblue -f 14 -b 28 -d & + + diff --git a/database/configuration/x_win/start_xroach.sh b/database/configuration/x_win/start_xroach.sh new file mode 100644 index 00000000..5d083146 --- /dev/null +++ b/database/configuration/x_win/start_xroach.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# this is a simple shell that can be dropped in ~/.kde/Autostart to show a +# bunch of roaches on the root X window. +# note: you must enable desktop | behavior | general | allow programs in +# desktop window for the fishtank to show up. + +xroach -roaches 23 -rc purple -rgc green -speed 0.05 -squish & + + diff --git a/database/forms/check_register.odt b/database/forms/check_register.odt new file mode 100644 index 0000000000000000000000000000000000000000..79c4e0820b020f9af357714be56b0bd612886e5c GIT binary patch literal 12273 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 l{<(ErZsfnyAoG80>*`8qh<5@K5&`1J3>68 \ ( .\ (. ) \(_/ ) + ~- _) \_- ooo @ (_) @ \(_//. + / /_C (-.____) /((O)/ \ ._/\~_. +/ |_\ / / /\\\\`-----'' _|>o< |__ +| \ooooO ( \ \\ \\___/ \ `_'_', / + \ \__-| \ `)\\-^\\ ^--. /_(.(.)- _\ + \ \ ) |-`--.`--=\-\ /-//_ ' ( c D\ + \_\_) |-___/ / \ V /.~ \/\\\ (@)___/ ~| + / | / | |. /`\\_/\/ / / + / | ( C`-'` / | \/ (/ / +/_________- \ `C__-~ | / (/ / + | | | \__________| \ (/ +~ + === ====== + === OO ===== + === OO ===== + === ===== + === + #################### ## + #################### ## + ### ## + #### ## + ### ### ## + ###### ### ### ## +# # ### ### ## + # # ### ### ## + ## ### ### ## + ## ### ### ## + ### ### ##### + ### ### #### + ### #### ### + ########## ## +~ + /\ + __ /||\ __ + |\\ || //| + \\ || // + \\ || // + \\ || // + / \\||// \ + <=============**==============> + \ //||\\ / + // || \\ + // || \\ + // || \\ + |// || \\| + `-- \||/ --' + \/ +~ +[Reuters 8/16/92] SHANGHAI--A 24-year-old bus passenger, Dong Huibo, died +in the street, after tangling with one of the city's dreaded woman bus +conductors. His nightmare began inside the bus when the ticketpuncher +snarled an insult about the shape of his backside. She swore at him, +slapped his face and broke his glasses, made a grab for his testicles, then +stood back and aimed a vicious kick at his private parts. As he scrambled +out of a window, the driver--also a woman--slammed her foot on the +accelerator pedal and sent him flying. (From: Di Bi Cao) +~ +----------------------------------------- +|@@@@@@@^^~~~~~~~~~~~~~~~~~~~~^^@@@@@@@@| +|@@@@@@^ ~^ @ @@ @ @ @ I ~^@@@@@@| +|@@@@@ ~ ~~ ~I @@@@@| +|@@@@' ' _,w@< @@@@| +|@@@@ @@@@@@@@w___,w@@@@@@@@ @ @@@| +|@@@@ @@@@@@@@@@@@@@@@@@@@@@ I @@@| +|@@@@ @@@@@@@@@@@@@@@@@@@@*@[ i @@@| +|@@@@ @@@@@@@@@@@@@@@@@@@@[][ | ]@@@| +|@@@@ ~_,,_ ~@@@@@@@~ ____~ @ @@@| +|@@@@ _~ , , `@@@~ _ _`@ ]L J@@@| +|@@@@ , @@w@ww+ @@@ww``,,@w@ ][ @@@@| +|@@@@, @@@@www@@@ @@@@@@@ww@@@@@[ @@@@| +|@@@@@_|| @@@@@@P' @@P@@@@@@@@@@@[|c@@@@| +|@@@@@@w| '@@P~ P]@@@-~, ~Y@@^'],@@@@@@| +|@@@@@@@[ _ _J@@Tk ]]@@@@@@| +|@@@@@@@@,@ @@, c,,,,,,,y ,w@@[ ,@@@@@@@| +|@@@@@@@@@ i @w ====--_@@@@@ @@@@@@@@| +|@@@@@@@@@@`,P~ _ ~^^^^Y@@@@@ @@@@@@@@@| +|@@@@^^=^@@^ ^' ,ww,w@@@@@ _@@@@@@@@@@| +|@@@_xJ~ ~ , @@@@@@@P~_@@@@@@@@@@@@| +|@@ @, ,@@@,_____ _,J@@@@@@@@@@@@@| +|@@L `' ,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| +|@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| +----------------------------------------- +~ + . /\ + . / \ + / / + . / / + . / / + . / / + _ / / + \\ / / + \\/---/ + \\ / + / \\/ + / / + \ / + \/ +~ +Ronald Wilson Reagan can be rearranged into Insane Anglo Warlord +and George Herbert Walker Bush into Huge Berserk Rebel Warthog, and of +course H. Ross Perot can be rearranged into Sport Horse and Short Poser. +~ +HSIN-HSIN-MING (AFFIRMING FAITH IN MIND) + +wong@rkna50.riken.go.jp (Wong Weng Fai) posted a version of this, +and I thought I would send another, longer version. This is from +(if my memory serves) Roshi Kapleau's "Zen--Dawn in the West", +so I assume it is his translation. +~ +The Great Way is not difficult +For those who do not pick and choose. +~ +When preferences are cast aside +The Way stands clear and undisguised. +~ +But even slight distinctions made +Set Heaven and Earth far apart. +~ +If you would clearly see the truth, +Discard opinions pro and con. +~ +To founder in like and dislike +Is nothing but the mind's disease. +~ +And not to see the Way's deep truth +Disturbs the Mind's essential peace. +~ +The Way is perfect like vast space, +Where there's no lack and no excess. +~ +Our choice to choose and to reject +Prevents our seeing this simple truth. +~ +Both striving for the outer world +As well as for the inner void +Condemns us to entangled lives. +~ +Just calmly see that all is One +And by themselves false views will go. +~ +Attempts to stop activity +Only fill you with activity. +~ +Remaining in duality +You'll never know of unity. +~ +And not to know this unity +Lets conflict lead you far astray. +~ +When you assert that things are real, +You miss their true reality. +But to assert that things are void +Also misses reality. +~ +The more you talk and think on this, +The further from the truth you'll be. +~ +Cut off all useless thoughts and words +And there's nowhere you cannot go. +Returning to the root itself, +You'll find the meaning of all things. +~ +If you pursue appearances, +You overlook the primal source. +~ +Awakening is to go beyond +Both emptiness as well as form. +~ +All changes in this empty world +Seem real because of ignorance. +~ +Do not go searching for the truth, +Just let those fond opinions go. +~ +Abide not in duality; +Refrain from all pursuit of it. +~ +If there's a trace of right and wrong, +True Mind is lost, confused, distraught. +~ +From One-Mind comes duality, +But cling not even to this one. +~ +When this One-Mind rests undisturbed, +Then nothing in the world offends. +~ +And when nothing can give offense, +Then all obstructions cease to be. +~ +If all thought-objects disappear, +The thinking subject drops away. +~ +For things are things because of mind, +As mind is mind because of things. +~ +These two are merely relative, +And both at source are emptiness. +~ +In emptiness these are not two, +Yet in each are contained all forms. +~ +Once coarse and fine are seen no more, +Then how can there be taking sides? +~ +The Great Way is without limit, +Beyond the easy and the hard. +~ +But those who hold to narrow views +Are fearful and irresolute; +Their frantic haste just slows them down. +~ +If you're attached to anything, +You surely will go far astray. +~ +Just let go now of clinging mind, +And all things are just as they are. +In essence nothing goes or stays. +~ +See into the true self of things, +And you're in step with the Great Way, +Thus walking freely and undisturbed. +~ +But live in bondage to your thoughts, +And you will be confused, unclear. +~ +This heavy burden weighs you down- +O why keep judging good and bad? +~ +If you would walk the highest way, +Do not reject the sense domain. +~ +For as it is, whole and complete, +This sense world is enlightenment. +~ +The wise do not strive after goals, +But fools themselves in bondage put. +~ +The One Way knows no differences; +The foolish cling to this and that. +To seek Great Mind with thinking mind +Is certainly a grave mistake. +~ +From small mind comes rest and unrest, +But mind awakened transcends both. +~ +Delusion spawns dualities- +These dreams are naught but flowers of air- +Why work so hard at grasping them? +~ +Both gain and loss, and right and wrong- +Once and for all get rid of them. +~ +When you are no longer asleep, +All dreams will vanish by themselves. +~ +If mind does not discriminate, +All things are as they are, as one. +~ +To go to this mysterious source +Frees us from all entanglements. +~ +When all is seen with "equal mind", +To our self-nature we return. +~ +This single mind goes right beyond +All reasons and comparisons. +~ +Stop movement and there's no movement, +Stop rest and no-rest comes instead. +~ +When rest and no-rest cease to be, +Then even Oneness disappears. +This ultimate Finality's +Beyond all laws; can't be described. +~ +With single mind one with the Way, +All ego-centered strivings cease. +~ +Doubts and confusion disappear, +And so true faith pervades our life. +~ +There is no thing that clings to us, +And nothing that is left behind. +~ +All's self-revealing, void and clear, +Without exerting power of mind. +~ +Thought cannot reach this state of truth; +Here feelings are of no avail. +~ +In this true world of emptiness, +Both self and other are no more. +~ +To enter this true empty world, +Immediately affirm "Not-Two". +~ +In this "Not-Two" all is the same, +With nothing separate or outside. +~ +The wise in all times and places +Awaken to this primal truth. +~ +The Way's beyond all space, all time; +One instant is ten thousand years. +~ +Not only here, not only there, +Truth's right before your very eyes. +~ +Distinctions such as large and small +Have relevance for you no more. +~ +The largest is the smallest too- +Here limitations have no place. +~ +What is is not, what is not is- +If this is not yet clear to you, +You're still far from the inner truth. +~ +One thing is all, all things are one. +Know this and all's whole and complete. +~ +When faith and mind are not separate, +And not separate are mind and faith, +This is beyond all words, all thought. +~ +For here there is + no yesterday, + no tomorrow, + no today. +~ +You don't understand because you are a technocrat, an engineer. +You work with your hands. +I however am a visionary. I work with my mouth. + -- Random HBO Movie +~ +So we'll go to the top of the toppest blue space, +The Official Katroo Birthday Sounding-Off Place! +Come on! Open your mouth and sound off at the sky! +Shout loud at the top of your voice, + "I AM I! + ME! + I am I! + And I may not know why + But I know that I like it. + _Three cheers_! I AM I!" + -- Theodor Geisel (Dr. Seuss) +~ +no! +try not. +do, +or do not. +there is no try. + -- Yoda +~ +Human conduct is ever unreliable +until man is anchored in the Divine, +Everything in future will improve +if you are making a spiritual effort now. + -- Swami Sri Yukteswar +~ +A strange weed this be +what's restoreth my vitality. + -- Popeye +~ +Q: Is an opinion true or false? +A: Depends on whether it's mine or yours.... + -- egocentrist +~ +Would anyone ever admit that an opinion was wrong? +Wouldn't most people change the opinion instead, +having then a new, well-considered, and more correct opinion? + -- fred t. hamster +~ +Through the dark of future past, +The magician longs to see. +One chants out between two worlds, +"Fire, walk with me!" +~ +It's really very simple... I found wearing women's clothes... relaxed me. + -- Dennis Bryson +~ +the first differentiation between reality as conceived and reality as it IS +consists of an analogy: conceptions of reality (all ideas entertained by a +knower) are maps, while reality is the territory being mapped. +whether or not two individuals can be said to live in the same territory +is unclear, because their conceptions may be different enough that their maps +of reality are very different. it is also unclear whether there can be said +to be two individuals at all--if the self and other are one, then all +distinctions between selves ultimately disappear in the final analysis. + -- fred t. hamster +~ +This message partially funded by the Apathy Partnership of Earth (APE). +~ +Styrofoam never dies for as long as you live. + -- Deputy Andy Brennan, Twin Peaks +~ +? +! + -- Aleister Crowley +~ +Q: + What's subtlety? +A: + A dog howling at the moon, + not because he's a dog, + but because he wants to. + -- fred t. hamster +~ +I want to share something with you, the three +little sentences that will get you through life: + #1. Cover for me, + #2. Ooh, good idea boss! + #3. It was like that when I got here... + -- Homer Simpson to Bart +~ +you wouldn't know the truth if it bit off your nose, stuck a gasoline +nozzle into the gaping wound, filled you up with high octane, and +lit a match. +~ +In a world where children blow up children, +everyone's a threat. + -- psycho cop, on Star Trek: The Next Generation +~ +world systems +============= +environments based in chaotic realms inevitably concern themselves less with +conservation of mass or energy, ignore laws regarding increasing entropy, +cease to be rational or defined. chaotic ones within orderly realms still +recall this power and warp the space where they stand. + -- fred t. hamster +~ + ____ . _ . + /# /_\_ |\_|/__/| + | |/o\o\ / / \/ \ \ + | \\_/_/ /__|O||O|__ \ + / |_ | "I do solemnly swear by the sacred |/_ \_/\_/ _\ | +| ||\_ ~| bedpan to fix the patient, and | | (____) | || +| ||| \/ ease his wretched suffering, even \/\___/\__/ // +| |||_ until all hope is lost." (_/ || + \// | | || + || | | ||\ + ||_ \ \ //_/ + \_| o| \______// + /\___/ __ || __|| + / ||||__ (____(____) + (___)_) +~ +When they came for the Fourth Amendment I didn't say anything + because I had nothing to hide. +When they came for the Second Amendment I didn't say anything + because I didn't own a gun. +When they came for the Fifth and Sixth Amendments I didn't say anything + because I had committed no crimes. +When they came for the First Amendment I couldn't say anything. + +(taken from the Urine Nation News, spring/summer 1993, number 12, page 1) +~ +The bother of doing +something may be great, +but the bother of not doing it +while continuing to think about it +is even greater. + -- fred t. hamster +~ +Then we sat on the sand for some time and observed, +how the oceans that covered the world were perturbed, +by the tides from the orbiting moon overhead, +"How relaxing the sound of the waves is," you said. +I began to expound upon tidal effects, +when you asked me to stop, looking somewhat perplexed, +so I did not explain why the sunset turns red, +and we watched the occurrence in silence instead. + -- By Data +~ + Ode to Spot +Felis catus, Your visual, olfactory, +is your taxonomic nomenclature, and auditory senses, +an endothermic quadruped, contribute to your hunting skills, +carnivorous by nature? and natural defenses. + +I find myself intrigued A tail is quite essential +by your subvocal oscillations, for your acrobatic talents, +a singular development you would not be so agile, +of cat communications, if you lacked its counterbalance, +that obviates your basic and when not being utilized, +hedonistic predilection, to aid in locomotion, +for a rhythmic stroking of your fur it often serves to illustrate, +to demonstrate affection. the state of your emotion. + + Oh Spot, the complex levels of behavior you display, + connote a fairly well developed cognitive array, + and though you are not sentient, Spot, + and do not comprehend, I nonetheless consider you + a true and valued friend. + + -- By Data +~ +There is no secret to excel in playing the shakuhachi. +Blow not intensely, but from your heart. +Although technique is secondary, it helps to express your true self. +If we are natural, we make fine sound. +If we have an open mind, our sound will be mellow. +If we have right attitudes toward life, +our music will be acceptable to everyone. +Take care of your sound as you would care for yourself. + -- Koga +~ +You can't save money by spending it. Beware claims to the contrary. + -- fred t. hamster +~ +If you correct your mind, the rest of your life will fall into place. +This is true because the mind is the governing aspect of human life. +If the river flows clearly and cleanly through the proper channel, +all will be well along its banks. + +The Integral Way depends on decreasing, not increasing: + To correct your mind, rely on not-doing. +Decrease thinking and clinging to complications; + keep your mind detached and whole. +Eliminate mental muddiness and obscurity; + keep your mind crystal clear. +Avoid daydreaming and allow your pure original insight to emerge. +Quiet your emotions and abide in serenity. +Don't go crazy with the worship of idols, images, and ideas; + this is like putting a new head on top of the head you already have. + +Remember: if you can cease all restless activity, + your integral nature will appear. + + -- Hua Hu Ching -- 45 +~ +Dualistic thinking is a sickness. +Religion is a distortion. +Materialism is cruel. +Blind spirituality is unreal. + +Chanting is no more holy than listening to the + murmur of a stream, counting prayer beads no more + sacred than simply breathing, religious robes no + more spiritual than work clothes. + +If you wish to attain oneness with the Tao, don't get + caught up in spiritual superficialities. +Instead, live a quiet and simple life, free of ideas and + concepts. +Find contentment in the practice of undiscriminating + virtue, the only true power. +Giving to others selflessly and anonymously, radiating + light throughout the world and illuminating your + own darkness, your virtue becomes a sanctuary for + yourself and all beings. + +This is what is meant by embodying the Tao. + + -- Hua Hu Ching -- 47 +~ + ________________ _______________ + / \ / / \ \ + / / \ \ \ | - - \ + | | | / - \ | + / / \ \ + | ___\ \| | / / \____________ \ \ + | / | | \ | + | | __ | | \ \ +/ | \ | | \ | +| | \ | | ==== | | +| | __ | | (o-) _ | | +| __\ (_o) | / \ | | +| | | Heh Heh Heh / ) ) | | + \ || \ / Huh Huh Huh / ) / | | + | |__ \ / \ |___ - | | + | | (*___\ / \ *' | | + | | _ | / \ |____ | | + | | //_______| ####\ | | + | / |_|_|_|___/\ ------ |_/ + \| \ - | | | + | _----_______/ \_____ | + | / \ | + |_____/ \__________| + + "Beavis and Butthead are catching up in the poles." + "Yeah, our poles are rising." +~ +The chief cause of problems is solutions. -- Eric Severeid +~ +[CND, 12/15/93] A women trafficking gang, consisting of 69 members, was +rounded up by the Inner Mongolia police, the Inner Mongolian Daily said. +The gang, operating in several nearby provinces, enticed a total of 200 +unemployed women to make the journey to Inner Mongolia with the promise +of good jobs and shelter. The women, ranging from 15 to 41, then were sold +as wives or servants to local peasants who had difficulties in finding +a spouse. + -- Daluo Jia +~ + A genuine ASCII stereogram! +Here's an ASCII single image random dot stereogram for your enjoyment. To +get the 3d effect, you need to diverge (unfocus) your eyes such that two +adjacent letters in the same row come together. To help you focus, try to +make the two capital O's at the top look like three. Once you've done that, +the rest of the image should jump out of the screen at you! + O O +n n n n n n n n n n n n n n n n n +f f f f f f f f f f f f f f +e e e e e e e e e e e e e e e e e +a a a a a a a a a a a a a a +a a a a a a a a a a a a a a a a a +r r r r r r r r r r r r r r +r r r r r r r r r r r r r r r r r +~ +Try moving your head back from the screen and moving it about a bit +once you have focused on the image to increase the stereo effect even +more. + + O O +. . . . . . . . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . . + . . . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . + . . . . . . . . . + . . . . . . . + . . . . . . . +. . . . . . . +| | | | | | | +| | | | | | | +| | | | | | | +| | | | | | | +| | | | | | | +| | | | | | | + . . . . . . . + . . . . . . . + . . . . . . . . . + . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . . . + . . . . . . . . . . . . . . . . . +. . . . . . . . . . . . . . . . . . . . . . . +~ + The following a 3-d Maze (3x4) consisting of the following objects: +circle, square, asterisk, hour-glass, triangle, and a square with two lines +in it. The rules are as follows: You can 'warp' from one part of the maze to +another by matching similar objects. Each 'warp' counts as one move. You +can also travel along the lines, if there is a line. This also counts as one +move. (I got the idea of this from a GAMES magazine I read a LONG time ago) +Try to go from "START" to "END" in the least amount of moves. I would give +out a prize to somebody, if I could think of one(that doesn't cost anything, +of course!). (Take this as a first maze...I didn't plan it out, and it's not +very difficult.) I would suggest that people that have worked with +stereograms EXTENSIVELY try this, as it is hard to move your eyes around and +still keep focused on the 3-D image. E-mail me the number of moves it took +you and the path you followed (e.g. 6 Sqauare -warp- Asterisk Circle...etc.), +I'll post the list of people that reply to it within the next week and got +the lowest number of moves. Good luck!! + X X +[qL|[NYcUCdH>,(&[qL|[NYcUCdH>,(&[qL|[NYcUCdH>,(&[qL|[NYcUCdH>,(&[qL|[NYcUCdH>,( +UflL:FL#OaEu/_QqUflL:FL#OaEu/_QqUflL:FL#OaEu/_QqUflL:FL#OaEu/_QqUflL:FL#OaEu/_Q +rwA#nbud>*Dj+X*Dj+X*Dj+X*Dj+X*Dj+X< +$*-Xj+V&%Ui\START*-Xj+V&%UiSTART;*-Xj+V&%UiSTART;*-Xj+V&%UiSTART;*-Xj+V&%Uieg%( +BDX"aZ3Y0mdH&KF%BDX"aZ3Y0mdH&KF%BDX"aZ3Y0mdH&KF%BDX"aZ3Y0mdH&KF%BDX"aZ3Y0mdH&KF +WL(-W->WTPh:UQ:oWL(-W->WT:UQ:oWL(-eBW->WT:UQ:oW-YoeBW->WT:UQ:oW-YBW|H->WT:UQ:oW +cTV:wpR6GN\ne![|cTV:wpR6GnlR,gO$66yAP&WlR,gO$66yAP&WlR,gO$66yAP&Wl,gMO$66yAP&W"\$f";MP\$4?hS<>"\$f";\$S4?hS>"!\$f";\$4?hh>"!q$f"F*;\$4?h"v!q$f"*;]j\$4?h"v +=/twTn#S9ikXS%`y=/twTn#S9iS%[`y/t2nwTn#S9i[`y't2n/Tn#\\S9i[`y2Yn/Tn#\S?I9i[`y2Y +d1|,!mJqjd8x^=!Bd1|,!mJqjd8x!Bd1=1|,!mJqjd!Bd1=1|,!mJV^qjd!Bd1|,!mJV^qIGjd!Bd1| +Sy<6*yAW|!l;tZ]nSy<6*yAW|!l;t]n'Sy<6*yAW|!l;t]nSy+<6*yAW|!l;t]nSy<6X*yAW|!l;t]n +&)v;_!*qY*L)-IQd&)v;_!*qY*L)-Qd5&)v;_!*qY*L)-Qd&)Bv;_!*qY*L)-Qd&)v;6_!*qY*L)-Qd +t?K!lfksx)kzAp2Wt?K!lfksx)kzA2W:t?K!lfksx)kzA2Wt?%K!lfksx)kzA2Wt?K!Llfksx)kzA2W +UAsuytGXa{(cnug]UAsuytGXa{(cng](UAsuytGXa{(cng]UAzsuytGXa{(cng]UAsuXytGXa{(cng] +|Is@[A-F0)KmcN>o|Is@[A-F0)Kmc>o#|Is@[A-F0)Kmc>o|Ins@[A-F0)Kmc>o|Is@>[A-F0)Kmc>o +NHnuKxzh"2zQ=hq+NHnuKxzh"2zQ=hNmhHnuKxzh"2zQ=hNHn85uKxzh"2zQNHn85uKxzh".I2zQNHn +vj5-juY;&H>b[w"mvj5-juY;&H>b[mBjX!5-juY;&H>mBjXc!-juYY:;&H>mXc!;juYJ:;&.fH>mXc! +zE"A{TfCX?9H9e;:zE"A{TfCX?9H;8:z"mA{TfCX?9H;8z="mATRwfCX?9H;="m{TRwbCX?zt9H;="m +OI5{{UQa5D.Vo/rVOI5{{UQa5D./@rOoI{D{UQa5D./@rotI{DU.ZQa5D./@tI{pU.ZNa5D85./@tI{ +VvmyUF3wGZ.Vd-0.VvmyUF3wGZd1-0.VvmUKqF3wGZd0.VvKmKqF3?kwGZd0vKmxqF3okwGU!Zd0vKm +Yo{BgD`f\`$JRtu'Yo{BgD`f\JRtu'Yo{BgDis`f\JRtu'YBg3#Dis`f\JRtYBg3#Dis`f\ygJRtYBg +80[vV7`fchW6A#'x80[vV7`fchW6A'x-80[vV7`fchW6A'x80r[vV7`fchW6A'x80r[vV7`fchW6A'x +O"As"N5\Ul_^TV+{S>As"N5\Ul^TkV+{S>As"N5\Ul^TV+;{S>As"N5\Ul^TV+;{S>As"N5\Ul^T +/U!sJ3m%/qr?4m!I/U!sJ3m%/qr?4!IB/U!sJ3m%/qr?4!I/Uw!sJ3m%/qr?4!I/Uw!sJ3m%/qr?4!I +yh6Sd(VC3g&>\H<.yh6Sd(VC3g&>\<.Vyh6Sd(VC3g&>\<.yhs6Sd(VC3g&>\<.yhs6Sd(VC3g&>\<. +;*<*[r!OIx.k(ZB^;*<*[r!OIk(ZB^;*<*[rkw!OIk(ZB^<*[i^rkw!OIk(Z<*[i^rkw!OI]Fk(Z<*[ +9xGH.E=u,tZXVx!<9xGH.E=u,tZx!<9xGH;W.E=u,tZx9x'GHW.TzE=u,tZx9xHW.TzE=.|u,tZx9xH +\Hu"\@V#8"mLukmM\Hu"\@V#8"mLuM\Hs;u"\@V#8"mM\PHs;u\@DV#8"mM\PHs;\@DECV#8"mM\PHs ++TnXOmPMQKTVwlSV+TnXOmPMQKTVwV+TJInXOmPMQKTV+@TJInOmmPMQKTV+@TJIOmmizPMQKTV+@TJ +JaXq{$*|_pP|-`[wJaXq{$*|_pP`[wJaXqS*{$*|_pP`Ja,Xq*{K3$*|_pP`Jaq*{K3$*g.|_pP`Jaq +A7_qF(|hvg5>t;!nA7_qF(|hv>t;!nA7_qF(f"|hv>t;!n_qFIf(f"|hv>t;_qFIf(f"|hvY*>t;_qF +Clzp3ggIZF7:E.cqClzp3ggIZF7:E.cqClzp3ggIZF7:E.cqClzp3ggIZF7:E.cqClzp3ggIZF7:E.c +UENDu6z[k&JUENDu6z[k&JUENDu6z[k&JUENDu6z[k&JEND>u6z[k& +h-Q!-z98>A%65jWBh-Q!-z98>A%65jWBh-Q!-z98>A%65jWBh-Q!-z98>A%65jWBh-Q!-z98>A%65jW +#7ch;MFM9k0{T2."#7ch;MFM9k0{T2."#7ch;MFM9k0{T2."#7ch;MFM9k0{T2."#7ch;MFM9k0{T2. +IMfNj5t*M,K`xKz9IMfNj5t*M,K`xKz9IMfNj5t*M,K`xKz9IMfNj5t*M,K`xKz9IMfNj5t*M,K`xKz +~ + # + # # # ### + ## # # # # ## #### # + ## # # # #### ######## # ## + ### # # # ## # ## # + ### ## # ## # ### ### ## ## + # #### ### # # # ## # # # # ### + ## ############# # # # ## ### + # # # # ## ### ## ### + # # ## # ## ### # ### + ## # ## # ## # + # #### # ## #### ### + ##### # # ## # # # ##### # + # # # # # # ## # ##### # + ## # # # # ## ### # + ### # # # ## # + ## # ## ## ##### ## + # ## # # ## ##### ## + ###### # # # # ### ### + ## # # # # ###### ## + ## # ##### # ##### ### ## ## + # ## ## ## # ## ## # ### #### + ### #### # # #### # # ######## + ##### # ## ### # ### # + #### ## # ### #### ## + ## ### ### ## ## # + ## #### # ## ## + ## # # ## + # # # ## # + # # # ################## + ### # ## # # + ### # # ## #### ## ## + ### #### # #### + ## # ## ##### # + ## # ## # ## ## ##### + # ### ## # ## #### ## # ### + ## ##### # ######### # #### + # # # # # ## ## + ## # # # # ## + # # # # ## ## + # # # # # + ## # # ### + # # ##### + # ## ##### + ## # #### + # ## ##### + ## ### # + ######## # + ##### # + ### # + # ## + # # + # ##### + # ###### + # ####### + # ####### + +~ +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"" ""$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" " " o "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ " o "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o o "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" o " o"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" " $$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" " o " " "$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$$$$$$$$" o " "o o $$$$$$$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$$$$$$$ o" " o o $$$$$$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$$$$$$ "$$ " o " " $$ "$$$$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$$$$$ $$ o o " o " $$ $$$$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$$$" o$$ o o o $$$ "$$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$$" o $$$ " o o " " $$$ " "$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$" o$$$$ " o $$$$ "$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$ " $$$$ " oo$$$$$$$oooo $$$$" " $$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$" " $$$$$o """ o o"""o$$$$" " ""$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$ " ooooo$$$$$$$$oo o o oo$$$$$$$ooooo o o $$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$"$$$$$$$$$$$$$$$$$$$o $$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$ o$$$"" " $ ""$$$$$$$$" ""$$$$$$$$"" " """"$$o" "$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$oo$" " $o $"$$$$oo " o$$$$" o$o " ""$oo"$$$$$$$$$$$$ +$$$$$$$$$$$$$ o" " o " $$" o "" o o " o o$o " o "" "$$$$$$$$$$$ +$$$$$$$$$$$$" $ " " $$ "$$$$$$$$ $$ o o o $ ""$$$$$$$$$$ +$$$$$$$$$$$" o o o o o$$ "$$$$$$ o $$ o o o $$$$$$$$$ +$$$$$$$$$$ o $ " $$oo "$$$$$$" oo$$ $ $$$$$$$$ +$$$$$$$$$ " o " "$ $$$$$$ "$" " " " o $$$$$$$ +$$$$$$$$ " " " " o "o$$$$$$o" o " o $$$$$$ +$$$$$$$" " o " o $$$$$$$$$o " o o " " o $$$$$ +$$$$$"o " o o o " "$$$$" ""$$$oo o " " $$$$ +$$$$"o o " $ o " oo$$$$"o o"$$$$oo o " o "$$ +$$$$ " $ o oo$o$$"$" o o """$$$oooo "" o " o $$ +$$$$$ " " o " o o o o o o o " " " o$$$ +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +~ + ___ ______ + /__/\ ___/_____/\ + \ \ \ / /\\ + \ \ \____ / \ + ___\ \ \ /\___/___ \ + / / \__\/ / \ /\ \ + ____/ /_______/ \ / _\/_____ + / / \ \ / / / /\ + __/ / \ \ / / / / _\__ + / / / \_______\/ / / / / /\ +/_/___/___________________/ /_______/ /___/ \ +\ \ \ ___________ \ \ \ \ \ / + \_\ \ / /\ \ \ \ \___\/ + \ \/ / \ \ \ \ / + \__/ / \ \ \_______\/ + /__________/ \ \ / + \ _____ \ /_____\/ + \ / /\ \ / \ \ \ + /____/ \ \ / \ \ \ + \ \ /___\/ \ \ \ + \____\/ \__\/ + +~ + m + $m mm m + "$mmmmm m$" mmmmmmm$" + """$m m$ m$"""""" + mmmmmmm$$$$$$$$$"mmmm + mmm$$$$$$$$$$$$$$$$$$ m$$$$m " m " +$$$$$$$$$$$$$$$$$$$$$$ $$$$$$"$$$ + mmmmmmmmmmmmmmmmmmmmm $$$$$$$$$$ + $$$$$$$$$$$$$$$$$$$$$ $$$$$$$""" m + "$$$$$$$$$$$$$$$$$$$$$ $$$$$$ " " + """""""$$$$$$$$$$$m """" + mmmmmmmm" m$ "$mmmmm + $$"""""" "$ """"""$$ + m$" "m " + " + +~ + o + _---| _ _ _ _ _ + o ---| o ]-I-I-I-[ + _ _ _ _ _ _ _---| | _---| \ ` ' / + ]-I-I-I-I-[ ---| | ---| |. | + \ ` '_/ | / \ | | /^\| + [*] __| ^ / ^ \ ^ | |*|| + |__ ,| / \ / `\ / \ | ===| + ___| ___ ,|__ / /=_=_=_=\ \ |, _| + I_I__I_I__I_I (====(_________)___|_|____|____ + \-\--|-|--/-/ | I [ ]__I I_I__|____I_I_| + |[] '| | [] |`__ . [ \-\--|-|--/-/ + |. | |' |___|_____I___|___I___|---------| + / \| [] .|_|-|_|-|-|_|-|_|-|_|-| [] [] | + <===> | .|-=-=-=-=-=-=-=-=-=-=-| | / \ + ] []|` [] ||.|.|.|.|.|.|.|.|.|.||- <===> + ] []| ` | |/////////\\\\\\\\\\.||__. | |[] [ + <===> ' ||||| | | | ||||.|| [] <===> + \T/ | |-- ||||| | O | O | ||||.|| . |' \T/ + | . _||||| | | | ||||.|| | | | +../|' v . | .|||||/____|____\|||| /|. . | . ./ +.|//\............/...........\........../../\\\ +~ + ___--___ + ___---___--___- + ___---___--- - + ___---___--- + ___---___--- + ___---___--- +___---___---_________________________________ +============================================= + |||| + |------------------------------------------- + |-___-----___-----___-----___-----___-----__ + / _ \===/ _ \ / _ \===/ _ \ / _ \===/ _ + ( (.\ oOo /.) ) ( (.\ oOo /.) ) ( (.\ oOo /. + \__/=====\__/ \__/=====\__/ \__/=====\_ + ||||||| ||||||| ||||||| + ||||||| ||||||| ||||||| + ||||||| ||||||| ||||||| + ||||||| ||||||| ||||||| + ||||||| ||||||| ||||||| + ||||||| ||||||| ||||||| + ||||||| ||||||| ||||||| + ||||||| ||||||| ||||||| + ||||||| ||||||| ||||||| + (oOoOo) (oOoOo) (oOoOo) + J%%%%%L J%%%%%L J%%%%%L + ZZZZZZZZZ ZZZZZZZZZ ZZZZZZZZZ + ========================================== + __|_________________________________________ + +~ + ____----------- _____ +\~~~~~~~~~~/~_--~~~------~~~~~(__) \ + `---`\ _-~ | ( oo \ + _-~ <_ | /\_| \[] + / ___ ~~--[""] | __/__|__-------'_ +> /~` \ |-. `\~~.~~~~~ _ ~ - _ + ~| ||\% | | ~ ._ ~ _ ~ ._ + `_//|_% \ | ~ . ~-_ /\ + `--__ | _-____ /\ ~-_ \/. + ~--_ / ,/ -~-_ \ \/ _______---~/ + ~~-/._< \ \`~~~~~~~~~~~~~ ##--~/ + \ ) |`------##---~~~~-~ ) ) + ~-_/_/ ~~ ~~ +~ + .; + .`;' + . ` ;;' + . ` ` ` ;;;' + ` @ ;;; + ` ;;; + ` ;;;; + ` ;;;;;. + ` .;;;;;;**. + ` ;`* .;;; `**. + . ` ;;`****. '*. + '** ` ;;;;'****. . + '****` ;;;;;`***. + '*****` ;;;;;; `**. + ` **` ;;;;;;; .* + `*` ;;;;;;;; + ` ;;;;;;;; + ` ;;;;;;;; + ` ;;;;;;; + ` ;;;;;; . * + ` ;;;; *** + . ;; *** + * * . ;; ** +*** * ` .;; * +** .****. * * +* . * ^^ *'. * **** + * * * **** +*** * * *** +*** ** + * *** + *** + ** +~ + oooo$$$$$$$$$$$$oooo + oo$$$$$$$$$$$$$$$$$$$$$$$$o + oo$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o o$ $$ o$ + o $ oo o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$o $$ $$ $$o$ +oo $ $ "$ o$$$$$$$$$ $$$$$$$$$$$$$ $$$$$$$$$o $$$o$$o$ +"$$$$$$o$ o$$$$$$$$$ $$$$$$$$$$$ $$$$$$$$$$o $$$$$$$$ + $$$$$$$ $$$$$$$$$$$ $$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$ + $$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$ $$$$$$$$$$$$$$ """$$$ + "$$$""""$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ "$$$ + $$$ o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ "$$$o + o$$" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$o + $$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" "$$$$$$ooooo$$$$o + o$$$oooo$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o$$$$$$$$$$$$$$$$$ + $$$$$$$$"$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$"""""""" + """" $$$$ "$$$$$$$$$$$$$$$$$$$$$$$$$$$$" o$$$ + "$$$o """$$$$$$$$$$$$$$$$$$"$$" $$$ + $$$o "$$""$$$$$$"""" o$$$ + $$$$o oo o$$$" + "$$$$o o$$$$$$o"$$$$o o$$$$ + "$$$$$oo ""$$$$o$$$$$o o$$$$"" + ""$$$$$oooo "$$$o$$$$$$$$$""" + ""$$$$$$$oo $$$$$$$$$$ + """"$$$$$$$$$$$ + $$$$$$$$$$$$ + $$$$$$$$$$" + "$$$"""" + +~ + ___ _,.--.,_ + .-~ ~--"~-. ._ "-. + / ./_ Y "-. \ + Y :~ ! Y + lq p | / .| + _ \. .-, l / |j +()\___) |/ \_/"; ! + \._____.-~\ . ~\. ./ + Y_ Y_. "vr"~ T + ( ( |L j -Row + [nn[nn..][nn..] + ~~~~~~~~~~~~~~~~~~~~~~~ + Bob The Elephant +~ + ,,ggddY"""Ybbgg,, + ,agd888b,_ "Y8, ___`""Ybga, + ,gdP""88888888baa,.""8b "888g, + ,dP" ]888888888P' "Y `888Yb, + ,dP" ,88888888P" db, "8P""Yb, + ,8" ,888888888b, d8888a "8, + ,8' d88888888888,88P"' a, `8, + ,8' 88888888888888PP" "" `8, + d' I88888888888P" `b + 8 `8"88P""Y8P' 8 + 8 Y 8[ _ " 8 + 8 "Y8d8b "Y a 8 + 8 `""8d, __ 8 + Y, `"8bd888b, ,P + `8, ,d8888888baaa ,8' + `8, 888888888888' ,8' + `8a "8888888888I a8' + `Yba `Y8888888P' adP' + "Yba `888888P' adY" + `"Yba, d8888P" ,adP"' Normand Veilleux + `"Y8baa, ,d888P,ad8P"' from + ``""YYba8888P""'' Spaceship Earth + +~ +========================================================================= + ________________ _______________ _______________ + /_______________/\ /_______________\ /\______________\ + \\\\\\\\\\\\\\\\\ \ ||||||||||||||||| / //////////////// + \\\\\\\\\\\\\\\\\/ ||||||||||||||||| / //////////////// + \\\\\\_______/\ ||||||_______\ / //////_____\ + \\\\\\\\\\\\\ \ |||||||||||||| / ///////////// + \\\\\\\\\\\\\/____ |||||||||||||| / ///////////// + \\\\\___________/\ ||||| / //// + \\\\\\\\\\\\\\\\ \ ||||| / //// + \\\\\\\\\\\\\\\\/ ||||| \//// + +========================================================================= +~ + 88 d8'd8' 88 88 ad88888ba ,adba, ,d8 + 88 d8'd8' 88 88 d8" 8 8 "8b 8I I8 ,d8" + 88 "" "" aa88aaa88aa Y8, 8 8 "fbdP' ,d8" + 88 ""88"""88"" `Y8a8a8a, ,d8" + 88 aa88aaa88aa `"8"8"8b, ,d8" + "" ""88"""88"" 8 8 `8b ,d8" ,adba, + aa 88 88 Y8a 8 8 a8P ,d8" 8I I8 + 88 88 88 "Y88888P" 8" "fbdP' + 8 8 +~ +\ / / + \\\' , / // + \\\//, _/ //, + \_-//' / //<, + \ /// > \\\`__/_ + /,)-^>> _\` \\\ + (/ \\ //\\ + // _//\\\\ + ((` (( +~ + * + * * ** * + * ** ** ** + ** * *** ** + *** * ** *** + *** * **** *** + **** **** + ** * ********* * *** + ***** ********************** + ******** *************** **** + ****** ***************** ** ** + ** ******************* *** ******** + ******************** *** ******* + ********************* *** ** **** + ********* * *********************** *** **** + ********** ************************ **** ** *** + **** *********************** * **** + ************** ****** * *** * ** + *********** ** **** * *** * ** ********** * + ************* ***** ***** ** * ** ****** * + * ************* **** ******* *** ** +************** **************** *********** ** + ****** ******************************* ** + ********************************* ** + ******************************** ** ** + ******************************* ** ********* + **** *************************** *** ***** + ***** * *** ******************** ** + ***** * ***************** ** ****** + **** ********************* * ***** + ***** ****************** ** + **** **************** * ***** + * ***************** ** ****** + ** ************** ** ** + ** ************* * + *** ************* ** + * *** ********************* + ****** ******* * *** *** + ******* ** *** + ************ ******** + ********************** * ********** + ************************* *********** + ************************** *********** + ************************* *********** + ************************ ********** + ********************* ********* + **************** *** + * ****** +~ + . ,. + T."-._..---.._,-"/| + l|"-. _.v._ (" | + [l /.'_ \; _~"-.`-t + Y " _(o} _{o)._ ^.| + j T ,--. T ] + \ l ( /-^-\ ) ! ! + \. \. "~" ./ /c-..,__ + ^r- .._ .- .-" `- . ~"--. + > \. \ + ] ^. \ + 3 . "> . Y -Row + ,.__.--._ _j \ ~ . ; | +( ~"-._~"^._\ ^. ^._ I . l + "-._ ___ ~"-,_7 .Z-._ 7" Y ; \ _ + /" "~-(r r _/_--._~-/ / /,.--^-._ / Y + "-._ '"~~~>-._~]>--^---./____,.^~ ^.^ ! + ~--._ ' Y---. \./ + ~~--._ l_ ) \ + ~-._~~~---._,____..--- \ + ~----"~ \ + \ +~ + |\_ \|\|| + -' | `. -- ||||/ + /7 `-._ /7 |||||/ +/ `-.____/ |||||||/`-.____________ +\-'_ \-' ||||||||| `-._ + -- `-. -/||||||||\ `` -`. + |\ /||||||\ \_ | `\\ + | \ \_______...-//|||\|________...---'\ \ \\ + | \ \ || | \ ``-.__--. | \ | ``-.__--. + | |\ \ / | |\ \ ``---'/ / | | ``---' + _/ / _| ) __/_/ / _| ) __/ / _| | + /,__/ /,__/ /,_/,__/_/,__/ /,__/ /,__/ tbk + +~ + + ooooooo + o o o o o o o o "o "$" " + o " o "o + o o " "" o + " " " " " $$ooo o" + o"o"o o " o " + " " $o $"o"$ " " + oo$$$oo " " $ $o $ " o + o $$"$$$$$o oo$$$$$$$$oo o"oo"oo o + o$$"$$o$$$$$$"$o$o$o$o$o$$o "o$ "o $ "o + $$"$o$$o"$"$$$o$$o$o$o$o$$$o$$$o $ $ $" o + $$"$o$o$"$"$"$o$o$o$o$o$o$$$o$o$"o "o"oo" + " "$o$$o$o"$"$"$$"$o$o$o$o$o"$"$o"$"$o "o o" + $o$o$o$"$"$$o$"$$$$o$o$o$"$$o$"$"$oo $ + "o$o"$"$$o$o$"$"$"$o$o"$$o$o$"$"$"$ + $"$$o$o$o$"$"$"$$$$$o$o$o"$"$"$o$ + o "$o$o$o$o$"$"$o$o"$o$$o$"$"$"$o$ + $o "o$o$o"$"$"$o$"$$o$o$"$"$"$$o" + $$ $$$$$$$$$"$o$$o$o$o$$"$"$o"$$ + $" $$$$$$$o$"$$o$o$o$o$"$"$"$$o + o o$$$$$$$o"$o$o$"$$o"$"$"$"o$" + oo$$$$$$$o$"$o$o$"$o$"$"$"$"$ + $$$$$$$$$$o$"$o$o$"$o$"$"$$"" + $$o$$$$$o$o"$$o$o$"$o$"$$" + o $$o$$$"o$o$"$o$o$"$o$"$o$ + $$$"$o"$$o$o$$"$$o$$o" + " """" """"$"$o$$"$ + o "$$"$$"$$o o$$o + o o o $"$$$"$$"$oo"$o$o$$$o + o o $$$o$$"$$"$$$$$o$$o"$o$ + " o$$o$"$"$$$$o$"o$o$"$"$$ + " " $$""o$$$$"$o$"$$$$$$$$$$"" " + o ooo$$"o$$$"$$"$$"$$$$$$$$"" + o o$o$"$$"" $" $o$o$o$ + $ " o$"$"$ $o$o$ + "o "o" " " $ "$$$$"$$ o o$$$$"o o "o "o "o +" " o " "o" """o""$$$$$$o o $$o$ " " "o "o o + " " " " " " $$" $" " " o + o""""$""""" oo$$ $$ o " " + o "o oo " " $oo $$ + "o " o oo"o$o $" o + o """""""" "o "o o"""" " " + o $ o" $" + " "$ooooooooooooooo o " " + " o + " " " " + +~ +Aw mom, you act like I'm not even wearing a bungie cord! -- Calvin +~ + PROBLEM SOLVING + P R O C E S S + + + YES ============================= NO + +-----------|| Does the Darn Thing work? ||-----------+ + | ============================= | + V V ++----------+ +---------+ +---------+ +| Don't | NO | Does | +-------+ YES | Did you | +| mess | +---| anyone |<------| YOU |<---------| mess | +| with it! | | | know? | | MORON | | with it | ++----------+ | +---------+ +-------+ +---------+ + | V | YES | NO + | +------+ +-----------+ | + | | HIDE | V V + | | IT | +--------+ +-----------+ + | +------+ | YOU | YES | WILL THEY | + | | +------->| POOR |<------------| CATCH YOU?| + | | | |BASTARD!| +-----------+ + | | | |________| | NO + | | | | | + | | | V V + | | | +---------------+ +-----------+ + | | | NO | CAN YOU BLAME | |DESTROY THE| + | | +------| SOMEONE ELSE? | | EVIDENCE | + | | +---------------+ +-----------+ + | | | YES | + | | v | + | | ============================ | + | +---->|| N O ||<---------+ + +------------>|| P R O B L E M || + ============================ +~ + ,a, + ,ad8""Y8a, + ,ad8"" "Y8a, + ,ad8"" "Y8a, + ,ad8"" "Y8a, + ,ad8"" ,ad8a, "Y8a, + ,ad8"" ,ad8"" 8"Y8a, "Y8a, + ,ad8"" ,ad8"" 8 "Y8a, "Y8a, + ,ad8"" ,ad8"" 8 "Y8a, "Y8a, + ,gPPR8, ,ad8"" ,ad88a, "Y8a, "Y8a, +dP' `Yb ,ad8"" ,ad8"" "Y8a, ,ad8"" ,ad8"8 +8) (8,ad8"" ,ad8"" Y888"" ,ad8"" 8 +Yb d8P" ,ad8"" ,ad8"" ,ad8"" 8 + "8ggg8" ,ad8"" ,ad8"" ,ad8"" ,8 + ,gPPR8, ,ad8"" ,ad8"" ,ad8"" + dP' `Yb ,ad8"" ,ad8"" ,ad8"" + 8) (8,ad8"" ,ad8"" ,ad8"" + Yb d8P" ,ad8"" ,ad8"" + "8ggg8" ,ad8"" ,ad8"" + ,gPPR8, ,ad8"" + dP' `Yb ,ad8"" + 8) (8,ad8"" + Yb d8P" + "8ggg8" +~ + .... NO! ... ... MNO! ... + ..... MNO!! ...................... MNNOO! ... + ..... MMNO! ......................... MNNOO!! . +.... MNOONNOO! MMMMMMMMMMPPPOII! MNNO!!!! . + ... !O! NNO! MMMMMMMMMMMMMPPPOOOII!! NO! .... + ...... ! MMMMMMMMMMMMMPPPPOOOOIII! ! ... + ........ MMMMMMMMMMMMPPPPPOOOOOOII!! ..... + ........ MMMMMOOOOOOPPPPPPPPOOOOMII! ... + ....... MMMMM.. OPPMMP .,OMI! .... + ...... MMMM:: o.,OPMP,.o ::I!! ... + .... NNM:::.,,OOPM!P,.::::!! .... + .. MMNNNNNOOOOPMO!!IIPPO!!O! ..... + ... MMMMMNNNNOO:!!:!!IPPPPOO! .... + .. MMMMMNNOOMMNNIIIPPPOO!! ...... + ...... MMMONNMMNNNIIIOO!.......... + ....... MN MOMMMNNNIIIIIO! OO .......... + ......... MNO! IiiiiiiiiiiiI OOOO ........... + ...... NNN.MNO! . O!!!!!!!!!O . OONO NO! ........ + .... MNNNNNO! ...OOOOOOOOOOO . MMNNON!........ + ...... MNNNNO! .. PPPPPPPPP .. MMNON!........ + ...... OO! ................. ON! ....... + ................................ +~ + ------ _____ + / \ ___\ ___/ ___ + --/- ___ / \/ / / / \ + / / \__ //_ \ + / \ / ___ | + | ___ \/+--/ / + \__ \ \ / + \__ | / + \ /____ / / | / + _____/ ___ \/ /\ + \__ / / | | + / \____/ \ / // + // / / // / /\ /-_-/\//-__- + / / // / \__// / / / // + // / / // / // / // / + /// // / / / // / // + // // // / // / / + / / / / / / / / + /// / / / // // / // // + /// / / / / / / +/// / // / / // / / / / + // /// / /// / / + / / // /// / +~ + _________ + /|\ /|\ + / | \ / | \ + /__|__/ \__|__\ +|\ | | | | /| +| \ |----|--| / | +| /\-\---|---/\ | +|/_____|\|_____\| + \ | \ / | / + \ | / \ | / + \|/ \|/ + -------- + +~ + ____ + /.../\ + /.../##\ + /.../####\ + /.../######\ + /.../###/\###\ + /.../###/ \###\ + /.../###/\ \###\ + /.../###/ \ \###\ + /.../###/ \ \###\ + /.../###/ \ \###\ + /.../###/ \ \###\ + /.../###/ \ \###\ + /.../###/ \ \###\ + /.../###/______________\ \###\ + /........................\ \###\ + /..........................\ \###\ +----------------------------- \###/ +\ \#/ + \_________________________________/ + +~ +___. .. . _ .. . ___. .__ . . _ . . __ __ _ __ _ . .___ + | | ||\||- ||\| | | ||_)|\| | ||\| | ||_)| ||_) | || | | + ` `-`` ``- `` ` ``` ` `-`` \` ` `-`` ` ``` `-`` \`-`| `-``-` ` +~ + . . + ... :``..': + : ````.' :''::' + ..:.. : .'' : +``. `: .' : + : : : : + : : : : + : : : : + : : :..''''``::. + : ...:..' .'' + .' .' .::::' + :..'''``::::::: + ' `:::: + `::. + `:: + :::. + ..:```.:'`. ::'`. + ..' `:.: :: + .: .:``::: + .: ..'' ::: + : .'' :: + : :: + : + : + . +~ + ;;;;: ==;; :==== :+++=;; ::;=i===+: :=;;=+: ,,,,:==+; + =tt;;: i+; ++++ :ii=: :::i++= ==;;+: :::=;+; , +:; iIt=: ;t+; ii=;::;+;=+; +;=; ;i+i, ==;=+; :,;;;i : , +:: :tIi: +i+ =+::=;;,;;;+; +;=; :=i==+;,:,;=;;+= ::;=;++ : ; + ,i; iIt: t+= :+=,,,,::: ;,+;=;;: ;:,,;==;+=;+= ::;;=i; : ;ii + RYi +Yt: ++i :i;,::==;; ;:i;=;;: ======+=i=++ :;=tYY: : :tt + VXY= =Yi; :t=+ it==+It;; ;:+:=;:: :,;;=:=+==+ :;tYVY :: =Iii + tVYt ;Yt, i+= +t+;=iI,; ;:;:;;:= :i :=;+ :;i+tt : :Iti + iYY: :Yi, :i+: :t+;++i:; ;,;,;;;= :t +=+ ;i+ii :: =Y+ + =YY= ;Vi I++ :++=;==;;; +,=,,,,+ ;i;::;it, :i+t=;;: +I+ + IX+ =V; +i+=;;==;::,:::,,t,+,,,,;;iiii+iIiX=:=+ii;:;: iI+ + iXi =VItYIi=+======;;::;;ii;:,,,,,;iittiYV+XXttiit+, Yt + IVYXYYIYi+;;;;;;;;;;;;;+it+:::::::=====+IttIItiitMBttti + +tVVVttti+;::;;======+YBBVVVVXRVti+++==+IXIiYi+iRWWi+i= + +=ttYVIi= ....,,,,+iIRWWWWWWWMBBRYI+....... +YMBYX==++ + ==+t: .;;===::,,:,,;=iIIItIIt=:,,:::===iii=;.,iXY+i;i + t;+.IIY+++iii++;===:==++++I,:;;:,:=iiVXIBVRt+;,.IXY;t + ii+, =itIItY==+tt+=+++;=ti;=;+M+i=;;=;=i+=it+tVti+==;.itI= +=ii + i:===iIttIii+,:,, ,,:+++;t=t;V++;=;=;:,, ,,:;+IYii+==;.+Y+Ii===:i + ti iii=i;;,, ;i: =i ,;.i=t=I=;Ri=, ,; ,it: ,:.,==;;;i+=YYttt, + iit :Y++IIIIi+;;,,,,,,:;:i+;I=i;=II=;:;;;;;;;;;+++tIt+ii;iII; iYi= + t= iV+tYYYVYVt===;;;===ii+=ti+=+=iii+==+;=+=IYVXRitYYItYXtVt= :+= + Yi XX+iYYYVYR+i+++===;tIti=i+ttit;;=+====+;=IRVRRXtIIYM+YYI+ ++=I + tt==Y+iYYYVYRI+++++=+IIti+++tt+=iYI+;;==+=+ttMMMRBXiiYi=+YYIt=;; + t+iY:;iYYYYXRYi+++++Yt+iIt=+iItVXYYtIXBXYYVRWWWBXIi++=+=X+ ;It + ti;I iYYYIYYVVRI++++i+ti++=:,;;++IX+YWWWMBWWWXYYi+++=;t:;IItY + tt:,+;YYYYYYYYX+++====:::;=:;;;::++iYRWWBYt+tt+==++==:=;:=+ + tYYYYIYYt++++=;=YYI+;,,BYBBYIi++iii+=+I+++++;=I + +YYYYYYt+++==iYYII;ittitVBBXBI+++==+i++=+=;=:i + +YYYIYt+++iBt=:,...:;;=++itXXI++==i++==;;ii: + IYYYI=;+=iitI;,;.,=iitIiiI;++=+++===;=:I; + YYYt==tittIi;+:::=+tItIBIt+=+i++==;+=;+ + iYYIIIttt+=;=::::::=++ti+++=i==;+i=:= + tIYYIIIVi++;;:::=+;+IYt+=;;IVY+:,; + ;,ttIYIY++ii;=;==Y+iVYt===YIi;;Ii + ;=:,:=iI====;;;;:=;;+ii=+i+iIttIi + :==+++,;=;::::::::=;=+t++==it+iti + + Tutankhamen +~ + __ + /\ \ + / \ \ + / /\ \ \ + / / /\ \ \ + / /_/ \ \ \ + / \ \ / \ \ + / /\ \ \/ /\ \ \ + / / /\ \/ / /\ \ \ + / / / \ / / \ \ \ + / / / __ \/_/ \ \ \ + / / / /\ \ Pat \ \ \ + / / / / \ \ Taylor / \ \ + / / / / /\ \ \ / /\ \ \ + / / / / / /\ \ \ / / /\ \_\ + / / / / /_/ \ \_\ / / / \/_/ + / / / / \ \ / / / / / / __ __ + / / / / /\ \ \/ / / / / / /\_\ /\ \ + / / / / / /\ \/ / / / / / / / / \ \ \ + / / /_/ / / \ / / / / / / / /____\_\ \ +/ / /__\/ / / / / / / / / / /__________\ +\/_______/ / / / / / / \/_____________/ + / / / / / / + / / /_/ / / + / / /__\/ / + \/_______/ +~ + _ ,d8b_ + ,aP88b, P" "8. + ,8" "bb88db, lb + dl " "b, .B' + "b. "b ,9' + "b "b + .J) "b. + .d "8a. + 8 ,d' "9 + d8 " "b + 88 d B + 88 MP P + 88 'ba+ + "8 YP + "8 d P + ba_b, "b."b, .d' + L;;PMMMbaa_ "d88ad8' + L.n;;;;;"YMba.""8. + ,V;;;;;;;;;;"Man,8b. + gd;;;;;;;;;;;;;"Mb;Mb. + gd;;;;;;;;;;;;;;;;;;"Mb, + d8b;;;;;;;;b;;;;;;;;;;;"b, + 7P"8ba;;;;;B;;;;;;;;;;;;"Na + 8" "8b;;a";;;;;;;;;;;;;lB. + B "8b,d;;paaaaaaqaL;;;"B +,B P" """' ""Y8bmdMb +dP dP "ba, +8 B' "ba +8, B "ba_ +"b B b8_ + B 78 8^"8, + P d" "8 "8, + 6. aP B, 6J +(8 .d, , , Bg. B' + "8bP"8.P "ba. .P 8P + 'W8 "8ba. B" + 8. l8 .8" + "b. 8a B' + d8'b. B. XP' + d" "ba. P XP + .8 "ba_ ad8ba + l8 'Bb, aP"_ "8. + '8 .adP""9a .d" d lb + l .ad" "ad" f" lP + 8 ' "b 88 l' ;l" + 8a 'l ll l lB + 8b lb "8.'l. "P + '9 P' lb.' 8" + '98P 8ba _aP" + '""' +~ + ,oood8888888888booo, + ,oo8" "8bo, + ,o8" `8b, + ,od8' `8b, + ,od8 `8ba + od8' `8b + ,o8 `8b + ,o8 ,ad'8b 8b + d8 ,dP' 8 8b + ,8 ,od88 8' 8, + 8' d8' 8baa `8 + :8 ,P `8P 8: + 8 ood8'ba aP' 8 + Y, o8' aP' dP + `Y, o' ,aa 8 ,8' + Y8, d8aaaaaP' ba 8Y + Y8o Y8' 888 dY + `Y8 ,8 `P dY' + Y8o ,odP' ,8ba dY' + `Yb d8P' a88P ,dP + boood8P' 8P' oP' + oodP""""' a8P,d8' + ,odP""' a8 + ,dP' o' + dP d8 + d8' 8 + od' o d8 + ,d8' 8 o ,8 + &8 8a 8 8 ,8 + $' 8 d8 8' 8' + $ 8 o 8' 8' 8 + $ oo 8' `""booooooob 8' ooo 8 + $ o$"$o ,P o 8 8 d8"""" 8 `8 + $' d$' d8 od do d8 d8 8 `8 8 + $' d$' dP 8' d8 d8' 8 8 `8 `b + $' od$ dP ,8' d8' d8 8b 8 `"b 8b +$' od' d"' od od 88 `8 "8 `8 `8, +$ od' 8 8' `8 8b 8 8 "o `8, +$oo"" 8b 8 `8o 8bo "8 8b 8b 8b + " 8b 8' 8o $$$ "8 8 `b `"oo + Yb `b `8"$$$" 8b 8b 8b $$o + `8 `b 8o "b `$o$$$$" + 8 `$$ `o o$$ + `$o$$$P" `"$"""" +~ +happiness is a state of mind +more than anything else, +but so is everything else. + -- fred t. hamster +~ +Horse sense is the thing a horse has which keeps it from betting on people. + -- W. C. Fields +~ +Famous last words: + 1) "Don't worry, I can handle it." + 2) "You and what army?" + 3) "If you were as smart as you think you are, you wouldn't be a cop." +~ +For every complex problem, there is a solution that is simple, neat, and wrong. + -- H. L. Mencken +~ +I may not be totally perfect, but parts of me are excellent. + -- Ashleigh Brilliant +~ +Pittsburgh Driver's Test +No. 7: + The car directly in front of you has a flashing right tail light but + a steady left tail light. This means: + +(a) one of the tail lights is broken; you should blow your horn to call + the problem to the driver's attention. +(b) the driver is signaling a right turn. +(c) the driver is signaling a left turn. +(d) the driver is from out of town. + + +(The correct answer is (d). Tail lights are used in some foreign + countries to signal turns.) +~ +Famous last words: + + 1. Don't unplug it, it will just take a moment to fix. + 2. Let's take the shortcut, he can't see us from there. + 3. What happens if you touch these two wires tog-- + 4. We won't need reservations. + 5. It's always sunny there this time of the year. + 6. Don't worry, it's not loaded. + 7. They'd never (be stupid enough to) make him a manager. +~ +Certainly there are things in life that money can't buy, +but it's very funny--Did you ever try buying them without money? + -- Ogden Nash +~ +Lactomangulation, n.: + Manhandling the "open here" spout on a milk carton so badly + that one has to resort to using the "illegal" side. + -- Rich Hall, "Sniglets" +~ +If the code and the comments disagree, then both are probably wrong. + -- Norm Schryer +~ +Experience is what causes a person to make new mistakes instead of old ones. +~ +Every creature has within him the wild, uncontrollable urge to punt. +~ +The most merciful thing in the world, I think, is the inability of the +human mind to correlate all its contents. We live on a placid island of +ignorance in the midst of black seas of infinity, and it was not meant that +we should voyage far. The sciences, each straining in its own direction, +have hitherto harmed us little; but some day the piecing together of +dissociated knowledge will open up such terrifying vistas of reality, and +of our frightful position therein, that we shall either go mad from the +revelation or flee from the deadly light into the peace and safety of a +new dark age. + -- H. P. Lovecraft +~ + "Krusty non-toxic Cologne + 'The smell of the big top' + Warning: Use in a well ventilated area. + May stain furniture. + Prolonged use may cause chemical burns." + -- The Simpsons +~ +a girlfriend is a bottle of wine, +a wife is a wine bottle. +~ +The very powerful and the very stupid have one thing in common. +Instead of altering their views to fit the facts, they alter the facts +to fit their views... which can be very uncomfortable if you happen to +be one of the facts that needs altering. + -- Doctor Who, "Face of Evil" +~ +In the beginning was The Plan +And then came The Assumptions +And the Assumptions were without form +And The Plan was completely without substance +And the darkness was upon the face of the workers. +And they spoke amongst themselves, saying + "It is a crock of sh*t, and it stinketh." +And the workers went unto their Supervisors and sayeth, + "It is a pail of dung and none may abide the odor thereof." +And the Supervisors went unto their Managers and sayeth unto them, + "It is a container of excrement and it is very stong +Such that none may abide by it." +And the Managers went unto the Directors and sayeth, + "It is a vessel of fertilizer, and none may abide its strength." +And the Directors spoke amongst themselves, saying one to another, + "It contains that which aids plant growth, and it is very strong." +And the Directors went unto the Vice Presidents and sayeth unto them, + "It promotes growth and is very powerful." +And the Vice Presidents went unto the President and sayeth unto Him, + "This new Plan will actively promote the growth and efficiency + Of this Company, and these Areas in particular." +And the President looked upon The Plan, +And saw that it was good, and The Plan became Policy. +This is how sh*t happens. +~ +be unafraid to think fully on a small matter. -- fred t. hamster +~ +The late Dudjom Rinpoche, head of the Nyingma sect of the Tibetan Buddhist +tradition, wrote one book on the evils of tobacco. He detailed a number of +problems, both physical and spiritual, which would derive from the use of +tobacco. Interestingly, he claims the origin of the plant to be a demoness +who vowed to take rebirth as a plant to afflict humankind. + -- Neal J. King +~ +be unafraid to walk across the water. -- fred t. hamster +~ + _ + _ / \ o + / \ | | o o o + | | | | _ o o o o + | \_| | / \ o o o + \__ | | | o o + | | | | ______ ~~~~ _____ + | |__/ | / ___--\\ ~~~ __/_____\__ + | ___/ / \--\\ \\ \ ___ <__ x x __\ + | | / /\\ \\ )) \ ( " ) + | | -------(---->>(@)--(@)-------\----------< >----------- + | | // | | //__________ / \ ____) (___ \\ + | | // __|_| ( --------- ) //// ______ /////\ \\ + // | ( \ ______ / <<<< <>-----<<<<< / \\ + // ( ) / / \` \__ \\ + //-------------------------------------------------------------\\ + +Every now and then when your life gets complicated and the weasels +start closing in, the only cure is to load up on heinous chemicals and +then drive like a bastard from Hollywood to Las Vegas... with the +music at top volume and at least a pint of ether. + -- H. S. Thompson, "Fear and Loathing in Las Vegas" +~ +dude, i have your tickets +for the grateful briquettes +on saturday the 20th. +they are 30 apiece +so get out your wallets +i need the rental lease +or be hit with mallets +for my friend has no peace +and i have no ballots. +without which you get no tickets +for my friend has no spigots +disgorging money in hiccups +in the land of honey and pickups. + -- fred t. hamster +~ +Some people imagine that only the person who physically carries out the +killing is creating a negative karmic effect, and that the person who just +gave the orders is not--or, if he is, then only a little. But you +should know that the same karmic result comes to everyone involved, +including even anyone who just felt pleased about it--and therefore how +much more so the person who actually ordered that the killing be carried +out. Each person gets the whole karmic result of killing one animal. It +is not as if one act of killing could be divided up among many people. + + From the Nying-ma _kunzang lama'i shelung_ (The Words of My Perfect Teacher) + written by Patrul Rinpoche (1808-1887), (Padmakara translation group, + trans., New York: HarperCollins Publishers, 1994, 104.) +~ +did you hear about the professor that got some human lips grafted onto his +anus? then when his students were kissing butt, they could get some response. +and he can also now wear a hat on his ass and sit on his head, and no one +knows the difference... +~ +Alpha: +Software undergoes alpha testing as a first step in getting user +feedback. Alpha is Latin for "doesn't work." +~ +Beta: +Software undergoes beta testing shortly before it's released. Beta is +Latin for "still doesn't work." +~ +Computer: +Instrument of torture. The first computer was invented by Roger +"Duffy" Billingsly, a British scientist. In a plot to overthrow Adolf +Hitler, Duffy disguised himself as a German ally and offered his +invention as a gift to the surly dictator. The plot worked. On April +8, 1945, Adolf became so enraged at the "Incompatible File Format" +error message that he shot himself. The war ended soon after Hitler's +death, and Duffy began working for IBM. +~ +CPU: +Central propulsion unit. The CPU is the computer's engine. It +consists of a hard drive, an interface card and a tiny spinning wheel +that's powered by a running rodent--a gerbil if the machine is a 286, +a ferret if it's a 386 and a ferret on speed if it's a 486. +~ +Default Directory: +Black hole. Default directory is where all files that you need +disappear to. +~ +Error message: +Terse, baffling remark used by programmers to place blame on users for +the program's shortcomings. +~ +File: +A document that has been saved with an unidentifiable name. It helps +to think of a file as something stored in a file cabinet--except when +you try to remove the file, the cabinet gives you an electric shock +and tells you the file format is unknown. +~ +Hardware: +Collective term for any computer-related object that can be kicked or +battered. +~ +Help: +The feature that assists in generating more questions. When the help +feature is used correctly, users are able to navigate through a series +of Help screens and end up where they started from without learning +anything. +~ +Input/Output: +Information is input from the keyboard as intelligible data and output +to the printer as unrecognizable junk. +~ +Interim Release: +A programmer's feeble attempt at repentance. +~ +Memory: +Of computer components, the most generous in terms of variety, and the +skimpiest in terms of quantity. +~ +Printer: +A joke in poor taste. A printer consists of three main parts: the +case, the jammed paper tray and the blinking red light. +~ +Programmers: +Computer avengers. Once members of that group of high school nerds +who wore tape on their glasses, played Dungeons and Dragons, and +memorized Star Trek episodes; now millionaires who create +"user-friendly" software to get revenge on whoever gave them noogies. +~ +Reference Manual: +Object that raises the monitor to eye level. Also used to compensate +for that short table leg. +~ +Scheduled Release Date: +A carefully calculated date determined by estimating the actual +shipping date and subtracting six months from it. +~ +User-Friendly: +Of or pertaining to any feature, device or concept that makes perfect +sense to a programmer. +~ +Users: +Collective term for those who stare vacantly at a monitor. Users are +divided into three types: novice, intermediate and expert. + - Novice Users--People who are afraid that simply pressing a key + might break their computer. + - Intermediate Users--People who don't know how to fix their computer + after they've just pressed a key that broke it. + - Expert Users--People who break other people's computers. +~ + X X X X +H8!p(_;XLk/\c@Si8!p(_;XLk/\c@Si8!p(_;XLk/\c@Si8!p(_;XLk/\c@Si8!p(_;XLk/\c@S +FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/ +DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$ +UKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ +JKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH +JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH +&^JKjgzh~&^JKjgzh~&^JKjgzh~&^JKjgzh~&^JKjgzh~&^JKjgzh~&^JKjgzh~&^JKjgzh~&^J +KL)(8&*:KL)(8&*:KL)(8&*:KL)(8&*:KL)(8&*:KL)(8&*:KL)(8&*:KL)(8&*:KL)(8&*:KL) ++J23*&^jj\+J23*&^jj\+J23*&^jj\+J23*&^jj\+J23*&^jj\+J23*&^jj\+J23*&^jj\+J23* +H8!p(_;XLk/\c@Si8!p(_;XLk/\c@Si8!p(_;XLk/\c@Si8!p(_;XLk/\c@Si8!p(_;XLk/\c@S +FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/ +DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$ +UKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ +JKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH +JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH +FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/?*&l|FD/ +DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$%#DJK%$ +UKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ+IO3GUKJ +JKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH//8JHJKH +JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH&HJKL|JKH +&^JKjgzh~&^JKjgzh~&^JKjgzh~&^JKjgzh~&^JKjgzh~&^JKjgzh~&^JKjgzh~&^JKjgzh~&^J +~ +;;;;;;;;;;;;;;;;;;;;;;;;;itIRXItIt=:;iVRIIII:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;:+ItXRYtIIi;IIRXiII+=;;:::;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;::;==+ittYRViII+iiRRtIIiiti++=;;::;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;::;=+iitIIiitIXViII+tYRtIII=iIYIti++=;::;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;:;=+iitIi+=;;=+ttYYtIiiiYIII+==;;=+tItii+=::;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;:;=+itIt+:;=+itt++=ttitt+tiiII+iti++=;;+IIii+=::;;;;;;;;;;;;+V=;; +;;;;;;;;;;;;::=+itIt;;=+itt+;;=+iiii+i==tiII==;=iti++=:=IIii+;::;;;;;;;;;+YMRt; +;;;;;;;;;;;::=+itY;:=+itt=;+itIYIt+;;+Iiiii+IIt+=;=tt++=:+Yti+=::;;;;;;;:WXXWt; +;;;;;;;;;;,;+iiII;;=+iI=;+itYI+;;++tIItIYtiIiiYYti+;+Ii+=;=Yti+=::;;;;;;+RBRM;; +;;;;;;;;;::=+iIY;;+itI;=itYY+;+=tXtIIIIWWIIIIt:+YIt+;=Ii+=;=Yti+=,;;;;;;YMBWY=+ +;;;;;;;;;,;+itY=;=+iI==itYI;=it=BMIIIiMB=;IIIt+=iVt+=;=t++=:iYii+;,;;;;=XXWWVMY +;;;;;;;;::=iiII:;+iIt;+iIV;;+titV.:IIiMt.;YYVRBBBBBY++:Iti+;:YIi+=,;;;iXXRWWBi; +;;;;;;;;,;+iYV+:=++t;;==tt,+YYIII;:ii+IVRBXRBRRRBBBX=+;+t++;:IIi+==+iIItYYVt;;; +;;;;;;;;::+IWWIVXRYi++ii=i;RXXXXtiXXXMMV=;+iRMWWWMYti+;tti=;=titIIIIIti=;;;;;;; +;;;;;;;;;,=XWWXMWXViItt+iYiYMWWWMY+MVBMW+;;MWWBViIYt+==tiiIIIIIIti+=;:;;;;;;;;; +;;;;;;;;;,;tRY+ittt+tIVBIIVt:=tIYVVYVBRV==XWRt;;iIiitIIIIIItti++=::;;;;;;;;;;;; +;;;;;;;;;;,+++iiiI+YRYi;+itYY+;=+ittiiIIYBBBiitIIIIIIIIii=:IIii+;,;;;;;;;;;;;;; +;;;;;;;;;;:;;,;i+VBMX+tI+;+itIYt==ittVRWWWWIIIIItItti+++;;IIii+;,;;;;;;;;;;;;;; +;;;;;;;;;;;;,,:tYYYYi=+itt+;=+iiIIIIYWWWWWWIIIII=itii+;:+Itii+:,;;;;;;;;;;;;;;; +;;;;;;;;;;;;::,==iIIIi=;=+iiiIIIIIItWWRMWWWVIII+ii+=;;+IIii+=::;;;;;;;;;;;;;;;; +;;;;;;;;;;;::,,,:;+IIIIt+iIIIIItiiiMWWXWWWWMiII==;;+tItii+=::;;;;;;;;;;;;;;;;;; +;;;;;;;;;;,:,,,,,,:;iIIIIIIti+;;;+IWWWXWWWWWYIi+tIYtii+=;::;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;,,:::,,,::::=itiiiIII+itIWWWMWWWWWXI+tiii+=;:::;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;:,,:,,:;;;;;:IYIIItiIItMWWWWWWWWWXI;=;;:::;;;;;;;;;;;;;;;;;;;;;;;;;; +~ + Why E-Mail Is Like a Penis? + * In the long-distant past, its only purpose was to transmit information + considered vital to the survival of the species. Some people still + think that's the only thing it should be used for, but most folks today + use it for fun most of the time. + * It has no conscience and no memory. Left to its own devices, it will + just do the same damn dumb things it did before. + * It provides a way to interact with other people. Some people take this + interaction very seriously, others treat it as a lark. Sometimes it's + hard to tell what kind of person you're dealing with until it's too + late. + * If you don't apply the appropriate protective measures, it can spread + viruses. + * It has no brain of its own. Instead, it uses yours. If you use it too + much, you'll find it becomes more and more difficult to think + coherently. + * We attach an importance to it that is far greater than its actual size + and influence warrant. + * If you're not careful what you do with it, it can get you in big + trouble. + * It has its own agenda. Somehow, no matter how good your intentions, it + will warp your behavior. Later you may ask yourself "why on earth did + I do that?" + * Some folks have it, some don't. + Those who have it would be devastated if it were ever cut off. They + think that those who don't have it are somehow inferior. They think it + gives them power. They are wrong. + Those who don't have it may agree that it's a nifty toy, but think it's + not worth the fuss that those who do have it make about it. Still, many + of those who don't have it would like to try it. + * Once you've started playing with it, it's hard to stop. Some people + would just play with it all day if they didn't have work to do. + Thank you, please come again. +~ + _ + _|_|_ + ^/ . ..\^ + ___[=========]___ + ___-==++""" . /. . . \ . """++==-___ + __-+"" __\ .. . . | .. . | . . . /__ ""+-__ + /\__+-"" `-----=====\_ _/=====-----' ""-+__/\ + _/_/ ""="" \_\_ + /_/ \_\ + // | \\ +/") \ | / ("\ +\O\ \*/ /O/ + \_) ---**O**--- (_/ + /*\ + / | \ + | +~ +WHY GOD NEVER RECEIVED TENURE AT ANY UNIVERSITY + +1. He had only one major publication. +2. It had no references. +3. It wasn't published in a refereed journal. +4. Some even doubt he wrote it himself. +5. It may be true that he created the world, + but what has he done since then? +6. His cooperative efforts have been quite limited. +7. The scientific community has had a hard time + replicating his results. +8. He never applied to the Ethics Board + for permission to use human subjects. +9. When one experiment went awry he tried to cover it up + by drowning the subjects. +10. When subjects didn't behave as predicted, + he deleted them from the sample. +11. He rarely came to class, just told students + to read the Book. +12. Some say he had his son teach the class. +13. He expelled his first two students for learning. +14. Although there were only ten requirements, + most students failed his tests. +15. His office hours were infrequent and usually + held on a mountaintop. +~ +When we walk upon Mother Earth, we always plant our feet carefully +because we know the faces of our future generations are looking up +at us from beneath the ground. We never forget them. + -- Oren Lyons, Onondaga Nation +~ +When the shoe fits, +the foot is forgotten... + -- Chuang Tzu +~ +Talent, will, and genius are natural phenomena, like volcanoes, +lakes, mountains, winds, stars, clouds. + -- George Sand -- 1874 +~ +Creeping Featurism is the desire to add every technologically possible +feature to a product whether or not the market needs it or will pay +for it. + -- Leu Platt, CEO Hewlett Packard +~ +everybody's equal, but nobody's the same. -- fred t. hamster +~ +Very often a change of self is needed more than a change of scene. + -- A. C. Benson +~ +The successful mother sets her children free +and becomes more free herself in the process. + -- Robert J. Havinghurst +~ +No amount of energy will take the place of thought. +A strenuous life with its eyes shut is a kind of wild insanity. + -- Henry Van Dyke +~ +It takes less time to do a thing right than to explain why you did it wrong. + -- Longfellow +~ +It's strange the way the imagination, having exhausted one field, +turns for rest and reinvigoration to another. + -- Ellen Glasgow +~ +Men travel faster now, but I do not know if they go to better things. + -- Willa Cather +~ +People who do not understand themselves have a craving for understanding. + -- Dr. Wilhem Stekel +~ +Love is the greatest refreshment of life. + -- Pablo Picasso +~ +An idea isn't responsible for the people who believe it. + -- Don Marquis +~ +I want to find someone on the earth so intelligent +that he welcomes opinions which he condemns. + -- John Jay Chapman +~ +You can exert no influence if you are not susceptible to influence. + -- Carl G. Jung +~ +The greater the difficulty +the more glory in surmounting it. + -- Epicurus +~ +Society is always engaged in a vast conspiracy to preserve itself--at the +expense of the new demands of each new generation. + -- John Haynes Holmes +~ +The only thing that is really difficult is to prove what one believes. + -- Paul Cezanne +~ +There is one word which may serve as a rule of practice for +all one's life--reciprocity. + -- Confucious +~ +Each time we make a decision, it is determined by the good or evil forces, +respectively, which are dominant. + -- Erich Fromm +~ +We taste and feel and see the truth. +We do not reason ourselves into it. + -- W. B. Yeats +~ +"You inside and the wind outside, tangled on the window blind, tell +me why you treated me so unkind. Down where the sun don't shine I'm lonely +and I call your name, no place to go ain't that a shame...." + -- J. Garcia +~ +The point of living, and of being an optimist, is to be foolish enough +to believe the best is yet to come. + -- Peter Ustinov +~ +"Observing formations of pigs flying south for the winter is several orders + of magnitude more likely than having two competing C[++] compilers deal + with more than eight lines of source code the same way." + -- Steve Rimmer -- windows columnist +~ +If asses were rainbows, we'd all have a pot of gold. + -- Walrus +~ +the bird sings +i laugh inside +i have mined +the branches. + -- fred t. hamster +~ +Good judgment is a result of experience. +Experience is a result of poor judgment. + -- Anonymous +~ +in programming, insanity is not a handicap. + -- the walrus +~ + you + have + the + ideal + job +~ +you don't have to get mad every time you have the right to. -- fred t. hamster +~ +It's actually quite straightforward, but first you must be familiar +with the 9 Palaces and 24 Directions. + -- Feng Shui +~ +Every fairly intelligent person realizes that +the price of respectability is a muffled soul +bent on the trivial and mediocre. + -- Walter Lippman +~ +People will admit to anything on the Internet. -- fred t. hamster +~ +If all the beasts were gone, men would die +from great loneliness of spirit, +for whatever happens to the beasts also happens to the man. +All things are connected. +Whatever befalls the earth befalls the sons of earth. + -- Chief Seathl (Seattle) of Suwamish tribe, + State of Washington, 1855 +~ +keep doing good, but don't be a do-gooder. -- fred t. hamster +~ +All systems of thought confine their thinkers within the accepted boundaries. +To free the mind from this conceptual jail, step back from and outside of the +system; detachment enables both sides of the system (that which is within +and that which is not within) to be perceived and dealt with as an object +of knowledge. + -- fred t. hamster +~ +That which exists requires no reaffirmation by the mind; it simply is. +It is that which does not exist that needs constant renewing contact by +the mind; else it would fade from its only sphere of influence, the internal +stage upon which it dances and captures the imagination. + -- fred t. hamster +~ +Everyone's entitled to their own opinions, no matter how stupid. + -- Frank Zappa (paraphrase unfortunately... know the exact quote?) +~ +i love to program. +i live to program. +i am the beaver who reincarnated as a carpenter. +i am the squirrel who came back as jimmy carter. +if there's anything more fun than programming (where i actually use my brain +mainly and not my body), then i don't want to know what it is. +algorithms are my bread. +objects are my butter. +state machines are my toaster. +library hierarchies are my table. +my house is composed of invisible data and my nation rides along cables. +i really need a girlfriend. + -- fred t. hamster +~ +every religion constitutes a view of reality and a view of the world. +each one might suit a particular person differently. +for any religion to claim that it is the one true religion is ridiculous +because it asserts that its one way of viewing reality is the only correct +one. surely this is a bad case of religious egotism or selfishness. + -- fred t. hamster +~ +talking about dharma is like making a home movie. when you watch it later, +you see it, you hear it, and yet it isn't really there and the vital +experience of being there is missing. but when you are there... you know +it and feel it in a way your brain can't accurately record for later +playback. + -- fred t. hamster +~ +Deja Fu: The feeling that somehow, somewhere, you've been kicked in the +head like this before. +~ +A day without sunshine is like night. +~ +There is a CD out entitled "The Worst of Jefferson Airplane". If you buy +this, take it home, play it, and enjoy it, should you take it back and +demand a refund? +~ +College is a fountain of knowledge... and the students are there to drink. +~ +A polar bear is a rectangular bear after a coordinate transform. +~ +Some people say that I must be a horrible person, but that's not true. +I have the heart of a young boy--in a jar on my desk. + -- Stephen King, 3/8/90 +~ +He who dies with the most toys, is, nonetheless, still dead. +~ +Photons have mass? I didn't know they were catholic! +~ +If you had everything, where would you keep it? +~ +I am returning this otherwise good typing paper to you because someone +has printed gibberish all over it and put your name at the top. + -- Supposedly an English Professor, Ohio University +~ +What was sliced bread the greatest thing since? +~ +When aiming for the common denominator, be prepared +for the occasional division by zero. +~ +When you're swimmin' in the creek +And an eel bites your cheek +That's a moray! + -- Fabulous Furry Freak Brothers +~ +Q: How many surrealists does it take to screw in a lightbulb? +A: Two. One to hold the giraffe and the other to fill the bathtub with +brightly colored machine tools. +~ +"... one of the main causes of the fall of the Roman Empire was that, lacking +zero, they had no way to indicate successful termination of their C programs." + -- Robert Firth +~ +Grabel's Law: 2 is not equal to 3--not even for very large values of 2. +~ +Diplomacy is the art of saying "nice doggy" until you can find a rock. +~ +There are two major products to come out of Berkeley: LSD and BSD. +We don't believe this to be a coincidence. +~ +If toast always lands butter-side down, and cats always land on their feet, +what happen if you strap toast on the back of a cat and drop it? + -- Steven Wright +~ +One night I walked home very late and fell asleep in somebody's satellite +dish. My dreams were showing up on TV's all over the world. + -- Steven Wright +~ +My dental hygienist is cute. Every time I visit, I eat a whole package of +Oreo cookies while I'm in the waiting room. Sometimes she has to cancel +the rest of the afternoon's appointments. + -- Steven Wright +~ +Right now I'm having amnesia and deja vu at the same time. I think I've +forgotten this before. + -- Steven Wright +~ +Smoking cures weight problems... eventually. + -- Steven Wright +~ +I have an inferiority complex. But it's not a very good one. + -- Steven Wright +~ +I was in the supermarket the other day, and I met a lady in the aisle where +they keep the generic brands. Her name was 'woman'. + -- Steven Wright +~ +I had a friend who was a clown. When he died, all his friends went to the +funeral in one car. + -- Steven Wright +~ +I'd like to sing you a song now about my old girlfriend. It's +called, 'They'll Find Her When the Leaves Blow Away 'Cause I'm +Not Raking 'Til Spring.' + -- Steven Wright +~ +When I woke up this morning my girlfriend asked me, 'Did you sleep good?' +I said, 'No, I made a few mistakes.' + -- Steven Wright +~ +I was trying to daydream, but my mind kept wandering. + -- Steven Wright +~ +The other day, I was walking my dog around my building... on the ledge. + -- Steven Wright +~ +Some people are afraid of heights. Not me, I'm afraid of widths. + -- Steven Wright +~ +I spilled spot remover on my dog. He's gone now. + -- Steven Wright +~ +Referring to a glass of water: I mixed this myself. Two parts H, one +part O. I don't trust anybody! + -- Steven Wright +~ +I went to the cinema, and the prices were: Adults $5.00, children $2.50. +So I said, 'Give me two boys and a girl.' + -- Steven Wright +~ +I went to a restaurant that serves 'breakfast at any time.' So I ordered +French Toast during the Renaissance. + -- Steven Wright +~ +There's a pizza place near where I live that sells only slices. In the back +you can see a guy tossing a triangle in the air. + -- Steven Wright +~ +I went to a general store. They wouldn't let me buy anything specifically. + -- Steven Wright +~ +I went down the street to the 24-hour grocery. When I got there, the guy +was locking the front door. I said, 'Hey, the sign says you're open 24 +hours.' He said, 'Yes, but not in a row.' + -- Steven Wright +~ +There was a power outage at a department store yesterday. Twenty people +were trapped on the escalators. + -- Steven Wright +~ +I bought my brother some gift-wrap for Christmas. I took it to the Gift Wrap +Department and told them to wrap it, but in a different print so he would +know when to stop unwrapping. + -- Steven Wright +~ +I was born by Cesarean section... But not so you'd notice. It's just that +when I leave a house, I go out through the window. + -- Steven Wright +~ +When I was little, my grandfather used to make me stand in a closet for five +minutes without moving. He said it was elevator practice. + -- Steven Wright +~ +I didn't get a toy train like the other kids. I got a toy subway instead. +You couldn't see anything, but every now and then you'd hear this rumbling +noise go by. + -- Steven Wright +~ +Last week the candle factory burned down. Everyone just stood around and +sang 'Happy Birthday'. + -- Steven Wright +~ +A wino asked me for change... I gave him my shirt. + -- Steven Wright +~ +I bought this thing for my car. You put it on your car, it sends out this +little noise, so when you drive through the woods, deer won't run in front +of your car. I installed it backwards by accident... Driving down the +street with a herd of deer chasing me. + -- Steven Wright +~ +The ice cream truck in my neighborhood plays 'Helter Skelter'. + -- Steven Wright +~ +The sky already fell. Now what? + -- Steven Wright +~ +My girlfriend and I went on a picnic. I don't know how she did it, but she +got poison ivy on the brain. When it itched, the only way she could scratch +it was to think about sandpaper. + -- Steven Wright +~ +Trees that grow in smoggy cities are needed to make carbon paper. + -- Steven Wright +~ +What's another word for Thesaurus? + -- Steven Wright +~ +When I was crossing the border into Canada, they asked if I had any firearms +with me. I said, 'Well, what do you need?' + -- Steven Wright +~ +Why doesn't the fattest man in the world become a hockey goalie? + -- Steven Wright +~ +You know how it is when you decide to lie and say the check is in the mail, +and then you remember it really is? + -- Steven Wright +~ +I've been doing a lot of abstract painting lately, extremely abstract. No +brush, no paint, no canvas, I just think about it. + -- Steven Wright +~ +My watch is three hours fast, and I can't fix it. So I'm going to move to +New York. + -- Steven Wright +~ +When I die, I'm leaving my body to science fiction. + -- Steven Wright +~ +One time I went to a museum where all the work on display had been done by +children. They had all the paintings up on refrigerators. + -- Steven Wright +~ +When I was a little kid we had a sand box. It was a quicksand box. I was +an only child... Eventually. + -- Steven Wright +~ +One day I got on the bus, and when I stepped in, I saw the most gorgeous +blond Chinese girl. I sat beside her. I said, 'Hi', And she said, 'Hi', +and then I said, 'Nice day, isn't it?' And she said, 'I saw my analyst +today and he says I have a problem. So I asked, 'What's the problem?' +She replied, 'I can't tell you. I don't even know you.' I said, 'Well, +sometimes it's good to tell your problems to a perfect stranger on a bus.' +So she said, 'Well, my analyst said I'm a nymphomaniac and I only like +Jewish cowboys... By the way, my name is Denise.' I said, 'Hello, Denise. +My name is Bucky Goldstein.' + -- Steven Wright +~ +Today I was arrested for scalping low numbers at the deli. I sold a #3 for +28 bucks. + -- Steven Wright +~ +Do you think that when they asked George Washington for ID that he just +whipped out a quarter? + -- Steven Wright +~ +I'm writing an unauthorized autobiography. + -- Steven Wright +~ +What happens if you put a slinky on an escalator? + -- Steven Wright +~ +If a word in the dictionary were misspelled, how would we know? + -- Steven Wright +~ +If you tell a joke in the forest, but nobody laughs, was it a joke? + -- Steven Wright +~ +If you were going to shoot a mime, would you use a silencer? + -- Steven Wright +~ +In Vegas, I got into a long argument with the man at the roulette wheel over +what I considered to be an odd number. + -- Steven Wright +~ +Is "tired old cliche" one? + -- Steven Wright +~ +My aunt gave me a walkie-talkie for my birthday. She says if I'm good, +she'll give me the other one next year. + -- Steven Wright +~ +At an October re-trial in Leeds, England, jurors took about an +hour to acquit police officer Andrew Whitfield, 30, of stealing a +calculator worth about $4. The cost of the trial, plus the original +mistrial, plus keeping Whitfield on paid suspension for 14 months +as required by law, was about $158,000. + -- News of the Weird -- Compiled by Chuck Shepard +~ +Whenever I watch TV and see those poor starving kids all over the world, +I can't help but cry. I mean I'd love to be skinny like that but not +with those flies and death and stuff. + -- Mariah Carey +~ +Question: + If you could live forever, would you and why? +Answer: + I would not live forever, because we should not live forever, because + if we were supposed to live forever, then we would live forever, but we + cannot live forever, which is why I would not live forever. + -- Miss Alabama in the 1994 Miss Universe contest +~ +Researchers have discovered that chocolate produces some of the same reactions +in the brain as marijuana... The researchers also discovered other +similarities between the two, but can't remember what they are. + -- Matt Lauer on NBC's Today show, August 22 +~ +I haven't committed a crime. What I did was fail to comply with the law. + -- David Dinkins, New York City Mayor, answering accusations that he + failed to pay his taxes. +~ +Smoking kills. If you're killed, you've lost a very important part of your +life. + -- Brooke Shields, during an interview to become spokesperson + for a federal anti-smoking campaign +~ +I've never had major knee surgery on any other part of my body. + -- Winston Bennett, University of Kentucky basketball forward +~ +Outside of the killings, Washington has one of the lowest crime rates +in the country. + -- Mayor Marion Barry, Washington, D.C. +~ +Beginning in February 1976 your assistance benefits will be discontinued... +Reason: + it has been reported to our office that you expired on January 1, 1976. + -- Letter from the Illinois Department of Public Aid +~ +The Holocaust was an obscene period in our nation's history... This century's +history... We all lived in this century. I didn't live in this century. + -- Dan Quayle, then Indiana senator and Republican vice-presidential + candidate during a news conference in which he was asked his opinion + of the Holocaust. +~ +I've always thought that underpopulated countries in Africa are vastly +underpolluted. + -- Lawrence Summers, chief economist of the World Bank, explaining why + we should export toxic wastes to Third World countries +~ +After finding no qualified candidates for the position of principal, +the school board is extremely pleased to announce the appointment of +David Steele to the post. + -- Philip Streifer, Superintendent of Schools, Barrington, Rhode Island +~ +The doctors X-rayed my head and found nothing. + -- Dizzy Dean explaining how he felt after being hit on the head + by a ball in the 1934 World Series +~ + there are some who believe that intelligence can only be won at the cost of +other's intelligence; that is, there is no way to be intelligent without being +more intelligent than someone else. i hypothesize that talking to this kind +of person is a very draining experience because all that they are interested +in is proving how much smarter they are than you are. + i believe that people who want to increase intelligence everywhere are easy +to talk to; they want you to know what they know and vice-versa, not to prove +that they are the most intelligent person in the room. + -- fred t. hamster +~ +You are 97% water; the other 3% keeps you from drowning. + -- P. E. Morris +~ +It got to the end of our show, so I was just wandering around. I had +this maternity dress on and a white face and I was doing unattractive +things, spitting on people, things like that. + -- Iggy Pop, sept. 22, 1968, several minutes before he signed his first + record contract. +~ +The credit belongs to those who are actually in the arena, who strive +valiantly; who know the great enthusiasms, the great devotions, and speed +themselves in a worthy cause; who at the best, know the triumph of high +achievement; and who, at the worst, if they fail, fail while daring +greatly, so that their place shall never be with those cold and timid +souls who know neither victory nor defeat. + -- Theodore Roosevelt +~ +I am not a vegetarian because I love animals; I am a vegetarian +because I hate plants. + -- A. Whitney Brown +~ +My young brother asked me what happens after we die. I told him we get +buried under a bunch of dirt and worms eat our bodies. I guess I should +have told him the truth--that most of us go to Hell and burn eternally, +but I didn't want to upset him. + -- Deep Thoughts Jr., Age 10 +~ +When I go to heaven, I want to see my grandpa again. But he better have +lost the nose hair and the old-man smell. + -- Deep Thoughts Jr., Age 5 +~ +I once heard the voice of God. It said "Vrrrrmmmm." Unless it was just +a lawn mower. + -- Deep Thoughts Jr., Age 11 +~ +I don't know about you, but I enjoy watching paint dry. I imagine that +the wet paint is a big freshwater lake that is the only source of water +for some tiny cities by the lake. As the lake gets drier, the population +gets more desperate, and sometimes there are water riots. Once there was +a big fire and everyone died. + -- Deep Thoughts Jr., Age 13 +~ +I like to go down to the dog pound and pretend that I've found my dog. +Then I tell them to kill it anyway because I already gave away all of +his stuff. Dog people sure don't have a sense of humor. + -- Deep Thoughts Jr., Age 14 +~ +I believe you should live each day as if it is your last, which is why I +don't have any clean laundry because, come on, who wants to wash clothes +on the last day of their life? + -- Deep Thoughts Jr., Age 15 +~ +It sure would be nice if we got a day off for the president's birthday, +like they do for the queen. Of course, then we would have a lot of people +voting for a candidate born on July 3 or December 26, just for the long +weekends. + -- Deep Thoughts Jr., Age 8 +~ +As you make your way through this hectic world of ours, set aside a few +minutes each day. At the end of the year, you'll have a couple of days +saved up. + -- Deep Thoughts Jr., Age 7 +~ +Democracy is a beautiful thing, except for that part about letting just +any old yokel vote. + -- Deep Thoughts Jr., Age 10 +~ +Home is where the house is. + -- Deep Thoughts Jr., Age 6 +~ +Often, when I am reading a good book, I stop and thank my teacher. That +is, I used to, until she got an unlisted number. + -- Deep Thoughts Jr., Age 15 +~ +It would be terrible if the Red Cross Bloodmobile got into an accident. +No, wait. That would be good because if anyone needed it, the blood would be +right there. + -- Deep Thoughts Jr., Age 5 +~ +Give me the strength to change the things I can, the grace to accept the +things I cannot, and a great big bag of money. + -- Deep Thoughts Jr., Age 13 +~ +I bet living in a nudist colony takes all the fun out of Halloween. + -- Deep Thoughts Jr., Age 13 +~ +For centuries, people thought the moon was made of green cheese. Then +the astronauts found that the moon is really a big hard rock. That's what +happens to cheese when you leave it out. + -- Deep Thoughts Jr., Age 6 +~ +Think of the biggest number you can. Now add five. Then, imagine if you +had that many Twinkies. Wow, that's five more than the biggest number +you could come up with! + -- Deep Thoughts Jr., Age 6 +~ +The only stupid question is the one that is never asked, except maybe +"Don't you think it is about time you audited my return?" or "Isn't it morally +wrong to give me a warning when, in fact, I was speeding?" + -- Deep Thoughts Jr., Age 15 +~ +Once, I wept for I had no shoes. Then I came upon a man who had no feet. +So I took his shoes. I mean, it's not like he really needed them, right? + -- Deep Thoughts Jr., Age 15 +~ +I often wonder how come John Tesh isn't as popular a singer as some +people think he should be. Then, I remember it's because he sucks. + -- Deep Thoughts Jr., Age 15 +~ +If we could just get everyone to close their eyes and visualize world +peace for an hour, imagine how serene and quiet it would be until the +looting started. + -- Deep Thoughts Jr., Age 15 +~ +Give me ambiguity or give me something else. +~ +Lobotomies for republicans? Why be redundant? +~ +The last time we mixed politics and religion, +people got burned at the stake. +~ +Eschew Obfuscation. +~ +Minimum wage for politicians. +~ +inova, +i dancing nude green +proton, i +(a haiku-style anagram for "inova engineering and production") + -- fred t. hamster +~ +If at first you don't succeed, destroy all evidence that you tried. +~ +A conclusion is the place where you got tired of thinking. +~ +Eagles may soar, but weasels aren't sucked into jet engines. +~ +Experience is something you don't get until just after you need it. +~ +A conscience is what hurts when all your other parts feel so good. +~ +He who hesitates is probably right. +~ +Never do card tricks for the group you play poker with. +~ +The colder the X-ray table, the more of your body is required on it. +~ +To succeed in politics, it is often necessary to rise above your principles. +~ +Two wrongs are only the beginning. +~ +You never really learn to swear until you learn to drive. +~ +The problem with the gene pool is that there is no lifeguard. +~ +Monday is an awful way to spend 1/7th of your life. +~ +The sooner you fall behind, the more time you'll have to catch up. +~ +A clear conscience is usually the sign of a bad memory. +~ +Change is inevitable... except from vending machines. +~ +Plan to be spontaneous tomorrow. +~ +Half the people you know are above average. +~ +99 percent of lawyers give the rest a bad name. +~ +42.7 percent of all statistics are made up on the spot. +~ +If at first you don't succeed, then skydiving definitely isn't for you. +~ +Oh, yeah, what are you gonna do? Release the dogs? Or +the bees? Or the dogs with bees in their mouth and when +they bark, they shoot bees at you? + -- Homer Simpson +~ +Son, when you participate in sporting events, it's not +whether you win or lose... it's how drunk you get. + -- Homer Simpson +~ +Kids, you tried your best and you failed miserably. +The lesson is, never try. + -- Homer Simpson +~ +It's not easy to juggle a pregnant wife and a troubled child, but +somehow I managed to fit in eight hours of TV a day. + -- Homer Simpson +~ +Homer: Are you saying you're never going to eat any animal again? What + about bacon? +Lisa: No. +Homer: Ham? +Lisa: No! +Homer: Pork chops? +Lisa: Dad, those all come from the same animal! +Homer: Heh heh heh... ooh... yeah.... right, Lisa. A wonderful... + magical animal. +~ +Marge: Do you want your son to be Chief Justice of the + Supreme Court, or a sleazy male stripper? +Homer: Can't he be both, like the late Earl Warren? +Marge: Earl Warren was never a stripper! +Homer: Oh, now who's being naive? +~ +Homer: But every time I learn something new, it pushes out + something old! Remember that time I took a home + wine-making course and forgot how to drive? +Marge: That's because you were drunk! +Homer: And how! +~ +Oh, Lisa, you and your stories: Bart's a vampire, beer +kills brain cells. Now let's go back to that... +building...thingie... where our beds and TV... is. + -- Homer Simpson +~ +Operator! Give me the number for 911! + -- Homer Simpson +~ +Lenny: Hey, Homer? What do I tell the boss? +Homer: Tell him I'm going to the back seat of my car with + the woman I love, and I won't be back for ten minutes! +~ +Big brother representative: Now, Mr. Simpson, may I ask why + you're here? +Homer's brain: Don't say revenge. Don't say revenge. +Homer: Ummm... revenge? +Homer's brain: Okay, that's it. I'm outta here. +(step step step step step...slam) +~ +Homer: Okay, brain. You don't like me, and I don't like + you, but let's get through this thing and then I can + continue killing you with beer. +Homer's Brain: It's a deal! +~ +Homer: But Marge! I was a political prisoner! +Marge: How were you a political prisoner? +Homer: I kicked a giant mouse in the butt! Do I have to + draw you a picture? +~ +Homer: Bart, a woman is like a beer. They look good, they + smell good, and you'd step over your own mother just + to get one! (chugs beer) +~ +Old man: Take this doll, but beware; it carries a terrible curse. +Homer: Ooo, that's bad. +Old man: But it comes with a free serving of frozen yogurt! +Homer: That's good! +Old man: The frozen yogurt is also cursed. +Homer: That's bad. +Old man: But it comes with your choice of toppings! +Homer: That's good! +Old man: The toppings contain potassium benzoate... +Homer: ...? +Old man: That's bad. +Homer: Can I go now? +~ +Getting out of jury duty is easy. The trick is to say +you're prejudiced against all races. + -- Homer Simpson +~ +Homer's brain: Use reverse psychology. +Homer: Oh, that sounds too complicated. +Homer's brain: Okay, don't use reverse psychology. +Homer: Okay, I will! +~ +Homer: When I first heard that Marge was joining the police academy, I thought + it would be fun and zany, like that movie--Spaceballs. But instead it was + dark and disturbing. Like that movie--Police Academy. +~ +Marge: Homer, did you call the audience "Chicken"? +Homer: No! I swear on this bible! +Marge: That's not a bible. That's a book of carpet samples. +Homer: Mmmm... fuzzy. +~ +Lisa: Dad, we did something very bad! +Homer: Did you wreck the car? +Bart: No. +Homer: Did you raise the dead? +Lisa: Yes. +Homer: But the car's okay? +Bart & Lisa: Uh-huh. +Homer: All right then. +~ +Mmmmm... reprocessed pig fat... -- Homer Simpson +~ +(praying): Dear Lord, the gods have been good to me. As +an offering, I present these milk and cookies. If you +wish me to eat them instead, please give me no sign +whatsoever... thy will be done (munch munch munch). + -- Homer Simpson +~ +Homer: (On George HW Bush) I didn't vote for him! +Marge: You didn't vote for anybody. +Homer: I voted for Prell to go back to the old glass bottle. + Then I became deeply cynical. +~ +What's the point of going out? We're just going to +wind up back here anyway. + -- Homer Simpson +~ +I would kill everyone in this room for a drop of sweet beer. + -- Homer Simpson +~ +All right, brain, I don't like you and you don't like me--so let's +just do this and I'll get back to killing you with beer. + -- Homer Simpson +~ +You can't be a real country unless you have a beer and an airline -- +it helps if you have some kind of a football team, or some nuclear +weapons, but at the very least you need a beer. + -- Frank Zappa +~ +Always do sober what you said you'd do drunk. That will teach you to +keep your mouth shut. + -- Ernest Hemmingway +~ +Always remember that I have taken more out of alcohol than alcohol has +taken out of me. + -- Winston Churchill +~ +He was a wise man who invented beer. + -- Plato +~ +Time is never wasted when you're wasted all the time. + -- Catherine Zandonella +~ +A woman drove me to drink and I didn't even have the decency to thank her. + -- W. C. Fields +~ +"Sir, if I were your wife, I would put poison in your coffee." + -- Lady Nancy Astor speaking to Winston Churchill +"Madam, if I were your husband, I would drink it." + -- Churchill's reply +~ +If God had intended us to drink beer, He would have given us stomachs. + -- David Daye +~ +Work is the curse of the drinking class. + -- Oscar Wilde +~ +When I read about the evils of drinking, I gave up reading. + -- Henny Youngman +~ +Beer is proof that God loves us and wants us to be happy. + -- Benjamin Franklin +~ +If you ever reach total enlightenment while drinking beer, I bet it +makes beer shoot out your nose. + -- Deep Thoughts by Jack Handey +~ +Without question, the greatest invention in the history of mankind is +beer. Oh, I grant you that the wheel was also a fine invention, but +the wheel does not go nearly as well with pizza. + -- Dave Barry +~ +The problem with the world is that everyone is a few drinks behind. + -- Humphrey Bogart +~ +Why is American beer served cold? So you can tell it from urine. + -- David Moulton +~ +People who drink light "beer" don't like the taste of beer; they just +like to pee a lot. + -- Capital Brewery, Middleton, WI +~ +Give me a woman who loves beer and I will conquer the world. + -- Kaiser Wilhelm +~ +Not all chemicals are bad. Without chemicals such as hydrogen and +oxygen, for example, there would be no way to make water, a vital +ingredient in beer. + -- Dave Barry +~ +I drink to make other people interesting. + -- George Jean Nathan +~ +They who drink beer will think beer. + -- Washington Irving +~ +An intelligent man is sometimes forced to be drunk to spend time with +his fools. + -- For Whom the Bell Tolls, Ernest Hemmingway +~ +You're not drunk if you can lie on the floor without holding on. + -- Dean Martin +~ +I will not carve gods. + -- Bart Simpson (at the blackboard) +~ +I will not spank others. + -- Bart Simpson (at the blackboard) +~ +I will not aim for the head. + -- Bart Simpson (at the blackboard) +~ +I will not barf unless I'm sick. + -- Bart Simpson (at the blackboard) +~ +I will not expose the ignorance of the faculty. + -- Bart Simpson (at the blackboard) +~ +I saw nothing unusual in the teacher's lounge. + -- Bart Simpson (at the blackboard) +~ +I will not conduct my own fire drills. + -- Bart Simpson (at the blackboard) +~ +Funny noises are not funny. + -- Bart Simpson (at the blackboard) +~ +I will not snap bras. + -- Bart Simpson (at the blackboard) +~ +I will not fake seizures. + -- Bart Simpson (at the blackboard) +~ +This punishment is not boring and pointless. + -- Bart Simpson (at the blackboard) +~ +My name is not Dr. Death. + -- Bart Simpson (at the blackboard) +~ +I will not defame New Orleans. + -- Bart Simpson (at the blackboard) +~ +I will not prescribe medication. + -- Bart Simpson (at the blackboard) +~ +I will not bury the new kid. + -- Bart Simpson (at the blackboard) +~ +I will not teach others to fly. + -- Bart Simpson (at the blackboard) +~ +I will not bring sheep to class. + -- Bart Simpson (at the blackboard) +~ +A burp is not an answer. + -- Bart Simpson (at the blackboard) +~ +Teacher is not a leper. + -- Bart Simpson (at the blackboard) +~ +Coffee is not for kids. + -- Bart Simpson (at the blackboard) +~ +I will not eat things for money. + -- Bart Simpson (at the blackboard) +~ +I will not yell "She's Dead" at roll call. + -- Bart Simpson (at the blackboard) +~ +The principal's toupee is not a Frisbee. + -- Bart Simpson (at the blackboard) +~ +I will not call the principal "spud head". + -- Bart Simpson (at the blackboard) +~ +Goldfish don't bounce. + -- Bart Simpson (at the blackboard) +~ +Mud is not one of the 4 food groups. + -- Bart Simpson (at the blackboard) +~ +No one is interested in my underpants. + -- Bart Simpson (at the blackboard) +~ +I will not sell miracle cures. + -- Bart Simpson (at the blackboard) +~ +I will return the seeing-eye dog. + -- Bart Simpson (at the blackboard) +~ +I do not have diplomatic immunity. + -- Bart Simpson (at the blackboard) +~ +I will not charge admission to the bathroom. + -- Bart Simpson (at the blackboard) +~ +The cafeteria deep fryer is not a toy. + -- Bart Simpson (at the blackboard) +~ +All work and no play makes Bart a dull boy. + -- Bart Simpson (at the blackboard) +~ +I will not say "Springfield" just to get applause. + -- Bart Simpson (at the blackboard) +~ +I am not authorized to fire substitute teachers. + -- Bart Simpson (at the blackboard) +~ +My homework was not stolen by a one-armed man. + -- Bart Simpson (at the blackboard) +~ +I will not go near the kindergarten turtle. + -- Bart Simpson (at the blackboard) +~ +I am not deliciously saucy. + -- Bart Simpson (at the blackboard) +~ +Organ transplants are best left to professionals. + -- Bart Simpson (at the blackboard) +~ +The Pledge of Allegiance does not end with "Hail Satan". + -- Bart Simpson (at the blackboard) +~ +I will not celebrate meaningless milestones. + -- Bart Simpson (at the blackboard) +~ +There are plenty of businesses like show business. + -- Bart Simpson (at the blackboard) +~ +Five days is not too long to wait for a gun. + -- Bart Simpson (at the blackboard) +~ +I will not waste chalk. + -- Bart Simpson (at the blackboard) +~ +I will not skateboard in the halls. + -- Bart Simpson (at the blackboard) +~ +Underwear should be worn on the inside. + -- Bart Simpson (at the blackboard) +~ +I will never win an emmy. + -- Bart Simpson (at the blackboard) +~ +I will not torment the emotionally frail. + -- Bart Simpson (at the blackboard) +~ +Bitter, unsuccessful middle aged loser wallowing in an unending sea of inert, +drooping loneliness looking for 24 year old needy leech-like hanger-on to +abuse with dull stories, tired sex and Herb Alpert albums. + -- from "A Collection of Personal Ads From Alternative Newspapers," + by Skippy Williams and Zohre Crumpton, 1996, Simon and Schuster. +~ +Me--trying to sleep on the bus station bench, pleading with you to give me a +cigarette; you--choking on my odor, tripping over your purse trying to get +away; at the last moment, our eyes meeting. Yours were blue. +Can I have a dollar? + -- from "A Collection of Personal Ads From Alternative Newspapers," + by Skippy Williams and Zohre Crumpton, 1996, Simon and Schuster. +~ +Imp and angel. Disembodied head in jar, 24, seeks pixie goddess to fiddle with +while Rome burns. You bring marshmallows. No. I make joke. You like laugh? +I like comebacks and confessions. Send photo of someone else. + -- from "A Collection of Personal Ads From Alternative Newspapers," + by Skippy Williams and Zohre Crumpton, 1996, Simon and Schuster. +~ +I am spitting kitty. Ftt Fttttttt. I am angry bear. Grrrrr. I am large +watermelon seed stuck in your nose. Zermmmmmmmmmm. I am small biting spider +in your underwear. Yub yub yub. No mimes. + -- from "A Collection of Personal Ads From Alternative Newspapers," + by Skippy Williams and Zohre Crumpton, 1996, Simon and Schuster. +~ +Three toed mango peeler searching for wicked lesbian infielder. Like screaming +and marking territory with urine? Let's make banana enchiladas together in my +bathtub. You bring the salsa. + -- from "A Collection of Personal Ads From Alternative Newspapers," + by Skippy Williams and Zohre Crumpton, 1996, Simon and Schuster. +~ +Mongoloid spastic underwear model with extra limb (you guess where?) in search +of bottlenosed dolphin and extra prickly cactus juice. Soup is good food. + -- from "A Collection of Personal Ads From Alternative Newspapers," + by Skippy Williams and Zohre Crumpton, 1996, Simon and Schuster. +~ +I like eating mayonnaise and peanut butter sandwiches in the rain, watching +Barney Miller reruns, peeing on birds in the park and licking strangers on the +subway; you eat beets raw, have climbed Kilimanjaro, and sweat freely and +often. Must wear size five shoes. + -- from "A Collection of Personal Ads From Alternative Newspapers," + by Skippy Williams and Zohre Crumpton, 1996, Simon and Schuster. +~ +Timber! Falling downward is the lumber of my love. You grind your axe of +passion into my endangered headlands. Don't make me into a bureau. I want +to be lots and lots of toothpicks. + -- from "A Collection of Personal Ads From Alternative Newspapers," + by Skippy Williams and Zohre Crumpton, 1996, Simon and Schuster. +~ +Small lumpy squid monkey seeks healthy woman with no identifying scars, any +age. Must have all limbs. Recommend appreciation of high-pitched, screeching +noises. Must like being bored and lonely. Must not touch the squids, EVER. + -- from "A Collection of Personal Ads From Alternative Newspapers," + by Skippy Williams and Zohre Crumpton, 1996, Simon and Schuster. +~ +There is a little place in the jumbled sock drawer of my heart where you +match up all the pairs, throw out the ones with holes in them, and buy me +some of those neat dressy ones with the weird black and red geometrical +designs on them. + -- from "A Collection of Personal Ads From Alternative Newspapers," + by Skippy Williams and Zohre Crumpton, 1996, Simon and Schuster. +~ +Mmmm Pez! Rabid Wonder Woman fan looking for someone in satin tights, +fighting for our rights and the old red, white 'n blue. You look like +Linda Carter? Big plus. Know all words to theme song? Marry me. + -- from "A Collection of Personal Ads From Alternative Newspapers," + by Skippy Williams and Zohre Crumpton, 1996, Simon and Schuster. +~ +Sanctimonious mordacious raconteur seeking same for hijinks and hiballs. +SJM 27 wants to look someone in the eye so don't be tall. Or, if you can't +help it, enjoy laying down. Wanna swim upstream? + -- from "A Collection of Personal Ads From Alternative Newspapers," + by Skippy Williams and Zohre Crumpton, 1996, Simon and Schuster. +~ +Remember that summer you spent with your parents in Hawaii and how mad you +were that they made you go? And how you were hopelessly bored until you saw +the most gorgeous man you'd ever encountered strolling down the beach looking +at you, skillfully removing your skimpy bikini with his piercing eyes? And +how you spent the last month imagining him taking you in every possible way, +masturbating feverishly day and night, wishing he would reappear, but he never +did because you were 15 and he would have gone to jail? That was me, and +you just turned 18. + -- from "A Collection of Personal Ads From Alternative Newspapers," + by Skippy Williams and Zohre Crumpton, 1996, Simon and Schuster. +~ +Angry, simple-minded, balding, partially blind ex-circus flipper boy with a +passion for covering lovers in sour cream and gravy seeks exotic, heavily +tattooed piercing fanatic, preferably hairy, either sex, for whippings, +bizarre sex and fashion consulting. No freaks. + -- from "A Collection of Personal Ads From Alternative Newspapers," + by Skippy Williams and Zohre Crumpton, 1996, Simon and Schuster. +~ +A freshman at Eagle Rock Junior High won first prize at the Greater Idaho +Falls Science Fair, April 26. He was attempting to show how conditioned +we have become to the alarmists practicing junk science and spreading fear +of everything in our environment. In his project he urged people to sign +a petition demanding strict control or total elimination of the chemical +"dihydrogen monoxide." And for plenty of good reasons, since + + 1. it can cause excessive sweating and vomiting; + 2. it is a major component in acid rain; + 3. it can cause severe burns in its gaseous state; + 4. accidental inhalation can kill you; + 5. it contributes to erosion; + 6. it decreases effectiveness of automobile brakes; + 7. it has been found in tumors of terminal cancer patients. + +He asked 50 people if they supported a ban of the chemical. Forty-three said +yes, six were undecided, and only one knew that the chemical was water. The +title of his prize winning project was, "How Gullible Are We?". +The conclusion is obvious. +~ +If you ever see me getting beaten by the police, put down the video camera +and come help me. + -- Bobcat Goldthwait +~ +I ask people why they have deer heads on their walls. They always say because +it's such a beautiful animal. There you go. I think my mother is attractive, +but I have photographs of her. + -- Ellen DeGeneres +~ +I have six locks on my door all in a row. When I go out, I lock every other +one. I figure no matter how long somebody stands there picking the locks, they +are always locking three. + -- Elayne Boosler +~ +Ever wonder if illiterate people get the full effect of alphabet soup? + -- John Mendoza +~ +Relationships are hard. It's like a full-time job, and we should treat it like +one. If your boyfriend or girlfriend wants to leave you, they should give you +two weeks' notice. There should be severance pay and before they leave you, +they should have to find you a temp. + -- Bob Ettinger +~ +I don't know what's wrong with my television set. I was getting C-Span and +the Home Shopping Network on the same station. I actually bought a +congressman. + -- Bruce Baum +~ +I had a linguistics professor who said that it's man's ability to use language +that makes him the dominant species on the planet. That may may be. But I +think there's one other thing that separates us from animals. We aren't +afraid of vacuum cleaners. + -- Jeff Stilson +~ +Did you ever walk in a room and forget why you walked in? I think that's how +dogs spend their lives. + -- Sue Murphy +~ +Maybe there is no actual place called hell. Maybe hell is just having to +listen to our grandparents breathe through their noses when they're eating +sandwiches. + -- Jim Carrey +~ +The statistics on sanity are that one out of every four Americans is suffering +from some form of mental illness. Think of your three best friends. If they +are okay, then it's you. + -- Rita Mae Brown +~ +Now they show you how detergents take out bloodstains, a pretty violent image +there. I think if you've got a T-shirt with a bloodstain all over it, maybe +laundry isn't your biggest problem. Maybe you should get rid of the body +before you do the wash. + -- Jerry Seinfeld +~ +USA Today has come out with a new survey: Apparently three out of four people +make up 75 percent of the population. + -- David Letterman +~ +A lady came up to me on the street and pointed at my suede jacket. 'You know +a cow was murdered for that jacket?' she sneered. I replied in a psychotic +tone, 'I didn't know there were any witnesses. Now I'll have to kill you too.' + -- Jake Johansen +~ +I always wanted to be somebody, but I should have been more specific. + -- Lily Tomlin +~ +The Swiss have an interesting army. Five hundred years without a war. Pretty +impressive. Also pretty lucky for them. Ever see that little Swiss Army knife +they have to fight with? Not much of a weapon there. Corkscrews. Bottle +openers. "Come on, buddy, let's go. You get past me, the guy in back of me, +he's got a spoon. Back off. I've got the toe clippers right here." + -- Jerry Seinfeld +~ +Why does Sea World have a seafood restaurant? I'm halfway through my +fishburger and I realize, Oh my God... I could be eating a slow learner. + -- Lynda Montgomery +~ +Sometimes I think war is God's way of teaching us geography. + -- Paul Rodriguez +~ +Republicans understand the importance of bondage between a mother and child. + -- Vice President Dan Quayle +~ +Welcome to President Bush, Mrs. Bush, and my fellow astronauts. + -- Vice President Dan Quayle +~ +I believe we are on an irreversible trend toward more freedom +and democracy--but that could change. + -- Vice President Dan Quayle, 5/22/89 +~ +One word sums up probably the responsibility of any vice president, and that +one word is 'to be prepared'. + -- Vice President Dan Quayle, 12/6/89 +~ +May our nation continue to be the beakon of hope to the world. + -- The Quayles' 1989 Christmas card +~ +Verbosity leads to unclear, inarticulate things. + -- Vice President Dan Quayle +~ +We don't want to go back to tomorrow, we want to go forward. + -- Vice President Dan Quayle +~ +I have made good judgments in the past. I have made good judgments in the +future. + -- Vice President Dan Quayle +~ +The future will be better tomorrow. + -- Vice President Dan Quayle +~ +We're going to have the best-educated American people in the world. + -- Vice President Dan Quayle +~ +People that are really very weird can get into sensitive positions and have a +tremendous impact on history. + -- Vice President Dan Quayle +~ +I stand by all the misstatements that I've made. + -- Vice President Dan Quayle to Sam Donaldson, 8/17/89 +~ +We have a firm commitment to NATO, we are a part of NATO. We have a firm +commitment to Europe. We are a part of Europe. + -- Vice President Dan Quayle +~ +I am not part of the problem. I am a Republican. + -- Vice President Dan Quayle +~ +I love California, I practically grew up in Phoenix. + -- Vice President Dan Quayle +~ +A low voter turnout is an indication of fewer people going to the polls. + -- Vice President Dan Quayle +~ +When I have been asked during these last weeks who caused the riots and +the killing in L.A., my answer has been direct and simple: Who is to blame +for the riots? The rioters are to blame. Who is to blame for the killings? +The killers are to blame. + -- Vice President Dan Quayle +~ +Illegitimacy is something we should talk about in terms of not having it. + -- Vice President Dan Quayle, 5/20/92 (reported in Esquire, 8/92) +~ +Murphy Brown is doing better than I am. At least she knows she still has a job +next year. + -- Vice President Dan Quayle, 8/18/92 +~ +We are ready for any unforeseen event that may or may not occur. + -- Vice President Dan Quayle, 9/22/90 +~ +For NASA, space is still a high priority. + -- Vice President Dan Quayle, 9/5/90 +~ +Quite frankly, teachers are the only profession that teach our children. + -- Vice President Dan Quayle, 9/18/90 +~ +The American people would not want to know of any misquotes that Dan Quayle +may or may not make. + -- Vice President Dan Quayle +~ +We're all capable of mistakes, but I do not care to enlighten you on the +mistakes we may or may not have made. + -- Vice President Dan Quayle +~ +It isn't pollution that's harming the environment. It's the impurities +in our air and water that are doing it. + -- Vice President Dan Quayle +~ +[It's] time for the human race to enter the solar system. + -- Vice President Dan Quayle +~ +Public speaking is very easy. + -- Dan Quayle to reporters in 10/88 +~ +We have to believe in free will. We've got no choice. + -- Isaac B. Singer +~ +The president has kept all of the promises he intended to keep. + -- Clinton aide George Stephanopolous speaking on "Larry King Live" +~ +The police are not here to create disorder. +They're here to preserve disorder. + -- Former Chicago mayor Daley + during the infamous 1968 convention +~ +Traditionally, most of Australia's imports come from overseas. + -- Former Australian cabinet minister Keppel Enderbery +~ +It is wonderful to be here in the great state of Chicago. + -- Former U.S. Vice-President Dan Quayle +~ +The internet is a great way to get on the net. + -- Republican presidential candidate Bob Dole +~ +It is bad luck to be superstitious. + -- Andrew Mathis +~ +It's like an Alcatraz around my neck. + -- Boston mayor Menino on the shortage of city parking spaces +~ +They're multipurpose. Not only do they put the clips on, but they take +them off. + -- Pratt & Whitney spokesperson explaining why the company charged the + Air Force nearly $1,000 for an ordinary pair of pliers +~ +We're going to turn this team around 360 degrees. + -- Jason Kidd, upon his drafting to the Dallas Mavericks +~ +I'm not going to have some reporters pawing through our papers. +We are the president. + -- Hillary Clinton commenting on the release of subpoenaed documents +~ +When more and more people are thrown out of work, unemployment results. + -- Former U.S. President Calvin Coolidge +~ +China is a big country, inhabited by many chinese. + -- Former French President Charles de Gaulle +~ +That lowdown scoundrel deserves to be kicked to death by a jackass, and +i'm just the one to do it. + -- A congressional candidate in Texas +~ +Things are more like they are now than they ever were before. + -- Former U.S. President Dwight D. Eisenhower +~ +Without censorship, things can get terribly confused in the public mind. + -- General William Westmoreland, during the war in Viet Nam +~ +If you let that sort of thing go on, your bread and butter will be cut +right out from under your feet. + -- Former British foreign minister Ernest Bevin +~ +Almonds and peaches are members of the Rosaceae family (roses) and are +both in the subfamily Amygdaloideae, which also includes plums, cherries +and apricots. +~ +The symbol on the "pound" key (#) is called an octothorpe. +~ +Charlie Brown's father was a barber. +~ +Nutmeg is toxic and can cause fatal overdoses just from eating too much. +~ +Of the six men who made up the Three Stooges, three of them were real +brothers (Moe, Curly and Shemp). +~ +In Mel Brooks' "Silent Movie," mime Marcel Marceau is the only person who +has a speaking role. +~ +Pulp Fiction cost $8 million to make--$5 million going to actor's salaries. +~ +A full seven percent of the entire Irish barley crop goes to the +production of Guinness beer. +~ +Los Angeles's full name is "El Pueblo de Nuestra Señora la Reina de los +Angeles de Porciúncula" or "The Village of Our Lady, the Queen of Angels, +of Porziuncola", although its official name is simply "El Pueblo de +la Reina de Los Angeles". +~ +A cat has 32 muscles in each ear. +~ +An ostrich's eye is bigger than its brain. +~ +Tigers have striped skin, not just striped fur. +~ +Deborah Winger did the voice of E.T. +~ +In most advertisements, including newspapers, the time displayed +on an analog watch is 10:10. +~ +Donald Duck's middle name is Fauntleroy. +~ +Al Capone's business card said he was a used furniture dealer. +~ +The muzzle of a lion is like a fingerprint--no two lions have the same +pattern of whiskers. +~ +Bob Dylan's given name is Robert Allen Zimmerman. +~ +Research by the School of Psychology at the University of Plymouth in +2003 demonstrated that goldfish have a memory-span of at least three +months and can distinguish between different shapes, colours and sounds. +~ +The plastic things on the end of shoelaces are called aglets. +~ +It was discovered on a space mission that a frog can throw up. The frog +throws up it's stomach first, so the stomach is dangling out of it's +mouth. Then the frog uses it's forearms to dig out all of the stomach's +contents and then swallows the stomach back down again. +~ +Bingo is the name of the dog on the Cracker Jack box. +~ +ABBA got their name by taking the first letter from each of their first +names (Agnetha, Bjorn, Benny, Anni-frid.) +~ +The Beatles song "Dear Prudence" was written about Mia Farrow's sister, +Prudence, when she wouldn't come out and play with Mia and the Beatles +at a religious retreat in India. +~ +The giant squid has the largest eyes in the world. +~ +The name for Oz in the "Wizard of Oz" was thought up when the creator, +Frank Baum, looked at his filing cabinet and saw A-N and O-Z, hence "Oz." +~ +Horses and rabbits cannot normally vomit, but have been observed in +extreme cases appearing to vomit. For example, horses with severe colic +can produce fermented stomach contents, and rabbits have been observed +expelling stomach contents due to over-eating or health issues. +~ +Virgina Woolf liked to write standing up. +Mark Twain often wrote while lying down. +~ +Testimonial from Col. George Harvey, Mark Twain's publisher: + I think that perhaps the funniest thing about Mark Twain now is not +his writing, but his bed. He lies in bed a good deal; he says he has +formed the habit. His bed is the largest one I ever say, and on it is +the weirdest collection of objects you ever saw, enough to furnish a +Harlem flat--books, writing materials, clothes, any and everything that +could foregather in his vicinity. + He looks quite happy rising out of the mass, and over all prowls a +huge black cat of a very unhappy disposition. She snaps and snarls and +claws and bites, and Mark Twain takes his turn with the rest; when she +gets tired of tearing up manuscript she scratches him and he bears it +with a patience wonderful to behold. + -- interview subtitled "Mark Twain's Bed," Washington Post, + March 26, 1905, p. F12 +~ +Testimonial from Katy Leary, Mark Twain's servant: + Mr. Clemens borrowed a kitten one time, called Bambino, from Clara, who +had him in the sanitarium, and had trained him to wash his own face in the +bowl every morning--which shows that he was a very smart little cat. He +used to have this kitten up in his room at the Fifth Avenue house and he +taught it to put out a light, too. He had a tiny little lamp to light his +cigars with at the head of the bed, and after he got all fixed and didn't +want the light any more, he taught that cat to put his paw on the light +and put it out. Bambino would jump on the bed, look at Mr. Clemens to see +if he was through with the light, and when Mr. Clemens would bow twice to +him, he'd jump over on to that table quick, and put his little paw right +on the lamp! Mr. Clemens was always showing him off; he did that for a lot +of people that come there to call. + One night he got kind of gay, when he heard some cats calling from the +back fence, so he found a window open and he stole out. We looked high +and low but couldn't find him. Mr. Clemens felt so bad that he advertised +in all the papers for him. He offered a reward for anybody that would +bring the cat back. My goodness! the people that came bringing cats to +that house! A perfect stream! They all wanted to see Mr. Clemens, of +course. + Two or three nights after, Katherine heard a cat meowing across the +street in General Sickles' back yard, and there was Bambino--large as +life! So she brought him right home. Mr. Clemens was delighted and then +he advertised that his cat was found! But the people kept coming just +the same with all kinds of cats for him--anything to get a glimpse of +Mr. Clemens! + -- A Lifetime with Mark Twain, by Mary Lawton +~ +If your everyday life seems poor, don't blame it; blame yourself; admit to +yourself that you are not enough of a poet to call forth its riches; because +for the creator there is no poverty and no poor, indifferent place. + -- Rainer Maria Rilke +~ +Compassion is the chief and perhaps the only law of human existence. + -- Fyodor Dostoyevsky +~ +If we concede that human life can be governed by reason, the possibility of +life is destroyed. + -- Leo Tolstoy +~ +What an abyss of uncertainty, whenever the mind feels overtaken by itself; +when it, the seeker, is at the same time the dark region through which it +must go seeking, and where all its equipment will avail nothing. Seek? More +than that: create. It is face to face with something which does not yet exist, +to which it alone can give reality and substance, which it alone can bring into +the light of day. + -- Marcel Proust +~ +As soon as you trust yourself, you will know how to live. + -- Johann Wolfgang von Goethe +~ +I believe that everything depends on attention. I only see you if I pay +attention. I only exist, in my own eyes, if I pay attention to myself. + -- Nadia Boulanger +~ +The sex was so good that even the neighbors had a cigarette. +~ +If you smoke after sex, you're doing it too fast. +~ +I don't suffer from insanity, I enjoy every minute of it. +~ +If ignorance is bliss, you must be orgasmic. +~ +Good girls get fat, bad girls get eaten. +~ +The more people I meet, the more I like my dog. +~ +A bartender is just a pharmacist with a limited inventory. +~ +I need someone really bad... are you really bad? +~ +If, a two letter word for futility. +~ +Earth is the insane asylum for the universe. +~ +To all you virgins, thanks for nothing. +~ +The more you complain, the longer God lets you live. +~ +My kid had sex with your honor student. +~ +Don't hit me. My lawyer's in jail. +~ +If something goes without saying, LET IT! +~ +If at first you do succeed, try not to look astonished. +~ +IRS: We've got what it takes to take what you've got. +~ +Hard work has a future payoff, laziness pays off now. +~ +Life's a buffet... so eat me! +~ +Montana--At least our cows are sane! +~ +Jesus died for my sins and all I got was this lousy T-shirt. +~ +Mean people rule! +~ +Guns don't kill people, postal workers do. +~ +Born again pagan. +~ +God must love stupid people, he made so many. +~ +I said "no" to drugs, but they just wouldn't listen. +~ +The gene pool could use a little chlorine. +~ +There's too much youth, how about a fountain of smart. +~ +Forget about World Peace... Visualize Using Your Turn Signal! +~ +Warning: Dates in Calendar are closer than they appear. +~ +I know what you're thinking, and you should be ashamed of yourself. +~ +Don't drink and drive, you might hit a bump and spill your drink. +~ +Elvis is dead, and I'm not feeling too good myself. +~ +Lottery: A tax on people who are bad at math. +~ +Friends help you move. Real friends help you move bodies. +~ +Very funny, Scotty. Now beam down my clothes. +~ +Always be nice to your children because they are +the ones who will choose your rest home. + -- Phyllis Diller +~ +I like you, but I wouldn't want to see you working with subatomic particles. +~ +Sex on television can't hurt you unless you fall off. +~ +I'm not offended by all the dumb blond jokes because I know I'm not +dumb... and I also know that I'm not blond. + -- Dolly Parton +~ +You see a lot of smart guys with dumb women, but you hardly ever see a +smart woman with a dumb guy. + -- Erica Jong +~ +I never married because there was no need. I have three pets at home +which answer the same purpose as a husband. I have a dog which growls +every morning, a parrot which swears all afternoon and a cat that +comes home late at night. + -- Marie Corelli +~ +I am a marvelous housekeeper. Every time I leave a man I keep his house. + -- Zsa Zsa Gabor +~ +I want to have children, but my friends scare me. One of my friends told +me she was in labor for 36 hours. I don't even want to do anything that +feels GOOD for 36 hours. + -- Rita Rudner +~ +I'm not going to vacuum 'til Sears makes one you can ride on. + -- Roseanne +~ +I think--therefore I'm single. + -- Lizz Winstead +~ +Behind every successful man is a surprised woman. + -- Maryon Pearson +~ +Our struggle today is not to have a female Einstein get appointed as +assistant professor. It is for a woman schlemiel to get as quickly +promoted as a male schlemiel. + -- Bella Abzug +~ +I have yet to hear a man ask for advice on +how to combine marriage and a career. + -- Gloria Steinem +~ +Sometimes I wonder if men and women really suit each other. Perhaps they +should live next door and just visit now and then. + -- Katharine Hepburn +~ +God is my favorite fictional character. + -- Homer Simpson +~ +Computers in the future may weigh no more than 1.5 tons. + -- Popular Mechanics, forecasting the relentless march of science, 1949 +~ +I think there is a world market for maybe five computers. + -- Thomas Watson, chairman of IBM, 1943 +~ +I have traveled the length and breadth of this country and talked with the +best people, and I can assure you that data processing is a fad that won't +last out the year. + -- The editor in charge of business books for Prentice Hall, 1957 +~ +But what... is it good for? + -- Engineer at the Advanced Computing Systems Division of IBM, 1968, + commenting on the microchip +~ +This 'telephone' has too many shortcomings to be seriously considered as a +means of communication. The device is inherently of no value to us. + -- Western Union internal memo, 1876 +~ +The wireless music box has no imaginable commercial value. Who would pay for +a message sent to nobody in particular? + -- David Sarnoff's associates in response to his urgings for investment + in the radio in the 1920s +~ +The concept is interesting and well-formed, but in order to earn better than +a 'C,' the idea must be feasible. + -- A Yale University management professor in response to Fred Smith's + paper proposing reliable overnight delivery service + (Smith went on to found Federal Express Corp.) +~ +Who the hell wants to hear actors talk? + -- H. M. Warner, Warner Brothers, 1927 +~ +I'm just glad it'll be Clark Gable who's falling on his face and not Gary +Cooper. + -- Gary Cooper on his decision not to take the leading role in "Gone + With The Wind" +~ +A cookie store is a bad idea. Besides, the market research reports say +America likes crispy cookies, not soft and chewy cookies like you make. + -- Response to Debbi Fields' idea of starting Mrs. Fields' Cookies +~ +We don't like their sound, and guitar music is on the way out. + -- Decca Recording Co. rejecting the Beatles, 1962 +~ +If I had thought about it, I wouldn't have done the experiment. The +literature was full of examples that said you can't do this. + -- Spencer Silver on the work that led to the unique adhesives for 3-M + "Post-It" Notepads +~ +So we went to Atari and said, 'Hey, we've got this amazing thing, even built +with some of your parts, and what do you think about funding us? Or we'll +give it to you. We just want to do it. Pay our salary, we'll come work for +you.' And they said, 'No.' So then we went to Hewlett-Packard, and they +said, 'Hey, we don't need you. You haven't got through college yet.' + -- Apple Computer Inc. founder Steve Jobs on attempts to get Atari and H-P + interested in his and Steve Wozniak's personal computer +~ +Professor Goddard does not know the relation between action and reaction and +the need to have something better than a vacuum against which to react. He +seems to lack the basic knowledge ladled out daily in high schools. + -- 1921 New York Times editorial about Robert Goddard's revolutionary + rocket work +~ +You want to have consistent and uniform muscle development across all of your +muscles? It can't be done. It's just a fact of life. You just have to +accept inconsistent muscle development as an unalterable condition of weight +training. + -- Response to Arthur Jones, who solved the "unsolvable" problem by + inventing Nautilus +~ +Drill for oil? You mean drill into the ground to try and find oil? You're +crazy. + -- Drillers who Edwin L. Drake tried to enlist to his project to drill + for oil in 1859 +~ +Stocks have reached what looks like a permanently high plateau. + -- Irving Fisher, Professor of Economics, Yale University, 1929 +~ +Airplanes are interesting toys but of no military value. + -- Marechal Ferdinand Foch, Professor of Strategy, + Ecole Superieure de Guerre +~ +Everything that can be invented has been invented. + -- Charles H. Duell, Commissioner, U.S. Office of Patents, 1899, arguing + that the office should be closed +~ +Louis Pasteur's theory of germs is ridiculous fiction. + -- Pierre Pachet, Professor of Physiology at Toulouse, 1872 +~ +The abdomen, the chest, and the brain will forever be shut from the intrusion +of the wise and humane surgeon. + -- Sir John Eric Ericksen, British surgeon, appointed Surgeon-Extraordinary + to Queen Victoria 1873 +~ +640K ought to be enough for anybody. + -- Attributed to Bill Gates, 1981, but he asserts that he did not say this. +~ +Three things are certain: +Death, taxes, and lost data. +Guess which has occurred. +~ +Everything is gone; +Your life's work has been destroyed. +Squeeze trigger (yes/no)? +~ +Windows NT crashed. +I am the Blue Screen of Death. +No one hears your screams. +~ +Seeing my great fault +Through darkening blue windows +I begin again +~ +The code was willing, +It considered your request, +But the chips were weak. +~ +Printer not ready. +Could be a fatal error. +Have a pen handy? +~ +A file that big? +It might be very useful. +But now it is gone. +~ +Errors have occurred. +We won't tell you where or why. +Lazy programmers. +~ +Server's poor response +Not quick enough for browser. +Timed out, plum blossom. +~ +Chaos reigns within. +Reflect, repent, and reboot. +Order shall return. +~ +Login incorrect. +Only perfect spellers may +enter this system. +~ +This site has been moved. +We'd tell you where, but then we'd +have to delete you. +~ +Wind catches lily +scatt'ring petals to the wind: +segmentation fault +~ +ABORTED effort: +Close all that you have. +You ask way too much. +~ +First snow, then silence. +This thousand dollar screen dies +so beautifully. +~ +With searching comes loss +and the presence of absence: +"My Novel" not found. +~ +The Tao that is seen +Is not the true Tao, until +You bring fresh toner. +~ +The Web site you seek +cannot be located but +endless others exist +~ +Stay the patient course +Of little worth is your ire +The network is down +~ +A crash reduces +your expensive computer +to a simple stone. +~ +There is a chasm +of carbon and silicon +the software can't bridge +~ +Yesterday it worked +Today it is not working +Windows is like that +~ +To have no errors +Would be life without meaning +No struggle, no joy +~ +You step in the stream, +but the water has moved on. +This page is not here. +~ +No keyboard present +Hit F1 to continue +Zen engineering? +~ +Hal, open the file +Hal, open the damn file, Hal +open the, please Hal +~ +Out of memory. +We wish to hold the whole sky, +But we never will. +~ +Having been erased, +The document you're seeking +Must now be retyped. +~ +The ten thousand things +How long do any persist? +Netscape, too, has gone. +~ +Rather than a beep +Or a rude error message, +These words: "File not found." +~ +Serious error. +All shortcuts have disappeared. +Screen. Mind. Both are blank. +~ +Indecision may or may not be my problem. + -- Jimmy Buffet +~ +If you pick up a starving dog and make him prosperous, he will not bite you. +This is the principal difference between a dog and a man. + -- Mark Twain +~ +If you want to know what God thinks about money, just look at the people He +gives it to. + -- Old Irish Saying +~ +I think men who have a pierced ear are better prepared for marriage. +They've experienced pain and bought jewelry. + -- Rita Rudner +~ +I would love to speak a foreign language but I can't. +So I grew hair under my arms instead. + -- Sue Kolinsky +~ +The second day of a diet is always easier than the first. +By the second day you're off it. + -- Jackie Gleason +~ +Bigamy is having one wife too many. Monogamy is the same. + -- Oscar Wilde +~ +If a woman has to choose between catching a fly ball and saving an infant's +life, she will choose to save the infant's life without even considering if +there is a man on base. + -- Dave Barry +~ +Somebody hits me, I'm going to hit him back. Even if it does look like he +hasn't eaten in a while. + -- Charles Barkley, after blatantly elbowing an Angolan basketball + opponent in the Olympics. +~ +I think that's how Chicago got started. A bunch of people in New York said, +'Gee, I'm enjoying the crime and the poverty, but it just isn't cold enough. +Let's go west.' + -- Richard Jeni +~ +The show business newspaper Daily Variety reported in December that John +Kricfalusi, creator of TV's "The Ren & Stimpy Show," was threatening legal +action against the producers of the Comedy Central show "South Park" for +ripping off a cartoon character. According to Kricfalusi, his character +"Nutty the Friendly Dump," an animated piece of excrement, must have been +the basis for "South Park"'s "Mr. Hankey the Christmas Poo," a holiday- +dressed, singing, dancing piece of excrement. +~ +If a man is standing in the middle of the forest speaking and there is +no woman around to hear him, is he still wrong? +~ +If a deaf person swears, does his mother wash his hands with soap? +~ +If someone with multiple personalities threatens to kill himself, is +it considered a hostage situation? +~ +Is there another word for synonym? +~ +Isn't it a bit unnerving that doctors call what they do "practice?" +~ +When sign makers go on strike, is anything written on their signs? +~ +Where do forest rangers go to "get away from it all?" +~ +Why isn't there mouse-flavored cat food? +~ +What do you do when you see an endangered animal eating an endangered plant? +~ +If a parsley farmer is sued, can they garnish his wages? +~ +Would a fly without wings be called a walk? +~ +Why do they lock gas station bathrooms? Are they afraid someone +will clean them? +~ +If a stealth bomber crashes in a forest, will it make a sound? +~ +If a turtle doesn't have a shell, is he homeless or naked? +~ +Why don't sheep shrink when it rains? +~ +Can vegetarians eat animal crackers? +~ +If the police arrest a mime, do they tell him he has the right to +remain silent? +~ +Why do they put Braille on the drive-through bank machines? +~ +How do they get the deer to cross at that yellow road sign? +~ +Why do they sterilize the needles for lethal injections? +~ +Why did kamikaze pilots wear helmets? +~ +Is it true that cannibals don't eat clowns because they taste funny? +~ +Whoever said you can't buy happiness forgot about puppies. + -- Gene Hill +~ +In dog years, I'm dead. + -- Unknown +~ +Dogs feel very strongly that they should always go with you in the car, in +case the need should arise for them to bark violently at nothing right in +your ear. + -- Dave Barry +~ +(of dogs) +I wonder what goes through his mind when he sees us peeing in his water bowl. + -- Penny Ward Moser +~ +Outside of a dog, a book is probably man's best friend, +and inside of a dog, it's too dark to read. + -- Groucho Marx +~ +To his dog, every man is Napoleon; hence the constant popularity of dogs. + -- Aldous Huxley +~ +A dog teaches a boy fidelity, perseverance, and to turn around three times +before lying down. + -- Robert Benchley +~ +Did you ever walk into a room and forget why you walked in? I think that is +how dogs spend their lives. + -- Sue Murphy +~ +Did you hear about the dyslexic agnostic insomniac who stays up all night +wondering if there really is a Dog? + -- Unknown +~ +I think animal testing is a terrible idea; they get all nervous and give +the wrong answers. + -- Unknown +~ +I loathe people who keep dogs. They are cowards who haven't got the guts +to bite people themselves. + -- August Strindberg +~ +Ever consider what they [our pets] must think of us? I mean, here we come +back from a grocery store with the most amazing haul--chicken, pork, half +a cow. They must think we're the greatest hunters on earth! + -- Anne Tyler +~ +My dog is worried about the economy because Alpo is up to 99 cents a can. +That's almost $7.00 in dog money. + -- Joe Weinstein +~ +If I have any beliefs about immortality, it is that certain dogs I have known +will go to heaven, and very, very few persons. + -- James Thurber +~ +You enter into a certain amount of madness when you marry a person with pets. + -- Nora Ephron +~ +Don't accept your dog's admiration as conclusive evidence that you are +wonderful. + -- Ann Landers +~ +Women and cats will do as they please and men and dogs should relax and get +used to the idea. + -- Robert A. Heinlein +~ +In order to keep a true perspective of one's importance, everyone should have +a dog that will worship him and a cat that will ignore him. + -- Dereke Bruce, Taipei, Taiwan +~ +When a man's best friend is his dog, that dog has a problem. + -- Edward Abbey +~ +Cat's Motto: No matter what you've done wrong, always try to +make it look like the dog did it. + -- Unknown +~ +Money will buy you a pretty good dog, but it won't buy +the wag of his tail... No one appreciates the very +special genius of your conversation as the dog does. + -- Christopher Morley +~ +A dog is the only thing on earth that loves you more than he loves himself. + -- Josh Billings +~ +Man is a dog's idea of what God should be. + -- Holbrook Jackson +~ +The average dog is a nicer person than the average person. + -- Andrew A. Rooney +~ +He is your friend, your partner, your defender, your dog. You are his life, +his love, his leader. He will be yours, faithful and true, to the last beat +of his heart. You owe it to him to be worthy of such devotion. + -- Unknown +~ +Heaven goes by favour. If it went by merit, you would stay out and your dog +would go in. + -- Mark Twain +~ +I care not for a man's religion whose dog and cat are not the better for it. + -- Abraham Lincoln +~ +If there are no dogs in Heaven, then when I die I want to go where they went. + -- Unknown +~ +Things that upset a terrier may pass virtually unnoticed by a Great Dane. + -- Smiley Blanton +~ +I've seen a look in dogs' eyes, a quickly vanishing look of amazed contempt, +and I am convinced that basically dogs think humans are nuts. + -- John Steinbeck +~ +There is no psychiatrist in the world like a puppy licking your face. + -- Ben Williams +~ +One machine can do the work of fifty ordinary men. +No machine can do the work of one extraordinary man. + -- Elbert Hubbard +~ +(AP) Tokyo: Tokyo commuter Katsuo Katugoru caused havoc on a crowded tube +train when his inflatable underpants unexpectedly went off. The rubber +underwear was made by Katsuo himself, and designed to inflate to 30 times +their original size in the event of a tidal wave. +"I am terrified of water, and death by drowning is my greatest fear" said +Katsuo, 48. "Unfortunately I set them off accidently while looking for a +boiled sweet on a rush hour train. They were crushing everybody in the +carriage until a passenger stabbed them with a pencil." +~ +Kindness cannot be taught by harshness -- +not by any amount of harshness. + -- Raymond M. Smullyan / The Tao is Silent +~ +Reno versus Gates: +Full employment for lawyers +Until end of time + -- fred t. hamster +~ +Inflate your tires by all means, but then hide your bicycle pump where it +cannot tempt you. + -- attributed to a spokesman for the Nakhon Ratchasima hospital +~ +Duct tape is like the Force. It has a light side, a dark side, and it +holds the universe together.... + -- Carl Zwanzig +~ +There is a theory which states that if ever anybody discovers exactly what the +Universe is for and why it is here, it will instantly disappear and be replaced +by something even more bizarre and inexplicable. There is another theory which +states that this has already happened. + -- Douglas Adams +~ +Only two things are infinite, the universe and +human stupidity, and I'm not sure about the former. + -- Albert Einstein +~ +Astronomers say the universe is finite, which is a comforting thought for +those people who can't remember where they leave things. + -- Unknown +~ +In answer to the question of why it happened, I offer the modest proposal that +our Universe is simply one of those things which happen from time to time. + -- Edward P. Tryon +~ +It is well to remember that the entire universe, with one trifling exception, +is composed of others. + -- John Andrew Holmes +~ +Technology is a way of organizing the universe so +that man doesn't have to experience it. + -- Max Frisch +~ +The universe is a big place, perhaps the biggest. + -- Kilgore Trout (Kurt Vonnegut, Jr.) +~ +I'm astounded by people who want to 'know' the universe when it's hard enough +to find your way around Chinatown. + -- Woody Allen +~ +In the beginning the Universe was created. This has made a lot of people +very angry and been widely regarded as a bad move. + -- Douglas Adams +~ +The crux... is that the vast majority of the +mass of the universe seems to be missing. + -- William J. Broad +~ +Programming today is a race between software engineers striving to build +bigger and better idiot-proof programs, and the Universe trying to produce +bigger and better idiots. So far, the Universe is winning. + -- Rich Cook +~ +There is a coherent plan in the universe, +though I don't know what it's a plan for. + -- Fred Hoyle +~ +We are an impossibility in an impossible universe. -- Ray Bradbury +~ +My theology, briefly, is that the universe was dictated but not signed. + -- Christopher Morley +~ +I'm worried that the universe will soon need replacing. +It's not holding a charge. + -- Edward Chilton +~ +The surest sign that intelligent life exists elsewhere in the universe +is that it has never tried to contact us. + -- Calvin and Hobbes (Bill Watterson) +~ +As of tomorrow, employees will only be able to access the building using +individual security cards. Pictures will be taken next Wednesday and +employees will receive their cards in two weeks. + -- pointy haired boss contest. (This was the winning quote from + Fred Dales at Microsoft Corporation in Redmond, Washington) +~ +What I need is a list of specific unknown problems we will encounter. + -- pointy haired boss contest. (Lykes Lines Shipping) +~ +E-mail is not to be used to pass on information or data. It should be used +only for company business. + -- pointy haired boss contest. (Accounting manager, Electric Boat Company) +~ +This project is so important, we can't let things that are more important +interfere with it. + -- pointy haired boss contest + (Advertising/Marketing manager, United Parcel Service) +~ +Doing it right is no excuse for not meeting the schedule. No one will believe +you solved this problem in one day! We've been working on it for months. Now, +go act busy for a few weeks and I'll let you know when it's time to tell them. + -- pointy haired boss contest + (R&D supervisor, Minnesota Mining and Manufacturing / 3M Corp.) +~ +My Boss spent the entire weekend retyping a 25-page proposal that only needed +corrections. She claims the disk I gave her was damaged and she couldn't edit +it. The disk I gave her was write-protected. + -- pointy haired boss contest. (CIO of Dell Computers) +~ +Quote from the Boss: "Teamwork is a lot of people doing what 'I' say." + -- pointy haired boss contest. (Marketing executive, Citrix Corporation) +~ +"How About Friday?" My sister passed away and her funeral was scheduled for +Monday. When I told my Boss, he said she died so that I would have to miss +work on the busiest day of the year. He then asked if we could change her +burial to Friday. He said, "That would be better for me." + -- pointy haired boss contest. (Shipping executive, FTD Florists) +~ +"We know that communication is a problem, but the company is +not going to discuss it with the employees." + -- pointy haired boss contest + (Switching supervisor, AT&T Long Lines Division) +~ +We recently received a memo from senior management saying: "This is to inform +you that a memo will be issued today regarding the subject mentioned above." + -- pointy haired boss contest. (Microsoft, Legal Affairs Division) +~ +One day my Boss asked me to submit a status report to him concerning a project +I was working on. I asked him if tomorrow would be soon enough. He said "If +I wanted it tomorrow, I would have waited until tomorrow to ask for it!" + -- pointy haired boss contest + (New business manager, Hallmark Greeting Cards) +~ +Speaking the Same Language: As director of communications I was asked to +prepare a memo reviewing our company's training programs and materials. In +the body of the memo one of the sentences mentioned the "pedagogical approach" +used by one of the training manuals. The day after I routed the memo to the +executive committee, I was called into the HR director's office and told that +the executive vice president wanted me out of the building by lunch. When I +asked why, I was told that she wouldn't stand for "perverts" (pedophilia)? +working in her company. Finally he showed me her copy of the memo, with her +demand that I be fired--and the word "pedagogical" circled in red. The HR +manager was fairly reasonable, and once he looked the word up in his +dictionary, and made a copy of the definition to send back to her, he told me +not to worry. He would take care of it. Two days later a memo to the entire +staff came out--directing us that no words which could not be found in the +local Sunday newspaper could be used in company memos. A month later, I +resigned. In accordance with company policy, I created my resignation memo +by pasting words together from the Sunday paper. + -- pointy haired boss contest. (Taco Bell Corporation) +~ +This gem is the closing paragraph of a nationally-circulated memo from a large +communications company: "(Company name) is endeavorily determined to promote +constant attention on current procedures of transacting business focusing +emphasis on innovative ways to better, if not supersede, the expectations of +quality!" + -- pointy haired boss contest. (Lucent Technologies) +~ +Marriage isn't a word, it's a sentence. + -- unknown, seen on back of Charlottesville, VA cab +~ +Have you ever noticed... Anybody going slower than you is an idiot, +and anyone going faster than you is a f*cking maniac? + -- George Carlin +~ +You have to stay in shape. My grandmother, she started walking five +miles a day when she was 60. She's 97 today and we don't know where +the hell she is. + -- Ellen DeGeneres +~ +I'm not into working out. My philosophy: No pain, no pain. + -- Carol Leifer +~ +I have a great diet. You're allowed to eat anything you want, but you +must eat it with naked fat people. + -- Ed Bluestone +~ +I went into a McDonald's yesterday and said, "I'd like some fries." +The girl at the counter said, "Would you like some fries with that?" + -- Jay Leno +~ +The reason most people play golf is to wear clothes they would not be +caught dead in otherwise. + -- Roger Simon +~ +I'm desperately trying to figure out why kamikaze pilots wore helmets. + -- Dave Edison +~ +If it weren't for electricity we'd all be watching television by candlelight. + -- George Gobel +~ +Don't spend two dollars to dry clean a shirt. Donate it to the +Salvation Army instead. They'll clean it and put it on a hanger. +Next morning buy it back for seventy-five cents. + -- Billiam Coronel +~ +Suppose you were an idiot... And suppose you were a member of +Congress... But I repeat myself. + -- Mark Twain +~ +Our bombs are smarter than the average high school student. At least +they can find Kuwait. + -- A. Whitney Brown +~ +My mom said she learned how to swim. Someone took her out in the lake +and threw her off the boat. That's how she learned how to swim. I +said, "Mom, they weren't trying to teach you how to swim." + -- Paula Poundstone +~ +I worry that the person who thought up Muzak may be thinking up +something else. + -- Lily Tomlin +~ +What do people mean when they say the computer went down on me? + -- Marilyn Pittman +~ +Why is it that when we talk to God we're said to be praying, but when +God talks to us we're schizophrenic? + -- Lily Tomlin +~ +When you look at Prince Charles, don't you think that someone in the +Royal family knew someone in the Royal family? + -- Robin Williams +~ +Where lipstick is concerned, the important thing is not color, but to +accept God's final word on where your lips end. + -- Jerry Seinfeld +~ +[Modern economists are] "used to measuring the 'standard of living' by +the amount of annual consumption, assuming all the time that a man who +consumes more is 'better off' than a man who consumes less. A Buddhist +economist would consider this approach excessively irrational: since +consumption is merely a means to human well-being, the aim should be to +obtain the maximum of well-being with the minimum of consumption... +The less toil there is, the more time and strength is left for artistic +creativity. Modern economics, on the other hand, considers consumption +to be the sole end and purpose of all economic activity." + -- E. F. Schumacher (1911-1977), from "Small Is Beautiful: + A Study of Economics As If People Mattered", 1973. +~ +As a net is made up of a series of ties, so everything in this world +is connected by a series of ties. If anyone thinks that the mesh of +a net is an independent, isolated thing, he is mistaken. It is called +a net because it is made up of a series of interconnected meshes, and +each mesh has its place and responsibility in relation to other meshes. + -- Shakyamuni Buddha +~ +When debugging, suspect the more complicated code, +but keep an eye on the simple code lest it get too cocky. + -- fred t. hamster +~ +I love deadlines. I like the whooshing sound they make as they fly by. + -- Douglas Adams +~ +As I let go of my feelings of guilt, I am in touch +with my inner sociopath. +~ +I have the power to channel my imagination into ever-soaring +levels of suspicion and paranoia. +~ +I assume full responsibility for my actions, except the ones +that are someone else's fault. +~ +I no longer need to punish, deceive, or compromise myself, +unless I want to stay employed. +~ +In some cultures what I do would be considered normal. +~ +I honor my personality flaws for without them +I would have no personality at all. +~ +Joan of Arc heard voices, too. +~ +I am grateful that I am not as judgmental as all those +censorious, self-righteous people around me. +~ +I need not suffer in silence while I can still +moan, whimper, and complain. +~ +As I learn the innermost secrets of people around me, they +reward me in many ways to keep me quiet. +~ +When someone hurts me, I know that forgiveness is cheaper +than a lawsuit, but not nearly as gratifying. +~ +The first step is to say nice things about myself. +The second is to do nice things for myself, the third, +to find someone to buy me nice things. +~ +As I learn to trust the universe, +I no longer need to carry a gun. +~ +I am at one with my duality. +~ +Blessed are the flexible, +for they can tie themselves into knots. +~ +Only a lack of imagination saves me from +immobilizing myself with imaginary fears. +~ +I will strive to live each day as +if it were my 50th birthday. +~ +I honor and express all facets of my being, +regardless of state and local laws. +~ +Today I will gladly share my experience and advice, for there +are no sweeter words than "I told you so." +~ +False hope is better than no hope at all. +~ +A good scapegoat is almost as good as a solution. +~ +Just for today, I will not sit in my living room all day in my +underwear in the Hollywood Cafe. Instead, I will move my +computer into the bedroom. +~ +Who can I blame for my problems? +Just give me a minute. +I'll find someone. +~ +Why should I waste my time reliving the past when +I can spend it worrying about the future? +~ +The complete lack of evidence is the surest +sign that the conspiracy is working. +~ +I am learning that criticism is not nearly as +effective as sabotage. +~ +Becoming aware of my character defects leads me +naturally to the next step of blaming my parents. +~ +To have a successful relationship I must learn to +make it look like I'm giving as much as I'm getting. +~ +I am willing to make the mistakes if someone else +is willing to learn from them. +~ +Before I criticize a man, I walk a mile in +his shoes. That way, if he gets angry, he's +a mile away and barefoot. +~ +Madness takes its toll. Please have exact change. +~ +Canadian = unarmed American with health care. +~ +Mohandas K. Gandhi's list of "Seven Blunders Of The World That Lead +To Violence": + Wealth without work + Science without humanity + Pleasure without conscience + Worship without sacrifice + Knowledge without character + Politics without principle + Commerce without morality +~ +`Tis the longest Purse Conquers the longest Sword. + -- Daniel Defoe +~ +A Man that will lie still, should never hope to rise; +he that will lie in a Ditch and pray, may depend upon it he +shall lie in the Ditch and die. + -- Daniel Defoe +~ +He that has Truth on his Side, is a fool, as well +as a Coward, if he is afraid to own it because of +the Currency or Multitude of other Men's Opinions. + -- Daniel Defoe +~ +Writers' earnings are the reward of industry and the prize of learning. + -- Daniel Defoe +~ +Absolute necessity forces many a poor distressed +person to do things which his very soul abhors. + -- Daniel Defoe +~ +The rising greatness of the British nation +is not owing to war and conquests, +to enlarging its dominion by the sword, +or subjecting the people of other countries to our power; +but it is owing to trade. +An estate is a pond, trade is a spring, +conquest is a Thing attended with Difficulty, Hazard, +Expense, and a Possibility of Miscarriage. + -- Daniel Defoe +~ +24 hours in a day. 24 beers in a case. coincidence? + -- Stephen Wright +~ +When Human Folk at Table Eat, +A Kitten must not mew for meat, +Or Jump to grab it from the Dish +(Unless it happens to be fish). + -- Oliver Herford +~ +When I am at peace with myself, and in good spirits--for +instance, on a journey, in a carriage, or after a good meal, +or while taking a walk, or at night when I can't sleep--then +thoughts flow into me most easily and at their best. Where +they come from and how -- that I cannot say; nor can I do +anything about it. + -- Wolfgang Amadeus Mozart +~ +It is a mistake to think that the practice of my art has +become easy to me. I assure you no one has given so much +care to the study of composition as I. There is scarcely +a famous master in music whose works I have not frequently +and diligently studied. + -- Wolfgang Amadeus Mozart +~ +If one has talent it pushes for utterance and torments +one; it will out. And then one is out without questioning. +And, look you, there is nothing in this thing of learning +out of books. Here, here and here [the ear, the head, the +heart] is your school. If everything is right there, then +take your pen and down with it; afterwards ask the opinion +of a man who knows his business. + -- Wolfgang Amadeus Mozart +~ +It is no just function of government to prohibit what is not wrong. + -- Abraham Lincoln +~ +If you love wealth more than liberty, the tranquility of servitude better +than the animating contest of freedom, depart from us in peace. We ask not +your counsel or your arms. Crouch down and lick the hand that feeds you. +May your chains rest lightly upon you and may posterity forget that you +were our countrymen. + -- Samuel Adams +~ +You may call, you may call, +But the little black cats won't hear you, +The little black cats are maddened, +By the green light of the moon. + -- Elizabeth Coatsworth +~ +on exception handling... + the point is to create dependable systems. some languages are better than +others for this. some languages provide no support for verification, although +the semantics of most programming constructs are well established. exceptions +predate much of the work done in formal verification of the last 30 years. +many people have tried (with varying degrees of success) to fit exceptions into +the grand scheme and make them verifiable. flaviu cristian has shown how +exception handlers can be fitted with preconditions for their activation and +postconditions for their completion. + however, an attempt to postfit a system with hooks for verifiability will +fail if the system is very large. and programming in this manner is simply a +living hell. the programmer is not generally equipped to set up all of his +handlers for this type of reasoning, even if he has a clear idea for when the +handlers are to be invoked. + what is needed is a general language structure that is capable of embodying +the reasons and the services of the exception-like items in the system. + -- fred t. hamster +~ +why aren't programs published in better boxes? +thicker cardboard, smaller profiles, thin like coffee table books, +packaged with only essentials and fitting together well... +you'd be proud to have a software library if the damn boxes weren't +so flimsy and didn't take up so much room. +~ +Here's a thought... The government should only be allowed to test urine for +drugs if they collect the pee in their own mouths. +~ +the drug war is a war on liberty that cannot possibly be won without the +utter removal of all rights or at least the indefinite suspension thereof. +urine testing is the best emblem of this horrible war; it makes the police +look like a bunch of perverted piss sniffers. it is not cost effective +for testing large numbers of people and it is insanely easy to fake if one +wants to anyway. we need to stop the drug war and start the war on ignorance. +this ignorance is an ignorance of the REAL scientifically determined results +of using the drugs in question. studies show that marijuana, for example, +DOES have therapeutic results and that it is almost completely HARMLESS. +smoke inhalation is not harmless, but there are other ways to get stoned than +by inhaling burning vapors. the root ignorance that MUST be warred upon is +an ignorance of the fact that people should leave each other alone and not +be such shrieking paranoid shrews about what their neighbors are doing. +put down your goddamned binoculars, you nosy cretin, and get to the business +of living your own life. +~ +drug prices are caused to soar by the organized crime elements +that are behind the drug war (and behind the republican party). +organized crime at the same time causes the war to look just as +ridiculous as the carrie nation hatchet smashing of beer kegs so +that people will buy more drugs and think that governmental +authority is a crock. who benefitted from the intense CAMP +activity in california? the mafia, because prices on marijuana +soared. who got screwed by CAMP? the people, who made their +honest living off of growing marijuana, which at the time was +our nation's largest commodity. it's time to toss the republican +crooks out of office on their asses. american patriots smoke pot: +george washington, thomas jefferson, benjamin franklin, and thou, +if you like. unamerican creeps are against marijuana and against +the right of the america people to control their own destiny. +i think it really is that simple. +~ +ode to a petty tyrant... + what you don't seem to realize is that productivity is mood dependent. +you seem to think that i need to be degraded repeatedly as some form of +character builder, but what if someone came to this school as your student +who had already been degraded enough in his life? what if every time you +were cutting on them in some sly tricky sarcastic form you were in fact +rubbing a sore spot and only inflaming their opinions as to your anus-nature? + did you realize that one of your students comes back to the office +and just swears and swears at the computer he's working at? this wasn't +because the machine was bad, or his program was bad, or he was bad, +but because he was mad at _you_. + the solution, i think, is to respect people a whole lot more. we're +not here for professors to deride us. this isn't some f*cking fraternity +with you chumps as the chief hazing marshalls. we're here to learn and if +our learning is colored with hues of condemnation and abuse, it diminishes +you, it diminishes us, and it diminishes the whole search for truth and +knowledge. + -- fred t. hamster +~ +i don't believe the druidic and grecian universities had this +same kind of "cut him up, slice and dice!" attitude that prevails +at our fine american universities in this day and age. that +attitude is surely a holdover from roman times, when the +patriarchal society demanded discipline and obedience from all +of its members, especially those with license to think. this +approach is impractical these days (because it doesn't work +very well), except at the higher levels in education, where +it is still fervently practiced. root it out, toss it away; +we don't need authoritarian learning, we need to follow the truth +back to all its origins and forward to the myriad potential fruits. +your rule book stopped being effective a long time ago. + -- fred t. hamster +~ +on artistic and technical integrity... +in the beginning, i had a totally egotistical attitude that the rest of the +world was screwed and i was right. this is actually somewhat correct, because +one must have his own voice and thoughts, except for the fact that i was not +_that_ correct and the world was not _that_ wrong. but at least what i chose +to say and what i chose to be were right for me. over the past few years i +have sought to learn the phd game, but i have been doing it by suppressing +my own voice and allowing others to tell me what i should say. this is +fundamentally f*cked, because now i have become dependent on them as my voice +instead of relying on the tao to guide the research and for my own voice to +speak what needs to be said. is it any wonder that i have devolved to this +state of being unable to say anything on my own? after years of thinking for +myself, i have allowed myself to become placed in a situation where i was +dependent on someone else as my source of "the scoop". numerous problems +result when others' biases are not what i want to express, yet they have +potency to affect my presentation. the fact that when the bogus guru doesn't +understand something, he usually tells you that you are the one who does not +understand, is no help. he has no subtlety and yet seeks the original +thoughts, leading him to warp other people's original thoughts into his own +mental framework, often losing the original spark and leading the ideas astray +into his own personal interests. i need to cut loose and start thinking on my +own again. if the ideas in my research here have any merit at all, they have +to be proven using my own metrics, not others' metrics. and this no longer +seems possible in this stringently bulletheaded "hard science" department +which instead only enforces conventional viewpoints and does not reach out +into the unknown where the really interesting concepts live.... + -- fred t. hamster +~ +niceness here is the ineffable. +the only difference between you and the wolf is that you want to be nice, +and fail. the wolf only wants to appear nice while remaining vile inside, +and he succeeds. your demeanor to others is sometimes nice and sometimes +not nice because of your failures in actually being the way you want to +be. and the reason that you fail to be the way you want to be is that you +lack the spiritual strength to keep it up. you can't synchronize your +noble desires with your weak mind/body, and it is mainly because you reject +spirituality itself that you fail in achieving spiritual strength. +one cannot succeed in something one does not believe in except through +dumb beginner's luck. and yet to fully believe in something is to be +trapped in it, without the capacity to disbelieve and free oneself. +you need a fluid belief that accepts what is true within the constraints +of its validity. can this flow be strong enough to become who and what +you want to be? + -- fred t. hamster +~ +eek on the candidate from louis cypher... i notice with some lack of +surprise that "ollie north" can be rearranged into the nice phrase +"o, rot in hell". i think this needs to be made more public. perhaps +we should publish an epigram regarding this particular correspondence +between reality and republicanism. +~ + 1. Commit to your business. + 2. Share your profits with all your associates. + 3. Motivate your partners. + 4. Communicate everything you possibly can to your partners. + 5. Appreciate everything your associates do for the business. + 6. Celebrate your success. + 7. Listen to everyone in your company. + 8. Exceed your customers' expectations. + 9. Control your expenses better than your competition. +10. Swim upstream. + -- "Sam's Rules For Building A Business", from "Made In + America: My Story", by Sam Walton. +~ +Find some humor in your failures. Don't take yourself so seriously. +Loosen up, and everybody around you will loosen up. Have fun. Show +enthusiasm--always. When all else fails, put on a costume and sing a +silly song. Then make everybody else sing with you. Don't do a hula +on Wall Street. It's been done. Think up your own stunt. All of +this is more important, and more fun, than you think, and it really +fools the competition. "Why should we take those cornballs at Wal-Mart +seriously?" + -- Sam Walton +~ +If life was fair, Elvis would be alive and all the +impersonators would be dead. + -- Johnny Carson +~ +In elementary school, in case of fire you have to line +up quietly in a single file line from smallest to +tallest. What is the logic? Do tall people burn slower? + -- Warren Hutcherson +~ +Every time a baseball player grabs his crotch, it makes +him spit. That's why you should never date a baseball player. + -- Marsha Warfield +~ +Some women hold up dresses that are so ugly and they +always say the same thing: 'This looks much better on.' +On what? On fire? + -- Marsha Warfield +~ +The cat went here + and there, +The moon spun round + like a top, +And the nearest kin + of the moon, +The creeping cat, looked + up. + -- W. B. Yeats +~ +The cat always leaves a mark on his friend. + -- Spanish proverb +~ + A herd of buffalo can move only as fast as the slowest buffalo. +when the herd is hunted, it is the slowest and weakest ones at the back +that are killed first. This natural selection is good for the herd as a +whole, because the general speed and health of the whole group keeps +improving by the regular culling of the weakest members. + In much the same way, the human brain can only operate as fast as +the slowest brain cells. Intake of alcohol, we all know, kills brain +cells, but naturally it attacks the slowest and weakest brain cells +first. In this way, regular consumption of beer eliminates the weaker +brain cells, making the brain a faster and more efficient machine. + That's why you always feel smarter after a few beers. +~ +1. Never use a metaphor, simile, or other figure of speech + which you are used to seeing in print. +2. Never use a long word where a short one will do. +3. If it is possible to cut a word out, always cut it out. +4. Never use the passive where you can use the active. +5. Never use a foreign phrase, a scientific word or a jargon + word if you can think of an everyday English equivalent. +6. Break any of these rules sooner than say anything + outright barbarous. + -- George Orwell, "Politics And The English Language." +~ +Who is General Failure and why is he reading my hard disk? + -- Felix von Leitner (leitner@inf.fu-berlin.de) +~ +linux: the choice of a GNU generation. + -- ksh@cis.ufl.edu put this on t-shirts in 1993 +~ +There are two types of Linux developers--those who can +spell, and those who can't. There is a constant pitched +battle between the two. +~ +> Other than the fact Linux has a cool name, could someone explain +> why I should use Linux over BSD? + +No. That's it. The cool name, that is. We worked very hard on +creating a name that would appeal to the majority of people, and +it certainly paid off: thousands of people are using linux just +to be able to say "OS/2? Hah. I've got Linux. What a cool name". +386BSD made the mistake of putting a lot of numbers and weird +abbreviations into the name, and is scaring away a lot of people +just because it sounds too technical. + -- Linus Torvalds' follow-up to a question about Linux +~ +When you say "I wrote a program that crashed Windows", people +just stare at you blankly and say, "Hey, I got those with the +system, *for free*". + -- Linus Torvalds +~ +We come to bury DOS, not to praise it. + -- Paul Vojta, regarding Linux +~ +How should I know if it works? That's what beta testers +are for. I only coded it. + -- Attributed to Linus Torvalds +~ +I develop for Linux for a living, I used to develop for DOS. +Going from DOS to Linux is like trading a glider for an F117. + -- Lawrence Foard +~ +I'd crawl over an acre of 'Visual This++' and 'Integrated +Development That' to get to gcc, Emacs, and gdb. Thank you. + -- Vance Petree, Virginia Power +~ +If you want to travel around the world and be invited to +speak at a lot of different places, just write a Unix +operating system. + -- Linus Torvalds +~ +All language designers are arrogant. Goes with the territory... + -- Larry Wall +~ +Unix, MS-DOS, and Windows NT (also known as +the Good, the Bad, and the Ugly). + -- Matt Welsh +~ +I would rather spend 10 hours reading someone else's source +code than 10 minutes listening to Muzak waiting for technical +support which isn't. + -- Dr. Greg Wettstein, Roger Maris Cancer Center +~ +Your job is being a professor and researcher: That's one +hell of a good excuse for some of the brain-damages of minix. + -- Linus Torvalds to Andrew Tanenbaum +~ +We use Linux for all our mission-critical applications. +Having the source code means that we are not held hostage +by anyone's support department. + -- Russell Nelson, President of Crynwr Software +~ +We are Pentium of Borg. Division is futile. You will be approximated. +~ +The chat program is in public domain. This is not the GNU +public license. If it breaks then you get to keep both pieces. + -- Copyright notice for the chat program +~ +DOS: n., A small annoying boot virus that causes random + spontaneous system crashes, usually just before saving + a massive project. Easily cured by UNIX. See also + MS-DOS, IBM-DOS, DR-DOS. + -- David Vicker +~ +MSDOS didn't get as bad as it is overnight--it took over +ten years of careful development. + -- dmeggins@aix1.uottawa.ca +~ +On the Internet, no one knows you're using Windows NT. + -- Ramiro Estrugo +~ +People disagree with me. I just ignore them. + -- Linus Torvalds, regarding the use of C++ for the Linux kernel +~ +Linux: The OS people choose without $200,000,000 of persuasion. + -- Mike Coleman +~ +The memory management on the PowerPC can be used to frighten small children. + -- Linus Torvalds +~ +Eh, that's it, I guess. No 300 million dollar unveiling event for this +kernel, I'm afraid, but you're still supposed to think of this as the +"happening of the century" (at least until the next kernel comes along). +Oh, and this is another kernel in that great and venerable "BugFree(tm)" +series of kernels. So be not afraid of bugs, but go out in the streets +and deliver this message of joy to the masses. + -- Linus Torvalds, in the announcement for Linux kernel version 1.3.27 +~ +> Linux is not user-friendly. + +It _is_ user-friendly. It is not ignorant-friendly +and idiot-friendly. +~ +I tried to get some documentation out of Digital on this, but +as far as I can tell even _they_ don't have it ;-) + -- Linus Torvalds +~ +Excusing bad programming is a shooting offence, +no matter _what_ the circumstances. + -- Linus Torvalds +~ +Some people have told me they don't think a fat penguin +really embodies the grace of Linux, which just tells me +they have never seen a angry penguin charging at them in +excess of 100mph. They'd be a lot more careful about what +they say if they had. + -- Linus Torvalds, announcing Linux v2.0 +~ +Ooohh... "FreeBSD is faster over loopback, when compared to Linux +over the wire". Film at 11. + -- Linus Torvalds +~ +C is quirky, flawed, and an enormous success. + -- Dennis M. Ritchie +~ +If Bill Gates is the Devil then Linus Torvalds must be the Messiah. +~ +Let's put it this way: +1. A 32-bit counter will expire in little over a year. +2. A 64-bit counter will expire in little over 2^32 years, + or roughly the time the sun (not the Sun) is expected to expire. +3. The odds of your computer hardware surviving the + aforementioned event without reboot are very slim. +Any questions? +~ +The only way tcsh "rocks" is when the rocks are attached +to its feet in the deepest part of a very deep lake. + -- Linus Torvalds +~ +In accord to UNIX philosophy, PERL gives you enough rope +to hang yourself. + -- Larry Wall, Randal Schwartz: Programming Perl + (aka the Camel Book). +~ +Anyone can build a fast processor. +The trick is to build a fast system. + -- Seymour Cray +~ +Hoping the problem magically goes away by ignoring +it is the "Microsoft approach to programming" and +should never be allowed. + -- Linus Torvalds +~ +One OS to rule them all, +One OS to find them. +One OS to call them all, +And in salvation bind them. +In the bright land of Linux, +Where the hackers play. + -- J. Scott Thayer, with apologies to J.R.R. Tolkien +~ +I'm not one of those who think Bill Gates is the devil. +I simply suspect that if Microsoft ever met up with the +devil, it wouldn't need an interpreter. + -- From N. Petreley's column, "Down to the Wire", + Sept. 1996 issue of Inforworld. +~ +After all, how do you give Microsoft the benefit of the +doubt when you know that if you throw it into a room with +truth, you'd risk a matter/anti-matter explosion. + -- From N. Petreley's column, "Down to the Wire", + Sept. 1996 issue of Inforworld) +~ +The local betaware broker was sitting in the bar, keeping an eye +for potential customers. It was easy to spot him, once you knew +the signs. A slightly paranoid look, but still eager to meet +new people. Not unlike a drug dealer or prostitute. This guy, +however, was carrying a laptop. + I sat in the chair beside him. "Any new stuff for +Linux configuration?", I said, looking at the opposite wall +of bottles. + The broker looked at me, startled, then quickly away. Then +back at me. "What are you, a cop?" The traditional greeting of +the underworld. It made me feel right at home. + "Nope, I just want to install Deb..." + "Shutup. I don't want to go to jail." + I turned around, looked around, then turned back, and put my +knife against his ribs. "Sing or die: where's software for +managing a group of Debian boxes easily?" + His face was pale, and he whispered through his teeth. +"cfgtool. At Lasu's site. http://www.iki.fi/liw/programs/". + I stood up, and walked quickly to the kitchen, and on +out. As I was closing the kitchen door behind me, I heard the +all too familiar sound of MessySoft Police Cars braking in the +street. It would be a hectic night, but I was still one step +ahead. + -- Lars Wirzenius, advertising his cfgtool program +~ +Microsoft seems to have gotten a lot of mileage out of the +C2 rating for NT with no network connection. I wonder if a B3 +rating for Linux with no power cord might be of value. + -- riordan@math.umn.edu +~ +In the United States there is more space where nobody is +than where anybody is. This is what makes America what it is. + -- Gertrude Stein, explaining early 20th century America +~ +Ah, yes, divorce, from the Latin word meaning +"to rip out a man's genitals through his wallet". + -- Robin Williams +~ +Women complain about premenstrual syndrome, but I think of +it as the only time of the month that I can be myself. + -- Roseanne +~ +Women need a reason to have sex. Men just need a place. + -- Billy Crystal +~ +I just broke up with someone and the last thing she said +to me was, 'You'll never find anyone like me again!' +I'm thinking, "I should hope not! If I don't want you, +why would I want someone like you?" + -- Larry Miller +~ +If you want to say it with flowers, a single rose says: "I'm cheap!" + -- Delta Burke +~ +According to a new survey, women say they feel more comfortable +undressing in front of men than they do undressing in front of other +women. They say that women are too judgemental, where, of course, +men are just grateful. + -- Jay Leno +~ +I am not the boss of my house. I don't know when I lost it. +I don't know if I ever had it. But I have seen the boss's job +and I do not want it. + -- Bill Cosby +~ +In the last couple of weeks I have seen the ads for the Wonder Bra. +Is that really a problem in this country? Men not paying enough +attention to women's breasts? + -- Jay Leno +~ +My mom said the only reason men are alive is for lawn care +and vehicle maintenance. + -- Tim Allen +~ +We have women in the military, but they don't put us in the front lines. +They don't know if we can fight, if we can kill. I think we can. +All the general has to do is walk over to the women and say, "You see +the enemy over there? They say you look fat in those uniforms." + -- Elayne Boosler +~ +There's a new medical crisis. Doctors are reporting that many men +are having allergic reactions to latex condoms. They say they cause +severe swelling. So what's the problem? + -- Jay Leno +~ +There's very little sexual advice in men's magazines, +because men don't think there's a lot they don't know. +Women do. Women want to learn. Men think, "I know what +I'm doing, just show me somebody naked." + -- Jerry Seinfield +~ +Men are liars. We'll lie about lying if we have to. +I'm an algebra liar. I figure two good lies make a positive. + -- Tim Allen +~ +Men do not like to admit to even momentary imperfection. +My husband forgot the code to turn off the alarm. When +the police came, he wouldn't admit he'd forgotten the +code... he turned himself in. + -- Rita Rudner +~ +If you can't beat them, arrange to have them beaten. + -- George Carlin +~ +Instead of getting married again, I'm going to find a woman +I don't like and give her a house. + -- Lewis Grizzard +~ +One of the chief duties of the mathematician in acting +as an advisor to scientists is to discourage them from +expecting too much from mathematics. + -- Norbert Wiener +~ +I wonder how much deeper the ocean would be without sponges. +~ +It is easy to guess why the rabble dislike cats. +A cat is beautiful; it suggests ideas of luxury, +cleanliness, voluptuous pleasures. + -- Charles Baudelaire +~ + An artist must regulate his life. + + Here is a time-table of my daily acts. I rise at 7.18; am inspired from +10.23 to 11.47. I lunch at 12.11 and leave the table at 12.14. A healthy +ride on horse-back round my domain follows from 1.19 pm to 2.53 pm. Another +bout of inspiration from 3.12 to 4.7 pm. From 5 to 6.47 pm various +occupations (fencing, reflection, immobility, visits, contemplation, +dexterity, natation, etc.) + + Dinner is served at 7.16 and finished at 7.20 pm. From 8.9 to 9.59 pm +symphonic readings (out loud). I go to bed regularly at 10.37 pm. Once a +week (on Tuesdays) I awake with a start at 3.14 am. + + My only nourishment consists of food that is white: eggs, sugar, shredded +bones, the fat of dead animals, veal, salt, coco-nuts, chicken cooked in +white water, moldy fruit, rice, turnips, sausages in camphor, pastry, cheese +(white varieties), cotton salad, and certain kinds of fish (without their +skin). I boil my wine and drink it cold mixed with the juice of the +Fuschia. I have a good appetite but never talk when eating for fear of +strangling myself. + + I breathe carefully (a little at a time) and dance very rarely. When +walking I hold my ribs and look steadily behind me. + + My expression is very serious; when I laugh it is unintentional, and I +always apologize very politely. + + I sleep with only one eye closed, very profoundly. My bed is round with a +hole in it for my head to go through. Every hour a servant takes my +temperature and gives me another. + -- Erik Satie's description of "A Day in the Life of a Musician" +~ +Confront a child, a puppy and a kitten with sudden danger; +the child will turn instinctively for assistance, the puppy +will grovel in abject submission to the impending visitation, +the kitten will brace its tiny body for a frantic resistance. + -- H.H. Munro (Saki) +~ + It is fair to say that, in general, no problems have been exhausted; +instead, men have been exhausted by the problems. Fresh talent approaching +the analysis of a problem without prejudice will always see new +possibilities -- some aspect not considered by those who believe that a +subject is fully understood. Our knowledge is so fragmentary that +unexpected findings appear in even the most fully explored topics... + In summary, there are no small problems. Problems that appear small +are large problems that are not understood. Instead of tiny details +unworthy of the intellectual, we have men whose tiny intellects cannot rise +to penetrate the infinitesimal. Nature is a harmonious mechanism where all +parts, including those appearing to play a secondary role, cooperate in a +functional whole. In contemplating this mechanism, shallow men arbitrarily +divide its parts into essential and secondary, whereas the insightful +thinker is content with classifying them as understood and poorly +understood, ignoring for the moment their size and immediately useful +properties. No one can predict their importance in the future." + -- Santiago Ramon y Cajal, "Advice for a Young Investigator," 1897 +~ +He who would do good to another must do it in Minute +Particulars: General Good is the plea of the scoundrel, +hypocrite and flatterer, for Art and Science cannot +exist but in minutely organized particulars. + -- William Blake +~ +The Atoms of Democritus +And Newton's Particles of Light +Are sands upon the Red Sea shore +Where Israel's tents do shine so bright. + -- William Blake +~ +To see a world in a grain of sand +And a heaven in a wild flower, and: +And did those feet in ancient time +Walk upon England's mountains green? ... +And was Jerusalem builded here +Among these dark Satanic mills? + -- William Blake +~ +The harlot's cry from street to street, +Shall weave Old England's winding sheet. + -- William Blake +~ +Tyger! Tyger! burning bright +In the forests of the night, +What immortal hand or eye +Could frame thy fearful symmetry? +.................. +Did he who made the Lamb make thee? + -- William Blake +~ +The cat, an aristocrat in type and origin, whom we +have slandered, merits at least our esteem. + -- Alexandre Dumas +~ +society's crumbling, but at least we're getting it televised. + -- fred t. hamster +~ +I think maybe it's my purpose in life. + -- The "Guiness Book of World Records" TV show's winner for the category + of longest combined finger nails for ten fingers. +~ + If we will only allow that, as we progress, we remain unsure, we will +leave opportunities for alternatives. We will not become enthusiastic for +the fact, the knowledge, the absolute truth of the day, but remain always +uncertain. The English have developed their government in this direction, it +is called 'muddling through,' and although a rather silly, stupid sounding +thing, it is the most scientific way of progressing. To decide upon the +answer is not scientific. In order to make progress, one must leave the door +to the unknown ajar. + We are only at the beginning of the development of the human race; of +the development of the human mind, of intelligent life; we have years and +years in the future. It is our responsibility not to give an answer today as +to what it is all about, to drive everybody down in that direction and say: +'This is the solution to it all.' Because we will be chained to the limit +of our present imagination, we will only be able to do those things that we +think are the things to do. Whereas, if we leave some room for discussion, +and proceed in a way analogous to the sciences, then this difficulty will +not arise. I believe, therefore, that although it is not the case today, +there may some day come a time, or I should hope, that the power of +government should be limited. That governments ought not to be empowered to +decide the validity of scientific theories. That is a ridiculous thing for +them to try to do. That they are not to decide the various descriptions of +history or of economic theory or of philosophy. + Only in this way can the real possibilities of the future human race be +ultimately developed. + -- Richard Feynman, from "The Beat Of A Different Drum: The Life And + Science of Richard Feynman," by Jagdish Mehra. Published by Oxford + University Press 1996. +~ +hey, when you come to think of it, isn't it +only the assholes in life who have zero tolerance +about things? and now they're trying to promote +that as a virtue. i just don't buy it. + -- fred t. hamster +~ +The spirit of the age cannot be compassed by the processes of human +reason. It is an inclination, an emotional tendency that works upon weaker +minds, through the unconscious, with an overwhelming force of suggestion +that carries them along with it. To think otherwise than our contemporaries +think is somehow illegitimate and disturbing; it is even indecent, morbid +or blasphemous, and therefore socially dangerous for the individual. He is +stupidly swimming against the social current. Just as formerly the +assumption was unquestionable that everything that exists takes its rise +from the creative will of a God who is spirit, so the nineteenth century +discovered the equally unquestionable truth that everything arises from +material causes. Today the psyche does not build itself a body, but on the +contrary, matter, by chemical action, produces the psyche. This reversal of +outlook would be ludicrous if it were not one of the outstanding features of +the spirit of the age. It is the popular way of thinking, and therefore it +is decent, reasonable, scientific and normal. Mind must be thought to be an +epiphenomenon of matter. The same conclusion is reached even if we say not +"mind" but "psyche," and in place of matter speak of brain, hormones, +instincts or drives. To grant the substantiality of the soul or psyche is +repugnant to the spirit of the age, for to do so would be heresy. + -- Carl Jung, from his 1933 book, "Modern Man In Search Of A Soul" +~ +There are many ways to explain an event, and some are better than +others. Even if neuroscientists someday decode the entire wiring +diagram of the brain, human behavior makes the most sense when it +is explained in terms of beliefs and desires, not in terms of volts +and grams. Physics provides no insights into the machinations of a +crafty lawyer, and even fails to enlighten us about many simpler +acts of living things. As Richard Dawkins observed, 'If you throw +a dead bird into the air it will describe a graceful parabola, +exactly as physics books say it should, then come to rest on the +ground and stay there. It behaves as a solid body of a particular +mass and wind resistance ought to behave. But if you throw a live +bird in the air it will not describe a parabola and come to rest on +the ground. It will fly away, and may not touch land this side of +the county boundary.' We understand birds in terms of their innards. +To know why they move and grow, we cut them open and put bits under +a microscope. We need yet another kind of explanation for artifacts +like a chair and a crowbar: a statement of the function the object is +intended to perform. It would be silly to understand why chairs have +a stable horizontal surface by cutting them open and putting bits of +them under a microscope. The explanation is that someone designed +the chair to hold up a human behind. + -- Steven Pinker, from his book, "How The Mind Works," + Norton, 1997. +~ + What wonder, then, that the world goes from bad to worse, and that +its evils increase more and more, as boredom increases, and boredom is the +root of all evil. The history of this can be traced from the very beginning +of the world. The gods were bored, and so they created man. Adam was bored +because he was alone, and so Eve was created. Thus boredom entered the +world, and increased in proportion to the increase of population. Adam was +bored alone; then Adam and Eve were bored together, then Adam and Eve and +Cain and Abel were bored en famille; then the population of the world +increased, and the peoples were bored en masse. To divert themselves they +conceived the idea of constructing a tower high enough to reach the heavens. +This idea is itself as boring as the tower was high, and constitutes a +terrible proof of how boredom gained the upper hand. + -- Soren Kierkegaard, from "Either/Or" +~ + Whatsoever therefore is consequent to a time of War, where every man +is Enemy to every man; the same is consequent to the time, wherein men live +without other security, than what their own strength, and their own +invention shall furnish them withall. In such condition, there is no place +for Industry; because the fruit thereof is uncertain: and consequently no +Culture of the Earth; no Navigation, nor use of the commodities that may be +imported by Sea; no commodious building; no Instruments of moving, and +removing such things as require much force; no Knowledge of the face of the +Earth; no account of Time; no Arts; no Letters; no Society; and which is +worst of all, continual fear, and danger of violent death; and the life of +man, solitary, poor, nasty, brutish, and short. + -- Thomas Hobbes +~ +A house without a cat, and a well-fed, well-petted, and properly revered +cat, may be a perfect house perhaps, but how can it prove its title? + -- Mark Twain +~ + The group cannot function if it has to maintain a large amount of +individual preconceptions and personal experiences. The design group must, +as a whole, have the ability and the opportunity to leave things behind, +that is, select what to remember. + If designers are to come up with novel ideas, they may have to forget +what was 'named and framed' as a problem or solution earlier in the process. +A group of designers needs to work its way through ideas, visions, and +operative images without being held up by heavy demands for documentation of +the process. In a creative design process one thing leads to another, +analogies and metaphors influence the design thinking in new ways, and a +certain amount of chaos is always present... + The characteristics of oral cultures can be applied to group design... +Group members need to repeat themselves, to be redundant and nonlinear in +their arguments, to forget, to make references and analogies in a situated +and intuitive ways. + -- Tone Bratteteig & Erik Stolterman, "Design in Groups--And All That + Jazz," in: M. Kyng & L. Mathiassen, "Computers and Design in Context," + MIT Press 1997. +~ + Where science has progressed by searching for commonalities and +patterns, the arts have celebrated diversity and have resisted attempts to +encapsulate their activities in rules and formulae. They are the ultimate +manifestations of the unpredictabilities and asymmetries of Nature. After +all, what more chaotically unpredictable outcomes are there than some of +those that issue from the human mind? So intractable has been the problem +of finding pattern in creative activity, that few would even seek it. If +one looks not at science and art, but at scientists and artists, one finds a +reflection of this divide. Two populations that overlap only a little, +convergent thinkers and divergent thinkers, specialists and generalists -- +these labels reflect the differences of which we speak... + While science has enlarged its past horizons beyond order and symmetry +to embrace diversity and unpredictability, the humanities have yet to +appreciate the full force of commonality and pattern as a unifying factor in +the interpretation of human creativity. Just as science has begun to +appreciate the ways in which its view of Nature must reconcile the ways in +which Nature is both simple and complex, so the arts and humanities must +appreciate the lessons to be drawn from the regularities of Nature. It is +not enough to collect examples of diversity: the coexistence of diversity +with universal behavior is what requires exploration and reconciliation. + -- John D. Barrow, from "The Artful Universe," Clarendon Press: + Oxford, 1995. +~ + I first became interested in Darwin in college when I read about +Darwin's experience with John Gould. When Darwin returned to England after +he visited the Galapagos, he distributed his finch specimens to professional +zoologists to be properly identified. One of the most distinguished experts +was John Gould. What was the most revealing was not what happened to Darwin, +but what had not happened to Gould. + Darwin's notes show Gould taking him through all the birds he had +named. Gould kept flip-flopping back and forth about the number of +different species of finches: The information was there, but he didn't quite +know what to make of it. He assumed that since God made one set of birds +when he created the world, the specimens from different locations would be +identical. It didn't occur to him to look for differences by location. Gould +thought that the birds were so different that they might be distinct species. + What was remarkable to me about the encounter is the completely +different impact it had on the two men. Gould thought the way he had been +taught to think, like an expert taxonomist, and didn't see, in the finches, +the textbook example of evolution unfolding right before him. Darwin didn't +even know they were finches. So the guy who had the intelligence, knowledge, +and the expertise didn't see the differences, and the guy with far less +knowledge and expertise came up with an idea that shaped the way we think +about the world. + Darwin came up with the idea because he was a productive thinker. He +generated a multiplicity of perspectives and theories. Gould would compare +new ideas and theories with his existing patterns of experience. He thought +reproductively. If the ideas didn't fit with what he had been taught, he +rejected them as worthless. On the other hand, Darwin was willing to +disregard what past thinkers thought and was willing to entertain different +perspectives and different theories to see where they would lead. + Most of us are educated to think like John Gould. We were all born +to be spontaneous, creative thinkers. Yet a great deal of our education may +be regarded as the inculcation of mind-sets. We were taught how to handle +problems and new phenomena with fixed mental attitudes (based on what past +thinkers thought) that predetermine our responses to problems or situations. +In short, we were taught 'what' to think instead of 'how' to think. We +entered school as a question mark and graduated a period. + -- Michael Michalko, in "Cracking Creativity: The Secrets of Creative + Genius," Ten Speed Press, 1998. +~ + The wisdom of the late industrial era was always to start with what +the customer needed and backtrack to which products and services those needs +called for. That fit when the customer already understood the need and the +product, and innovation meant a different shaped bottle for liquid detergent. +In BLUR, technical change is happening so fast, your product must educate the +customer (beepers for kids on dates?) and the customer must educate you. You +can't afford the time delay to put something new in front of the customer. + Instead, start with what technology will make possible, co-develop it as +fast as you can with the customer, and be flexible and adaptive enough to +adjust it according to customer needs as you go. As in software, the first +release is your take on things. The customer enters the feedback loop and +starts to influence things with release 2.0 and beyond. + -- Stan Davis & Christopher Meyer, "Blur: The Speed Of Change In The + Connected Economy," Addison-Wesley 1998. +~ + The traditional hidden curriculum of school demands that people of a +certain age assemble in groups of about thirty under the authority of a +professional teacher for from five hundred to a thousand times a year. It +does not matter if the teacher is authoritarian so long as it is the +teacher's authority that counts; it does not matter if all meetings occur +in the same place so long as they are somehow understood as attendance. The +hidden curriculum of school requires--whether by law or by fact--that a +citizen accumulate a minimum quantum of school years in order to obtain his +civil rights... + The translation of the need for learning into the demand for schooling +and the conversion of the quality of growing up into the price tag of a +professional treatment changes the meaning of 'knowledge' from a term that +designates intimacy, intercourse, and life experience into one that +designates professionally packaged products, marketable entitlements, and +abstract values. + -- Ivan Illich, from "After Deschooling, What?" +~ +I consider children to be very expensive and complicated pets. + -- fred t. hamster +~ + History tells us that the most successful cures for poverty come from +within. Foreign aid can help, but like windfall wealth, can also hurt. It +can discourage effort and plant a crippling sense of incapacity. As the +African saying has it, 'The hand that receives is always under the one that +gives.' No, what counts is work, thrift, honesty, patience, and tenacity... + To be sure, we are living in a dessert age. We want things to be +sweet; too many of us work to live and live to be happy. Nothing wrong with +that; it just does not promote high productivity. You want high productivity? +Then you should live to work and get happiness as a by-product. + Not easy. They who live to work are a small and fortunate elite. But +it is an elite open to newcomers, self-selected, the kind of people who +accentuate the positive. In this world, the optimists have it, not because +they are always right, but because they are positive. Even when wrong, they +are positive, and that is the way of achievement, correction, improvement, +and success. Educated, eyes-open optimism pays; pessimism can only offer +the empty consolation of being right. + The one lesson that emerges is the need to keep trying. No miracles. +No perfection. No millennium. No apocalypse. We must cultivate a +skeptical faith, avoid dogma, listen and watch well, try to clarify and +define ends, the better to choose means. + -- David Landis, from "The Wealth And Poverty Of Nations: Why Some Are + So Rich And Some So Poor," W. W. Norton & Co., 1998. +~ + During my travels around the country, visiting inner-city neighborhoods +and talking to young people I've met there, I have been struck again and +again by the stark differences between their childhoods and my own. When I +was growing up in the Bronx, I wasn't rich -- at least not in a material +sense, but I had the matchless blessing of being reared by two devoted +parents--backed up by a platoon of doting aunts and uncles--who gave me +the love, discipline and motivation I needed to succeed. + Too many of today's kids are not getting the same kind of nurturing +environment that I -- and most Americans -- once took for granted. As many +as 15 million youngsters are 'at risk' in today's America. They are in +danger of being lost for good unless the more fortunate among us step +forward and lend a hand... It is this glorious cycle of giving, receiving +and giving back that we want to pass along to the next generation of +Americans. We want them to believe in America, and we want them to know +that America believes in them. + -- Colin Powell's autobiography, "My American Journey" (Ballantine, 1996) +~ + In the past fifteen years one big American company after another has +done this [i.e., downsized itself] -- among them IBM, Sears, and GM. Each +first announced that laying off 10,000 or 20,000 or even 50,000 people would +lead to an immediate turnaround. A year later there had, of course, been no +turnaround, and the company laid off another 10,000 or 20,000 or 50,000 -- +again without results. In many if not most cases, downsizing has turned out +to be something that surgeons have warned against: 'amputation before +diagnosis.' The result is always a casualty. + But there have been a few organizations -- some large companies (GE, +for instance) and a few hospitals (Beth Israel in Boston, for instance) -- +that quietly, and without fanfare, did turn themselves around, by rethinking +themselves. They did not start out by downsizing. If fact, they knew that +to start by reducing expenditures is not the way to get control of costs. +The starting point is to identify the activities that are productive, that +should be strengthened, promoted, and expanded. Every agency, every policy, +every program, every activity should be confronted with these questions: +'What is your mission?' 'Is it still the right mission?' 'Is it still +worth doing?' 'If we were not already doing this, would we go into it now?' +This questioning has been done often enough in all kinds of organizations -- +businesses, hospitals, churches, an even local governments -- that we know +it works. + The overall answer is almost never 'This is fine as it stands; let's +keep on.' But in some -- indeed, a good many -- areas, the answer to the +last question is 'Yes, we should go into this again, but with some changes. +We have learned a few things.' + -- Peter F. Drucker, from "Managing in a Time of Great Change," + Truman-Talley Books/Dutton, 1995. +~ + Work expands so as to fill the time available for its completion. +General recognition of this fact is shown in the proverbial phrase 'It is +the busiest man who has time to spare.' Thus, an elderly lady of leisure +can spend the entire day in writing and dispatching a postcard to her niece +at Bognor Reis. An hour will be spent in finding the postcard, half an +hour in a search for spectacles, half an hour in a search for the address, +an hour and a quarter in composition, and twenty minutes in deciding +whether or not to take an umbrella when going to the mailbox in the next +street. The total effort that would occupy a busy man [or woman] for three +minutes all told may in this fashion leave another person [man or woman] +prostate after a day of doubt, anxiety, and toil. + Granted that work (and especially paperwork) is thus elastic in its +demands on time, it is manifest that there need be little or no +relationship between the work to be done and the size of the staff to which +it may be assigned. A lack of real activity does not, of necessity, result +in leisure. A lack of occupation is not necessarily revealed by a manifest +idleness. The thing to be done swells in importance and complexity in a +direct ration with the time to be spent. This fact is widely recognized, +but less attention has been paid to its wider implications, more especially +in the field of publication administration. Politicians and taxpayers have +assume (with occasional phases of doubt) that a rising total in the number +of civil servants must reflect a growing volume of work to be done. +Cynics, in questioning this belief, have imagined that the multiplication +of officials must have left some of them idle or all of them able to work +for shorter hours. But this is a matter in which faith and doubt seem +equally misplaced. The fact is that the number of officials and the +quantity of the work are not related to each other at all. The rise in the +toil of those employed is governed by Parkinson's Law and would be much the +same whether the volume of the work were to increase, diminish, or even +disappear. The importance of Parkinson's Law lies in the fact that it is a +law of growth based upon an analysis of the factors by which that growth is +controlled. + The validity of this recently discovered law must rest mainly on +statistical proofs, which will follow. Of more interest to the general +reader is the explanation of the factors underlying the general tendency to +which this law gives definition. Omitting technicalities (which are +numerous) we may distinguish at the outset two motive forces. They can be +represented for the present purpose by two almost axiomatic statements, +thus: (1) 'An official wants to multiply subordinates, not rivals' and (2) +'Officials make work for each other.' + -- C. Northcote Parkinson, from "Parkinson's Law & Other Studies + in Administration," (1957; Buccaneer Books ed., 1996). +~ +The smallest feline is a masterpiece. + -- Leonardo Da Vinci +~ + Though it may be discomforting to admit, throughout history children +have always played violent games. Early in this century, young boys played +'war' with lines of tin soldiers, knocking them down one by one, or in one +fell swoop, in a simulated battle. The next generation played cowboys and +Indians or cops and robbers, where the youngsters themselves fell down and +played dead. When parents stopped buying soldier figures and fake guns, +children created their own weapons and continued to play out good guy/bad +guy plots. Children are attracted to violence and critical studies of +older media forms, including the fairy tale, suggest it is not always in +children's best interests to remove from their cultural experience all +material that parents deem is too provocative or violent. + This is not a simple issue. + Clearly much of the concern about violence in video games and other +media is misplaced--the main sources of violent behavior lies elsewhere. +These include parental violence toward children and violence between +nations which portray the use of force in the real world by important +institutions--parents and government--as an acceptable way to solve +problems or vent anger. + Nevertheless, the impact of video games, television, film or other +media violence (such as the gory details of murders in the print media) +probably has a negative impact on violent behavior in society, as does the +proliferation of weapons. + The impact of media violence on an individual child's behavior is +probably very specific to the child and his or her social context. There +is no evidence, for example, that the use of "Beat-'em-up" video games +leads to violent behavior of well-adjusted youth coming from loving +families. The impact is also probably specific to age. Just as it makes +sense to restrict a 10-year-old's access to movies with extreme violent +content, so it makes sense to restrict age-inappropriate video games. Age +ratings on video game packages do appear to be helpful and appropriate. + Most violent games today are cartoon-like. This will change as games +become more realistic and, 3D, and eventually realistic of virtual reality, +ensuring that this issue will not disappear. + It is inappropriate for children of any age to spend significant +parts of life using violent video games, or any video games for that +matter. Conversely, for most children it makes little sense to deny access +to age-appropriate games simply for fear that these will lead to antisocial +behavior. Successful parenting is a question of balance. + -- Don Tapscott, "Growing Up Digital" +~ + Literacy-based education, as all other literacy experiences, assumes +that people are the same. It presumes that each human being can and must +be literate. Just as the goal of industry was to turn out standardized +products, education assumes the same task through the mold of literacy. +Diplomas and certificates testify how like the mold the product is. To +those who have problems with writing or reading, the labels legasthenic and +dyslexic are applied. Dyscalculus is the name given to the inability to +cope with numbers. The question of why we should expect uniform cognitive +structures covering the literate use of language or numbers, but not the +use of sounds, colors, shapes, and volume, is never raised. Tremendous +effort is made to help individuals who simply cannot execute the +sequentiality of writing or the meaning of successive numbers. Nothing +similar is done to address cognitive characteristics of persons inclined to +means different from literacy... + Education needs to reconsider its expectation of a universal common +denominator, based on the industrial model of standardization. Rather than +taming and sanitizing the minds of students, education has not only to +acknowledge differences in aptitudes and interests but also to stimulate +them. Every known form of energy is the expression of difference and not +the result of leveling. + -- Mihai Nadin, "The Civilization of Illiteracy," (1997) +~ + Anthropologists have identified a number of characteristics that seem +common to most non-technological societies past and present. These +societies tend to value practical rather than abstract knowledge, their +'primitive' rituals are part of the regular day-to-day realities of life, +the groups tend not to support specialists other than the shaman, every +member of the group can to some extent do every task, and all share the +responsibility for all others. Principally, the 'primitive' takes a holist +view of life that examines all social decisions for their effect on the +community and the environment. + These social values may fit well in the webbed communities of the +mid-twenty-first century because they are more appropriate to small, +relatively simple social structures that up to now had seemed to be +disappearing... For such communities, the most valuable skills would be +generalist rather than specialist. They would prize the ability to +connect, to think imaginatively, to understand how data are related, to see +patterns in machine-generated innovation, and to assess its social effect +before releasing it on society... + Today, billions of human talents could be on the verge of +self-expression if we are willing to take new views and see where they +might lead us. + -- James Burke & Robert Ornstein, "The Axemaker's Gift: A Double-Edged + History of Human Culture," (Grosset/Putnam 1995). +~ +(History is) indeed little more than the register of +the crimes, follies and misfortunes of mankind. + -- Edward Gibbon +~ +All that is human must retrograde if it does not advance. + -- Edward Gibbon +~ +The beauty of the second amendment is that it will +not be needed until they try to take it. + -- Thomas Jefferson +~ +No free man shall ever be debarred the use of arms. + -- Thomas Jefferson +~ +Firearms are second only to the Constitution in importance; they are +the people's liberty's teeth. + -- George Washington +~ +Every citizen should be a soldier. This was the case with the Greeks +and Romans, and must be that of every free state. + -- Thomas Jefferson +~ +Government is not reason, it is not eloquence. It is force. +Like fire, it is a dangerous servant and a fearsome master. + -- George Washington +~ +I have found a certain type calls himself a liberal... Now I always +thought I was a liberal. I came up terribly surprised one time when I +found out that I was a right-wing, conservative extremist... + -- John Wayne +~ +Liberty has never come from the government. Liberty has always come +from the subjects of government. The history of liberty is the history +of resistance. The history of liberty is a history of the limitation of +governmental power, not the increase of it. + -- Woodrow Wilson +~ +I believe there are more instances of abridgment of freedom of the +people by gradual and silent encroachments of those in power than by +violent and sudden usurpations... + -- James Madison +~ +You vote for me and I'll give you family values... I promise you the +most ethical administration in the history of our country. + -- William Jefferson Clinton +~ +Many give lip service, but few delegate authority in important +matters. And that means all they delegate is dog-work. A real +leader does as much dog-work for his people as he can: he can do +it, or see a way to do without it, ten times as fast. And he +delegates as many important matters as he can because that creates +a climate in which people grow. + -- Robert Townsend, founder of Avis Rent-a-Car +~ +True leadership must be for the benefit of the followers, not the +enrichment of the leaders. In combat, officers eat last. Most +people in big companies today are administered, not led. They are +treated as personnel, not people. + -- Robert Townsend, founder of Avis Rent-a-Car +~ +How do you spot a leader? They come in all ages, shapes, sizes, +and conditions. Some are poor administrators, some are not overly +bright. One clue: since most people per se are mediocre, the true +leader can be recognized because, somehow or other, his people +consistently turn in superior performances. + -- Robert Townsend, founder of Avis Rent-a-Car +~ +Before you commit yourself to a new effort, it's worth asking +yourself a couple of questions: "Are we really trying to do +something worthwhile here? Or are we just building another +monument to some diseased ego?" + -- Robert Townsend, founder of Avis Rent-a-Car +~ +Beware the boss who walks on water and never makes a mistake. +Save yourself a lot of grief and seek employment elsewhere. + -- Robert Townsend, founder of Avis Rent-a-Car +~ + Every genius is a revolutionary who produces a good deal of commotion +in the world. After he has abolished the old rules he writes his own, the +new ones, which no one even half understands; and after he has stupefied +and bewildered everybody, he leaves the world neither understood nor +regretted. Not always does the next generation comprehend and appreciate +him properly. Sometimes it may even take a whole century. + -- Frederic Chopin (1810-1849) +~ +To assume a cat's asleep +Is a great mistake. +He can close his eyes and keep +Both his ears awake. + -- Aileen Fisher +~ + A most nerve-wracking confirmation of this came some time ago during +an interview with the producer and the writer of the TV mini-series 'Peter +the Great.' Defending the historical inaccuracies in the drama -- which +included a fabricated meeting between Peter and Sir Isaac Newton -- the +producer said that no one would watch a dry, historically faithful +biography. The writer added that it is better for audiences to learn +something that is untrue, if it is entertaining, than not to learn anything +at all. And just to put some icing on the cake, the actor who played Peter, +Maximilian Schell, remarked that he does not believe in historical truth and +therefore sees no reason to pursue it. + I do not mean to say that the trivialization of American public +discourse is all accomplished on television. Rather, television is the +paradigm for all our attempts at public communication. It conditions our +minds to apprehend the world through fragmented pictures and forces other +media to orient themselves in that direction... + As a medium for conducting public business, language has receded in +importance; it has been moved to the periphery of culture and has been +replaced at the center by the entertaining visual image... When a culture +becomes overloaded with pictures; when logic and rhetoric lose their +binding authority; when historical truth becomes irrelevant; when the +spoken or written word is distrusted or makes demands on our attention that +we are incapable of giving; when our politics, history, education, +religion, public information, and commerce are expressed largely in visual +imagery rather than words, then a culture is in serious jeopardy. + -- Neil Postman, from "Amusing Ourselves to Death: Public + Discourse in the Age of Show Business," (Viking Press 1986). +~ + It is a perplexing and unpleasant truth that when men already have +'something worth fighting for,' they do not feel like fighting. People who +live full, worthwhile lives are not usually ready to die for their own +interests nor for their country nor for a holy cause. Craving, not having, +is the mother of a reckless giving of oneself. + 'Things which are not' are indeed mightier than 'things that are.' In +all ages men have fought most desperately for beautiful cities yet to be +built and gardens yet to be planted... + It is strange, indeed, that those who hug the present and hang on to +it with all their might should be the least capable of defending it. And +that, on the other hand, those who spurn the present and dust their hands of +it should have all its gifts and treasures showered on them unasked. + Dreams, vision and wild hopes are mighty weapons and realistic tools. +The practical-mindedness of a true leader consists in recognizing the +practical value of these tools. Yet this recognition usually stems from a +contempt of the present which can be traced to a natural ineptitude in +practical affairs. The successful businessman is often a failure as a +communal leader because his mind is attuned to the 'things that are' and his +heart set on that which can be accomplished in 'our time.' Failure in the +management of practical affairs seems to be a qualification for success in +the management of public affairs. And it is perhaps fortunate that some +proud natures when suffering defeat in the practical world do not feel +crushed but are suddenly fired with the apparently absurd conviction that +they are eminently competent to direct the fortunes of the community and the +nation. + -- Eric Hoffer, "The True Believer", 1951 +~ +Bought me a cat, the cat pleased me, +I fed my cat under yonder tree, +Cat goes fiddle-i-fee, fiddle-i-fee. + -- Traditional Folk Song +~ + Love is one aspect of what I have called the productive orientation: +the active and creative relatedness of man to his fellow man, to himself and +to nature. In the realm of thought, this productive orientation is +expressed in the proper grasp of the world by reason. In the realm of +action, the productive orientation is expressed in productive work, the +prototype of which is art and craftsmanship. In the realm of feeling, the +productive orientation is expressed in love, which is the experience of +union with another person, with all men, and with nature, under the +condition of retaining one's sense of integrity and independence. In the +experience of love the paradox happens that two people become one, and +remain two at the same time. Love in this sense is never restricted to one +person. If I can love only one person, and nobody else, if my love for one +person makes me more alienated and distant from my fellow man, I may be +attached to this person in any number of ways, yet I do not love. If I can +say, 'I love you,' I say, 'I love you in you also myself.' Self-love, in +this sense, is the opposite of selfishness. The latter is actually a greedy +concern with oneself which springs from and compensates for the lack of +genuine love for oneself. Love, paradoxically, makes me more independent +because it makes me stronger and happier--yet it makes me one with the +loved person to the extent that individuality seems to be extinguished for +the moment. In loving I experience 'I am you,' you-the loved person, +you-the stranger, you-everything alive. In the experience of love lies the +only answer to being human, lies sanity. + -- Erich Fromm, from "The Sane Society", 1955 +~ +# NO WARRANTY: THIS WORK IS PROVIDED ON AN "AS IS" BASIS. THE AUTHOR +# PROVIDES NO WARRANTY WHATSOEVER, EITHER EXPRESS OR +# IMPLIED, REGARDING THE WORK, INCLUDING WARRANTIES WITH +# RESPECT TO ITS MERCHANTABILITY OR FITNESS FOR ANY +# PARTICULAR PURPOSE. +# +# Author contact: Peter Alexander Merel +# Internet: pete@cssc-syd.tansu.oz.au +# UUCP: {uunet,mcvax}!munnari!cssc-syd!pete +# Snail: 1/18-20 Orion Road, Lane Cove NSW 2066 Australia +# Phone: +61 2 911 3130 +# +# Copyright: Copyright (C) 1992 Peter Alexander Merel +# Permission to copy all or part of this work is granted, +# provided that the copies are not made or distributed +# for resale (except nominal copying fee may be charged), +# and provided that the NO WARRANTY, author-contact, and +# copyright notice are retained verbatim & are displayed +# conspicuously. If anyone needs other permissions that +# aren't covered by the above, please contact the author. +# +# Version: 1.0 +# +# Tao Te Ching. +# Peter Merel's Interpolation based upon the translations of: +# Lin Yutang, Ch'u Ta-Kao, Gia-Fu Feng & Jane English, +# Richard Wilhelm, and Aleister Crowley. +~ + Seeing is of course indispensable to learning, particularly in +science, which is of the eye. Visual aids therefore have a place in the +laboratory. And most students, not being future scientists, will learn +more from good films of important experiments than from their own fumbling +attempts. But sometimes they must fumble too, and have a teacher who +fumbles on occasion, and thinks all the time he is in class. One learns +not by a photographic copying of things shown, but by an internal drama +imitative of the action witnessed. When the instructor gropes for a word, +corrects himself, interjects a comment or an analogy not directly called +for, he gives a spectacle of man thinking which no slick film or televised +show will provide. + -- Jacques Barzun, from "Science: The Glorious Entertainment", 1964 +~ + Far from behaving (or should one say behavioring?) with the regular +intelligibility of a clock, the bent of the living and of man in particular +is to MISbehave, in all senses of the word--from developing allergies, +which make poison out of delicacies, to committing crimes which, as in +saints and statesman, can later seem the highest wisdom. It is even proved +by research that man must have his ration of dreaming, that is, of +irregular and inaccurate thinking. These facts of experience require that +any science of the regularities of behavior be always qualified and +admonished by another discipline, a learned lore of misbehavior. + -- Jacques Barzun, from "Science: The Glorious Entertainment", 1964 +~ +Beware of all enterprises that require new clothes, and not rather +a new wearer of clothes. + -- Henry David Thoreau +~ +What men call social virtue, good fellowship, is commonly but the virtue +of pigs in a litter, which lie close together to keep each other warm. + -- Henry David Thoreau +~ +The mass of men lead lives of quiet desperation. +What is called resignation is confirmed desperation. + -- Henry David Thoreau +~ +Some circumstantial evidence is very strong, +as when you find a trout in the milk. + -- Henry David Thoreau +~ + I went to the woods because I wished to live deliberately, to front +only the essential facts of life, and see if I could not learn what it had +to teach, and not, when I came to die, discover that I had not lived. I did +not wish to live what was not life, living is so dear; nor did I wish to +practise resignation, unless it was quite necessary. I wanted to live deep +and suck out all of the marrow of life, to live so sturdily and Spartanlike +as to put to rout all that was not life, to cut a broad swath and shave +close, to drive life into a corner, and reduce it to its lowest terms, and, +if it proved to be mean, why then to get the whole and genuine meanness of +it, and publish its meanness to the world; or if it were sublime, to know it +by experience, and be able to give a true account of it in my next excursion. + -- Henry David Thoreau, from "Walden; or Life in the Woods" +~ + The Mind has a different relish, as well as the Palate; and you will +as fruitlessly endeavour to delight all Men with Riches or Glory, (which yet +some Men place their Happiness in,) as you would to satisfie all Men's +Hunger with Cheese or Lobsters; which though very agreeable and delicious +fare to some, are to others extremely nauseous and offensive: And many +People would with Reason prefer the griping of an hungry Belly, to those +Dishes, which are a Feast to others. Hence it was, I think, that the +Philosophers of old did in vain enquire, whether Summum bonum (the chief +good) consisted in Riches, or bodily Delights, or Virtue, or Contemplation: +And they might have as reasonably disputed, whether the best Relish were to +be found in Apples, Plumbs, or Nuts; and have divided themselves into Sects +upon it. For as pleasant Tastes depend not on the things themselves, but +their agreeableness to this or that particulate Palate, wherein there is +great variety: So the greatest Happiness consists, in the having those +things which produce the greatest Pleasure, and the absence of those which +cause any disturbance, any pain, which to different Men are very different +things. If therefore Men in this Life only have hope; if in this Life they +can only enjoy, 'tis not strange, nor unreasonable, they should seek their +Happiness by avoiding all things that disease them here, and by preferring +all that delight them; wherein it will be no wonder to find variety and +difference. For if there be no Prospect beyond the Grave, the inference is +certainly right, Let us eat and drink, let us enjoy what we delight in, for +to morrow we shall die. This, I think, may serve to shew us the Reason, +why, though all Men's desires tend to Happiness, yet they are not moved by +the same Object. Men may chuse different things, and yet all chuse right, +supposing them only like a Company of poor Insects, whereof some are Bees, +delighted with Flowers, and their sweetness; others, Bettles, delighted with +other kinds of Viands; which having enjoyed for a Season, they should cease +to be, and exist no more for ever. + -- John Locke, from "An Essay Concerning Human Understanding." +~ +Businesses will not buy Linux because there is no one to sue. + -- LinuxToday +~ + Even if something new does not require a disruption of the old, there +is no space. People, time and resources are fully stretched -- in many +cases there is actually a cutting-back in resources. + The paradox is that as we advance into the future the need for change +gets greater and greater (to cope with changes in population, pollution, +etc., and to make full use of our new technologies) but the possibility of +change gets less and less because everything is already committed. + A wise general does not commit all his troops but keeps a strategic +reserve which can be used as the need and opportunity arise. Society does +not do this, because we believe that we have all the bases covered and that +progress will come about through evolution, the clash of opinions and the +occasional lone innovator. + In addition to allocating funds to research, most successful +corporations also allocate funds to new business divisions or venture +groups. Like the strategic reserves of a general, these groups are outside +the day-to-day combat and are looking for new opportunities. + Democracy could not easily tolerate this principle of strategic +reserve, for the unallocated resources would be the target of every +department or issue that felt it was under-funded. Emergency funds do +exist, but not space and resources for change. + The same thing applies on the thinking level. A person who knows all +the answers, has an opinion on everything, has a certainty backed up by +rational argument, has very little possibility of further progress. Such a +person is unlikely to walk away from a discussion with anything more than a +reaffirmation of how right he or she has been all along. + -- Edward deBono, from "I Am Right, You Are Wrong" (Penguin Books, 1990) +~ + The pleasantest time of day here is at sunset. Then accompanied by +some fifteen girls and little children I walk through the village to the end +of Siufaga, where we stand on an iron bound point and watch the waves splash +us in the face, while the sun goes down, over the sea and at the same time +behind the cocoanut covered hills. Most of the adult population is going +into the sea to bathe, clad in lavalavas with buckets for water borne along +on shoulder poles. All the heads of families are seated in the fatele +(village guesthouse) making kava. At one point a group of women are filling +a small canoe with a solution of native starch (arrowroot). And perhaps, +just as we reach the store, the curfew-angelus will stop us, a wooden bell +will clang mellowly through the village. The children must all scurry to +cover, if we're near the store, it's the store steps, and sit tight until +the bell sounds again. Prayer is over. Sometimes we are all back safely in +room when the bell sounds, and then the Lord's Prayer must be said in +English, while flowers are all taken out of their hair and the siva song +stopped in the middle. But once the bell sounds again, solemnity, never of +a very reliable depth, is sloughed off, the flowers replaced in the girls' +hair, the siva song replaces the hymn, and they begin to dance, by no means +in a puritan fashion. Their supper comes about eight and sometimes I have a +breathing spell, but usually the supper hours don't jibe well enough for +that. They dance for me a great deal, they love it and it is an excellent +index to temperament, as the dance is so individualistic, and the audience +think it is its business to keep up incessant comment. + -- Margaret Mead, from "Coming of Age in Samoa" +~ +There aren't any embarassing questions--only embarassing answers. + -- Carl Rowan +~ + Recently one of us was leading a group of thirty Western +businesspeople through Japan to learn about Japanese management techniques. +We took the bullet train from Hiroshima to Osaka, and since the train +stopped for only twenty seconds in Hiroshima, it would have been impossible +to get all the executives and their luggage on the train at the same time. +So we hired a trucking company and crew to transfer the luggage separately. +The crew removed all luggage from each individual's room in Hiroshima and +placed it in his pre-checked room in another chain's hotel in Osaka. Can +you imagine doing that in the United States and ever seeing your luggage +again? + One of the executives, thinking his shoes were too worn, had discarded +them in the wastepaper basket in his Hiroshima hotel room. Imagine his shock +when he entered his room in Osaka and saw his old shoes carefully laid in +the wastepaper basket there. Was the Japanese trucking company politely +saying that these shoes still had life and should yet be thrown away, or was +the company accommodating a crazy foreigner who liked to keep his shoes in +the wastepaper basket? Either way, the company was organized for highly +intelligent service at every level. + -- Stan David and Jim Botkins, from "The Monster Under the Bed: How + Business is Mastering the Opportunity of Knowledge for Profit", + (Simon & Schuster, 1994). +~ + The term 'information' appears to cover too much that seems +distinctive: knowledge, data, information in a narrow sense that some treat +as synonymous with data, news, intelligence, and numerous other colloquial +and specialized denotations and connotations. However, the distinctions +implied by oppositions such as observations/theories, data/knowledge, raw +intelligence/finished intelligence, accounting details/management are +secondary, not fundamental, in characterizing information resources. They +reflect only relative judgments. For instance, one person's knowledge is +often another's raw data. What a vice president for marketing, production, +or finance thinks he knows is just data to the chief executive officer's +staff. What a scientist thinks he knows about the merits of a flu vaccine +or the safety of a nuclear reactor is just data for presidential policy and +politics. Data or knowledge are just types of information content--of +greater or lesser value, of greater or lesser cost. + -- Anthony Oettinger, from "The Information Resources Policy Handbook" +~ + Given the fact that there seems to be a fundamental willingness to +accept the machine as almost human, the issue of what type of relationship +is possible seems to center around what friendship-cues might be +artificially generated. Appearance and voice quality could certainly be +tailored so that the machine would look and sound attractive and friendly. +The software could be written to suggest an interesting and unique +personality, and the conversational style might appear as humorous and +good-natured. The machine would not only impress us with its intelligence +and knowledge of the world, but would also convey the impression that it +was warm and understanding. Its ability to integrate our interests and +attitudes into its own framework and its willingness to be influenced by +our point of view would also enhance our respect for the machine. The fact +that it appeared to take our opinions seriously might be regarded as a +compliment, and it is clear that if we are prepared to accept compliments +from a computer then we are implicitly accepting it as a social agent. + Friendships are not made in a day, and the computer would be more +acceptable as a friend if it simulated the gradual changes that occur when +one person is getting to know another. At an appropriate time it might +also express the endearment that stimulates attachment and intimacy. The +whole process would be accomplished with subtlety to avoid giving an +impression of overfamiliarity or ingratiation, which would be likely to +produce irritation or animosity. After experiencing a wealth of powerful, +well-timed indicators, the user would be likely to accept the computer as +far more than a machine and might well come to regard it as a friend. + -- Neil Frude, from "The Intimate Machine: Close Encounters with + Computers and Robots," 1983. +~ + By this time the stars were moving out of the Hollywood Hotel and +beginning to live in their own private houses with servants, most of whom +were their peers in everything but sex appeal--which pinpoints the reason for +the film capital's mass misbehavior. To place in the limelight a great +number of people who ordinarily would be chambermaids and chauffeurs, give +them unlimited power and instant wealth, is bound to produce a lively and +diverting result. + -- Anita Loos +~ + The four years passed at college were, for his purposes, wasted. +Harvard College was a good school, but at bottom what the boy +disliked most was any school at all. He did not want to be one in a +hundred -- one percent of an education. He regarded himself as the +only person for whom his education had value, and he wanted the whole +of it. He got barely half of an average. + Long afterwards, when the devious path of life led him back to +teach in his turn what no student naturally cared or needed to know +[medieval history], he diverted some dreary hours of faculty meetings +by looking up his record in the class-lists, and found himself graded +precisely in the middle. In the one branch he most needed -- +mathematics -- barring the few first scholars, failure was so nearly +universal that no attempt at grading could have had value, and +whether he stood fortieth or ninetieth must have been an accident or +the personal favor of the professor. Here his education failed +lamentably. At best he could never have been a mathematician; at +worst he would never have cared to be one; but he needed to read +mathematics, like any other universal language, and he never reached +the alphabet. + -- Henry Adams, from "The Education of Henry Adams" +~ + The pedagogical method of observation has for its base the liberty of +the child; and liberty is activity. + Discipline must come through liberty. Here is a great principle which +is difficult for followers of common-school methods to understand. How +shall one obtain discipline in a class of free children? Certainly in our +system, we have a concept of discipline very different from that commonly +accepted. If discipline is founded upon liberty, consider an individual +disciplined only when he has been rendered as artificially silent as a mute +and as immovable as a paralytic. He is an individual annihilated, not +disciplined. + We call an individual disciplined when he is master of himself, and +can, therefore, regulate his own conduct when it shall be necessary to +follow some rule of life. Such a concept of active discipline is not easy +either to comprehend or to apply. But certainly it contains a great +educational principle, very different from the old-time absolute and +undiscussed coercion to immobility. + -- Maria Montessori, inventor of the "Montessori Method" +~ + A company could conceivably have within it a monastery-style unit +that writes software... a research team organized like an improvisational +jazz combo... a compartmentalized spy-network, with need-to-know rules, +operating within the law, to scout for merger or acquisition +possibilities... and a sales force organized as a highly motivated 'tribe' +complete with its own war songs and emotional membership rituals. + ...(T)he units of a flex-firm may draw information, people, and money +from one another and from outside organizations as needed. They may be next +door to one another or continents apart. Their functions may partly +overlap, like information in a hyper-media data base; for other purposes, +the functions may be logically, geographically, or financially divided. +Some may use many central services provided by headquarters; others may +choose to use only a few. + In turn this requires freer, faster flows of information. This will +mean crisscrossing, up, down, and sideways conduits--neural pathways that +bust through the boxes in the table of organization so that people can trade +the ideas, data, formulae, hints, insights, facts, strategies, whispers, +gesture, and smiles that turn out to be essential to efficiency. + -- Alvin Toffler, from "Powershift" +~ +Here in the newspaper business, we have definitely caught Internet Fever. +In the old days, we used to -- get this! -- actually CHARGE MONEY for our +newspapers. Ha! What an old-fashioned, low-tech, non-digital concept! +Nowadays all of the modern newspapers spend millions of dollars operating +Web sites where we give away the entire newspaper for free. Sometimes we +run advertisements in the regular newspaper urging our paying customers to +go to our Web sites instead. 'Stop giving us money!' is the shrewd marketing +thrust of these ads. Why do we do this? Because all the other newspapers +are doing it! This is called 'market penetration.' + -- Dave Barry +~ + A highly competitive person has a hard row to hoe. There is no +satisfaction in winning a competition unless it is a stiff and fair one. +Stiff is easy to define; it is stiff if one's own realistic assessment of +one's abilities make the odds long--the longer the odds, the greater +satisfaction on winning. Fair is harder to define, for if one wins a +contest against long odds, there must be a reason. The odds weren't really +long; they only appeared to be so. Isn't it unfair to appear to be an +underdog when one really isn't? Let's start with some obvious distinctions: +A professional gambler needs to win in order to earn his living. Fairness +is not his concern. He tries to be unfair in various ways: Keeping cards up +his sleeve is one way that the rest of us universally deplore; the morality +of concealing his skill to attract dupes is hardly less questionable. +Fairness means at least an honest deal (no hidden cards) and no intentional +concealment of one's abilities. + -- Herbert A. Simon, from "Models of My Life" (Basic Books, 1991) +~ + The United States is trying to promote democracy around the +world and holds itself up as a model. However, it is obvious that +our democracy has been reduced to charisma and money. + Surely intelligent and conscientious citizens can make wise and +informed decisions? I for one cannot... I do not know anyone who +spends more hours than I do in search of information and wisdom, yet +it avails me little. Many people with power spend much of their time +flying around the world, being wined and dined, but that means they +have little time to do their homework. + Is the idea of democracy based on an informed electorate just a dream? + + -- Ronald Hilton, founder of WAIS (World Association of + International Studies). +~ + When there is communication without need for communication, +merely so that someone may earn the social and intellectual prestige +of becoming a priest of communication, the quality and communicative +value of the message drop like a plummet... In the arts, the desire +to find new things to say and new ways of saying them is the source +of all life and interest. Yet every day we meet with examples of +painting where, for instance, the artist has bound himself from the +new canons of the abstract, and has displayed no intention to use these +canons to display an interesting and novel form of beauty, to pursue +the uphill fight against the prevailing tendency toward the commonplace +and the banal... + I speak here with feeling which is more intense as far as concerns +the scientific artist than the conventional artist, because it is in +science that I have first chosen to say something. What sometimes +enrages me and always disappoints and grieves me is the preference of +great schools of learning for the derivative as opposed to the original, +for the conventional and thin which can be duplicated in many copies +rather than the new and powerful, and for arid correctness and +limitation of scope and method rather than for universal newness and +beauty, wherever it may be seen. + -- Norbert Wiener, from "The Human Use of Human Beings: Cybernetics + and Society" (1950) +~ + Please do not suppose that the only function of puzzles is to entertain. +Puzzles are a way of teaching mathematics. Indeed, they are the best way to +teach it. Fred Hoyle, the famous British astronomer who taught mathematics +at Cambridge University for twenty years states in strong terms his belief +that mathematics should never be 'taught' at all. Students must learn for +themselves. How? By solving puzzles. The functions of the teacher should +be, first, to select in a wise way the material on which the puzzles are +based, second, to make sure the puzzles are well suited in difficulty to the +sophistication of the student, third, to answer questions, and finally, if +the teacher is capable of it, to give an occasional word of inspiration. + -- Martin Gardner +~ +A cat's idea of what is comfortable and what is not +is incomprehensible to a human. + -- Colette +~ +In the sciences, hypothesis always precedes law, which is to say, there is +always a lot of tall guessing before a new fact is established. The +guessers are often quite as important as the fact-finders; in truth, it +would not be difficult to argue that they are more important. New facts are +seldom plucked from the clear sky; they have to be approached and smelled +out by a process of trial and error, in which bold and shrewd guessing is an +integral part. The Greeks were adept at such guessing, and the scientists +of the world have been following the leads they opened for more than two +thousand years. + -- H. L. Mencken, "A Mencken Chrestomathy," 1982 +~ +I love a dog. He does nothing for political reasons. + -- Will Rogers +~ +If a dog will not come to you after having looked you in the face, +you should go home and examine your conscience. + -- Woodrow Wilson +~ +I am called a dog because I fawn on those who give me anything, +I yelp at those who refuse, and I set my teeth in rascals. + -- Diogenes +~ +Those sighs of a dog! They go to the heart so much more +deeply than the sighs of our own kind because they are utterly +unintended, regardless of effect, emerging from one who, heaving +them, knows not that they have escaped him! + -- John Galsworthy +~ +(Of dogs) I marvel that such small ribs as these can +cage such vast desire to please. + -- Ogden Nash +~ +'Tis sweet to hear the watch-dog's honest bark +Bay deep-mouth'd welcome as we draw near home; +'Tis sweet to know there is an eye will mark +Our coming, and look brighter when we come. + -- Lord Byron +~ +Near this spot are deposited the remains of one who possessed +Beauty without Vanity, Strength without Insolence, Courage without +Ferocity, and all the Virtues of Man without his Vices. This praise, +which would be unmeaning Flattery, if inscribed over human ashes, is +but a just Tribute to the Memory of BOATSWAIN, a Dog. + -- Inscription on the monument raised for Lord Byron's dog, Boatswain +~ +Histories are more full of examples of the fidelity of dogs than of friends. + -- Alexander Pope +~ +A door is what a dog is perpetually on the wrong side of. + -- Ogden Nash +~ +Living with a dog is easy--like living with an idealist. + -- H. L. Mencken +~ +The great pleasure of a dog is that you may make a fool +of yourself with him and not only will he not scold you, +but he will make a fool of himself too. + -- Samuel Butler (d. 1902), Note-Book +~ +The dog has seldom been successful in pulling man up to his level +of sagacity, but man has frequently dragged the dog down to his. + -- James Thurber +~ +I agree with Agassiz that dogs possess something very like a conscience. + -- Charles Darwin +~ +For the strength of the pack is the wolf, +and the strength of the wolf is the pack. + -- Rudyard Kipling +~ +All of the animals except humans know that the +principal business of life is to enjoy it. + -- Samuel Butler +~ +Cowardly dogs bark loudest. -- John Webster +~ +Dogs like to obey. It gives them security. -- James Herriot +~ +A dog's best friend is his illiteracy. -- Ogden Nash +~ +Take a dog for a companion and a stick in your hand. -- English Proverb +~ +A lean dog shames its master. -- Japanese Proverb +~ +The dog was created specially for children. +He is a god of frolic. + -- Henry Ward Beecher, Proverbs from Plymouth Pulpit +~ +Dogs have not the power of comparing. A dog will take a small piece +of meat as readily as a large, when both are before him. + -- Samuel Johnson +~ +All knowledge, the totality of all questions and all answers, +is contained in the dog. + -- Franz Kafka +~ +The dog has an enviable mind; it remembers the nice things in life +and quickly blots out the nasty. + -- Barbara Woodhouse +~ +If dogs could talk, perhaps we would find it as hard to get along +with them as we do with people. + -- Karel Capek +~ +Our perfect companions never have fewer than four feet. + -- Colette +~ +You become responsible forever for what you have tamed. + -- Antoine de Saint-Exupery +~ +A dog has the soul of a philosopher. -- Plato +~ +The more I see of men, the more I like dogs. -- Madame Anne Maria de Stael +~ +The reason a dog has so many friends is that +he wags his tail instead of his tongue. + -- Anonymous +~ +We see how he is at once in a world of smells of which +we know nothing, which so occupy and absorb his attention +as to make him practically blind to everything about him +and deaf to all sounds, even his master's voice impatiently +calling him. + -- W. H. Hudson +~ +A man may smile and bid you hail +Yet wish you to the devil; +But when a good dog wags his tail, +You know he's on the level. + -- Submitted to www.dog.com by TJ Brown +~ +Old dogs, like old shoes, are comfortable. +They might be a bit out of shape and a little +worn around the edges, but they fit well. + -- Bonnie Wilcox +~ +In order to really enjoy a dog, one doesn't merely try to +train him to be semi-human. The point of it is to open +oneself to the possibility of becoming partly a dog. + -- Edward Hoagland +~ +I'd be happy to have my biography be the stories of my dogs. +To me, to live without dogs would mean accepting a form of blindness. + -- Thomas McGuane +~ +Every dog should have a man of his own. There is nothing like a +well-behaved person around the house to spread the blanket for him, +or bring him his supper when he comes home man-tired at night. + -- Corey Ford +~ +Not Carnegie, Vanderbilt, and Astor +together could have raised money enough +to buy a quarter share in my little dog... + -- Ernest Thompson Seton +~ +I'd rather have an inch of dog than miles of pedigree. -- Dana Burnet +~ +...in a healthy dog-owner relationship, praise is virtually +an automatic reaction, an attitude toward the dog, a way of +living with the dog. The most common mistake is to consider +praise as simply a reward. + -- The Monks of New Skete +~ +Dogs need to sniff the ground; it's how they keep abreast of +current events. The ground is a giant dog newspaper, containing +all kinds of late-breaking dog news items, which, if they are +especially urgent, are often continued in the next yard. + -- Dave Barry +~ +A dog is not "almost human" and I know of no greater insult to the +canine race than to describe it as such. The dog can do many things +which man cannot do, never could do, and never will do. + -- John Holmes +~ +imagine yourself lying in the tall purple grass with the sharp blades +rubbing up against your flesh like rusty razors while the dark red +moon drops drips of blood into the oozing green lake down the hill. +tattered fish things leap and whirl in the lake while the tired orange +sun flickers on the verge of going out. the clacking and biting +insects that infest the grass crawl in and out of your body as you lie +there trying to relax while large unseen animals rummage in the forest +causing trees to crash down as they pass. dust blows in thick +whirlwinds making the sooty air impossible to breathe as you hack +chunks of meat up from within, but symbiotic parasites rush to repair +the damage while tickling your insides ferociously. +ah... tranquility. + -- sparklecuss +~ +It all comes from here, the stench and the peril. -- Frodo +~ +I do not like... the omission of a bill of rights providing +clearly and without the aid of sophisms for freedom of religion, +freedom of the press, protection against standing armies, +restriction against monopolies, the eternal and unremitting force +of the habeas corpus laws, and trials by jury in all matters of +fact triable by the laws of the land and not by the law of +nations. + -- Thomas Jefferson to James Madison, 1787 +~ +A bill of rights is what the people are entitled to against +every government on earth, general or particular; and what no +just government should refuse, or rest on inferences. + -- Thomas Jefferson to James Madison, 1787. Papers, 12:440 +~ +It astonishes me to find... [that so many] of our countrymen... +should be contented to live under a system which leaves to their +governors the power of taking from them the trial by jury in +civil cases, freedom of religion, freedom of the press, freedom +of commerce, the habeas corpus laws, and of yoking them with a +standing army. This is a degeneracy in the principles of +liberty... which I [would not have expected for at least] four +centuries. + -- Thomas Jefferson to William Stephens Smith, 1788 +~ +A bill of rights [will] guard liberty against the legislative as +well as the executive branches of the government. + -- Thomas Jefferson to Francis Hopkinson, 1789 +~ +The declaration of rights is, like all other human blessings, +alloyed with some inconveniences and not accomplishing fully its +object. But the good in this instance vastly outweighs the evil. + -- Thomas Jefferson to James Madison, 1789 +~ +By a declaration of rights, I mean one which shall stipulate +freedom of religion, freedom of the press, freedom of commerce +against monopolies, trial by juries in all cases, no suspensions +of the habeas corpus, no standing armies. These are fetters +against doing evil which no honest government should decline. + -- Thomas Jefferson to Alexander Donald, 1788 +~ +(cat haiku) +You must scratch me there! +Yes, above my tail! Behold, +Elevator butt. +~ +(cat haiku) +prickly herbal scent +flips me inside out and back; +catnip is my kind + -- fred t. hamster +~ +(cat haiku) +I need a new toy. +Tail of black dog keeps good time. +Pounce! good dog! good dog! +~ +The best exercise for a cat is another cat. + -- Jo and Paul Loeb +~ +(cat haiku) +In deep sleep hear sound +Cat vomit hairball somewhere. +Will find in morning. +~ +[Of the Romans...] +Is not a certain dullness their most visible characteristic? +What is the history of their speculative mind? -- a blank. +What their literature? -- a copy. They have left not a single +discovery in any abstract science; not a single perfect or +well-formed work of high imagination. The Greeks, the perfection +of narrow and accomplished genius, bequeathed to mankind the ideal +forms of self-idolizing art -- the Romans imitated and admired; +the Greeks explained the laws of nature -- the Romans wondered and +despised; the Greeks invented a system of numerals second only to +that now in use -- the Romans counted to the end of their days +with the clumsy apparatus which we still call by their name; the +Greeks made a capital and scientific calendar -- the Romans began +their month when the Pontifex Maximus happened to spy out the new +moon. Throughout Latin literature, this is the perpetual puzzle +-- Why are we free and they slaves? we praetors and they barbers? +Why do the stupid people always win, and the clever people always +lose? + -- Walter Baghehot +~ +(cat haiku) +The rule for today. +Touch my tail, I shred your hand. +New rule tomorrow. +~ +'Well, then,' the Cheshire Cat went on, 'you see a dog growls +when it's angry, and wags its tail when it's pleased. Now, I +growl when I'm pleased, and wag my tail when I'm angry. +Therefore I'm mad.' + -- Cheshire Cat (from Alice's Adventures in Wonderland, + by Lewis Carroll) +~ +(cat haiku) +stalk the birds with care-- +twitch my tail and they may scare; +human gets a share. + -- fred t. hamster +~ +(cat haiku) +i am not a rug, +my flatness is intended, +i have rubber bones. + -- fred t. hamster +~ +(cat haiku) +fat human waddles +close to my sensitive tail +may be time to shriek. + -- fred t. hamster +~ +(cat haiku) +fish in pond bigger +than me; goldfish in bowl just +the right size--kerchomp. + -- fred t. hamster +~ +Yet gentle will the griffin be, +Most decorous and fat, +And walk up to the Milky Way +And lap it like a cat. + -- Vachel Lindsay +~ +(cat haiku) +Blur of motion, then-- +Silence, me, a paper bag +What is so funny? +~ +(cat haiku) +The mighty hunter +Returns with gifts of plump birds +Your foot just squashed one. +~ + Every good teacher has his own special art; with some, it is a genius +for a clarity that sometimes is more lucid than the complexities of the +subject justify. Sometimes it is a talent for apothegm or leading +suggestion, a word that evokes a vista or an idea that opens a world. I +cannot now quite remember what Professor Beard's special technique was. He +was clear, he was suggestive, he was witty. But none of these things could +quite account for the hold he had on the smug and the rebels alike, on both +the pre-lawyers and pre-poets. I suspect it was a certain combination of +poetry, philosophy, and honesty in the man himself, a sense he communicated +that politics mattered far beyond the realm commonly called political, and +an insight he conveyed into the life that forms of government furthered or +betrayed. One morning he came into class as usual, stood against the wall, +and half-closing his eyes, said: + "Gentlemen, today we are to discuss the budget system in State +government. I am sure that must seem to you a dull subject. But if you will +tell me, gentlemen, how much per capita a nation spends on its Army, on its +Navy, on education, on public works, I shall be able to tell you, I think, +as much about that nation as if you gave me the works of its poets and +philosophers." + We listened with revised and revived attention to an exposition, full +of figures and detail, of the State budget system. Charles A. Beard showed +us what politics had to do with the life beyond it and which it made +possible. And he taught us, too, the difference between the forms of +government and the living substance of its operations... Nobody who has ever +listened to Beard can disdain the study of politics in favor of the study of +"higher things". He has been too well taught, as tragic world events have +since shown, how government may nourish or destroy "higher things". + -- Irwin Edman, speaking of the historian Charles A. Beard +~ +There is no substitute for a lifetime. + -- Ezra Pound +~ + The technologies which have had the most profound effects on human life +are usually simple. A good example of a simple technology with profound +historical consequences is hay. Nobody knows who invented hay, the idea of +cutting grass in the autumn and storing it in large enough quantities to +keep horses and cows alive through the winter. All we know is that the +technology of hay was unknown to the Roman Empire but was known to every +village of medieval Europe. Like many other crucially important +technologies, hay emerged anonymously during the so-called Dark Ages. +According to the Hay Theory of History, the invention of hay was the +decisive event which moved the center of gravity of urban civilization from +the Mediterranean basin to Northern and Western Europe. The Roman Empire did +not need hay because in a Mediterranean climate the grass grows well enough +in winter for animals to graze. North of the Alps, great cities dependent on +horses and oxen for motive power could not exist without hay. So it was hay +that allowed populations to grow and civilization to flourish among the +forests of Northern Europe. Hay moved the greatness of Rome to Paris and +London, and later to Berlin and Moscow and New York. + -- Freeman Dyson, from his 1985 Gifford Lectures +~ +(cat haiku) +Small brave carnivores +Kill pine cones and mosquitoes, +Fear vacuum cleaner +~ +(cat haiku) +Wanna go outside. +Oh, no! Help! I got outside! +Let me back inside! +~ +If you're a geek at a circus +and the only tool you have is a sledgehammer, +then you'll use it everywhere. +We're Microsoft and +Windows is our sledgehammer. + -- FTH +~ +An appeaser is one who feeds a crocodile-- +hoping it will eat him last. + -- Winston Churchill +~ +Being poor is a frame of mind. Being broke is only a temporary situation. + -- Mike Todd +~ +When a friend speaks to me, whatever he says is interesting. + -- Jean Renoir +~ +Up to his shoulders +In grasses coarse as silk, +The white cat with the yellow eyes +Sits with his paws together, +Tall as a quart of milk. + -- James Kirkup +~ +Life is a gamble, at terrible odds--if it was a bet you wouldn't take it. + -- Tom Stoppard +~ +What is the most innocent place in any country? +Is it not the insane asylum? +These people drift through life truly innocent, +unable to see into themselves at all. + -- Arthur Miller +~ +We live by our genius for hope; +we survive by our talent for dispensing with it. + -- V. S. Pritchett +~ +People change and forget to tell each other. + -- Lillian Hellman +~ +Professional work of any sort tends to narrow the mind, to limit the point +of view, and to put a hallmark on a man of a most unmistakable kind. On the +one hand are the intense, ardent natures, absorbed in their studies and +quickly losing interest in everything but their profession, while other +faculties and interest 'fust' unused. On the other hand are the bovine +brethren, who think of nothing but the treadmill and the corn. From very +different causes, the one from concentration, the other from apathy, both +are apt to neglect those outside studies that widen the sympathies and help +a man to get the best there is out of life... the medical man, perhaps more +than any other man, needs that higher education of which Plato speaks, +'that education in virtue from youth upwards, which enables a man to pursue +the ideal perfection.' It is not for all, nor can all attain it, but there +is comfort and help in the pursuit, even though the end is never reached. + -- William Osler +~ +We cherish our friends not for their ability to amuse us, +but for ours to amuse them. + -- Evelyn Waugh +~ + Scientific research is solving puzzles. The pleasure to +be got from it is the pleasure of the crossword or jig-saw addict. +First the blank diagram, or the scatter of meaningless pieces; then +an occasional tentative clue or the few pieces of the same colour +that seem to fit together; next a period of frustration, going over +and over the list of clues, or trying piece after piece in the most +unlikely conjunctions; then--ah the sweet joy of the word that +completes a doubtful acrostic, or the section that springs to life +as a tree, or a house or a pot of flowers; finally, the completion +of the pattern, with clue after clue solved in rapid succession, or +the last few pieces tumbling into place. By accepting the challenge, +the tension, the concentration, the frustration, we heighten the +pleasure of the moment or revelation. The more difficult the puzzle, +the greater the tension--and so much greater the delights of solution. + -- John Ziman +~ +The most certain way to succeed is to always try one more time. + -- Thomas Edison +~ +(cat haiku) +hmmm... box is stinky. +where are those boots that he has? +they smell close enough. + -- fred t. hamster +~ +This one is from my dogs to my cats: + +I will sniff your butt +I like your litter box gifts +I call them ho-hos + -- GP Hardley +~ +You can drop humans anywhere and they'll thrive. +Only the rat does as well. + -- Jeannette Desor +~ +(cat haiku) +even kitty pure +in heart may become a wolf +when the catnip blooms + -- fred t. hamster +~ +housecat haiku of realization: + + birds flicker past me, +safely in their cage of glass. + wait! who's in the cage? + + -- fred t. hamster +~ +At this point I reveal myself in my true colours, as a stick-in-the-mud. +I hold a number of beliefs that have been repudiated by the liveliest +intellects of our time. I believe that order is better than chaos, +creation better than destruction. I prefer gentleness to violence, +forgiveness to vendetta. On the whole I think that knowledge is +preferable to ignorance, and I am sure that human sympathy is more +valuable than ideology. I believe that in spite of the recent triumphs +of science, men haven't changed much in the last two thousand years; and +in consequence we must still try to learn from history. History is +ourselves. I also hold one or two beliefs that are more difficult to put +shortly. For example, I believe in courtesy, the ritual by which we +avoid hurting other people's feelings by satisfying our own egos. And I +think we should remember that we are part of a great whole, which for +convenience we call nature. All living things are our brothers and +sisters. Above all, I believe in the God-given genius of certain +individuals, and I value a society that makes their existence possible. + -- Kenneth Clark, from "Civilization" +~ +If you want to feel proud of yourself, you need to do things of which +to feel proud. Feelings follow actions. + -- Oseola McCarty +~ +To learn and from time to time to apply what +one has learned--isn't that pleasure? + -- Confucious, 500 B.C. +~ +top 6 rejected ingredients in ben & jerry's "phish food" flavor: + +6. capers and whitefish +5. caramel-covered seaweed nuggets +4. bloodworms +3. magic mushroom ripple +2. oyster crackers + +and the number one rejected ingredient... + +1. barnacle crunch + + -- fred t. hamster +~ +top 6 reasons to own a house rabbit: + +6. learning to splice electrical wires +5. free compost everywhere in the house +4. having your ankles bitten and scratched during rutting season +3. finding out what night feces are and that special feeling they give + when between your toes +2. having guests ask "what is that incredible stench?" + +and the number one reason to own a house rabbit... + +1. if all else fails, there's always hasenpfeffer. + + -- fred t. hamster +~ +My childhood was a period of waiting for the moment when I could send +everyone and everything connected with it to hell. + -- Igor Stravinsky +~ +Education is an admirable thing, but it is well to remember from time to +time that nothing that is worth knowing can be taught. + -- Oscar Wilde +~ +We will take almost any kind of criticism except the observation that +we have no sense of humor. A man will admit to being a coward, a liar, a +thief, an adulterer, a poor mechanic, or a bad swimmer, but tell him that +he has a dreadful sense of humor and you might as well have slandered his +mother. Even if he is civilized enough to pretend to make light of your +statement, he will still secretly believe that he has, not only a good +sense of humor, but one superior to most. This is all the more surprising +when you consider that not one person in a million can give you any kind of +intelligent answer as to what humor is or why he or she laughs. + -- Steve Allen +~ +To write weekly, to write daily, to write shortly, to write +for busy people catching trains in the morning or for tired +people coming home in the evening, is a heartbreaking task +for men who know good writing from bad. + -- Virginia Woolf +~ +ugh. all thoughts scrambled. now eating them on toast. -- fred t. hamster +~ +I love being a writer. What I can't stand is the paperwork. + -- Peter De Vries +~ +Housework can kill you if done right. + -- Erma Bombeck +~ +All human evil comes from a single cause-- +man's inability to sit still in a room. + -- Blaise Pascal (spoken like a true nun) +~ +An idealist is one who, on noticing that a rose smells better than a +cabbage, concludes that it will also make better soup. + -- H. L. Mencken +~ + School curricula reinforce the impression that logical subjects like math +and science require starting with basics and progressively adding more +sophisticated conclusions and applications. But the very nature of logical +laws make it equally feasible to work backward from conclusions, or +observations, to hypotheses. Deduction and induction are entirely +complementary. + In reality, scientists and mathematicians do not do their crafts in the +linear, progressive way their subjects are usually taught. Practitioners +commonly start with a flash of insight (the stereotypical light bulb lighting), +a hunch, a dream, a guess, an elaborate hypothesis or postulate, and then work +backward, forward, and around it to try to make it fit with established +knowledge. Physicists or engineers commonly try using complex mathematical +gadgets to solve the problems that interest them without knowing or caring how +the math was logically derived. Experimenters tinker in laboratories and make +surprising discoveries that theoreticians then labor to try to explain +logically. Alternatively, theorists like Einstein come up with wild new +theories like relativity that experiments may have to struggle for decades to +find a way to test and prove. Scientific knowledge does not grow incrementally +down a predictable track. Rather it grows volcanolike, sometimes oozing in +patient rivulets, sometimes erupting in fiery ferment, and occasionally +exploding, blowing away the rock of established truth. + Pedantic, linear teaching rarely conveys the true drama and mystery of the +human quest for knowledge. School plods where human imagination naturally +leaps. + -- Lewis J. Perelman, from "School's Out" +~ +the people who are the most in need of help are often the +same ones who cannot receive it. for example, those who +believe all human minds are isolated and who have built their +lives on that principle can only rarely come to appreciate the +connections between us. their loneliness and self-imposed +isolation seems to be the normal state of affairs to these +people; little do they realize that if they relaxed their +armored ego barrier, then the thoughts and emotions of others +would start to be perceptible. they desperately cling to the +belief system that IS their problem in such a way that they +cannot see the solution, nor can they even believe that the +solution exists. should these people consciously strive to +relax that barrier, it would dissolve quickly. many just +cannot do that and never will. many can however, and can +relax in the invisible web of human mentation that underlies +all of our consciousnesses like a huge, comfortable and active +safety net. + -- fred t. hamster +~ +All progress is based upon a universal innate desire on the part +of every organism to live beyond its income. + -- Samuel Butler +~ + Attempts to mimic the mind of man are as yet in their infancy. +The road, however, is open, and it conjures up thoughts which are +exciting but also in some ways frightening. What if man eventually +were to produce a mechanical creature, with or without organic +parts, equal or superior to himself in all respects, including +intelligence and creativity? Would it replace man, as the superior +organisms of the earth have replaced or subordinated the less well- +adapted in the long history of evolution? + It is a queasy thought: that we represent, for the first time +in the history of life on the earth, a species capable of bringing +about its own possible replacement. Of course, we have it in our +power to prevent such a regrettable denouement by refusing to build +machines that are too intelligent. But it is tempting to build them +nevertheless. What achievement could be grander than the creation +of an object that surpasses the creator? How could we consummate +the victory of intelligence over nature more gloriously than by +passing on our heritage, in triumph, to a greater intelligence--of +our own making? + -- Isaac Asimov, in "Asimov's Guide to Science" +~ +Home is the place where, +when you have to go there, +they have to take you in. + -- Robert Frost +~ +The closer that journalism has approached the standing of an +authentic profession, oddly enough, the less attractive its +individual practitioners appear to have become in the public +mind. This irony is traceable in large measure to the +distinguished work of the press in its persistent recording +of the futility and manifold injustices of the Vietnam War and +its disclosures in the Watergate scandal. Those protracted +traumas scarred the national psyche, which in turn found +solace by blaming the press for battening on the troubles it +apparently delighted in reporting. Reporters came to be seen +as arrogant in the conduct of their duties, habitually +adversarial in posture, often insensitive, and unapologetic +about substituting their own right to demand the truth for the +public's right not to be stalked ruthlessly like so much grist +for the milling of tomorrow's headlines. This impression has +been deepened by the coarseness of television news, which is +essentially a headline service trading on its emotional graphic +appeal and dealing so superficially with events and so rarely +with the complex issues behind them that its effect is to +divert rather than to inform; TV remains primarily an +entertainment medium that has not challenged the role of +newspapers as the prime recorders of the community's serious +business. But because we see television correspondents +questioning the President or putting it to the police chief, +they become personalities in their own right, far more imposing +than a faceless byline over a printed story. + -- Richard Kluger, from "The Paper: The Life and Death of the New + York Herald Tribune" +~ +The fat cat on the mat may seem to dream +Of nice mice that suffice for him, or cream. + -- J. R. R. Tolkien +~ +Baseball is almost the only orderly thing in a very +unorderly world. If you get three strikes, even the +best lawyer in the world can't get you off. + -- Bill Veeck +~ + A man will never become a philosopher by worrying forever +about the writings of other men, without ever raising his own +eyes to nature's works in the attempt to recognize there the +truths already known and to investigate some of the infinite +number that remain to be discovered. + -- Galileo Galilei +~ +A difference of taste in jokes is a great strain on the affections. + -- George Elliot +~ +One doesn't discover new lands without consenting to lose sight +of the shore for a very long time. + -- Andre Gide +~ +Today in America--the child of European imperialism--a new revolution +is rising. It is the revolution of our time. It is the only revolution +that involves radical, moral, and practical opposition to the spirit of +nationalism. It is the only revolution that, to that opposition, joins +culture, economic and technological power, and a total affirmation of +liberty for all in the place of archaic prohibitions. It therefore +offers the only escape for mankind today; the acceptance of technological +civilization as a means and not as an end, and--since we cannot be saved +either by the destruction of civilization or by its continuation--the +development of the ability to reshape that civilization without +annihilating it. + -- Jean-Francois Revel +~ +There is a great man who makes every man feel small. +But the real great man is the man who makes every man feel great. + -- G. K. Chesterton +~ +What usually happens in the educational process is that the faculties +are dulled, overloaded, stuffed and paralyzed so that by the time most +people are mature they have lost their innate capabilities. + -- R. Buckminster Fuller +~ +Our memories are card indexes--consulted, and then put back in +disorder, by authorities whom we do not control. + -- Cyril Connolly +~ +More computing sins are committed in the name of efficiency +(without necessarily achieving it) than for any other single +reason--including blind stupidity. + -- W. A. Wulf +~ +We should forget about small efficiencies, say about 97% of +the time: premature optimization is the root of all evil. + -- Donald Knuth +~ +The best is the enemy of the good. + -- Voltaire +~ +The first rule of intelligent tinkering is to save all the parts. + -- Paul Ehrlich +~ +A billion here, a billion there, and pretty soon you're talking big money. + -- Senator Everett M. Dirksen +~ + During the last three decades, neuroscientists throughout the world +have probed the nervous system in fascinating detail and have learned a +great deal about the laws of mental life and about how these laws emerge +from the brain. The pace of progress has been exhilarating, but--at the +same time--the findings make many people uncomfortable. It seems somehow +disconcerting to be told that your life, all your hopes, triumphs and +aspirations simply arise from the activity of neurons in your brain. But +far from being humiliating, this idea is ennobling, I think. + Science--cosmology, evolution and especially the brain sciences--is +telling us that we have no privileged position in the universe and that our +sense of having a private nonmaterial soul "watching the world" is really an +illusion (as has long been emphasized by Eastern mystical traditions like +Hinduism and Zen Buddhism). Once you realize that far from being a +spectator, you are in fact part of the eternal ebb and flow of events in the +cosmos, this realization is very liberating. Ultimately this idea also +allows you to cultivate a certain humility--the essence of all authentic +religious experience. + -- V. S. Ramachandran, in "Phantoms in the Brain" +~ +A celebrity is one who is known to many persons +he is glad he doesn't know. + -- H. L. Mencken +~ +Money is a singular thing. It ranks with love as man's greatest +source of joy, and with death as his greatest source of anxiety. + -- John Kenneth Galbraith +~ +By his very success in inventing labor-saving devices, modern man +has manufactured an abyss of boredom that only the privileged classes +in earlier civilizations have ever fathomed. + -- Lewis Mumford +~ +Scientists, who nearly always speak extemporaneously in public +presentations, note that humanists almost always read papers +at professional meetings, and rarely show slides--except for +art historians, who always use two screens simultaneously--even +for the most visual subjects. Why, 'we' ask, do 'they' not +realize that written and spoken English are different languages, +and that very few people can read well in public--a particular +irony since humanists supposedly hold language as their primary +tool of professional competence. But 'they,' on the other hand, +rightly ridicule 'our' tendencies to darken a lecture room even +before we reach the podium and to rely almost entirely upon a +string of pictures thereafter. A stale joke proclaims that if +Galileo had first presented the revolutionary results of Siderius +Nuncius as a modern scientific talk, his opening line could only +have been: 'first slide please.' + -- R. R. Shearer and S. J. Gould, in "Science," 5 Nov 99 +~ +Prowling his own quiet backyard or asleep by the fire, +he is still only a whisker away from the wilds. + -- Jean Burden +~ + Futurology is a fashion. The approach of the end of the current +millennium has stimulated it. But it looks like a fashion in decline. It +seems to have peaked when public interest in the future was enlivened by +debate between scientific perfectibilians and apocalyptic visionaries. The +optimists predicted a world made easy by progress, lives prolonged by +medical wizardry, wealth made universal by the alchemy of economic growth, +society rectified by the egalitarianism of technologically prolonged +leisure. The pessimists foresaw nuclear immolation or population explosion +or a purgative world revolution -- a cosmic struggle reminiscent of the +millennium of Christian prophetic tradition -- which would either save or +enslave mankind. + No one gets excited by such visions today. Scientific progress has +been, at best, disappointing -- encumbering us with apparently insoluble +social and moral problems, or else, at worst, alarming -- threatening us +with the mastery of artificially intelligent machines or genetically +engineered human mutants. Economic growth has become the bogey of the +ecologically anxious. Meanwhile, world revolution and the nuclear holocaust +have been postponed, and apocalyptic prophecy has resorted to forebodings -- +variously unconvincing or uncompelling -- about ecological cataclysms. +Proliferation of nuclear weapons and the discovery that even peaceful +nuclear installations can poison great parts of the world has, in some ways, +made disaster impend more darkly; but lingering extinction and little local +nuclear holocausts seem to lack, in public esteem the glamour of a sudden +and comprehensive armageddon. The future has become depressing rather than +dramatic, and futurology has lost allure. + -- Felipe Fernandez-Armesto, in "Millennium: a History of the Last + Thousand Years", 1995. +~ +We all love to instruct, though we can teach only what is not worth knowing. + -- Jane Austen, in "Pride and Prejudice" +~ +(cat haiku) +liquid ball of fur +dances with a beam of light, +never catches it. +~ +Nothing is really work unless you would rather be doing something else. + -- James M. Barrie +~ +Anyone can do any amount of work provided it isn't the work he is +supposed to be doing at that moment. + -- Robert Benchley +~ +(cat haiku) +He flies without wings, +Fast as a shadow can go. +Black slash on white snow. +~ +(cat haiku) +Once quick with rat life, +Now just carnage: tail, head, fur. +Two-scoop burial. +~ +(cat haiku) +Willy stalks field mice, +Wild Bill to small buffalo. +Kills them just for show. +~ +(cat haiku) +Cats are like haiku: +Subtle, delicate, perfect +Communicators. +~ +Once fat with meat. +Now just feet. +Rat food and bat food +Supplement cat food. +~ +The best way to be boring is to leave nothing out. + -- Voltaire +~ +I would rather be governed by the first two thousand people in the +Boston telephone directory than by the first two thousand people on the +faculty of Harvard University. + -- William F. Buckley +~ + Civility will always have its critics. In 1997, when New York's +chief judge proposed rules requiring lawyers on opposing sides to be civil +to each other, the prominent divorce lawyer Raoul Felder wrote a caustic +response in the New York Times. "If lawyers truly care about the causes +they represent, they should, on occasion, get hot under the collar, raise +their voices, become pugnacious," he wrote. Conflict, argued Felder, is +what the legal profession is about. Civility, he concluded, "may not always +be the right reaction in an adversarial courtroom." + This argument reminds me of the commentary by Ed Rollins that it +was his job to diminish the reputation of his client's opponent. Nastiness, +in other words, is not merely the option but the responsibility of the +political profession. The legal profession too: "I have never heard a +client complain that his or her lawyer was rude," Felder tells us. In both +cases, law and politics, rudeness is evidently justified on the ground that +rudeness is what the client is paying for. + As any student of civility would, I find this a fascinating notion: +that there are professions for which incivility is a requirement. I suppose +I disbelieve it; or, rather, if there are such professions, I am skeptical +of their morality, because they fail to convey a message that we are, all of +us, not lone drivers but fellow passengers. It may be that law and politics +seem so dismally rude because their principal ethic is merely one of +victory, an ethic materially enriching and emotionally satisfying, but +morally unimportant. If lawyers are paid to be rude and political +consultants to be nasty, and if their incivility is linked to the fact that +they are also paid to win, we should scarcely be surprised that professional +athletes find it comfortable to brawl with fans, spit on umpires, take bites +out of ears, and, in one unfortunate case nicknamed "Assassin," specialize +in injuring fellow football players. After all, the athletes want to win too. + Some etiquette. Some democracy. + -- Stephen L. Carter, in "Civility", 1998 +~ +A cat is a lion in a jungle of small bushes. + -- Indian Proverb +~ +A cat pours his body on the floor like water. It is restful just to see him. + -- William Lyon Phelps +~ +Nothing is more dangerous than an idea, when you have only one idea. + -- "Alain" [Emile Chartier] +~ + Newspapers have changed their character during my lifetime. They used +to be the principal carriers of the world's news, but television holds that +position now. Television, however, has serious limitations; it is a visual +medium, and it is dominated by the principle that nothing is news unless you +can take a picture of it. It is here that the newspapers still hold their +own; so much of what goes on in the political world cannot be effectively +photographed; statesmen, in their expensive but uninteresting clothes, make +very poor TV and their prolonged deliberations are dull when we see them on +the box. Politics must be interpreted, and newspapers have become their +untiring interpreters... Intelligence, not perhaps on its highest level +but far beyond the sheer emotionalism of TV, has found its refuge in the +newspapers. + -- Robertson Davies, from "The Merry Heart: Reflections on Reading, + Writing, & The World of Books". +~ +To improve communications, work not on the utterer but on the recipient. + -- Peter Drucker +~ + Despite the incorporated homicide or suicide called war, despite the +crimes of individuals, the natural conflicts of domestic parties and +national ambitions, I believe, after fifty years of studying history, +that man is physically, mentally, and morally better, on the average, +than at any time in the past; that our poverty, so disgraceful amid our +unprecedented wealth, is not so shameless as the slavery that supported +an enfranchised minority in Periclean Athens or Augustan Rome; that our +marital chaos and moral laxity are no worse than in the England of +Charles II or the France of Louis XV; that more good books are being +published than ever before and more widely read; and that art will soon +rise to a new level of self-discipline and social significance. + I mourn the ugly slums of our cities and the distress of those who +cannot find work for their hands to do; but I see realized around me, +in an unparalleled proportion of our people, such a spread of home +ownership, family income, physical comforts, educational opportunities, +political freedom, and scientific powers as would amaze and gladden our +Founding Fathers if they could return to see what their progeny and +their institutions have done... This time, this moment, is as good as +any that ever was, and is incomparably more wonderful. + -- Will Durant +~ +I am enough of an artist to draw freely upon my imagination. +Imagination is more important than knowledge. +Knowledge is limited. Imagination encircles the world. + -- Albert Einstein +~ +You do not lead by hitting people over the head. +That's assault, not leadership. + -- Dwight D. Eisenhower +~ +When elephants fight, only the grass gets hurt. + -- Swahili Saying +~ +Men who know the same things are not long the best company for each other. + -- Ralph Waldo Emerson +~ +The gingham dog and the calico cat +Side by side on the table sat; +'Twas half-past eight and (what do you think!) +Nor one nor t'other had slept a wink! + -- Eugene Field +~ +bob1: I was going to just sublime into the roll + and see what I could get.... +fred: slime, you mean? you're not going to evaporate, right? +bob1: Whatever, formally declare I shall be in control + and see if anyone listens... +fred: that's the doctrine of the supine? +bob1: By sublime I meant so slowly that you don't notice. +fred: the lime doctor sublimes merging into the supine + mesmerized accomplished cow-orking accomplices slimefully. +bob1: mmm I see. Anyway I must rejoin the family and stuff. + Perhaps I shall be back later. I must get into work early + tomorrow if I want all this to work. +fred: by the time i spline my spleen in the stream + i will have strummed the strumpet's stoking stork. +bob1: Holy f*ck batman. +~ +There is nothing to be learned from history anymore. +We're in science fiction now. + -- Allen Ginsberg +~ + One of the interesting features of communication is that, broadly +speaking, to be perceived, information must reside in more than one context. +We know what something is by contrast with what it is not. Silence makes +musical notes perceivable; conversation is understood as a contrast of +contexts, speaker and hearer; words, breaks and breaths. In turn, in order +to be meaningful, these contexts of information must be relinked through +some sort of judgment of equivalence or comparability. This occurs at all +levels of scale, and we all do it routinely as part of everyday life. + None of this is new in theories of information and communication: we +have long had models of signals and targets, background, noise and filters, +signals, and quality controls. We are moving this insight here to the level +of social interaction. People often cannot see what they take for granted +until they encounter someone who does not take it for granted. + -- Geoffrey C. Bowker & Susan Leigh Star, from "Sorting Things Out: + Classification and Its Consequences," (MIT Press). +~ +If there is technological advance without social advance, there is, +almost automatically, an increase in human misery. + -- Michael Harrington +~ +Nothing great in the world has been accomplished without passion. + -- Hegel +~ +When people are free to do as they please, they usually imitate each other. + -- Eric Hoffer +~ +Facts do not cease to exist because they are ignored. + -- Aldous Huxley +~ +He was a very valiant man who first adventured on eating of oysters. + -- King James I +~ +A man of genius makes no mistakes. His errors are volitional, and are +the portals of discovery. + -- James Joyce +~ +But this 'long run' is a misleading guide to current affairs. +In the long run we are all dead. + -- John Maynard Keynes +~ +Education is a crutch with which the foolish attack the wise to prove +that they are not idiots. + -- Karl Kraus +~ +Advertising may be described as the science of arresting the human +intelligence long enough to get money from it. + -- Stephen Leacock +~ +I don't like money actually, but it quiets my nerves. + -- Joe Louis +~ +(cat haiku) +Black heart on white fur +Green eyes ... last sight for poor rat, +Plaything of a God. +~ +(cat haiku) +Black face, cold blue eyes +fish pond, golden fish surprise. +Wet paws ... not water! +~ +spelling offers more +room for controversy than +the world's religions + -- fred t. hamster +~ +cappuccino is +caffeination in its most +excellent format + -- fred t. hamster +~ +buddha, dharma and +sangha are the three jewels +a buddhist cleaves to + -- fred t. hamster +~ +millennium, yes! +charging into the twenty-first +century, for sure + -- fred t. hamster +~ +fred barks, lily hides +two dogs of the same breeding +why so different? +~ +haikus force structure +on an otherwise very +chaotic word world + -- fred t. hamster +~ +kevin, kory, kyle; +three celtic names held by three +amazing nephews + -- fred t. hamster +~ + The most fruitful and natural exercise of the mind, in my opinion, is +conversation. I find the use of it more sweet than of any other action of +life; and for that reason it is that, if I were now compelled to choose, I +should sooner, I think, consent to lose my sight, than my hearing and +speech... The study of books is a languishing and feeble motion that heats +not, whereas conversation teaches and exercises at once. If I converse with +an understanding man, and a rough disputant, he presses hard upon me and +pricks me on both sides; his imaginations raise up mine to more than +ordinary pitch; jealousy, glory, and contention, stimulate and raise me up +to something above my self... + -- Michel Montaigne +~ +(cat haiku) +only the choicest +of meats, kibbles and catnip +will i deign to sniff. + -- fred t. hamster +~ +(cat haiku) +fine fur flies from me +filling all of your clean rooms +i am super fluff! + -- fred t. hamster +~ +(cat haiku) +watery sky leads +to quizzical expression: +"fix outdoors for me!" + -- fred t. hamster +~ +(cat haiku) +inclement weather +bores more cats even than own +internal ennui. + -- fred t. hamster +~ +(cat haiku) +Open the door, Man! +I wish to go out ... what's this? +Wet fur? I think not. +~ +(cat haiku) +Monsoon for felines. +They ground when wet. No static. +Rainy faced disgrace. +~ +Distrust any enterprise that requires new clothes. + -- H. D. Thoreau +~ +Clothes make the man. Naked people have little or no influence on society. + -- Mark Twain +~ +We are not sure that words can always save lives, but we know that +silence can certainly kill. + -- James Orbinski of "Doctors Without Borders", winners of the 1999 Nobel + Peace Prize +~ +The masses, by definition, neither should nor can direct their own +personal existence, and still less rule society in general. + -- Jose Ortega y Gasset +~ +It is precisely because man's vital time is limited, precisely because +he is mortal, that he needs to triumph over distance and delay. For an +immortal being, the motor-car would have no meaning. + -- Jose Ortega y Gasset +~ +We live at a time when man believes himself fabulously capable of +creation, but he does not know what to create. Lord of all things, +he is not lord of himself... Hence the strange combination of a +sense of power and a sense of insecurity. + -- Jose Ortega y Gasset +~ +The mass-man is he whose life lacks any purpose, and simply goes drifting +along. Consequently, though his possibilities and his powers be enormous, +he constructs nothing. And it is this type of man who decides in our time... + -- Jose Ortega y Gasset +~ +In the schools, which were such a source of pride to the last century, it +has been impossible to do more than instruct the masses in the technique +of modern life; it has been found impossible to educate them. + -- Jose Ortega y Gasset +~ + Like cars over the years, computers are getting easier to use, and in +some respects the changes are analogous to the placement of gauges by idiot +lights. And, like drivers who favor gauges, some computer users belittle the +trend toward easy-to-use systems. These users appear to thrive on +complexity. They are often experts who enjoy getting the most out of their +computers; they view computing as an end rather than a means. + Unlike cars, easy-to-use computers aren't called idiot-proof, they're +called user-friendly. As a marketing achievement, this terminology ranks +with 'Palmetto bugs,' which is a term used in Florida-at the instigation of +some genius in the real estate industry, I'm told-for large, flying +cockroaches." + -- John Shore, from "The Sachertorte Algorithm: And Other Antidotes + to Computer Anxiety". +~ + Ads manipulate us into being dissatisfied... We are encouraged to feel +anxious or sorry for ourselves. Advertising teaches us to live on the level +of the pleasure principle. This leads to impulse-control problems and to +feelings of entitlement. "I am the center of the universe and I want what I +want now." This thinking creates citizens who are vulnerable to quick fixes. +It leads to citizens filled with self-pity, which is the flip side of +entitlement. + Advertising teaches that people shouldn't have to suffer, that pain +is unnatural and can be cured. They say that effort is bad and convenience +is good and that products solve complex human problems. Over and over people +hear that their needs for love, security and variety can be met with +products. They may reject the message of any particular ad, but over time +many buy the big message -- buying products is important. + -- Mary Pipher, in "The Shelter of Each Other: Rebuilding Our Families" +~ +I have no use for bodyguards, but I have a very special use for two +highly trained certified public accountants. + -- Elvis Presley +~ +Punctuality is the virtue of the bored. + -- Evelyn Waugh +~ +Three o'clock is always too late or too early for anything you want to do. + -- Jean-Paul Sartre +~ +Skill without imagination is craftsmanship and gives us many useful +objects such as wickerwork picnic baskets. Imagination without skill +gives us modern art. + -- Tom Stoppard +~ +What is this talk of "release?" We do not make software "releases." +Our software "escapes" leaving a bloody trail of designers and quality +assurance people in its wake. + -- MoncriefJM@gvl.esys.com, as seen on the on the PerlTK mailing list +~ +It is truly enough said that a corporation has no conscience. But a +corporation of conscientious men is a corporation with a conscience. + -- Henry David Thoreau +~ +(cat haiku) +Headfull of acid, +Cat on the carpet, melting... +Here pussy. Here God! +~ +(cat haiku) +vomit kitty, now! +you don't eat all my buds, cat. +i'll smoke you instead. +~ +The future is made of the same stuff as the present. -- Simone Weil +~ +he's such a tight ass that when he breaks wind all the dogs howl. + -- fred t. hamster +~ +See the kitten on the wall, +Sporting with the leaves that fall, +Withered leaves, one, two and three +Falling from the elder tree, +Through the calm and frosty air +Of the morning bright and fair. + -- William Wordsworth +~ +But the Kitten, how she starts, +Crouches, stretches, paws and darts! + -- William Wordsworth +~ + All those who succeed in America -- no matter what their circle of +origin or their sphere of action -- are likely to become involved in the +world of the celebrity... This world has not been built from below, as a +slow and steady linking of local societies and metropolitan 400s. It has +been created from above... With the incorporation of the economy, the +ascendancy of the military establishment, and the centralization of the +enlarged state, there have arisen the national elite, who, in occupying the +command posts of the big hierarchies, have taken the spotlight of publicity +and become subjects of the intensive build-up. At the same time, with the +elaboration of the national means of mass communication, the professional +celebrities of the entertainment world have come fully and continuously into +the national view. As personalities of national glamour, they are at the +focal point of all the means of entertainment and publicity. Both the +metropolitan 400 and the institutional elite must now compete with and +borrow prestige from these professionals in the world of the celebrity. + But what are the celebrities? The celebrities are The Names that +need no further identification. Those who know them so far exceed those of +whom they know as to require no exact computation. Wherever the celebrities +go, they are recognized, and moreover, recognized with some excitement and +awe. Whatever they do has publicity value. More or less continuously, over a +period of time, they are the material for the media of communication and +entertainment. And, when that time ends -- as it must -- and the celebrity +still lives -- as he may -- from time to time it may be asked, "Remember +him?" That is what celebrity means. + -- C. Wright Mills, from "The Power Elite," 1956 +~ +I'm not really very good at what I do, but I'm very +popular, because I return my pages. + -- Unidentified computer support technician +~ +Experience is that marvelous thing that enables you to +recognize a mistake when you make it again. + -- Franklin P. Jones +~ +Nobody roots for Goliath. + -- Wilt Chamberlain +~ +(cat haiku) +Gentle pussy, bit +By a bat. Rabid, frothing... +Animal control! +~ +I have a dream that one day this nation will rise up +and live out the true meaning of its creed: "We hold +these truths to be self-evident, that all men are +created equal." + -- Martin Luther King Jr. +~ + We generally think of music as a product of art rather than commerce +or technology. It depends, in fact, on all three. Together, these great +dynamic systems match individual creativity and individual desire. They +thus generate change, variety and an endless array of critics--all +determined that music, like the rest of society, should conform to +"one best way". + That would be a terrible deal. By tolerating music that pleases +others but not ourselves, we preserve a system that has delivered a +historical wonder... We can listen to perfectly performed music to suit +any mood or taste at any time, music that moves us in ways particular to our +individual senses and our individual souls. + -- Virginia Postrel +~ +There are three rules for writing the novel. +Unfortunately, no one knows what they are. + -- W. Somerset Maugham +~ +His lack of education is more than compensated for by his +keenly developed moral bankruptcy. + -- Woody Allen +~ +How is it possible to find meaning in a finite world, +given my waist and shirt size? + -- Woody Allen +~ +I can't listen to that much Wagner. I start getting +the urge to conquer Poland. + -- Woody Allen (Manhattan Murder Mystery) +~ +two databases +become unified as one +much damage ensues + -- fred t. hamster +~ +urgh, mutual friends have we few +of whom hooktown still has purview. +lest we examine too far +i wonder regarding your car-- +could it survive a furious country drive? +for to hamster freehold must you arrive. +many entertainments have we here... +movies, a rabbit and beer. +perhaps what you were chasing, +twain's fuzz is constantly abasing. +to extend no further this diatribe, +why don't you drop over, or "arribe"? + -- fred t. hamster +~ +If no one ever took risks, Michelangelo would +have painted on the Sistine floor. + -- Neil Simon +~ +It isn't what we don't know that gives us trouble, +it's what we know that ain't so. + -- Will Rogers +~ +I don't know the key to success, but the key to failure +is trying to please everyone. + -- Bill Cosby +~ + Today, when man seems to have reached the beginning of a new, +richer, happier human era, his existence and that of the generations to +follow is more threatened than ever. How is this possible? + Man had won his freedom from clerical and secular authorities, he +stood alone with his reason and his conscience as his only judges, but he +was afraid of the newly won freedom; he had achieved 'freedom from' -- +without yet having achieved 'freedom to' -- to be himself, to be productive, +to be fully awake. Thus he tried to escape from freedom. His very +achievement, the mastery over nature, opened up the avenue for his escape. + In building the new industrial machine, men became so absorbed in +the new task that it became the paramount goal of his life. His energies, +which once were devoted to the search for God and salvation, were now +directed toward the domination of nature and ever-increasing material +comfort. He ceased to use production as a means for a better life, but +hypostatized it instead to an end in itself, an end to which life was +subordinated. In the process of an ever-increasing size of social +agglomerations, man himself became a part of the machine, rather than its +master. He experienced himself as a commodity, as an investment; his aim +became to be a success, that is, to sell himself as profitably as possible +on the market. His value as a person lies in his salability, not in his +human qualities of love, reason or in his artistic capacities. Happiness +becomes identical with consumption of newer and better commodities, the +drinking in of music, screen plays, fun, sex, liquor and cigarettes. Not +having a sense of self except the one which conformity with the majority can +give, he is insecure, anxious, depending on approval. He is alienated from +himself, worships the product of his own hands, the leaders of his own +making, as if they were above him, rather than made by him. He is in a +sense back where he was before the great human evolution began in the second +millennium B.C. + -- Erich Fromm, "The Sane Society" +~ +If I could I would always work in silence and obscurity, +and let my efforts be known by their results. + -- Emily Bronte +~ +The fox knows many things -- the hedgehog knows one big thing. + -- Archilochus +~ +I've learned not to put things in my mouth that are bad for me. + -- Monica Lewinsky on CNN's Larry King Live discussing her + miraculous Jenny Craig weight-loss. +~ +Both the assembling and the distribution of knowledge in the world +at present are extremely ineffective, and thinkers of the forward- +looking type whose ideas we are now considering, are beginning to +realize that the most hopeful line for the development of our racial +intelligence lies rather in the direction of creating a new world +organ for the collection, indexing, summarizing and release of +knowledge, than in any further tinkering with the highly conservative +and resistant university system, local, national, and traditional in +texture, which already exists. + -- H. G. Wells (1937) +~ +a haiku for object bus overload... + +those data bursts have +overturned my info cart-- +stranded on the net. + + -- fred t. hamster +~ +Any intelligent fool can make things bigger, more complex, +and more violent. It takes a touch of genius--and a lot +of courage--to move in the opposite direction. + -- E. F. Schumacher +~ +It is amazing how much one can learn from somebody +who is not generally thought of as successful. + -- Michael Korda +~ +Half the world is composed of people who have +something to say and can't, and the other have +nothing to say and keep on saying it. + -- Robert Frost +~ +Security is mostly a superstition. It does not exist in nature... +Life is either a daring adventure or it is nothing. + -- Helen Keller +~ +His tongue is by turns a sponge, a brush, a comb. +He cleans himself, he smoothes himself, +he knows what is proper. + -- Hippolyte Taine +~ +To turn $100 into $110 is work. +To turn $100 million into $110 million is inevitable. + -- Edgar Bronfman +~ +A problem well stated is a problem half solved. + -- Charles F. Kettering +~ +The spirit of the West, the modern spirit, is a Greek discovery +and the place of the Greeks is in the modern world. + -- Edith Hamilton (1867-1963) +~ +fish flavored flappers +squirm happily under tongue, +cloak orgasmic clit. +~ +There are no whole truths: all truths are half-truths. It is trying +to treat them as whole truths that plays the devil. + -- Alfred North Whitehead +~ +Somewhere on this globe, every ten seconds, there is a woman +giving birth to a child. She must be found and stopped. + -- Sam Levenson +~ +The cat has complete emotional honesty -- +an attribute not often found in humans. + -- Ernest Hemingway +~ +no morning coffee... +gray matter is not present, +dial tone in my head. + -- fred t. hamster +~ +choad /chohd/ /n./ + Synonym for 'penis' used in alt.tasteless and popularized by the denizens +thereof. They say: "We think maybe it's from Middle English but we're all +too damned lazy to check the OED." [I'm not. It isn't. --ESR] + This term is alleged to have been inherited through 1960s underground +comics, and to have been recently sighted in the Beavis and Butthead +cartoons. Speakers of the Hindi, Bengali and Gujarati languages have +confirmed that `choad' is in fact an Indian vernacular word equivalent +to `f*ck'; it is therefore likely to have entered English slang via the +British Raj. +~ +The race may not be to the swift nor the victory +to the strong, but that's how you bet. + -- Damon Runyon +~ +Basic research is when I'm doing what I don't know I'm doing. + -- Wernher von Braun +~ + For decades, people have warned that pervasive databanks and +surveillance technology are leading inevitably to the death of privacy and +democracy. But these days, many people who hear the word 'privacy' think +about those kooks living off in the woods with their shotguns: these folks +get their mail at post office boxes registered under assumed names, grow +their own food, use cash to buy what they can't grow for themselves, and +constantly worry about being attacked by the federal government-or by space +aliens. If you are not one of these people, you may well ask, "Why should I +worry about my privacy? I have nothing to hide." + The problem with this word 'privacy' is that it falls short of +conveying the really big picture. Privacy isn't just about hiding things. +It's about self-possession, autonomy, and integrity. As we move into the +computerized world of the twenty-first century, privacy will be one of our +most important civil rights. But this right of privacy isn't the right of +people to close their doors and pull down their window shades -- perhaps +because they want to engage in some sort of illicit or illegal activity. +It's the right of people to control what details about their lives stay +inside their own houses and what leaks to the outside... + Today, more than ever before, we are witnessing the daily erosion of +personal privacy and freedom. We're victims of a war on privacy that's being +waged by government eavesdroppers, business marketers, and nosy neighbors... +We know our privacy is under attack. The problem is that we don't know how +to fight back. + -- Simson Garfinkel, in "Database Nation: The Death of Privacy in + the 21st Century" +~ +A gentleman who had been very unhappy in marriage, married +immediately after his wife died: Johnson said, it was the +triumph of hope over experience. + -- James Boswell's "Life of Johnson" +~ +One evening while dozing in my armchair, I was roused +by the sound of the harpsichord. My cat had started +his musical stroll... I had a sheet of paper to hand, +and transcribed his composition. + -- Domenico Scarlatti +~ +Politics is the art of looking for trouble, finding it +everywhere, diagnosing it incorrectly, and applying +the wrong remedies. + -- Groucho Marx +~ +A hospital bed is a parked taxi with the meter running. + -- Groucho Marx +~ +Please accept my resignation. I don't want to belong to +any club that will accept me as a member. + -- Groucho Marx +~ +Let's find out what everyone is doing, +and then stop everyone from doing it. + -- A. P. Herbert +~ +I was to learn later in life that we tend to meet any +new situation by reorganizing, and a wonderful method +it can be for creating the illusion of progress while +producing confusion, inefficiency, and demoralization. + -- Petronius Arbiter, quoted in Robert Townsend's + "Up the Organization" +~ +With malice toward none; with charity for all; with firmness in +the right, as God gives us to see the right, let us strive on to +finish the work we are in; to bind up the nation's wounds; to care +for him who shall have borne the battle, and for his widow, and +his orphan -- to do all which may achieve and cherish a just, and +a lasting peace, among ourselves, and with all nations. + -- Abraham Lincoln +~ + As we approach a new century and a changing international economic +climate, we think that scientific and technological education should be our +highest priority, and yet these fields, at least the way they are practiced +today, only tangentially affect the heart and soul, where morality and +values are rooted, while music goes right to the heart. + Studying music, one learns about talent, thought, work, expression, +beauty, technique, collaboration, aesthetic judgment, inspiration, taste, +and a host of other elements that shape life in all its aspects. As we +learn to control our fingers, lips, and breath in making music, subliminally +music is shaping us, making us people of sensitivity and judgment. + -- Thomas Moore, in "The Re-Enchantment of Everyday Life" +~ +How frighteningly few are the persons whose death would +spoil our appetite and make the world seem empty. + -- Eric Hoffer +~ +A bad attitude is the worst thing that can happen to +a group of people. It's infectious. + -- Roger Allan Raby +~ +All that is gold does not glitter; not all who wander are lost. + -- J. R. R. Tolkien +~ +What is originality? Undetected plagiarism. + -- Dean William R. Inge (ed: what a psycho...) +~ +True genius doesn't fulfill expectations, it shatters them. + -- Arlene Croce +~ +Let me listen to me and not to them. + -- Gertrude Stein +~ +If like truth, the lie had but one face, we would be on better terms. +For we would accept as certain the opposite of what the liar would say. +But the reverse of truth has a hundred thousand faces and an infinite field. + -- Montaigne +~ +The truth is multi-faceted; no one person can see all of it and no single +viewpoint can capture all of it. This is why any attempt to record the +nature of reality in one majestic work of science or religion or philosophy +is doomed to fail; that grand catalog is a necessarily-flawed perspective +upon the shimmering mind-blower that is the full totality of truth. +Still, we must try to get our minds around it during our whole lives. + -- fred t. hamster +~ +I am a great believer, if you have a meeting, in knowing +where you want to come out before you start the meeting. +Excuse me if that doesn't sound very democratic. + -- Nelson Rockefeller +~ + Lawyers have, as Jonathan Swift observed, "a peculiar cant and +jargon of their own, that no other mortal can understand". They take care +to ensure that all legal business, including the drafting of legislation, is +conducted in this language "so that it will take thirty years to decide +whether the field left me by my ancestors for six generations belongs to me +or to a stranger three hundred miles off". This language, condemned by +Jeremy Bentham as "literary garbage", "lawyers' cant", and "flash language", +serves various purposes, none of them in the public interest. It unites +lawyers, distinguishing them from laymen. It makes the law mysterious and +incomprehensible to those laymen, thus ensuring a steady supply of work for +lawyers who are needed to interpret the language they have invented. The +language of the law fosters the illusion that legal problems are remediable +only by the application of the medicine of the specialist. Only a lawyer, +can resolve the complexities of the problem: "Better see a lawyer; don't +trust Whatsisname" (as the memorable Law Society advertisement warned +consumers). Legal language also enshrouds the law, hiding it from the +public it exists to serve. The idiom of the lawyer leads to public ignorance +of the content of the law (which paradoxically refuses to recognize that +ignorance of the law should be a defence), to uninformed criticism and to +unmerited praise. It provokes the indifference of too many laymen towards +the law and the contempt of so many litigants for a legal system they do not +understand. + -- David Pannick, barrister and Fellow of All Souls College, Oxford +~ +It usually takes me more than three weeks +to prepare a good impromptu speech. + -- Mark Twain +~ +Hollywood films, in general, either want to tell us a truth we +already know or a falsehood we want to believe in. + -- William Goldman +~ +The function of socialism is to raise suffering to a higher level. + -- Norman Mailer +~ + Men of Athens, fellow citizens, this is not a trial of Socrates, but +of ideas, and of Athens. You are not prosecuting me for any unlawful or +impious act against our city or its altars. No evidence of any such sort +has been brought against me. You are not prosecuting me for anything I did, +but for what I have said and taught. You are threatening me with death +because you do not like my views and my teaching. This is a prosecution of +ideas and that is something new in our city's history. In this sense, +Athens is in the dock, not Socrates. Each of you, as my judges, is a +defendant. + Let me be frank. I do not believe in your so-called freedom of +speech, but you do. I believe the opinions of ordinary men are only beliefs +without substance, pale shadows of reality, not to be taken seriously, and +only likely to lead a city astray. I think it absurd to encourage the free +utterance of unfounded or irrational opinions, or to base civic policy on a +count of heads, like cabbages. Hence I do not believe in democracy. But +you do. This is your test, not mine. How can you boast of your free speech +if you suppress mine? + The test of truly free speech is not whether what is said or taught +conforms to any rule or ruler, few or many. Even under the worst dictator, +it is not forbidden to agree with him. It is the freedom to disagree that +is freedom of speech. This has been the Athenian rule until now, the pride +of our city, the glory on which your orators dwell. Will you turn your back +on it now? Ideas are not as fragile as men. They cannot be made to drink +hemlock. My ideas--and my example--will survive me. But the good name of +Athens will wear a stain forever, if you violate its traditions by +convicting me. The shame will be yours, not mine. + -- I. F. Stone, suggesting a defense for Socrates +~ +I do not seek. I find. + -- Pablo Picasso +~ +The one thing that is certain is that anyone who uses the phrase +"outside the box" is as deeply inside the box as a person can be. + -- Michael Lewis +~ +I can't understand why a person will take a year to write a novel +when he can easily buy one for a few dollars. + -- Fred Allen +~ + Many of my friends are under the impression that I write these humorous +nothings in idle moments, when the wearied brain is unable to perform the +serious labors of the economist. My own experience is exactly the other way. +The writing of solid, instructive stuff, fortified by facts and figures, is +easy enough. There is no trouble in writing a scientific treatise on the +folklore of Central China, or a statistical inquiry into the declining +population of Prince Edward Island. But to write something out of one's own +mind, worth reading for its own sake, is an arduous contrivance only to be +achieved in fortunate moments, few and far between. Personally, I would sooner +have written Alice in Wonderland than the whole Encyclopedia Britannica. + -- Stephen Leacock +~ +It's easier to find a travel companion than to get rid of one. + -- Art Buchwald +~ +God must love the rich or he wouldn't have divided +so much among so few of them. + -- H. L. Mencken +~ +I have been more concerned with the obscure than with the famous. +They are more often themselves. They have had no need to create a +figure to protect themselves from the world or to impress it. Their +idiosyncrasies have had more chance to develop in the limited circle +of their activity, and since they have never been in the public eye +it has never occurred to them that they have anything to conceal. +They display their oddities because it has never struck them that +they are odd. And after all it is with the common run of men that +we writers have to deal; kings, dictators, commercial magnates are +from our point of view very unsatisfactory. To write about them is +a venture that has often tempted writers, but the failure that has +attended their efforts shows that such beings are too exceptional to +form a proper ground for a work of art. They cannot be made real. +The ordinary is the writer's richer field. Its unexpectedness, its +singularity, its infinite variety afford unending material. The +great man is too often all of a piece; it is the little man that is +a bundle of contradictory elements. He is inexhaustible. You never +come to the end of the surprises he has in store for you. For my +part I would much sooner spend a month on a desert island with a +veterinary surgeon than with a prime minister. + -- W. Somerset Maugham, from "The Summing Up" +~ +Fanaticism consists in redoubling your efforts +when you have forgotten your aim. + -- George Santayana +~ +The importance of people as creators and carriers of knowledge is forcing +organizations to realize that knowledge lies less in its databases than in +its people. It's been said, for example, that if NASA wanted to go to the +moon again, it would have to start from scratch, having lost not the data, +but the human expertise that took it there last time. Similarly, Tom +Davenport and Larry Prusake argue that when Ford wanted to build on the +success of the Taurus, the company found that the essence of that success +had been lost with the loss of the people that created it. Their knowledge +was not stored in information technologies. It left when they left. + -- John Seely Brown and Paul Duguid, in "The Social Life of Information" +~ +Either we live by accident and die by accident, +or we live by plan and die by plan. + -- Thornton Wilder +~ +Too long a sacrifice can make a stone of the heart. + -- William Butler Yeats +~ +...the act of producing a letter, even one which is never mailed, +necessitates a form of creative concentration which can improve our +lives. Copies of our own letters are useful for our records and +memories. If their recipients think them worth saving, they can have +value and effect far beyond that of the spoken word. In friendship, +the letter is not only a message but a gift, a physical symbol of +esteem and affection. In business or politics, the letter can not +only express the concerns of the moment but remain as a document of +such concerns, available for prolonged scrutiny by more than one +reader. Moreover, while speakers and listeners in a debate are +vulnerable to emotion and subject to fallacy, the well-written letter +remains calm and crisp and is subject to nothing except superior reason. +It can convince the open-minded, goad the weak-hearted, give our +opponents an exact index of the level and intensity of our commitment, +and be quoted by those who agree with us. But perhaps most importantly, +our letters are the proof and body of our concern for life in its detail +and our conviction that this concern should be shared with others. + -- Robert Grudin, from "Time and the Art of Living" +~ +Mass transportation is doomed to failure in North America because a +person's car is the only place where he can be alone and think. + -- Marshall McLuhan +~ +You've got to accentuate the positive, eliminate the negative, latch +on to the affirmative, and don't mess with Mister In-Between. + -- Johnny Mercer +~ + Once, as promised, bots start interacting with one another, +understanding bot behavior may become impossible. Anyone who has +had to call a help line with a problem about the way an operating +system from one vendor and program from another are working together-- +or failing to work--knows how hard it is to get anyone to take +responsibility for software interactions. Support staff rapidly +renounce all knowledge of (and usually interest in) problems that +arise from interactions because there are just too many possibilities. +So it's easy to imagine sophisticated programmers, let alone ordinary +users, being unable to unravel how even a small group of bots reached +a particular state autonomously. The challenge will be unfathomable +if, as one research group has it, we can "anticipate a scenario in +which billions of intelligent agents will roam the virtual world, +handling all levels of simple to complex negotiations and transactions. + If human agents are confused with digital ones, if human action is +taken as mere information processing, and if the social complexities of +negotiation, delegation, and representation are reduced to "when x, do y," +bots will end up with autonomy without accountability. Their owners, by +contrast, may have accountability without control. + -- John Seely Brown and Paul Duguid, in "The Social Life of Information" +~ +Life does not consist mainly--or even largely--of facts and +happenings. It consists mainly of the storm of thoughts that +is forever blowing through one's head. + -- Mark Twain +~ +Hain't we got all the fools in town on our side? +And ain't that a big enough majority in any town? + -- Mark Twain's Huckleberry Finn +~ + Every other country scorns American materialism while striving in +every big and little way to match it. Envy obviously has something to do +with it, but there is a true basis for this debate, and it is whether +America is in its ascendance or its decline. + I myself think I recognize here several of the symptoms that Edward +Gibbons maintained were signs of the decline of Rome, and which arose not +from external enemies but from inside the country itself. A mounting love +of show and luxury. A widening gap between the very rich and the very poor. +An obsession with sex. Freakishness in the arts masquerading as +originality, and enthusiasm pretending to creativeness... + There is, too, the general desire to live off the state, whether it +is a junkie on welfare or an airline subsidized by the government: in a +word, the notion that Washington -- Big Daddy -- will provide. And, most +disturbing of all, a developing moral numbness to vulgarity, violence, and +the assault on the simplest human decencies. + Yet the original institutions of this country still have great +vitality: the Republic can be kept, but only if we care to keep it. Much of +the turmoil in America springs from the energy of people who are trying to +apply those institutions to forgotten minorities who have awakened after a +long sleep. + As I see it, in this country -- a land of the most persistent +idealism and the blandest cynicism -- the race is on between its decadence +and its vitality. There are the woes, which we share with the world, that +you can see from your window: overpopulation; the pollution of the +atmosphere, the cities and the rivers; the destruction of nature. I find it +impossible to believe that a nation that produced such dogged and ingenious +humans as Jefferson and Eli Whitney, John Deere and Ford, Kettering and +Oppenheimer and Edison and Franklin, is going to sit back and let the worst +happen. There is now a possibility, at least, that nuclear energy can help +us to cure incurable diseases, to preserve our food indefinitely, and +through breeder reactors, which renew more power in the act of spending it, +can actually clean the cities and, let us pray, the oceans. And that would +take us over a historical watershed that none of us has ever conceived. + -- Alistair Cooke +~ +There are some enterprises in which a careful +disorderliness is the true method. + -- Herman Melville +~ +The dollar bills the customer gets from the tellers in four banks +are the same. What is different are the tellers. + -- Stanley Marcus +~ + I grew up in the last days of the British Empire. My childhood fell +in that era when the words 'imperialism' and 'the West' had not yet acquired +the connotations they have today -- they had not yet become, that is, mere +synonyms for 'racism,' 'oppression,' and 'exploitation'. + Or, at any rate, they had not yet become so among the intellectual, +professional, and governing classes of Egypt. In Cairo it was entirely +ordinary, among those classes, to grow up speaking English or French or +both, and quite ordinary to attend an English or French school. It was taken +for granted among the people who raised us that there was unquestionably +much to admire in and learn from the civilization of Europe and the great +strides that Europe had made in human advancement. No matter that the +European powers were politically oppressive and indeed blatantly unjust; nor +did it seem to matter that the very generation which raised us were +themselves locked in struggle with the British for Egypt's political +independence. There seemed to be no contradiction for them between pursuing +independence from the European powers and deeply admiring European +institutions, particularly democracy, and Europe's tremendous scientific +breakthroughs. + -- Leila Ahmed, from "A Border Passage: From Cairo to America-- + A Woman's Journey" +~ +the only possible mental bases for racism must ultimately be ignorance +or stupidity or both. it's only a little-minded weak person that has to +feel superior to another person just because of their skin color or +nationality. but i try not to in turn feel superior to racists; i find +only sadness for them instead. by imagining them trapped inside such an +awful and constraining mental prison, my compassion is evoked for these +confused folks. +~ +Things are in the saddle, and ride mankind. + -- Ralph Waldo Emerson +~ +One day Alice came to a fork in the road and saw a Cheshire cat +in a tree. "Which road do I take?" she asked. His response was +a question: "Where do you want to go?" "I don't know," Alice +answered. "Then," said the cat, "it doesn't matter." + -- Lewis Carroll +~ +It's not true that life is one damn thing after another-- +it's one damn thing over and over. + -- Edna St. Vincent Millay +~ +Avoid fried meats which angry up the blood. If your stomach +disputes you, lie down and pacify it with cool thoughts. Keep +the juices flowing by jangling around gently as you move. Go +very light on the vices, such as carrying on in society. The +social life ain't restful. Avoid running at all times. Don't +look back. Something may be gaining on you. + -- Leroy "Satchel" Paige, from his autobiography, + "How To Stay Young." +~ +Facts are all accidents. They all might have been different. +They all may become different. They all may collapse together. + -- George Santayana +~ +A word is dead +When it is said, +Some say. + +I say it just +Begins to live that day. + -- Emily Dickinson +~ +Thinking about it the other day, I realized that some of my unhappiest moments +have been in organizations. Somehow it seems to be quite respectable to do +things in organizations that you would never do in private life. I have had +people insult me to my face in front of colleagues. I have had my feelings +rammed down my throat on the pretext that it would do me good and have been +required to do things which I didn't agree with because the organization wished +it... In my worst moments I have thought that organizations were places +designed to be run by sadists and staffed by masochists.... + -- Charles Handy +~ +The best organizations to be in, it seems, are the busiest ones as long +as they are busy for someone else. The worst are those that are obsessed +with their own innards... The healthiest are those which exist for others, +not for themselves. Show me a business or a school or a church that is +preoccupied with its customers or clients, determined to do its best for them +and not just to survive for the sake of surviving, and I'll bet you that they +don't have time for too many committees, for forms, for politicking or for +nitpicking about mistakes. Those are the organizations which are fun to be +in, which give you room to be yourself, to express yourself, to grow. + -- Charles Handy +~ +The art of being wise is the art of knowing what to overlook. + -- William James +~ +Life is the process of finding out, too late, everything +that should have been obvious at the time. + -- John D. MacDonald +~ +If work was a good thing the rich would have it all +and not let you do it. + -- Elmore Leonard +~ +Any law that takes hold of a man's daily life cannot prevail +in a community, unless the vast majority of the community are +actively in favor of it. The laws that are the most operative +are the laws which protect life. + -- Henry Ward Beecher +~ +The graveyard is full of indispensable men. + -- Charles de Gaulle +~ +I read about an Eskimo hunter who asked the local missionary +priest, "If I did not know about God and sin, would I go to hell?" +"No," said the priest, "not if you did not know." +"Then why," asked the Eskimo earnestly, "did you tell me?" + -- Annie Dillard +~ + The day came when Grandmother couldn't keep all her stuff in the two +tiny rooms to which she was finally reduced. So she packed everything +she didn't need into enormous shopping bags and took off for the bank +in the center of the city where she kept her account, by then down to +a few pennies. Her husband had started the bank and had been its +chairman until he died, and she was still treated with the consideration +due his widow. But when she appeared with her shopping bags and asked +to have the contents put on her account, the manager balked. "We can't +put things on an account," he said, "only money." + "That's mean and ungrateful of you," said Grandmother, "you only do +this to me because I am a stupid old woman." + And she promptly closed her account and drew out the balance. Then +she went down the street to the nearest branch of the same bank, reopened +her account there, and never said a word about her shopping bags. + "Grandmother," we'd say, "if you thought the bank was unfriendly why +did you reopen your account at another branch?" + "It's a good bank," she said; "after all, my late husband founded it." + "Then why not demand that the manager at the new branch take your +stuff?" + "I never banked there before. He didn't owe me anything." + -- Peter Drucker, "Adventures of a Bystander" +~ +Everything should be made as simple as possible, but not simpler. + -- Albert Einstein +~ +Talking about music is like dancing about architecture. + -- unknown +~ +Writing about music is like dancing about architecture-- +it's a really stupid thing to want to do. + -- Elvis Costello, in an interview by Timothy White entitled + "A Man out of Time Beats the Clock." Musician magazine + No. 60 (October 1983), p. 52. +~ +The pressures for upscale consumption, and the work schedules +that go along with it, created millions of exhausted, stressed- +out people who started wondering if the cycle of work and spend +was really worth it. And some concluded that it wasn't. So +they started downshifting, reducing their hours of work and, in +the process, earning and spending less money. Downshifters are +opting out of excessive consumerism, choosing to have more +leisure and balance in their schedules, a slower pace of life, +more time with their kids, more meaningful work, and daily lives +that line up squarely with their deepest values. These are not +just fast-track yuppies leaving $200,000 jobs in Manhattan to +settle in Montana, although there are plenty of those. +Downshifters can be found at all income levels, from the +comfortable suburbanites whose homes are paid for, to those who +are counting every penny, resigned to the fact that they'll never +own a home. Their jobs were leaving them drained, depressed, or +wondering what life is all about. Now they may not have as much +money, but they are spending every day answering that all- +important question. + -- Juliet B. Schor +~ +The right to be let alone--the most comprehensive of rights and +the right most valued by civilized men. + -- Louis D. Brandeis +~ +The need to reach better mutual understanding through dialogue +is strong in all sectors of society, but in none more than the +business community. The growth of technology, the increase in +the number of knowledge workers, and the blurring of boundaries +of all kinds are transforming relationships at all levels of +business. The traditional top-down style of leadership in a +fortress-type company semi-isolated from others is increasingly +out of vogue. It is being replaced by what I have come to +think of as "relational leadership," where the defining task of +leaders is developing webs of relationships with others rather +than handing down visions, strategies, and plans as if they +were commandments from the mountaintop. + -- Daniel Yankelovich, "The Magic of Dialogue" +~ +Life is one long process of getting tired. + -- Samuel Butler +~ +Every great mistake has a halfway moment, a split second when it can +recalled and perhaps remedied. + -- Pearl S. Buck +~ + Fortunately, many teachers intuitively know that the best way to +achieve their goals is to enlist students' interest on their side. +They do this by being sensitive to students' goals and desires, and +they are thus able to articulate the pedagogical goals as meaningful +challenges. They empower students to take control of their learning; +they provide clear feedback to the students' efforts without +threatening their egos and without making them self-conscious. They +help students concentrate and get immersed in the symbolic world of +the subject matter. As a result, good teachers still turn out +children who enjoy learning, and who will continue to face the world +with curiosity and interest. + It is to be hoped that with time the realization that children +are not miniature computing machines will take root in educational +circles, and more attention will be paid to motivational issues. +Unless this comes to pass, the current problems we are having with +education are not likely to go away. + -- Mihaly Csikszentmihalyi, from "Creativity: Flow and Psychology + of Discovery and Invention" +~ +The more human beings proceed by plan the more effectively they may +be hit by accident. + -- Friedrich Duerrenmatt +~ +We never see what's under our feet; +we're too busy trying to see +what's in the stars. + -- Quintus Ennius +~ +Paradoxes have played a dramatic role in intellectual history, +often foreshadowing revolutionary developments in science, +mathematics, and logic. Whenever, in any discipline, we discover +a problem that cannot be solved within the conceptual framework +that supposedly should apply, we experience shock. The shock may +compel us to discard the old framework and adopt a new one. It +is to this process of intellectual molting that we owe the birth +of many of the major ideas in mathematics and science. Zeno's +paradox of Achilles and the tortoise gave birth to the idea of +convergent infinite series. 'Antinomies' (internal contradictions +in mathematical logic) eventually blossomed into Godel's theorem. +The paradoxical result of the Michelson-Morley experiment on the +speed of light set the stage for the theory of relativity. The +discovery of wave-particle duality of light forced a reexamination +of deterministic causality, the very foundation of scientific +philosophy, and led to quantum mechanics. + -- Anatol Rapoport +~ +Examinations are formidable even to the best prepared, for the +greatest fool may ask more than the wisest man can answer. + -- Charles Caleb Colton +~ +More than any other time in history, mankind faces a crossroads. +One path leads to despair and utter hopelessness. +The other, to total extinction. +Let us pray we have the wisdom to choose correctly. + -- Woody Allen +~ +Some books are undeservedly forgotten; +none are undeservedly remembered. + -- W. H. Auden +~ +Every age is fed on illusions, lest men should renounce +life early and the human race come to an end. + -- Joseph Conrad +~ + Why is autobiography the most popular form of nonfiction +for modern readers? Why are so many people moved to write their +life stories today? And what is it about the genre that makes +it appeal to readers not just in the Western world, but also in +non-Western cultures, like those of Japan and India or the many +cultures of Africa?... + What makes the reading of autobiography so appealing is the +chance it offers to see how this man or that woman whose public +self interests us has negotiated the problem of self-awareness +and has broken the internalized code a culture supplies about +how life should be experienced. Most of us, unless faced with +emotional illness, don't give our inner life scripts a fraction +of the attention we give to the plots of movies or TV specials +about some person of prominence. Yet the need to examine our +inherited scripts is just beneath the surface of consciousness, +so that while we think we are reading a gripping story, what +really grips us is the inner reflection on our own lives the +autobiographer sets in motion. + -- Jill Ker Conway +~ +Philosophy is a game with objectives and no rules. +Mathematics is a game with rules and no objectives. + -- Unknown +~ +The brain is a wonderful organ. It starts working +the moment you get up in the morning and does not stop +until you get into the office. + -- Robert Frost +~ +We have a criminal jury system which is superior to any +in the world; and its efficiency is only marred by the +difficulty of finding twelve men every day who don't know +anything and can't read. + -- Mark Twain +~ +If you start to think about your physical or moral condition, +you usually find that you are sick. + -- Johann Wolfgang von Goethe +~ +A modest man is usually admired--if people ever hear of him. + -- Edgar Watson Howe +~ + Modern housekeeping, despite its bad press, is among the most +thoroughly pleasant, significant, and least alienated forms of work +that many of us will encounter even if we are blessed with work +outside the home that we like. Once, it was so physically onerous +and arduous that it not infrequently contributed to a woman's total +physical breakdown. Today, laundry, cleaning, and other household +chores are by and large physically light or moderate work that doctors +often recommend to people for their health, as evidence shows that +housework is good for weight control and healthy hearts. + Seen from the outside, housework can look like a Sisyphean task +that gives you no sense of reward or completion. Yet housekeeping +actually offers more opportunities for savoring achievement than +almost any other work I can think of. Each of its regular routines +brings satisfaction when it is completed. These routines echo the +rhythm of life, and the housekeeping rhythm is the rhythm of the body. +You get satisfaction not only from the sense of order, cleanliness, +freshness, peace and plenty restored, but from the knowledge that you +yourself and those you care about are going to enjoy these benefits. + -- Cheryl Mendelson +~ +I'll tell you what it's like to be No. 1. I compare it to climbing +Mount Everest. It's very difficult. Lives are lost along the way. +You struggle and struggle and finally you get up there. And guess +what there is once you get up there? Snow and ice. + -- David Merrick +~ +Never ask a person what to do, always tell him or her. +If it's the wrong thing to do, or if there is a better way, +they'll come back and tell you. But if you don't tell them +what to do, they won't do anything but make a study. + -- Eugenia Schwartzwald +~ + War, it seems to me, after a lifetime of reading about the subject, +mingling with men of war, visiting the sites of war and observing its +effects, may well be ceasing to commend itself to human beings as a +desirable or productive, let alone rational, means of reconciling their +discontents. This is not mere idealism. Mankind does have the capacity, +over time, to correlate the costs and benefits of large and universal +undertakings. + Throughout much of the time for which we have a record of human +behaviour, mankind can clearly be seen to have judged that war's benefits +outweighed its costs, or appeared to do so when a putative balance was +struck. Now the computation works in the opposite direction. Costs +clearly exceed benefits. Some of these costs are material. The +superinflationary expense of weapon procurement distorts the budgets even +of the richest states, while poor states deny themselves the chance of +economic emancipation when they seek to make themselves militarily +formidable. + The human costs of actually going to war are even higher. Rich +states, as between themselves, recognize that they are not to be borne. +Poor states which fall into war with rich states are overwhelmed and +humiliated. Poor states which fight each other, or are drawn into civil +war, destroy their own well being, and even the structures which make +recovery from the experience of war possible. War truly has become a +scourge, as was disease throughout most of human history. The scourge +of disease has, almost within living memory, been very largely defeated +and, though it is true that disease had no friends as war has had friends, +war now demands a friendship which can only be paid in false coin. + A world political economy which makes no room for war demands, it +must be recognized, a new culture of human relations. As most cultures of +which we have knowledge were transfused by the warrior spirit, such a +cultural transformation demands a break with the past for which there are +no precedents. There is no precedent, however, for the menace with which +future war now confronts the world. + -- John Keegan, "A History of Warfare" +~ +Creditors are a superstitious sect, +great observers of set days and times. + -- Benjamin Franklin, "Poor Richard's Almanack" +~ + Despite the abundant evidence that compassion is a basic +human trait, the view has long prevailed that human beings are +either heartless or brutal toward most of their fellows. Every +compendium of familiar quotations has an abundance of statements +like "The greatest enemy to man is man" (Robert Burton) but +almost none like "Precious is man to man" (Thomas Carlyle). +Altruism research attests that kindness is as integral to human +nature as cruelty, yet in the news and in the historical record +cruel acts vastly outnumber kind ones. + Why are we keenly aware of the despicable in us but largely +insensible of the admirable? Not, I suggest, because the +despicable is common and the admirable rare but the very reverse. +Prosocial behavior of all sorts, including altruism, is so normal +and expected that we scarcely notice it but are struck by its +absence or opposite. We see nothing unusual in a passerby's +helping a fallen elderly person to his feet but are surprised and +disturbed if the passerby ignores him. We expect people to be +kind and helpful to a stranger in distress; we are startled and +troubled when they look away and hurry past. Cruelty is +attention-getting, kindness unremarkable, and so we agree with +Seneca that "man delights to ruin man" and with William James that +"of all the beasts of prey... [man is] the only one that preys +systematically on its own species". + -- Morton Hunt, "The Compassionate Beast" +~ +Life is all memory, except for the one present moment +that goes by you so quick you hardly catch it going. + -- Tennessee Williams +~ + There are serious reasons members of my generation are feeling a high +level of anxiety and unhappiness these days, but it is interesting to look +at how we "know" this: the polls. I used to like polls because I like vox +pop, and polls seemed a good way to get a broad sampling. But now I think +the vox has popped--the voice has cracked from too many command performances. +Polls are contributing to a strange new volatility in public opinion... + The dramatic rises and drops are fueled in part by mass media and their +famous steady drumbeat of what's not working, from an increase in reported +child abuse to a fall in savings. When this tendency is not prompted by +ideology it is legitimate: good news isn't news. But the volatility is also +driven by the polls themselves. People think they have to have an answer +when they are questioned by pollsters, and they think the answer has to be +"intelligent" and "not naive". This has the effect of hardening opinions +that haven't even been formed yet. Poll questions do not invite subtlety or +response. This dispels ambiguity, when a lot of thoughts and opinions are +ambiguous. + And we are polled too often. We are constantly having our temperature +taken, like a hypochondriac who is looking for the reassurance no man can +have, i.e., that he will not die... Nations that use polls as daily +temperature readings inevitably give inauthentic readings, and wind up not +reassured but demoralized. + -- Peggy Noonan +~ +The chief object of education is +not to learn things but to unlearn things. + -- G. K. Chesterton +~ +When a subject becomes totally obsolete we make it a required course. + -- Peter Drucker +~ +He will kill mice and he will be kind to babies... +but when the moon gets up and the night comes, he is +the Cat that Walks by Himself. + -- Rudyard Kipling +~ +Everyone is a prisoner of his own prejudices. +No one can eliminate prejudices--just recognize them. + -- Edward R. Murrow +~ +I am the least difficult of men. +All I want is boundless love. + -- Frank O'Hara +~ +If you can't say anything good about someone, sit right here by me. + -- Alice Roosevelt Longworth +~ +Let's face it, life is mainly wasted time. + -- John Berryman +~ +Pray for the dead and fight like hell for the living! + -- Mother Jones +~ +Time spent with cats is never wasted. + -- Colette +~ + Most of us find ourselves at one time or another in a toy shop, +looking for a gift for a child. Those of us who have not been in such +stores since our youth can easily be bewildered, especially if we were +born before the 1960s. Our favorite toys or games--fire engines, +Tinkertoys, or baby dolls--have disappeared or are hidden in row after +row of heroic fighters, fashion dolls, and exotic stuffed animals. The +more practical of us enter the store with a list of items desired by +the child--this season's action figure, the newest Barbie, or the +latest video game. Veteran toy shoppers may enjoy the inevitable +transformations as Teenage Mutant Ninja Turtles give way to Mighty +Morphin Power Rangers and Locket Surprise Barbie to Tropic Splash +Barbie. But equally we may be appalled to think about the dozens of +Ninja Turtles that an older boy just had to have a year earlier but +that were then shunned by a younger brother who just had to have Power +Rangers. Why don't kids pass down their toys as we remember giving our +building blocks and dollhouses to our younger brothers and sisters? It +is easy to wonder whether each year's must-have toys are really for +children's play or whether their ever-changing forms represent other +forces at work. + There have been disturbing changes in the making of playthings in +the last few decades. Since the late 1960s many old toy companies, +venerated for manufacturing toys passed from one generation to the +next, have disappeared... + A tradition of manufacturing boys' construction and science sets +promised parents that their children would be preparing to join the +adult world of engineering, industry, and science... The old kitchen +play sets, dollhouses, and baby dolls that were to teach girls the arts +of housekeeping and childcare are also less in evidence today. Toys +that seem to prepare children for adult life have become harder to +find. + The ever-expanding toy industry reflects a general American +commitment to unrestrained markets and to constant change, a commitment +at least a century old. Americans have long admired the new and have +enriched those who produce it. For decades American parents have +enjoyed sharing the world of consumption with their offspring. At +first they did so knowing that they ultimately mediated between toys +and their children. When the floodgates were opened and torrents of +toys were presented directly to kids, parents found themselves merely +providers of funds to buy toys. + -- Gary Cross, "Kids' Stuff: Toys and the Changing World of American + Childhood." +~ +Business is never so healthy as when, like a chicken, it must +do a certain amount of scratching around for what it gets. + -- Henry Ford +~ +If you assign people duties without granting +them any rights, you must pay them well. + -- Johann Wolfgang von Goethe +~ +It is far easier to write ten passable effective sonnets, +good enough to take in the not too inquiring critic, +than one effective advertisement that will take in a few +thousand of the uncritical buying public. + -- Aldous Huxley +~ +tail end of the day +work escapes my fat fingers +like greasy wieners + -- fred t. hamster +~ +Anything that won't sell, I don't want to invent. +Its sale is proof of utility and utility is success. + -- Thomas Alva Edison +~ +Our lives are suspended like our planet in gimbals of duality, +half sunlight and half shadow. If we plead with nature, it is +in vain; she is wonderfully indifferent to our fate, and it is +her custom to try everything and to be ruthless with incompetence. +Ninety-nine percent of all the species that have lived on Earth +have died away, and no stars will wink out in tribute if we in +our folly soon join them. + -- Timothy Ferris +~ +It is characteristic of the present time always to be conscious +of the medium. It is almost bound to end in madness, like a man +who whenever he looked at the sun and the stars was conscious of +the world going round. + -- Kierkegaard +~ +The vanity of man revolts from the serene indifference of the cat. + -- Agnes Repplier +~ +There are two means of refuge from the +miseries of life: music and cats. + -- Albert Schweitzer +~ +Open source should be about giving away things voluntarily. +When you force someone to give you something, it's no longer +giving, it's stealing. Persons of leisurely moral growth +often confuse giving with taking. + -- Larry Wall +~ +Stripped of ethical rationalizations and philosophical +pretensions, a crime is anything that a group in power +chooses to prohibit. + -- Freda Adler +~ +People are more violently opposed to fur than +leather because it's safer to harass rich women +than motorcycle gangs. + -- Anonymous +~ +A fanatic is one who can't change his mind and won't +change the subject. + -- Winston Churchill +~ +Do not criticize your government when out of the +country. Never cease to do so when at home. + -- Winston Churchill +~ +Some civil servants are neither servants nor civil. + -- Winston Churchill +~ +Golf is a game whose sole aim is to hit a very small +ball into an even smaller hole, with weapons +singularly ill designed for that purpose. + -- Winston Churchill +~ +If it is a blessing, it is certainly very well disguised. + -- Winston Churchill +~ +In war, you can only be killed once, but in politics, many times. + -- Winston Churchill +~ +Some people's idea of free speech is that they are free +to say anything they like, but if anyone says anything +back, that is an outrage. + -- Winston Churchill +~ +I am ready to meet my Maker. Whether my Maker is +prepared for the great ordeal of meeting me is another +matter. + -- Winston Churchill +~ +If you destroy a free market you create a black market. + -- Winston Churchill +~ +If you have ten thousand regulations you destroy +all respect for the law. + -- Winston Churchill +~ +Nothing in life is so exhilarating as to be shot at +without result. + -- Winston Churchill +~ +It is a socialist ideal that making profits is a vice. +I consider the real vice is making losses. + -- Winston Churchill +~ +If you would know the value of money, go and try to borrow some. + -- Benjamin Franklin +~ +The world is charged with the grandeur of God. + -- Gerard Manley Hopkins +~ +The sounder your argument, the more satisfaction you get out of it. + -- Edward W. Howe +~ + Expertise, it may be argued, sacrifices the insight of common sense +to intensity of experience. It breeds an inability to accept new views from +the very depth of its preoccupation with its won conclusions. It too often +fails to see round its subject. It sees its results out of perspective by +making them the centre of relevance to which all other results must be +related. Too often, also, it lacks humility; and this breeds in its +possessors a failure in proportion which makes them fail to see the obvious +which is before their very noses. It has, also, a certain caste-spirit about +it, so that experts tend to neglect all evidence which does not come from +those who belong to their own ranks. + Above all, perhaps, and this most urgently when human problems are +concerned, the expert fails to see that every judgment he makes not purely +factual in nature brings with it a scheme of values which has no special +validity about it. He tends to confuse the importance of his facts with the +importance of what he proposes to do about them. + -- Harold J. Laski, "The Limitations of the Expert" +~ +If women are to do the same work as men, +we must teach them the same things. + -- Plato +~ +Dogs may fawn on all and some +As they come; +You, a friend of loftier mind, +Answer friends alone in kind; +Just your foot upon my hand +Softly bids it understand. + -- Algernon Charles Swinburne +~ +The only way to get rid of temptation is to yield to it. + -- Oscar Wilde +~ +The machine does not isolate man from the great problems +of nature but plunges him more deeply into them. + -- Antoine de Saint-Exupery +~ +Today in USA Today, experts were quoted as saying that the breakup of +Microsoft would result in shoddy products and missed deadlines... + +And Microsoft is worrying about being able to do business as usual. +~ +The justice department has released its recommendations on +names for the two parts of Microsoft, post split: + +MICROS~1 and MICROS~2 +~ +I think we should partition Microsoft into an OS company +(called "C:") and an apps company ("D:"). Then we should +blow away both partitions.... + -- Doug Steinfeld +~ +Microsoft has argued in court that the US government's plan +to break up the company is "defective in numerous respects, +making the document vague and ambiguous." + +In which respect it's much like Microsoft's documentation. +~ +Immediately after the announcement by Judge Penfield of splitting +the company in two, Microsoft sued Micro.Com Specialists, LLC, and +Software Research, Inc. (owners of www.soft.com) for infringement +of copyrights and cyber-squatting. +~ +your brain will enlarge as necessary. just don't get angry or +confused or scared or sad, if possible... you'll be amazed at +what that blob of cells is capable of. + -- fred t. hamster +~ +If you like laws and sausage, you should +never watch either being made. + -- Otto von Bismarck +~ +The most happy marriage I can picture or imagine to myself +would be the union of a deaf man to a blind woman. + -- Samuel Taylor Coleridge +~ +A doctor can bury his mistakes, but an architect can only +advise his clients to plant vines. + -- Frank Lloyd Wright +~ +He taught me housekeeping; when I divorce I keep the house. + -- Zsa Zsa Gabor, speaking of her ex-husband +~ +There is really no such thing as bad weather, +only different kinds of good weather. + -- John Ruskin +~ +She's not a babe. She's a sophisticated real-time computer system. + -- Spokesman for Anova, a virtual news anchor +~ +The man who laughs has not yet been told the terrible news. + -- Berthold Brecht +~ +I am easily satisfied with the very best. + -- Winston Churchill +~ +Uncertainty and expectations are the joys of life. +Security is an insipid thing, and the overtaking and +possessing of a wish, discovers the folly of the chase. + -- William Congreve +~ + Sleep is simply not dispensable, regardless of the attempts in +today's society to treat it as if it were simply unproductive 'downtime'. +The desire to get more sleep is not a sign of laziness, nor does it +represent a lack of ambition. The need for sleep is real... + When we try to sleep less than the 8-hour minimum, things start to +deteriorate. First of all, the effects of less than 8 hours of sleep a +night seem to accumulate as a sleep debt. If you lose 2 hours today and 2 +hours tomorrow, on the third day your efficiency is as low as if you had +lost 4 hours in one night. This is the way our sleep debt builds up. +Eventually, if the sleep debt becomes large enough, we become slow, clumsy, +stupid, and, possibly, dead. This is not an exaggeration. Remember, the +national death rate by accidents jumps 6 percent as a result of simply +losing 1 hour of sleep as we shift to daylight savings time in the spring... + Perhaps someday society will act to do something about sleepiness. +It may even come to pass that someday the person who drives or goes to work +while sleepy will be viewed as being as reprehensible, dangerous, or even +criminally negligent as the person who drives or goes to work while drunk. +If so, perhaps the rest of us can all sleep a little bit more soundly. + -- Stanley Coren +~ +You haven't lived until you've lived with a cat. + -- Doris Day +~ +When you appeal to force, there's one thing you must never do--lose. + -- Dwight D. Eisenhower +~ +It is because the body is a machine that education is possible. +Education is the formation of habits, a superinducing of an +artificial organization upon the natural organization of the body. + -- Thomas H. Huxley +~ + If someone asks you, "Where do you live?" you are likely to answer +with the name of a neighborhood, or a nearby geographic landmark. But if you +give the question a sharper focus and ask yourself "And do I really live +there?" then the answer becomes more vexing. Most of us can't claim to +really live in the neighborhoods where we sleep. Few of us have the time to +take part in the life of the community, and in many cases there is no +community life to take part in. To varying degrees many of us can say of +our neighborhoods what Gertrude Stein said of Oakland: "There is no _there_ +there." + Bodenstandigkeit is German philosopher Martin Heidegger's term for +the sense of being rooted in a place. It is this connection to a place that +grounds us in Being, Heidegger claimed, and even if you don't buy into the +existential mumbo-jumbo, it's not hard to understand the underlying insight: +People who have no rootedness to a place are tumbleweeds, blown about on the +currents of the zeitgeist. You have to be somebody, from somewhere, to know +who you are. + -- Jeremy Iggers +~ +You don't understand anything until you learn it more than one way. + -- Marvin Minsky +~ +When you confer spiritual authority on another person, you +must realize that you are allowing them to pick your pocket +and sell you your own watch. + -- Alan Watts +~ +What then is the education to be? Perhaps we could hardly find a +better answer than that which the experience of the past has already +discovered--which consists, I believe, in gymnastic for the body and +music for the mind. + -- Plato +~ +The word 'genius' isn't applicable in football. +A genius is a guy like Norman Einstein. + -- Joe Theisman, NFL quarterback +~ +Sometimes the only way you can feel good about yourself is by making +someone else look bad. And I'm tired of making other people feel good +about themselves. + -- Homer Simpson +~ +I was determined to know beans. + -- Henry David Thoreau +~ +Today, the notion of a star-spangled melting pot seems quaint, +of another age. Increasingly, America is a fractured landscape, +its people partitioned into dozens of cultural enclaves, its +ideals reflected through differing prisms of experience. And +this fracturing is likely to continue as the self-concept of +America shifts from a majority white-minority black nation to +a pluralistic society of many ethnic and racial groups. At the +close of what's been called the American Century, during which +the nation emerged as the dominant world power in commerce and +politics, old myths are dying hard and new ones are just being +forged. In this clustered world, the national identity is +changing, and most of us don't even know it. Forget the melting +pot. America today would be better characterized as a salad bar. + -- Michael J. Weiss +~ +Personally, I'm always ready to learn, +although I do not always like being taught. + -- Winston Churchill +~ +The Constitution only gives people the right to +pursue happiness. You have to catch it yourself. + -- Ben Franklin +~ +Tragedy is when I cut my finger. Comedy is when +you fall down an open manhole and die. + -- Mel Brooks +~ +As a general rule, the most successful man in +life is the man who has the best information. + -- Benjamin Disraeli +~ +It is one of the blessings of old friends that +you can afford to be stupid with them. + -- Ralph Waldo Emerson +~ +A child of five would understand this. +Send someone to fetch a child of five. + -- Groucho Marx +~ +Either this man is dead or my watch has stopped. + -- Groucho Marx +~ +I find television very educating. Every time somebody turns +on the set, I go into the other room and read a book. + -- Groucho Marx +~ +I refuse to join any club that would have me as a member. + -- Groucho Marx +~ +I've had a perfectly wonderful evening. +But this wasn't it. + -- Groucho Marx +~ +Those are my principles, and if you don't like +them... well, I have others. + -- Groucho Marx +~ +When I picked up your book I was so convulsed with laughter +that I had to set it down, but one day I intend to read it. + -- Groucho Marx +~ +Some people claim that marriage interferes with romance. +There's no doubt about it. Anytime you have a romance, +your wife is bound to interfere. + -- Groucho Marx +~ +God has made the cat to give man the +pleasure of caressing the tiger. + -- Victor Hugo +~ +We are not without accomplishment. We have +managed to distribute poverty equally. + -- Nguyen Co Thatch, Vietnamese Foreign +~ +Chance is always powerful. Let your hook be +always cast. In the pool where you least expect +it, will be a fish. + -- Ovid +~ +If a man does not keep pace with his companions, perhaps it is +because he hears a different drummer. Let him step to the music +which he hears, however measured or far away. + -- Henry David Thoreau +~ +The difficulty is to try and teach the multitude that something can +be true and untrue at the same time. + -- Arthur Schopenhauer +~ +God made the Idiot for practice, and then He made the School Board. + -- Mark Twain +~ +People often applaud an imitation and then sneer at the real thing. + -- Aesop +~ +A thought which does not result +in an action is nothing much, +and an action which does not proceed +from a thought is nothing at all. + -- Georges Bernanos +~ +given that: + +Security holes are only software bugs when +one of the software's requirements is security. + +we wonder: + +do outlook, internet explorer and, indeed, windows +itself have 'bugs'? because that would indicate +that microsoft has a requirement of security... +it seems more likely that any security problems are just +a by-product of the microsoft swiss cheese development +methodology(tm)... if it doesn't stink and have a lot +of holes, then it's not from microsoft. + -- fred t. hamster +~ +The biggest fool in the world is he who merely does his work +supremely well, without attending to appearance. + -- Michael Korda +~ +Now and then there is a person born who is so unlucky that he runs +into accidents which started out to happen to somebody else. + -- Don Marquis +~ +Daydreaming does not enjoy tremendous prestige in our culture, +which tends to regard it as unproductive thought. Writers +perhaps appreciate its importance better than most, since a +fair amount of what they call work consists of little more +than daydreaming edited. Yet anyone who reads for pleasure +should prize it too, for what is reading a good book but a +daydream at second hand? Unlike any other form of thought, +daydreaming is its own reward. For regardless of the result +(if any), the very process of daydreaming is pleasurable. +And, I would guess, is probably a psychological necessity. +For isn't it in our daydreams that we acquire some sense of +what we are about? Where we try on futures and practice our +voices before committing ourselves to words or deeds? +Daydreaming is where we go to cultivate the self, or, more +likely, selves, out of the view and earshot of other people. +Without its daydreams, the self is apt to shrink down to the +size and shape of the estimation of others. + -- Michael Pollan +~ +Eccentricity is not, as dull people would have us believe, +a form of madness. It is often a kind of innocent pride, +and the man of genius and the aristocrat are frequently +regarded as eccentrics because genius and aristocrat are +entirely unafraid of and uninfluenced by the opinions and +vagaries of the crowd. + -- Dame Edith Sitwell +~ +There are times when I think that the ideal library is +composed solely of reference books. They are like +understanding friends--always ready to change the subject +when you have had enough of this or that. + -- J. Donald Adams +~ +Mothers, fathers, aren't supposed to change, any more than +they are supposed to leave, or die. They must not do that. + -- Martin Amis +~ +The greatest obstacle to discovery is not +ignorance--it is the illusion of knowledge. + -- Daniel J. Boorstin +~ +Congress is so strange. A man gets up to speak and says nothing. +Nobody listens--and then everybody disagrees. + -- Boris Marshalov +~ +All music is folk music. I ain't never heard no horse sing a song. + -- Louis "Satchmo" Armstrong +~ +Creative minds have always been known +to survive any kind of bad training. + -- Anna Freud +~ +Victory goes to the player who makes the next-to-last mistake. + -- Chessmaster Savielly Grigorievitch Tartakower +~ +I recently read that love is entirely a matter of chemistry. +That must be why my wife treats me like toxic waste. + -- David Bissonette +~ +I married the first man I ever kissed. +When I tell my children that they just about throw up. + -- Barbara Bush +~ +The trouble with some women is they get all excited +about nothing--and then they marry him. + -- Cher +~ +Half of the harm that is done in this world is due to people +who want to feel important. They don't mean to do harm. +But the harm does not interest them. + -- T. S. Eliot +~ +When you're finished changing, you're finished. + -- Benjamin Franklin +~ +It is time we start searching for the fountain of age, time that +we stop denying our growing older and look at the actuality of our +own experience, and that of other women and men who have gone +beyond denial to a new place in their sixties, seventies, eighties. +It is time to look at age on its own terms, and put names on its +values and strengths as they are actually experienced, breaking +through the definition of age solely as deterioration or decline +from youth. Only then will we see that the problem is not age +itself, to be denied or warded off as long as possible, that the +problem is not those increasing numbers of people living beyond +sixty-five, to be segregated from the useful, valuable, pleasurable +activities of society so that the rest of us can keep our illusion +of staying forever young. Nor is the basic political problem the +burden on society of those forced into deterioration, second +childhood, even senility. The problem is not how we can stay young +forever, personally--or avoid facing society's problems politically +by shifting them onto age. The problem is, first of all, how to +break through the cocoon of our illusory youth and risk a new stage +in life, where there are not prescribed role models to follow, no +guidepost, no rigid rules or visible rewards, to step out into the +true existential unknown of these new years of life now open to us, +and to find our own terms for living it. + -- Betty Friedan +~ +When I am getting ready to reason with a man, I spend one-third +of my time thinking about myself and what I am going to say and +two-thirds about him and what he is going to say. + -- Abraham Lincoln +~ +Blessed are they who have nothing to say +and who cannot be persuaded to say it. + -- James Russell Lowell +~ + Personal computers are less able to sense human presence +than are modern toilets or outdoor floodlights that have simple +motion sensors. Your inexpensive auto-focus camera has more +intelligence about what is in front of it than any terminal or +computer system. + When you lift your hands from your computer keyboard, it +does not know whether the pause is reflective, a nature break, +or an interruption for lunch. It cannot tell the difference +between talking to you alone or in front of six other people. +It does not know if you are in your night-or party clothes or +no clothes at all. For all it knows, you could have your back +to it while it was showing you something important, or you +could be out of earshot altogether while it was speaking to you. + We think today solely from the perspective of what would +make it easier for a person to use a computer. It may be time +to ask what will make it easier for computers to deal with +humans. For example, how can you possibly hold a conversation +with people if you don't even know they are there? You can't +see them, and you don't know how many there are. Are they +smiling? Are they even paying attention? We talk longingly +about human-computer interactions and conversational systems, +and yet we are fully prepared to leave one participant in this +dialogue totally in the dark. It is time to make computers see +and hear. + -- Nicholas Negroponte +~ +The energy produced by the breaking down of the +atom is a very poor kind of thing. Anyone who +expects a source of power from the transformation +of these atoms is talking moonshine. + -- Ernest Rutherford +~ +Finance is the art of passing currency from +hand to hand until it finally disappears. + -- Robert W. Sarnoff +~ + The scientific community speaks about its work in a cool +and disinterested manner. To present an exciting profile +would be unprofessional. Any excess of emotion would suggest +a lack of neutrality and therefore a tendency to read what +they want in the facts rather than reporting what they see. +Scientific objectivity must therefore appear to be boring. + Scientists are well aware that their work is neither +boring nor objective. If it were, very few discoveries would +be made. + Social science, being falsely empirical, is triply +obsessed by the obligation to present itself as the objective +interpretation of observed reality. Since the more or less +hard edges of scientific inquiry are not involved, social +scientists are free to be more categorical about truth, +reality and what they call facts. They therefore seek to be +more boring than scientists. + -- John Ralston Saul +~ +Despair is perfectly compatible with a good dinner, I promise you. + -- William M. Thackeray +~ +A genius is someone who can do anything except make a living. + -- Joey Adams +~ +If you like a man's laugh before you know anything of him, +you may say with confidence that he is a good man. + -- Fyodor Dostoevski +~ +Fortune befriends the bold. + -- John Dryden +~ +Success in almost any field depends more on energy +and drive than it does on intelligence. This explains +why we have so many stupid leaders. + -- Sloan Wilson +~ +Socialism is Bolshevism with a shave. + -- Detroit Journal +~ +I like long walks, especially when they are taken by people who annoy me. + -- Noel Coward +~ +Give a man a fire, and he'll be warm for a day. +Set a man on fire, and he'll be warm for the rest of his life. +~ +It is a myth, not a mandate, a fable not a logic, +and symbol rather than a reason by which men are moved. + -- Irwin Edman +~ +Dakota tribal wisdom says that when you're on a dead horse, the best +strategy is to dismount. Of course, there are other strategies. You +can change riders. You can get a committee to study the dead horse. +You can benchmark how other companies ride dead horses. You can +declare that it's cheaper to feed a dead horse. You can harness +several dead horses together. But after you've tried all these things, +you're still going to have to dismount. + -- Gary Hamel +~ +You think it's a conspiracy by the networks to put bad shows on TV. +But the shows are bad because that's what people want. It's not like +Windows users don't have any power; I think they are happy with +Windows, and that's an incredibly depressing thought. + -- Steve Jobs +~ +Coaches have to watch for what they don't want to see +and listen to what they don't want to hear. + -- John Madden +~ +Never forget the power of silence, that massively disconcerting +pause which goes on and on and may at last induce an opponent to +babble and backtrack nervously. + -- Lance Morrow +~ +Florence Nightingale, on her kitten's reaction to an older cat: + +"The little one stands her ground, and when the old enemy comes near +enough kisses his nose and makes the peace. That is the lesson of +life: to kiss one's enemy's nose always standing one's ground." +~ + In the game known as Broken Telephone (or Chinese Whispers) a child +whispers a phrase into the ear of a second child, who whispers it into +the ear of a third child, and so one. Distortions accumulate, and when +the last child announces the phrase, it is comically different from the +original. The game works because each child does not merely degrade the +phrase, which would culminate in a mumble, but reanalyzes it, making a +best guess about the words the preceding child had in mind. + All languages change through the centuries. We do not speak like +Shakespeare (1564-1616), who did not speak like Chaucer (1343-1400), who +did not speak like the author of Beowulf (around 750-800). As the +changes take place, people feel the ground eroding under their feet and +in every era have predicted the imminent demise of the language. Yet +the twelve hundred years of changes since Beowulf have not left us +grunting like Tarzan, and that is because language change is a game of +Broken Telephone. + A generation of speakers uses their lexicon and grammar to produce +sentences. The younger generation listens to the sentences and tries to +infer the lexicon and grammar, the remarkable feat we call language +acquisition. The transmission of a lexicon and grammar in language +acquisition is fairly high in fidelity -- you probably can communicate +well with your parents and your children -- but it is never perfect. +Words rise and fall in popularity, as the needs of daily life change, and +also as the hip try to sound different from the dweebs and graybeards. +Speakers swallow or warp some sounds to save effort, and enunciate or +shift others to make themselves understood. Immigrants or conquerors +with regional or foreign accents may swamp the locals and change the pool +of speech available to children. + Children, for their part, do not mimic sentences like parrots but try +to make sense of them in terms of underlying words and rules. They may +hear a mumbled consonant as no consonant at all, or a drawn-out or +mispronounced vowel as a different vowel. They may fail to discern the +rationale for a rule and simply memorize its outputs as a list. Or they +may latch on to some habitual way of ordering words and hypothesize a new +rule to make sense of it. The language of their generation will have +changed, though it need not have deteriorated. Then the process is +repeated with their children. Each change may be small, but as changes +accumulate over centuries they reshape the language just as erosion and +sedimentation imperceptibly sculpt the earth. + -- Steven Pinker +~ +Ideas won't keep: something must be done about them. + -- Alfred North Whitehead +~ +Simple solutions seldom are. It takes a very unusual +mind to undertake analysis of the obvious. + -- Alfred North Whitehead +~ +Ninety-Ninety Rule n. + "The first 90% of the code accounts for the first 90% of the +development time. The remaining 10% of the code accounts for the +other 90% of the development time." Attributed to Tom Cargill of +Bell Labs, and popularized by Jon Bentley's September 1985 +"Bumper-Sticker Computer Science" column in "Communications of +the ACM". + It was there called the "Rule of Credibility", a name which +seems not to have stuck. Other maxims in the same vein include +the law attributed to the early British computer scientist Douglas +Hartree: "The time from now until the completion of the project +tends to become constant." +~ +Growth for the sake of growth is the ideology of the cancer cell. + -- Edward Abbey +~ +No matter how bad things get +you got to go on living, +even if it kills you. + -- Sholom Aleichem +~ +A man's palate can, in time, become accustomed to anything. + -- Napoleon Bonaparte +~ +Because they did not see merit where they should have seen it, +people, to express their regret, will go and leave a lot of +money to the very people who will be the first to throw stones +at the next person who has anything to say and finds a difficulty +in getting a hearing. + -- Samuel Butler +~ +I praise loudly, I blame softly. + -- Catherine II of Russia +~ +Deny yourself! You must deny yourself! +That is the song that never ends. + -- Goethe +~ +There are few more doleful sounds than the +laughter of a man without humour. + -- Michael Holroyd +~ +Children are the first to lose their innocence, +artists the second: idiots never. + -- Augustus John +~ +What is the good of being an island, +if you are not a volcanic island? + -- Wyndham Lewis +~ +Manners are a sensitive awareness of the feelings of others. If you +have that awareness, you have good manners, no matter what fork you use. + -- Emily Post +~ + Most human beings have to spend their lives in utter vulnerability. +All are murderable and torturable, and survive only through the restraint +shown by more powerful neighbors. All are born unequal, in terms of +capacity or strength. All are born to the inherent frailty of the human +condition, naked and helpless, vulnerable all through life to the will of +others, limited by ignorance, limited by physical weakness, limited by +fear, limited by the phobias that fear engenders. + For nearly three thousand years now, the political and social genius +of what we can permissibly call 'Western man' has struggled with these +brute facts of our unsatisfactory existence. Ever since the Hebrews +discovered personal moral responsibility and the Greeks discovered the +autonomy of the citizen, the effort has been made--with setbacks and +defeats, with dark ages and interregnums and any number of irrelevant +adventures on the side-to create a social order in which weak, fallible, +obstinate, silly, magnificent man can maintain his dignity and exercise his +free and responsible choice. + -- Adlai Stevenson +~ +Let us work without theorizing. It's the only way to +make life endurable. + -- Voltaire +~ +At many human faults a cat +Will never take offense: +Two things though they cannot stand: +The wretched Door, the horrid Fence. + -- Brian Aldiss +~ +I have never let my schooling interfere with my education. + -- Mark Twain +~ +[Chess is] as elaborate a waste of human intelligence +as you can find outside an advertising agency. + -- Raymond Chandler (1888-1959) U.S. crime-fiction writer +~ +Chess is a foolish expedient for making idle people believe they are +doing something very clever when they are only wasting their time. + -- George Bernard Shaw (1856-1960) Irish playwright, critic +~ +Every moment in life is precious; that's why I play chess. + -- Joseph Siroker, coffee house chess player and guru +~ +I don't want any yes-men around me. I want everybody +to tell me the truth even if it costs them their jobs. + -- Samuel Goldwyn +~ +A bank is a place that will lend you money if you can +prove that you don't need it. + -- Bob Hope +~ +Giving money and power to government is like +giving whiskey and car keys to teenage boys. + -- P. J. O'Rourke +~ +The very moral person usually has quite good manners because good +manners are usually some sort of basic consideration. + -- Louis Auchincloss +~ +It takes little talent to see clearly what lies under one's nose, +a good deal of it to know in which direction to point that organ. + -- W. H. Auden +~ +On two occasions, I have been asked [by members of Parliament], "Pray, Mr. +Babbage, if you put into the machine wrong figures, will the right answers +come out?" I am not able to rightly apprehend the kind of confusion of +ideas that could provoke such a question. + -- Charles Babbage +~ +Knowledge and human power are synonymous. + -- Francis Bacon +~ +The man who can drive himself further once the +effort gets painful is the man who will win. + -- Roger Bannister +~ +You grow up on the day you have your first real laugh at yourself. + -- Ethel Barrymore +~ +I think one of the reasons I'm popular again is +because I'm wearing a tie. You have to be different. + -- Tony Bennett +~ +When you come to a fork in the road, take it. + -- Yogi Berra +~ +With a gentleman I am always a gentleman and a half, +and with a fraud I try to be a fraud and a half. + -- Otto Von Bismarck +~ +Humor is by far the most significant activity of the human brain. + -- Edward De Bono +~ +I have discovered that we may be in some degree whatever character +we choose. Besides, practice forms a man to anything. + -- James Boswell +~ +There are no small parts, only small actors. + -- Marlon Brando +~ +Charm is a way of getting the answer 'yes' +without having asked any clear question. + -- Albert Camus +~ +Instead of looking at life as a narrowing funnel, we can see it +ever widening to choose the things we want to do, to take the +wisdom we've learned and create something. + -- Liz Carpenter +~ +Life at university, with its intellectual and inconclusive discussions +at a postgraduate level is on the whole a bad training for the real world. +Only men of very strong character surmount this handicap. + -- Paul Chambers +~ + The First Daily Sin is imitation. How can the network evening +news programs be so similar? We're in a commercial, highly competitive +struggle for viewers, and yet our solution for standing out in the +marketplace is--do just what the competition is doing. CBS research shows +that half the viewers of any given evening news broadcast--on CBS, NBC, or +ABC--only watch that particular program one night a week. The implication +is obvious: To these viewers, it doesn't make much of a difference which +one they watch--or whether they watch at all. + The Second Daily Sin is predictability. How often are you surprised +by something you see on the news? + The Third Daily Sin is artificiality. If you stop and really +listen to how a typical television reporter tells a story, you'll hear how +artificial it sounds. Even words--'pontiff' comes quickly to mind--that you +never hear in real life. Nobody talks that way--except for us. + The Fourth Daily Sin is laziness. The people I work with put in +long hours and are very devoted to their jobs. They're certainly not lazy +in the conventional sense. But I think we've all become lazy in our +thinking, in our reluctance to dig out original stories and come up with +new ways to tell them. + The Fifth Daily Sin is oversimplification. Our audience is smarter +and more thoughtful than a lot of us think. The people out there in America +know that life is not as simple as what they see on the news: a world of +heroes and villains, winners and losers, exploiters and victims. Yet that's +what we show them, night after night. + The Sixth Daily Sin is hype. Can you remember the last 'story +you'll never forget?' How about the one before that? I can't. Over the +years we've exaggerated so much that we've eroded our own ability to convey +what's truly significant. + The Seventh Daily Sin is cynicism. I think we're cynical about the +audience and cynical about our ability to make a difference in peoples' +lives. Journalists today are held in low esteem, but that doesn't have to +be. Our viewers and listeners are also hungry for honest information, for +help in coping with a bewildering world. We have an enormous opportunity to +win our good name back--and insure our own survival in the bargain. + -- Andrew Heyward, President of CBS News +~ +Desperation is sometimes as powerful an inspirer as genius. + -- Benjamin Disraeli +~ +Dr. Laura eats the Bible, live +------------------------------ + Laura Schlessinger is a radio personality who dispenses advice to +people who call in to her radio show. Paramount Television Group is +currently producing a "Dr. Laura" television show. She has become a +convert to Judaism, and now she is Ba'al T'shuvah. + Recently, she made some statements about homosexuals, based on +biblical edicts. The following is an open letter to Dr. Laura that +was posted on the internet. + +-- + +Dear Dr. Laura, + Thank you for doing so much to educate people regarding God's Law. +I have learned a great deal from your show, and I try to share that +knowledge with as many people as I can. + When someone tries to defend the homosexual lifestyle, for example, +I simply remind him that Leviticus 18:22 clearly states it to be an +abomination. End of debate. + Now I do need some advice from you, however, regarding some of the +specific laws and how to best follow them. + a) When I burn a bull on the altar as a sacrifice, I know it creates +a pleasing odour for the Lord (Lev.1:9). The problem is my neighbours. +They claim the odour is not pleasing to them. Should I smite them? + b) I would like to sell my daughter into slavery, as sanctioned in +Exodus 21:7. In this day and age, what do you think would be a fair +price for her? + c) I know that I am allowed no contact with a woman while she is in +her period of menstrual uncleanliness (Lev.15:19-24). The problem is, how +do I tell? I have tried asking, but most women take offence. + d) Lev. 25:44 states that I may indeed possess slaves, both male and +female, provided they are purchased from neighbouring nations. A friend +of mine claims that this applies to Mexicans, but not Canadians. Can +you clarify? Why can't I own Canadians? + e) I have a neighbour who insists on working on the Sabbath. Exodus +35:2 clearly states he should be put to death. Am I morally obligated +to kill him myself? + f) A friend of mine feels that even though eating shellfish is an +Abomination (Lev. 11:10), it is a lesser abomination than homosexuality. +I don't agree. Can you settle this? + g) Lev. 21:20 states that I may not approach the altar of God if I +have a defect in my sight. I have to admit that I wear reading glasses. +Does my vision have to be 20/20, or is there some wiggle room here? + h) Most of my male friends get their hair trimmed, including the hair +around their temples, even though this is expressly forbidden by Lev. +19:27. How should they die? + i) I know from Lev. 11:6-8 that touching the skin of a dead pig makes +me unclean, but may I still play football if I wear gloves? + j) My uncle has a farm. He violates Lev. 19:19 by planting two +different crops in the same field, as does his wife by wearing garments +made of two different kinds of thread (cotton / polyester blend). He +also tends to curse and blaspheme a lot. Is it really necessary that we +go to the trouble of getting the whole town together to stone them? +(Lev. 24:10-16) Couldn't we just burn them to death at a private family +affair like we do with people who sleep with their in-laws? (Lev. 20:14) + I know you have studied these things extensively, so I am confident +you can help. Thank you again for reminding us that God's word is +eternal and unchanging. +Your devoted disciple and adoring fan, +Aaron +~ +Logic, like whiskey, loses its beneficial effect +when taken in too large quantities. + -- Lord Dunsany +~ +It is strange to be known so universally and yet to be so lonely. + -- Albert Einstein +~ +It is theory that decides what can be observed. + -- Albert Einstein +~ + It would be too pat, perhaps, to say that modern people, men and +women, expect the unexpected. But they certainly expect, or are inured +to, constant change... Unquestionably, people sense constant movement, +change, alteration, and 'progress'. Even clothes are supposed to change +from year to year: there is this year's fashion, and last year's fashion, +and the fashions of the year before. + Then there is the idea of 'news,' that is, of something novel +happening every day, something worth reporting. Millions of people wake up +in the morning and watch the news on television; they may also listen to +radio news throughout the day and later catch the evening television news. +It would be unthinkable to read in the newspapers or to hear on television +that 'nothing much happened today.' There is always news, always something +going on, always change. Some days bring major headlines; other days are +quieter. But there is never no news: the message we get every day is that +things are never exactly the same. + -- Lawrence M. Friedman, from "The Horizontal Society" +~ +Education makes us more stupid than the brutes. A thousand voices +call to us on every hand, but our ears are stopped with wisdom. + -- Jean Giraudoux +~ +I think if you know what you believe, it makes it a lot easier +to answer questions. I can't answer your question. + -- Presidential candidate GW Bush, in response to a question about + whether he wished he could take back any of his answers in the + first debate. Reynoldsburg, Ohio, Oct. 4, 2000 +~ +Notice the difference between what happens when a man +says to himself, "I have failed three times," and what +happens when he says, "I am a failure." + -- S. I. Hayakawa +~ +If you always do what interests you, +at least one person is pleased. + -- Katharine Hepburn +~ +I cannot imagine a pleasant retired life of peace +and meditation without a cat in the house. + -- Paul Von Hindenberg +~ +Our achievements speak for themselves. What we have to keep track of +are our failures, discouragements, and doubts. We tend to forget the +past difficulties, the many false starts, and the painful groping. + -- Eric Hoffer +~ +It is cheering to see that the rats are +still around--the ship is not sinking. + -- Eric Hoffer +~ +Always keep faith in the mind of clear light and be safe! + -- Jeffrey Hopkins +~ + Until now, in the Western world, leisure was the exclusive +possession of a privileged class, which took upon itself the task of +playing on behalf of the whole overworked society. For all the injustices +which this entailed, it can be argued that this inequality in the +distribution of leisure gave the minority that enjoyed it a certain +responsibility for the quality of its amusements. + Today our machines have turned leisure into an almost universal +and obligatory state, one which many of us are finding enervating and even +painful. To live free of the burden of grinding toil is the oldest of +man's dreams. Yet no sooner has he rid himself of the accursed necessity +of earning his living by the sweat of his brow, then he is confronted by a +huge and alarming vacuum which -- if he is not to go mad -- must be quickly +and entirely filled. With this new and abundant leisure come certain +inescapable demands not to squander unimaginatively the resources that +industrialization has opened for us. Many of us-consciously seldom, but +unconsciously often-find this challenge so disturbing that we flee back to +artificially strenuous work or even to war in order to escape the +perplexities of choice presented to abundant leisure. + This is a problem which you, as members of the Mass Audience, will +be sharing with hundreds of millions of your fellow citizens the world over. + -- John Houseman +~ +If you want to write, keep cats. + -- Aldous Huxley +~ +It is because the body is a machine that education is possible. +Education is the formation of habits, a superinducing of an artificial +organization upon the natural organization of the body. + -- Thomas H. Huxley +~ +If you care enough for a result, you will most certainly attain it. + -- William James +~ +Excellence in any department can be attained only by the labor of +a lifetime; it is not to be purchased at a lesser price. + -- Samuel Johnson +~ +The surest way to forfeit the esteem of a cat is to +treat him as an inferior being. + -- Michael Joseph +~ +When you learn not to want things so badly, life comes to you. + -- Jessica Lange +~ +Don't tell me how hard you work. +Tell me how much you get done. + -- James Ling +~ +The misfortunes hardest to bear are these which never came. + -- James Russell Lowell +~ +Marriage is a great institution, but I'm not ready for an institution. + -- Mae West +~ +A bachelor is a selfish, undeserving guy who has +cheated some woman out of a divorce. + -- Don Quinn +~ +Serocki's Stricture: Marriage is always a bachelor's last option. +~ +The gods gave man fire and he invented fire engines. +They gave him love and he invented marriage. +~ +When two people are under the influence of the most violent, most insane, +most delusive, and most transient of passions, they are required to swear +that they will remain in that excited, abnormal, and exhausting condition +continuously until death do them part. + -- George Bernard Shaw +~ +Marriage is nature's way of keeping people from fighting with strangers. +~ +If you're upset and wonder what your wife does with +all the grocery money, stand sideways and look at yourself. +~ +I don't worry about terrorism; +I was married for two years. + -- Sam Kinison +~ +Every man wants a wife who is beautiful, understanding, +economical and a good cook. Unfortunately, the law allows +only one wife. +~ +Men are men before they are lawyers, or physicians, or merchants, or +manufacturers; and if you make them capable and sensible men, they +will make themselves capable and sensible lawyers or physicians. + -- John Stuart Mill +~ + Most of America's millionaires are first-generation rich. How is +it possible for people from modest backgrounds to become millionaires in +one generation? Why is it that so many people with similar socioeconomic +backgrounds never accumulate even modest amounts of wealth? + Most people who become millionaires have confidence in their own +abilities. They do not spend time worrying about whether or not their +parents were wealthy. They do not believe that one must be born wealthy. +Conversely, people of modest backgrounds who believe that only the wealthy +produce millionaires are predetermined to remain non-affluent. Have you +always thought that most millionaires are born with silver spoons in their +mouths? If so, consider the following facts that our research uncovered +about American millionaires: + * Only 19 percent receive any income or wealth of any kind from a + trust fund or an estate. + * Fewer than 20 percent inherited 10 percent or more of their wealth. + * More than half never received as much as $1 in inheritance. + * Fewer than 25 percent ever received "an act of kindness" of $10,000 + or more from their parents, grandparents, or other relatives. + * Ninety-one percent never received, as a gift, as much as $1 of the + ownership of a family business. + * Nearly half never received any college tuition from their parents + or other relatives. + * Fewer than 10 percent believe they will ever receive an inheritance + in the future. + America continues to hold great prospects for those who wish to +accumulate wealth in one generation. In fact, America has always been a +land of opportunity for those who believe in the fluid nature of our +nation's social system and economy. + -- from the book "The Millionaire Next Door" +~ + In Rome, Athens, and Sparta, honor alone was the reward for the +greatest of services. A wreath of oak-leaves or laurel, a statue or public +congratulations was an immense reward for winning a battle or capturing a town. + In these cities, a man who had accomplished some great feat was +sufficiently rewarded by the accomplishment itself. He could not meet any +of his fellow-citizens without feeling the pleasure of having done +something for them; he could calculate the extent of his services by the +number of his countrymen. Everybody is capable of doing good to one man, +but it is god-like to contribute to the happiness of an entire society." + -- Montesquieu +~ +I'll let you in on a secret. George Bush is not going to +be the next president of the United States. Get over it, +folks. It's not going to happen. + -- Michael Moore, 9/21/2000, quoted by Eun-Kyung Kim (AP) +~ + As I considered the premise put forth in the meeting room: that +the shortest road to wisdom and peace with the world is the one that turns +inward, away from direct sensory contact with other creatures. I will not +assert that meditation, psychotherapy, and philosophical introspection are +unproductive, but I simply can't accept that inward is the only or best way +for everyone to turn. The more disciplined practitioners of contemplative +traditions can turn inward and still get beyond the self, but many others +simply become swamped by self-indulgence. There are far too many people +living in our society who forget daily that other creatures--five kingdoms' +worth of them--are cohabiting the planet with us. + Over half a century ago, Robinson Jeffers suggested that it may be +just as valid to turn outward: "The whole human race spends too much +emotion on itself. The happiest and freest man is the scientist +investigating nature or the artist admiring it, the person who is +interested in things that are not human. Or if he is interested in human +beings, let him regard them objectively as a small part of the great music." + -- Gary Paul Nabhan +~ +SWM 33, black belt kama sutra, seeking wonder woman. bring me your +inhibitions and i will shatter them, you sweet thing. no violence. +no caustics. sin is the word of restriction, so come pet my crowley. +aleister can cook; aunt jemima treatment for all. +~ +Chance favors only the prepared mind. + -- Louis Pasteur +~ +I shut my eyes and all the world drops dead; +I lift my eyes and all is born again. + -- Sylvia Plath +~ +Books do furnish a room. + -- Anthony Powell +~ + For the benefit of the two or three other people in this society +who don't know what 'Cats' is about, here's the answer: It's about a bunch +of cats. The cats jump around in a postnuclear junkyard for some two and a +half hours, bumping and grinding to that curiously Mesozoic pop music for +which Andrew Lloyd Webber is famous--the kind of full-tilt truckin' that +sounds like the theme from 'The Mod Squad.' There's an Elvis impersonator +cat, and a cat that looks like Cyndi Lauper, and a cat that looks like +Phyllis Diller. All the other cast members look like Jon Bon Jovi with two +weeks of facial growth. + Sure, 'Cats' is allegedly based upon the works of T. S. Eliot, but +from what I could tell, the show had about as much to do with the author of +'The Waste Land' as those old Steve Reeves movies had to do with Euripides. +'Cats' is what 'Grease' would look like if all the cast members dressed up +like KISS. To give you an idea of how bad 'Cats' is, think of a musical +where you're actually glad to hear 'Memory' reprised a third time because +all the other songs are so awful. Think of a musical where the songs are so +bad that 'Memory' starts to sound like 'Ol' Man River' by comparison. +That's how bad 'Cats' is. + -- Joe Queenan +~ +Originality consists in trying to be like everybody else--and failing. + -- Raymond Radiguet +~ +green sandwich glowing bright, thou droppest mushrooms +on my tights, i slackly drool and whine and moan, for i +will soon give you a better, more acidic home. +~ +By all means marry. If you get a good wife you will become +happy, and if you get a bad one you will become a philosopher. + -- Socrates +~ +The life of the creative man is lead, directed, and controlled by +boredom. Avoiding boredom is one of our most important purposes. + -- Saul Steinberg +~ +The new generation of software must be designed from day one to be pirated. + -- CinemaElectric CEO Jim Robinson +~ +thought is the only antidote to stupidity, +but stupid people don't realize they need it. + -- fred t. hamster +~ +i don't have to speak +in haiku at all times since +i am way too cool + -- fred t. hamster +~ + What is more interesting in this world than our fellow human +beings and other living creatures? Why do we know so few of what must be +out there? What kind of philistines are we? Yet we can make a more +practical point than this. We need to interact with other species whether +we want to or not. They are our food and our environment: homes, scenery, +soil, even the oxygen in the air is provided by courtesy of plants and +photosynthesizing bacteria. We need actively to exploit our fellow +creatures to survive. This is not an option: we have to exploit them unless +we prefer to die. Therefore purely for selfish reasons (as well as for +reasons that we may hope are less selfish) we also need to conserve them. +Besides, even if we learnt to do without our fellow creatures--perhaps +found some inexhaustible supply of food on some distant planet--they would +not necessarily ignore us. We are flesh, too, for all our conceit, and many +are more than happy to feed upon us. To contain, exploit, or conserve our +fellow creatures we need to keep tabs on them. + -- Colin Tudge +~ + Nature's technology occurs on the surface of the same planet as +that of human culture, so it endures the same physical and chemical +limitations and must use the same materials. But nature copes and invents +in a way fundamentally different from what we do. At the very least, the +rate at which she alters herself is glacial by our cultural standard. + The very shapes of the two technologies differ dramatically. Just +look around you. Right angles are everywhere: the edges of this page, desk +corners, street corners, floor corners, shelves, doors, boxes, bricks, and +on and on. Then look at field, park, or forest. Where are the right angles? +Absent? No, but rare, which raises questions. Why so few right angles in +nature? Why do civilizations find them so serviceable? + Natural and human technologies differ extensively and pervasively. +We build dry and stiff structures; nature mostly makes hers wet and +flexible. We build of metals; nature never does. Our hinges mainly slide; +hers mostly bend. We do wonders with wheels and rotary motion; nature makes +fully competent boats, aircraft, and terrestrial vehicles that lack them +entirely. Our engines expand or spin; hers contract or slide. We fabricate +large devices directly; nature's large things are cunning proliferations of +tiny components. + -- Steven Vogel's, from "Cats' Paws and Catapults: Mechanical Worlds + of Nature and People" +~ +If you are not too long, I will wait here for you all my life. + -- Oscar Wilde +~ +I will be brief. Not nearly so brief as Salvador Dali, who gave the +world's shortest speech. He said I will be so brief I have already +finished, and he sat down. + -- Edward O. Wilson +~ +yahoo throws a shoe; +trouble in digital zone +e-log drops out chute + -- fred t. hamster +~ +Take care not to step on the foot of a learned idiot. +His bite is incurable. + -- Paul Gaugin +~ +A great sailor can sail even with a torn canvas. -- Seneca +~ +What if everything is an illusion and nothing exists? +In that case, I definitely overpaid for my carpet. + -- Woody Allen +~ + Unlike the Industrial Revolution, the Biomimicry Revolution +introduces an era based not on what we can extract from nature, but on what +we can learn from her... 'doing it nature's way' has the potential to +change the way we grow food, make materials, harness energy, heal +ourselves, store information, and conduct business. + In a biomimetic world, we would manufacture the way animals and +plants do, using sun and simple compounds to produce totally biodegradable +fibers, ceramics, plastics, and chemicals. Our farms, modeled on prairies, +would be self-fertilizing and pest-resistant. To find new drugs or crops, +we would consult animals and insects that have used plants for millions of +years to keep themselves healthy and nourished. Even computing would take +its cue from nature, with software that "evolves" solutions, and hardware +that uses the lock-and-key paradigm to compute by touch. + In each case, nature would provide the models: solar cells copied +from leaves, steely fibers woven spider-style, shatterproof ceramics drawn +from mother-of-pearl, cancer cures compliments of chimpanzees, perennial +grains inspired by tallgrass, computers that signal like cells, and a +closed-loop economy that takes its lessons from redwoods, coral reefs, and +oak-hickory forests. + The biomimics are discovering what works in the natural world, and +more important, what lasts. After 3.8 billion years of research and +development, failures are fossils, and what surrounds us is the secret to +survival. The more our world looks and functions like this natural world, +the more likely we are to be accepted on this home that is ours, but not +ours alone. + -- Janine M. Benyus +~ +It is better to have a lion at the head of an army of sheep, +than a sheep at the head of an army of lions. + -- Daniel Defoe +~ +stinky pinky in +my eye, i must cry out at +your nail gouge like lye. + -- fred t. hamster +~ +What one knows is, in youth, of little moment; +they know enough who know how to learn. + -- Henry Adams +~ +One of the greatest pains to human nature is the pain of a new idea. + -- Walter Bagehot +~ +A successful person is one who can lay a firm foundation +with the bricks that others throw at him or her. + -- David Brinkley +~ +Nobody who is not prepared to spoil cats will get +from them the reward they are able to give. + -- Compton MacKenzie +~ + The other night when I went into a restaurant in Santa Monica, +there was one president--Clinton. When I ordered a pizza there +was another one--Gore. When I paid the bill there was a third +president--Bush, and when I walked out onto Ocean Boulevard there +was no president because Bill is now the husband of a senator from +New York. + Today I am witnessing the spectacle of a hyper-technological +America which is sitting on the ruins of its electoral system +waiting for absentee ballots in the mail. + -- Bepe Severgnini, columnist for Milan's Corriere della Sera +~ +do you think it would hurt very much to swallow a whole egg, in shell? +would it be better to boil it first so it's hard or to leave it runny? +i'm wondering which way would be most likely to keep the shell from +breaking before it exits the body... +~ +I will do everything in my power to restrict abortions. + -- George W. Bush, Dallas Morning News, October 22, 1994 +~ +I saw the report that children in Texas are going hungry. Where? +You'd think the governor would have heard if there are pockets of +hunger in Texas. + -- George W. Bush, whose state ranks 2nd in total number of children + living in poverty, to Austin American Statesman, 12/18/99 +~ +"Please," Bush whimpers, his lips pursed in mock desperation, "don't kill me." + -- Bush mocking what Karla Faye Tucker said on Larry King when asked, + "What would you say to Governor Bush?" prior to her execution by + lethal injection as reported by Talk magazine, September 1999 +~ +An atmosphere of adolescence, a lack of gravitas--a carelessness, even a +recklessness, perhaps born of things having gone a bit too easily so far. + -- George Will, August 11, 1999, referring to Talk magazine's + interview with Bush +~ +Sitting down and reading a 500-page book on +public policy or philosophy or something. + -- GW Bush was asked to name something he isn't + good at by Talk magazine, September 1999 issue +~ +Bush should not advertise any allergy to serious things. A critical +mass of lightness in a candidate causes the public mind to snap closed, +with the judgement, "Not ready for prime time." + -- George Will, August 11, 1999 +~ +Bush is taking a political party on his ride. He and it will care if +on Nov. 7, 2000, people think of Gore or Bradley as an unexciting but +serious professor and of him as an amiable fraternity boy, but a boy. + -- George Will, August 11, 1999 +~ +What I'm against is quotas. I'm against hard quotas, quotas that +basically delineate based upon whatever. However they delineate, +quotas, I think, vulcanize society. + -- George W. Bush (Austin American-Statesman 3/23/99) +~ +Son, I love your strategy: Don't let them get to know you. + -- Barbara Bush +~ +If George is elected President, it would destroy my faith +in the office because he is such an ordinary guy. + -- David Rosen, Midland geologist & former neighbor of GW Bush +~ +He's this week's pet rock. + -- unknown, regarding GW Bush +~ +There ought to be limits to freedom. We're aware of the site, +and this guy is just a garbage man. + -- GW Bush, commenting on the website www.gwbush.com +~ +The Bush network is the only genuine network in the +Republican Party. It is the Establishment. + -- Bill Kristol, editor of The Weekly Standard +~ +"I will look at each piece of legislation when +it makes it to my desk," and "I will review that +when it makes it to my desk." + -- GW Bush, refusing to comment on anything + before it's absolutely unavoidable +~ +Reporters noticed four Latino men sitting at the Plaza, looking bored, +wearing matching shirts from Buena Vista Farms. Buena Vista, it turns +out, is a horse ranch run by Gerald Parsky, Bush's California chairman. +The four men said they were brought to the event and were being paid +their regular wages for attending. + -- Salon Magazine reporting on a Bush fundraiser in California +~ +Asked how he would define "compassionate conservatism," Bush replied: + "Making sure every child can read, making sure that we encourage + faith-based organizations... when it comes to helping neighbors in + need, making sure that our neighborhoods are safe, making sure that + the state of Texas recognizes that people from all walks of life have + got a shot at the Texas dream but, most importantly, making sure that + government is not the answer to people's problems." +This may be the only time a candidate promised not to solve any problems +(with the possible exception of Utah Phillips, who ran on the "Sloth & +Indolence" platform.) + -- from georgebush2000.com +~ +I'd demand a recount. + -- William F. Buckley, in the early '60s, in response to a + reporter's question asking him what he would do if he + were to win in his race for the office of Mayor of New York +~ +Half of the American people have never read a newspaper. +Half never voted for President. +One hopes it is the same half. + -- Gore Vidal +~ +Start every day off with a smile and get it over with. -- W. C. Fields +~ +Every man wishes to be wise, +and they who cannot be wise are almost always cunning. + -- Samuel Johnson +~ +Finally, in conclusion, let me say just this. + -- Peter Sellers +~ + When issues of public policy are discussed in the outward form +of an argument, often the conclusions reached are predetermined by +the assumptions and definitions inherent in a particular vision of +social processes. Different visions, of course, have different +assumptions, so it is not uncommon for people who follow different +visions to find themselves in opposition to one another across a +vast spectrum of unrelated issues, in such disparate fields as law, +foreign policy, the environment, racial policy, military defense, +education, and many others. To a remarkable extent, however, +empirical evidence is neither sought beforehand nor consulted after +a policy has been instituted. Facts may be marshaled for a position +already taken, but that is very different from systematically +testing opposing theories by evidence. Momentous questions are +dealt with essentially as conflicts of vision. + -- Thomas Sowell, from "The Vision of the Anointed" +~ +An election is a moral horror, as bad as a battle except for the +blood; a mud bath for every soul concerned in it. + -- George Bernard Shaw, "Back to Methuselah," 1921 +~ +If a person is obviously mentally disabled, such as having Down's +syndrome or Alzheimer's, decent people respond with sympathy and +understanding; and so why, if people merely have low IQs, are they +treated with ridicule and contempt? + -- Geoff Kuenning +~ +Always go to other people's funerals, +otherwise they won't come to yours. + -- Yogi Berra +~ +Animals are such agreeable friends-- +they ask no questions, +they pass no criticisms. + -- George Eliot +~ +All right everyone, line up alphabetically according to your height. + -- Casey Stengel +~ +Willy + +Willy flies without wings, +Fast as a shadow can go. +Black slash on white snow. + +Black face, cold blue eyes +fish pond, golden fish surprise. +Wet paws ... cold water! + +Black heart on white fur +Green eyes ... last sight for poor mouse, +Punctured by Willy. + +Willy stalks field mice. +Wild Bill to small buffalo. +Kills them just for show. + +Cat slays Two in Dawn +Homicide. Gruesome Remains +Left On Welcome Mat. + -- Bill Magee +~ +Petey + +Pete's eyes spit light; +Blue diamond icicles, +Red retinal fires. + +Sharp nose, fast heart, mouse +Scrounges by the sewer. Pete +Pounces, sharp claws out. + +Once quick with mouse life, +Now just carnage: tail, fur, skull. +Two-scoop burial. + +Nowhere a rabbit +Or goldfish or cardinal +who loves our Petey. + +They hate Pete there, +Crouched on the mossy rocks, +Prospecting for gold. + -- Bill Magee +~ +Louis + +Louis wants to play. +Its asthma, Lou! My asthma +Tears us apart, m'boy. + +Louis -- scared by deer -- +spent all July and August +Inside our closet. + +We don't let Louis out. +He is too dumb even for inside. +Eyes blue oceans of space. + +Hot air fluffs Louis. +Willy warms the newspapers. +Pete? Purrs in her arms. + -- Bill Magee +~ +dharma farm glistens +like an earthy gem; yaks romp +on verdant old hills. + -- fred t. hamster +~ +Noir comme le diable, [ Black as the devil, +chaud comme l'enfer, [ hot as hell, +pur comme un ange, [ pure as an angel, +doux comme l'amour. [ sweet as love. + -- Talleyrand, 18th century French diplomat, describing his concept + of a good cup of coffee +~ +Coffee has two virtues. It is wet and it is warm. -- Dutch proverb +~ +Coffee is the common man's gold, and like gold, it brings +to every person the feeling of luxury and nobility. + -- Sheik Abd-al-Kadir, In Praise of Coffee, 1587 +~ +Another head--and a black alpaca jacket and a +serviette this time--to tell us coffee is ready. +Not before it is time, too. + -- D. H. Lawrence, from "Sea and Sardinia" +~ +Give a frontiersman coffee and tobacco, +and he will endure any privation, +suffer any hardship, but let him be without +these two necessaries of the woods, and he +becomes irresolute and murmuring. + -- U.S. Army Lt. William Whiting, 1849 +~ +Coffee is real good when you drink it it gives you time to think. +It's a lot more than just a drink; it's something happening. +Not as in hip, but like an event, a place to be, but not like a +location, but like somewhere within yourself. It gives you time, +but not actual hours or minutes, but a chance to be, like be +yourself, and have a second cup. + -- Gertrude Stein +~ +Last comes the beverage of the Orient shore, +Mocha, far off, the fragrant berries bore. +Taste the dark fluid with a dainty lip, +Digestion waits on pleasure as you sip. + -- Pope Leo XII +~ +A very good drink they call Chaube that is almost as black as ink +and very good in illness, especially of the stomach. This they +drink in the morning early in the open places before everybody, +without any fear or regard, out of clay or China cups, as hot as +they can, sipping it a little at a time. + -- German physician and botanist Leonhard Rauwolf in 1582 +~ +The little campfires, rapidly increasing to hundreds in number, +would shoot up along the hills and plains, and as if by magic, +acres of territory would be illuminous with them. Soon they +would be surrounded by the soldiers, who made it an inevitable +rule to cook their coffee first. + -- John D. Bilings, a Union veteran, in his book, Hardtack and Coffee +~ +Only Irish coffee provides in a single glass +all four essential food groups: +alcohol, caffeine, sugar, and fat. + -- Alex Levine +~ +Strong coffee, much strong coffee, is what awakens me. +Coffee gives me warmth, waking, an unusual force and +a pain that is not without very great pleasure. + -- Napoleon Bonaparte +~ +I would rather suffer with coffee than be senseless. + -- Napoleon Bonaparte +~ +The ability to deal with people is as purchasable a commodity +as sugar or coffee. And I pay more for that ability than for +any other under the sun. + -- John D. Rockefeller, Jr. +~ +If you want to improve your understanding, drink coffee; +it is the intelligent beverage. + -- Sydney Smith +~ +Wine is for aging, not coffee. + -- Ken Hutchinson, Starsky and Hutch +~ +I have measured out my life with coffee spoons. + -- T. S. Elliot +~ +After a few months' acquaintance with European "coffee" one's mind +weakens, and his faith with it, and he begins to wonder if the rich +beverage of home, with it's clotted layer of yellow cream on top of +it, is not a mere dream after all, and a thing which never existed. + -- Mark Twain, in "A Tramp Abroad" +~ +The morning cup of coffee has an exhilaration about it which the +cheering influence of the afternoon or evening cup of tea cannot +be expected to reproduce. + -- Oliver Wendall Holmes, Sr. +~ +It's just like when you've got some coffee that's too black, which means +it's too strong. What do you do? You integrate it with cream, you make +it weak. But if you pour too much cream in it, you won't even know you +ever had coffee. It used to be hot, it becomes cool. It used to be +strong, it becomes weak. It used to wake you up, now it puts you to sleep. + -- Malcolm X, 196, Message to the Grass Roots +~ +Make my coffee like I like my men: hot, black, and strong. + -- Willona Wood, Good Times +~ +Never drink black coffee at lunch; +it will keep you awake in the afternoon. + -- Jilly Cooper, 1970, "How to Survive from Nine to Five" +~ +Resolve to free yourselves from the slavery +of the tea and coffee and other slop-kettle. + -- William Cobbett, 1829, "Advise to Young Men" +~ +Tobacco, coffee, alcohol, hashish, prussic acid, +strichnine, are weak dilutions: the surest poison is time. + -- Ralph Waldo Emerson, "Society and Solitude: Old Age" +~ +Coffee, according to the women of Denmark, is to the body +what the Word of the Lord is to the soul. + -- Isak Dinesen, 1934 +~ +Coffee: we can get it anywhere, and get as loaded +as we like on it, until such teeth-chattering, +eye-bulging, nonsense-gibbering time as we may be +classified unable to operate heavy machinery. + -- Joan Frank, 1991 +~ +Many people are like instant coffee: +the minute they get in hot water they dissolve. + -- Anonymous, from Toronto Globe and Mail; July 10, 1993 +~ +The discovery of coffee has enlarged the realm of illusion +and given more promise to hope. + -- Isidore Bourdon +~ +The powers of a man's mind are directly proportional +to the quantity of coffee he drank. + -- Sir James MacKintosh - 18th century philosopher +~ +If it wasn't for coffee, I'd have no discernible personality at all. + -- David Letterman Esquire Interview Fall '94 +~ +Ah! How sweet coffee tastes! +Lovelier than a thousand kisses, +sweeter far than muscatel wine! + -- From J. S. Bach's "Coffee Cantata," 1732 +~ +You make good coffee... You're a slob, +but you make good coffee. + -- Cher, in "Moonstruck" +~ +See how special you are? I serve you coffee in the parlor. + -- Anthony Quinn to Sophia Loren in "The Black Orchid" +~ +The first cup is for the guest, +the second for enjoyment, +the third for the sword. + -- Arabic proverb about coffee +~ +The vacuum pot is truly the CD player of coffeemakers; +all you taste is the coffee. + -- Corby Kummer, food expert +~ +People are kind of like zombies in Hong Kong nowadays. +You don't see that glow anymore. In terms of colour +Hong Kong looks a bit grey. To counter that, I think +we should give out free espresso samples to give people +more caffeine; triple espresso with Irish cream syrup, +iced! People just need to get a bit more wired. + -- David Wu, Actor and Channel V VJ, quoted in Post Magazine 29 August 99 +~ +a small fish is this +with so many other big +tasks still to complete +~ +For myself I am an optimist--it does not seem +to be much use being anything else. + -- Winston Churchill +~ +One of the advantages of being disorderly is that one +is constantly making exciting discoveries. + -- A. A. Milne +~ +Computers are useless. They can only give you answers. + -- Pablo Picasso +~ +There are two means of refuge from the +miseries of life: music and cats. + -- Albert Schweitzer +~ +Among those whom I like or admire, I can find no common denominator, +but among those whom I love, I can: all of them make me laugh. + -- W. H. Auden +~ +A clever man commits no minor blunders. + -- Johann Wolfgang von Goethe +~ +my fingers have slipped; +keyboard greasy from pizza +sloppily consumed. + -- fred t. hamster +~ +One could not be a successful scientist without realizing that, +in contrast to the popular conception supported by newspapers +and mothers of scientists, a goodly number of scientists are +not only narrow-minded and dull, but also just stupid. + -- James Watson +~ +Fine things books, but perhaps the moment has come to stop taking them +so seriously. Who was it that said people who are always reading never +discover anything? I'm not sure if that is true, but I do know that +reading and thinking are not necessarily the same thing. Sometimes +reading supplies the most cunning of all means of avoiding thought. It +would be good once in awhile to try thinking without the stimulus of +books, to become not an out of-the-box -- never, please, that -- but at +least an out-of-the-book thinker. Books may furnish a room, but there +surely are other things quite as suitable for furnishing a mind. Time, +I think, for me to attempt to find out what these might be. + -- Joseph Epstein +~ +Economics is extremely useful as a form of employment for economists. + -- John Kenneth Galbraith +~ +You can observe a lot by just watching. -- Yogi Berra +~ +No wonder Al Gore thinks he is president--this is a most confusing time. +The leading rap singer is white, the world's best golfer is black, and +Bill Clinton just got back from Vietnam. + -- Paul Harvey, Early December 2000 +~ +The most exciting phrase to hear in science, the one that heralds new +discoveries, is not 'Eureka!' (I've found it!), but "That's funny...". + -- Isaac Asimov +~ +Is not life a hundred times too short for us to bore ourselves? + -- Friedrich Nietzsche +~ +Dr. Seuss takes a look at election recounts: +I cannot count them in a box. +I cannot count them with a fox. +I cannot count them by computer. +I will not with a Roto-Rooter. +I cannot count them card-by-card. +I will not 'cause it's way too hard. +I cannot count them on my fingers. +I will not while suspicion lingers. +I'll leave the country in a jam-- +I won't count ballots, Sam-I-Am. +~ + For some years, we have been surprised and distressed by the +intellectual trends in certain precincts of American academia. +Vast sectors of the humanities and the social sciences seem to +have adopted a philosophy that we shall call, for want of a better +term, "postmodernism": an intellectual current characterized by +the more-or-less explicit rejection of the rationalist tradition +of the Enlightenment, by theoretical discourses disconnected from +any empirical test, and by a cognitive and cultural relativism +that regards science as nothing more than a 'narration,' a 'myth' +or a social construction among many others. + To respond to this phenomenon, one of us (Sokal) decided to try +an unorthodox (and admittedly uncontrolled), experiment: submit to +a fashionable American cultural-studies journal, "Social Text," a +parody of the type of work that has proliferated in recent years, +to see whether they would publish it. The article, entitled +"Transgressing the Boundaries: Toward a Transformative Hermeneutics +of Quantum Gravity", is chock-full of absurdities and blatant +non-sequiturs. In addition, it asserts an extreme form of cognitive +relativism: after mocking the old-fashioned "dogma" that "there +exists an external world, whose properties are independent of any +individual human being and indeed of humanity as a whole", it +proclaims categorically that "physical `reality', no less than +social `reality', is at bottom a social and linguistic construct". +By a series of stunning leaps of logic, it arrives at the conclusion +that "the [Pi] of Euclid and the G of Newton, formerly thought to be +constant and universal, are now perceived in their ineluctable +historicity; and the putative observer becomes fatally de-centered, +disconnected from any epistemic link to a space-time point that can +no longer be defined by geometry alone". The rest is in the same vein. + And yet, the article was accepted and published. Worse, it was +published in a special issue of 'Social Text' devoted to rebutting the +criticisms leveled against postmodernism and social constructivism by +several distinguished scientists. For the editors of 'Social Text,' +it was hard to imagine a more radical way of shooting themselves in +the foot. + -- Alan Sokal and Jean Bricmont, "Fashionable Nonsense: Postmodern + Intellectuals' Abuse of Science." +~ +May you live all the days of your life. + -- Jonathan Swift +~ + We take pleasure in answering thus prominently the communication +below, expressing at the same time our great gratification that its +faithful author is numbered among the friends of The Sun: + 'I am 8 years old. Some of my little friends say there is no Santa +Claus. Papa says, "If you see it in The Sun, it's so." Please tell me +the truth, is there a Santa Claus?' -- Virginia O'Hanlon + Virginia, your little friends are wrong. They have been affected by +the skepticism of a sceptical age. They do not believe except they see. +They think that nothing can be which is not comprehensible by their little +minds. All minds, Virginia, whether they be men's or children's, are +little. In this great universe of ours, man is a mere insect, an ant, in +his intellect as compared with the boundless world about him, as measured +by the intelligence capable of grasping the whole of truth and knowledge. + Yes, Virginia, there is a Santa Claus. + He exists as certainly as love and generosity and devotion exist, and +you know that they abound and give to your life its highest beauty and joy. +Alas! how dreary would be the world if there were no Santa Claus! It would +be as dreary as if there were no Virginias. There would be no childlike +faith then, no poetry, no romance to make tolerable this existence. We +should have no enjoyment, except in sense and sight. The external light +with which childhood fills the world would be extinguished. + Not believe in Santa Claus! You might as well not believe in fairies. +You might get your papa to hire men to watch in all the chimneys on +Christmas eve to catch Santa Claus, but even if you did not see Santa Claus +coming down, what would that prove? Nobody sees Santa Claus, but that is no +sign that there is no Santa Claus. The most real things in the world are +those that neither children nor men can see. Did you ever see fairies +dancing on the lawn? Of course not, but that's no proof that they are not +there. Nobody can conceive or imagine all the wonders there are unseen and +unseeable in the world. + You tear apart the baby's rattle and see what makes the noise inside, +but there is a veil covering the unseen world which not the strongest man, +nor even the united strength of all the strongest men that ever lived could +tear apart. Only faith, poetry, love, romance, can push aside that curtain +and view and picture the supernal beauty and glory beyond. Is it all real? +Ah, Virginia, in all this world there is nothing else real and abiding. + No Santa Claus? Thank God he lives and lives forever. A thousand +years from now, Virginia, nay 10 times 10,000 years from now, he will +continue to make glad the heart of childhood. + Merry Christmas and a Happy New Year!!!! + -- The editorial "Yes, Virginia, There is a Santa Claus," first printed + in the New York Sun in 1897. +~ +Most Internet appliances that I've seen are simply dumbed-down +PCs, and some analysts call any damn thing that can access the +Internet--other than a PC--an Internet appliance, even if it's +your cell phone or a wireless PDA. That leads to some huge +market projections, which are pretty much meaningless as a +single figure. I think the idea of Internet appliances as a +unique market is a pipe dream, with a lot of people sucking +off that hookah. + -- Will Strauss, president of Forward Concepts, a market + research firm +~ +Science is organized knowledge. Wisdom is organized life. + -- Immanuel Kant +~ +Ah but a man's reach should exceed his grasp, or what's a heaven for? + -- Robert Browning +~ +She played with her cat, and it was a wonder to watch the white hand +and the white paw frolic in the shade of night. + -- Paul Verlaine +~ +Just because something doesn't do what you planned +it to do doesn't mean it's useless. + -- Thomas Alva Edison +~ +Spare no expense to make everything as economical as possible. + -- Samuel Goldwyn +~ +smirking idiot +drools on oval office desk +while country expires + -- about the dubya years +~ +brave new president +laughs as he pulls death switch for +the innocents too + -- about the dubya years +~ +they call me a shrub +but i shrug and ask daddy +for black ops killing + -- about the dubya years +~ +educatedest +moron ever known on earth: +gee dubya bee, dude. + -- about the dubya years +~ +bush bipartisan +means "we do it my way" and +bend over for me + -- about the dubya years +~ +smiling lies, dumb looks, +idea-free, insults all, +my prez--GWB! + -- about the dubya years +~ +my feeling is that humans are both divine and full of sh*t. +most people are drowning in their own mental diarrhea. + +solution? a sewer pipe for the mind, perhaps. or at least to +realize that the contagion is part of us and has to be purified +from within. there is no external enemy to kill or persecute; +all evil deeds and words and thoughts are our own property. +no one else can take them away for you; you must "dispose of +properly". clean these damaged mental constituents using your +will to heal your consciousness. +~ +If you believe that feeling bad or worrying long enough will +change a past or future event, then you are residing on another +planet with a different reality system. + -- William James +~ +Hanging is too good for a man who makes puns; +he should be drawn and quoted. + -- Fred Allen +~ +A day without rebooting is like a day without Microsoft. -- fred t. hamster +~ +I've met many thinkers and many cats, +but the wisdom of cats is infinitely superior. + -- Hippolyte Taine +~ + People living and working in a business system cannot change it. +Their perspectives are foreshortened, their information gathering and +measurement systems reinforce the past, and their incentives encourage +continuity. Archimedes proclaimed, 'Give me where to stand, and I will move +the earth.' But where should those who might change a business system be +standing? + The answer is that every organization needs two business systems. +Borrowing a term from linguistics, we shall call them the 'surface system' +and the 'deep system.' The surface system is comprised of the organized +tasks of the business processes, with their attendant jobs, structures, +systems, and values. But this surface system is in periodic need of major +change. Accomplishing that change is the job of the deep system. + The deep system creates no customer value; it makes no products and +delivers no services. It doesn't process orders, develop new products, or +create value for customers. Rather it monitors, governs, adjusts, and +reforms the surface system that does create customer value. A company's +deep system bears the responsibility for detecting external changes, +determining what those changes mean, and intervening to modify or transform +the surface system accordingly. The deep system, working beneath the +surface, embodies the capacity to change. + The deep system continually hurls challenges: Is this still the +right way or the best way to do things? If not, what is? The deep system +ensures that the appropriate internal change--moderate or radical--takes +place, shaping and reshaping the organization to take account of, and +whenever possible take advantage of, ongoing external change. + -- Michael Hammer +~ +There is nothing more difficult to take in hand, more perilous to +conduct, or more uncertain in its success, than to take the lead in the +introduction of a new order of things. + -- Nicolas Machiavelli +~ +Only those who take leisurely what the people of the world are busy +about can be busy about what the people of the world take leisurely. + -- Chang Ch'ao +~ +I am opposed to millionaires, but it would +be dangerous to offer me the position. + -- Mark Twain +~ +Never eat more than you can lift. -- the muppet Miss Piggy +~ +tired of knowing +politics are poison, but +it has to be said... + +dubya bush will lead +to lower expectations +at every level + -- fred t. hamster +~ + Distance changes utterly when you take the world on foot. A mile +becomes a long way, two miles literally considerable, ten miles +whopping, fifty miles at the very limits of conception. The world, +you realize, is enormous in a way that only you and a small community +of fellow hikers know. Planetary scale is your little secret. + Life takes on a neat simplicity, too. Time ceases to have any +meaning. When it is dark, you go to bed, and when it is light again +you get up, and everything in between is just in between. It's quite +wonderful, really. + You have no engagements, commitments, obligations, or duties; no +special ambitions and only the smallest, least complicated of wants; +you exist in tranquil tedium, serenely beyond the reach of +exasperation, 'far removed from the seats of strife,' as the early +explorer and botanist William Bartram put it. All that is required +of you is a willingness to trudge. + There is no point in hurrying because you are not actually going +anywhere. However far or long you plod, you are always in the same +place: in the woods. It's where you were yesterday, where you will +be tomorrow. The woods is one boundless singularity. Every bend in +the path presents a prospect indistinguishable from every other, every +glimpse into the trees the same tangled mass. For all you know, your +route could describe a very large, pointless circle. In a way, it +would hardly matter. + -- Bill Bryson, "A Walk in the Woods: Rediscovering America on the + Appalachian Trail." +~ +You can't cheat an honest man. -- W. C. Fields +~ +Most conversations are simply monologues +delivered in the presence of a witness. + -- Margaret Millar +~ +There's nothing to writing. All you do is sit down +at a typewriter and open a vein. + -- Red Smith +~ +Omit needless words. Vigorous writing is concise. A sentence +should contain no unnecessary words, a paragraph no unnecessary +sentences, for the same reason that a drawing should have no +unnecessary lines and a machine no unnecessary parts. + -- William Strunk Jr. +~ + The small and elite group of scientists who create most of the +flavor in most of the food now consumed in the United States are +called 'flavorists.' They draw on a number of disciplines in their +work: biology, psychology, physiology, and organic chemistry. A +flavorist is a chemist with a trained nose and a poetic sensibility. +Flavors are created by blending scores of different chemicals in +tiny amounts--a process governed by scientific principles but +demanding a fair amount of art. In an age when delicate aromas and +microwave ovens do not easily co-exist, the job of the flavorist is +to conjure illusions about processed food and, in the words of one +flavor company's literature, to ensure 'consumer likeability.' The +flavorists with whom I spoke were discreet, in keeping with the +dictates of their trade. They were also charming, cosmopolitan, and +ironic. They not only enjoyed fine wine but could identify the +chemicals that give each grape its unique aroma. One flavorist +compared his work to composing music. A well-made flavor compound +will have a 'top note' that is often followed by a 'dry-down' and a +'leveling-off,' with different chemicals responsible for each stage. +The taste of a food can be radically altered by minute changes in the +flavoring combination. 'A little odor goes a long way,' one +flavorist told me. + In order to give a processed food a taste that consumers will +find appealing, a flavorist must always consider the food's +'mouthfeel'--the unique combination of textures and chemical +interactions that affect how the flavor is perceived. Mouthfeel can +be adjusted through the use of various fats, gums, starches, +emulsifiers, and stabilizers. The aroma chemicals in a food can be +precisely analyzed, but the elements that make up mouthfeel are much +harder to measure. How does one quantify a pretzel's hardness, a +french fry's crispness? Food technologists are now conducting basic +research in rheology, the branch of physics that examines the flow and +deformation of materials. A number of companies sell sophisticated +devices that attempt to measure mouthfeel. The TA.XT2i Texture +Analyzer, produced by the Texture Technologies Corporation, of +Scarsdale, New York, performs calculations based on data derived from +as many as 250 separate probes. It is essentially a mechanical mouth. +It gauges the most-important rheological properties of a food--bounce, +creep, breaking point, density, crunchiness, chewiness, gumminess, +lumpiness, rubberiness, springiness, slipperiness, smoothness, +softness, wetness, juiciness, spreadability, springback, and tackiness. + -- Eric Schlosser, from "Why McDonald's Fries Taste So Good", + Atlantic Monthly, 2001. +~ +We haven't failed. We now know a thousand things that +won't work, so we are much closer to finding what will. + -- Thomas Edison +~ +If you surveyed a hundred typical middle-aged Americans, I bet you'd find that +only two of them could tell you their blood types, but every last one of them +would know the them song from 'The Beverly Hillbillies'. + -- Dave Barry +~ +The first thing to learn in intercourse with others is +non-interference with their own particular ways of being +happy, provided those ways do not assume to interfere by +violence with ours. + -- William James +~ +My dad was the town drunk. Usually that's not so bad, but New York City? + -- Henny Youngman +~ +Ninety percent of the politicians give the other +ten percent a bad name. + -- Henry Kissinger +~ +You can imagine my embarrassment when I killed the wrong guy. + -- Joe Valachi +~ +We must abandon the prevalent belief in the +superior wisdom of the ignorant. + -- Daniel Boorstin +~ + If the primary effect of the media in the late twentieth century was +to turn nearly everything that passed across their screens into +entertainment, the secondary and ultimately more significant effect was +to force nearly everything to turn itself into entertainment in order to +attract media attention. Daniel Boorstin had coined the term 'pseudo- +event' to describe events that had been concocted by public relations +practitioners to get attention from the press. Movie premieres, +publishing parties, press conferences, balloon crossings, sponsored +sporting contests, award ceremonies, demonstrations and hunger strikes, +to name just a few examples, all were synthetic, manufactured pseudo- +events that wouldn't have existed if someone hadn't been seeking +publicity and if the media hadn't been seeking something to fill their +pages and airwaves, preferably something entertaining. + But the idea of pseudo-events almost seemed quaint by the late +twentieth century. Most people realized that the object of virtually +everyone in public life of any sort was to attract the media and that +everyone from the top movie stars to the parents of septuplets now had +to have a press agent to promote them. What most people were also coming +to realize, if only by virtue of how much the media had grown, was that +pseudo-events had proliferated to such an extent that one could hardly +call them events anymore because there were no longer any seams between +them and the rest of life, no way of separating the pseudo from the +so-called authentic. Almost everything in life had appropriated the +techniques of public relations to gain access to the media, so that it +wasn't the pseudo-event one was talking about anymore when one cited the +cleverness of PR men and women: it was pseudo-life. + Yet not even pseudo-life did full justice to the modern condition. +That's because the media were not just passively recording the public +performances and manipulations of others, even when life was nothing but +manipulations. Having invited these performances in the first place, +the media justified covering them because they were receiving media +attention, which is every bit as convoluted as it sounds. The result +was to make of modern society one giant Heisenberg effect, in which the +media were not really reporting what people did; they were reporting +what people did to get media attention. In other words, as life was +increasingly being lived for the media, so the media were increasingly +covering themselves and their impact on life. + -- Neal Gabler, in "Life, the Movie" +~ +So little time, so little to do. + -- Oscar Levant +~ +Consistency requires you to be as ignorant +today as you were a year ago. + -- Bernard Berenson +~ +Who becomes a CPO? Stephanie Perrin, the chief privacy officer of +Montreal-based Zero-Knowledge Systems, explains: "Obviously, we're +not going to just pick somebody from the legal department," because +privacy is more than a matter of just following the law. "You have +to have a fundamental commitment to - dare I say it? - morality. +Privacy is not just good business. We are framing the information +age, and it is important to take that job seriously. We really do +look at privacy as a human right, and not just a luxury item for +spoiled North Americans. We're talking about the global information +infrastructure." +~ +The sweetest joy, the wildest woe is love. + -- Pearl Bailey +~ +He's simply got the instinct for being unhappy highly developed. + -- H.H. Munro +~ +No easy problems ever come to the president of the United States. +If they are easy to solve, someone else has solved them. + -- Dwight D. Eisenhower +~ +Your presence in the class is disruptive and affects the other students! + -- A teacher's complaint to the teenage Albert Einstein +~ + When you're in a world of experts and so-called professionals, the +common sense of the people is marginalized. + I'll give you an example. We're talking about building a building +not too far from where I live, in this warehouse district. It's an old, +totally undistinguished warehouse in what has been designated a historic +district. So we're working up the Environmental Impact Report, and you hire +a consultant. The consultant is this young lady who says, 'The developer +wants to tear this whole warehouse down and build an eight-story building, +and that would have a negative impact on the historic warehouse district.' + I say, 'Wait a minute. Why? It's an eight-story building. What's +different? Why do you say it's negative? Why isn't it positive? It will +bring more people -- there are not enough people down here.' + 'No,' she said, 'in my professional judgment...' And that is given +weight by the courts. Her opinion could have more sway than the city +council, the manager, the mayor, and all the people of Oakland put together. + That's amazing. That's what I call 'expertise versus common sense.' +What do they call those guys in Russia: the nomenklatura? The class of +folks who run things. I think that's an important issue: reclaiming the +power of ordinary people to control their lives. Every time we turn around, +we've got some kind of a state or federal rule or regulation. + 'In my professional judgment...' That whole academic discipline was +probably created less than 25 years ago, and that lore -- I call it lore; +these are like stories you tell around the campfire -- is then raised to +the level of legal significance with greater authority than the vote of the +people and the elected representatives. The people have lost their +democratic right to make a decision over the shape of their lives, and you +can find that happening on a lot of issues more controversial than historic +preservation. + -- Jerry Brown +~ +Gardens are not made by sitting in the shade. + -- Rudyard Kipling +~ +Man invented language to satisfy his deep need to complain. + -- Lilly Tomlin +~ +Technology is a gift of God. After the gift of life it is perhaps +the greatest of God's gifts. It is the mother of civilizations, of +arts and of sciences. + -- Freeman Dyson +~ +You make men love their government and their country by giving them the +kind of government and the kind of country that inspire respect and love; +a country that is free and unafraid, that lets the discontented talk in +order to learn the causes of their discontent and end those causes, that +refuses to impel men to spy on their neighbors, that protects its citizens +vigorously from harmful acts while it leaves the remedies for objectionable +ideas to counterargument and time. + -- Zechariah Chafee, Jr. +~ +Kites rise highest against the wind--not with it. + -- Winston Churchill +~ +Shall I tell you the secret of the true scholar? It is this: every +man I meet is my master in some point, and in that I learn of him. + -- Ralph Waldo Emerson +~ +A cucumber should be well-sliced, dressed with +pepper and vinegar, and then thrown out. + -- Samuel Johnson +~ + Technology is often defined as the creation of tools to gain control +over the environment. However, this definition is not entirely sufficient. +Humans are not alone in their use or even creation of tools. Orangutans in +Sumatra's Suaq Balimbing swamp make tools out of long sticks to break open +termite nests. Crows fashion tools from sticks and leaves. The leaf-cutter +ant mixes dry leaves with its saliva to create a paste. Crocodiles use tree +roots to anchor dead prey. + What is uniquely human is the application of knowledge--recorded +knowledge--to the fashioning of tools. The knowledge base represents the +genetic code for the evolving technology. And as technology has evolved, +the means for recording this knowledge base has also evolved, from the oral +traditions of antiquity to the written design logs of nineteenth-century +craftsmen to the computer-assisted design databases of the 1990s. + Technology also implies a transcendence of the materials used to +comprise it. When the elements of an invention are assembled in just the +right way, they produce an enchanting effect that goes beyond the mere +parts. When Alexander Graham Bell accidentally wire-connected two moving +drums and solenoids (metal cores wrapped in wire) in 1875, the result +transcended the materials he was working with. For the first time, a human +voice was transported, magically it seemed, to a remote location. Most +assemblages are just that: random assemblies. But when materials--and in +the case of modern technology, information--are assembled in just the +right way, transcendence occurs. The assembled object becomes far greater +than the sum of its parts. + -- Ray Kurzweil +~ +All the world's a stage and most of us are desperately unrehearsed. + -- Sean O'Casey +~ +The foresight of the astronomer who predicts with complete precision +the state of the solar system many years in advance is absolutely the +same in kind as that of the savage who predicts the next sunrise. +The only difference lies in the extent of their knowledge. + -- Auguste Comte +~ +Nothing strengthens the judgment and quickens +the conscience like individual responsibility. + -- Elizabeth Cady Stanton +~ + The bug, that perverse and elusive malfunctioning of hardware and +later of software, was born in the nineteenth century. It was already +accepted shop slang as early as 1878, when Thomas Edison described his +style of invention in a letter to a European representative: 'The first +step is an intuition and it comes with a burst, then difficulties arise-- +this thing gives out and then that--"Bugs"--as such little faults and +difficulties are called--show themselves, and months of intense watching, +study and labor are requisite before commercial success--or failure--is +certainly reached.' + Edison implies that this use of 'bug' had not begun in his laboratory +but was already standard jargon. The expression seems to have originated +as telegrapher's slang. Western Union and other telegraph companies, with +their associated branch offices, formed America's first high-technology +system. About the time of Edison's letter, Western Union had over twelve +thousand stations, and it was their condition that probably helped inspire +the metaphor. City offices were filthy, and clerks exchanged verse about +the gymnastics of insects cavorting in the cloakrooms. When, in 1945, a +moth in a relay crashed the Mark II electromechanical calculator that the +Navy was running at Harvard--it can still be seen taped in the original +logbook--the bug metaphor had already been around for at least seventy- +five years. + -- Edward Tenner, from "Why Things Bite Back" +~ +Look back, and smile on perils past. + -- Sir Walter Scott +~ +I never see what has been done; I only see what remains to be done. + -- Marie Curie +~ +Cast a cold eye +On life, on death. +Horseman, pass by! + -- William Butler Yeats' auto-epitath +~ + The bug, that perverse and elusive malfunctioning of hardware and +later of software, was born in the nineteenth century. It was already +accepted shop slang as early as 1878, when Thomas Edison described his +style of invention in a letter to a European representative: 'The first +step is an intuition and it comes with a burst, then difficulties arise-- +this thing gives out and then that--"Bugs"--as such little faults and +difficulties are called--show themselves, and months of intense watching, +study and labor are requisite before commercial success--or failure--is +certainly reached.' + Edison implies that this use of 'bug' had not begun in his +laboratory but was already standard jargon. The expression seems to have +originated as telegrapher's slang. Western Union and other telegraph +companies, with their associated branch offices, formed America's first +high-technology system. About the time of Edison's letter, Western Union +had over twelve thousand stations, and it was their condition that probably +helped inspire the metaphor. City offices were filthy, and clerks exchanged +verse about the gymnastics of insects cavorting in the cloakrooms. When, in +1945, a moth in a relay crashed the Mark II electromechanical calculator +that the Navy was running at Harvard--it can still be seen taped in the +original logbook--the bug metaphor had already been around for at least +seventy-five years. + -- Edward Tenner, "Why Things Bite Back" +~ +But the true threats to stability and peace are these nations that are not +very transparent, that hide behind the--that don't let people in to take a +look and see what they're up to. They're very kind of authoritarian regimes. +The true threat is whether or not one of these people decide, peak of anger, +try to hold us hostage, ourselves; the Israelis, for example, to whom we'll +defend, offer our defenses; the South Koreans. + -- George W. Bush, Media roundtable, Washington, D.C., March 13, 2001 +~ +The charity that is a trifle to us can be precious to others. + -- Homer +~ +It wasn't until late in life that I discovered +how easy it is to say "I don't know". + -- W. Somerset Maugham +~ +It is a good morning exercise for a research scientist to discard a +pet hypothesis every day before breakfast. It keeps him young. + -- Konrad Lorenz +~ +He is the best physician who is the most ingenious inspirer of hope. + -- Samuel Taylor Coleridge +~ +Failure is not an option. + -- Gene Kranz +~ + The year 1950 was the last full cry of urban America, at least on +the surface. It was the year many of the cities visited in this book +reached their historic peaks in population. Everybody was working, in folk +memory, and in fact. Armies clad in overalls poured out of plants at +quitting time or watched as the next shift filed in. Houses cost a couple +of thousand bucks, or in high-cost cities some fifteen thousand. The +mortgage was often less than a hundred a month. The teeming ethnic ghettos +of the early century had given way to a more comfortable life, with +religion and ethnicity, race and class still used as organizing principles +for the neighborhood. The rough edges of the immigrant 'greenhorns' were +worn smooth, and a confident younger generation now entered a fuller, +richer American life. Grandma and Grandpa had their accents and old ways +intact, and still mumbled sayings in the language your parents used when +they didn't want you to understand. You could still find Il Progresso, +Freiheit, Norske Tidende, and Polish Daily Zgoda on the newsstands, but the +neighborhoods themselves were no longer alien places. It was the ghetto, +yes, but made benign by assimilation. + It was this world that the first surge tide into the suburbs left +behind. They were people for whom the city had done its work, making +Americans out of families from Dublin to Donetsk. America had given the +urban young educations, and expectations. For many, those expectations had +been nurtured through world war and economic depression. Something better +was needed for the baby boomers. + Today we look back on it all in hurt and wonder. How did this +happen? Where did that good life go? When an accidental detour or a missed +expressway exit brings us into contact with the world we left behind, we +can still place all the blame firmly and squarely elsewhere. The shuttered +factories and collapsing row houses, the vacant storefronts and rutted +streets are regarded with the same awe reserved for the scenes of natural +disasters. We look out on a world that somehow, in the American collective +memory, destroyed itself. + -- Ray Suarez, PBS journalist +~ +The Bible tells us to love our neighbors, and also to love our +enemies; probably because generally they are the same people. + -- G. K. Chesterton +~ +Happiness isn't something you experience; it's something you remember. + -- Oscar Levant +~ +If I had to live my life again, I'd make the same mistakes, only sooner. + -- Tallulah Bankhead +~ +Kids don't watch when they are stimulated and look away when +they are bored. They watch when they understand and look away +when they are confused. + -- Malcolm Gladwell +~ +We especially need imagination in science. It is not all +mathematics, nor all logic, it is somewhat beauty and poetry. + -- Maria Mitchell +~ +The masons have a "temple of living stones". +Well, I have a temple of stoned living. +Except that the temple is my parents' basement. +~ +Pain is just another form of information. + -- Don DeLillo +~ + The traditional image of Asian countries--and the one I held before +we moved there--was of overwhelmingly poor societies: city dwellers +starving in the streets and farmers slaving to raise barely enough rice +to feed a family, while a tiny clique of well-connected magnates lived +behind barbed-wire fences in ornate mansions. Today, those scenes can +still be found in parts of East Asia. But for the most part, the Asian +countries are building a huge middle class, in which most people have +about as much as everybody else. + Japan has been the model; when Japan became a rich country, it did +so in ways that spread the wealth broadly and evenly. In opinion polls +today, more than 90 percent of the Japanese people describe themselves +as 'middle class.' In the other 'high-performing Asian economies' the +economic boom has also been broadly distributed... + You can legitimately question whether equal distribution of a +nation's wealth is a sign of social success. The American dream, in +economic terms, at least, has generally been the dream of enormous +success--not of making as much money as everybody else but rather of +getting really rich. And that dream has been one of the key reasons +for the dynamism and resiliency of the United States over the decades. +On the other hand, the egalitarian distribution of wealth, and the +resulting sense that everybody is getting a relatively fair shake, is +surely one of the reasons that Asian countries have civil and stable +societies. + -- T. R. Reid, "Confucius Lives Next Door: What Living in the East + Teaches Us About Living in the West" +~ +Who will underrate the influence of loose popular +literature in debauching the popular mind? + -- Walt Whitman +~ +There are so many ways of earning a living and most of them are failures. + -- Gertrude Stein +~ +Nothing shocks me. I'm a scientist. + -- Indiana Jones +~ +Lawyers, I suppose, were children once. + -- Charles Lamb +~ +Analyzing humor is like dissecting a frog. +Few people are interested and the frog dies of it. + -- E. B. White +~ + This idea of breaking the world into pieces and then explaining +the pieces in terms of smaller pieces is called reductionism. It +would be perfectly justified to consider Murray Gell-Mann, the father +of the quark, to be the century's arch-reductionist. But very early +on, long before mushy notions of holism became trendy, Gell-Mann +appreciated an important truth: While you can reduce downward, that +doesn't automatically mean you can explain upward. People can be +divided into cells, cells into molecules, molecules into atoms, atoms +into electrons and nuclei, nuclei into subatomic particles, and those +into still tinier things called quarks. But, true as that may be, +there is nothing written in the laws of subatomic physics that can be +used to explain higher-level phenomena like human behavior. There is +no way that one can start with quarks and predict that cellular life +would emerge and evolve over the eons to produce physicists. Reducing +downward is vastly easier than explaining upward--a truth that bears +repeating. + -- George Johnson, "Strange Beauty: Murray Gell-Mann and the + Revolution in Twentieth-Century Physics" +~ +Perhaps no mightier conflict of mind occurs ever again in a lifetime +than that first decision to unseat one's own tooth. + -- Gene Fowler +~ +The difficulty lies, not in the new ideas, but in escaping the +old ones that ramify, for those brought up as most of us have +been, into every corner of our minds. + -- John Maynard Keynes +~ + In the centuries preceding the introduction of the printing press, +those who held power reinforced it by uses of language that mystified the +powerless and kept them subservient. Even more so in today's world, power +is inextricably tied to the use of language, and today's priesthood of +professionals in many fields employs jargon-fueled mystification. That is a +political use of language that deliberately excludes the 'powerless' lay +audience from participation. The doctor whose technical terms confuse, no +less than the politician whose equivocations obfuscate, the lawyer whose +terms intimidate, and the accountant whose explanations obscure, is taking +advantage of audiences through what are called 'gatekeeper' uses of +language. They include euphemisms, jargon and other devices designed to +prevent rather than augment the free flow of knowledge. + Lawyers have been particularly egregious in this practice. +Gatekeeper language also frequently masks what physicians do especially in +circumstances that can be fraught with emotion. A relative of a cancer +patient, seeking straight and hard informational answers to questions -- +how bad is the situation, how much time does she have left, is there any +hope -- was informed by her doctor that his relative's cancer was +'treatable' and her prognosis was 'guarded...' + The philosopher Jrgen Habermas provides an insight into the +political nature of gatekeeper uses of language through his identification +of the 'scientization of the public sphere,' a process now occurring in +many societies. In this trend, elites effectively disqualify members of the +public from being able to participate in policy discussions by insisting +that only specialists can really understand what is going on. When +politicians come to believe that only they can understand what is going on +in the high councils of government, and that their job is to translate it +for us and to protect themselves in the process, the language they aim at +the electorate takes on more and more aspects of purposeful deceit. + -- Tom Shachtman, "The Inarticulate Society" +~ +If you think you are too small to make a difference, +try sleeping in a closed room with a mosquito. + -- African proverb +~ +It does not do to leave a dragon out of your calculations, +if you live near him. + -- J. R. R. Tolkien +~ +Trying to determine what is going on in the world by reading newspapers +is like trying to tell the time by watching the second hand of a clock. + -- Ben Hecht +~ +Friendships, like marriages, are dependent on avoiding the unforgivable. + -- John S. MacDonald +~ +authority does not exist. +we are all equally feeble when compared +with the profundity of the universe. + -- fred t. hamster +~ +Sports do not build character. They reveal it. + -- Heywood Hale Broun +~ +Anybody who has survived his childhood has enough information +about life to last him the rest of his days. + -- Flannery O'Connor +~ +Why does man kill? He kills for food. And not only food: +frequently there must be a beverage. + -- Woody Allen +~ + We live with strangers. Those we love most, with whom we share +a shelter, a table, a bed, remain mysterious. Wherever lives overlap +and flow together, there are depths of unknowing. Parents and children, +partners, siblings, and friends repeatedly surprise us, revealing the +need to learn where we are most at home. We even surprise ourselves +in our own becoming, moving through the cycles of our lives. There is +strangeness hidden in the familiar. + At the same time there is familiarity hidden in the strange. We can +look with curiosity and respect at the faces of men and women we have +never met. Learning to recognize these strangers with whom we share an +increasingly crowded and interdependent world, we can imagine ourselves +joined in a single family, perhaps by a marriage between adventurous +grandchildren. + Strangers marry strangers, whether they have been playmates for +years or never meet before the wedding day. They continue to surprise +each other through the evolutions of love and the growth of affection. +Lovers, gay and straight, begin in strangeness and often, for the zest +of it, find ways to increase their differences. + Children arrive like aliens from outer space, their needs and +feelings inaccessible, sharing no common language, yet for all their +strangeness we greet them with love. Traditionally, the strangeness of +infants has been understood as temporary, the strangeness of incomplete +beings who are expected to become predictable and comprehensible. This +expectation has eased the transition from generation to generation, the +passing on of knowledge and responsibility, on which every human society +depends. Yet the gap between parent and child, like the gap between +partners, is not left behind with the passage of time. Today, in a +world of rapid change, it is increasing, shifting into new rhythms still +to be explored. + -- Mary Catherine Bateson, in "Full Circles, Overlapping Lives: + Culture and Generation in Transition" +~ +Never express yourself more clearly than you are able to think. + -- Niels Bohr +~ +At every crossroads on the path that leads to the future, +tradition has placed 10,000 men to guard the past. + -- Maurice Maeterlink +~ +Before you run in double harness, look well to the other horse. + -- Ovid +~ +There is nothing in the dark that isn't there when the lights are on. + -- Rod Serling +~ +The overly sure belief that one knows the truth, +but that this truth doesn't need to be tested or verified, +is the root of all human errors. + -- fred t. hamster +~ +Any fool can make a rule and every fool will mind it. + -- Henry David Thoreau +~ +How many cares one loses +when one decides +not to be something +but to be someone. + -- Coco Chanel +~ +In other words, can you forgive the people who have pissed +you off in the past? If not, it seems like you're trapped +in their piss. Or yours. Or something. +~ +If someone says 'can't', that shows you what to do. + -- John Cage +~ +Ideologies separate us. Dreams and anguish bring us together. + -- Eugene Ionesco +~ +If a thing can not go on forever, it will come to an end. + -- Herbert Stein +~ +Nothing ever gets anywhere. +The earth keeps turning round and round and gets nowhere. +The moment is the only thing that counts. + -- Jean Cocteau +~ +Don't walk in front of me, I may not follow; +don't walk behind me, I may not lead; +walk beside me, and just be my friend. + -- Albert Camus +~ +Pleasure is very seldom found where it is sought; our brightest +blazes of gladness are commonly kindled by unexpected sparks. + -- Samuel Johnson +~ +The following is a poem made up entirely of quotations from George +W. Bush. The quotes have been arranged, for aesthetic reasons only, +by Washington Post writer Richard Thompson. + + MAKE THE PIE HIGHER + by George W. Bush + + I think we all agree, the past is over. + This is still a dangerous world. + It's a world of madmen and uncertainty + and potential mental losses. + + Rarely is the question asked + Is our children learning? + Will the highways of the internet + become more few? + How many hands have I shaked? + + They misunderestimate me. + I am a pitbull on the pantleg of opportunity. + + I know that the human being + and the fish can coexist. + + Families is where our nation finds hope, + where our wings take dream. + + Put food on your family! + Knock down the tollbooth! + Vulcanize Society! + + Make the pie higher! Make the pie higher! + +~ +Those crazy kids! +~ +When I hear somebody sigh, "Life is hard", I am always +tempted to ask: "Compared to what?" + -- Sydney Harris +~ +What is laid down, ordered, factual is never enough +to embrace the whole truth: life always spills over +the rim of every cup. + -- Boris Pasternak +~ +Hard is the herte that loveth nought in May. + -- Geoffrey Chaucer +~ +Don't ever take a fence down until you know why it was put up. + -- G. K. Chesterton +~ +{{ your mental acuity sharpens, your self image improves, your breath +becomes minty fresh, the problems in your life all seem simpler and you +feel you can deal with them each separately and conquer them one by one, +your hair grows back in all the right places and stops growing in your +ears and other inappropriate places, when i snap my fingers, you will +feel wholly refreshed and renewed... }} + +*SNAP* + + -- fred t. hamster +~ +If you owe the bank $100, that's your problem. +If you owe the bank $100 million, that's the bank's problem. + -- J. Paul Getty +~ + The Hutterites (who came out of the same tradition as the Amish +and the Mennonites) have a strict policy that every time a colony +approaches 150, they split it in two and start a new one. "Keeping +things under 150 just seems to be the best and most efficient way to +manage a group of people," Bill Gross, one of the leaders of a +Hutterite colony outside Spokane told me. "When things get larger +than that, people become strangers to one another." + The Hutterites, obviously, didn't get this idea from contemporary +evolutionary psychology. They've been following the 150 rule for +centuries... At 150, the Hutterites believe, something happens-- +something indefinable but very real--that somehow changes the nature +of community overnight. "In smaller groups people are a lot closer. +They're knit together, which is very important if you want to be +effective and successful at community life," Gross said. "If you +get too large, you don't have enough work in common. You don't have +enough things in common, and then you start to become strangers and +that close-knit fellowship starts to get lost." + Gross spoke from experience. He had been in Hutterite colonies +that had come near to that magic number and seen first-hand how +things had changed. "What happens when you get that big is that the +group starts, just on its own, to form a sort of clan." He made a +gesture with his hands, as if to demonstrate division. "You get two +or three groups within the larger group. That is something you +really try to prevent, and when it happens it is a good time to +branch out." + -- Malcolm Gladwell, "The Tipping Point" +~ +If men could regard the events of their own lives with more open +minds, they would frequently discover that they did not really +desire the things they failed to obtain. + -- Andre Maurois +~ +Middle age is when you're sitting at home on Saturday night +and the telephone rings and you hope it isn't for you. + -- Ogden Nash +~ +The greatest pleasure in life is doing what people say you cannot do. + -- Walter Bagehot +~ + I think a power to do something is of value. Whether the result +is a good thing or a bad thing depends on how it is used, but the +power is a value. Once in Hawaii I was taken to see a Buddhist +temple. In the temple a man said, "I am going to tell you something +that you will never forget." And then he said, "To every man is +given the key to the gates of heaven. The same key opens the gates +of hell." + And so it is with science. In a way it is a key to the gates of +heaven, and the same key opens the gates of hell, and we do not have +any instructions as to which is which gate. Shall we throw away the +key and never have a way to enter the gates of heaven? Or shall we +struggle with the problem of which is the best way to use the key? +That is, of course, a very serious question, but I think that we +cannot deny the value of the key to the gates of heaven. + All the major problems of the relations between society and +science lie in this same area. When the scientist is told that he +must be more responsible for his effects on society, it is the +applications of science that are referred to. If you work to develop +nuclear energy you must realize also that it can be used harmfully. +Therefore, you would expect that, in a discussion of this kind by a +scientist, this would be the most important topic. But I will not +talk about it further. I think that to say these are scientific +problems is an exaggeration. They are far more humanitarian problems. +The fact that how to work the power is clear, but how to control it is +not, is something not so scientific and is not something that the +scientist knows so much about. + -- Richard P. Feynman, "The Meaning of It All: Thoughts of a Citizen + Scientist" +~ +If once a man indulges himself in Murder, very soon he comes to think +little of Robbing, and from Robbing he comes next to Drinking and +Sabbath-breaking, and from that to Incivility and Procrastination. + -- Thomas De Quincey +~ +These are not books, lumps of lifeless paper, +but minds alive on the shelves. + -- Gilbert Highet +~ +Let us not look back in anger, +nor forward in fear, +but around in awareness. + -- James Thurber +~ +Anyone can become angry. That is easy. But to be angry with +the right person, to the right degree, at the right time, for +the right purpose, and in the right way--that is not easy. + -- Aristotle +~ +A nation in which grown men say things like "I am not a happy camper" +at momentous junctures is manifestly not the Shining City on the Hill +that our forefathers dreamed about. + -- Joe Queenan +~ + Among the devices that we use to impose order upon a complicated +(but by no means unstructured) world, classification--or the division +of items into categories based on perceived similarities--must rank +as the most general and most pervasive of all. And no strategy of +classification cuts deeper--while providing such an even balance of +benefits and difficulties--than our propensity for division by two, +or dichotomy. + Some basic attributes of surrounding nature do exist as complem- +entary pairings--two large lights in the sky representing day and +night; two sexes that must couple their opposing parts to produce a +continuity of generations--so we might argue that dichotomization +amounts to little more than good observation of the external world. +But far more often than not, dichotomization leads to misleading or +even dangerous oversimplification. People and beliefs are not either +good or evil (with the second category ripe for burning); and +organisms are not either plant or animal, vertebrate or invertebrate, +human or beast. We seem so driven to division by two, even in +clearly inappropriate circumstances, that I must agree with several +schools of thought (most notably Claude Levi-Strauss and the French +structuralists) in viewing dichotomization more as an inherent +mechanism of the brain's operation than as a valid perception of +external reality. + -- Stephen Jay Gould +~ +Crude classifications and false generalizations +are the curse of the organized life. + -- H. G. Wells +~ +Whosoever shall not fall by the sword or by famine, +shall fall by pestilence, so why bother shaving? + -- Woody Allen +~ +Those who really deserve praise are the people who, while human +enough to enjoy power, nevertheless pay more attention to justice +than they are compelled to do by their situation. + -- Thucydides +~ +It is easier to get forgiveness than permission. + -- Grace Hopper +~ +The only thing necessary for the triumph of evil +is for good men to do nothing. + -- Edmund Burke +~ +If the aborigine drafted an IQ test, all of +Western civilization would presumably flunk it. + -- Stanley Garn +~ +It takes a wise man to discover a wise man. + -- Diogenes Laertes +~ + The human race has reached a critical time in its social evolution +when it has no choice but to make peace with its biological origins and +to learn how to live once again as a member and partner of the natural +community rather than as its dominator and destroyer. In other words, +we must rediscover how to live as our savage ancestors once lived--in +nature, rather apart from it, much less above it. We must, that is, +invent the civilized analogue of the hunter-gatherer way of life, the +only sustainable mode of human existence the planet has ever known. +Suggesting that we live in a much simpler and more natural way does not +imply a return to the Stone Age or anything like it: we have many +possibilities open to us that were not available to our forebears, for +we have been enormously enriched and enlightened by the long experience +of civilization (or at least so one hopes). Nevertheless, how such a +profound transformation of civilization toward a more experienced and +wiser savagery can be achieved is obviously an immensely difficult +question, because it will clearly entail radical changes in every aspect +of our way of life. + Just how radical is suggested by one of the most poignant and +pointed critiques of modern civilization ever uttered. Breaking into +a filmed interview on the destruction of the Amazon rain forest, an +anonymous Kayapo Indian woman shouted, "We don't want your dams. Your +mothers did not hold you enough. You are all orphans." It is perhaps +too simple to say that the good society is one in which your mother-- +and by extension your father, your community, and indeed your entire way +of life--holds you enough, so that you grow up feeling that the world is +a good place and that life is intrinsically satisfying just as it is and +that there is thus no need to make it more satisfying by accumulating +endless wealth and power at others' expense. But this at least points +in the right direction: to become more experienced and wiser savages, to +meet the real political challenge of the twenty-first century, we shall +have to create cultures so rich and nurturing that we would have no need +to pursue happiness; we could simply enjoy it. + -- William Ophuls, in "Requiem for Modern Politics: The Tragedy of + the Enlightenment and the Challenge of the New Millennium." +~ +The reward for work well done is the opportunity to do more. + -- Jonas Salk +~ +In Washington, it's dog eat dog. +In academia, it's exactly the opposite. + -- Robert Reich +~ +I have had many troubles in my life, but the worst of them never came. + -- James A. Garfield +~ +There comes a time when for every addition of knowledge you forget +something that you knew before. It is of the highest importance, +therefore, not to have useless facts elbowing out the useful ones. + -- Arthur Conan Doyle +~ +The wind and the waves are always on the side of the ablest navigators. + -- Edward Gibbon +~ +I write when I'm inspired, and I see to it that I'm inspired +at nine o'clock every morning. + -- Peter de Vries +~ +It is no secret that organized crime in America takes in over forty +billion dollars a year. This is quite a profitable sum, especially +when one considers that the Mafia spends very little for office supplies. + -- Woody Allen +~ +It is not necessary to change. Survival is not mandatory. + -- W. Edwards Deming +~ +A man is called a good fellow for doing things which, if done +by a woman, would land her in a lunatic asylum. + -- H. L. Mencken +~ +Those who desire to give up Freedom +in order to gain Security, will not have, +nor do they deserve, either one. + -- Thomas Jefferson +~ +There's no reason to be the richest man in the cemetery. +You can't do any business from there. + -- Colonel (Harlan) Sanders +~ +To imagine is everything, to know is nothing at all. + -- Anatole France +~ +There is no such thing as conversation. It is an illusion. +There are only intersecting monologues. + -- Rebecca West +~ +You can only be young once. But you can always be immature. + -- Dave Barry +~ + Memory's malfunctions can be divided into seven fundamental transgressions +or 'sins.' Just like the ancient seven deadly sins, the memory sins occur +frequently in everyday life and can have serious consequences for all of us. +Transience, absent-mindedness, and blocking are sins of omission: we fail to +bring to mind a desired fact, event, or idea. Transience refers to a weakening +or loss of memory over time. It's probably not difficult for you to remember +now what you have been doing for the past several hours. But if I ask you +about the same activities six weeks, six months, or six years from now, chances +are you'll remember less and less. + Absent-mindedness involves a breakdown at the interface between attention +and memory. Absent-minded memory errors--misplacing keys or eyeglasses, or +forgetting a lunch appointment--typically occur because we are preoccupied with +distracting issues or concerns, and don't focus attention on what we need to +remember. + The third sin, blocking, entails a thwarted search for information that we +may be desperately trying to retrieve. We've all failed to produce a name to +accompany a familiar face. This frustrating experience happens even though we +are attending carefully to the task at hand, and even though the desired name +has not faded from our minds--as we become acutely aware when we unexpectedly +retrieve the blocked name hours or days later. + In contrast to these three sins of omission, the next four sins of +misattribution, suggestibility, bias, and persistence are all sins of +commission: some form of memory is present, but it is either incorrect or +unwanted. The sin of misattribution involves assigning a memory to the wrong +source: mistaking fantasy for reality, or incorrectly remembering that a friend +told you a bit of trivia that you actually read about in a newspaper. +Misattribution is far more common than most people realize, and has potentially +profound implications in legal settings. The related sin of suggestibility +refers to memories that are implanted as a result of leading questions, +comments, or suggestions when a person is trying to call up a past experience. + The sin of bias reflects the powerful influences of our current knowledge +and beliefs on how we remember our pasts. We often edit or entirely rewrite +our previous experiences--unknowingly and unconsciously--in light of what we +now know or believe. The result can be a skewed rendering of a specific +incident, or even of an extended period in our lives, which says more about how +we feel now than about what happened then. + The seventh sin -- persistence -- entails repeated recall of disturbing +information or events that we would prefer to banish from our minds altogether: +remembering what we cannot forget, even though we wish that we could. Recall +the last time that you suddenly awoke at 3:00 a.m., unable to keep out of your +mind a painful blunder on the job or a disappointing result on an important +exam. In more extreme cases of serious depression or traumatic experience, +persistence can be disabling and even life-threatening. + -- Daniel Schacter, "The Seven Sins of Memory: How the Mind Forgets + and Remembers." +~ +When I was a young man I observed that nine out of ten things I did were +failures. I didn't want to be a failure, so I did ten times more work. + -- George Bernard Shaw +~ +People demand freedom of speech as a compensation +for the freedom of thought which they seldom use. + -- Soeren Kierkegaard +~ +Go confidently in the direction of your dreams. +Live the life you have imagined. + -- Henry David Thoreau +~ +Brave men are all vertebrates; they have their softness +on the surface and their toughness in the middle. + -- G. K. Chesterton +~ +Being willing to accept that the past-- +even the priceless, irreplaceable past-- +will be largely lost is a sign of mental health. + -- Tim Cavanaugh +~ +We are what we repeatedly do. +Excellence, then, is not an act, but a habit. + -- Aristotle +~ +Man is free at the moment he wishes to be. -- Voltaire +~ +It requires wisdom to understand wisdom: +the music is nothing if the audience is deaf. + -- Walter Lippman +~ +What difference does it make how much you have? +What you do not have amounts to much more. + -- Seneca +~ + We now all live in a society marked by increasing, not decreasing, +interconnection and mutual reliance. Each of our lives is affected by +more people than ever before. We generally need the cooperation of +more people than ever to accomplish even those goals we set for +ourselves. This insight needs no elaborate demonstration; one is +reminded of it with every smoggy breath we take, and every time we step +on an airplane and ponder how our life depends on the competent, +attentive behavior of dozens of strangers, from the pilot to the air +traffic controller to the ground mechanic who was supposed to inspect +the extent of the metal fatigue on the wings. Technology increases the +links that tie people together, voluntarily or not, and the complexity +of our economic system and the organizations we work for multiplies +these linkages. + To exercise control over what happens to you as an individual, you +must be involved with others in a process that decides what happens to +you and your fellow citizens collectively. We can no longer separate +the quality of personal life from the quality of social life. To +preserve private space, we must also preserve the commons. + That is one reason why even in a society so seduced by and attached +to autonomy, many people are now getting fed up with what can only be +called acts of vandalism against the public space. Threatening, +disrespectful, wanton, and manifestly selfish acts--from the warfare of +crack dealers and gangs to the shameless greed of the S&L thieves and +the destruction of our very environment by the industrial polluters-- +are poisonous to everyone, not just those directly harmed. Of course, +we pay for such things as taxpayers and as consumers, but that is not +the most important cost. All of us are morally impoverished by these +assaults on the quality and integrity of our common life. + -- Willard Gaylin and Bruce Jennings, "The Perversion of Autonomy: + The Proper Uses of Coercion and Constraints in a Liberal Society" +~ +Imagination grows by exercise, and contrary to common +belief, is more powerful in the mature than in the young. + -- W. Somerset Maugham +~ +Education is what survives when what has been learned has been forgotten. + -- B. F. Skinner +~ +The supreme happiness in life is the conviction that we are loved-- +loved for ourselves, or rather, loved in spite of ourselves. + -- Victor Hugo +~ +If one does not know to which port one is sailing, no wind is favorable. + -- Seneca +~ +Sometimes I worry about being a success in a mediocre world. + -- Lily Tomlin +~ +The wicked leader is one whom the people despise. +The good leader is one whom the people revere. +The great leader is one about whom the people say, +"We did it ourselves." + -- Lao Tzu +~ +Reject your sense of injury and the injury itself disappears. + -- Marcus Aurelius +~ +Ideas pull the trigger, but instinct loads the gun. + -- Don Marquis +~ +No enterprise is more likely to succeed than one +concealed from the enemy until it is ripe for execution. + -- Niccolo Machiavelli +~ +Every exit is an entry somewhere. + -- Tom Stoppard +~ +Spoon feeding in the long run teaches us +nothing but the shape of the spoon. + -- E. M. Forster +~ +We cannot live only for ourselves. A thousand fibers +connect us with our fellow men. + -- Herman Melville +~ +There is much pleasure to be gained from useless knowledge. + -- Bertrand Russell +~ +If this is coffee, please bring me some tea, +but if this is tea, please bring me some coffee. + -- Abraham Lincoln +~ +All men who have turned out worth anything have had +the chief hand in their own education. + -- Sir Walter Scott +~ +Few things can help an individual more +than to place responsibility on him, +and to let him know that you trust him. + -- Booker T. Washington +~ +The last time somebody said, "I find I can write much +better with a word processor," I replied, "They used to +say the same thing about drugs." + -- Roy Blount, Jr. +~ +The world of knowledge takes a crazy turn +When teachers themselves are taught to learn. + -- Bertolt Brecht (1898-1956), German playwright, poet +~ +Power corrupts. Absolute power is kind of neat. + -- John Lehman +~ +Art produces ugly things which frequently become more beautiful with time. +Fashion, on the other hand, produces beautiful things which always become +ugly with time. + -- Jean Cocteau +~ +To become vegetarian is to step into the stream which leads to nirvana. + -- Shakyamuni Buddha +~ +the best part about killing software bugs is that +there's no goo nor exoskeleton fragments left behind. +~ +Any activity becomes creative when the doer cares +about doing it right, or doing it better. + -- John Updike +~ +Great spirits have always encountered +violent opposition from mediocre minds. + -- Albert Einstein +~ +There's a fine line between genius and +insanity. I have erased this line. + -- Oscar Levant +~ +Don't worry about people stealing an idea. If it's +original, you will have to ram it down their throats. + -- Howard Aiken +~ +Knowledge is of two kinds. We know a subject ourselves, +or we know where we can find information upon it. + -- Samuel Johnson +~ + Historically, consumer acceptance of new technologies has been +slow and cumbersome, delaying anticipated profits by decades. +Indeed, consumer acceptance of any innovation is typically slow, +despite extraordinary benefits and convenience. + When cake mixes were first created, they required consumers to +only add water--a major behavioral shift. Consumers felt a cake +made with such a mix could not possibly be as good as a homemade +cake. So cake mix formulas were revised to require the addition of +an egg and milk. The new mixes met with great success, because the +behavior shift required of consumers was minor. Eventually some +consumers became comfortable adding only water (some never will). + -- Kathy Biro +~ +An education isn't how much you have committed to memory, or even +how much you know. It's being able to differentiate between what +you know and what you don't. + -- Anatole France +~ +A man is a worker. If he is not that he is nothing. + -- Joseph Conrad +~ +All the world's a stage, and all the men and women merely +players. They have their exits and their entrances, and one +man in his time plays many parts, his acts being seven ages. + -- Shakespeare, "As You Like It" +~ +Behold the turtle. He makes progress only when he sticks his neck out. + -- James Bryant Conant +~ + I say to the House as I said to ministers who have joined this +government, I have nothing to offer but blood, toil, tears, and sweat. +We have before us an ordeal of the most grievous kind. We have before +us many, many months of struggle and suffering. + You ask, what is our policy? I say it is to wage war by land, sea, +and air. War with all our might and with all the strength God has given +us, and to wage war against a monstrous tyranny never surpassed in the +dark and lamentable catalogue of human crime. That is our policy. + You ask, what is our aim? I can answer in one word. It is victory. +Victory at all costs--Victory in spite of all terrors--Victory, however +long and hard the road may be, for without victory there is no survival. + Let that be realized. No survival for the British Empire, no +survival for all that the British Empire has stood for, no survival for +the urge, the impulse of the ages, that mankind shall move forward +toward his goal. + I take up my task in buoyancy and hope. I feel sure that our cause +will not be suffered to fail among men. I feel entitled at this +juncture, at this time, to claim the aid of all and to say, "Come then, +let us go forward together with our united strength." + -- Winston Churchill, first speech to Parliament, May 13, 1940 +~ +Be it religion, love under all its forms, literature, or art, +there is not a single spiritual force that does not become an +object of commercial exploitation. + -- Etienne Gilson +~ +History is a nightmare from which I am trying to awake. + -- James Joyce from "A Portrait of the Artist as a Young Man" +~ +Stand your ground. Don't fire unless fired upon, +but if they mean to have a war, let it begin here! + -- John Parker, leader of the "Minutemen", April 19, 1775 +~ +These are the times that try men's souls. + -- Tom Paine +~ + Yesterday, December 7, 1941--a day which will live in infamy--the +United states of America was suddenly and deliberately attacked by +naval and air forces of the empire of Japan... The attack yesterday +on the Hawaiian Islands has caused severe damage to American naval and +military forces. I regret to tell you that very many American lives +have been lost. In addition, American ships have been reported +torpedoed on the high seas between San Francisco and Honolulu... Japan +has therefore undertaken a surprise offensive extending throughout the +Pacific area. The facts of yesterday and today speak for themselves. +The people of the United States have already formed their opinions and +well understand the implications to the very life and safety of our +nation. + As Commander in Chief of the Army and Navy I have directed that all +measures be taken for our defense, that always will our whole nation +remember the character of the onslaught against us. No matter how long +it may take us to overcome this premeditated invasion, the American +people, in their righteous might, will win through to absolute victory. + I believe that I interpret the will of the Congress and of the +people when I assert that we will not only defend ourselves to the +uttermost but will make it very certain that this form of treachery +shall never again endanger us. Hostilities exist. There is no +blinking at the fact that our people, our territory and our interests +are in grave danger. With confidence in our armed forces, with the +unbounding determination of our people, we will gain the inevitable +triumph. So help us God. + -- Franklin Delano Roosevelt +~ +Our strength grows out of our weakness. + -- Ralph Waldo Emerson +~ +The proper function of man is to live, not to exist. +I shall not waste my days in trying to prolong them. +I shall use my time. + -- Jack London +~ +What we obtain too cheap, we esteem too lightly; +it is dearness only that gives everything its value. + -- Tom Paine +~ +it's hard not to blame microsoft for the new worm. they point at +their feeble little patch (which actually doesn't stop all the +types of attack nimda uses), but why aren't their operating systems +automatically getting the patch if it's critical? also, why are +there so many huge gaping holes in the first place? they'll never +stop plugging them... it's like a dyke made out of sponges. + -- fred t. hamster +~ +after i got my millionth annoying spam today, i decided that there must +be something to it after all. perhaps i can write spam all day while +wearing only underpants and make money by magic too. clearly no one has +shut off the spammers' internet connections yet, so they survive somehow. +subsistence-level living based on a life of evil, here i come! +i like to call this: +"my frist spam" + ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + +Your nuglids are perspiratory max! +Investment opportunity from gravy! +Get in on the ground step business man where revenues flow like wines. +Please to respond, urgent information overload impendimint. +20,000 other fine Americans have found this to be lifeblood for retirement +and frolic away from troubles of money. + ++++ SouLing's Institute of Higher Profits +++ + +We instruct nubile businesspeoples with wisdom and our cadets conquer +all marches of finance. +Don't miss out! You can be next! +Join our team and we hike the goalposts of life! + +http://somethinglikeyouveneverseenbeforewowcool-dumbyankeesendmecashnow.com + +SouLing Institute +Chauncy, Nebraska 55512-1212 + ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- +~ +Courage is grace under pressure. -- Ernest Hemingway +~ +To build may have to be the slow and laborious task of years. +To destroy can be the thoughtless act of a single day. + -- Winston Churchill +~ +I learned that it is the weak who are cruel, and that +gentleness is to be expected only from the strong. + -- Leo Rosten +~ + The liberties of our country, the freedom of our civil constitution, are +worth defending at all hazards; and it is our duty to defend them against all +attacks. We have received them as a fair inheritance from our worthy +ancestors: they purchased them for us with toil and danger and expense of +treasure and blood, and transmitted them to us with care and diligence. + It will bring an everlasting mark of infamy on the present generation, +enlightened as it is, if we should suffer them to be wrested from us by +violence without a struggle, or be cheated out of them by the artifices of +false and designing men. + Of the latter we are in most danger at present; let us therefore be aware +of it. Let us contemplate our forefathers and posterity; and resolve to +maintain the rights bequeathed to us from the former, for the sake of the +latter. + Instead of sitting down satisfied with the efforts we have already made, +which is the wish of our enemies, the necessity of the times, more than ever, +calls for our utmost circumspection, deliberation, fortitude, and perseverance. +Let us remember that 'if we suffer tamely a lawless attack upon our liberty, +we encourage it, and involve others in our doom.' It is a very serious +consideration, which should deeply impress our minds, that millions yet unborn +may be the miserable sharers in the event. + -- Samuel Adams, tasty American Patriot +~ +The bravest are surely those who have the clearest vision of what is +before them, glory and danger alike, and yet notwithstanding go out +to meet it. + -- Thucydides +~ +I do not know what I may appear to the world; but to myself +I seem to have been only like a boy playing on the seashore, +and diverting myself now and then finding a smoother pebble +or a prettier shell than ordinary, whilst the great ocean of +truth lay all undiscovered before me. + -- Isaac Newton +~ +As a rule, software systems do not work well until they have been +used, and have failed repeatedly, in real applications. + -- Dave Parnas +~ +Everything happens to everybody sooner or later if there is time enough. + -- George Bernard Shaw +~ +When fanatics are on top, there is no limit to oppression. + -- H. L. Mencken +~ +Knowing is not enough; we must apply. +Willing is not enough; we must do. + -- Johann Wolfgang von Goethe +~ +tkelele ekki eekkle cthulhu hurf hurf hurf *plat* + -- H. P. Lovecraft's "cat" ejecting a hairball +~ +If we survive danger it steels our courage more than anything else. + -- Reinhold Niebuhr +~ +Computer Science is no more about computers +than astronomy is about telescopes. + -- Edsger W. Dijkstra +~ +What we become depends on what we read after all of the +professors have finished with us. The greatest university +of all is a collection of books. + -- Thomas Carlyle +~ +The surest way to corrupt a youth is to instruct him to hold in +higher esteem those that think alike than those who think differently. + -- Friedrich Nietzsche +~ +The cause of America is in a great measure the cause of all mankind. + -- Tom Paine +~ +clean underwear is +crucial in these turbulent +final days of earth... + +president shrub does +wonders for sagging garment +industry's profits. + -- fred t. hamster +~ +will afghanis have +christian values? hate the bombs, +but love the bombers? +~ +Fortitude is the guard and support of the other virtues. + -- John Locke +~ +Whenever you are asked if you can do a job, tell 'em, "Certainly, +I can!" Then get busy and find out how to do it. + -- Theodore Roosevelt +~ +Be not afraid of greatness: some are born great, some achieve +greatness, and some have greatness thrust upon them. + -- William Shakespeare +~ +fred had a job but +then he frittered it away +waxing poetic. + -- fred t. hamster +~ +Half this game is ninety percent mental. + -- Yogi Berra +~ +Action may not always bring happiness; +but there is no happiness without action. + -- Benjamin Disraeli +~ +The world is faced with a transcendent conflict between those who love life +and those who love death both for themselves and their enemies. + -- Charles Krauthammer +~ +doomed bovines eat sand +scrape on cactus in AZ +then chowed on and gone + -- fred t. hamster +~ +To find out what one is fitted to do, +and to secure an opportunity to do it, +is the key to happiness. + -- John Dewey +~ +I have never understood Brahms. I believe he was +burning the midnight oil trying to be complicated. + -- Albert Einstein, in a conversation with Peter G. Neumann +~ +Creative minds have always been known to survive any kind of bad training. + -- Anna Freud +~ +It is the mark of an educated mind to be able to +entertain a thought without accepting it. + -- Aristotle +~ +The fastest way to succeed is to look as if you're playing +by somebody else's rules, while quietly playing by your own. + -- Michael Korda +~ +tie your future to +a brick; microsoft sinking +fast as cement shoes +~ +Journalism is popular, but it is popular mainly as fiction. +Life is one world, and life seen in the newspapers is another. + -- G. K. Chesterton +~ +We don't know a millionth of one percent about anything. + -- Thomas Alva Edison +~ +Writing is easy. All you do is stare at a blank sheet +of paper until drops of blood form on your forehead. + -- Gene Fowler +~ +Any activity becomes creative when the doer +cares about doing it right, or doing it better. + -- John Updike +~ +Experience with technology teaches us that once a technology makes +something possible, it gets applied, whether for good or bad. + -- Donald A. Norman +~ +The final test of a leader is that he leaves behind him in +other men the conviction and the will to carry on. + -- Walter Lippman +~ +Without education we are in a horrible and deadly +danger of taking educated people seriously. + -- G. K. Chesterton +~ +When your work speaks for itself, don't interrupt. + -- Henry J. Kaiser +~ +Insane people are always sure that they are fine. It is only +the sane people who are willing to admit that they are crazy. + -- Nora Ephron +~ +Humor is perhaps a sense of intellectual perspective: an awareness +that some things are really important, others not; and that the two +kinds are most oddly jumbled in everyday affairs. + -- Christopher Morley +~ +Nobody ever died of laughter. + -- Max Beerbohm +~ + Mein Herr looked so thoroughly bewildered that I thought it best +to change the subject. "What a useful thing a pocket-map is!" I +remarked. + "That's another thing we've learned from your Nation," said Mein +Herr, "map-making. But we've carried it much further than you. What +do you consider the largest map that would be really useful?" + "About six inches to the mile." + "Only six inches!" exclaimed Mein Herr. "We very soon got to six +yards to the mile. Then we tried a hundred yards to the mile. And +then came the grandest idea of all! We actually made a map of the +country, on the scale of a mile to the mile!" + "Have you used it much?" I enquired. + "It has never been spread out, yet," said Mein Herr: "the farmers +objected; they said it would cover the whole country, and shut out +the sunlight! So we now use the country itself, as its own map, and +I assure you it does nearly as well." + -- Lewis Carroll, "Sylvie and Bruno Concluded" +~ +The fly has an autonomous system that avoids being swatted. It has +the ability to see and navigate and make decisions on millisecond time +scales. We've never been able to make artificial vision systems that +come within orders of magnitude of that, with all the computation we +can throw at them. + -- Carver Mead +~ +The world is too much with us; late and soon, Getting and spending, +we lay waste our powers: Little we see in Nature that is ours; +We have given our hearts away, a sordid boon! + -- William Wordsworth +~ +You return and again take the proper course, guided by what? +By the picture in mind of the place you are headed for. + -- John McDonald +~ +The world is a dangerous place to live, not because of the people who +are evil, but because of the people who don't do anything about it. + -- Albert Einstein +~ +Xtianity the Easy Way +--------------------- + +jesus heal my head, +so i won't drop dead; +god better get me real happy, +'cause he's my celestial pappy. + +yo god, i'll just lie around here at home, +you fill my pockets with money while i moan. +it'll make me real religious, +if you ain't fictitious... + +being a zealot is a lot of hassle, +and with my faith i must wrassle. +'til jesus gets my brain repaired, +to humans my butt remains bared. + +surmounting one's lot is for others, +who have energy unsmothered, +by wacky notions of invisible spirits, +that reward and punish holy twits. + +heaven better live up to my imagination, +and to its oft repeated reputation; +the next life better not suck, holy dad, +'cause this one on earth you f*cked pretty bad. + -- fred t. hamster +~ +When one door closes another door opens; but we so often look so +long and so regretfully upon the closed door, that we do not see +the ones which open for us. + -- Alexander Graham Bell +~ +The mind is its own place and +in itself can make a heaven +of hell, a hell of heaven. + -- John Milton +~ +Knowing is not enough; we must apply. +Willing is not enough; we must do. + -- Johann von Goethe +~ +I've developed a new philosophy... I only dread one day at a time. + -- Charlie Brown, in Charles Schultz's cartoon "Peanuts" +~ +It is hard to say exactly when the monumentalization of the trivial became +a way of life in America. It may have been when the National Football League +started according contests between large men in skintight pants the sort of +solemn designations formerly reserved for armed global conflicts. + -- Michael Kelly, in "The Atlantic Monthly" +~ +When you are courting a nice girl an hour seems like a second. +When you sit on a red-hot cinder a second seems like an hour. +That's relativity. + -- Albert Einstein +~ +We must be willing to let go of the life we have planned, +so as to have the life that is waiting for us. + -- E. M. Forster +~ +The best way to predict the future is to invent it. + -- Alan Kay +~ +Unless one's predictions are confirmed more often than a random +guesser's, we should be suspicious of their quality, however +cogent they may have seemed when made. + -- Richard A. Posner +~ +It is said an eastern monarch once charged his wise men to invent a +sentence, to be ever in view, and which should be true and appropriate +in all times and situations. They presented him with the words, +"And this, too, shall pass away." How much it expresses! How chastening +in the hour of pride! How consoling in the depths of affliction! + -- Abraham Lincoln +~ +Keep away from people who try to belittle your ambitions. +Small people always do that, but the really great make you +feel that you, too, can become great. + -- Mark Twain +~ +The belief that microsoft has done it all right this time with Windows-XP +is just like... the suspension of disbelief that allows people to think +it's perfectly normal to see kids flying around on brooms in the Harry +Potter movie while they're in the darkened movie theater. It's time to +turn on the lights in your mental theater... + -- fred t. hamster +~ +We always deceive ourselves twice about the people we love-- +first to their advantage, then to their disadvantage. + -- Albert Camus +~ + The majority of important television commercials take the form of +religious parables organized around a coherent theology. Like all religious +parables, they put forward a concept of sin, intimations of the way to +redemption, and a vision of Heaven. They also suggest what are the roots of +evil and what are the obligations of the holy. + Consider, for example, the Parable of the Ring Around the Collar. This +is to television scripture what the Parable of the Prodigal Son is to the +Bible, which is to say it is an archetype containing most of the elements of +form and content that recur in the genre. + The narrative structure of the Parable of the Ring Around the Collar is, +indeed, comfortably traditional. The story has a beginning, a middle, and an +end. A married couple is depicted in some relaxed setting--a restaurant, say-- +in which they are enjoying each other's company and generally having a +wonderful time. But then a waitress approaches their table, notices that the +man has a dirty collar, stares at it boldly, sneers with cold contempt, and +announces to all within hearing the nature of his transgression. The man is +humiliated and glares at his wife with scorn, for she is the source of his +shame. She, in turn, assumes an expression of self-loathing mixed with a +touch of self-pity... + The parable continues by showing the wife at home using a detergent +that never fails to eliminate dirt around the collars of men's shirts... + In television-commercial parables, the root cause of evil is +"Technological Innocence", a failure to know the particulars of the +beneficent accomplishments of industrial progress... + Technological innocence refers not only to ignorance of detergents, +drugs, sanitary napkins, cars, salves, and foodstuffs, but also to +ignorance of technical machinery such as savings banks and transportation +systems. + -- Neil Postman, "Amusing Ourselves to Death: Public Discourse in the Age + of Show Business" +~ +Finish each day and be done with it. You have done what you could; some +blunders and absurdities have crept in; forget them as soon as you can. +Tomorrow is a new day; you shall begin it serenely and with too high a +spirit to be encumbered with your old nonsense. + -- Ralph Waldo Emerson +~ +I don't know much about being a millionaire, +but I'll bet I'd be darling at it. + -- Dorothy Parker +~ +term: Microflaccidity +definition: An addiction to Microsoft products accompanied by a decrease in IQ. +~ +Poetry is what gets lost in translation. + -- Robert Frost +~ +Telling us to obey instinct is like telling us to obey "people". People +say different things: so do instincts. Our instincts are at war.... Each +instinct, if you listen to it, will claim to be gratified at the expense +of the rest.... + -- C. S. Lewis +~ +[Hollywood is] a place where they shoot too many films and not enough actors. + -- Walter Winchell +~ +Money sometimes makes fools of important persons, +but also makes important persons of fools. + -- Walter Winchell +~ +I made my money by selling too soon. + -- Bernard Baruch +~ +Tell me who admires you and loves you, and I will tell you who you are. + -- Charles Augustin Sainte-Beauve +~ +False facts are highly injurious to the progress of science, for they often +endure long; but false views, if supported by some evidence, do little harm, +for everyone takes a salutory pleasure in proving their falseness. + -- Charles Darwin +~ +I never hated a man enough to give him his diamonds back. + -- Zsa-Zsa Gabor +~ +I like to listen. I have learned a great deal from +listening carefully. Most people never listen. + -- Ernest Hemingway +~ +Ten point list of lessons learned +from the high-tech industry failures during 2001: + + 1) Prediction tools must improve. + 2) It's hugely difficult to build chicken and egg simultaneously. + 3) Venture capital firms' demands that startups generate $50 million + in revenue within three years were unrealistic. + 4) Companies used narrowcast to broadcast. + 5) Free is folly. + 6) We all, like sheep, will go astray (with enough pressure). + 7) Many startups were fundamentally uncreative and "un-Internet." + 8) Too early to market? Too bad. + 9) New stuff doesn't replace old. + 10) Nothing changes overnight. + + -- Webmergers.com +~ +The beginnings and endings of all human undertakings are untidy. + -- John Galsworthy +~ +The ultimate result of shielding men from the +effects of folly is to fill the world with fools. + -- Herbert Spencer +~ +Sports is the toy department of human life. -- Howard Cosell +~ +First keep the peace within yourself, +then you can also bring peace to others. + -- Thomas Kempis +~ +What one has not experienced, one will never understand in print. + -- Isadora Duncan +~ +Newspapermen learn to call a murderer "an alleged murderer" and the +King of England "the alleged King of England" to avoid libel suits. + -- Stephen Leacock +~ +I told the doctor I broke my leg in two places. +He told me to quit going to those places. + -- Henny Youngman +~ +Never mistake motion for action. -- Ernest Hemingway +~ +The first duty of a leader is to make himself +be loved without courting love. To be loved +without "playing up" to anyone--even to himself. + -- Andre Malraux +~ +All you need in this life is ignorance and +confidence, and then success is sure. + -- Mark Twain +~ +Creativity represents a miraculous coming together of the uninhibited +energy of the child with its apparent opposite and enemy, the sense of +order imposed on the disciplined adult intelligence. + -- Norman Podhoretz +~ +If only I could master the skill of telefecalkinesis, the act of +sh*tting on someone from any distance. Another useful skill is +evilknievelportation, the ability to jump out of the way right +before some horrible accident occurs. This is useful to avoid +the telefecalkinetics. + -- fred t. hamster +~ +Annual income twenty pounds, annual expenditure nineteen six, +result happiness. Annual income twenty pounds, annual +expenditure twenty pound ought and six, result misery. + -- Charles Dickens, in "David Copperfield" +~ +Advertising is a valuable economic factor because it is the cheapest +way of selling goods, particularly if the goods are worthless. + -- Sinclair Lewis +~ +It's the wonder of the world, it's a rocket to the moon, it gets you high, +it gets you low, but once you get that glow... Love, love, hooray for love, +who was ever too blase for love? Make this the night for love. If we have +to fight, let's fight for love. Some sigh and cry for love--Ah, but in +Pa-ree they die for love. Some waste away for love. Just the same--hooray +for love! + -- Harold Arlen +~ +Reality is that which, when you stop believing in it, doesn't go away. + -- Philip K. Dick +~ +Progress might have been all right once, but it has gone on too long. + -- Ogden Nash +~ +Rarely do great beauty and great virtue dwell together. + -- Petrarch +~ +The young have aspirations that never come to pass, +the old have reminiscences of what never happened. + -- H.H. Monroe (Saki) +~ +An inventor is a person who makes an ingenious arrangement +of wheels, levers and springs, and believes it civilization. + -- Ambrose Bierce +~ +There's nothing quite like doing nothing. + -- John Ong +~ +For a successful technology, reality must take precedence +over public relations, for Nature cannot be fooled. + -- Richard Feynmann +~ +The mind commands the body and the body obeys. +The mind commands itself and finds resistance. + -- St. Augustine +~ + The English word "way" is perhaps the nearest translation that we can +make to the Chinese word tao. It is usually pronounced "dow." The Tao +means many things. Primarily, it means the way of nature, the process of +the universe. But it also means a way of life, a way of living in +accordance with that process. We have lost the idea that our occupations +are vocations. Our idea of an occupation is that it is a way of making +money. We make a very, very destructive division between work and play. +We spend eight hours, or whatever it may be, at work in order to earn the +money to enjoy ourselves in the other eight hours. And that is a perfectly +ridiculous way of living. It is much better to be very poor indeed than to +do something so stupid as boring ourselves and wasting ourselves for eight +hours in order to be able to enjoy ourselves the other eight hours. The +result of this fantastic division between work and play is that work +becomes drudgery, and play becomes empty. When we say that our occupation +should also be our vocation, we are speaking of a conception of life within +which work and play should be identical. + It is interesting that Hindus, when they speak of the creation of the +universe, do not call it the work of God, they call it the play of God, +the Vishnu-lila, lila meaning "play." And they look upon the whole +manifestation of all the universes as a play, as a sport, as a kind of +dance--lila perhaps being somewhat related to our word lilt. We in the +West have tended to lose the idea of our work, our profession, as being a +way, a tao. + Now, mind you, these ways I am talking about in Asia are not followed +by an enormous number of people, except in a kind of nominal, superficial +way. And I am not trying to make any vast comparisons between Asian society +and Western society or to say that the total Asian way of life is superior +to ours. I do not think it is, but I do not think it is necessarily +inferior, either; it is just different. But the fact remains that there is +an aspect of Asian religion and philosophy that is very subdued in Western +religion and philosophy, so that you might say that the Way, in the sense of +the Chinese Tao, does not quite exist in the West, in any recognizable form. +It does exist, yes. It exists unofficially, it exists occasionally, but it +is never clearly recognized. + -- Alan Watts, "The Way of Zen" +~ +It is not certain that everything is uncertain. + -- Blaise Pascal +~ +We used to think that +if we knew one, we knew two, +because one and one are two. +We are finding that we must +learn a great deal more about "and". + -- Sir Arthur Eddington +~ +The so-called Pythagoreans, who were the first to take up mathematics, +not only advanced this subject, but, saturated with it, they fancied +that the principles of mathematics were the principles of all things. + -- Aristotle +~ +Civilization advances by extending the number of important +operations which we can perform without thinking of them. + -- Alfred North Whitehead +~ +Any solution to a problem changes the problem. + -- R. W. Johnson +~ +I believe cats to be spirits come to earth. A cat, I am sure, +could walk on a cloud without coming through. + -- Jules Verne +~ +Pay no attention to what the critics say; there has +never been set up a statue in honor of a critic. + -- Jean Sibelius +~ +Do not ask things to happen as you wish, but wish them to +happen as they do happen, and your life will go smoothly. + -- Epictetus +~ +One of the things most beguiling to cat lovers is the +intractability of a cat... its refusal to surrender +the least part of its spiritual independence. + -- Marguerite Steen +~ +Sometimes when I reflect back on all the beer I drink I feel ashamed. +Then I look into the glass and think about the workers in the brewery +and all of their hopes and dreams. If I didn't drink this beer, they +might be out of work and their dreams would be shattered. Then I say +to myself, "It is better that I drink this beer and let their dreams +come true than be selfish and worry about my liver." + -- Deep Thoughts by Jack Handey +~ +I feel sorry for people who don't drink. When they wake up in the +morning, that's as good as they're going to feel all day. + -- Frank Sinatra +~ +The problem with some people is that when they aren't drunk, they're sober. + -- William Butler Yeats +~ +Drinking provides a beautiful excuse to pursue the one activity that +truly gives me pleasure, hooking up with fat, hairy girls. + -- Ross Levy +~ +What contemptible scoundrel has stolen the cork to my lunch? + -- Tee Mans +~ +Life is a waste of time, time is a waste of life, so get +wasted all of the time and have the time of your life. + -- Michelle Mastrolacasa +~ +I'd rather have a bottle in front of me, than a frontal lobotomy. + -- Tom Waits +~ +Newscasters have gotten so repellant, +talk shows so superficial, +sitcoms so unfunny, +dramas so mundane, +movies so predictable, +that the Food Network offers the best +fare on TV in every sense of the word. + -- Florence King +~ +My doctor told me to stop having intimate dinners for four. +Unless there are three other people. + -- Orson Welles +~ +The people's good is the highest law. -- Cicero +~ +In all recorded history there has not been one economist who +has had to worry about where the next meal would come from. + -- Peter Drucker +~ +The great thing in this world +is not so much where we stand, +as in what direction we are moving. + -- Oliver Wendell Holmes +~ +Reality is the leading cause of stress +amongst those in touch with it. + -- Jane Wagner +~ +If at first you don't succeed, find out if the loser gets anything. + -- Bill Lyon +~ +The nice thing about egotists is that +they don't talk about other people. + -- Lucille S. Harper +~ +Wisdom outweighs any wealth. -- Sophocles +~ +A problem is a chance for you to do your best. -- Duke Ellington +~ +It is not how much we do, but how much love we put in the doing. +It is not how much we give, but how much love we put in the giving. + -- Mother Teresa +~ +The human mind is like an umbrella--it functions best when open. + -- Walter Gropius +~ +They always say that time changes things, but +you actually have to change them yourself. + -- Andy Warhol +~ +If the automobile had followed the same development cycle as the +computer, a Rolls-Royce would today cost $100, get a million miles +per gallon, and explode once a year, killing everyone inside. + -- Robert X. Cringely +~ +The man who says he is willing to meet you +halfway usually is a poor judge of distance. + -- Laurence J. Peter +~ +To do two things at once is to do neither. + -- Publilius Syrus +~ +We live in an age when pizza gets to your home before the police. + -- Jeff Marder +~ +Never keep up with the Joneses. Drag them down to your level. + -- Quentin Crisp +~ +Hell, there are no rules here, we're trying to accomplish something. + -- Thomas Edison +~ +The days of the digital watch are numbered. -- Tom Stoppard +~ +A little drowsing cat is an image of perfect beatitude. + -- Champfleury +~ +World peace must develop from inner peace. +Peace is not just mere absence of violence. +Peace is, I think, the manifestation of human compassion. + -- His Holiness the 14th Dalai Lama +~ +The human mind treats a new idea the same way +the body treats a strange protein; it rejects it. + -- P. B. Medawar +~ +A hat should be taken off when you greet a +lady and left off for the rest of your life. +Nothing looks more stupid than a hat. + -- P. J. O'Rourke +~ +Not only is there no God, but try getting a plumber on weekends. + -- Woody Allen +~ +It is a very sad thing that nowadays +there is so little useless information. + -- Oscar Wilde +~ +I was born not knowing and have had only a +little time to change that here and there. + -- Richard Feynman +~ +I hate mankind, for I think myself one of the +best of them, and I know how bad I am. + -- Joseph Baretti +~ +Delusions of grandeur make me feel a lot better about myself. + -- Jane Wagner +~ +It's a rare person who wants to hear what he doesn't want to hear. + -- Dick Cavett +~ +Just because your voice reaches halfway around the world doesn't +mean you are wiser than when it reached only to the end of the bar. + -- Edward R. Murrow +~ +What can you say about a society that says that +God is dead and Elvis is alive? + -- Irv Kupcinet +~ +Poets have been mysteriously silent on the subject of cheese. + -- G. K. Chesterton +~ +Hard work never killed anybody, but why take a chance? + -- Edgar Bergen, as "Charlie McCarthy" +~ +A conference is a gathering of important people who singly can +do nothing, but together can decide that nothing can be done. + -- Fred Allen +~ +Whatever you do will be insignificant, +but it is very important that you do it. + -- Mahatma Gandhi +~ +Cats know how to obtain food without labor, +shelter without confinement, +and love without penalties. + -- W. I. George +~ +Most men are within a finger's breadth of being mad. + -- Diogenes the Cynic +~ +When it is not necessary to make a decision, +it is necessary not to make a decision. + -- Lord Falkland +~ +Reminds me of my safari in Africa. Somebody forgot the corkscrew +and for several days we had nothing to live on but food and water. + -- W. C. Fields +~ +Why isn't there a special name for the tops of your feet? -- Lily Tomlin +~ +I took a speed reading course and read "War and +Peace" in twenty minutes. It involves Russia. + -- Woody Allen +~ +Nothing fixes a thing so intensely in the memory as the wish to forget it. + -- Michel de Montaigne +~ +I've had a perfectly wonderful evening. But this wasn't it. + -- Groucho Marx +~ +In the future everyone will be famous for 15 minutes. -- Andy Warhol +~ +Sometimes I've believed as many as six impossible things before breakfast. + -- Lewis Carroll +~ +The reason grandparents and grandchildren get +along so well is that they have a common enemy. + -- Sam Levenson +~ +A man thinks that by mouthing hard words he understands hard things. + -- Herman Melville +~ +The prayer of the scientist if he prayed, which is not likely: +Lord, grant that my discovery may increase knowledge and help other men. +Failing that, Lord, grant that it will not lead to man's destruction. +Failing that, Lord, grant that my article in "Brain" be published before +the destruction takes place. + -- Walker Percy, in "Love in the Ruins" +~ +Psychiatry enables us to correct our faults by +confessing our parents' shortcomings. + -- Laurence J. Peter +~ +Cooperate on Standards, Compete on Implementation + -- Sun Corporations's Founding Principle +~ +If I could drop dead right now, I'd be the happiest man alive. + -- Sam Goldwyn +~ +Someday I want to be rich. Some people get so rich they lose +all respect for humanity. That's how rich I want to be. + -- Rita Rudner +~ +Kids who have yet to master spelling or basic math are in no position to +dogmatize about scientific questions like global warming or nuclear power. + -- Thomas Sowell +~ +If you can't ride two horses at the same time you shouldn't be in the circus. + -- Dennis Healey +~ + Whenever a person walks in on the middle of a film or a conversation, or +starts a new friendship, opens a book, takes a new job, or moves to a new +city, his first need is to orient himself. We all must know, in a general +way, what to expect so that we can plan and respond intelligently and feel +comfortable. And although all animals work with their senses and brains to +orient themselves, human being do something unique. We live less simply and +directly in the world than do other animals. We make a version of a world, +an interpretation of it, and then we live in that. The degree of comfort +and success that we achieve in our lives depends on how well that +interpretation suits our circumstances. + Another way to state this idea is that genetically built into people is +a special organizing mode of perception. The philosopher Susanne Langer +calls this mode transformational: we are co-creators of our own perceptions. +In the very act of physically perceiving, we interpret; we transform the raw +data gathered by our senses into complex symbolic meanings. We literally +cannot function and survive without seeing in our world evidence of order +and purpose. We take nothing at face value; we systematize, explain, weave +a large network of connected meanings. + While nonhuman animals toil for their lives, play, or lie in the sun--do +whatever is suitable for the moment--only people fret and practice and +struggle to achieve distant or abstract goals. We are the only animals who +live partly removed from our immediate physical circumstances. This +extravagance is the source of our language, art, science, music, religions, +philosophies: those things we value most. Aside from such direct physical +causes of death as hunger, exposure, old age, or disease, the one +circumstance we truly cannot survive is living in a raw, uninterpreted +place--in chaos. Each of us either finds a meaning in some traditional +religion or philosophy or patches together one of his own, or else he +panics, loses the will to live, and, in one way or another perishes. + -- Shirley Park Lowery, "Familiar Mysteries: The Truth in Myth" +~ +I believe that a scientist looking at nonscientific +problems is just as dumb as the next guy. + -- Richard Feynman +~ +The operating system for the *world* is too important a resource to be +entrusted to just one proprietary company. No one company can handle +the responsibility, nor can any one company be trusted to stay honest +and fair when wielding such awesome power. + -- fred t. hamster +~ +Middle age is when you've met so many people that every +new person you meet reminds you of someone else. + -- Ogden Nash +~ +Nothing is impossible for the man who doesn't have to do it himself. + -- A. H. Weiler +~ +I was walking down the street wearing glasses when the prescription ran out. + -- Steven Wright +~ +Sometimes I think we're alone. Sometimes I think we're not. +In either case, the thought is staggering. + -- R. Buckminster Fuller +~ +Birds fly over the rainbow, why then oh why can't I? + -- E. Y. Harburg +~ +The software business is the worst +of all possible business models, +except for all the others. + -- Marc Andreessen +~ +Instant gratification takes too long. + -- Carrie Fisher +~ +Meetings are indispensable when you don't want to do anything. + -- John Kenneth Galbraith +~ +No one can have a higher opinion of him than I have, +and I think he's a dirty little beast. + -- W. S. Gilbert +~ +Man's capacity for justice makes democracy possible, but +man's inclination to injustice makes democracy necessary. + -- Reinhold Niebuhr +~ +The great tragedy of Science--the slaying of a +beautiful hypothesis by an ugly fact. + -- Thomas H. Huxley +~ +Against logic there is no armor like ignorance. -- Laurence J. Peter +~ +In mathematics you don't understand things. +You just get used to them. + -- John von Neumann +~ +Well, if I called the wrong number, why did you answer the phone? + -- James Thurber +~ +As for me, except for an occasional heart attack, +I feel as young as I ever did. + -- Robert Benchley +~ +Who is rich? He that is content. Who is that? Nobody. + -- Benjamin Franklin +~ +When ideas fail, words come in very handy. -- Johann Wolfgang von Goethe +~ +The wisdom of the wise, and the experience of ages, +may be preserved by quotation. + -- Benjamin Disraeli +~ +Hi Mark, +I am not familiar with the command RTFM. +I tried giving it but got an warning that the command does not exist. +Regards, +Sodip + -- A real user's response to advice +~ +A happy childhood has spoiled many a promising life. + -- Robertson Davies +~ +The test of a first-rate intelligence is the ability to hold two +opposed ideas in the mind at the same time, and still retain the +ability to function. + -- F. Scott Fitzgerald +~ +Everything you've learned in school as "obvious" becomes less and +less obvious as you begin to study the universe. For example, there +are no solids in the universe. There's not even a suggestion of a +solid. There are no absolute continuums. There are no surfaces. +There are no straight lines. + -- R. Buckminster Fuller +~ + Our happiness doesn't depend on somebody else's action or on anything +else. It doesn't depend on our success, but rather on the effort we're +willing to put into everything we do. Even if people disappoint or fail +us left and right, even if people turn against us, hurt us, lie about us, +don't understand us, even if they think they know everything about us and +judge us unfairly, they can't infringe upon our happiness. True happiness +means that we have a deep-seated peace and tranquility that transcends all +the difficulties of life, that cannot be disturbed by the chaos and +warfare that might touch our lives. + Digging in the trenches of a Nazi concentration camp Victor Frankl once +said to a fellow inmate: "This is where you've got to find your happiness-- +right here in this trench, in this camp." It is a simple matter of fact: +you can be just as happy in a concentration camp, horrific and terrible as +it surely is, as you can in any other circumstance in life. + For this is where we're supposed to find our happiness--where we are +now, wherever that might happen to be, in all that we do, in whatever +circumstances we find ourselves. Being happy involves the intense +struggle of entering intimately into all that we do. + -- "The Monks of New Skete, In the Spirit of Happiness" +~ +Upon this gifted age, in its dark hour, +Rains from the sky a meteoric shower +Of facts . . . they lie unquestioned, uncombined. +Wisdom enough to leech us of our ill +Is daily spun; but there exists no loom +To weave it into fabric. + -- Edna St. Vincent Millay, from "Upon This Age" +~ +An ignorant person is one who doesn't know what you have just found out. + -- Will Rogers +~ +Everybody gets so much information all day long +that they lose their common sense. + -- Gertrude Stein +~ +Rationalists, wearing square hats, +Think, in square rooms, +Looking at the floor, +Looking at the ceiling. +They confine themselves +To right-angled triangles. +If they tried rhomboids, +Cones, waving lines, ellipses-- +As, for example, the ellipse of the half-moon-- +Rationalists would wear sombreros. + -- Wallace Stevens +~ +I believe in getting into hot water; it keeps you clean. + -- G. K. Chesterton +~ +fat in dee head / fat in dee mind / thin on dee love / absent dee kind +yon falwell got dee big head / falwell got dee tiny brain +falwell don' like what you be liken' / falwell hate what he don' understan' +which be everyteen' for dis one / falwell dee bigot boy. + -- love poem to jerry f. +~ +The cat is, above all things, a dramatist. + -- Margaret Benson +~ +I do not feel obliged to believe that the same God who has endowed +us with sense, reason and intellect has intended us to forgo their use. + -- Galileo Galilei +~ +The human mind treats a new idea the same way +the body treats a strange protein; it rejects it. + -- P. B. Medawar +~ +I would never die for my beliefs because I might be wrong. + -- Bertrand Russell +~ +The key to being a good manager is keeping the people +who hate me away from those who are still undecided. + -- Casey Stengel +~ +Reality is nothing but a collective hunch. -- Lily Tomlin +~ +To invent, you need a good imagination and a pile of junk. -- Thomas Edison +~ +If I knew I was going to live this long, +I'd have taken better care of myself. + -- Mickey Mantle +~ +What we anticipate seldom occurs; what we least expected generally happens. + -- Benjamin Disraeli +~ +The cat is the mirror of the human mind, personality and attitude, +just as the dog mirrors his human's physical appearance. + -- Winfred Carriere +~ +Is sloppiness in speech caused by ignorance or apathy? +I don't know and I don't care. + -- William Safire +~ +Historians are like deaf people who go on +answering questions that no one has asked them. + -- Leo Tolstoy +~ +There are two moments worthwhile in writing, the one when you +start and the other when you throw it in the waste-paper basket. + -- Samuel Beckett +~ +The capacity of human beings to bore one another seems +to be vastly greater than that of any other animal. + -- H. L. Mencken +~ +I have yet to see any problem, however complicated, which, when you +looked at it in the right way, did not become still more complicated. + -- Poul Anderson +~ +common sense is just the things your parents tell you that +actually make it into your brain when you're a kid. + + don't touch hot things, + look both ways when crossing the street, + don't eat dirt. + +these ones work; thus they survive. + -- fred t. hamster +~ +Men have become the tools of their tools. -- Henry David Thoreau +~ +Lying increases the creative faculties, expands the +ego, and lessens the frictions of social contacts. + -- Clare Booth Luce +~ +You can't build a reputation on what you are going to do. + -- Henry Ford +~ +When we remember we are all mad, the mysteries +disappear and life stands explained. + -- Mark Twain +~ +Why was I born with such contemporaries? -- Oscar Wilde +~ +It is the nature of all greatness not to be exact. -- Edmund Burke +~ +What's on your mind, if you will allow the overstatement? -- Fred Allen +~ +The folly of mistaking a paradox for a discovery, a metaphor for a proof, +a torrent of verbiage for a spring of capital truths, and oneself for an +oracle, is inborn in us. + -- Paul Valery +~ +The mad mind does not halt. If it halts, it is enlightenment. + -- Chinese Zen Saying +~ +He who controls his mind and has cut off desire and anger realizes the Self. + -- The Bhagavad Gita +~ +A fly, when it exists, has as much being as God. -- Soren Kierkegaard +~ +Our final experience, like our first, is conjectural. +We move between two darknesses. + -- E. M. Forster +~ +If you have not lived through something, it is not true. -- Kabir +~ +I like a view but I like to sit with my back to it. -- Gertrude Stein +~ +Lightning flashes! +So bright in the eyes of those +clutching at the thought of death. + -- Issa +~ +The more faithfully you listen to the voice within you, +the better you will hear what is sounding outside. +And only she who listens can speak. + -- Dag Hammarskjold +~ +If knowledge does not liberate the self from the self, +then ignorance is better than such knowledge. + -- Sina'i +~ +When the eye wakes up to see again, +it suddenly stops taking anything for granted. + -- Frederick Franck +~ +Only when we know little things do we know +anything; doubt grows with knowledge. + -- Johann Wolfgang Von Goethe +~ +Among corn stalks +wind rippling +just for the corn. + -- Soen Nakagawa +~ +He, O men, is the wisest, who, like Socrates, +knows that his wisdom is in truth worth nothing. + -- Plato +~ +Words are just like a man carrying a lamp to look for +his property, by which he can say: this is my property. + -- The Lankavatara Sutra +~ +That best portion + of a good man's life, +His little, nameless, + unremembered acts +Of kindness and love. + -- William Wordsworth +~ +I don't know whether I believe in God or not. I think, really, I'm +some sort of Buddhist. But the essential thing is to put oneself +in a frame of mind which is close to that of prayer. + -- Henri Matisse +~ +Life is like stepping onto a boat that is about to sail out to sea and sink. + -- Shunryu Suzuki +~ +Such stillness +The cries of the cicadas +Sink into the rocks. + -- Basho +~ +We live in a rainbow of chaos. -- Paul Cezanne +~ +love's like a purple dinosaur because it can't outrun a truck, +life's like a bowl of nixons sometimes you f*ck. + -- fred t. hamster +~ +Never interrupt your enemy when he is making a mistake. + -- Napoleon Bonaparte +~ +Almost all absurdity of conduct arises from the +imitation of those whom we cannot resemble. + -- Samuel Johnson +~ +We are at the very beginning of time for the human race. It is not +unreasonable that we grapple with problems. But there are tens of +thousands of years in the future. Our responsibility is to do what +we can, learn what we can, improve the solutions, and pass them on. + -- Richard Feynman +~ +The playful kitten, with its pretty little tigerish gambols, is infinitely +more amusing than half the people one is obliged to live with in the world. + -- Lady Sydney Morgan +~ +The mystery of life is not a problem to be +solved but a reality to be experienced. + -- Aart Van Der Leeuw +~ +Have no designs on becoming a Buddha. This has nothing +whatever to do with sitting or lying down. + -- Dogen +~ +When we are unable to find tranquility within ourselves, +it is useless to seek it elsewhere. + -- Francois, Duc de La Rochefoucauld +~ +We must not, in trying to think about how we can make a big +difference, ignore the small daily differences we can make which, +over time, add up to big differences that we often cannot foresee. + -- Marian Wright Edelman +~ +Golf is a good walk spoiled. + -- Mark Twain +~ +The day we fear as our last is but the birthday of eternity. -- Seneca +~ +Life is stressful enough without customer service. -- Thomas Oliver +~ +To assume a cat's asleep is a grave mistake. +He can close his eyes and keep both his ears awake. + -- Aileen Fisher +~ +Depend upon it, sir, when a man knows he is to be hanged +in a fortnight, it concentrates his mind wonderfully. + -- Samuel Johnson +~ +The tendency has always been strong to believe that whatever received +a name must be an entity or being, having an independent existence of +its own. And if no real entity answering to the name could be found, +men did not for that reason suppose that none existed, but imagined +that it was something peculiarly abstruse and mysterious. + -- John Stuart Mill +~ +You aren't a failure until you start blaming others for your mistakes. + -- John Wooten +~ + The master, Hseuh-feng, asked a newly arrived monk where +he had come from. The monk answered: "From the Monastery of +Spiritual Light." + The master said, "In the daytime we have sunlight; in the +evening, lamplight. What is spiritual light?" + The monk had no answer. + The Master said, "Sunlight. Lamplight." + -- Zen mondo +~ +There is no wealth but life. -- John Ruskin +~ +Approach it and there is no beginning; follow it +and there is no end. You can't know it, but you +can be it, at ease in your own life. + -- Lao-Tzu +~ +And the end of all our exploring will be to arrive where +we started and know the place for the first time. + -- T. S. Eliot +~ +There's an old saying in Tennessee--I know it's in Texas, probably in +Tennessee--that says: Fool me once, shame on [pause] shame on you. +[Pause] Fool me [long, uncomfortable, agonizing pause] you can't get +fooled again. + -- Bush at East Literature Magnet School in Nashville yesterday. +~ +Concentration is my motto--first honesty, +then industry, then concentration. + -- Andrew Carnegie +~ +Nothing strengthens authority so much as silence. -- Charles de Gaulle +~ +If you would make a man happy, do not add to his possessions but +subtract from the sum of his desires. + -- Seneca +~ +Live life like your hair is on fire. -- Ashley Brilliant +~ + City people have a hard time handling the silences when they first +come out to the country. After a certain period of solitude, I myself +experiencing an aloneness that is sometimes disturbing, but the country +eventually cleanses my spirit and purges my body of the sounds, fumes and +toxins of urban life. I think cities feed psychological stress and tension +in many ways, including an overload of electrical forces and energy. When +you go to a country cabin without electricity, you will be surprised at how +tensions fall away. + Some practitioners advocate that urban dwellers removed from the +country find a space in the yard or the garden and dig a hole deep enough +to enfold the body. Lie down in the hole. Make sure your body can lie just +below the surface of the ground. Stay in this hollow of earth. You will be +surprised how rested you will feel simply because you have escaped for a +moment the man-made influences. You have retreated for a moment to Mother +Earth's very simple electrical systems. For the chemical and electrical +balance of the body to be calibrated, you have to stay close to the earth +itself, align yourself with its polarity so that your body can find harmony +between the interior world and the exterior universe. + Once we become detached from nature, we begin to think we can do +without it. The lights of the Great White Way overpower the stars. It is +very hard to see the brightest constellation when you live in or near a +city. The dark solitude of the country reunites you with the universe of +the stars. The woods and hills restore in you something primal in yourself. +The sea's pulse sets your own heartbeat. + -- James Earl Jones, "Voices and Silences" +~ +Blessed is he who expects nothing, for he shall never be disappointed. + -- Jonathan Swift +~ +i think i found my other leak now. and this was a frelling dumb one. +somehow the deletion of the memory was commented out. wtf? +arrrrrrrhhhh... i don't know what i'm doing.... +i hate the C and everything coded in it. + -- fred t. hamster +~ +When men are pure, laws are useless; +when men are corrupt, laws are broken. + -- Benjamin Disraeli +~ +Only the mediocre are always at their best. -- Jean Giraudoux +~ +If you believe the doctors, nothing is wholesome; +if you believe the theologians, nothing is innocent; +if you believe the military, nothing is safe. + -- Lord Salisbury +~ +He is indebted to his memory for his jests +and to his imagination for his facts. + -- Richard Brinsley Sheridan +~ +If scientific reasoning were limited to the logical processes of +arithmetic, we should not get very far in our understanding of the +physical world. One might as well attempt to grasp the game of +poker entirely by the use of the mathematics of probability. + -- Vannevar Bush +~ +As a public company, and as a CEO, you have to worry about the stock price +to some extent. But you figure out after a while that there are very few +things you can do in the short term that can positively impact stock price. +If you build a great company, the value of the company goes up. That's very +important for employees to realize coming out of this age of day trading, +mass hysteria and "we're all going to be bazillionaires." You've got to get +people saying: "Why are we really here?" Well, we're here to build a great +company. That takes time. You may have a great day today and the stock goes +down, and you may have a horrible day tomorrow and the stock goes up. Short +term, they don't have a lot to do with each other. But over a long period of +time you build a great company, and it's going to be worth a lot. Customers +will reward it. Shareholders will reward it. Employees will want to be a +part of it and beat our door down to want to work here. + -- Michael Dell, Founder of Dell Computers +~ +Cats have the gift of appearing at ease in any situation--high, low or +anywhere in between. + -- Dr. Morag Kerr +~ +I am no more humble than my talents require. -- Oscar Levant +~ +Happiness is nothing more than good health and a bad memory. + -- Albert Schweitzer +~ +It could probably be shown by facts and figures that there +is no distinctly American criminal class except Congress. + -- Mark Twain +~ +The torch of doubt and chaos, this is what the sage steers by. -- Chuang-Tzu +~ +I would patch them, but I have not a half-sheet of paper. +Ah, well--at least torn windows don't need to be pushed open. +The blowing wind puts out my lamp. +Rain falling from the eaves wets my inkstone. + -- Ikkyu +~ +The quest for certainty blocks the search for meaning. +Uncertainty is the very condition to impel man to unfold his powers. + -- Erich Fromm +~ +I tell you: one must still have chaos in one, +to give birth to a dancing star. + -- Friedrich Nietzsche +~ +At that pond + the frog is growing old now-- + among fallen leaves. + -- Buson +~ +Consciousness is a being, +the nature of which +is to be conscious of +the nothingness of its being. + -- Jean-Paul Sartre +~ +Since it is all too clear, +It takes time to grasp it. +When you understand that it's foolish +To look for fire with fire, +The meal is already cooked. + -- Wu-men +~ +May you live all the days of your life. -- Jonathan Swift +~ +How could there be any question of acquiring +or possessing, when the one thing needful for +a man is to /become/--to /be/ at last, and to die +in the fullness of his being. + -- Antoine de Saint-Exupery +~ +Death, the most dreaded of evils, is therefore of no concern to us; +for while we exist death is not present, +and when death is present we no longer exist. + -- Epicurus +~ +Singing and danching are the voice of dharma. -- Hakuin +~ +All great truths begin as blasphemies. -- George Bernard Shaw +~ +Hope is not a strategy. -- Thomas McInerney +~ +I do not take a single newspaper, nor read one a month, +and I feel myself infinitely the happier for it. + -- Thomas Jefferson +~ +As I know more of mankind I expect less of them, and am ready now +to call a man a good man upon easier terms than I was formerly. + -- Samuel Johnson +~ +Sometimes the veil between human and animal intelligence wears +very thin--then one experiences the supreme thrill of keeping +a cat, or perhaps allowing oneself to be owned by a cat. + -- Catherine Manley +~ +I'm never going to be famous. My name will never be writ large on the +roster of Those Who Do Things. I don't do any thing. Not one single +thing. I used to bite my nails, but I don't even do that any more. + -- Dorothy Parker +~ +One of the symptoms of an approaching nervous breakdown is the +belief that one's work is terribly important. + -- Bertrand Russell +~ +The universality of change, when completely understood, is the seeing +into the heart of all things, and the Mind that thus understands is +the Mind that truly seeks the way. + -- Nagarjuna +~ +The spirit down here in man and the spirit up there in the sun, +in reality are only one spirit, and there is no other one. + -- The Upanishads +~ +To free oneself is nothing--the really arduous task is to +know what to do with one's freedom. + -- Andre Gide +~ +I hate women because they always know where things are. -- James Thurber +~ +I have never taken any exercise except sleeping and resting. -- Mark Twain +~ +Be careful about reading health books. +You may die of a misprint. + -- Mark Twain +~ +Autumn, cloud blades on the horizon. +The west wind blows from ten thousand miles. +Dawn, in the clear morning air. +Farmers busy after long rain. +The desert trees shed their few green leaves. +The mountain pears are tiny but ripe. +A Tartar flute plays by the city gate. +A single wild goose climbs into the void. + -- Tu Fu +~ +When you get there, there isn't any there there. -- Gertrude Stein +~ +I want to sing like birds sing, +not worrying who hears or what they think. + -- Jelaluddin Rumi +~ +The practice of zazen is not +for gaining a mystical mind. +Zazen is for allowing a clear mind-- +as clear as a bright autumn day. + -- Shunryu Suzuki +~ +... and he was almighty because he had wrenched +from chaos the secret of its nothingness. + -- Jean-Paul Sartre +~ +Practice thirty more years. -- Zen proverb +~ +Student: "Roshi, what are you doing here?" +Suzuki-roshi: "Nothing special." + -- Zen mondo +~ +After the leaves fall in the village at the foot of Ogura Peak, +one can see through the tree branches the moon shining in the clear. + -- Saigyo +~ +Chaos often breeds life, where order breeds habit. + -- Henry Adams +~ +You yourselves must make the exertion. +The Buddhas are only teachers. + -- Shakyamuni Buddha +~ +Cease from practice based on intellectual understanding, pursuing words, +and following after speech, and learn the backward step that turns your +light inward to illuminate your self. Body and mind of themselves will +drop away, and your original face will be manifest. + -- Dogen +~ +Next time you have a bad day, imagine this: You are a Siamese twin. +Your brother that is attached to you at the shoulder is gay. +You are not. But you only have one ass. +~ +From now on, ending a sentence with a preposition +is something up with which I will not put. + -- Winston Churchill +~ +The meaning of life is to see. -- Hui-Neng +~ +Fundamentally not one thing exists. -- Hui-Neng +~ +In the blue heavens, cold geese calling. +On the empty hills, leaves flying. + -- Ryokan +~ +When a thing is funny, search it carefully for a hidden truth. + -- George Bernard Shaw +~ +An intense love of solitude, distaste for involvement in worldly +affairs, persistence in knowing the Self and awareness of the goal +of knowing--all this is called true knowledge. + -- The Bhagavad Gita +~ +Some people are born to lift heavy weights. +Some are born to juggle with golden balls. + -- Max Beerbohm +~ +Every man serves a useful purpose: A miser, for +example, makes a wonderful ancestor. + -- Lawrence J. Peter +~ +Smokey the Bear Sutra + +Once in the Jurassic about 150 million years ago, +the Great Sun Buddha in this corner of the Infinite +Void gave a Discourse to all the assembled elements +and energies: to the standing beings, the walking beings, +the flying beings, and the sitting beings -- even grasses, +to the number of thirteen billion, each one born from a +seed, assembled there: a Discourse concerning +Enlightenment on the planet Earth. + +"In some future time, there will be a continent called +America. It will have great centers of power called +such as Pyramid Lake, Walden Pond, Mt. Rainier, Big Sur, +Everglades, and so forth; and powerful nerves and channels +such as Columbia River, Mississippi River, and Grand Canyon +The human race in that era will get into troubles all over +its head, and practically wreck everything in spite of +its own strong intelligent Buddha-nature." + +"The twisting strata of the great mountains and the pulsings +of volcanoes are my love burning deep in the earth. +My obstinate compassion is schist and basalt and +granite, to be mountains, to bring down the rain. In that +future American Era I shall enter a new form; to cure +the world of loveless knowledge that seeks with blind hunger: +and mindless rage eating food that will not fill it." + +And he showed himself in his true form of + + SMOKEY THE BEAR + + a.. A handsome smokey-colored brown bear standing on his hind legs, +showing that he is aroused and + watchful. + + b.. Bearing in his right paw the Shovel that digs to the truth beneath +appearances; cuts the roots of useless + attach- ments, and flings damp sand on the fires of greed and war; + + c.. His left paw in the Mudra of Comradely Display -- indicating that all +creatures have the full right to live to + their limits and that deer, rabbits, chipmunks, snakes, dandelions, and +lizards all grow in the realm of the + Dharma; + + d.. Wearing the blue work overalls symbolic of slaves and laborers, the +countless men oppressed by a + civilization that claims to save but often destroys; + + e.. Wearing the broad-brimmed hat of the West, symbolic of the forces that +guard the Wilderness, which is the + Natural State of the Dharma and the True Path of man on earth: all true +paths lead through mountains -- + + f.. With a halo of smoke and flame behind, the forest fires of the +kali-yuga, fires caused by the stupidity of + those who think things can be gained and lost whereas in truth all is +contained vast and free in the Blue Sky + and Green Earth of One Mind; + + g.. Round-bellied to show his kind nature and that the great earth has +food enough for everyone who loves her + and trusts her; + + h.. Trampling underfoot wasteful freeways and needless suburbs; smashing +the worms of capitalism and + totalitarianism; + + i.. Indicating the Task: his followers, becoming free of cars, houses, +canned foods, universities, and shoes; + master the Three Mysteries of their own Body, Speech, and Mind; and +fearlessly chop down the rotten + trees and prune out the sick limbs of this country America and then burn +the leftover trash. + +Wrathful but Calm. Austere but Comic. Smokey the Bear will +Illuminate those who would help him; but for those who would hinder or +slander him, + + HE WILL PUT THEM OUT. + +Thus his great Mantra: + + Namah samanta vajranam chanda maharoshana + Sphataya hum traks ham nam + + "I DEDICATE MYSELF TO THE UNIVERSAL DIAMOND + BE THIS RAGING FURY DESTROYED" + +And he will protect those who love woods and rivers, +Gods and animals, hobos and madmen, prisoners and sick +people, musicians, playful women, and hopeful children: + +And if anyone is threatened by advertising, air pollution, television, +or the police, they should chant SMOKEY THE BEAR'S WAR SPELL: + + DROWN THEIR BUTTS + CRUSH THEIR BUTTS + DROWN THEIR BUTTS + CRUSH THEIR BUTTS + +And SMOKEY THE BEAR will surely appear to put the enemy out +with his vajra-shovel. + + a.. Now those who recite this Sutra and then try to put it in practice +willl accumulate merit as countless as the + sands of Arizona and Nevada. + + b.. Will help save the planet Earth from total oil slick. + + c.. Will enter the age of harmony of man and nature. + + d.. Will win the tender love and caresses of men, women, and beasts. + + e.. Will always have ripe blackberries to eat and a sunny spot under a +pine tree to sit at. + + f.. AND IN THE END WILL WIN HIGHEST PERFECT ENLIGHTENMENT. + + thus have we heard. +~ +I didn't attend the funeral, but I sent a +nice letter saying that I approved of it. + -- Mark Twain +~ +We are here to witness the creation and to abet it. -- Annie Dillard +~ +Today is the eighth of the month, tomorrow is the thirteenth. -- Zen proverb +~ +Often I am still listening when the song is over. + -- Marquis de Saint-Lambert +~ +Worldly acquisitions of wealth and the need of clinging to them, as well as +the pursuit of the Eight Worldly Aims, I regard with as much loathing and +disgust as a man who is suffering from biliousness regardeth the sight of +rich food. Nay, I regard them as if they were the murderers of my father; +therefore it is that I am assuming this beggarly and penurious mode of life. + -- Milarepa +~ +Vast solitude +My thinning body +Transparent autumn + -- Soen Nakagawa +~ +Maturity is only a short break in adolescence. -- Jules Feiffer +~ +It is not from the benevolence of the butcher, the brewer or the baker +that we expect our dinner, but from their regard to their own interest. + -- Adam Smith +~ +Fashion is a form of ugliness so intolerable +that we have to alter it every six months. + -- Oscar Wilde +~ +Rock journalism is people who can't write interviewing people who +can't talk for people who can't read. + -- Frank Zappa +~ + Bodhidharma sat facing the wall. The Second Patriarch, after standing +outside in the snow for so long, cut off his arm. "My mind is not yet +at peace. Please, Master, put my mind at peace." + Bodhidharma said: "Bring me your mind and I will pacify it for you." + The Second Patriarch replied: "Although I have searched for my mind, +it is totally ungraspable." + Bodhidharma said: "In that case I have pacified your mind for you." +~ +Explanation of the unspeakable cannot be finished. -- Shakyamuni Buddha +~ +We need to find God, and He cannot be found in noise and restlessness. + -- Mother Teresa +~ +Since it is the practice of enlightenment, +that practice has no beginning and +since it is enlightenment within the practice, +that realization has no end. + -- Dogen +~ +The quieter you become, the more you can hear. -- Baba Ram Dass +~ +As long as you seek for something, you will get +the shadow of reality and not reality itself. + -- Shunryu Suzuki +~ +Why is it that our memory is good enough to retain the least +triviality that happens to us, and yet not good enough to +recollect how often we have told it to the same person? + -- Francois, Duc de La Rochefoucauld +~ +I love being married. It's so great to find that one +special person you want to annoy for the rest of your life. + -- Rita Rudner +~ +I don't give a damn for a man that can only spell a word one way. + -- Mark Twain +~ +A happy childhood has spoiled many a promising life. -- Robertson Davies +~ +At this point in history genius has become +a commodity, an ambition, even a lifestyle. + -- Marjorie Garber +~ +A desk is a dangerous place from which to watch the world. -- Jean le Carre +~ +Martyrdom is the only way in which a +man can become famous without ability. + -- George Bernard Shaw +~ +For there is nothing sweeter than his peace when at rest. +For there is nothing brisker than his life when in motion. + -- Christopher Smart +~ +It's not autumn's cold that keeps me awake, +but what I feel before the grasses and trees in my courtyard. +My banana tree has lost its leaves; my parasol tree is old; +and night after night--the sound of wind, the sound of rain. + -- Chujo Joshin +~ +I believe in an ultimate decency of things. -- Robert Louis Stevenson +~ +Autumn light +fills the room +vacancy. + -- Soen Nakagawa +~ +We have two eyes to see two sides of things, but there must be a third eye +which will see everything at the same time and yet not see anything. +That is to understand Zen. + -- D. T. Suzuki +~ +Yes it is in our idleness, in our dreams, that the submerged truth +sometimes comes to the top. + -- Virginia Woolf +~ +Having precise ideas often leads to a man doing nothing. -- Paul Valery +~ +The cat was a creature of absolute convictions, +and his faith in his deductions never varied. + -- Mary E. Wilkins Freeman +~ +Let every nation know, whether it wishes us well or ill, that we shall pay +any price, bear any burden, meet any hardship, support any friend, oppose +any foe to assure the survival and the success of liberty. + -- John F. Kennedy +~ +With Henry Adams we see the moment when the pronouncements +of philosophers ceased to be greeted with forehead slaps of +recognition or shouts of "Heretic!" and began to be met +with mumbles of "Oh, shut up." + -- P. J. O'Rourke +~ +The nature of mind, when understood, no human words can +compass or disclose. Enlightenment is naught to be obtained, +and he that gains it does not say he knows. + -- Bodhidharma +~ +As long as you haven't experienced this: to die and so to grow, +you are only a troubled guest on the dark earth. + -- Johann Wolfgang von Goethe +~ +Nirvana is right here, before our eyes. -- Hakuin +~ +It is the stars not known to science that I would know, +the stars which the lonely traveler knows. + -- Henry David Thoreau +~ +You've got to take the bitter with the sour. -- Sam Goldwyn +~ +And a man shall be free, and as pure as the day prior to +his conception in his mother's womb, when he has nothing, +wants nothing and knows nothing. + -- Meister Eckhart +~ +Words, like eyeglasses, blur everything they do not make clear. + -- Joseph Joubert +~ +Outside mind there is no Buddha, +Outside Buddha there is no mind. + -- Ma-Tsu +~ +They are ill discoverers that think there is no land, +when they can see nothing but sea. + -- Sir Francis Bacon +~ +My father hated radio and could not wait for television to be +invented so he could hate that too. + -- Peter de Vries +~ +Opportunity is missed by most people because +it is dressed in overalls and looks like work. + -- Thomas Alva Edison +~ +Three may keep a secret, if two of them are dead. -- Benjamin Franklin +~ +When the politicians complain that TV turns the proceedings into a circus, +it should be made clear that the circus was already there, and that TV +has merely demonstrated that not all the performers are well trained. + -- Edward R. Murrow +~ +In literature as in love, we are astonished at what is chosen by others. + -- Andre Maurois +~ +The art of medicine consists in amusing the +patient while nature cures the disease. + -- Voltaire +~ +I am enlightened, and always have been, simultaneously +with the beginning of the universe. + -- The Buddha, first words after realizing the truth +~ +A billion stars go spinning through the night, +blazing high above your head. +But in you is the presence that will be, +when the stars are dead. + -- Rainer Maria Rilke +~ +The goal of Buddhism is to bring about right human life, not to have +the teaching, or teacher, or sentient beings, or Buddhism, or Buddha. +But if you think that without any training you can have that kind of +life, that is a big mistake. + -- Shunryu Suzuki +~ +So much of what we call management consists +in making it difficult for people to work. + -- Peter Drucker +~ +People with courage and character always seem sinister to the rest. + -- Hermann Hesse +~ +Excess on occasion is exhilarating. It prevents moderation from +acquiring the deadening effect of a habit. + -- W. Somerset Maugham +~ +Everything you know is wrong. -- Firesign Theatre +~ +One day Yuan-wu took the high seat, and said: + A monk asked Yun-men: 'Where did all the buddhas come from?' + Yun-men answered: 'The East Mountain walks over the water.' + But if I were asked, I would not answer that way. + I would say: 'A fragrant breeze comes of itself from the south, + and in the palace a refreshing coolness stirs.' + -- Zen mondo +~ +I would believe only in a god who could dance. -- Friedrich Nietzsche +~ +The moon floats above the pines, and the night veranda is cold as the +ancient, clear sound comes from your finger tips. The old melody usually +makes the listeners weep, but Zen music is beyond sentiment. Do not play +again unless the Great Sound of Lao-tzu accompanies you. + -- Hseuh-T'ou +~ +Seriousness is the only refuge of the shallow. -- Oscar Wilde +~ +Logically considered, Zen may be full of contradictions and repetitions. +But as it stands above all things, it goes serenely on its own way. + -- D. T. Suzuki +~ +Clouds come from time to time--and bring a chance +to rest from looking at the moon. + -- Basho +~ +Listen. Make a way for yourself inside yourself. +Stop looking in the other way of looking. + -- Jelaluddin Rumi +~ +Do not be an embodiment of fame; do not be a storehouse of schemes; +do not be an undertaker of projects; do not be a proprietor of wisdom. +Wander where there is no trail. Hold on to all that you have received +from Heaven but do not think you have gotten anything. Be empty, that +is all. The Perfect Man uses his mind like a mirror--going after nothing, +welcoming nothing, responding but not storing. + -- Chuang-Tzu +~ +In the end, everything is a gag. -- Charlie Chaplin +~ +One can not be certain of living +even into the evening. +In the dim first light +I watch the waves +from a departing boat. + -- Shinkei +~ +The truth dazzles gradually, or else the world would be blind. + -- Emily Dickinson +~ +All things in this world are impermanent. +They have the nature to rise and pass away. +To be in harmony with this truth brings true happiness. + -- Buddhist Chant +~ +True words always seem paradoxical but no other form +of teaching can take their place. + -- Lao-Tzu +~ +Did you not know that at the edge of a deep valley there is an excellent +pine tree growing up straight in spite of the many years of cold? + -- Keizan Zenji +~ +Death destroys a man, but the idea of death saves him. + -- E. M. Forster +~ +In a snowfall that covers the winter grass +a white heron uses his own whiteness to disappear. + -- Dogen +~ +Settle the self on the self. -- Shunryu Suzuki +~ +Finished, finished... +when it is completely finished +there is nothing to finish. + -- Soen Nakagawa +~ +I think age is a very high price to pay for maturity. -- Tom Stoppard +~ +Which is the more beautiful, feline movement or feline stillness? + -- Elizabeth Hamilton +~ +We cannot speak without incurring some risk, at least in theory; +the only way of being absolutely safe is to say nothing. + -- Isaiah Berlin +~ +Numerous studies demonstrate that people can be motivated to creativity +simply with the addition of an instruction to "be creative." + -- Richard Saul Wurman +~ + Our obsessions with history and prophecy perhaps reflect an inability +to comprehend the implications of geological time. The mind's traditional +organization of duration into past, present, and future really has more +relevance to the five-thousand-year-old earth of the seventeenth century +than it does to the five-billion-year-old one of the twentieth. Past and +future require certain limitations and symmetries to be meaningful--there +must be a plot or at least a story. But time is really not much like a +story. It is more like an ocean current that rises from imperfectly +perceived depths and flows into unseen distances. + This immensity might seem to diminish the present--the living moment-- +to utter insignificance, but actually the present looms much larger in +geological time than in historical time. If time is a story, the present +is merely a hiatus between the significant events that were and will be. +If time is an ocean, however, the present is not less important than the +other moments, which stretch away on all sides, any more than a single +water molecule in an ocean is less important than the others. In a sense +each living moment is the whole of time--an eternal present--because it +can't be set apart from all the other moments. + -- David Rains Wallace, from "Idle Weeds" +~ +Success usually comes to those who are too busy to be looking for it. + -- Henry David Thoreau +~ +We are generally the better persuaded by the reasons we +discover ourselves than by those given to us by others. + -- Blaise Pascal +~ +You can't wait for inspiration. You have to go after it with a club. + -- J. M. Barrie +~ +Television has raised writing to a new low. -- Samuel Goldwyn +~ +I detest life-insurance agents; they always argue +that I shall some day die, which is not so. + -- Stephen Leacock +~ +People everywhere confuse what they read in newspapers with news. + -- A. J. Liebling +~ +He can compress the most words into the smallest ideas of any man I ever met. + -- Abraham Lincoln +~ +If the voter cannot grasp the details of the problems of the day because he +has not the time, the interest or the knowledge, he will not have a better +public opinion because he is asked to express his opinion more often. + -- Walter Lippmann +~ +i have eliminated every fault, +except for pride... +doomed to walk the earth +another time. + -- fred t. hamster +~ +In spite of the cost of living, it's still popular. -- Laurence J. Peter +~ +Among the maxims on Lord Naoshige's wall there was this one: "Matters of +great concern should be treated lightly." Master Ittei commented, "Matters +of small concern should be treated seriously." Among one's affairs there +should not be more than two or three matters of what one could call great +concern. If these are deliberated upon during ordinary times, they can be +understood. Thinking about things previously and then handling them lightly +when the time comes is what this is all about. To face an event and solve +it lightly is difficult if you are not resolved beforehand, and there will +always be uncertainty in hitting your mark. However, if the foundation is +laid previously, you can think of the saying, "Matters of great concern +should be treated lightly," as your basis for action. + -- Hagakure, book of the samurai +~ +There is something to be learned from a rainstorm. When meeting with +a sudden shower, you try not to get wet and run quickly along the road. +But doing such things as passing under the eaves of houses, you still +get wet. When you are resolved from the beginning, you will not be +perplexed, though you still get the same soaking. This understanding +extends to everything. + -- Hagakure, book of the samurai +~ +I deserve good things. +I am entitled to my share of happiness. +I refuse to beat myself up. +I am attractive person. +I am fun to be with. + -- Stuart Smalley +~ +The intelligent man finds almost everything ridiculous, +the sensible man hardly anything. + -- Goethe +~ +In the history of the world, no one has ever washed a rented car. + -- Lawrence Summers +~ + I refuse to accept the idea that man is mere flotsam and jetsam in the +river of life unable to influence the unfolding events which surround him. +I refuse to accept the view that mankind is so tragically bound to the +starless midnight of racism and war that the bright daybreak of peace and +brotherhood can never become a reality. + I refuse to accept the cynical notion that nation after nation must +spiral down a militaristic stairway into the hell of thermonuclear +destruction. I believe that unarmed truth and unconditional love will +have the final word in reality. This is why right temporarily defeated is +stronger than evil triumphant. + I believe that even amid today's motor bursts and whining bullets, +there is still hope for a brighter tomorrow. I believe that wounded +justice, lying prostrate on the blood-flowing streets of our nations, can +be lifted from this dust of shame to reign supreme among the children of +men. + I have the audacity to believe that peoples everywhere can have three +meals a day for their bodies, education and culture for their minds, and +dignity, equality and freedom for their spirits. I believe that what +self-centered men have torn down, men other-centered can build up. I still +believe that one day mankind will bow before the altars of God and be +crowned triumphant over war and bloodshed, and nonviolent redemptive +goodwill will proclaim the rule of the land. + -- Martin Luther King, Jr +~ +Every increased possession loads us with new weariness. -- John Ruskin +~ +If a cluttered desk is the sign of a cluttered mind, +what is the significance of a clean desk? + -- Lawrence J. Peter +~ +I've been on a diet for two weeks and all I've lost is two weeks. + -- Totie Fields +~ +I am not young enough to know everything. -- Oscar Wilde +~ +I had never held a position for more than four years, +and did not so much plan my new jobs as flee my old ones. + -- Joseph Epstein +~ +It's a poor sort of memory that only works backward. + -- The White Queen, Lewis Carroll's Alice in Wonderland +~ +I have always felt that a politician is to be judged by + the animosities he excites among his opponents. + -- Winston Churchill +~ +All things are difficult before they are easy. -- Thomas Fuller +~ +There's no business like show business, but +there are several businesses like accounting. + -- David Letterman +~ +All truth passes through three stages. +First, it is ridiculed. +Second, it is violently opposed. +Third, it is accepted as being self-evident. + -- Arthur Schopenhauer +~ +Nonviolence means avoiding not only external physical violence +but also internal violence of spirit. You not only refuse to +shoot a man, but you refuse to hate him. + -- Martin Luther King, Jr. +~ +Talent is like the battery in a car. It'll get you started, +but if the generator is bad, you don't go very far. + -- Ellis Marsalis +~ +No man ever listened himself out of a job. -- Calvin Coolidge +~ +I was unable to devote myself to the learning of this algebra and the continued +concentration upon it, because of obstacles in the vagaries of time which +hindered me; for we have been deprived of all the people of knowledge save for +a group, small in number, with many troubles, whose concern in life is to +snatch the opportunity, when time is asleep, to devote themselves meanwhile to +the investigation and perfection of a science; for the majority of people who +imitate philosophers confuse the true with the false, and they do nothing but +deceive and pretend knowledge, and they do not use what they know of the +sciences except for base and material purposes; and if they see a certain +person seeking for the right and preferring the truth, doing his best to refute +the false and untrue and leaving aside hypocrisy and deceit, they make a fool +of him and mock him. + -- Omar Khayyam, "Treatise on Demonstration of Problems of Algebra" +~ +It is a profitable thing, if one is wise, to seem foolish. -- Aeschylus +~ +Imagine what it would be like if TV actually were good. +It would be the end of everything we know. + -- Marvin Minsky +~ +Many engineering deadlocks have been broken by people who are not engineers +at all. This is simply because perspective is more important than IQ. + -- Nicholas Negroponte +~ +We are doomed to choose, and every choice may entail an irreparable loss. + -- Isaiah Berlin +~ +California is a fine place to live--if you happen to be an orange. + -- Fred Allen +~ +Those whom the Gods would destroy, they first call promising. + -- Cyril Connolly +~ +It is difficult to produce a television documentary that is both +incisive and probing when every twelve minutes one is interrupted +by twelve dancing rabbits singing about toilet paper. + -- Rod Serling +~ +All successful newspapers are ceaselessly querulous and bellicose. They +never defend anyone or anything if they can help it; if the job is forced +on them, they tackle it by denouncing someone or something else. + -- H. L. Mencken +~ +The corollary of constant change is ignorance. This is not often +talked about: we computer experts barely know what we're doing. + -- Ellen Ullman +~ +The visionary lies to himself, the liar only to others. + -- Friedrich Nietzsche +~ +Moral indignation is jealousy with a halo. -- H. G. Wells +~ +Part of the inhumanity of the computer is that, +once it is competently programmed and working smoothly, +it is completely honest. + -- Isaac Asimov +~ +Psychiatry enables us to correct our faults by +confessing our parents' shortcomings. + -- Laurence J. Peter +~ +No opera plot can be sensible, for people do not sing +when they are feeling sensible. + -- W. H. Auden +~ +All truths are easy to understand once they are +discovered; the point is to discover them. + -- Galileo Galilei +~ +We are more ready to try the untried when what we do is inconsequential. +Hence the fact that many inventions had their birth as toys. + -- Eric Hoffer +~ +Considering how dangerous everything is, +nothing is really very frightening. + -- Gertrude Stein +~ +A bore is a man who deprives you of solitude +without providing you with company. + -- Gian Vincenzo Gravina +~ +What the world needs is more geniuses with humility. +There are so few of us left. + -- Oscar Levant +~ +In the end, we will remember not the words of +our enemies, but the silence of our friends. + -- Martin Luther King +~ +Happiness is an imaginary condition, formerly attributed by the +living to the dead, now usually attributed by adults to children, +and by children to adults. + -- Thomas Szasz +~ +You must first have a lot of patience to learn to have patience. + -- Stanislaw W. Lec +~ +I know the answer! The answer lies within the heart of all mankind! +The answer is 12? I think I'm in the wrong building. + -- Charles M. Schulz in "Peanuts" +~ +The key to being a good manager is keeping the people who +hate me away from those who are still undecided. + -- Casey Stengel +~ +What is written without effort is in general read without pleasure. + -- Samuel Johnson +~ +People who've never fired a gun ("weapon", in Army-talk) explain "fire power" +realities. Nobody--yet--would pretend to be an expert on brain surgery. But +military tactics and modern warfare? What could be easier, right? + -- Kenneth G. Robinson +~ +When two men in business always agree, one of them is unnecessary. + -- William Wrigley Jr. +~ +Few people think more than two or three times a year; I have made an +international reputation for myself by thinking once or twice a week. + -- George Bernard Shaw +~ +Thought is only a flash between two long nights, but this flash is everything. + -- Henri Poincare +~ +A well-laid business plan is no guarantee against the +disappearance of the industry on which it is based. + -- Tim Cavanaugh +~ +The way to protect human freedom is to make sure that your society is one +in which the benefits of being a member of the society are so tempting and +so great that people will take responsibility in order for it to happen. + -- Daniel Dennett +~ +Technology seems to always start out promising specialization and +personalization. In the end, it delivers more homogenization instead. + -- Lee Gomes +~ +Try to learn something about everything and everything about something. + -- Thomas H. Huxley +~ +Art is making something out of nothing and selling it. -- Frank Zappa +~ +Chess is as elaborate a waste of human intelligence +as you can find outside an advertising agency. + -- Raymond Chandler +~ +The only way to discover the limits of the possible +is to go beyond them into the impossible. + -- Ben Hecht +~ +Life is full of misery, loneliness, and +suffering--and it's all over much too soon. + -- Woody Allen +~ +An economist is a man who states the obvious in terms of the incomprehensible. + -- Alfred A. Knopf +~ +The real problem is not whether machines think but whether men do. + -- B. F. Skinner +~ +In great affairs men show themselves as they wish to be seen; +in small things they show themselves as they are. + -- Nicholas Chamfort +~ +Pain is no evil, unless it conquers us. -- Charles Kingsley +~ +There's no reason to burn books if you don't read them. The education system +in this country is just terrible, and we're not doing anything about it. + -- Ray Bradbury +~ +Each of us inevitable; each of us limitless-- +each of us with his or her right upon the earth. + -- Walt Whitman +~ +In science, the credit goes to the man who convinces the +world, not to the man to whom the idea first occurs. + -- Sir William Osler +~ +It's a funny thing about life; if you refuse to accept +anything but the best, you very often get it. + -- W. Somerset Maugham +~ +Never let the future disturb you. You will meet it, if you have to, +with the same weapons of reason which today arm you against the present. + -- Marcus Aurelius +~ +We have a natural opportunity to investigate the connections +of a problem when looking back at its solution. + -- George Polya +~ +Money teaches us to count, but science, inasmuch as it is +not governed by money, might yet teach us to think. + -- Christopher M. Kelty +~ +Painting, like flipping burgers or shearing sheep, is physical labor. It is +enough nowadays to declare yourself an artist and then to declare some artifact +in the vast world of found objects to be *your* work of art. + -- Thomas M. Disch +~ +The art of creation is older than the art of killing. -- Ed Koch +~ +In wisdom gathered over time I have found that +every experience is a form of exploration. + -- Ansel Adams +~ +Anyone who in discussion relies upon authority +uses not his understanding but his memory. + -- Leonardo Da Vinci +~ +We've had a taste of Siemens before in the past. -- Bonafide PHB +~ +The awareness of the ambiguity of one's highest achievements (as well +as one's deepest failures) is a definite symptom of maturity. + -- Paul Tillich +~ +A guilty conscience needs to confess. +A work of art is a confession. + -- Albert Camus +~ +When I examine myself and my methods of thought, +I come to the conclusion that the gift of fantasy +has meant more to me than my talent for absorbing +positive knowledge. + -- Albert Einstein +~ +A censor is a man who knows more than he thinks you ought to. + -- Laurence J. Peter +~ +No sensible decision can be made any longer without taking into +account not only the world as it is, but the world as it will be. + -- Isaac Asimov +~ +We have profoundly forgotten everywhere that cash-payment is not +the sole relation of human beings. + -- Thomas Carlyle +~ +No poet, no artist of any art, has his complete meaning alone. +His significance, his appreciation is the appreciation of his +relation to the dead poets and artists. + -- T. S. Eliot +~ +There's a moment coming. It's not here yet. It's still on the way. +It's in the future. It hasn't arrived. Here it comes. +Here it is... It's gone. + -- George Carlin +~ +Irresponsibility is part of the pleasure of all art; +it is the part the schools cannot recognize. + -- James Joyce +~ +It is easy to spot an informed man-- +his opinions are just like your own. + -- Miguel de Unamuno +~ +If you see in any given situation only what everybody else +can see, you can be said to be so much a representative of +your culture that you are a victim of it. + -- S. I. Hayakawa +~ +If you are going through hell, keep going. -- Winston Churchill +~ + The Founders of the American nation were one of the most creative +groups in modern history. Some among them, especially in recent years, +have been condemned for their failures and weaknesses--for their racism, +sexism, compromises, and violations of principle. And indeed moral +judgments are as necessary in assessing the lives of these people as of +any others. But we are privileged to know and to benefit from the outcome +of their efforts, which they could only hopefully imagine, and ignore their +main concern: which was the possibility, indeed the probability, that their +creative enterprise--not to recast the social order but to transform the +political system--would fail: would collapse into chaos or autocracy. +Again and again they were warned of the folly of defying the received +traditions, the sheer unlikelihood that they, obscure people on the outer +borderlands of European civilization, knew better than the established +authorities that ruled them; that they could successfully create something +freer, ultimately more enduring than what was then known in the centers of +metropolitan life. + Since we inherit and build on their achievements, we now know what the +established world of the eighteenth century flatly denied but which they +broke through convention to propose -- that absolute power need not be +indivisible but can be shared among states within a state and among +branches of government, and that the sharing of power and the balancing of +forces can create not anarchy but freedom. + We know for certain what they could only experimentally and prayerfully +propose--that formal, written constitutions, upheld by judicial bodies, can +effectively constrain the tyrannies of both executive force and populist +majorities. + We know, because they had the imagination to perceive it, that there +is a sense, mysterious as it may be, in which human rights can be seen to +exist independent of privileges, gifts, and donations of the powerful, and +that these rights can somehow be defined and protected by the force of law. + We casually assume, because they were somehow able to imagine, that +the exercise of power is no natural birthright but must be a gift of those +who are subject to it." + -- Bernard Bailyn, from "To Begin The World Anew: The Genius and + Ambiguities of the American Founders +~ +Discretion in speech is more than eloquence. -- Francis Bacon +~ +I fell asleep reading a dull book, and I dreamed that +I was reading on, so I awoke from sheer boredom. + -- Heinrich Heine +~ +I used to visit and revisit it a dozen times a day, and stand in deep +contemplation over my vegetable progeny with a love that nobody could +share or conceive of who had never taken part in the process of creation. +It was one of the most bewitching sights in the world to observe a hill +of beans thrusting aside the soil, or a rose of early peas just peeping +forth sufficiently to trace a line of delicate green. + -- Nathaniel Hawthorne +~ +Another cause of your sickness, and the most important: +you have forgotten what you are. + -- Boethius +~ +All writers are vain, selfish and lazy, and at the very bottom of +their motives there lies a mystery. Writing a book is a horrible, +exhausting struggle, like a long bout of some painful illness. +One would never undertake such a thing if one were not driven on +by some demon whom one can neither resist nor understand. + -- George Orwell +~ +When a man mistakes his thoughts for persons and things, he is mad. + -- Samuel Taylor Coleridge +~ +Did you ever see dishonest calluses on a man's hands? Hardly. +When a man's hands are callused and women's hands are worn, +you may be sure honesty is there. That's more than you can +say about many soft white hands. + -- Henry Ford +~ +The march of invention has clothed mankind with powers of which +a century ago the boldest imagination could not have dreamt. + -- Henry George +~ +The American invents +as the Greek chiseled, +as the Venetian painted, +as the modern Italian sings. + -- Willis M. West +~ +If it's mechanical, has tits, or wheels, it will give you trouble. + -- dano +~ +Life is a tragedy when seen in close-up, but a comedy in long-shot. + -- Charles Chaplin +~ +A small nose means a small penis. Small noses make for difficult breathing +and small penises make for difficulty breeding. + -- rando +~ +Executives are like joggers. If you stop a jogger, he goes on running +on the spot. If you drag an executive away from his business, he goes +on running on the spot, pawing the ground, talking business. He never +stops hurtling onwards, making decisions and executing them. + -- Jean Baudrillard +~ +Every man bears in himself the germs of every human quality; +but sometimes one quality manifests itself, sometimes another, +and the man often becomes unlike himself, +while still remaining the same man. + -- Leo Tolstoy +~ +You must trust and believe in people or life becomes impossible. + -- Anton Checkov +~ +If we don't change direction soon, we'll end up where we're going. + -- Professor Irwin Corey +~ +When you wish to instruct be brief--so that people's minds can +quickly grasp what you have to say, understand your point, and +retain it accurately. Unnecessary words just spill over the +side of a mind already crammed to the full. + -- Cicero +~ +I just don't understand why anyone is unable to realize what a disaster +a meeting is for a business. Never meet. If it can't be settled in a +five minute conversation, it's probably insoluble no matter how many +people talk about it for however long. + -- fred t. hamster +~ +Writing comes more easily if you have something to say. -- Sholem Asch +~ +We the unwilling, +led by the unknowing, +are doing the impossible +for the ungrateful. +We have done so much for so long with so little +We are now qualified to do anything with nothing. + -- Blue Collar Lament +~ +Endure, and save yourself for happier times. -- Virgil +~ +I don't think being funny is anyone's first choice. -- Woody Allen +~ +i thank You God for most this amazing day: for the leaping greenly +spirits of trees and a blue true dream of sky; and for everything +which is natural which is infinite which is yes + -- ee cummings +~ +None but myself ever did me any harm. I was, I may say, the only enemy to +myself: my own projects, that expedition to Moscow, and the accidents which +happened there, were the causes of my fall. I must say, though, that those +who failed to oppose me, who readily agreed with me, accepted all my views, +and yielded easily to my opinions, were those who did me the most injury, +and were my worst enemies, because, by surrendering to me so easily, they +encouraged me to go too far... I was then too powerful for any man, except +myself, to injure me. + -- Napoleon Bonaparte +~ +If a man can write a better book, preach a better sermon, or make a better +mousetrap than his neighbor, though he build his house in the woods, the +world will make a beaten path to his door. + -- Ralph Waldo Emerson, attributed by Sarah B. Yule, Borrowings, 1889 +~ +Nothing is at last sacred but the integrity of your own mind. + -- Ralph Waldo Emerson +~ +All conservatives are such from personal defects. + -- Ralph Waldo Emerson +~ +The reliance on Property, including the reliance on governments which +protect it, is the want of self-reliance. + -- Ralph Waldo Emerson +~ +We are symbols, and inhabit symbols. + -- Ralph Waldo Emerson +~ +The louder he talked of his honour, the faster we counted our spoons. + -- Ralph Waldo Emerson +~ +Fame is proof that people are gullible. + -- Ralph Waldo Emerson +~ +What is the hardest task in the world? To think.... + -- Ralph Waldo Emerson +~ +The greatest discovery of any generation is that a +human being can alter his life by altering his attitude. + -- William James +~ +Were we perfectly acquainted with the object, +we should never passionately desire it. + -- Francois De La Rochefoucauld +~ +The cat, having sat upon a hot stove lid, will not sit upon a hot +stove lid again. But he won't sit upon a cold stove lid, either. + -- Mark Twain +~ +There were two "Reigns of Terror", if we could but remember and consider it; +the one wrought murder in hot passions, the other in heartless cold blood; +the one lasted mere months, the other had lasted a thousand years; the one +inflicted death upon a thousand persons, the other upon a hundred million; +but our shudders are all for the horrors of the momentary Terror, so to +speak; whereas, what is the horror of swift death by the axe compared with +lifelong death from hunger, cold, insult, cruelty and heartbreak? A city +cemetery could contain the coffins filled by that brief terror that we have +all been so diligently taught to shiver at and mourn over; but all France +could hardly contain the coffins filled by that older and real Terror--that +unspeakable bitter and awful Terror which none of us has been taught to see +in its vastness or pity as it deserves. + -- Mark Twain +~ +You never know what is enough unless you know what is more than enough. + -- William Blake +~ +To be wholly overlooked, and to know it, are intolerable. -- John Adams +~ +In theory, there is no difference between theory and practice. +In practice, there is. + -- Yogi Berra +~ +The charm, one might say the genius, of memory is that it is choosy, +chancy and temperamental; it rejects the edifying cathedral and indelibly +photographs the small boy outside, chewing a hunk of melon in the dust. + -- Elizabeth Bowen +~ +Literature is the art of writing something that will be +read twice; journalism what will be read once. + -- Cyril Connolly +~ +It takes at least a couple of decades to realize that you were well taught. +All true education is a delayed-action bomb assembled in the classroom for +explosion at a later date. An educational fuse of 50 years long is by no +means unusual. + -- Kenneth D. Gangel +~ +When a thing ceases to be a subject of controversy, +it ceases to be a subject of interest. + -- William Hazlitt +~ +Men are generally idle, and ready to satisfy themselves, and intimidate the +industry of others, by calling that impossible which is only difficult. + -- Samuel Johnson +~ +An author is a fool who, not content with boring those +he lives with, insists on boring future generations. + -- Charles de Montesquieu +~ +Music is your own experience, your own thoughts, your wisdom. If you +don't live it, it won't come out of your horn. They teach you there's +a boundary line to music. But, man, there's no boundary line to art. + -- Charlie Parker +~ +If knowledge can create problems, it is not through +ignorance that we can solve them. + -- Isaac Asimov +~ +A wise man will make more opportunities than he finds. -- Sir Francis Bacon +~ +The range of what we think and do is limited by what we fail to notice. +And because we fail to notice that we fail to notice, there is little +we can do to change; until we notice how failing to notice shapes our +thoughts and deeds. + -- R. D. Laing +~ +People who are unhappy with the way things are tend to remain unhappy +even after they have changed them. The nature of their unhappiness +is such that change does not slake it. + -- Michael Lewis +~ +The bourgeoisie, by the rapid improvement of all instruments of +production, by the immensely facilitated means of communication, +draws all nations into civilization. + -- Karl Marx +~ +I learned one really sad fact from my career as a columnist: nobody changes +their mind about anything. Ever. Once we form the opinion, we become +evidence processors and we just collect all the evidence that supports our +opinion and reject all the evidence that disputes it. + -- Bob Metcalfe +~ +I keep the subject of my inquiry constantly before me, and wait till the first +dawning opens gradually, by little and little, into a full and clear light. + -- Isaac Newton +~ +The end of the road map is a cliff that both sides will fall off. + -- Secretary of State Colin L. Powell, on the Mideast peace effort, 2003. +~ +Once we admit that there is room for newness--that there are vastly more +conceivable possibilities than realized outcomes--we must confront the fact +that there is no special logic behind the world we inhabit, no particular +justification for why things are the way they are. Any number of arbitrary +small perturbations along the way could have made the world as we know it +turn out very differently. + -- Paul Romer +~ +Perpetual devotion to what a man calls his business, is only to be +sustained by perpetual neglect of many other things. + -- Robert Louis Stevenson +~ +All human beings should try to learn before they die +what they are running from, and to, and why. + -- James Thurber +~ +The wit makes fun of other persons; the satirist makes fun of the +world; the humorist makes fun of himself. + -- James Thurber +~ +All good books are alike in that they are truer than if they had really +happened and after you are finished reading one you will feel that all +that happened to you and afterwards it all belongs to you; the good and +the bad, the ecstasy, the remorse, and sorrow, the people and the places +and how the weather was. + -- Ernest Hemingway +~ +The true conquests, the only ones that leave no regret, +are those that have been wrested from ignorance. + -- Napoleon Bonaparte +~ +No love, no friendship can cross the path of our +destiny without leaving some mark on it forever. + -- Francois Mauriac +~ +Better beware of notions like genius and inspiration; they are a sort of magic +wand and should be used sparingly by anybody who wants to see things clearly. + -- Jose Ortega y Gasset +~ +A thick skin is a gift from God. -- Konrad Adenauer +~ +Far from idleness being the root of all evil, +it is rather the only true good. + -- Soren Kierkegaard +~ +It is the wretchedness of being rich that you have to live with rich people. + -- Logan Pearsall Smith +~ +The secret of joy is the mastery of pain. -- Anais Nin +~ +Everything is practice. -- Pele +~ +However much we guard ourselves against it, we tend to shape ourselves in +the image others have of us. It is not so much the example of others we +imitate, as the reflection of ourselves in their eyes and the echo of +ourselves in their words. + -- Eric Hoffer +~ +It is astonishing what you can do when you have +a lot of energy, ambition and plenty of ignorance. + -- Alfred P. Sloan Jr. +~ +The thing I hate about an argument is that it always interrupts a discussion. + -- G. K. Chesterton +~ +If there is no God, who pops up the next Kleenex? -- Art Hoppe +~ +For all these new and evolutionary facts, meanings, purposes, +new poetic messages, new forms and expressions, are inevitable. + -- Walt Whitman +~ +A man may fish with the worm that hath eat of a king, +and eat of the fish that hath fed of that worm. + -- Hamlet +~ +Only he is an artist who can make a riddle out of a solution. -- Karl Kraus +~ +Love demands infinitely less than friendship. -- George Jean Nathan +~ +People only see what they are prepared to see. -- Ralph Waldo Emerson +~ +We do not grow absolutely, chronologically. We grow sometimes in one +dimension, and not in another; unevenly. We grow partially. We are +relative. We are mature in one realm, childish in another. The past, +present, and future mingle and pull us backward, forward, or fix us in +the present. We are made up of layers, cells, constellations. + -- Anais Nin +~ +I hate house work! You make the beds, you do the dishes and six months +later you have to start all over again. + -- Joan Rivers +~ +The tree which moves some to tears of joy is in the eyes of others only +a green thing that stands in the way. Some see nature all ridicule and +deformity... and some scarce see nature at all. But to the eyes of the +man of imagination, nature is imagination itself. + -- William Blake +~ +If a man watches three football games in a row +he should be declared legally dead. + -- Erma Bombeck +~ +To see we must forget the name of the thing we are looking at. -- Claude Monet +~ +Perhaps a modern society can remain stable only by eliminating adolescence, +by giving its young, from the age of ten, the skills, responsibilities, and +rewards of grownups, and opportunities for action in all spheres of life. +Adolescence should be a time of useful action, while book learning and +scholarship should be a preoccupation of adults. + -- Eric Hoffer +~ +Being defeated appears to be an inexhaustible +wellspring of intellectual progress. + -- Reinhart Koselleck +~ +Any genuine teaching will result, if successful, in someone's knowing +how to bring about a better condition of things than existed earlier. + -- John Dewey +~ +George Orwell, on why someone might write a book... + + 1. Sheer egoism. Desire to seem clever, to be talked about, to be remembered +after death, to get your own back on grownups who snubbed you in childhood, +etc., etc. It is humbug to pretend that this is not a motive, and a strong +one. Writers share this characteristic with scientists, artists, politicians, +lawyers, soldiers, successful businessmen-in short, with the whole top crust +of humanity. The great mass of human beings are not acutely selfish. After +the age of about thirty they abandon individual ambition and live chiefly for +others or are simply smothered under drudgery. But there is also the minority +of gifted, willful people who are determined to live their own lives to the +end, and writers belong to this class. Serious writers, I should say, are on +the whole more vain and self-centered than journalists, though less interested +in money. + 2. Esthetic enthusiasm. Perception of beauty in the external world, or, on +the other hand, in words and their right arrangement. Pleasure in the impact +of one sound on another, in the firmness of good prose or the rhythm of a good +story. Desire to share an experience which one feels is valuable and ought not +to be missed. Above the level of a railway guide, no book is quite free from +esthetic considerations. + 3. Historical impulse. Desire to see things as they are, to find out true +facts and store them up for the use of posterity. + 4. Political purpose -- using the word "political" in the widest possible +sense. Desire to push the world in a certain direction, to alter other +people's idea of the kind of society that they should strive after. Once +again, no book is genuinely free from political bias. The opinion that art +should have nothing to do with politics is itself a political attitude. +~ +The only true exploration, the only real Fountain of Youth, will not be +in visiting foreign lands, but in having other eyes, in looking at the +universe through the eyes of others. + -- Marcel Proust +~ +They said it couldn't be done but sometimes it doesn't work out that way. + -- Casey Stengel +~ +To be wronged is nothing unless you continue to remember it. -- Cicero +~ +Nobody ever died of laughter. -- Max Beerbohm +~ +A thing worth having is a thing worth cheating for. -- W. C. Fields +~ +Women speak because they wish to speak, whereas a man speaks +only when driven to speech by something outside himself--like, +for instance, he can't find any clean socks. + -- Jean Kerr +~ +Winning is not a sometime thing; it's an all-time thing. You don't win +once in a while, you don't do things right once in a while, you do them +right all the time. Winning is habit. Unfortunately, so is losing. + -- Vince Lombardi +~ +It is by logic that we prove but by intuition that we discover. + -- Henri Poincare +~ +Our doubts are traitors, and make us lose the good we oft might win, +by fearing to attempt. + -- William Shakespeare, "Measure for Measure" +~ +Another unsettling element in modern art is that common symptom +of immaturity, the dread of doing what has been done before. + -- Edith Wharton +~ +Family quarrels are bitter things. They don't go according to any rules. +They're not like aches or wounds; they're more like splits in the skin +that won't heal because there's not enough material. + -- F. Scott Fitzgerald +~ +One looks back with appreciation to the brilliant teachers, but with +gratitude to those who touched our human feelings. The curriculum is +so much necessary raw material, but warmth is the vital element for +the growing plant and for the soul of the child. + -- Carl Jung +~ +Silence is one of the hardest arguments to refute. -- Josh Billings +~ +One can know a man from his laugh, and if you like a man's laugh before +you know anything of him, you may confidently say that he is a good man. + -- Fyodor Dostoevsky +~ +When I dare to be powerful, to use my strength in the service of my vision, +then it becomes less and less important whether I am afraid. + -- Aurdre Lorde +~ +Fascism should more properly be called corporatism, +since it is the merger of state and corporate power. + -- Benito Mussolini +~ +Take rest; a field that has rested gives a bountiful crop. -- Ovid +~ +There is hardly anything in the world that some men cannot make a little +bit worse and sell a little cheaper, and the people who consider price +only are this man's lawful prey. + -- John Ruskin +~ +The globe has been circumnavigated, but no man ever yet has; you may survey +a kingdom and note the results in maps, but all the savants in the world +could not produce a reliable map of the poorest human personality. + -- Alexander Smith +~ +Courage is the art of being the only one who knows you're scared to death. + -- Harold Wilson +~ +Do not let what you cannot do interfere with what you can do. + -- John Wooden +~ +One important key to success is self-confidence. +An important key to self-confidence is preparation. + -- Arthur Ashe +~ +If you believe that feeling bad or worrying long enough will +change a past or future event, then you are residing on another +planet with a different reality system. + -- William James +~ +As people grow up, they change brands. -- Al Ries +~ +If there is just one beam of sunshine coming into a room, +you can be sure that the cat is lazing in its heat. + -- Stuart and Linda MacFarlane +~ +If your parents never had children, chances are you won't, either. + -- Dick Cavett +~ +If the human race wishes to have prolonged and indefinite period +of material prosperity, they have only got to behave in a peaceful +and helpful way toward one another. + -- Winston Churchill +~ +All charming people have something to conceal, usually +their total dependence on the appreciation of others. + -- Cyril Connolly +~ +Part of the function of memory is to forget; the omni-retentive mind will break +down and produce at best an idiot savant who can recite a telephone book, and +at worst a person to whom every grudge and slight is as yesterday. + -- Christopher Hitchens +~ +Try to be one of the people on whom nothing gets lost. -- Henry James +~ +If a man hasn't discovered something that +he would die for, he isn't fit to live. + -- Martin Luther King Jr. +~ +The future is something which everyone reaches at the rate of +60 minutes an hour, whatever he does, whoever he is. + -- C. S. Lewis +~ +Most folks are about as happy as they make up their minds to be. + -- Abraham Lincoln +~ +I am happy to engage in discussion with those who accept that technology and +affluence are a net plus, but who worry about their troubling side effects. +Spare me, however, the sensitive souls who deplore technological advance and +economic growth over their cell phones on their way to the airport. + -- Charles Murray +~ +There are two golden rules for an orchestra: start together and finish +together. The public doesn't give a damn what goes on in between. + -- Sir Thomas Beecham +~ +The incompetent with nothing to do can still make a mess of it. + -- Laurence J. Peter +~ +The universe is full of magical things patiently +waiting for our wits to grow sharper. + -- Eden Phillpotts +~ +Imagine if every Thursday your shoes exploded if you tied them the +usual way. This happens to us all the time with computers, and +nobody thinks of complaining. + -- Jef Raskin +~ +My life needs editing. -- Mort Sahl +~ +Every day, in every way, I'm getting better and better. -- Emile Coue +~ +Everywhere I go I'm asked if I think the university stifles writers. My +opinion is that they don't stifle enough of them. There's many a best- +seller that could have been prevented by a good teacher. + -- Flannery O'Connor +~ +There is nothing so wrong in this world that a sensible +woman can't set it right in the course of an afternoon. + -- Jean Giraudoux +~ +There is no one, no matter how wise he is, who has not in his youth said +things or done things that are so unpleasant to recall in later life that +he would expunge them entirely from his memory if that were possible. + -- Marcel Proust +~ +Adults are obsolete children. -- Dr. Seuss +~ +Barbie would also be tired of Microsoft's licensing bullsh*t. + -- Maury Cesterino, Chief Software Architect, Mattel, Inc. +~ +When we drink, we get drunk. When we get drunk, we fall asleep. When we +fall asleep, we commit no sin. When we commit no sin, we go to heaven. +Sooooo, let's all get drunk and go to heaven! + -- Brian O'Rourke +~ +A really great talent finds its happiness in execution. + -- Johann Wolfgang von Goethe +~ +Allowing a schizophrenic in a cowboy costume to represent himself in a +death penalty case gives new meaning to the term "frontier justice." + -- Jim Marcus, executive director of the Texas Defender Service. +~ +Be still when you have nothing to say; when genuine passion moves +you, say what you've got to say, and say it hot. + -- D. H. Lawrence +~ +Creativity is not merely the innocent spontaneity of our youth +and childhood; it must also be married to the passion of the +adult human being, which is a passion to live beyond one's death. + -- Rollo May +~ +Our lives begin to end the day we become silent about things that matter. + -- Martin Luther King +~ +Life Shrinks or expands in proportion to one's courage. -- Anais Nin +~ +Freedom is the sure possession of those alone +who have the courage to defend it. + -- Pericles +~ +Psychoanalysis is that spiritual disease of +which it considers itself to be the cure. + -- Karl Kraus +~ +Teaching is arduous work, entailing much grinding detail and +boring repetition--a teacher, it has been said, never says +anything once--interrupted only occasionally by moments of +always surprising exultation. And I should like to add that +I don't think I learned a thing from my students, except that, +as one student evaluation informed me, I tend to jingle the +change in my pocket. + -- Joseph Epstein +~ +All humanity is passion; without passion, religion, history, +novels, art would be ineffectual. + -- Honore de Balzac +~ +We do not sit in such-and-such a way because a carpenter has built +a chair in such-and-such a way; rather, the carpenter makes the chair +as he does because someone wants to sit that way. + -- Adolf Loos +~ +No brilliance is required in law, just common sense +and relatively clean fingernails. + -- John Mortimer +~ +Difficulties strengthen the mind, as labor does the body. -- Seneca +~ +True friends stab you in the front. -- Oscar Wilde +~ +Chop your own wood, and it will warm you twice. -- Henry Ford +~ +Acting is the expression of a neurotic impulse. It's a bum's life. +The principal benefit acting has afforded me is the money to pay for +my psychoanalysis. + -- Marlon Brando +~ +Creativity is thinking up new things. Innovation is doing new things. + -- Theodore Levitt +~ +Life need not be easy provided only that it is not empty. -- Lise Meitner +~ +Some of us cannot be optimists, but all of us can be bigamists. + -- Mark Twain +~ +No more frisking as of old, +Or chasing his shadow over the lawn, +But a dignified old person, tickling +His nose against twig or flower in the border, +Until evening falls and bed-time's in order... +My cat and I grow old together. + -- A. L. Rowse +~ +Logic, like whiskey, loses its beneficial effect when taken +in too large quantities. + -- Lord Dunsany +~ +The first requisite for success is the ability to apply your physical and +mental energies to one problem incessantly without growing weary. + -- Thomas A. Edison +~ +Reading, after a certain age, diverts the mind too much from its creative +pursuits. Any man who reads too much and uses his own brain too little +falls into lazy habits of thinking. + -- Albert Einstein +~ +Engineering can be seen as a family of paths crossing a solution space--in +this case a space defined by all the possible arrangements and combinations +of geometry, time, and material properties that might satisfy the particular +specifications of a design. Filtering a good design out of these possibilities +by simple, direct calculation is impossible both because of the enormous number +of variables and because there are always elements in the specifications--like +aesthetics or ergonomics or compatibility with the corporate image--that can't +be reduced to a number or folded into a common denominator. What humans do in +these cases is: think up a completely wrong (but sincerely felt) approach to +the problem, jump in, fail, and then do an autopsy. Each failure contains +encrypted somewhere on its body directions for the next jump: "strengthen this +part," "tie this down next time," "buy a better battery." Good engineering is +not a matter of creativity or centering or grounding or inspiration or lateral +thinking, as useful as those might be, but of decoding the clever, even witty, +messages solution space carves on the corpses of the ideas in which you +believed with all your heart, and then building the road to the next message. + -- Fred Hapgood, from "Up the Infinite Corridor: MIT and Technical + Imagination" +~ +The hardest part of gaining any new idea is +sweeping out the false idea occupying that niche. + -- Robert Heinlein +~ +Humans are allergic to change. They love to say, "We've always done +it this way." I try to fight that. That's why I have a clock on my +wall that runs counter-clockwise. + -- Grace Hopper +~ +What we need is more people who specialize in the impossible. + -- Theodore Roethke +~ +A pessimist sees the difficulty in every opportunity; +an optimist sees the opportunity in every difficulty. + -- Winston Churchill +~ +I have always felt that everybody on earth goes about in disguise. + -- Sean O'Faolain +~ +At bottom, every man knows perfectly well that he is a unique being, +only once on this earth; and by no extraordinary chance will such a +marvelously picturesque piece of diversity in unity as he is, ever +be put together a second time. + -- Friedrich Nietzsche +~ +Contradiction is not a sign of falsity, nor the +lack of contradiction a sign of truth. + -- Blaise Pascal +~ +If you want something done, ask a busy person to do it. +The more things you do, the more you can do. + -- Lucille Ball +~ +The car as we know it is on the way out. To a large extent, I deplore +its passing, for as a basically old-fashioned machine, it enshrines a +basically old-fashioned idea: freedom. In terms of pollution, noise and +human life, the price of that freedom may be high, but perhaps the car, +by the very muddle and confusion it causes, may be holding back the +remorseless spread of the regimented, electronic society. + -- J. G. Ballard +~ +I live in company with a body, a silent companion, exacting and eternal. + -- Eugene Delacroix +~ +History never looks like history when you are living through it. + -- John W. Gardner +~ +In a cat's eyes round as golden bells, +The mad Spring's flame glows. +On a cat's gently closed lips, +The soft Spring's drowsiness lies. +On a cat's sharp whiskers, +The green Spring's life dances. + -- Jang-Hi Lee +~ +People always talk to me about my drinking; they never ask me about my thirst. + -- Oscar Levant +~ +Algebraic symbols are used when you do not know what you are talking about. + -- Philippe Shnoebelen +~ +We don't stop playing because we grow old; +we grow old because we stop playing. + -- George Bernard Shaw +~ +Competitions are for horse, not artist. -- Bela Bartok +~ +I feel that if a person has problems communicating +the very least he can do is to shut up. + -- Tom Lehrer +~ +It is, of course, totally pointless to call a cat when it is intent +on the chase. They are deaf to the interruptive nonsense of humans. +They are on cat business, totally serious and involved. + -- John D. MacDonald +~ +Look wise, say nothing, and grunt. Speech was given to conceal thought. + -- Sir William Osler +~ +The eye sees only what the mind is prepared to comprehend. + -- Robertson Davies +~ +Where do the words come from? The same mysterious place, I suspect, +where notes of music go. They precede ideas, and are inseparable +from them. For myself, I bow my head, touch wood, and utter a small +prayer that the flow of them never cease. + -- Joseph Epstein +~ +The only thing that I'd rather own than Windows is English, because then I +could charge you two hundred and forty-nine dollars for the right to speak it. + -- Scott McNealy, quoted at BrainyQuotes +~ +Only a monopolist could study a business and ruin it by giving away products. + -- Scott McNealy, quoted at ThinkExist.com +~ +Try moving off NT easily. You can move from Solaris to HP/UX to AIX or DEC +easily relative to moving off of NT, which is like a Roach Motel. Once you +check in, you never check out. + -- Scott McNealy, quoted at World of Quotes +~ +With Microsoft the first hit is always free--remember that all your life. + -- Scott McNealy, quoted at CNet +~ +I am convinced that if General Motors could eliminate [Microsoft] Office from +their entire company, they could get the 1999 cars out next year at half price. + -- Scott McNealy, quoted at Anti-Microsoft Association Web site +~ +We've got bayonets fixed, and we'll go into any cave no matter how dark and +dank it is. And in the air war [against Microsoft to win new developers], +we'll go after any developer and not just let them turn over to the dark side. + -- Scott McNealy, quoted at News.com +~ +Microsoft is now talking about the digital nervous system. +I guess I would be nervous if my system was built on their technology too. + -- Scott McNealy, November 4, 1998 - quoted at CNN.com +~ +Every time you turn on your new car, you're turning on 20 microprocessors. +Every time you use an ATM, you're using a computer. Every time I use a +settop box or game machine, I'm using a computer. The only computer you +don't know how to work is your Microsoft computer, right? + -- Scott McNealy, quoted at Anti-Microsoft Association Web site +~ +Slump? I ain't in no slump. I just ain't hittin'. -- Yogi Berra +~ +"I've seen things like this before," he told the daily Il Messaggero. +"Demons occupy a house and appear in electrical goods." + -- Gabriele Amorth, one of the Catholic Church's exorcists, quoted + in an article about Canneto di Caronia, a town where electronics + catch on fire. +~ +Nature is an unlimited broadcasting station, +through which God speaks to us every hour, +if we only will tune in. + -- George Washington Carver +~ +To accomplish great things, we must not only act, but also dream; +not only plan, but also believe. + -- Anatole France +~ +The first step to getting the things +you want out of life is this: +Decide what you want. + -- Ben Stein +~ +A scientist will never show any kindness for +a theory which he did not start himself. + -- Mark Twain, in "A Tramp Abroad" +~ +Grief upon griefs! +Disappointments upon disappointments. +What then? +This is a gay, merry world notwithstanding. + -- John Adams +~ +What is now proved was once imagined. -- William Blake +~ +We haven't had any tea for a week. +The bottom is out of the universe. + -- Rudyard Kipling +~ +If a man has no tea in him, he is incapable +of understanding truth and beauty. + -- Japanese Proverb +~ +Dogs eat. +Cats dine. + -- Ann Taylor +~ +Telephone, n.: An invention of the devil which abrogates some of the +advantages of making a disagreeable person keep his distance. + -- Ambrose Bierce, "The Devil's Dictionary" 1911 +~ +I count him braver who overcomes his desires than him who conquers +his enemies; for the hardest victory is the victory over self. + -- Aristotle +~ +Discretion of speech is more than eloquence; and to speak agreeably to him +with whom we deal is more than to speak in good words or in good order. + -- Francis Bacon +~ +Magnetism, as you recall from physics class, is a powerful force +that causes certain items to be attracted to refrigerators. + -- Dave Barry +~ +One sees great things from the valley, only small things from the peak. + -- G. K. Chesterton +~ +Encountering sufferings will definitely contribute to the elevation +of your spiritual practice, provided you are able to transform the +calamity and misfortune into the path. + -- H.H. the Dalai Lama, in "The Path to Tranquility: Daily Wisdom", + published by Snow Lion Publications +~ +In Buddhism, both learning and practice are extremely important, and they must +go hand in hand. Without knowledge, just to rely on faith, faith, and more +faith is good but not sufficient. So the intellectual part must definitely be +present. At the same time, strictly intellectual development without faith and +practice, is also of no use. It is necessary to combine knowledge born from +study with sincere practice in our daily lives. These two must go together. + -- H.H. the Dalai Lama, in "Answers: Discussions with Western + Buddhists", published by Snow Lion Publications +~ +The nature of beings is ever enlightened, +yet not realizing this, they wander endlessly in samsara. +May intense compassion arise within me +for sentient beings, whose suffering knows no bounds. + +In the moment of love, when the vibrant power of intense compassion +is uncontained, the empty essence shines forth nakedly. +May I never step off this supreme path of unity that never goes awry, +and practice it at all times, day and night. + -- "The Eighth Situpa on the Third Karmapa's Mahamudra Prayer", translated + by Lama Sherab Dorje, published by Snow Lion Publications +~ +The very ink with which all history is written is merely fluid prejudice. + -- Following the Equator +~ +Love is metaphysical gravity. -- R. Buckminster Fuller +~ +Whatever you do, or dream you can, begin it. +Boldness has genius and power and magic in it. + -- Johann Wolfgang von Goethe +~ +In accordance with the conditioning of desire, fear, disgust, and so forth-- +the conditioning of habitual tendencies that one has been accustomed to since +beginningless time--mind itself appears as body, enjoyments, abode, and so +forth. Yet, childish ordinary individuals do not comprehend that these +[appearances] are the identity of their own minds. Conceiving of mind as being +"here" and objects "over there," they hold the separation, the vast divide +between the apprehended and apprehender, to be established in actuality. This +is entirely imputation, or a deluded misapprehension of the way things are, as +when not knowing that the dream elephant is personal experience, but instead +apprehending it as an actual elephant in the external world. + -- "Speech of Delight: Mipham's Commentary on Shantarakshita's Ornament + of the Middle Way", translated by Thomas H. Doctor. +~ +Instead of prompting the appearance of delusions and/or hallucinations, +many of the patients receiving Valium displayed a progressive development +of dislikes and hates. The patients themselves deliberately used the term +"hate". This hatefulness first involved non-significant figures in the +patients' environment, progressed from there to the involvement of key +figures such as aides, nurses and physicians, and went on to the involvement +of important personal figures such as parents and spouses... This +hatefulness was of a peculiar type. The patients were unhappy with it; they +realized that it was unnatural and without basis, but were impotent to do +anything about it. Many of them, exhibiting real distress, would inquire, +"Why do I feel like this?" + -- P.E. Feldman, Journal of Neuropsychiatry +~ +The greatest book is not the one whose messages engraves itself on the brain, +but the one whose vital impact opens up other viewpoints, and from writer to +reader spreads the fire that is fed by various essences, until it becomes a +great conflagration. + -- Romain Rolland +~ +The world is a looking glass and gives back +to every man the reflection of his own face. + -- William M. Thackeray +~ +Travel is only glamorous in retrospect. -- Paul Theroux +~ +Patience and perseverance have a magical effect before which +difficulties disappear and obstacles vanish. + -- John Quincy Adams +~ +Beware the fury of a patient man. -- John Dryden +~ +I have always tried to hide my efforts and wished my works +to have a light joyousness of springtime which never lets +anyone suspect the labors it has cost me. + -- Henri Matisse +~ +You might find this discussion of attitudes threatening or insulting. No one +wants to be told they're practicing incorrectly. We don't want to hear that +we are uptight or repressed, that we are unskillful in dealing with our pain. +We just want to continue doing what we are doing. But there's a Tibetan saying +that the highest teaching is the one that reveals practice and any mistake you +might be making, it will only result in what you most truly desire--progress in +your spiritual development. Remember, I know of these mistakes because I've +made them all myself. Practice involves a radical transformation of our being, +and we have to learn to face and eventually to dissolve all the attitudes we +have about everything, not only meditation. So check yourself out. + -- Bruce Newman, in "A Beginner's Guide to Tibetan Buddhism: Notes from a + Practitioner's Journey" +~ +Nothing is better than the unintended humor of reality. -- Steve Allen +~ +At this point, a vote for Bush is a character flaw. -- Janeane Garofalo +~ +It is inexcusable for scientists to torture animals; let them make +their experiments on journalists and politicians. + -- Henrik Ibsen +~ +Heredity is what sets the parents of a teenager wondering about each other. + -- Laurence J. Peter +~ +Do what you can, with what you have, where you are. -- Theodore Roosevelt +~ +I have taken all knowledge to be my province. -- Sir Francis Bacon +~ +Genius is childhood recaptured at will. -- Charles Baudelaire +~ +Generally speaking, whenever we perceive things, our perception is deluded, in +that we project onto things a status of existence and a mode of being which is +simply not there. We exaggerate things, and the way they then appear falsely +to our minds gives rise to afflictive emotions. When we see our friends or +enemies, for instance, we superimpose on them a quality of desirability or +undesirability that is beyond the actual facts of the situation, and this +superimposition or exaggeration sparks off fluctuating states of emotion in +our mind. Towards our friends we feel strong attachment and desire, and +towards our enemies powerful anger and hatred. So if we are serious about +trying to purify our minds of these afflictive emotions, an understanding of +emptiness becomes crucial. + -- H.H. the Dalai Lama, in "Dzogchen: The Heart Essence of the Great + Perfection", Snow Lion Publications +~ +If you examine the nature of your own mind, you will realize that the +pollutants, such as afflictive emotions and thoughts rooted in a distorted +way of relating to the world, are actually unstable. No matter how powerful +an affliction, when you cultivate the antidote of true insight into the nature +of reality, it will vanish because of the power of the antidote, which +undermines its continuity. However, there is nothing that can undermine the +basic mind itself; nothing that can actually interrupt the continuity of +consciousness. The existence of the world of subjective experience and +consciousness is a natural fact. There is consciousness. There is mind. +There is no force that can bring about a cessation of your mental continuum. + -- His Holiness the 14th Dalai Lama, in "Illuminating the Path to + Enlightenment", published by Lama Yeshe Wisdom Archive +~ +When we habituate our minds to being fearless, to being brave and open towards +our emotions, fearlessness will also arise naturally. In order for this to +happen we must train in applying antidotes to our thought patterns that are +caught up in fear. In this way, we transcend fear first through a conceptual +process, which later becomes nonconceptual, a natural fearlessness. In order +to become fearless in this way, we need determination and the willingness to +face our emotions. With that strong determination and courage, fearlessness +will arise effortlessly. + -- from Trainings in Compassion: Manuals on the Meditation of + Avalokiteshvara, trans. by Tyler Dewar under the guidance of The + Dzogchen Ponlop Rinpoche, published by Snow Lion Publications +~ +The strongest element of growth lies in the human choice. -- George Eliot +~ +But again, I warn you my son: If you want to continue to be a devoted yogi, +generally you should never cling to dreams. If you do, you will eventually +expose yourself to the influence of the four maras. If your dreams are +positive, do not have any expectations. If we are filled with hopes and +expectations, even positive things can turn negative. If your dreams are +negative, don't take them too seriously. Learn to see negative dreams as +illusion, not real. Then, although a dream seems negative, because we realize +that it isn't real, it becomes a positive thing that prepares us for further +development and realization in the spiritual path. This is the practice of +a yogi. + -- "The Life of Gampopa", by Jampa Mackenzie Stewart, Snow Lion Publications +~ +In our time it is broadly true that political writing is bad writing. Where +it is not true, it will generally be found that the writer is some kind of + rebel, expressing his private opinions and not a "party line." Orthodoxy, +of whatever color, seems to demand a lifeless, imitative style. The political +dialects to be found in pamphlets, leading articles, manifestos, White Papers +and the speeches of under-secretaries do, of course, vary from party to party, +but they are all alike in that one almost never finds in them a fresh, vivid, +home-made turn of speech. When one watches some tired hack on the platform +mechanically repeating the familiar phrases--bestial atrocities, iron heel, +bloodstained tyranny, free peoples of the world, stand shoulder to shoulder-- +one often has a curious feeling that one is not watching a live human being +but some kind of dummy: a feeling which suddenly becomes stronger at moments +when the light catches the speaker's spectacles and turns them into blank +discs which seem to have no eyes behind them. And this is not altogether +fanciful. A speaker who uses that kind of phraseology has gone some distance +towards turning himself into a machine. The appropriate noises are coming out +of his larynx, but his brain is not involved, as it would be if he were +choosing his words for himself. If the speech he is making is one that he +is accustomed to make over and over again, he may be almost unconscious of +what he is saying, as one is when one utters the responses in church. And +this reduced state of consciousness, if not indispensable, is at any rate +favorable to political conformity. + -- George Orwell, "Politics and the English Language" +~ +An autobiography is only to be trusted when it reveals something disgraceful. +A man who gives a good account of himself is probably lying, since any life +when viewed from the inside is simply a series of defeats. + -- George Orwell +~ +Nothing is more honorable than a grateful heart. -- Seneca +~ +I am a devilish fellow, who has mastered many arts. -- August Strindberg +~ +Every real thought on every real subject +knocks the wind out of somebody or other. + -- Oliver Wendell Holmes +~ +Come, lovely cat, and rest upon my heart, +And let my gaze dive in the cold +Live pools of thine enchanted eyes that dart +Metallic rays of green and gold. + -- Charles Baudelaire +~ +A cat is a puzzle for which there is no solution. -- Hazel Nicholson +~ +As every cat owner knows, +nobody owns a cat. + -- Ellen Perry Berkeley +~ +One of the most important practices is that of tolerance, patience. Tolerance +can be learned only from an enemy; it cannot be learned from your guru. At +these lectures, for instance, you cannot learn tolerance, except perhaps when +you are bored! However, when you meet your enemy who is really going to hurt +you, then, at that moment you can learn tolerance. Shantideva makes a +beautiful argument; he says that one's enemy is actually a good spiritual +guide because in dependence upon an enemy one can cultivate patience, and in +dependence upon patience one accumulates great power of merit. Therefore, it +is as if an enemy were purposefully getting angry in order to help you +accumulate merit. + -- H.H. the Dalai Lama, in "The Dalai Lama at Harvard: Lectures on the + Buddhist Path to Peace", Snow Lion Publications +~ +In the intermediate stages of practice, you must be like a farmer during the +harvest. Once he has determined that it is time to reap his crop, he works +at it continuously, no matter what anyone tells him. Just as a farmer works +to make the most of the crop he has grown, we who now have opportunities and +conditions which are so valuable to our practice, should use them +immediately, understanding that there is no time to be wasted. + -- "The Life of Gampopa", by Jampa Mackenzie Stewart, Snow Lion Publications +~ +I'm growing old, I delight in the past. -- Henri Matisse +~ +Tyranny, like hell, is not easily conquered; yet we have this consolation +with us--that the harder the conflict, the more glorious the triumph. + -- Tom Paine +~ +Things equal out pretty well. +Our dreams seldom come true, +but then neither do our nightmares. + -- Charles Kennedy +~ +Inner development comes step by step. You may think "Today my inner calmness, +my mental peace is very small," but still, if you compare, if you look five, +ten, or fifteen years back, and think, "What was my way of thinking then? How +much inner peace did I have then and what is it today?", comparing it with what +it was then, you can realize that there is some progress, there is some value. +This is how you should compare--not with today's feeling and yesterday's +feeling, or last week or last month, even not last year, but five years ago. +Then you can realize what improvement has occurred internally. Progress comes +by maintaining constant effort in daily practice. + -- H.H. the Dalai Lama, in "Kindness, Clarity, and Insight", Snow Lion Pub. +~ +Men give me credit for some genius. All the genius I have lies in this; when +I have a subject in hand, I study it profoundly. Day and night it is before +me. My mind becomes pervaded with it. Then the effort that I have made is +what people are pleased to call the fruit of genius. It is the fruit of labor +and thought. + -- Alexander Hamilton +~ +Our society must make it right and possible for old people not to fear the +young or be deserted by them, for the test of a civilization is the way that +it cares for its helpless members. + -- Pearl S. Buck +~ +It is important for us to have a stable and peaceful mind, for it is mostly +through our mind that we experience suffering and problems. With diligence, +we can establish our minds in peace by abandoning the afflictions that create +obstacles. Meditation makes this possible because it establishes a steady +mind. Among the many types of meditation, calm abiding (shamatha) and deep +insight (vipashyana) are central to this process. In calm abiding, our mind +is focused inwardly, which allows us to suppress the afflictions so that they +do not actually manifest. There is a sense of distance between us and the +afflictions. It is not possible, however, to eradicate them with calm abiding +alone; deep insight is necessary to remove them at the root. + -- "Music in the Sky: The Life, Art & Teachings of the 17th Karmapa + Ogyen Trinley Dorje", by Michele Martin, Snow Lion Publications +~ +Learning from experience is a faculty almost never practiced. + -- Barbara Tuchman +~ + If you persevere in this practice of recognizing the state of natural +light, it will progressively become easier to repeat the lucid recognition +that you are dreaming. There will arise a steady awareness within the dream, +and you will know that you are dreaming. When you look in a mirror, you see +a reflection. Regardless of whether it is beautiful or ugly, you know that +it is a reflection. This is similar to knowing that a dream is a dream, to +being lucid. Whether the dream is tragic or ecstatic, you are aware that it +is merely a dream. + Awareness within the Dream State becomes a way to develop oneself and to +break one's heavy conditioning. With this awareness, one can manipulate the +dream material. For example, one can dream whatever one wishes, or one can +pick up a desired theme. One can continue dreaming from where one left off +on a previous occasion. + -- Chogyal Namkhai Norbu, in "Dream Yoga and the Practice of Natural Light" +~ +Responsibility educates. -- Wendell Phillips +~ +What is the first business of one who practices philosophy? To get rid +of self-conceit. For it is impossible for anyone to begin to learn that +which he thinks he already knows. + -- Epictetus +~ +There are two kinds of food--food for mental hunger and food for physical +hunger. Thus a combination of these two--material progress and spiritual +development is the most practical thing. I think that many Americans, +particularly young Americans, realize that material progress alone is not +the full answer for human life. Right now all of the Eastern nations are +trying to copy Western technology. We Easterners such as Tibetans, like +myself, look to Western technology feeling that once we develop material +progress, our people can reach some sort of permanent happiness. But when +I come to Europe or North America, I see that underneath the beautiful +surface there is still unhappiness, mental unrest, and restlessness. This +shows that material progress alone is not the full answer for human beings. + -- H.H. the Dalai Lama, in "The Dalai Lama: A Policy of Kindness", Snow Lion +~ +Nobody sees a flower--really--it is so small it takes time--we haven't +time--and to see takes time, like to have a friend takes time. + -- Georgia O'Keeffe +~ +The eighth root downfall is to regard our physical bodies, or the skandhas or +aggregates of our psycho-physical makeup, as impure and base. The reason why +this is a root downfall is because Vajrayana sees everything as sacred. All +appearances is a form of divinity, all sound is the sound of mantra, and all +thought and awareness is the divine play of the transcending awareness, the +Mahamudra experience. The potential for that sacredness exists within our +present framework, so to speak, of the five skandhas. Acknowledging psycho- +physical aggregates of an individual as the potential of the Buddhas of the +five families, or the five elements, or the five feminine aspects, and so +forth, is to recognize that, in tantra, the potential for the transformation +exists within our present situation. To disparage that potential as something +useless or impure or unwholesome is a root downfall, a basic contradiction, +from the point of view of tantric practice. + -- H.E. Kalu Rinpoche, in "Foundations of Tibetan Buddhism", Snow Lion Pub. +~ +Terrorists and totalitarians have always been two sides of one coin; +a totalitarian out of office is a terrorist. + -- David Gelernter +~ +Now that as humans we have met with spiritual teachings and have met a teacher, +we should not be like a beggar doing nothing meaningful year after year, ending +up empty-handed at death. I, an ordinary monk in the lineage of Buddha +Shakyamuni, humbly urge you to make efforts in spiritual practice. Examine the +nature of your mind and cultivate its development. Take into account your +welfare in this and future existences, and develop competence in the methods +that produce happiness here and hereafter. Our lives are impermanent and so +are the holy teachings. We should cultivate our practice carefully. + -- H.H. the Dalai Lama, in "The Path to Enlightenment", Snow Lion + Publications +~ +Time makes more converts than reason. -- Tom Paine +~ +We operate under a jury system in this country, and as much as +we complain about it, we have to admit that we know of no better +system, except possibly flipping a coin. + -- Dave Barry +~ +To be dead is to stop believing in +The masterpieces we will begin tomorrow. + -- Patrick Kavanagh, Irish poet +~ +The obvious is always least understood. -- Klemens von Metternich +~ +We are decent 99 percent of the time, when we could easily be vile. + -- R. W. Riis +~ +The whole world loves a maverick, but the whole world wants +the maverick to achieve something nobler than simple rebellion. + -- Kevin Patterson +~ + Insofar as the destructive effects of anger and hateful thoughts are +concerned, one cannot get protection from wealth; even if one is a +millionaire, one is subject to these destructive effects of anger and +hatred. Nor can education guarantee that one will be protected from +these effects. Similarly, the law cannot guarantee protection. Even +nuclear weapons, no matter how sophisticated the defense system may be, +cannot give one protection or defend one from these effects. + The only factor that can give refuge or protection from the destructive +effects of anger and hatred is the practice of tolerance and patience. + -- H.H. the Dalai Lama, in "Healing Anger: The Power of Patience from a + Buddhist Perspective", Snow Lion Publications +~ +Theoretically it may be comfortable to have compassion for "all sentient +beings," but through our practice we realize that "all sentient beings" is +a collection of individuals. When we actually try to generate compassion +for each and every individual, it becomes much more challenging. But if we +cannot work with one individual, then how can we work with all sentient +beings? Therefore it is important for us to reflect more practically, to +work with compassion for individuals and then extend that compassion further. + -- "Trainings in Compassion: Manuals on the Meditation of Avalokiteshvara", + translated by Tyler Dewar under the guidance of The Dzogchen Ponlop + Rinpoche, published by Snow Lion Publications +~ +The greatest challenge to any thinker is stating +the problem in a way that will allow a solution. + -- Bertrand Russell +~ +I never wonder to see men wicked, but I often wonder to see them not ashamed. + -- Jonathan Swift +~ +The worst, the hardest, the most disagreeable thing that you may have +to do may be the thing that counts most, because it is the hard discipline, +and it alone, that makes possible the highest efficiency. + -- Elihu Root +~ +I wake to sleep, and take my waking slow. +I learn by going where I have to go. + -- Theodore Roethke +~ +Don't let yesterday use up too much of today. -- Cherokee proverb +~ +Tea is drunk to forget the din of the world. -- T'ien Yiheng +~ +Women are like tea bags. They don't know how strong +they are until they get into hot water. + -- Eleanor Roosevelt +~ +There are two appropriate methods of mahamudra meditation to give rise to +the primordial awareness of the dharmadhatu: looking while the mind is +resting and looking while the mind is moving. The approach to the first +method is the meditation of calm abiding. One lets one's mind rest until +it abides calmly, and then with precision one looks at it. One looks for +how it rests, for where it abides, and whoever or whatever it is that +abides there. This is looking at the true nature of the mind while the +mind is resting. + -- Khenchen Thrangu Rinpoche, from "Everyday Consciousness and + Buddha-awakening", Snow Lion Publications +~ +It is better to debate a question without settling it +than to settle a question without debating it. + -- Joseph Joubert +~ +What is the purpose of the Dharma? Just like other spiritual traditions, +Buddhadharma is an instrument for training the mind--something we use to try +to work out the problems that we all experience; problems that originate +mainly at the mental level. Negative emotional forces create mental unrest, +such as unhappiness, fear, doubt, frustration and so forth; these negative +mental states then cause us to engage in negative activities, which in turn +bring us more problems and more suffering. Practicing Dharma is a way of +working out these problems, be they long-term or immediate. In other words, +Dharma protects us from unwanted suffering. + -- H.H. the Dalai Lama, "Illuminating the Path to Enlightenment", Snow Lion. +~ +Your true traveler finds boredom rather agreeable than painful. It is the +symbol of his liberty--his excessive freedom. He accepts his boredom, when +it comes, not merely philosophically, but almost with pleasure. + -- Aldous Huxley +~ +Everything in the world has a hidden meaning... Men, animals, trees, stars, +they are all hieroglyphics. When you see them you do not understand them. +You think they are really men, animals, trees, stars. It is only years +later that you understand. + -- Nikos Kazantzakis, from "Zorba the Greek" +~ +One has to belong to the intelligentsia to believe things +like that: no ordinary man could be such a fool. + -- George Orwell +~ +A thing long expected takes the form of the unexpected when at last it comes. + -- Mark Twain +~ +Life is what happens to you while you're busy making other plans. + -- John Lennon +~ + For a romantic relationship to survive, more than romantic love is needed. +We need to love the other person as a human being and as a friend. The sexual +attraction that feeds romantic love is an insufficient basis on which to +establish a long-term relationship. Deeper care and affection, as well as +responsibility and trust, must be cultivated. + In addition, we do not fully understand ourselves and are a mystery to +ourselves. Needless to say, other people are even more of a mystery to us. +Therefore, we should never presuppose, with a bored attitude that craves +excitement, that we know everything about our partner because we have been +together so long. If we have the awareness of the other person being a +mystery, we will continue to pay attention and be interested in him or her. +Such interest is one key to a long-lasting relationship. + -- Thubten Chodron, from "Buddhism for Beginners", Snow Lion Pub. +~ +Two cats can live as cheaply as one, and their owner has twice as much fun. + -- Lloyd Alexander +~ +Now here, you see, it takes all the running you can do, to keep in the +same place. If you want to get somewhere else, you must run at least +twice as fast as that! + -- Lewis Carroll +~ +Art is a kind of innate drive that seizes a human being and makes him its +instrument. To perform this difficult office it is sometimes necessary +for him to sacrifice happiness and everything that makes life worth living +for the ordinary human being. + -- Carl Jung +~ +Vision without action is a daydream; +action without vision is a nightmare. + -- Japanese proverb +~ +To satisfy a cat, a new state of being needs to be +created--halfway between in and out. + -- Stuart and Linda MacFarlane +~ +The Perfection of Zeal + 1. Thus, one who has patience should cultivate zeal, because Awakening is +established with zeal, and there is no merit without zeal, just as there is no +movement without wind. + 2. What is zeal? It is enthusiasm for virtue. What is said to be its +antithesis? It is spiritual sloth, clinging to the reprehensible, apathy, and +self-contempt. + 3. Spiritual sloth arises from indolence, indulging in pleasures, sleep, +and craving for lounging around due to one's apathy toward the miseries of the +cycle of existence. + 4. Scented out by the hunters, the mental afflictions, you have entered the +snare of rebirth. Why do you not recognize even now that you are in the mouth +of death? + -- Shantideva, in "A Guide to the Bodhisattva Way of Life", trans. from the + Sanskrit and Tibetan by Vesna A. Wallace and B. Alan Wallace, published by + Snow Lion Publications +~ +I have a perfect horror of words that are not backed up by deeds. + -- Theodore Roosevelt +~ +Sufferings arise from specific causes and conditions, which are collected by +individual sentient beings. That being so, it is extremely important that +individual sentient beings know what is to be practiced and what is to be +given up--what brings suffering and what brings long-lasting happiness. We +must show sentient beings the right path, which brings happiness and the wrong +path, which brings suffering. Therefore, when we talk about benefiting other +sentient beings, it is through showing them the path and helping them +understand what is to be given up and what is to be practiced. This is how +we can help other sentient beings. + -- H.H. the Dalai Lama in "Stages of Meditation", Snow Lion Publications +~ +We make our lives miserable by being miserable, so why not do exactly the +opposite, and make our lives happy, joyful, and harmonious, by being happy, +joyful, and harmonious? We create our own lives and yet we think that +something else is doing it. All we have to do is change our mental +reactions towards the opposite direction. And the way to do that is to +meditate, otherwise we won't have the strength of mind to do it. A mind +that can meditate is a mind that is one-pointed. And a mind that is one- +pointed, the Buddha said, is like an ax that has been sharpened. It has a +sharp edge that can cut through everything. If we want to remove stress +and strain, and have a different quality of life, we have every opportunity. +We need to strengthen our mind to the point where it will not suffer from +the things which exist in the world. + -- from "Buddhism Through American Women's Eyes", edited by Karma Lekshe + Tsomo, published by Snow Lion Publications +~ +I must study politics and war that my sons may have liberty to study +mathematics and philosophy. My sons ought to study mathematics and +philosophy, geography, natural history, naval architecture, navigation, +commerce and agriculture in order to give their children a right to study +painting, poetry, music, architecture, statuary, tapestry, and porcelain. + -- John Adams +~ +There is no kind of dishonesty into which otherwise good people more +easily and frequently fall than that of defrauding the government. + -- Benjamin Franklin +~ +Thank God I have the seeing eye, that is to say, as I lie in bed I can walk +step by step on the fells and rough land seeing every stone and flower and +patch of bog and cotton pass where my old legs will never take me again. + -- Beatrix Potter +~ +Umpire's heaven is a place where he works third base every game. +Home is where the heartache is. + -- Ron Luciano +~ +There exist no phenomena other than what arises from the mind. +Other than the meditation that occurs, where is the one who is meditating? +There exist no phenomena other than what arises from the mind. +Other than the behavior that occurs, where is the one who is behaving? +There exist no phenomena other than what arises from the mind. +Other than the samaya vow that occurs, where is the one who is guarding it? +There exist no phenomena other than what arises from the mind. +Other than the fruition that occurs, where is the one who is realizing it? +You should look at your own mind, observing it again and again. + -- from "Self-Liberation Through Seeing with Naked Awareness", translation + and commentary by John Myrdhin Reynolds, published by Snow Lion Pub. +~ +You must understand that I'm just as interested in someone I've +known for ten minutes as in someone I've known for ten years. + -- Alberto Giacometti +~ +Now in our day-to-day lives we know that the more stable, calm and contented +our mind is, the more feelings and experiences of happiness we will derive +from it. The more undisciplined, untrained, and negative our mind is, the +more we suffer mentally, and physically as well. So we can see only too well +that a disciplined and contented mind is the source of our happiness. + -- H.H. the Dalai Lama, from "Dzogchen: The Heart Essence of the Great + Perfection", published by Snow Lion Publications +~ +If we do not wish merely to know intellectually about the view of emptiness, +but rather wish to experience it ourselves in our own continuum, we should +build a firm foundation for this. Then, according to our mental ability we +should hear and consider both the sutras and treatises which teach the profound +view of emptiness as well as the good explanations of them by the experienced +Tibetan scholars in their commentaries. Together with this, we should learn +to make our own ways of generating experience of emptiness accord with the +precepts of an experienced wise man. + -- H.H. the Dalai Lama in "Buddhism of Tibet", published by Snow Lion Pub. +~ +A great deal of our suffering comes from having too many thoughts. And, at +the same time, the way we think is not sane. We are only concerned by our +immediate satisfaction and forget to measure its long-term advantages and +disadvantages, either for ourselves or for others. But such an attitude +always goes against us in the end. There is no doubt that by changing our +way of seeing things we could reduce our current difficulties and avoid +creating new ones. + -- H.H. the Dalai Lama +~ +If, motivated by the attainment of fame and gain for this life and hoping to +look good in the eyes of others, one's behavior appears to be temporarily +beautiful and one appears to be diligent in moral conduct, hearing, and +contemplation, then one should think, "What is the use of appearing good in the +eyes of others when my practice does not counteract the afflictions? When my +practice does counteract the afflictions then even if it is not beautiful, +what have I got to lose? + -- Ngorchen Konchog Lhundrub, from "Three Visions: Fundamental Teachings of + the Sakya Lineage of Tibetan Buddhism", published by Snow Lion Pub. +~ +Don't waste life in doubts and fears; spend yourself on the work before you, +well assured that the right performance of this hour's duties will be the best +preparation for the hours and ages that will follow it. + -- Ralph Waldo Emerson +~ +If you can't describe what you are doing as a process, +you don't know what you're doing. + -- W. Edwards Deming +~ +Our Dharma practice is the best offering to make to our teachers. If we have +material possessions, talents, and time, we can offer those. However, we don't +neglect our practice, for that is what our teacher cares about most. When we +follow the Dharma instructions we've received and keep whatever precepts we've +taken, that pleases our teacher more than anything else. + -- Thubten Chodron, in "Taming the Mind", Snow Lion Pub. +~ +If organized religion is the opium of the masses, then +disorganized religion is the marijuana of the lunatic fringe. + -- Principia Discordia +~ +When you are proclaiming peace with your lips, +be careful to have it even more fully in your heart. + -- St. Francis of Assisi +~ +Nationalism is an infantile disease. It is the measles of mankind. + -- Albert Einstein, "The World as I See It", 1934. +~ +It was, of course, a lie what you read about my religious convictions, a lie +which is being systematically repeated. If something is in me which can be +called religious then it is the unbounded admiration for the structure of the +world so far as our science can reveal it. + -- Albert Einstein, in "Albert Einstein : The Human Side", from a 1954 + letter to an atheist. +~ +This topic brings me to that worst outcrop of the herd nature, the military +system, which I abhor. That a man can take pleasure in marching in formation +to the strains of a band is enough to make me despise him. He has only been +given his big brain by mistake; a spinal cord was all he needed. + -- Albert Einstein, "The World as I See It", 1934. +~ +I'm not a teacher, but an awakener. -- Robert Frost +~ +It is very important to understand the context of the Buddhist emphasis on +recognizing that we are all in a state of suffering, otherwise there is a +danger we could misunderstand the Buddhist outlook, and think that it involves +rather morbid thinking, a basic pessimism and almost an obsessiveness about the +reality of suffering. The reason why Buddha laid so much emphasis on +developing insight into the nature of suffering is because there is an +alternative--there is a way out, it is actually possible to free oneself from +it. This is why it is so crucial to realize the nature of suffering, because +the stronger and deeper your insight into suffering is, the stronger your +aspiration to gain freedom from it becomes. So the Buddhist emphasis on the +nature of suffering should be seen within this wider perspective, where there +is an appreciation of the possibility of complete freedom from suffering. If +we had no concept of liberation, then to spend so much time reflecting on +suffering would be utterly pointless. + -- H.H. the Dalai Lama, "The Dalai Lama's Book of Awakening", Snow Lion Pub. +~ +A cat is a demure animal; it will not come into the living room +wagging its tail and knocking over lamps and tables. + -- H. Monger Burdock +~ +There is no need for a piece of sculpture in a home that has a cat. + -- Wesley Bates +~ +Lack of understanding of the true nature of happiness, it seems to me, is the +principal reason why people inflict sufferings on others. They think either +that the other's pain may somehow be a cause of happiness for themselves or +that their own happiness is more important, regardless of what pain it may +cause. But this is shortsighted, no one truly benefits from causing harm to +another sentient being. Whatever immediate advantage is gained at the expense +of someone else is short-lived. In the long run causing others misery and +infringing their rights to peace and happiness result in anxiety, fear and +suspicion within one-self. Such feelings undermine the peace of mind and +contentment which are the marks of happiness. + -- H.H. the Dalai Lama in "The Dalai Lama: A Policy of Kindness", Snow Lion +~ +When we're told that things are mere deceptive appearances, we shouldn't +misinterpret it as license to act in any way we please. Of course, none of +us would be so silly! Appearances are extremely powerful. You dream your +house is on fire and you're surrounded by flames. You can't escape and feel +terrified. You wake up soaked in sweat and screaming. What a relief! It was +just a nightmare and none of it actually happened, but while it all seemed to +be happening, you were in anguish. + -- Geshe Sonam Rinchen, in "The Thirty-seven Practices of Bodhisattvas", + published by Snow Lion Publications +~ +A man is not old until his regrets take the place of dreams. + -- John Barrymore +~ +I am independent! I can live alone and I love to work. -- Mary Cassatt +~ +If one ever wishes to retain one's fantasies about the good sense +of the people in the realm of literary taste, one does best never +to consult the bestseller lists. + -- Joseph Epstein +~ +The most exhausting thing in life is being insincere. -- Anne Morrow Lindbergh +~ +Speaking of others' faults can also be a way to distract ourselves from +acknowledging our own painful emotions. For example, if we feel hurt or +rejected because a dear one hasn't called us in a long time, rather than +feel the suffering nature of our attachment, we criticize our loved one +for being unreliable and inconsiderate. + -- Thubten Chodron, in "Taming the Mind", Snow Lion Publications +~ +A consistent man believes in destiny, a capricious man in chance. + -- Benjamin Disraeli +~ +The things that we love tell us what we are. -- St. Thomas Aquinas +~ +Everything on this planet functions according to the law of nature. Particles +come together, and on the basis of their co-operation everything around us, our +whole environment, can develop and be sustained. Our own body too has the same +structure. Different cells come together and work together in co-operation, +and as a result, human life is sustained. In a human community the same law +and principle of co-operation applies. Even for an aeroplane to fly or for a +single machine to work, it can only do so by depending on many other factors, +and with their co-operation. Without them it is impossible. Just so, to +sustain everyday life in human society we need co-operation. + -- H.H. the Dalai Lama in "Dzogchen: Heart Essence of the Great Pefection" +~ +We must go beyond textbooks, go out into the byways and +untrodden depths of the wilderness, and travel and explore +and tell the world the glories of our journey. + -- John Hope Franklin +~ +Great works are performed not by strength, but by perseverance. + -- Samuel Johnson +~ +Be happy. Talk happiness. Happiness calls out responsive gladness in +others. There is enough sadness in the world without yours. + -- Helen Keller +~ +There are two ways to slide easily through life: +to believe everything or to doubt everything; +both ways save us from thinking. + -- Alfred Korzybski +~ +We shall not cease from exploration. And at the end of our exploring will be +to arrive where we started and to know the place for the first time. + -- T.S. Eliot +~ +I conceive that pleasures are to be avoided if greater pains +be the consequence, and pains to be coveted that will terminate +in greater pleasures. + -- Michel de Montaigne +~ +A plot, if there is to be one, must be a secret. A secret that, if we only +knew it, would dispel our frustration, lead us to salvation; or else the +knowing of it in itself would be salvation. Does such a luminous secret +exist? Yes, provided it is never known. Known, it will only disappoint us. + -- Umberto Eco +~ +The subjects that were dearest to the examiners were almost invariably those I +fancied least... I should have liked to be asked to say what I knew. They +always tried to ask what I did not know. When I would have willingly displayed +my knowledge, they sought to expose my ignorance. This sort of treatment had +only one result: I did not too well in examinations. + -- Winston Churchill +~ +One of the great movements in my lifetime among educated people is the need to +commit themselves to action. Most people are not satisfied with giving money; +we also feel we need to work. + -- Peter Drucker +~ +One should really use the camera as though tomorrow you'd be stricken blind. + -- Dorothea Lange +~ +The universe is made of stories, not of atoms. -- Muriel Rukeyser +~ +Chemists are a strange class of mortals, impelled by an almost maniacal impulse +to seek their pleasures amongst smoke and vapour, soot and flames, poisons and +poverty, yet amongst all these evils I seem to live so sweetly that I would +rather die than change places with the King of Persia. + -- Johann Joachim Becher +~ +Cherishing children is the mark of a civilized society. -- Joan Ganz Cooney +~ +Too many pieces of music finish too long after the end. -- Igor Stravinsky +~ +Purifying the unwholesome: In the past we have engaged in unwholesome physical, +verbal, and mental actions. All these actions are of the past and cannot be +touched; we cannot reach back and wipe them away. They have left detrimental +imprints on our mind-stream that obstruct our spiritual practice and manifest +as suffering when they come to full maturation, giving rise to misfortune and +grief. However, these imprints can be affected, and even purified, by our +present behavior. They are like seeds carried along in the current of our +mind-stream. We cannot annihilate them without a trace, but we can burn them +so that they have little or no potency to cause damaging results. + -- B. Alan Wallace, in "The Seven-Point Mind Training", Snow Lion Pub. +~ +First it is important to recognize the human form as rare and precious. It is +not enough just to obtain this precious human form which has great potential; +rather, you should use that potential to its fullest extent by taking its +essence. For example, if a person's ascent to high office is not followed by +good work for the community and people, it is not very beneficial and +worthwhile. If, on the basis of full use of the potential, one is able to +accomplish great feats, that would truly be a great success. Therefore, it is +important initially to recognize all the significance and great potential of +this human existence. + -- H.H. the Dalai Lama in "The Path to Bliss", Snow Lion Publications +~ +We haven't had any tea for a week. +The bottom is out of the universe. + -- Rudyard Kipling +~ +Does one's integrity ever lie in what he is not able to do? I think that +usually it does, for free will does not mean one will, but many wills +conflicting in one man, Freedom cannot be conceived simply. + -- Flannery O'Connor, in "Wise Blood", 1952 +~ +We may seek a fortune for no greater reason than to secure the respect +and attention of people who would otherwise look straight through us. + -- Alain de Botton +~ +Only in the most unusual cases is it useful to determine whether +a book is good or bad; for it is just as rare for it to be one or +the other. It is usually both. + -- Robert Musil +~ +A lie gets halfway around the world before +the truth has a chance to get its pants on. + -- Winston Churchill +~ +Like yourself, everyone else from their own side equally does not want +suffering and equally wants happiness. For example, among ten ill people, +each of them just wants happiness; from their side they are all ill, and +they all want to be freed from their illness. Hence there is no possible +reason for making a biased exception, treating a certain one better and +neglecting the others. It is impossible to select one out for better +treatment. Moreover, from your own viewpoint, all sentient beings, in +terms of their connection with you over the course of lifetimes, have in +the past helped you and in the future will help again. Thus, you also +cannot find any reason from your own side to treat some better and others +worse. + -- H.H. the Dalai Lama, in "The Dalai Lama at Harvard: Lectures on the + Buddhist Path to Peace", Snow Lion Publications +~ +In everything that can be called art there is a quality of redemption. + -- Raymond Chandler +~ +Life is not a spectacle or a feast; it is a predicament. -- George Santayana +~ +Mediocre men often have the most acquired knowledge. -- Claude Bernard +~ +When we practice, initially, as a basis we control ourselves, stopping the +bad actions which hurt others as much as we can. This is defensive. After +that, when we develop certain qualifications, then as an active goal we +should help others. In the first stage, sometimes we need isolation while +pursuing our own inner development; however, after you have some confidence, +some strength, you must remain with, contact, and serve society in any +field--health, education, politics, or whatever. + -- H.H. the Dalai Lama in "Kindness, Clarity and Insight", Snow Lion Pub. +~ +Other paths that are aimed at "sudden awakening" lead one on an unmapped +journey that may offer no clear indications of progress. In contrast, in this +practice we have definite sign-posts along the way. Look at your mental +distortions and see how they are doing. After practicing for a month, a year, +six years, are the mental distortions somewhat diminished? Do wholesome +qualities arise more readily, more frequently, more deeply? At the very root +of the mental distortions, is the self-grasping attenuated? Is there less +self-centeredness and greater humility? Is there more loving concern for the +welfare of others? All of these are causes that lead either to well-being or +to misery. + -- B. Alan Wallace, in "The Seven-Point Mind Training", Snow Lion Pub. +~ +In the jungle of samsara arisings are ceaseless, there is never any let up in +the interplay between past, present and future and in the turbulent interchange +between inner thoughts and outer events. 'I', 'me' and 'mine' are tossed about +like corks in the waves. There is no exit from this on the level of 'I'. +There is no way to think yourself out of samsara. Even the thoughts that +everything is empty, or is Padmasambhava, or pure from the very beginning do +not help for no thought can unlock the door to awakening. The key is not +shaped like a thing; it is not a thought, a feeling or a sensation. It's not +something outer or inner. The key is your own nature, how you have been from +the very beginning, simple, raw, naked, uncontrived, free of all constructs. + -- Nuden Dorje, in "Being Right Here: A Dzogchen Treasure Text", Snow Lion +~ +Great ideas originate in the muscles. -- Thomas A. Edison +~ +If you tell the truth you don't have to remember anything. -- Mark Twain +~ +Mathematics is a game played according to certain +simple rules with meaningless marks on paper. + -- David Hilbert +~ +When you are poor enough, everything has some value. -- Barbara Ann Porte +~ +I do not know what I may appear to the world, but to myself I seem to have +been only a boy playing on the sea-shore, and diverting myself in now and +then finding a smoother pebble or a prettier shell than ordinary, whilst +the great ocean of truth lay all undiscovered before me. + -- Isaac Newton +~ +In ancient times, a candidate for initiation into the Mysteries was led through +a series of dark chambers, virtually a labyrinth, beset by terrifying sounds +and ominous presences. And then in the adytum, the final chamber, there was a +sudden illumination. The enthroned hierophant tells the candidate, "Behold the +light, my child! It is your own being and nature." Just this epopteia, or +sudden illumination, is the introduction to the Clear Light that is one's own +original nature. The course of the initiation in the ancient Mystery Religions +simulates the experience of death and rebirth and leads the candidate into what +lies beyond, so that one no longer need fear death. This initiatory process +may be compared to the Tibetan Book of the Dead. + -- John Myrdhin Reynolds, in "The Golden Letters", Snow Lion Pub. +~ +If only I had the theorems! Then I should find the proofs easily enough. + -- Bernhard Riemann +~ +Books are the carriers of civilization. Without books, history is silent, +literature dumb, science crippled, thought and speculation at a standstill. + -- Barbara Tuchman +~ +But in a larger sense we cannot dedicate, we cannot consecrate, we cannot +hallow this ground. The brave men, living and dead, who struggled here, +have consecrated it far above our poor power to add or detract. + -- Abraham Lincoln +~ +A memory is more atmospheric than accurate, more an evolving fiction than a +sacred text. And thank heavens. If rude, shameful, or brutal memories +can't be expunged, they can at least be diluted. So is nothing permanent +and fixed in life? By definition life is a fickle noun, an event in +progress. Still, we cling to philosophical railings, religious icons, +pillars of belief. We forget on purpose that Earth is rolling at 1,000 +miles an hour, and, at the same time, falling elliptically around our sun, +while the sun is swinging through the Milky Way, and the Milky Way migrating +along with countless other galaxies in a universe about 13.7 billion years +old. An event is such a little piece of time and space, leaving only a +mindglow behind like the tail of a shooting star. For lack of a better +word, we call that scintillation memory. + -- Diane Ackerman, in "An Alchemy Of Mind: The Marvel and Mystery of + the Brain" +~ +Many people who approach the practice of Buddhism are willing to sacrifice one +or two hours of their day in order to perform some ritual practice or engage in +meditation. Time is relatively easy to give up, even though their life may be +very busy. But, they are not willing to change anything of their personality-- +they are not willing to forgo anything of their negative character. With this +type of approach to Buddhism, it hardly matters how much meditation we do, our +practice remains merely a hobby or a sport. It does not touch our lives. In +order actually to overcome our problems, we have to be willing to change-- +namely to change our personality. We need to renounce and rid ourselves of +those negative aspects of it that are causing us so much trouble. + -- H.H. the Dalai Lama & Alexander Berzin in "The Gelug/Kagyu Tradition of + Mahamudra" +~ +Climb the mountains and get their good tidings. Nature's peace will flow into +you as sunshine flows into trees. The winds will blow their own freshness into +you, and the storms their energy, while cares will drop off like autumn leaves. + -- John Muir +~ +Whoso does not see that genuine life is a battle +and a march has poorly read his origin and his destiny. + -- Lydia Child +~ +The more intelligent one is, the more men of originality +one finds. Ordinary people find no difference between men. + -- Blaise Pascal +~ +The last proceeding of reason, is to recognize that +there is an infinity of things beyond it. + -- Blaise Pascal +~ +Argument is the worst sort of conversation. -- Jonathan Swift +~ +No burden is so heavy for a man to bear as a succession of happy days. + -- Max Planck +~ +Whatever games are played with us, we must play no games with ourselves. + -- Ralph Waldo Emerson +~ +I decline utterly to be impartial between the fire brigade and the fire. + -- Winston Churchill +~ +The self that is grasped by ignorance should be refuted, but we should not +refute the conventionally existent self. If we refute the self that is merely +conventionally existent, that is like a seeing a person and saying, "Oh, I +didn't see the person." Or we might look at another person and say, "All I +see is the basis of designation of the person or the visual form of the body." +This is no way to speak. It would be impossible to maintain any valid or +justifiable use of language in this way. We would be negating the existence +of a merely designated self, but we would not negate the self that is refuted +by means of the wisdom that investigates the nature of emptiness. Once again, +the self to be negated is the self which is grasped by ignorance. + -- Gen. Lamrimpa, in "Realizing Emptiness: Madhyamaka Insight Meditation", + translated by B. Alan Wallace, published by Snow Lion Publications +~ +In general, the countries of the East have had less material progress and thus +have great suffering from poverty. In the West, though poverty is not severe, +there is the suffering of worry and not knowing satisfaction. In both East and +West, many persons spend their lives in jealousy and competition; some think +only of money, and when they meet with conditions unfavourable to their wish +develop a dislike or enmity for these unfavourable circumstances from the very +orb of their heart. Within and between countries people are disturbed, not +trusting and believing each other, having to spend their lives in continual +lies and deceit. Since the most we can live is a hundred years, what point is +there in spending our lives in jealousy, deceit, and competition? + -- H.H. the Dalai Lama, in "Deity Yoga", translated and edited by Jeffrey + Hopkins, published by Snow Lion Publications +~ +She was fascinated by baths. I suppose total immersion in water +must have seemed to her a peculiar method of cleansing oneself. + -- Elizabeth Peters +~ +The uncompromising self-examination of a Rembrandt self-portrait remains, +almost four centuries on, far more 'shocking' than a museum installation +of cow carcasses. + -- Ralph Peters +~ +Complaint is the largest tribute Heaven receives. -- Jonathan Swift +~ +Mere belief in a source of refuge is not firm; unless there is valid cognition, +you are going only on the assertion that Buddhism is good. Refuge is not an +act of partisanship but is based on analysing what scriptures are reasonable +and what scriptures are not. In order for the mind to engage one-pointedly in +practice, there must be reasoned conviction that only the Buddhist path is +non-mistaken and capable of leading to the state of complete freedom from +defects and possession of all auspicious attainments. One should engage in +honest investigation, avoiding desire and hatred and seeking the teaching that +sets forth the means for fulfilling the aims of trainees. + -- H.H. the Dalai Lama, in "Tantra in Tibet", trans. & ed. by Jeffrey Hopkins +~ +A bad peace is even worse than war. -- Tacitus +~ +The way of fortune is like the milky way in the sky; which is a number of +smaller stars, not seen asunder, but giving light together; so it is a number +of little and scarce discerned virtues, or rather faculties and customs, that +make men fortunate. + -- Francis Bacon +~ +Ability will never catch up with the demand for it. -- Confucius +~ +There is more to life than increasing its speed. -- Mahatma Gandhi +~ +If cats could talk, they wouldn't. -- Nan Porter +~ +I read the newspaper avidly. It is my one form of continuous fiction. + -- Aneurin Bevan +~ + The teeth, hair and nails are not I, nor am I bone, blood, mucus, phlegm, +pus or lymph. + Bodily oil is not I, nor is sweat, fat or the entrails either. The cavity +of the entrails is not I, nor is excrement or urine. + Flesh is not I, nor are the sinews, warmth nor air. The bodily cavities are +not I, nor is any one of the six types of consciousness. + If the self truly exists in the manner in which it appears, then it should be +identifiable as one inspects the components of a person one by one. Following +the above verses, no part of the body, including the four elements and space, +nor the six types of consciousness can be identified as the self. This implies +that the self that experiences joy and sorrow and that appears to the mind as +if it existed independently does not exist at all. This is ascertained by +engaging in such analysis. + -- H.H. the Dalai Lama, in "Transcendent Wisdom", Snow Lion Publications +~ + Once, on behalf of his mother, Kyogom produced a thangka of the five buddha +families. He asked the guru, Gampopa, to bless it quickly. + Gampopa agreed, saying, "Burn this stick of incense and make a mandala +offering." + He then transformed himself into the Buddha, and from his ushnisha there +radiated a glorious light that dissolved into the thangka. The air resounded +with the tinkling of bells and the drumbeat of the damaru, and the sky was +filled with parasols, auspicious banners, and canopies. The sound of cymbals +was heard, and a rain of flowers fell from the sky. + When Kyogom saw this, the lama said, "This is the way to do a rapid +consecration." + -- Jampa Mackenzie Stewart, in "The Life of Gampopa", Snow Lion Pub. +~ +The absolute nature of the prohibition of torture and other forms of ill +treatment means that no exceptional circumstances whatsoever, whether a +state of war or a threat of war, internal political instability or any +other public emergency, may be invoked as a justification for torture. + -- Theo van Boven, UN official charged with monitoring incidents of torture +~ +No executive, legislative, administrative, or judicial measure authorizing +recourse to torture and cruel, inhuman or degrading treatment or punishment +can be considered as lawful under international law. + -- Theo van Boven, UN official charged with monitoring incidents of torture +~ +In a few hundred years, when the history of our time will be written from a +long-term perspective, it is likely that the most important event historians +will see is not technology, not the Internet, not e-commerce. It is an +unprecedented change in the human condition. For the first time--literally +--substantial and rapidly growing numbers of people have choices. For the +first time, they will have to manage themselves. And society is totally +unprepared for it. + -- Peter F. Drucker +~ +Let thy food be thy medicine and thy medicine be thy food. -- Hippocrates +~ +One of the lessons of the Web is that if people have access to information, +they will consume it, whether they are hungry or not. + -- Lee Gomes +~ +If cats seem distant and aloof, it is because this is not their native +planet--they are here just to visit and dominate. + -- Hank Roll +~ +History is the only laboratory we have in which to test +the consequences of thought. + -- Etienne Gilson +~ +In journalism it is simpler to sound off than it is to find out. +It is more elegant to pontificate than it is to sweat. + -- Harold Evans +~ +If we have only cultivated undistracted meditative concentration and lack the +supreme knowledge that realizes how things actually are, it is impossible to +see ultimate reality. On the other hand, if we have the correct view of +understanding identitylessness but no meditative concentration in which the +mind rests one-pointedly, our mind will be distracted by other objects, not be +under control, and thus not be workable. Consequently, it will be impossible +for the light of wisdom to shine clearly and realize ultimate reality. Another +analogy for the need to combine calm abiding and superior insight as an +inseparable unity is a sharp scalpel in the steady hand of an experienced +surgeon. If the scalpel is blunt or the surgeon's hand shaky, the operation +cannot be performed properly. In the same way, when the mind rests in a state +that involves both stillness and a crisp wakefulness or awareness, it is like +a steady hand that deftly operates on our objects of investigation with the +sharp blade of superior insight. + -- Karl Brunnhoelzl, in "The Center of the Sunlit Sky: Madyhamaka in the + Kagyu Tradition, Snow Lion Publications +~ +Golf is the only sport where the ball doesn't move until you hit it. + -- Ted William +~ +... when a candidate for public office faces the voters he does not face men +of sense; he faces a mob of men whose chief distinguishing mark is the fact +that they are quite incapable of weighing ideas, or even of comprehending +any save the most elemental--men whose whole thinking is done in terms of +emotion, and whose dominant emotion is dread of what they cannot understand. +So confronted, the candidate must either bark with the pack or be lost. + +... all the odds are on the man who is, intrinsically, the most devious and +mediocre--the man who can most adeptly disperse the notion that his mind is +a virtual vacuum. + +The Presidency tends, year by year, to go to such men. As democracy is +perfected, the office represents, more and more closely, the inner soul of +the people. We move toward a lofty ideal. On some great and glorious day the +plain folks of the land will reach their heart's desire at last, and the +White House will be adorned by a downright moron. + -- H. L. Mencken (July 26, 1920, in the Baltimore Sun): +~ +The mastery of the turn is the story of how aviation became practical as a +means of transportation. It is the story of how the world became small. + -- William Langewiesche +~ +Flight's greatest gift is to let us look around. I mean a simple form of +looking around, and one that requires little instruction--just gazing down at +the ordinary scenery sliding by below. The best views are views of familiar +things, like cities and farms and bottlenecked freeways. So set aside the +beauty of sunsets, the majesty of mountains, the imprint of winds on golden +prairies. The world beneath our wings has become a human artifact, our most +spontaneous and complex creation. Tourists may not like to contemplate the +evidence, with its hints of greed and self-destruction, but the fact remains +that the old sterilized landscapes--like designated outlooks and pretty parks +and sculpted gardens--have become obsolete, and that it is largely the airplane +that has made them so. The aerial view is something entirely new. We need to +admit that it flattens the world and mutes it in a rush of air and engines, and +that it suppresses beauty. But it also strips the facades from our +constructions, and by raising us above the constraints of the treeline and the +highway it imposes a brutal honesty on our perceptions. It lets us see +ourselves in context, as creatures struggling through life on the face of a +planet, not separate from nature, but its most expressive agents. It lets us +see that our struggles form patterns on the land, that these patterns repeat to +an extent which before we had not known, and that there is a sense to them. + -- William Langewiesche, "Inside The Sky: Meditation on Flight" +~ +A lot of good arguments are spoiled by some fool +who knows what he is talking about. + -- Miguel de Unamuno +~ +Anything one man can imagine, other men can make real. -- Jules Verne +~ +We have the bias of considering some people to be enemies and others to be +friends. If this really were true such that an enemy always remained an enemy +and a friend always remained a friend, then there might be a reason to hate +certain people and love others. But, again, this is not the case. There is +no certainty in relationships. + -- H.H. the Dalai Lama, in "The Dalai Lama at Harvard: Lectures on the + Buddhist Path to Peace", Snow Lion Publications +~ +Intelligence is quickness in seeing things as they are. -- George Santayana +~ +It is said that the awareness of a buddha is completely even, like the ocean, +taking in equally the joys and sorrows of all people, friends, loved ones, +relatives, and those never met. This is the meaning of a statement made by so +many of the world's great spiritual teachers, "Love your enemy." It doesn't +mean love the person you hate. You can't do that. Love those who hate you. + -- B. Alan Wallace, in "Buddhism with an Attitude: The Tibetan Seven-Point + Mind-Training", Snow Lion Publications +~ +Man is an animal that makes bargains: no other animal +does this--no dog exchanges bones with another. + -- Adam Smith +~ +Art is not about thinking something up. +It is the opposite--getting something down. + -- Julia Cameron +~ +Although we regard the realization of the selflessness of persons as something +particularly exalted and therefore difficult to achieve, in fact, if you look +directly at your mind and see its nature, you will realize this selflessness. +This is not a matter of trying to convince yourself that there is no self in +the mind. It is simply a matter of looking. And when you look, you will see +that there is no mind, and that therefore there is no self that could be +imputed on the basis of the mind. + -- Khenchen Thrangu Rinpoche, in "Pointing out the Dharmakaya", Snow Lion +~ +Cold! If the thermometer had been an inch longer we'd have frozen to death. + -- Mark Twain +~ +Love and scandal are the best sweeteners of tea. -- Henry Fielding +~ +Material progress is for the sake of achieving that happiness and relieving +that suffering which depends upon the body. But it is indeed difficult to +remove all suffering by these external means and thereby achieve complete +satisfaction. Hence there comes to be a great difference between seeking +happiness in dependence upon external things and seeking it in dependence +upon one's own internal spiritual development. Furthermore, even if the +basic suffering is the same, there is a great difference in the way we +experience it and in the mental discomfort that it creates, depending upon +our attitude towards it. Hence our mental attitude is very important in +how we spend our lives. + -- H.H. the Dalai Lama, in "Kindness, Clarity and Insight", Snow Lion Pub. +~ +Love is a condition in which the happiness of another +person is essential to your own. + -- Robert Heinlein +~ +Looking at a cat, like looking at clouds or stars or the ocean, makes +it difficult to believe there is nothing miraculous in this world. + -- Leonard Michaels +~ +Waking in the night +the lamp is low +the oil freezing. + -- Matsuo Basho +~ +Show me the man you honor, and I will know what kind of man you are. + -- Thomas Carlyle +~ + If you have love, deities and humans will love you and will naturally +gravitate toward you. Moreover, the Conqueror defeated Mara's armies with the +power of love, so love is the supreme protector, and so forth. Thus, although +love is difficult to develop, you must strive to do so. + The way to cultivate love is as follows. Just as you can develop compassion +once you have repeatedly thought about how living beings are made miserable by +suffering, develop love by thinking repeatedly about how living beings lack all +happiness, both contaminated and uncontaminated. When you become familiar with +this, you will naturally wish for beings to be happy. In addition, bring to +mind various forms of happiness and then offer them to living beings. + -- from "The Great Treatise on the Stages of the Path to Enlightenment", + Volume 2, Snow Lion Publications +~ +There is no less invention in aptly applying a thought found +in a book, than in being the first author of the thought. + -- Pierre Bayle +~ +You'll never need a lawn ornament if you have a cat in the yard. + -- Katherine Palmer Peterson +~ +Karma has four main characteristics. The first is its increasing effect: +goodness heralds further goodness and evil heralds further evil. Secondly, +karma is definite: in the long run, goodness always produces joy and negativity +always produces suffering. Thirdly, one never experiences a joy or sorrow that +does not have an according karmic cause. And lastly, the karmic seeds that are +placed on the mind at the time of an action will never lose their potency even +in a hundred million lifetimes, but will lie dormant within the mind until one +day the conditions that activate them appear. + -- H.H. the Dalai Lama, in "The Path to Enlightenment", Snow Lion +~ +The ideal of calm exists in a sitting cat. -- Jules Reynard +~ + There are generally two kinds of buddhist meditation: stabilizing meditation +and analytical meditation. The stabilizing type of meditation gets the brain +very mellow. It is a soothing of the mind to a clear state of calm, like a +still lake. Stabilization allows all thoughts to flow through the mind without +attachment to any of them. But the analytical type of meditation uses the +power of logic to examine what is bothering one. Through meditative analysis, +problems are conquered by finding their root causes and by developing +techniques for mitigating the effects of those causes on the mind. Analysis +can focus on topics such as impermanence, suffering, the need for patience, +and other topics. + These two phases of meditation are not practiced at different times of the +day, but from instant to instant. At first, when one is a beginner, one must +actively work to stabilize the mind and release all the distractions. But +just when one starts to doze off because this process is so relaxing, one +switches over to analytical meditation to keep the brain moving and to balance +out the bliss. + One has to pay careful attention to the state of one's mind to determine +what is needed at a particular time. But this wakeful refocusing of the +attention helps to develop mindfulness, which in turn helps develop +understanding and concentration. Later on, the switch between analytical +and stabilizing meditations will happen somewhat more automatically; if +thoughts start swirling too fast, the meditative mind seeks stabilization. If +things get too calm or placid, then analysis can bring the balance back to +thinking and improving. + Analysis is insufficient by itself, because it might be all about what's +currently "wrong" or it might produce a billion approaches to improving the +mind which cannot all be followed, but it would miss out on just enjoying our +somewhat short existence. Complete realization of the enjoyability of +sentience and sapience seems to come from stabilization, not analysis. And +stabilization is not enough by itself either, because one sees the result +of too much placidity in a person asleep in bed: they have a cow-like calm +without any thoughts breaking the surface to cause future progress. The +impetus behind the push towards enlightenment seems to come from analysis, +because one cannot see why one would want to fully awake and aware unless +one can logically compare that state with the current one. Thus analytical +and stabilizing meditations are the dynamic duo of meditation, the laurel +and the hardy, the R and the D, the anion and the cation, for without both +the ultimate mental state is unattainable. + -- fred t. hamster +~ +The bad news: there is no key to the universe. +The good news: it was never locked. + -- Swami Beyondananda +~ +What is tolerance? It is the consequence of humanity. We are all formed of +frailty and error; let us pardon reciprocally each other's folly. That is the +first law of nature. + -- Voltaire +~ +A man's manners are a mirror in which he shows his portrait. + -- Johann Wolfgang von Goethe +~ +If I wished to punish a province, I would have it governed by philosophers. + -- Frederick the Great +~ +I know that many people have difficulty thinking of spirits in the way I +describe them. There are many spirits described in Tibetan texts related to +specific places in Tibet. I'm not sure, if we live in New York or Tokyo, that +it's very helpful to try to connect to those spirits. When we are in Western +cities, rather than thinking of spirits living in mountain passes or caves, we +might find it easier to think that spirits travel the streets, creating anger +and agitation in the drivers. When we experience aggressive driving, it is a +good idea to breathe evenly and relax. Otherwise, we may find ourselves +connected to traffic demons! + -- "Healing with Form, Energy and Light: The Five Elements in Tibetan + Shamanism, Tantra and Dzogchen", published by Snow Lion Publications +~ +I'm all in favor of keeping dangerous weapons out of the hands of fools. +Let's start with typewriters. + -- Frank Lloyd Wright +~ +As long as algebra is taught in school, there will be prayer in school. + -- Cokie Roberts +~ +In Tantra the term bodhicitta assumes a more specific connotation. The essence +of enlightenment or "Buddha-nature," revealed in the sutras of the third +turning of the wheel of Dharma, is equated to the subtle essence (thig le) of +the human body, the coarse aspect of which is present in the seminal fluid and +in the ovum. Thus in Tantra, bodhicitta is recognized as the seed of the +manifestation of the infinite mandalas and deities who are all already +contained in potentiality in the energy structure of the physical body itself, +what is known as the "vajra body." However in spite of this underlying +recognition as its base, tantric practice entails visualization and commitments +of body voice, and mind to achieve the transformation of the energies of impure +vision into the pure dimension of the mandala and of the deities. + -- Chogyal Namkhai Norbu and Adriano Clemente, in "The Supreme Source: The + Fundamental Tantra of the Dzogchen Semde Kunjed Gyalpo", Snow Lion Pub. +~ +Never lend books, for no one ever returns them; the only books +I have in my library are books that other folks have lent me. + -- Anatole France +~ +It is impossible to keep a straight face in +the presence of one or more kittens. + -- Cynthia E. Varnardo +~ +I always explain that violence is not the human way. I believe that, +fundamentally, human nature is positive, gentle; therefore, the non-violent way +is the human way. Also, whatever result we achieve through non-violence has no +negative side effect. Through violence, even though we may get some kind of +satisfaction, negative side effects are also incurred. Then, most importantly, +whether we like it or not, we have to live side by side with the Chinese; thus, +in the long future, generation to generation, in order to live happily, +peacefully, it is extremely important, while we are carrying on the struggle, +to accord with the principle of non-violence. + -- H.H. the Dalai Lama, in "The Art of Peace: Nobel Peace Laureates discuss + Human Rights, Conflict and Reconciliation", Snow Lion Pub. +~ +Mencken's Creed + + I believe that religion, generally speaking, has been a curse to mankind-- +that its modest and greatly overestimated services on the ethical side have +been more than overcome by the damage it has done to clear and honest thinking. + I believe that no discovery of fact, however trivial, can be wholly useless +to the race, and that no trumpeting of falsehood, however virtuous in intent, +can be anything but vicious. + I believe that all government is evil, in that all government must +necessarily make war upon liberty... + I believe that the evidence for immortality is no better than the evidence +of witches, and deserves no more respect. + I believe in the complete freedom of thought and speech... + I believe in the capacity of man to conquer his world, and to find out what +it is made of, and how it is run. + I believe in the reality of progress. + I--But the whole thing, after all, may be put very simply. I believe that it +is better to tell the truth than to lie. I believe that it is better to be +free than to be a slave. And I believe that it is better to know than be +ignorant. + -- H. L. Mencken +~ + The larger the mob, the harder the test. In small areas, before small +electorates, a first-rate man occasionally fights his way through, carrying +even the mob with him by force of his personality. But when the field is +nationwide, and the fight must be waged chiefly at second and third hand, and +the force of personality cannot so readily make itself felt, then all the odds +are on the man who is, intrinsically, the most devious and mediocre--the man +who can most easily adeptly disperse the notion that his mind is a virtual +vacuum. + The Presidency tends, year by year, to go to such men. As democracy is +perfected, the office represents, more and more closely, the inner soul of the +people. We move toward a lofty ideal. On some great and glorious day the +plain folks of the land will reach their heart's desire at last, and the White +House will be adorned by a downright moron. + -- Henry Louis Mencken, "Bayard vs. Lionheart", Baltimore Evening Sun, + 26 July 1920. +~ +The possession of knowledge does not kill the sense of +wonder and mystery. There is always more mystery. + -- Anais Nin +~ +There is no excellent beauty that hath not some strangeness in the proportion. + -- Sir Francis Bacon +~ +I say that the art of sculpture is eight times as great as any other art based +on drawing, because a statue has eight views and they must all be equally good. + -- Benvenuto Cellini +~ + When we talk about patience or tolerance, we should understand that there are +many degrees, starting from a simple tolerance, such as being able to bear a +certain amount of heat and cold, progressing toward the highest level of +patience, which is the type of patience and tolerance found in the great +practitioners, the Bodhisattvas on the high levels of the Buddhist path. +Since patience or tolerance comes from a certain ability to remain firm and +steadfast, to not be overwhelmed by the adverse situations or conditions that +one faces, one should not see tolerance or patience as a sign of weakness, but +rather as a sign of strength coming from a deep ability to remain steadfast and +firm. We can generally define patience or tolerance in these terms. We find +that even in being able to tolerate a certain degree of physical hardship, like +a hot or cold climate, our attitude makes a big difference. + If we have the realization that tolerating immediate hardship can have long- +term beneficial consequences, we are more likely to be able to tolerate +everyday hardships. Similarly, in the case of those on the Bodhisattva levels +of the path practicing high levels of tolerance and patience, intelligence also +plays a very important role as a complementary factor. + -- H.H. the Dalai Lama, in "Healing Anger: The Power of Patience from a + Buddhist Perspective", Snow Lion Publications +~ +One difference between poetry and lyrics is that lyrics sort of fade into the +background. They fade on the page and live on the stage when set to music. + -- Stephen Sondheim +~ +All the good ideas I ever had came to me while I was milking a cow. + -- Grant Wood +~ +Completely pure desirous attachment expresses itself through Buddha Amitabha. +A person guided by desire, attachment, or grasping becomes diffused and loses +power over phenomena. Through completely purified desirous attachment, +however, one is able to gain control over, and to independently coordinate, +everything. This is because the entourage, possessions, merit, and so forth +are controlled by the power of this Buddha. In this way Amitabha grants us the +empowering enlightened activity and the empowering extraordinary achievements. + -- Khenchen Thrangu Rinpoche, in "Everyday Consciousness and + Buddha-awakening", Snow Lion Pub. +~ +No harm's done to history by making it something someone would want to read. + -- David McCollough +~ +If, in some cataclysm, all of scientific knowledge were to be destroyed, and +only one sentence passed on to the next generation of creatures, what statement +would contain the most information in the fewest words? I believe it is the +atomic hypothesis (or the atomic fact, if you wish to call it that) that all +things are made of atoms--little particles that move around in perpetual +motion, attracting each other when they are a little distance apart, but +repelling upon being squeezed into one another. + -- Richard P. Feynman +~ +The third aspect of the practice of generosity is giving the gift of the +Dharma by making the teachings available. Through the Dharma, we can bring +understanding into the lives of others and help them remove their patterns of +ignorance. We do not have to be great scholars to practice generosity through +the Dharma. We may know just one line of the Dharma, but if we know that one +line clearly and correctly, we can genuinely express it to others and help +them. If we know four lines of the teachings, through those four lines we can +help to clear up someone's ignorance or lessen someone's problems and +sufferings. What counts is not how much we know but how correctly we know it +and how sincerely we use it to help others. We should not wear the mask of the +Dharma, pretending we know a great deal and are doing great things, while +hiding ugliness inside us. + -- Khenpo Karthar Rinpoche, in "Dharma Paths", Snow Lion Pub. +~ +One difference between the destructive, negative emotions on the one side and +constructive, positive emotions on the other is that constructive, positive +emotions have a strong grounding in valid experience and reasoning. In fact, +the more we analyze these positive emotions, the more they are enhanced. +Negative, afflictive emotions, by contrast, are usually quite superficial. +They have no grounding in reason and often arise out of habit rather than +reasoned thought processes. + -- H.H. the Dalai Lama, in "Illuminating the Path to Enlightenment", + Snow Lion Publications +~ +Whenever you travel on an airplane, the flight attendants demonstrate what to +do in an emergency. They tell you to put the oxygen mask over your own nose +before putting it over your child's nose. That's not because you should be +selfish, but because if you pass out, you won't be able to help anyone at all. +So you see, the essential point is that we must first subdue our own minds +before we can effectively serve others. + +If you have failed to subdue your own mind, even if a thousand Buddhas surround +you, they will be of no benefit. If you want to subdue your enemies, you must +subdue your own mind. If you want to bring about world peace, subdue your +mind. A subdued mind is the Lama. A subdued mind is the Dakini, the chosen +deity, and the Buddha. A subdued mind is the pure land. You are already +imbued with the Buddha-nature. This is your inherent nature. What actual +benefit have you gotten from the essential nature of your mind? + -- Karma Chagme, in "A Spacious Path to Freedom: Practical Instructions on + the Union of Mahamudra and Atiyoga", Snow Lion Publications +~ +From: non-verbal sys-admin +To: support technician +Subject: harriet crash + + harriet crashed this afternoon + it is back online now + looks like a bad drive in the array + i have an ibm ticket opened + hope to have it serviced sometime tomorrow + + the current state or the repair + should not affect core team work in build + +--- + +From: support technician +To: non-verbal sys-admin +Subject: Re: harriet crash + + capital letters + complete sentences, grammar + needless in haiku +~ +I love cats because I enjoy my home; and little by little, +they become its visible soul. + -- Jean Cocteau +~ +I saw also that there was an ocean of darkness and death; but an infinite +ocean of light and love, which flowed over the ocean of darkness. + -- George Fox +~ +Besides the first degree of eminence in science, a professor with us must be +of sober and correct morals and habits, having the talent of communicating his +knowledge with facility, and of an accommodating and peaceable temper. The +latter is all important for the harmony of the institution. + -- Thomas Jefferson +~ +Like all the best families, we have our share of eccentricities, +of impetuous and wayward youngsters and of family disagreements. + -- Queen Elizabeth II +~ +We do not see, or we forget, that the birds that are idly singing around us +mostly live on insects or seeds, and are thus constantly destroying life. + -- Charles Darwin +~ +Obstacles to our practice may be external, internal or secret. The external +ones consist of natural disasters, harm inflicted by human and nonhuman +beings, sicknesses and so forth. Internal obstacles are our own disturbed +states of mind and emotions and all the obstructions to liberation and full +knowledge of all phenomena. Secret obstacles are seemingly good +circumstances, which distract the practitioner and make him or her forget +about practice. For instance, one might gain a good reputation as a sincere +practitioner and thereby attract followers and wealth, which in the end make +one neglect one's practice. + -- Geshe Sonam Rinchen, in "Eight Verses for Training the Mind", + Snow Lion Pub. +~ +From Linus Benedict Torvalds Oct 5 1991, 8:53 am +Newsgroups: comp.os.minix +From: torva...@klaava.Helsinki.FI (Linus Benedict Torvalds) +Date: 5 Oct 91 05:41:06 GMT +Local: Fri, Oct 4 1991 10:41 pm +Subject: Free minix-like kernel sources for 386-AT + +Do you pine for the nice days of minix-1.1, when men were men and wrote +their own device drivers? Are you without a nice project and just dying +to cut your teeth on a OS you can try to modify for your needs? Are you +finding it frustrating when everything works on minix? No more all- +nighters to get a nifty program working? Then this post might be just +for you :-) + +As I mentioned a month(?) ago, I'm working on a free version of a +minix-lookalike for AT-386 computers. It has finally reached the stage +where it's even usable (though may not be depending on what you want), +and I am willing to put out the sources for wider distribution. It is +just version 0.02 (+1 (very small) patch already), but I've successfully +run bash/gcc/gnu-make/gnu-sed/compress etc under it. + +Sources for this pet project of mine can be found at nic.funet.fi +(128.214.6.100) in the directory /pub/OS/Linux. The directory also +contains some README-file and a couple of binaries to work under linux +(bash, update and gcc, what more can you ask for :-). Full kernel +source is provided, as no minix code has been used. Library sources are +only partially free, so that cannot be distributed currently. The +system is able to compile "as-is" and has been known to work. Heh. +Sources to the binaries (bash and gcc) can be found at the same place in +/pub/gnu. + +ALERT! WARNING! NOTE! These sources still need minix-386 to be compiled + +(and gcc-1.40, possibly 1.37.1, haven't tested), and you need minix to +set it up if you want to run it, so it is not yet a standalone system +for those of you without minix. I'm working on it. You also need to be +something of a hacker to set it up (?), so for those hoping for an +alternative to minix-386, please ignore me. It is currently meant for +hackers interested in operating systems and 386's with access to minix. + +The system needs an AT-compatible harddisk (IDE is fine) and EGA/VGA. If +you are still interested, please ftp the README/RELNOTES, and/or mail me +for additional info. + +I can (well, almost) hear you asking yourselves "why?". Hurd will be +out in a year (or two, or next month, who knows), and I've already got +minix. This is a program for hackers by a hacker. I've enjouyed doing +it, and somebody might enjoy looking at it and even modifying it for +their own needs. It is still small enough to understand, use and +modify, and I'm looking forward to any comments you might have. + +I'm also interested in hearing from anybody who has written any of the +utilities/library functions for minix. If your efforts are freely +distributable (under copyright or even public domain), I'd like to hear +from you, so I can add them to the system. I'm using Earl Chews estdio +right now (thanks for a nice and working system Earl), and similar works +will be very wellcome. Your (C)'s will of course be left intact. Drop me +a line if you are willing to let me use your code. + + Linus + +PS. to PHIL NELSON! I'm unable to get through to you, and keep getting +"forward error - strawberry unknown domain" or something. +~ +Question: How do things exist if they are empty of inherent existence? + +His Holiness: The doctrines of emptiness and selflessness do not imply the non- +existence of things. Things do exist. When we say that all phenomena are void +of self-existence, it does not mean that we are advocating non-existence, that +we are repudiating that things exist. Then what is it we are negating? We are +negating, or denying, that anything exists from its own side without depending +on other things. Hence, it is because things depend for their existence upon +other causes and conditions that they are said to lack independent self- +existence. + -- H.H. the Dalai Lama, in "Answers: Discussions with Western Buddhists", + Snow Lion Publications +~ +You may break, you may shatter the vase if you will, +But the scent of the roses will hang round it still. + -- Thomas Moore +~ +Life is made up, not of great sacrifices or duties, but of little things, +in which smiles, and kindnesses, and small obligations, given habitually, +are what win and preserve the heart and secure comfort. + -- Sir Humphrey Davy +~ +It is only when I am doing my work that I feel truly alive. + -- Federico Fellini +~ +When you engage in a project or an activity that helps other sentient beings, +there is no question of a time limit. You must do it continuously. This is +how you should train your mind. If you think you will achieve enlightenment or +bodhichitta within a few days or months, and if you think that you will get +enlightened after entering into a retreat for three years and three months, you +are mistaken. When I hear the suggestion that you will attain Buddhahood if +you go into retreat for three years and three months, sometimes I jokingly say +that this is just like communist propaganda. I tell my Western friends that +wanting to practice the most profound and the quickest path is a clear sign +that you will achieve no result. How can you achieve the most profound and the +vast in the shortest way? The story of the Buddha says that he achieved +Buddhahood after three countless aeons. So harboring an expectation to achieve +Buddhahood within a short time-like three years and three months-is a clear +indication that you will make no real progress. We have to be practical. +There is no use in fooling others with your incomplete knowledge. + -- H.H. the Dalai Lama, in "Stages of Meditation", Snow Lion Publications +~ +Buddhist moral philosophy is strikingly pragmatic. Something is valuable +insofar as it is relevant to people's lives and useful for achieving their +happiness. If it will bring happiness for oneself and others, pursue it; if it +will bring suffering, better to avoid it. There are no absolute dictates; +ethical personal behavior is seen as simply the most practical way to cope with +the difficulties of the human condition. Although guidelines and copious +illustrations are given, nothing is absolute or definitive. The power and +freedom to make decisions rests with the individual. There is no arbitrary or +mysterious force controlling our lives; there is simply the law of cause and +effect. Of course, decisions are dependent upon many factors. The choices we +make are conditioned by circumstances, both outer and inner, but ultimately +human beings have free will to decide. Individuals make decisions and +experience the consequences of their decisions. + -- from "Buddhism Through American Women's Eyes", edited by Karma Lekshe + Tsomo, Snow Lion Pub. +~ +Chemistry stands at the pivot of science. On the one hand it deals with biology +and provides explanations for the processes of life. On the other hand it +mingles with physics and finds explanations for chemical phenomena in the +fundamental processes and particles of the universe. Chemistry links the +familiar with the fundamental. + -- P. W. Atkins +~ +In a cat's eyes, all things belong to cats. -- English Proverb +~ +A university is what a college becomes when the faculty +loses interest in students. + -- John Ciardi +~ +Ye can lead a man up to the university, but ye can't make him think. + -- Finley Peter Dunne +~ +The new electronic interdependence recreates the +world in the image of a global village. + -- Marshall McLuhan +~ +One of the ways in which cats show happiness is by sleeping. + -- Cleveland Amory +~ +For everything that lives is holy, life delights in life. -- William Blake +~ +Love is a kind of warfare. -- Ovid +~ +Appreciation is a wonderful thing: It makes what +is excellent in others belong to us as well. + -- Francois Marie Arouet Voltaire, 1694 +~ + From a Buddhist point of view, one might be able to distinguish different +states of dreaming. Generally speaking, a dream is a dream, something you +can't control. But for the highly advanced meditator, there could be +possibilities for gaining certain insights through dreams. + I know some Tibetans who lived in Tibet prior to the 1959 uprising. Before +their escape from Tibet, they did not know about the natural trails and passes +by which to get over the Himalayas into India. Some of these people I met had +very clear dreams of these tracks and, years later, when they actually had to +follow the actual trails, they found that they were already familiar with them +because of the very clear dreams they had had previously. + -- H.H. the Dalai Lama in "Consciousness at the Crossroads: Conversations + with the Dalai Lama on Brain Science and Buddhism", ed. by Zara Houshmand, + Robert B. Livingston and B. Alan Wallace, Snow Lion Publications +~ +I think, for the rest of my life, I shall refrain from looking up things. +It is the most ravenous time-snatcher I know. You pull one book from the +shelf, which carries a hint or a reference that sends you posthaste to +another book, and that to successive others. + -- Carolyn Wells (1862-1942), Mystery Writer +~ +It is a paradoxical but profoundly true and important principle of life +that the most likely way to reach a goal is to be aiming not at that +goal itself but at some more ambitious goal beyond it. + -- Arnold Toynbee +~ +Someday, after mastering the winds, the waves, the tides and gravity, +we shall harness for God the energies of love, and then, for a second +time in the history of the world, man will have discovered fire. + -- Pierre Teilhard de Chardin +~ + One method for disciples to develop bodhichitta entails recognizing all +beings as having been their mothers in some previous life and focusing on +their mothers' kindness. Similarly, guru-meditation requires focusing on +their mentors' kindness. + Many Westerners, however, have difficulty focusing on the kindness of their +mothers. Unable to find the goodness and kindness in their mothers, most +cannot find any goodness in themselves either. Although they may be desperate +for love and kindness, their mental blocks often prevent them from recognizing +and appreciating the kindness of others, for instance their spiritual mentors. +No matter how much kindness they receive, it is never enough. + One of the reasons for being unable to acknowledge our mothers' kindness +may be that they fail to live up to our models of ideal parents. Similarly, +when our spiritual mentors have shortcomings and do not live up to our models +of ideal teachers, we may also have difficulty recognizing their kindness. +Like children yearning for ideal love, we feel cheated if our mentors fail +to meet our expectations. + -- Alexander Berzin, in "Relating to a Spiritual Teacher: Building a Healthy + Relationship", Snow Lion Publications +~ +Wilderness is not a luxury but a necessity of the human spirit. + -- Edward Abbey +~ +I don't deserve this award, but I have arthritis +and I don't deserve that either. + -- Jack Benny +~ +I have come to believe over and over again that what is most +important to me must be spoken, made verbal and shared, even at +the risk of having it bruised or misunderstood. + -- Audre Geraldine Lorde +~ +The word dharma in Sanskrit means "that which holds". All existents are +dharmas, phenomena, in the sense that they hold or bear their own entity or +character. Also, a religion is a dharma in the sense that it holds persons +back or protects them from disasters. Here the term dharma refers to the +latter definition. In rough terms, any elevated action of body, speech or +mind is regarded as a dharma because through doing such an action one is +protected or held back from all sorts of disasters. Practice of such +actions is practice of dharma. + -- H.H. the Dalai Lama in "Buddhism of Tibet", Snow Lion Publications +~ +What will survive of us is love. -- Philip Larkin +~ +Do not take life too seriously. You will never get out of it alive. + -- Elbert Hubbard +~ +Advice is what we ask for when we already know the answer but wish we didn't. + -- Erica Jong +~ +Optimism can make you look stupid but cynicism always makes you look cynical. + -- Calum Fisher +~ +An architect proves his skill by turning the defects of a site into advantages. + -- Giovanni Lorenzo Bernini +~ +Physicists and astronomers see their own implications in the world +being round, but to me it means that only one-third of the world is +asleep at any given time and the other two-thirds is up to something. + -- Dean Rusk +~ +What is my purpose in life, what is my responsibility? Whether I like it or +not, I am on this planet, and it is far better to do something for humanity. +So you see that compassion is the seed or basis. If we take care to foster +compassion, we will see that it brings the other good human qualities. The +topic of compassion is not at all religious business; it is very important to +know that it is human business, that it is a question of human survival, that +is not a question of human luxury. I might say that religion is a kind of +luxury. If you have religion, that is good. But it is clear that even without +religion we can manage. However, without these basic human qualities we cannot +survive. It is a question of our own peace and mental stability. + -- H.H. the Dalai Lama, in "The Dalai Lama, A Policy of Kindness: An + Anthology of Writings By and About the Dalai Lama", compiled and edited + by Sidney Piburn, published by Snow Lion Publications +~ +Read no history: nothing but biography, for that is life without theory. + -- Benjamin Disraeli +~ + We often get angry when something we consider undesirable happens. But what +use is this anger? If we can change the situation, then let's go ahead and do +it. There's no need to be angry. It's very useful to think like this when +confronted with social problems and injustice. They can be changed, so rather +than be angry, it's wiser to work calmly to improve the society. + On the other hand, if the situation can't be changed, anger is equally +useless. Once our leg is broken, we can't unbreak it. All of the corruption +in the world can't be solved in a year. Getting angry at something we can't +alter makes us miserable. Worrying about or fearing something that hasn't +happened immobilizes us. Shantideva said in A Guide to the Bodhisattva's Way +of Life: + Why be unhappy about something If it can be remedied? And what is the + use of being unhappy about something If it cannot be remedied? + -- Thubten Chodron, in "Open Heart, Clear Mind", Snow Lion Publications +~ +From the Hacker's Lexicon: + +Heisenbug /hi:'zen-buhg/ from Heisenberg's Uncertainty Principle in +quantum physics n. A bug that disappears or alters its behavior when one +attempts to probe or isolate it. Antonym of Bohr bug; see also mandelbug, +schroedinbug. In C, nine out of ten heisenbugs result from either +fandango on core phenomena (esp. lossage related to corruption of the +malloc arena) or errors that smash the stack. + +Schroedinbug MIT: from the Schroedinger's Cat thought-experiment in +quantum physics n. A design or implementation bug in a program which +doesn't manifest until someone reading source or using the program in an +unusual way notices that it never should have worked, at which point the +program promptly stops working for everybody until fixed. Though this +sounds impossible, it happens; some programs have harbored latent +schroedinbugs for years. Compare heisenbug, Bohr bug, mandelbug. + +Bohr bug /bohr buhg/ from quantum physics n. A repeatable bug; one that +manifests reliably under a possibly unknown but well-defined set of +conditions. Antonym of heisenbug; see also mandelbug, schroedinbug. + +Mandelbug /mon'del-buhg/ from the Mandelbrot set n. A bug whose +underlying causes are so complex and obscure as to make its behavior +appear chaotic or even non-deterministic. This term implies that the +speaker thinks it is a Bohr bug, rather than a heisenbug. See also +schroedinbug. +~ +If I were not a physicist, I would probably be a musician. I often think in +music. I live my daydreams in music. I see my life in terms of music. + -- Albert Einstein +~ +Nothing's impossible I have found, +For when my chin is on the ground, +I pick myself up, Dust myself off, +Start all over again. + -- Dorothy Fields +~ +You will always be lucky if you know how to make friends with strange cats. + -- Colonial American Proverb +~ +The essence of dharma practice is to bring about a discipline within the mind, +a state of mind free of hatred, lust and harmful intentions. Hence the entire +message of the buddhadharma could be summed up in two succinct statements: +"Help others," and "If you cannot help them, at least do not harm others." +It is a grave error to think that apart from such a disciplining of the +physical and mental faculties there is something else called "the practice of +dharma." Various, and in some cases divergent, methods to achieve such an +inner discipline have been taught in the scriptures by the Buddha. + -- H.H. the Dalai Lama, "The Path to Bliss", Snow Lion Publications +~ +It is not the strongest of the species that survive, +nor the most intelligent, +but the one most responsive to change. + -- Charles Darwin +~ +As progress is made in dream practice, dreams become clearer and more detailed, +and a larger part of each dream is remembered. This is a result of bringing +greater awareness into the dream state. Beyond this increased awareness in +ordinary dreams is a second kind of dream called the dream of clarity, which +arises when the mind and the prana are balanced and the dreamer has developed +the capacity to remain in non-personal presence. Unlike the samsaric dream, in +which the mind is swept here and there by karmic prana, in the dream of clarity +the dreamer is stable. Though images and information arise, they are based +less on personal karmic traces and instead present knowledge available directly +from consciousness below the level of the conventional self. + -- Tenzin Wangyal Rinpoche, in "The Tibetan Yogas of Dream and Sleep", + published by Snow Lion Publications +~ +In order to improve the mind, we ought less to learn than to contemplate. + -- Rene Descartes +~ +Be careful when reading health books--you might die of a misprint. + -- Mark Twain +~ +The practice of pure perception is effective when it is practiced a lot. In +pure perception, look beyond the surface and imagine that other people are none +other than expressions of buddha-mind. We can choose to focus on positive +attributes. What we attend to becomes our reality and if we attend to the +buddhas in situations, in things, in other people it is the buddhas we engage +with and the reality of the buddhas that becomes our reality. + -- B. Alan Wallace, in "Buddhism with an Attitude: Tibetan Seven-Point + Mind-Training", Snow Lion Publications +~ +Correctness is clearly the prime quality. If a system does not do what it +is supposed to do, then everything else about it matters little. + -- Bertrand Meyer +~ +The path to genuine co-operation is again through sincere compassion and love. +Sometimes we misunderstand compassion as being nothing more than a feeling of +pity. Compassion is much, much more. It embraces not only a feeling of +closeness, but also a sense of responsibility. When you develop compassion, it +will help you enormously to generate inner strength and self-confidence, and to +reduce your feelings of fear and insecurity. So compassion and love, embodied +in an attitude of altruism, are qualities that are of tremendous importance for +the individual, as well as for society and the community at large. + -- H.H. the Dalai Lama, in "Dzogchen: The Heart Essence of the Great + Perfection", published by Snow Lion Publications +~ +The value of transmission is not only that of introducing the state of +knowledge, but lies also in its function of bringing about the maturing of the +transmission, right up until one reaches realization. For this reason the +relationship that links master and disciple is a very close one. The master, +in Dzogchen, is not just like a friend who helps and collaborates with the +disciple; rather the master is himself or herself the path. This is because +the practice of contemplation develops through the unification of the state of +the disciple with that of the master. The master is extremely important, too, +at the Sutra and the Tantra levels of teaching, in the former because he or she +is the holder of Buddha's teachings, and in the latter because he or she is the +source of all the manifestations of transformation. + -- Chogyal Namkhai Norbu, in "Dzogchen: The Self-perfected State", + published by Snow Lion Publications +~ +Setting the Appropriate Motivation + It is very important before receiving any Dharma teaching to set a proper +motivation, or reaffirm and enhance that motivation if we already basically +have it. This is important not only for those who are listening to a spiritual +discourse, but also for the person delivering it. If a discourse or +explanation is given with an attitude of pride, competitiveness or jealousy, it +will not do as a Dharma teaching. A Buddhist teaching must be given with the +sincere wish to benefit all beings by means of it. + Likewise, the listeners to a Buddhist teaching must have a proper +motivation, always thinking, "What new point can I learn from this that will +help me be of more benefit to others?" If we sit here with the notion to learn +something about mahamudra so that we can make a display of ourselves and +proudly talk to others about mahamudra so that they will consider us an +erudite, spiritual person, we have a completely wrong motivation. + -- H.H. the Dalai Lama, from "The Gelug/Kagyu Tradition of Mahamudra", + published by Snow Lion Publications +~ +There is one simple Divinity found in all things, everything has Divinity +latent within itself. For she enfolds and imparts herself even unto the +smallest beings. Without her presence nothing would have being, because she +is the essence of the existence of the first unto the last being. + -- Giordano Bruno, "The Expulsion of the Triumphant Beast", (1582) +~ +Even to have come forth is something, since I see that being able to conquer +is placed in the hands of fate. However, there was in me, whatever I was able +to do, that which no future century will deny to be mine, that which a victor +could have for his own: Not to have feared to die, not to have yielded to any +equal in firmness of nature, and to have preferred a courageous death to a +noncombatant life. + -- Giordano Bruno, "De Monade" +~ +Thus is the excellence of God magnified and the greatness of his kingdom made +manifest; He is glorified not in one, but in countless suns; not in a single +earth, a single world, but in a thousand thousand, I say in an infinity of +worlds! + -- Giordano Bruno +~ +The real story of our times is seldom told in the horse-puckey-filled memoirs +of dopey, self-serving presidents or generals, but in the outrageous, demented +lives of guys like Lenny Bruce, Giordano Bruno, Scott Fitzgerald--and Paul +Krassner. The burrs under society's saddle. The pains in the ass. + -- Harlan Ellison +~ +The Tibetan word that is usually translated as "blessing" or "inspiration" can +more literally be translated as "to transform into magnificence." We are +asking the Buddhas to transform our minds into magnificence. How that happens +isn't by the Buddha going in and pulling some switches inside our mind. +Because our mind is conditioned and changing, the mental energy of the Buddha's +realizations can affect our energy, so to speak. Conditioned phenomena affect +each other, so the force of Tara's realizations can positively affect our mind. + -- Thubten Chodron, in "How to Free Your Mind", Snow Lion Publications +~ +A botnet is comparable to compulsory military service for windows boxes. + -- Stromberg +~ +The greatest strength is gentleness. -- Iroquois proverb +~ +Whether or not we actually achieve the realisation of bodhicitta and to what +level or depth we gain such a realisation depends upon the force of our +experience of great compassion. This great compassion, which aspires to free +all sentient beings from suffering, is not confined to the level of mere +aspiration. It has a dimension of far greater power, which is the sense of +commitment or responsibility to personally bring about this objective of +fulfilling others' welfare. In order to cultivate this powerful great +compassion, we need to train our mind separately in two other factors. One is +to cultivate a sense of empathy with or closeness to all sentient beings, for +whose sake we wish to work so that they become free from suffering. The other +factor is to cultivate a deeper insight into the nature of the suffering from +which we wish others to be relieved. + -- H.H. the Dalai Lama, from "Lighting the Way", Snow Lion Publications +~ +Religion does not mean just precepts, a temple, monastery, or other external +signs, for these as well as hearing and thinking are subsidiary factors in +taming the mind. When the mind becomes the practices, one is a practitioner +of religion, and when the mind does not become the practices one is not. + -- H.H. the Dalai Lama, in "Deity Yoga", Snow Lion Pub. +~ +Hamlet's Cat's Soliloquy + +To go outside, and there perchance to stay +Or to remain within: that is the question: +Whether 'tis better for a cat to suffer +The cuffs and buffets of inclement weather +That Nature rains on those who roam abroad, +Or take a nap upon a scrap of carpet, +And so by dozing melt the solid hours +That clog the clock's bright gears with sullen time +And stall the dinner bell. +To sit, to stare Outdoors, and by a stare to seem to state +A wish to venture forth without delay, +Then when the portal's opened up, to stand +As if transfixed by doubt. +To prowl; to sleep; +To choose not knowing when we may once more +Our readmittance gain: aye, there's the hairball; +For if a paw were shaped to turn a knob, +Or work a lock or slip a window-catch, +And going out and coming in were made +As simple as the breaking of a bowl, +What cat would bear the household's petty plagues, +The cook's well-practiced kicks, the butler's broom, +The infant's careless pokes, the tickled ears, +The trampled tail, and all the daily shocks +That fur is heir to, when, of his own free will, +He might his exodus or entrance make +With a mere mitten? +Who would spaniels fear, +Or strays trespassing from a neighbor's yard, +But that the dread of our unheeded cries +And scratches at a barricaded door +No claw can open up, dispels our nerve +And makes us rather bear our humans' faults +Than run away to unguessed miseries? +Thus caution doth make house cats of us all; +And thus the bristling hair of resolution +Is softened up with the pale brush of thought, +And since our choices hinge on weighty things, +We pause upon the threshold of decision. + -- shakespaw +~ +It is not good to begin many different works, saying "This looks good; that +looks good", touching this, touching that, and not succeeding in any of them. +If you do not generate great desires but aim at what is fitting, you can +actualise the corresponding potencies and become an expert in that. With +success, the power or imprint of that practice is generated. + -- H.H. the Dalai Lama, in "Tantra in Tibet", Snow Lion Pub. +~ +Through the skillful methods of tantra, meditators are able to cultivate +pleasure in a way that actually aids in spiritual progress. Afflicted +grasping and desires based on mistaken ideas are the problem, not happiness +and pleasure. If the pursuit of happiness and pleasure can be separated +from afflictive emotions, then it can be incorporated into the path and +will even become a powerful aid to the attainment of enlightenment. + -- John Powers, in "Introduction to Tibetan Buddhism", Snow Lion Pub. +~ +REFUGE +What kind of refuge does Buddhism offer? How are Buddhists and non-Buddhists +differentiated? From the viewpoint of refuge, a Buddhist is someone who +accepts Buddha, his doctrine, and the spiritual community as the final refuge. +From the viewpoint of philosophy, a Buddhist is someone who asserts the four +views that guarantee a doctrine as being Buddhist. With respect to the three +refuges, called the Three Jewels, it is said that the Buddha is the teacher of +refuge but that the actual refuge is the Dharma, the doctrine. Buddha himself +said, "I teach the path of liberation. Liberation itself depends upon you." +From the same perspective, Buddha said, "You are your own master." The +spiritual community are those who assist one in achieving refuge. + -- H.H. the Dalai Lama, in "The Dalai Lama at Harvard", Snow Lion + Publications +~ +Bodhisattvas are motivated by universal compassion, and they seek the ultimate +goal of buddhahood in order to be of service to others. They embark on this +path with the generation of the mind of enlightenment, which Geshe Rabten +states is "the wish for Supreme Enlightenment for the sake of others. The sign +of the true Bodhicitta is the constant readiness to undergo any sacrifice for +the happines of all beings." Unlike ordinary beings, who think of their own +advantage, bodhisattvas consider how best to benefit others. + -- John Powers, in "Introduction to Tibetan Buddhism", Snow Lion Publications +~ +Why are there fierce protectors? Peaceful deities such as Tara have a certain +energy that calms and gladdens our mind. But sometimes our mind is so +belligerent and stuck that we need the kind of energy that goes "Pow!" to wake +us up or to pull us out of unproductive behavior. For this reason, the +Buddhas' wisdom and compassion appear in the form of these wrathful deities to +demonstrate clean-clear wisdom and compassion that act directly. This active +wisdom doesn't vacillate and pamper us. This wisdom doesn't say, "Well, +maybe," or, "Poor you. You deserve to be treated well, not like that horrible +person treated you." Instead, it's forceful: "Cut it out! Stop those false +expectations and preconceptions right now!" Sometimes we need that strong, +wise energy to be in our face to wake us up to the fact that our afflictions +and old patterns of thought and behavior are making us miserable. + -- Thubten Chodron, in "How to Free Your Mind: Tara the Liberator" +~ +I never joined the army because at ease was never that easy to me. Seemed +rather uptight still. I don't relax by parting my legs slightly and putting +my hands behind my back. That does not equal ease. At ease was not being +in the military. I am at ease, bro, because I am not in the military. + -- Mitch Hedberg +~ +The human essence of good sense finds no room with anger. Anger, jealousy, +impatience, and hatred are the real troublemakers; with them problems cannot +be solved. Though one may have temporary success, ultimately one's hatred or +anger will create further difficulties. With anger, all actions are swift. +When we face problems with compassion, sincerely and with good motivation, it +may take longer, but ultimately the solution is better, for there is far less +chance of creating a new problem through the temporary "solution" of the +present one. + -- H.H. the Dalai Lama in "Kindness, Clarity and Insight", Snow Lion Pub. +~ +Killing and eating meat are interrelated, so do we have to give up eating +animal products? I myself once tried to give it up, but health problems arose +and two years later my doctors advised me to again use meat in my diet. If +there are people who can give up eating meat, we can only rejoice in their +noble efforts. In any case, at least we should try to lessen our intake of +meat and not eat it anywhere where it is in scarce supply and our consumption +of it would cause added slaughter. + -- H.H. the Dalai Lama in "The Path to Enlightenment", Snow Lion Publications +~ +The mind is beyond expression, thought and conceptualization, because it is +empty. Yet, there is phenomena and appearance. The objects that you cling to +in this waking reality are dreamlike, but if you do not inquire into the nature +of this dream, you remain attracted as though there were really something here. +Upon examination, you find that these objects have no true existence at all and +are just like space. A practitioner who understands that phenomena lack +inherent existence and resemble space should then examine himself or herself to +discover whether he or she possesses an individual self. Then it will be +discovered that there is no truly existing examiner either. + -- Gyatrul Rinpoche, from "The Generation Stage in Buddhist Tantra", + published by Snow Lion Publications +~ +Life is pleasant. Death is peaceful. It's the transition that's troublesome. + -- Isaac Asimov +~ +In discursive meditations it is imperative that one's growing disenchantment +with mundane existence is complemented with growing confidence in the real +possibility of true freedom and lasting joy that transcends the vicissitudes +of conditioned existence. Without this faith and the yearning for such +liberation, the meditations may easily result in profound depression, in which +everything seems hollow, unreal, and futile. Thus instead of polarizing one's +desires towards the single-pointed pursuit of nirvana, one is reduced to a +debilitating kind of spiritual sloth. + -- B. Alan Wallace, in "Balancing the Mind: A Tibetan Buddhist Approach + to Refining Attention", Snow Lion Publications +~ +I believe that in human actions, the prime mover is motivation. On the spot, +it is important to tackle the symptoms of problems, but in the long run, it is +necessary to look at the motivation and whether there is possibility to change +it. For the long run, this is crucial. As long as the negative motivation is +not changed, then although there might be certain rules and methods to stop +counterproductive actions, human beings have the ability through various ways +to express their negative feeling. Thus, for the long run, we need to look at +our motivation and try to change it. This means that we must try to cultivate +the right kind of motivation and try to reduce the negative motivation. + -- H.H. the Dalai Lama, in "The Art of Peace: Nobel Peace Laureates Discuss + Human Rights, Conflict and Reconciliation", edited by Jeffrey Hopkins, + published by Snow Lion Publications +~ +When we talk of karma or action, it entails action committed by an agent, in +this case, oneself, in the past. So what type of future will come about, to a +large extent, lies within one's own hands and can be determined by the kind of +initiatives that one takes now. Not only that, but karma should not be +understood in terms of passive, static kind of force, but rather in terms of +active process. This indicates that there is an important role for the +individual agent to play in determining the course of the karmic process. +Consider, for instance, a simple act like fulfilling our need for food. In +order to achieve that simple goal one must take action on one's own behalf: one +needs to look for food, to prepare it, to eat it. This shows that even a +simple act, even a simple goal is achieved through action. + -- H.H. the Dalai Lama, in "Healing Anger: The Power of Patience from a + Buddhist Perspective", Snow Lion Publications +~ + Nothing is easier than to bring others down to our level, particularly in +cultures where it is taken as a sign of keen intelligence to view every person +and situation as a challenge to "name the ten things wrong with this picture." +The presence of Guru Rinpoche in so many forms in our world makes us question +life in a way pre-1959 persons rarely had to. Life isn't the same after +meeting the Dalai Lama or Kyentse Rinpoche and others. We can't erase them +from our minds, as inconvenient as these open doors to enlightenment might be. +We had other plans; we didn't ask to see so vividly another totally different +horizon. + The question, "How can I integrate this into my daily life?" doesn't plumb +the depth of the inquiry. I have translated for lamas in North America, +Europe, and Asia, and have found this to be the typical North American +question. I think the best answer is, "You can't; don't even try." But I +have to wonder about the question itself. What do you do when an event or an +encounter changes your life? If you won a 10,000,000 dollar jackpot, if a +dear friend dies, or if you fall deeply in love, do you ask, "How can I +integrate this into my daily life?" Some events change us, are earth- +shattering, and are not meant to be integrated into what can sometimes feel +like a rat race existence. Meeting Guru Rinpoche is one such event. + -- Ngawang Zangpo, in "Guru Rinpoche: His Life and Times", Snow Lion Pub. +~ +Now in terms of the actual practice, when one is immersed in the contemplation +of the clear light, since all dualistic appearances vanish, it becomes +impossible to distinguish the object from the consciousness perceiving it. +They seem to become as if they were one, like water mixed with water. Of +course, strictly speaking, there are two entities, subject and object, but +within the experience of the clear light this duality is lost. + -- H.H. the Dalai Lama, in "Answers: Discussions with Western Buddhists", + published by Snow Lion Publications +~ +Guardians of the teachings + There are eight principal classes of Guardians each with many subdivisions. +Some are highly realized beings, others not realized at all. Every place-- +every continent, country, city, mountain, river, lake or forest--has its +particular dominant energy, or Guardian, as have every year, hour and even +minute: these are not highly evolved energies. The various teachings all have +energies which have special relationships with them: these are more realized +Guardians. These energies are iconographically portrayed as they were +perceived when they manifested to masters who had contact with them, and their +awesome power is represented by their terrifyingly ferocious forms, their many +arms and heads, and their ornaments of the charnel ground. As with all the +figures in tantric iconography, it is not correct to interpret the figures of +the guardians as merely symbolic, as some Western writers have been tempted to +do. Though the iconographic forms have been shaped by the perceptions and +culture of those who saw the original manifestation and by the development of +tradition, actual beings are represented. + -- Chogyal Namkhai Norbu, "The Crystal and the Way of Light: Sutra, Tantra + and Dzogchen, Teachings of Chogyal Namkhai Norbu", compiled and edited by + John Shane, published by Snow Lion Publications +~ +5. When others out of jealousy treat me unreasonably, with abuse, slander, and +so on, I will learn to take all loss and offer the victory to them. When any +type of deserved or undeserved slander, prompted by jealousy, and so forth, +and unpleasant verbal abuse [comes to one], do not lose patience. Keep a +peaceful mind. Further, when problems arise, do not say "It is his fault, +not mine!" Accept the blame, as did Geshe Lang ri thang pa. [Reflect:] + "Whoever created this mess, it includes me! I have [a hand in it] also." +The reason for this is that we must endeavor toward generosity and the various +modes of ethical behavior, for the sake of purifying our many misdeeds and +completing the accumulations [of merit and wisdom]. + Therefore, when one shows kindness to slanderers, even if one does not +deserve the abuse and slander, it is said to be necessary for purifying our +misdeeds when problems arise. Taking all blame on ourselves prevents our evil +karma from arising. + Geshe Lang ri thang pa speaks of a person from the Valley of Phan who +sometimes gave a little butter cake to the Lamas, and at other times slandered +them for no reason. The Lamas regarded him with great kindness. This +cleansed their misdeeds and helped their accumulation of the two collections. +They claimed that his slanderous talk was great. Shantideva's +bodhisattvacaryavatara says: + Therefore, since patience can be generated + In dependence upon a very hate filled mind + And because it is the cause of patience, + Make offerings to it as if it was the most excellent doctrine! +More correct even than this statement is that ethics and patience lead to +great merit, and to the end of misdeeds such as anger. Therefore it is said +that the hardest practice is patience. [Learn] the patience that is keeping +still no matter what happens. + -- Lo Jong +~ +I not only use all the brains that I have, but all that I can borrow. + -- Woodrow Wilson (1856-1924) +~ +All sentient beings are exactly the same in that every one desires happiness +and seeks to avoid misery. We are not isolated entities disconnected from +each other. The happiness and suffering of other beings affect us. This +mutual relation is obvious. Sentient beings have been kind and have benefited +us directly and indirectly throughout beginningless time. These beings are +intrinsically the same as us in their pursuit of happiness and effort to avoid +suffering. Thus, it is essentially logical for us to train in cultivating an +impartial attitude wishing for the happiness of all beings. + -- H.H. the Dalai Lama, "Stages of Meditation", Snow Lion Pub. +~ + Our exaggerated sense of self and our compulsion to find happiness for this +larger-than-life self we have fabricated cause us to ignore, neglect and harm +others. Of course, it is our right to love and take care of ourselves, but +not at the expense of others. While "As long as I'm alright" is our motto, +we have no hesitation in acting with total disregard for others. + We may find this description of self-concern altogether too crass to apply +to us. "I'm not like that," we object, but though we may not consciously +think in this way, when self-concern is operating, our behavior shows a cold +indifference to others. Conflicts between partners, parents and children and +with other family members, conflicts between students and teachers and on a +larger scale within and between countries have their source in personal and +collective self-concern. + Buddhas and Bodhisattvas see clearly that our neglect of others, our self- +preoccupation and our disregard for the connection between actions and their +effects are responsible for all our miseries. + -- Geshe Sonam Rinchen, in "The Three Principal Aspects of the Path: An + Oral Teaching", translated and edited by Ruth Sonam, published by + Snow Lion Publications +~ + Ordinarily, it is difficult to remember one's past life. Such recollections +seem to be more vivid when the child is very young, such as two or three, and +in some cases even younger. ...When the present body is fully formed, the +ability to recall past life seems to diminish. + The mental associations with this life become increasingly dominant. There +is a close relationship during the first few years of one's life with the +continuum of consciousness from the previous life. But as experiences of this +life become more developed and elaborate, they dominate. + It is also possible within this lifetime to enhance the power of the mind, +enabling one to reaccess memories from previous lives. Such recollection +tends to be more accessible during meditative experiences in the dream state. +Once one has accessed memories of previous lives in the dream state, one +gradually recalls them in the waking state. + -- H.H Dalai Lama, in "Consciousness at the Crossroads: Conversations with + The Dalai Lama on Brain Science and Buddhism", Edited by Zara Houshmand, + Robert B. Livingston and B. Alan Wallace, Snow Lion Publications +~ +Relaxation involves a kind of awareness which reverses the normal tendency +that we have. Because, as we have seen, this ordinary sense of self that we +have lacks inherent self existence, it has to keep constructing itself and +that requires a particular kind of effort. The ego's root feeling is that if +I do not hold myself together there will be a falling apart into something +chaotic and difficult. So there is anxiety, an energetic anxiety which is +located in the body, in the whole energetic system of the body and +interpersonal turbulence reminds us again and again "If I don't keep it +together, I will get in trouble." The belief in reincarnation indicates that +for many lifetimes we have been caught up in this anxiety, this nervous +contraction which is holding our ordinary grasping sense of self in place. + -- from "Being Right Here: A Dzogchen Treasure Text of Nuden Dorje entitled + 'The Mirror of Clear Meaning,'" commentary by James Low, Snow Lion Pub. +~ +Buddhas are always striving for the welfare of beings migrating in cyclic +existence. In every hour and minute they create limitless forms of welfare +for beings throughout billions of emanations of their body, speech and mind. +For instance, in this aeon--an aeon being a period of an extremely great +number of years--they will appear in the aspect of one thousand supreme +Emanation Bodies (Nirmanakaya) as Buddhas, and each will have his own new +teaching. + -- H.H. the Dalai Lama, in "The Buddhism of Tibet", Snow Lion Publications +~ +Spiritual Mentors + The Buddhist teachings differentiate between flash insights (nyam, nyams), +and stable realizations (togpa, rtogs-pa). A flash insight does not make a +significant change in one's life, but may lead in that direction. A stable +realization, on the other hand, whether it be partial or complete, actually +produces a noticeable improvement that lasts. The distinction we are drawing +here between Dharma instructors and spiritual mentors derives from this +difference. Dharma instructors may have either insight or realization, +whereas spiritual mentors need to have some level of stable realization. + -- Alexander Berzin, in "Relating to a Spiritual Teacher: Building a + Healthy Relationship, published by Snow Lion Publications +~ +Not even computers will replace committees, because committees buy computers. + -- Edward Shepherd Mead +~ +If you can't do what you want, do what you can. -- Lois McMaster Bujold +~ +Human history is work history. The heroes of the people are work heroes. + -- Meridel le Sueur +~ +It is not the man who has too little, +but the man who craves more, that is poor. + -- Seneca +~ +Drive-in banks were established so most of the +cars today could see their real owners. + -- E. Joseph Crossman +~ +I base my fashion taste on what doesn't itch. -- Gilda Radner +~ +It's a job that's never started that takes the longest to finish. + -- J.R.R. Tolkien +~ +You can live to be a hundred if you give up all the things that +make you want to live to be a hundred. + -- Woody Allen +~ +No man needs a vacation so much as the man who has just had one. + -- Elbert Hubbard +~ +Do not look where you fell but where you slipped. +~ +There is nothing more demoralizing than a small but adequate income. + -- Edmund Wilson +~ +Measure not the work until the day's out and the labor done. + -- Elizabeth Barret Browning +~ +Don't judge each day by the harvest you reap but by the seeds that you plant. + -- Robert Louis Stevenson +~ +We are living in a world today where lemonade is made from +artificial flavors and furniture polish is made from real lemons. + -- Alfred E. Newman, Mad Magazine +~ +The more I want to get something done, the less I call it work. + -- Richard Bach +~ +You are what you are--and not what people think you are. -- O.W. Polen +~ +Without friends no one would choose to live, though he had all other goods. + -- Aristotle +~ +From what we get, we can make a living; what we give, however, makes a life. + -- Arthur Ashe +~ +I touch the future. I teach. -- Christa McAuliffe +~ +Curiosity is one of the permanent and +certain characteristics of a vigorous mind. + -- Samuel Johnson +~ +People ask for criticism, but they only want praise. -- W. Somerset Maugham +~ +Money is like muck, not good except it be spread. -- Sir Francis Bacon +~ +The problem is not that there are problems. The problem is expecting +otherwise and thinking that having problems is a problem. + -- Theodore Rubin +~ +We can lick gravity, but sometimes the paperwork is overwhelming. + -- Wernher von Braun +~ +The illiterate of the 21st century will not be those who cannot read and +write, but those who cannot learn, unlearn and relearn. + -- Alvin Toffler +~ +Wealth is the possession of whatever gives us happiness, +contentment or a sense of significance. + -- Ernest Wilson +~ +To love oneself is the beginning of a lifelong romance. -- Oscar Wilde +~ +I bought some batteries, but they weren't included. -- Steven Wright +~ +The ability to delude yourself may be an important survival tool. + -- Jane Wagner +~ +People who work sitting down get paid +more than people who work standing up. + -- Ogden Nash +~ +It is not the mountain we conquer but ourselves. -- Sir Edmund Hillary +~ +What we should ask of ourselves is growth, not perfection. -- Pat Boone +~ +Talk is cheap because supply exceeds demand. -- Unknown +~ +The purpose of life is to fight maturity. -- Dick Wertheimer +~ +Your true value depends entirely on what you are compared with. -- Bob Wells +~ +A good mind possesses a kingdom. -- Lucius Anaaeus Seneca +~ +I have learned that success is to be measured not so much by the +position that one has reached in life as by the obstacles he has +overcome while trying to succeed. + -- Booker T. Washington +~ +An intelligence test sometimes shows a man how smart +he would have been not to have taken it. + -- Laurence J. Peter +~ +I have not lost my mind--it's backed up on disk somewhere. -- Unknown +~ +Wise men talk because they have something to say; +fools, because they have to say something. + -- Plato +~ +Don't waste a thousand dollars' worth of emotion over a 5-cent triviality. + -- Anonymous +~ +Fortune does not change men, it unmasks them. -- Suzanne Necker +~ +Some people are born on third base and go +through life thinking they have hit a triple. + -- Barry Switzer +~ +I can't understand why people are scared of new ideas. +I'm frightened of the old ones. + -- John Cage +~ +It is better to have a permanent income than to be fascinating. + -- Oscar Wilde +~ +A verbal contract isn't worth the paper it's written on. -- Samuel Goldwyn +~ +When we practice, initially, as a basis we control ourselves, stopping the bad +actions which hurt others as much as we can. This is defensive. After that, +when we develop certain qualifications, then as an active goal we should help +others. In the first stage, sometimes we need isolation while pursuing our +own inner development; however, after you have some confidence, some strength, +you must remain with, contact, and serve society in any field--health, +education, politics, or whatever. + +There are people who call themselves religious-minded, trying to show this by +dressing in a peculiar manner, maintaining a peculiar way of life, and +isolating themselves from the rest of society. That is wrong. A scripture of +mind-purification (mind-training) says, "Transform your inner viewpoint, but +leave your external appearance as it is." This is important. Because the +very purpose of practicing the Great Vehicle is service for others, you should +not isolate yourselves from society. In order to serve, in order to help, you +must remain in society. + -- H.H. the Dalai Lama, in "The Dalai Lama, A Policy of Kindness", + Snow Lion Publications +~ + The indivisible nature of mind is said to possess a "mobile quality." This +mobile quality is described as currents of energy which flow through the +channels of various parts of the body, presiding over physical as well as +mental functions, and pass through the nostrils as breathing. Such currents +of energy, called "winds" (rlung, vayu), serve as the bridge between body and +mind. + The winds are a blend of two types of energy, one associated with +emotionality, called karmic or conditioned wind (las kyi rlung), and the other +related to the original state of the individual, called pristine awareness +wind (ye shes kyi rlung). Distinguished in terms of the three principles, +darkness (tamas), mobility (rajas), and buoyancy (sattva), winds are of three +types: wind of Rahu, solar wind, and lunar wind. Moreover, the winds are +differentiated as the five root winds (rtsa ba'i rlung), the natures of the +five elements, and five branch winds (yan lag gi rlung), produced through the +five elemental transformations. The winds of the five elements, or five +mandalas, flow back and forth through the right and left nostrils in the order +of generation of the elements and of birth (first space, then wind, fire, +water, earth) and in the order of dissolution of the elements and of death +(first earth, then water, and so on), respectively. In one day, they are +exhaled and inhaled 21,600 times, divided between the two nostrils, a time +corresponding to eight periods or watches (thun). The outward movement of +these energy currents as the breath diminishes the strength of the wind +associated with pristine awareness. Therefore, when outward movement +increases, there occur signs of death. If the winds are held inside, +pristine awareness wind is strengthened. Hence, many extraordinary powers +such as longevity are gained through breath control techniques for "holding +the winds" in the central channel. + -- Jamgon Kongtrul Lodro Taye, in "The Treasury of Knowledge, Book Six, + Part Four: Systems of Buddhist Tantra, The Indestructible Way of + Secret Mantra" +~ +When the power of love overcomes the love of power, the world will know peace. + -- Jimi Hendrix +~ + ...you should have the deep conviction that cessation of the sufferings and +the delusions is possible, and also that it is possible within your mind. +True cessation is a state where you have destroyed the delusions at their root +so that there remains no potential for their re-emergence. Such a cessation +can be realized only through the true paths that penetrate into the nature of +reality. + When you develop this conviction, you will also be able to develop faith in +a being who has really mastered cessation, who is the Buddha--a person who has +fully accomplished the realization of the dharma. If you contemplate along +such lines, you will be able to develop a very deep faith and conviction in +Buddha Shakyamuni and see him as an incomparable master. + What distinguishes Buddhist practitioners from others is the factor of +taking refuge. But merely seeking a refuge out of the fear of suffering is +not unique to Buddhists; non-Buddhists could also have such a motivation. The +unique practice of refuge that Buddhists should have is that of taking refuge +in the Buddha out of a deep conviction in his exceptional qualities and +realizations. If you think in such terms you will be able to understand Lama +Tsongkhapa's profound praise of Buddha Shakyamuni: "Those who are far from his +doctrine always reinforce the illusion of self-existence that they have within +themselves, whereas those who follow his guidance will be able to free +themselves from such confusions." + -- H.H. the Dalai Lama, in "The Path to Bliss", Snow Lion Publications +~ + ...they say that the initial realization of the nature of the mind is the +first breakthrough. It's a very important point in all Buddhist schools. At +that moment, you cease to be an ordinary person. You become in Buddhist +parlance an arya, a noble one. It doesn't mean you are finished. It doesn't +mean you are a high level bodhisattva. We can fall back from this. But +still, this is a big breakthrough. We now understand what is true and what is +not true. We don't have to take it all on faith any more. It is a direct +non-dual experience. The point is that it is very easy. It's not difficult, +and it's not something that can only be attained after years and years of +practice. + Our main obstacle is the fact that we don't know how to relax our minds +enough to be open to this experience. In the back of our minds we keep +thinking this is something so difficult and so advanced. For this reason we +don't recognize what is in front of our face. This is why a teacher can be +extraordinarily helpful. A teacher living within that realization is able-- +if the mind of the disciple is completely open--to transmit his or her +experience. The problem here is that we have too many hopes and fears; it +creates a barrier. It is very hard to be open. You can't just will it. + -- Ani Tenzin Palmo, in "Reflections on a Mountain Lake: Teachings on + Practical Buddhism" +~ +The Level of Initial Capacity + +All the essential spiritual practices related primarily to the achievement of +rebirth in the higher realms belong to what Atisha calls the 'small capacity'. + +Verse 3 + Know that those who by whatever means + Seek for themselves no more + Than the pleasures of cyclic existence + Are persons of the least capacity. + [Atisha's Lamp for the Path to Enlightenment] + +...the principal means for attaining birth in the higher realms is the ethical +discipline of refraining from the ten negative actions of body, speech and +mind. These comprise three actions of the body--killing, stealing and sexual +misconduct; four verbal actions--lying, divisive speech, harsh speech and +frivolous speech; and three mental actions--covetousness, ill-will and +harbouring wrong views. To live an ethically sound life, it helps to remind +ourselves of what are known as the four reflections, namely the preciousness +of human life; the inevitability of our death and the uncertainty of the time +of death; the infallibility of the law of cause and effect and the workings of +karma; and understanding the nature of suffering. Concerning the first +reflection, some Tibetan masters have said that when we contemplate the +preciousness of this human existence, we should literally cultivate the +determination to make our human life something precious in itself, rather than +allowing it to be wasted or to become a cause of future suffering. + +Contemplating these four reflections gives us the courage to engage earnestly +in the practice of the Dharma in order to free ourselves from the possibility +of rebirth in the lower realms. This involves a process of training our mind, +not just at the mental level but also at the level of our emotions and +actions. Living an ethical life is not a case of adhering to a set of +regulations imposed on us from outside, such as the laws of a country. Rather +it involves voluntarily embracing a discipline on the basis of a clear +recognition of its value. In essence, living a true ethical life is living a +life of self-discipline. When the Buddha said that 'we are our own master, we +are our own enemy', he was telling us that our destiny lies in our own hands. + + -- H.H. the Dalai Lama, in "Lighting the Way", Snow Lion Publications +~ +In his autobiography Freedom in Exile, His Holiness the Dalai Lama speaks of +his attachment as a child to the monastery's Master of the Kitchen, +commenting, "I sometimes think that the act of bringing food is one of the +basic roots of all relationships." And the connection between giving food and +understanding the interrelationship of all life is recognized also in stories +about the belated discovery of an enlightened master who lived humbly as a +monastery cook; or the stories of a great lama who gathers his disciples to +test their progress, only to discover that the most highly realized of all is +the cook, who has neither meditated nor studied, but who simply served the +others. + May you have long life, + may the house be filled with grain, + and may you have the luck + to make use of this abundance. + -- Tibetan drinking song + + -- Tsering Wangmo and Zara Houshmand, in "The Lhasa Moon Tibetan Cookbook" +~ +The Level of Middling Capacity + +In the following verse Atisha describes the characteristics of spiritual +trainees of the middling capacity. + +Verse 4 + Those who seek peace for themselves alone, + Turning away from worldly pleasures + And avoiding destructive actions + Are said to be of middling capacity. + -- Atisha's Lamp for the Path to Enlightenment + +The phrase "destructive actions" refers to the afflictions that, together with +karma, constitute the origin of suffering. This is why practitioners at the +level of middling capacity concentrate on the spiritual practices that are +primarily aimed at the elimination of the afflictions. Broadly speaking, +these practices fall into two categories. One is training the mind to +cultivate the genuine desire to gain freedom from cyclic existence, which is +often referred to as the cultivation of renunciation. The other is +cultivating the path to bring about the fulfillment of that wish for +renunciation. In order to train one's mind in this way, one needs to reflect +upon the defects of cyclic existence and to develop an understanding of the +causation chain of karma and the afflictions. Through these reflections one +cultivates the wish to gain freedom and then embarks upon the path to bring +about that freedom. + -- H.H. the Dalai Lama, in "Lighting the Way", Snow Lion Publications +~ +Number of Recitations + The sixth section of the yoga of speech concerns measuring the accumulation +of mantra recitations. How do you know when you have recited enough of a +particular mantra? Generally speaking, you should count a mantra until you +achieve some common spiritual power and ideally until you achieve the supreme +spiritual attainment. Wouldn't that be the best way? + After all, if you are really hungry, don't you eat until you are satisfied? +Similarly, if you plan a trip to San Francisco, you want to travel until you +arrive at your destination. You would not travel halfway and be satisfied +with that, would you? In the same way, when you recite a mantra, you have a +specific goal in mind: to gain the supreme spiritual attainment--buddhahood. +Wouldn't it be wise to keep on reciting the mantra until you have achieved +your goal, or at least until you achieve some perceptible improvement? + -- Gyatrul Rinpoche, in "The Generation Stage in Buddhist Tantra", published + by Snow Lion Publications +~ +The Level of Great Capacity + +Atisha continues his discussion on the three capacities by turning his +attention to spiritual trainees at the highest level. + +Verse 5 + Those who, through their personal suffering, + Truly want to end completely + All the suffering of others + Are persons of supreme capacity. + -- Atisha's Lamp for the Path to Enlightenment + +Practitioners at this level use their deep understanding of the nature of +suffering, derived from reflection on their personal experience, to recognise +the fundamental equality of oneself and others insofar as the desire to +overcome suffering is concerned. This then leads to the arising of a +spontaneous wish to free all sentient beings from their suffering, a wish +which becomes the powerful impetus for engaging in spiritual practices aimed +at bringing about this altruistic objective. + +The most important practice in relation to this altruistic goal is the +generation of bodhicitta, the altruistic aspiration to attain buddhahood for +the benefit of all beings. + -- H.H. the Dalai Lama, in Lighting the Way, Snow Lion Publications +~ +The Meaning of Empowerment + As for empowerment in general, what does the term wang, or empowerment, +signify? To begin with, our fundamental nature--what we term "the buddha +nature", or tathagatagarbha, the very nature of our mind, is inherently +present within us as a natural attribute. This mind of ours, the subject at +hand, has been going on throughout beginningless time, and so has the more +subtle nature of that mind. On the basis of the continuity of that subtle +nature of our mind rests the capacity we have to attain enlightenment. This +potential is what we call "the seed of buddhahood", "buddha nature", "the +fundamental nature", or tathagatagarbha. We all have this buddha nature, each +and every one of us. For example, this beautiful statue of Lord Buddha here, +in the presence of which we are now sitting, is a representation that honours +someone who attained buddhahood. He awakened into that state of enlightenment +because his nature was the buddha nature. Ours is as well, and just as the +Buddha attained enlightenment in the past, so in the future we can become +buddhas too. + In any case, there dwells within us all this potential which allows us to +awaken into buddhahood and attain omniscience. The empowerment process draws +that potential out, and allows it to express itself more fully. When an +empowerment is conferred on you, it is the nature of your mind--the buddha +nature--that provides a basis upon which the empowerment can ripen you. +Through the empowerment, you are empowered into the essence of the buddhas of +the five families. In particular, you are "ripened" within that particular +family through which it is your personal predisposition to attain buddhahood. + So, with these auspicious circumstances established in your mindstream, +and when you reflect on what is taking place and maintain the various +visualizations, the conditions are right for the essence of the empowerment to +awaken within you, as a state of wisdom which is blissful yet empty--a very +special state that is the inseparability of basic space and awareness. As you +focus your devotion in this way, it allows this special quality of mind, this +new capability, as it were, to awaken. + -- H.H. the Dalai Lama, in "Dzogchen: The Heart Essence of the Great + Perfection", published by Snow Lion Publications +~ +Value Our Good Circumstances + We often focus on a few circumstances in our life that aren't going well +instead of all those that are. Although we all have problems, when we over- +emphasize their importance, we easily begin thinking that we are incapable and +worthless. Such self-hatred immobilizes us and prevents us from developing +our good qualities and sharing them with others. + When we look at the broad picture, however, we can see many positive things +in our life. We can rejoice that we are alive and appreciate whatever degree +of good health we have. We also have food (often too much!), shelter, +clothing, medicine, friends, relatives, and a myriad of good circumstances. +Many of the people reading this book live in peaceful places, not in war-torn +areas. Many have jobs they like, and family and friends they appreciate. We +shouldn't take these for granted. Most importantly, from a spiritual +viewpoint, we have access to an authentic path, qualified teachers to guide +us, and kind companions who encourage us. We have genuine spiritual +aspirations and the time to cultivate these. Thinking about these good +conditions one by one, we will be filled with joy, and any sense of being +incapable and hopeless will vanish. + -- Thubten Chodron, in "Working with Anger", Snow Lion Publications +~ +15. This fresh state of present awareness, unspoiled + + This fresh state of present awareness (da lta'i shes pa) + Unspoiled by dualistic thoughts, + Effortlessly sustained in the natural state, + Is Buddha Kuntu Zangpo's wisdom mind. + + Do not hope or fear for good or bad outcomes. + Regardless of what formulation of thought occurs, + they arise and are liberated simultaneously; + Their essential nature is empty awareness. + Reach that unmoving, unassailable state. + +I, Jnana, spoke these words immediately In response to +Zangmo's supplication. May this be virtuous! + -- from "Wisdom Nectar: Dudjom Rinpoche's Heart Advice" translated and + introduced by Ron Garry, published by Snow Lion +~ + Concern for others to be happy and compassion wishing them to be free from +suffering are needed not only as the basis for a bodhichitta motivation for +mahamudra* practice, but also for keeping that practice on course to its +intended goal. When we have changed our focus in life from the contents of +our experience to the process of experience, there is great danger of becoming +fixated on mind itself. This is because the direct experience of mind itself +is totally blissful--in a calm and serene sense--and entails extraordinary +clarity and starkness. Concern for others is one of the strongest forces that +brings us back down to earth after having been up in the clouds. Although all +appearances exist as a function of mind, other beings do not exist merely in +our head. Their suffering is real and it hurts them just as much as ours +hurts us. + Furthermore, to be concerned about someone does not mean to be frantically +worried about this person. If we are fixated on our child's problems at +school, for example, we lose sight that whatever appearance of the problems +our mind gives rise to is a function of mind. Believing the appearance to be +the solid reality "out there," we again feel hopeless to do anything and thus +become extremely anxious and tense. We worry to the point of becoming sick +and we over-react toward our child, which does not help. If we focus instead +on the process of mind that gives rise to our perception of the problem as if +it existed as some horrible monster "out there," we do not eliminate our +concern for our child, only our worry. This allows us to take whatever clear +and calm action is necessary to alleviate the problem. Thus not only is +compassion necessary for successful practice of mahamudra, but mahamudra +realization is necessary for successful practice of compassion. + -- H.H. the Dalai Lama, in "The Gelug/Kagyu Tradition of Mahamudra", + published by Snow Lion Publications + * "Mahamudra" is a Sanskrit word meaning "great seal" and refers to the + nature of all phenomena. Mahamudra also refers to sophisticated Buddhist + systems of meditation and practice to realize this great sealing nature. +~ +Better to be deprived of food for three days, than tea for one. + -- Chinese Proverb +~ +45. This fresh present knowing, unbound + +This fresh present knowing, +Unbound by the intellect that clings to meditation, +Is naked unobstructed non-meditation. +Relax at ease +And settle in the state of naturalness. +This is the meaning of realization of meditation. + +When thoughts move, let them. +Movement arises and is liberated without a trace. +When there is no movement, don't search for it. +This is empty luminosity, naked empty awareness. +Tantric practice without suppression or + cultivation of thoughts +Brings the accomplishment of the destruction + of hope and fear. + +There is nothing more to add to this. +Madman Dudjom said this: +Let it remain like this in your heart. + -- from "Wisdom Nectar: Dudjom Rinpoche's Heart Advice", translated and + introduced by Ron Garry, published by Snow Lion Publications +~ + ...karma refers not only to our actions but, more importantly, to the +motivation or intention behind them. The acts themselves are not the primary +cause of our suffering; rather, it arises from the world of our intentions or, +in other words, from our thoughts and emotions. These afflictive states of +mind underlie our negative karma and are therefore the source of our +suffering. + + Obviously, these afflictions won't go away simply by saying prayers or +wishing them away; they can only be eliminated by cultivating their +corresponding remedies or antidotes. To understand how this process of +applying the antidote works we can observe our physical world. For instance, +we can contrast heat and cold: if we are suffering from the effects of too +cold a temperature, then we increase the thermometer on our heater or air- +conditioning unit and adjust it to our comfort. Thus, even in the physical +world we can see instances where opposing forces counter each other. + + ...From our own personal experience we recognise that anger and hostility +disturb our peace of mind and, more importantly, that they have the potential +to harm others. Conversely, we recognise that positive emotions like +compassion and loving kindness can engender in us a deep sense of peace and +serenity, beneficial results that we can extend to others as well. This +appreciation of their great value naturally leads to a desire to cultivate +these positive emotions. It is through this gradual process that the +antidotes work in decreasing and eventually eliminating their opposing forces +in the mental realm, the realm of our thoughts and emotions. + -- H.H. the Dalai Lama, in "Lighting the Way", published by Snow Lion +~ +Starting Dzogchen Practice + When we start to practice, in order to grasp the normal mind, our first +practice consists in engaging our mind. For example, if we have a problem of +some kind, we may go to a movie to distract ourselves from our problem. +Likewise when we start to practice, we try to calm down our problem-creating +mind in order to be able to observe the nature of thought. The observation of +the arising, abiding, and dissolving of thought in the empty state of the mind +is an essential practice in Dzogchen in order to discover that moving thoughts +are of the same nature as the thoughtless state of the mind. Since we are not +accustomed to meditation, it seems very difficult, and every slight sound or +movement, outside or inside the mind itself, becomes a major distraction +interfering with our ability to continue to practice. In order to overcome +this problem, we engage the mind in a practice so that it is not so easily +distracted, by focusing attention so that the movement of the mind caused by +thought or sense perception does not have the power to divert our +concentration. This first stage, of grasping the mind, is concentration +practice, described in detail in the Bonpo 'Ati' system. + -- Tenzin Wangyal Rinpoche, in "Wonders of the Natural Mind: The Essence of + Dzogchen in the Native Bon Tradition of Tibet", Snow Lion Publications +~ +Buddhism has the characteristics of what would be expected in a cosmic +religion for the future: it transcends a personal God, avoids dogmas and +theology; it covers both the natural & spiritual, and it is based on a +religious sense aspiring from the experience of all things, natural and +spiritual, as a meaningful unity + -- Albert Einstein in "Albert Einstein: The Human Side", edited by Helen + Dukas and Banesh Hoffman, Princeton University Press, 1954 +~ +Techniques for Improvement + All of us have attained a human life; we are, in a sense, incomparable among +the various types of sentient beings, as we are able to think about many +topics with a subtler mind and are endowed with vaster capabilities. Dogs, +birds, and so forth do communicate, but only humans can settle and ascertain +deep topics on the basis of words; it is obvious that there are no other +sentient beings capable of as many thoughts and techniques. Nowadays, humans +are engaging in many activities that were not even objects of thought a +century or two ago. The metaphors of the poets of the past, such as "the +wonderful house of the moon", are becoming actualities. + ...People have made great effort right up to this century, thinking to +become free from suffering, but we cannot point to even one person in the +world, no matter how rich he or she is, who has no worry--except for those +who have the inner happiness of renouncing the material way of life. Without +internal renunciation it is difficult to achieve happiness and comfort. + -- H.H. the Dalai Lama, Tsong-ka-pa and Jeffrey Hopkins from "Deity Yoga", + published by Snow Lion Publications +~ +Everything has beauty, but not everyone sees it. -- Confucius +~ +Vegetables are a must on a diet. I suggest carrot cake, +zucchini bread and pumpkin pie. + -- Jim Davis (Garfield) +~ +A painting in a museum hears more ridiculous opinions +than anything else in the world. + -- Edmond de Concourt +~ +Knowledge speaks, but wisdom listens. -- Jimi Hendrix +~ +There is nothing either good or bad, but thinking makes it so. + -- Hamlet (by William Shakespeare) +~ +People who have what they want are fond of telling people +who haven't what they want that they really don't want it. + -- Ogden Nash +~ +There are only two ways of telling the complete +truth--anonymously and posthumously. + -- Thomas Sowell +~ +The easiest way for your children to learn +about money is for you not to have any. + -- Katharine Whitehorn +~ +If you look good and dress well, you don't need a purpose in life. + -- Robert Pante +~ +Do not go where the path may lead, go instead +where there is no path and leave a trail. + -- Ralph Waldo Emerson +~ +The hardest thing in the world to understand is the income tax. + -- Albert Einstein +~ +I have a simple philosophy. +Fill what's empty. +Empty what's full. +Scratch where it itches. + -- Alice Roosevelt Longworth +~ +Time is what prevents everything from happening at once. + -- John Archibald Wheeler +~ +You know that children are growing up when they start +asking questions that have answers. + -- John J. Plomp +~ +The average man, who does not know what to do with his life, +wants another one which will last forever. + -- Anatole France +~ +Hold fast to dreams, for if dreams die, life is +a broken winged bird that canot fly. + -- Langston Hughes +~ +Spare no expense to save money on this one. -- Samuel Goldwyn +~ +It is absurd to divide people into good and bad. +People are either charming or tedious. + -- Oscar Wilde +~ +If absolute power corrupts absolutely, does absolute +powerlessness make you pure? + -- Harry Shearer +~ +Parents were invented to make children happy +by giving them something to ignore. + -- Ofden Nash +~ +Imagination is the one weapon in the war against reality. + -- Jules de Gaultier +~ +I'm a kind of paranoiac in reverse. +I suspect people of plotting to make me happy. + -- J. D. Salinger +~ +If you find it in your heart to care for +somebody else, you will have succeeded. + -- Maya Angelou +~ +Time is an illusion. +Lunchtime doubly so. + -- Douglas Adams +~ +Acquaintance, n.: +A person whom we know well enough to borrow from, +but not well enough to lend to. + -- Ambrose Bierce, "The Devil's Dictionary" 1911 +~ +The future belongs to those who believe in the beauty of their dreams. + -- Eleanor Roosevelt +~ +Eighty percent of success is showing up. - Woody Allen +~ +Pity the meek, for they shall inherit the earth. -- Don Marquis +~ +The only reason some people get lost in thought +is because it's unfamiliar territory. + -- Paul Fix +~ +A child becomes an adult when he realizes that he has +a right not only to be right but also to be wrong. + -- Thomas Szasz +~ +Everywhere is walking distance if you have the time. -- Steven Wright +~ +Only dull people are brilliant at breakfast. -- Oscar Wilde +~ +It is not in the stars to hold our destiny but in ourselves. + -- William Shakespeare +~ +If you can find something everyone agrees on, it's wrong. -- Mo Udall +~ +I'd like to live as a poor man with lots of money. -- Pablo Picasso +~ +The shortest distance between two points is under construction. + -- Noelie Altito +~ +Lack of money is no obstacle. +Lack of an idea is an obstacle. + -- Ken Hakua +~ +Conceal a flaw, and the world will imagine the worst. + -- Marcus Valerias Martialis +~ +Great minds discuss ideas; +average minds discuss events; +small minds discuss people. + -- Eleanor Roosevelt +~ +I tend to live in the past because most of my life is there. -- Herb Caen +~ +When a man tells you that he got rich through hard work, ask him: "Whose?" + -- Don Marquis +~ +Honest disagreement is often a good sign of progress. -- Mahatma Gandhi +~ +I'm living so far beyond my income that we may +almost be said to be living apart. + -- ee cummings +~ +I try to avoid looking forward or backward, +and try to keep looking upward. + -- Charlotte Bronte +~ +My best friend is the one who brings out the best in me. -- Henry Ford +~ +Without democracy, religion becomes extreme. +With religion, democracy becomes more spiritual. + -- Mohammad Khatami, president of Iran +~ +For fast acting relief, try slowing down. -- Lily Tomlin +~ +A wise man should have money in his head, but not in his heart. + -- Jonathan Swift +~ +Poor people have more fun than rich people, they say; and +I notice it's the rich people who keep saying it. + -- Jack Parr +~ +Whatever you are, be a good one. -- Abraham Lincoln +~ +Education's purpose is to replace an empty mind with an open one. + -- Malcolm Forbes +~ +Never spend your money before you have it. -- Thomas Jefferson +~ +Confusion is always the most honest response. -- Marty Indik +~ +Doing nothing is very hard to do... you never know when you're finished. + -- Leslie Nielsen +~ +Free advice is worth the price. -- Robert Half +~ +A person who trusts no one can't be trusted. -- Jerome Blattner +~ +There is nobody so irritating as somebody with +less intelligence and more sense than we have. + -- Don Herold +~ +He that plants trees loves others besides himself. -- Dr. Thomas Fuller +~ +To find something you can enjoy is far better +than finding something you can possess. + -- Glenn Holm +~ +An intellectual is a man who takes more words than +necessary to tell more than he knows. + -- Dwight D. Eisenhower +~ +Not everything that can be counted counts, and not +everything that counts can be counted. + -- Albert Einstein +~ +Brain, n.: an apparatus with which we think we think. + -- Ambrose Bierce, "The Devil's Dictionary" 1911 +~ +There is always some madness in love. +But there is always some reason in madness. + -- Friedrich Nietzsche +~ +We must learn to live together as brothers or perish together as fools. + -- Martin Luther King Jr. +~ +Good resolutions are simply checks that men +draw on a bank where they have no account. + -- Oscar Wilde +~ +The murals in restaurants are on par with the food in mueseums. + -- Peter De Vries +~ +Money can't buy friends, but it can get you a better class of enemy. + -- Spike Milligan +~ +Friends may come and go, but enemies accumulate. -- Thomas Jones +~ +It is only possible to live happily ever after on a day-to-day basis. + -- Margaret Bonnano +~ +Yesterday is history. +Tomorrow is a mystery. +Today is a gift. +That's why it's called the present. + -- Anonymous +~ +Money is in abundance, where are you? -- Reverend Ike +~ +Santa Claus had the right idea. Visit everyone once a year. + -- Victor Borge +~ +Laziness is nothing more than the habit of resting before you get tired. + -- Jules Renard +~ +You cannot fix what you will not face. -- James Baldwin +~ +There are too many people, and too few human beings. -- Robert Zend +~ +Income tax returns are the most imaginative fiction being written today. + -- Herman Wouk +~ +A good listener is usually thinking about something else. -- Kin Hubbard +~ +If it weren't for baseball, many kids wouldn't +know what a millionaire looked like. + -- Phyllis Diller +~ +Life isn't about finding yourself. Life is about creating yourself. + -- George Bernard Shaw +~ +Everybody likes a kidder, but nobody lends him money. -- Arthur Miller +~ +Always acknowledge a fault. This will throw those in authority off +their guard and give you an opportunity to commit more. + -- Mark Twain +~ +If only we'd stop trying to be happy we'd have a pretty good time. + -- Edith Wharton +~ +If computers get too organized, we can organize +them into a committee--that will do them in. + -- Bradley's Bromide +~ +It is not enough to have a good mind; the main thing is to use it well. + -- Rene Descartes +~ +If we stand tall it is because we stand +on the backs of those who came before us. + -- Yoruba Proverb +~ +The trouble with jogging is that, by the time you realize +you're not in shape for it, it's too far to walk back. + -- Franklin P. Jones +~ +I wonder if other dogs think poodles are members of a weird religious cult. + -- Rita Rudner +~ +By the time we've made it, we've had it. -- Malcolm Forbes +~ +It was such a lovely day I thought it a pity to get up. + -- W. Somerset Maugham +~ +It's not what you look at that matters, it's what you see. + -- Henry David Thoreau +~ +Accomplishing the impossible means only that +the boss will add it to your regular duties. + -- Doug Larson +~ +Government big enough to supply everything you need +is big enough to take everything you have... +The course of history shows that as a government grows, +liberty decreases. + -- Thomas Jefferson +~ +Computers can figure out all kinds of problems, except +the things in the world that just don't add up. + -- James Magary +~ +Traditions are group efforts to keep the unexpected from happening. + -- Barbara Tober +~ +The doors we open and close each day decide the lives we live. + -- Flora Whittemore +~ +When we ask for advice, we are usually looking for an accomplice. + -- Marquis de la Grange +~ +If more of us valued food and cheer and song +above hoarded gold, it would be a merrier world. + -- J.R.R. Tolkien +~ +While I was in Malaysia, I saw a T-shirt depicting a surfboard aloft huge +waves. Sitting on the surfboard was a figure meditating cross-legged. The +slogan read, "Riding the waves of life, be mindful, be happy." That's it. +Awareness. Being present. Knowing thoughts as thoughts, emotions as +emotions. It's just like riding a surfboard. You gradually develop the +poise to cruise along on the roughest seas until, no longer immersed in +the waves, you are riding on top of them. Of course you have to start +with small waves until you get your balance. Then the higher the wave, the +better! Likewise, when we begin to train in awareness, it is better if we +have an atmosphere which is nonthreatening and peaceful. That's why people +go on retreat. That's also a reason why people set aside regular sitting +periods. But once we learn how to be balanced, we become like a surfer who +finds that the bigger the wave, the greater the fun. + -- Ani Tenzin Palmo, in "Reflections on a Mountain Lake: Teachings on + Practical Buddhism", Snow Lion Publications +~ +The word "mantra" means "mind-protection". It protects the mind from ordinary +appearances and conceptions. "Mind" here refers to all six consciousnesses-- +eye, ear, nose, tongue, body and mental consciousnesses--which are to be +freed, or protected, from the ordinary world. There are two factors in mantra +training, pride in oneself as a deity and vivid appearance of that deity. +Divine pride protects one from the pride of being ordinary, and divine vivid +appearance protects one from ordinary appearances. Whatever appears to the +senses is viewed as the sport of a deity; for instance, whatever forms are +seen are viewed as the emanations of a deity and whatever sounds are heard are +viewed as the mantras of a deity. One is thereby protected from ordinary +appearances, and through this transformation of attitude, the pride of being a +deity emerges. Such protection of mind together with its attendant pledges +and vows is called the practice of mantra. + -- H.H. the Dalai Lama, Tsong-ka-pa and Jeffrey Hopkins in "Tantra in Tibet", + published by Snow Lion Publications +~ +There is nothing new under the sun, but there are +lots of old things we don't know. + -- Ambrose Bierce +~ +Win hearts, and you have all men's hands and purses. -- William Cecil Burleigh +~ +Never doubt that a small group of thoughtful citizens can change the world. +Indeed, it is the only thing that ever has. + -- Margaret Mead +~ +He who wonders discovers that this in itself is wonder. -- M. C. Escher +~ +Everything has its wonders, even darkness and silence, and I learn, +whatever state I may be in, therein to be content. + -- Helen Keller +~ +Training in compassion has the capacity to be both profound and vast--both +absolute and relative. Compassion has the quality of being approachable and +at the same time ungraspable. It manifests both the quality of shunyata, +emptiness, or egolessness, as well as the qualities of kindness and joyfulness. +Therefore, from the Mahayana point of view, compassion is the most important +practice we could ever engage in. It can lead us to the full realization of +enlightenment without any need for other practices. + -- from "Trainings in Compassion: Manuals on the Meditation of + Avalokiteshvara", translated by Tyler Dewar under the guidance of The + Dzogchen Ponlop Rinpoche, published by Snow Lion Publications +~ +Question: If the root of all suffering is attachment, are the desire to have a +family and the desire for liberation from suffering contradictory? + +Answer: I think that a distinction should be made between desires that are due +to ignorance and desires that are reasoned. In Tibetan, a difference can be +made between "wish" and "desire"; for instance a Bodhisattva is reborn through +his or her own wishes, not out of desire. Similarly, it is suitable to aspire +toward liberation. Also, persons, such as Foe Destroyers, who have completely +overcome all of the afflictive emotions, have thoughts such as, "Such and such +is good; I need it." Merely such thoughts are not afflictive consciousnesses. +Similarly, if we consider the desire for a family, there are persons practicing +the Bodhisattva path who have families; also, in the scriptures of discipline, +Buddha himself set forth vows for lay persons and vows for monks. Hence, there +is no general prohibition of the wish to have a family. + -- H.H. the Dalai Lama, in "The Dalai Lama at Harvard: Lectures on the + Buddhist Path to Peace", translated and edited by Jeffrey Hopkins, + Snow Lion Publications +~ +In general, most non-Buddhist religions meditate on the deity as being outside +the physical body. In these cases the deity takes the form of a refuge, or of +a protector or messenger. Thus do they meditate, and of course this is fine. +In the Buddhist tradition, however, the deity is not meditated on as being +outside of the physical body. One meditates on the deity as being one's own +essence expressing itself through oneself arising as the deity. One therefore +thinks, "I am the deity," and with this conviction one meditates. Why is it +justifiable to meditate in this manner? ...our own mind is in essence exactly +the same as the mind of a Buddha. In the philosophical treatises this is +sometimes referred to as "sugatagarbha" or "buddha-nature". + -- Khenchen Thrangu Rinpoche, in "Everyday Consciousness and Buddha- + Awakening", translated and edited by Susanne Schefczyk, published by + Snow Lion Publications +~ + If things did in fact exist the way they appear--if things did exist so +concretely--then when one looked into and investigated them, this inherent +existence should become even clearer, more obvious. However, when you seek +for the object designated, you cannot find it under analysis. + ...[That] which gives rise to the appearance of I is mind and body, but when +you divide this into mind and body and look for the I, you cannot find it. +Also the whole, body, is designated in dependence upon the collection of parts +of the body; if you divide this into its parts and look for the body, you +cannot find it either. Even the most subtle particles in the body have sides +and hence parts. Were there something partless, it might be independent, but +there is nothing that is partless. Rather, everything exists in dependence on +its parts... There is no whole which is separate from its parts. + ...No matter what the phenomenon is, internal or external, whether it be +one's own body or any other type of phenomenon, when we search to discover +what this phenomenon is that is designated, we cannot find anything that is it. + ...However, these things appear to us as if they do exist objectively and in +their own right, and thus there is a difference between the way things appear +to our minds and the way they actually exist... Since phenomena appear to us +in a way that is different from what we discover when analysing, this proves +that their concrete appearance is due to a fault of our minds. + -- The Fourteenth Dalai Lama, His Holiness Tenzin Gyatso, translated and + edited by Jeffrey Hopkins, co-edited by Elizabeth Napper, from "Kindness, + Clarity, and Insight" published by Snow Lion Publications +~ +The Buddhist notion of diligence is to delight in positive deeds. Its +opposite, called "le lo" in Tibetan, has three aspects. Le lo is usually +translated as "laziness," though only its first aspect refers to laziness as +we usually understand it. The first aspect is not doing something because of +indolence, even though we know that it is good and ought to be done. The +second aspect is faintheartedness. This comes about when we underestimate our +qualities and abilities, thinking, "I'm so incompetent and weak. It would be +good to do that, but I could never accomplish it." Not having the confidence +of thinking, "I can do it," we end up doing nothing. The third aspect refers +to being very busy and seeming diligent, but wasting time and energy on +meaningless activities that will not accomplish anything in the long run. +When we do many things for no real purpose, we fail to focus on what is truly +worthwhile and our path has no clear direction. When we refrain from these +three aspects of laziness, we are diligent. + -- Ringu Tulku Rinpoche, in "Daring Steps Toward Fearlessness: The Three + Vehicles of Buddhism", edited and translated by Rosemarie Fuchs, + published by Snow Lion Publications +~ + All events and incidents in life are so intimately linked with the fate of +others that a single person on his or her own cannot even begin to act. Many +ordinary human activities, both positive and negative, cannot even be +conceived of apart from the existence of other people. Even the committing of +harmful actions depends on the existence of others. Because of others, we +have the opportunity to earn money if that is what we desire in life. +Similarly, in reliance upon the existence of others it becomes possible for +the media to create fame or disrepute for someone. On your own you cannot +create any fame or disrepute no matter how loud you might shout. The closest +you can get is to create an echo of your own voice. + Thus interdependence is a fundamental law of nature. Not only higher forms +of life but also many of the smallest insects are social beings who, without +any religion, law, or education, survive by mutual cooperation based on an +innate recognition of their interconnectedness. The most subtle level of +material phenomena is also governed by interdependence. All phenomena, from +the planet we inhabit to the oceans, clouds, forests, and flowers that +surround us, arise in dependence upon subtle patterns of energy. Without +their proper interaction, they dissolve and decay. + -- Tenzin Gyatso, the Fourteenth Dalai Lama, in "The Compassionate Life" +~ +Going for Refuge to the Three Jewels + ...a buddha is someone who has attained full enlightenment through the +cultivation of compassion and the wisdom of no-self, the absence of self- +existence. From our discussion we also saw how the Dharma jewel is to be +understood as the path by which we can gradually accomplish the same result as +the fully awakened Buddha. Likewise, the Sangha jewel is the community of +sincere practitioners who have directly realised emptiness, the ultimate +nature of reality. + For those of us who consider ourselves to be practising Buddhists, it is +crucial to have this kind of deeper understanding of the Three Jewels when we +go for refuge to the Buddha, Dharma and Sangha. + -- H.H. the Dalai Lama, in "Lighting the Way", published by Snow Lion +~ +Being Mindful of Impermanence + + Loved ones who have long kept company will part. + Wealth created with difficulty will be left behind. + Consciousness, the guest, will leave the guest house of the body. + Let go of this life-- + This is the practice of Bodhisattvas. + +Although we have this human life with freedom and richness, which is so +valuable and difficult to get, it cannot last forever. This is because it is +not permanent and is subject to decay moment by moment. This life will +eventually become non-existent because our body and mind will separate. +Although death meditation involves reflecting on the moment by moment changing +nature of our life, it principally entails recognizing that one day it will +come to a complete stop and our mind will leave our body behind. Therefore, +we must take the essence from this life each day and try to fulfill a great +Dharma purpose because we will not have this opportunity for long. + -- from "Transforming Adversity into Joy and Courage: An Explanation of + 'The Thirty-Seven Practices of Bodhisattvas' ", by Geshe Jampa Tegchok, + edited by Thubten Chodron, published by Snow Lion Publications +~ +What do we mean when we speak of a truly compassionate kindness? Compassion +is essentially concern for others' welfare--their happiness and their +suffering. Others wish to avoid misery as much as we do. So a compassionate +person feels concerned when others are miserable and develops a positive +intention to free them from it. As ordinary beings, our feeling of closeness +to our friends and relatives is little more than an expression of clinging +desire. It needs to be tempered, not enhanced. It is important not to +confuse attachment and compassion.... A compassionate thought is motivated by +a wish to help release beings from their misery. + -- H.H. the Dalai Lama, in "Stages of Meditation", translated by Venerable + Geshe Lobsang Jordhen, Losang Choephel Ganchenpa, and Jeremy Russell, + published by Snow Lion Publications +~ +Tantric yogis succeed in their cultivation of wisdom more quickly than do +practitioners of the Perfection Vehicle because the tantric yogi, employing +deity yoga, can achieve a mind that is a union of calm abiding and special +insight--a mind of alert one-pointedness that realizes emptiness--in far +less time than the period of countless great aeons required for those who +practice sutra paths alone. Tantric yogis use deity yoga to enhance +meditation on emptiness; their use of deity yoga brings them more quickly to +an initial direct cognition of emptiness by enhancing their ability to combine +meditative stability with analysis.... Also, in Highest Yoga Tantra, +powerful, subtle consciousnesses that realize emptiness are manifested, +whereby the obstructions to liberation and omniscience are quickly overcome. + -- Daniel Cozort, in "Highest Yoga Tantra", Snow Lion Publications +~ +Howard C. Cutler, MD: + Is happiness a reasonable goal for most of us? Is it really possible? + +HH the Dalai Lama: + Yes. I believe that happiness can be achieved through training the mind. +When I say "training the mind," in this context I'm not referring to "mind" +merely as one's cognitive ability or intellect. Rather, I'm using the term +in the sense of the Tibetan word Sem, which has a much broader meaning, +closer to "psyche" or "spirit"; it includes intellect and feeling, heart +and mind. By bringing about a certain inner discipline, we can undergo a +transformation of our attitude, our entire outlook and approach to living. + When we speak of this inner discipline, it can of course involve many things, +many methods. But generally speaking, one begins by identifying those factors +which lead to happiness and those factors which lead to suffering. Having +done this, one then sets about gradually eliminating those factors which lead +to suffering and cultivating those which lead to happiness. That is the way. + -- H.H. the Dalai Lama and Howard C. Cutler, MD, in "The Art of Happiness: + A Handbook for Living", published by Snow Lion Publications +~ +It can be difficult to accept others and to accept ourselves. "I should be +better. I should be something different. I should have more." All of this +is conception; it's all mental fabrication. It's just the mind churning up +"shoulds," "ought tos," and "supposed tos." All this is conceptual rubbish, +and yet we believe it. Part of the solution is to recognize that these +thoughts are conceptual rubbish and not reality; this gives us the mental +space not to believe them. When we stop believing them, it becomes much +easier to accept what we are at any given moment, knowing we will change in +the next moment. We'll be able to accept what others are in one moment, +knowing that they will be different in the next moment. This is good stuff +for everyday practice; it's very practical. + -- Thubten Chodron, in "How to Free Your Mind: Tara the Liberator", + published by Snow Lion Publications +~ + The nature of samsaric evolution is not such that death is followed by +nothingness, nor that humans are always reborn as humans and insects as +insects. On the contrary, we all carry within us the karmic potencies of all +realms of cyclic existence. Many beings transmigrate from higher to lower +realms, others from lower to higher. The selection of a place of rebirth is +not directly in our own hands but is conditioned by our karma and delusions. +They who possess spiritual understanding can control their destiny at the time +of death, but for ordinary beings the process is very much an automatic chain +reaction of karmic seeds and habitual psychic response patterns.... + Our repeated experience of frustration, dissatisfaction and misery does not +have external conditions as its root cause. The problem is mainly our lack of +spiritual development. As a result of this handicap, the mind is controlled +principally by afflicted emotions and illusions. Attachment, aversion and +ignorance rather than a free spirit, love and wisdom are the guiding forces. +Recognizing this simple truth is the beginning of the spiritual path. + -- H.H. the Dalai Lama, edited and translated by Glenn H. Mullin, in + "The Path to Enlightenment", published by Snow Lion Publications +~ +If persons who have attained calm abiding keep their minds in calm abiding, +not only does the force of their meditative stabilization remain but their +other good qualities increase and do not degenerate. Similarly, persons who +have achieved special insight have clear perception not only with respect to +the object of observation on which they have been meditating but also with +respect to any other object to which they turn their minds. Persons who +cultivate calm abiding but not special insight will gain the factor of +stability but not that of an intense clarity; they will not be able to +manifest any antidote to the afflictive emotions. One must achieve an +intensity of clarity in order for anything to serve as an antidote to +ignorance, and to achieve that clarity one must cultivate special insight. + -- Geshe Gedun Lodro, in "Calm Abiding and Special Insight: Achieving + Spiritual Transformation through Meditation", translated and edited + by Jeffrey Hopkins, co-edited by Anne C. Klein and Leah Zahler, + published by Snow Lion Publications +~ +If you haven't found something strange during the day, +it hasn't been much of a day. + -- John A. Wheeler +~ +Don't try to solve serious matters in the middle of the night. + -- Philip K. Dick +~ + It is hypocrisy to say that all religions are the same. Different religions +have different views and fundamental differences. But it does not matter, as +all religions are meant to help in bringing about a better world with better +and happier human beings. On this level, I think that through different +philosophical explanations and approaches, all religions have the same goal +and the same potential. Take the concept[s] of the creator and self-creation +for instance. There are big differences between the two, but I feel they have +the same purpose. To some people, the concept of the creator is very powerful +in inspiring the development of self-discipline, becoming a good person with a +sense of love, forgiveness and devotion to the ultimate truth--the Creator or +God. + The other concept is self-creation: if one wants to be good, then it is one's +own responsibility to be so. Without one's own efforts one cannot expect +something good to come about. One's future is entirely dependent on oneself: +it is self-created. This concept is very powerful in encouraging an +individual to be a good and honest person. So you see, the two are different +approaches but have the same goal. + -- H.H. the Dalai Lama, in "Live in a Better Way: Reflections on Truth, Love + and Happiness", compiled and edited by Renuka Singh, published by Snow + Lion Publications +~ + The wise perceive that all things -persons and phenomena -arise in reliance +upon their own causes and conditions, and that based on this process we impute +mental labels upon things. The phenomena themselves have no true or inherent +existence from their own side. They have no self-nature whatsoever. + Were persons or phenomena to have a self-presence, there would be no need +for them to rely upon causes and conditions. Therefore one can be certain +that even the smallest speck of matter has no true, inherent existence from +its own side. + Although all things lack even the smallest speck of true existence, +nonetheless conventionally the laws of causes and conditions operate through +them, and conventionally all the phenomena in samsara and nirvana seem to +exist, arising in the same manner as do illusions, dreams and a reflected +image. + -- Glenn H. Mullin, in "The Six Yogas of Naropa: Tsongkhapa's commentary + entitled 'A Book of Three Inspirations: A Treatise on the Stages of + Training in the Profound Path of Naro's Six Dharmas' commonly referred + to as 'The Three Inspirations' ", published by Snow Lion Publications +~ +| George of the Bungle | + + And finally, new rule: America must recall the president. That's what this +country needs. A good old-fashioned, California-style, recall election! +Complete with Gary Coleman, porno actresses and action film stars. And just +like Schwarzenegger's predecessor here in California, George Bush is now so +unpopular he must defend his job against... Russell Crowe. Because at this +point I want a leader who will throw a phone at somebody. In fact, let's have +only phone throwers--Naomi Campbell can be the vice-president! + + Now I kid, but seriously Mr. President, this job can't be fun for you +anymore. There's no more money to spend--you used up all of that. You can't +start another war because you also used up the army. And now, darn the luck, +the rest of your term has become the Bush family nightmare: helping poor +people. Yeah, listen to your Mom. The cupboard's bare, the credit card's +maxed out, and no one's speaking to you: mission accomplished. Now it's time +to do what you've always done best: lose interest and walk away. Like you did +with your military service. And the oil company. And the baseball team. +It's time. Time to move on and try the next fantasy job. How about cowboy +or space man? + + Now I know what you're saying; you're saying that there's so many other +things that you as President could involve yourself in... Please don't. I +know, I know. There's a lot left to do. There's a war with Venezuela... and +eliminating the sales tax on yachts. Turning the space program over to the +church... and Social Security to Fannie Mae. Giving embryos the vote. + + But, Sir, none of that is going to happen now. Why? Because you govern +like Billy Joel drives. You've performed so poorly I'm surprised you haven't +given yourself a medal. You're a catastrophe that walks like a man. Herbert +Hoover was a shitty president, but even he never conceded an entire metropolis +to rising water and snakes. On your watch, we've lost almost all of our +allies, the surplus, four airliners, two trade centers, a piece of the +Pentagon, and the City of New Orleans. Maybe you're just not lucky! + + I'm not saying you don't love this country. I'm just wondering how much +worse it could be if you were on the other side. So, yes, God does speak to +you... and what he is saying is: "Take a hint." + + -- Bill Maher on Real Time, October 2005 +~ + We have the ability and the responsibility to choose to direct our actions +on a virtuous path. + When we weigh a particular act, to determine whether it is moral or +spiritual, our criterion should be the quality of our motivation. When someone +deliberately makes a resolution not to steal, if he or she is simply motivated +by the fear of getting caught and being punished by the law, it is doubtful +whether engaging in that resolution is a moral act, since moral considerations +have not dictated his or her choice. + In another instance, the resolution not to steal may be motivated by fear of +public opinion: "What would my friends and neighbors think? All would scorn +me. I would become an outcast." Though the act of making a resolution may be +positive, whether it is a moral act is again doubtful. + Now, the same resolution may be taken with the thought "If I steal, I am +acting against the divine law of God." Someone else may think, "Stealing is +nonvirtuous; it causes others to suffer." When such considerations motivate +one, the resolution is moral or ethical; it is also spiritual. In the +practice of Buddha's doctrine, if your underlying consideration in avoiding a +nonvirtuous act is that it would thwart your attainment of a state +transcending sorrow, such restraint is a moral act. + -- H.H. the Dalai Lama, in "An Open Heart: Practicing Compassion in Everyday + Life", edited by Nicholas Vreeland +~ + A conventional enemy may harm us, but patience and a refusal to retaliate +can bring us benefit both in this life and in the future. However, tolerance +towards [our own] hostile disturbing emotions and attempts at peaceful +coexistence with them will never bring us any reward. They will do us nothing +but harm if we don't take steps to drive them out. No conventional enemy can +do us such harm. The most an ordinary enemy can do is to defeat us for a +short space of time or destroy us in this life, but the disturbing emotions +will insure our misery for many lifetimes to come. + Shantideva says: + All other foes that I appease and wait upon + Will show me favors, give me every aid, + But should I serve my dark defiled emotions, + They will only harm me, draw me down to grief. + -- Geshe Sonam Rinchen, in "Eight Verses for Training the Mind: An Oral + Teaching", translated and edited by Ruth Sonam, published by Snow Lion + Publications +~ + There is a story about a princess who had a small eye problem that she felt +was really bad. Being the king's daughter, she was rather spoiled and kept +crying all the time. When the doctors wanted to apply medicine, she would +invariably refuse any medical treatment and kept touching the sore spot on her +eye. In this way it became worse and worse, until finally the king proclaimed +a large reward for whoever could cure his daughter. After some time, a man +arrived who claimed to be a famous physician, but actually was not even a +doctor. + He declared that he could definitely cure the princess and was admitted to +her chamber. After he had examined her, he exclaimed, "Oh, I'm so sorry!" +"What is it?" the princess inquired. The doctor said, "There is nothing much +wrong with your eye, but there is something else that is really serious." The +princess was alarmed and asked, "What on earth is so serious?" He hesitated +and said, "It is really bad. I shouldn't tell you about it." No matter how +much she insisted, he refused to tell her, saying that he could not speak +without the king's permission. + When the king arrived, the doctor was still reluctant to reveal his findings. +Finally the king commanded, "Tell us what is wrong. Whatever it is, you have +to tell us!" At last the doctor said, "Well, the eye will get better within a +few days--that is no problem. The big problem is that the princess will grow +a tail, which will become at least nine fathoms long. It may start growing +very soon. If she can detect the first moment it appears, I might be able to +prevent it from growing." At this news everyone was deeply concerned. And the +princess, what did she do? She stayed in bed, day and night, directing all +her attention to detecting when the tail might appear. Thus, after a few +days, her eye got well. + This shows how we usually react. We focus on our little problem and it +becomes the center around which everything else revolves. So far, we have +done this repeatedly, life after life. We think, "My wishes, my interests, my +likes and dislikes come first!" As long as we function on this basis, we will +remain unchanged. Driven by impulses of desire and rejection, we will travel +the roads of samsara without finding a way out. As long as attachment and +aversion are our sources of living and drive us onward, we cannot rest. + -- Ringu Tulku Rinpoche, in "Daring Steps toward Fearlessness: The Three + Vehicles of Buddhism", edited and translated by Rosemarie Fuchs, published + by Snow Lion Publications +~ +buddha mind listens +sound waves crashing over head +immutable calm +~ +time god shits in hand +catapults distraction dung +unstained mind abides +~ +Dr. Cutler: "... one of the reasons I brought up the topic of challenge at +work," I said to the Dalai Lama, "is because it relates to a concept that +seems to come up frequently these days in psychological literature, the +concept of flow.* This concept is increasingly mentioned in articles on human +happiness, and this state can commonly occur at work... while engaged in +activity, there's a feeling of effortlessness, a sense of total control over +what one is doing.... Although flow can occur in any activity, some +investigators have found that Americans experience more flow at work than they +do in their leisure time." + +The Dalai Lama: "You really like this flow, Howard!" the Dalai Lama exclaimed +with an amused chuckle. "... no matter how nice that state may be, I don't +think it is the most important source of satisfaction, fulfillment, or +happiness.... For one thing, you can't be in that state at all times.... So +through this flow, even if you get some temporary kind of happiness, it will +not be an ongoing thing. I think this flow state is not reliable or +sustainable, and I think it's much more important to develop other sources of +satisfaction through one's work that are brought about by training one's mind, +shaping one's outlook and attitude, integrating basic human values in the +workplace. For example, dealing with one's destructive emotions while at +work, reducing anger, jealousy, greed, and so on, and practicing relating to +others with kindness, compassion, tolerance, these are much more important and +stable sources of satisfaction than simply trying to create flow as much as +possible." + +Dr. Cutler: "...To the Dalai Lama, true happiness is associated with a sense +of meaning, and arises on the basis of deliberately cultivating certain +attitudes and outlooks. True happiness may take longer to generate, and +requires some effort, but it is this lasting happiness that can sustain us +even under the most trying conditions of everyday life." + +* flow is defined here as [people] being so completely absorbed in their work + that they lose track of time. + + -- H.H. the Dalai Lama and Howard C. Cutler, M.D., in "The Art of + Happiness at Work" +~ +Dzogchen could be defined as a way to relax completely. This can be clearly +understood from the terms used to denote the state of contemplation, such as +"leave it just as it is" (cog bzhag), "cutting loose one's tension" (khregs +chod), "beyond effort" (rtsol bral), and so on. Some scholars have classified +Dzogchen as a "direct path," comparing it to teachings such as Zen, where this +expression is often used. In Dzogchen texts, however, the phrases "direct +path" and "nongradual path" (cig car) are never used, because the concept of a +"direct path" implies necessarily that there must be, on the one hand, a place +from which one departs, and on the other, a place where one arrives. But in +Dzogchen there is a single principle of the state of knowledge, and if one +possesses this state one discovers that right from the beginning one is +already there where one wants to arrive. For this reason the state is said to +be "self-perfected" (lhun grub). + -- Chogyal Namkhai Norbu, in "Dzogchen: The Self-Perfected State", edited by + Adriano Clemente, translated from the Italian by John Shane, published by + Snow Lion Publications +~ + Since the situation in which we live is much changed but the attitude of +people who are in that situation is at variance with the times, this is one of +the causes of unnecessary pain, unnecessary problems. Therefore, education is +needed to communicate that the concept of violence is counterproductive, that +it is not a realistic way to solve problems, and that compromise is the only +realistic way to solve problems. Right from the beginning, we have to make +this reality clear to a child's mind--the new generation. In this way, the +whole attitude towards oneself, towards the world, towards others, can become +more healthy. I usually call this "inner disarmament." Without inner +disarmament, it is very difficult to achieve genuine, lasting world peace. + ...Through inner disarmament we can develop a healthy mental attitude, which +also is very beneficial for physical health. With peace of mind, a calm mind, +your body elements become more balanced. Constant worry, constant fear, +agitation of mind, are very bad for health. Therefore, peace of mind not only +brings tranquility in our mind but also has good effects on our body. + With inner disarmament, now we need external disarmament. As I mentioned +earlier, according to today's reality, there no longer is room for war, for +destruction. From a compassionate viewpoint, destruction, killing others, and +discriminating even against one's enemy are counterproductive. Today's enemy, +if you treat them well, may become a good friend even the next day. + -- H.H. the Dalai Lama in "The Art of Peace: Nobel Peace Laureates Discuss + Human Rights, Conflict and Reconciliation", edited by Jeffrey Hopkins, + published by Snow Lion Publications +~ +What we do for ourselves dies with us. +What we do for others and the world remains and is immortal. + -- Albert Pine +~ +Q: Where does hatred come from? + +A: That is a question which requires long hours of discussion. From the +Buddhist viewpoint, the simple answer is that it is beginningless. As a +further explanation, Buddhists believe that there are many different levels of +consciousness. The most subtle consciousness is what we consider the basis of +the previous life, this life and future lives. This subtle consciousness is +a transient phenomenon which comes about as a consequence of causes and +conditions. Buddhists have concluded that consciousness itself cannot be +produced by matter. Therefore, the only alternative is to accept the +continuation of consciousness. So that is the basis of the theory of rebirth. + +Where there is consciousness, ignorance and hatred also arise naturally. +These negative emotions, as well as the positive emotions, occur right from +beginningless time. All these are a part of our mind. However, these +negative emotions actually are based on ignorance, which has no valid +foundation. None of the negative emotions, no matter how powerful, have a +solid foundation. On the other hand, the positive emotions, such as +compassion or wisdom, have a solid basis: there is a kind of grounding and +rootedness in reason and understanding, which is not the case with afflictive +emotions like anger and hatred. + +The basic nature of the subtle consciousness itself is something neutral. So +it is possible to purify or eliminate all of these negative emotions. That +basic nature we call Buddha-nature. Hatred and negative emotions are +beginningless; they have no beginning, but there is an end. Consciousness +itself has no beginning and no end; of this we are certain. + + -- H.H. 14th Dalai Lama, in "Healing Anger: The Power of Patience from a + Buddhist Perspective", translated by Geshe Thupten Jinpa, published by + Snow Lion Publications +~ + When the sun is freed from clouds, the sun becomes clear and bright. +Similarly, when obstructions to omniscience are abandoned, wisdom becomes +clear light. + How does wisdom that is like the sun in a sky free from clouds dawn? It +is described as yogic direct perception. Between ordinary beings--those born +in dependence upon their individual karma--and yogins, here we are considering +yogins. Their wisdom is not speculation from an inferential point of view, as +is the case with ordinary beings. Neither is it pensive and lacking in +clarity. Rather, it sees directly, for which reason it is called yogic direct +perception. When we ordinary beings think about a thing, there is something +in the way, obstruction, due to which we do not see clearly and directly. +When those obstructions--the afflictive obstructions and the obstructions to +omniscience--have been dispelled, then knowledge arises as yogic direct +perception. + When yogic direct perception arises, how does it see? It sees phenomena +in a conventional context and it also sees reality in an ultimate context. In +the conventional context, wisdom sees the shapes, colors, and defining +characteristics of whatever things exist in worldly realms, individually and +without mixing them, just as they are. This wisdom knows the varieties of +phenomena. Similarly, in the context of reality, wisdom sees the meaning of +emptiness directly; this wisdom knows the mode of all phenomena. In +dependence upon release from the afflictive obstruction and the obstruction to +omniscience, the wisdoms knowing the modes and the varieties actually arise. + Someone in whom those two wisdoms are present is a buddha. + + -- Kenchen Thrangu Rinpoche, in "Essential Practice: Lectures on + Kamalashila's 'Stages of Meditation in the Middle Way School' ", + translated by Jules B. Levinson, published by Snow Lion Publications +~ + Sometimes people mistakenly look on vows and pledges as if these were a +type of punishment, but this is not at all the case. For example, just as +we follow certain methods of eating and drinking to improve our health and +certainly not to punish ourselves, so the rules the Shakymuni Buddha +formulated are for controlling counter-productive ill-deeds and ultimately for +overcoming afflictive emotions, because these are self-ruinous. Thus, to +relieve oneself from suffering, one controls the motivations and deeds +producing suffering for one's own sake. Realizing from his own experience +that suffering stems from one's own afflictive emotions as well as actions +contaminated with them, he sets forth styles of behavior to reduce the problem +for our own profit, certainly not to give us a hard time. Hence, these rules +are for the sake of controlling sources of harm. + -- H.H. the Dalai Lama, Dzong-ka-ba, and Jeffrey Hopkins, in "Yoga Tantra: + Paths to Magical Feats, published by Snow Lion Publications +~ +Giving with an open heart brings us joy and directly benefits others. Goods +are then shared more equitably within our society and among nations, soothing +the ill-feeling of social inequity and promoting world peace. Sharing is a +source of our continued existence as a species. As His Holiness the Dalai +Lama says, it is not survival of the fittest, but survival of those who +cooperate the most, that makes a species prosper. None of us exists +independently; we have to depend on others simply to stay alive. Thus, +helping others and sharing wealth benefits both self and others. Generosity +makes us happy now, enables our species to continue to prosper, and creates +positive karma that brings us prosperity in the future. In addition, it is an +essential trait of an enlightened being. Who ever heard of a stingy Buddha? + -- Bhikshuni Thubten Chodron, in "How to Free Your Mind: Tara the Liberator", + published by Snow Lion Publications +~ + I have made the point in the past that it is not necessary to consider +someone one's guru from the very outset simply because one has heard the +explanation of the Buddha's teachings from that person. At first, it is much +better if one does not have that kind of attitude toward them, simply +regarding them as a Dharma-friend. One gets teachings, and time goes by. +Then, when one feels that one knows that person quite well, and can take them +as one's guru without any danger of transgressing the commitments that +accompany such a relationship, when one has that kind of confidence, then one +can go ahead and take him or her as one's guru. The Lord Buddha himself made +it quite clear in both the Vinaya sutras and in the Mahayana scriptures, and +even in the Tantrayana, in a very detailed fashion, what the qualities of a +teacher should be. This is why I often criticize the Tibetan attitude of +seeing whatever the guru does as good, of respecting everything that the guru +does right from the start without the initial period of examination. Of +course, if the guru is really qualified, then to have such an attitude is +very worthwhile. + Take the cases of Naropa and Marpa, for example. Sometimes it appears as +though some of the things Tilopa asked of Naropa, or Naropa of Marpa, were +unreasonable. Deep down, however, these requests had great meaning. Because +of their great faith in their gurus, Naropa and Marpa did as instructed. +Despite the fact that they appeared to be unreasonable, because the teachers +were qualified, their actions had some meaning. In such situations, it is +necessary that from the disciple's side all of the actions of the teacher be +respected. But this cannot be compared to the case of ordinary people. +Broadly speaking, I feel that the Buddha gave us complete freedom of choice to +thoroughly examine the person who is to be our guru. This is very important. +Unless one is definite, one should not take them as a guru. This preliminary +examination is a kind of precautionary measure. + -- H.H. the Dalai Lama, "Answers: Discussions with Western Buddhists", edited + by Jose Ignacio Cabezon, published by Snow Lion Publications +~ +13. Transforming suffering into the path + Even if someone tries to cut off your head + When you haven't done the slightest thing wrong, + Out of compassion take all his misdeeds + Upon yourself-- + This is the practice of Bodhisattvas. + -- from "The Thirty-seven Practices of Bodhisattvas" + by Gyelsay Togme Sangpo + Although we have done nothing to deserve it, someone may attack us, beat +us, or perpetrate other forms of violence on us. Certainly it is tempting to +get angry in such a situation, but our anger will do no good. In fact, this +person is creating the cause for his own unfortunate rebirth by attacking us, +and the karma he creates is even heavier if we hold any of the three sets of +vows: pratimoksha, Bodhisattva, or tantric. Thus, we cultivate compassion, +and wish to take the person's karma and resultant suffering on ourselves. For +example, if a crazy person attacks a person who is sane, the latter will not +only not fight back but try to help, by giving him medicine and wanting him to +get well. The sane person sees that the crazy person does not know what he is +doing. He is out of control. + Similarly, when someone harms us, we should recognize that he too is out +of control and is being led by his three poisonous attitudes. Similarly, we +can remember that we are experiencing the ripening result of harmful actions +we did in past lives, so why blame the other person? In addition, that person +is causing our negative karma to be exhausted now, rather than later when the +result could be much more difficult to bear. In this way, we will not be +angry or retaliate, but will pray for and try to help the other. In "The +Eight Verses of Thought Transformation," it says, "Whenever I meet a person of +bad nature who is overwhelmed by negative energy and intense suffering, I will +hold such a rare one dear, as if I had found a precious treasure." People like +this suffer greatly because they think only of themselves, not of others, and +thus they are worthy of compassion, the wish that they be free from suffering +and its causes. + Being patient when harmed by others does not mean that we take no action +to prevent harm from occurring. Rather, patience frees our mind from the fog +of anger and gives us the clarity and kindness to respond to a situation in a +helpful way. Free of anger, we look for ways to resolve conflict other than +seeking revenge. + -- Geshe Jampa Tegchok, in "Transforming Adversity into Joy and Courage: + An Explanation of 'The Thirty-seven Practices of Bodhisattvas' ", edited + by Thubten Chodron, published by Snow Lion Publications +~ +Some people run a race to see who is the fastest. +I run a race to see who has the most guts. + -- Steve Prefontaine +~ + Shantideva expresses tremendous courage, which transcends all boundaries +of space and time, in a verse of his "Guide to the Bodhisattva's Way of Life" +(Bodhicaryavatara). He writes: + As long as space endures, + As long as sentient beings remain, + Until then, may I too remain + And dispel the miseries of the world. + When the altruistic intention is supported by insight into emptiness, and +particularly by the direct realization of emptiness, one is said to have +attained the two dimensions of bodhichitta which are known as conventional and +ultimate bodhichitta. With both these practices of compassion and wisdom, the +practitioner has within his or her hands the complete method for attaining the +highest spiritual goal. Such a person is truly great and worthy of +admiration. + If one is able to cultivate these spiritual qualities within oneself then, +as Chandrakirti writes very poetically in his Entry to the Middle Way +(Madhyamakavatara), with one wing of altruistic intention and another wing of +insight into emptiness, one can traverse the whole of space and soar beyond +the state of existence to the shores of fully enlightened buddhahood. + ...make an effort to contemplate, study and meditate, but without any +shortsighted expectations. You should have the same attitude as Shantideva -- +that as long as space exists you will remain to dispel suffering in the world. +When you have that kind of determination and courage to develop your capacity, +then a hundred years, an aeon, a million years are nothing to you. +Furthermore, you will not consider that the different human problems we have +here and there are in any way insurmountable. Such an attitude and vision +bring some kind of real inner strength. + -- H.H. the XIV Dalai Lama, in "Transforming the Mind: Teachings on + Generating Compassion", translated by Geshe Thupten Jinpa, edited by + Dominique Side and Geshe Thupten Jinpa. +~ +The Tantric Way of Purifying One's Views + The second important attitude is the Tantric way of purifying one's views, +which means to transform one's ordinary and dualistic views and conceptions +into a higher spiritual vision. + For instance, you transform the place where teachings are received from an +ordinary classroom into a complete and perfected mandala of the deities. You +view the teacher as a pure form of Shen Lha Okar, the Buddha of Compassion, by +mentally transforming him from an ordinary person into an enlightened one who +has manifested in a human body to guide all sentient beings. You transform +your companions and classmates from ordinary beings into deities and +goddesses, and believe that they all have love, compassion, and care for all +sentient beings. + The purpose of transforming your views into pure visions toward these +objects is to realize the extraordinary nature of this experience. This gives +you a special reason to receive blessings and powers from the teacher (lama), +the enlightened ones (Sangye), the deities (yidam), and the female +manifestations of the enlightened ones (khadro), in order to develop your +wisdom and stability. This is the essence of the practice of purifying one's +view according to the Tantric ways. + -- Latri Khenpo and Geshe Nyima Dakpa Rinpoche, in "Opening the Door to + Bon", published by Snow Lion Publications +~ +MENSA: My Ego Never Stops Aching +~ +...more people are driven insane through religious hysteria +than by drinking alcohol. + -- W. C. Fields +~ +Success without honor is an unseasoned dish; it will +satisfy your hunger, but it won't taste good. + -- Joe Paterno +~ +Desire is the source of endless problems. The more desires we have, the more +we have to plan and work hard to realize them. Some time ago a businessman +told me that the more he developed his company, the more he felt like making +it even bigger. And the more he tried to make it bigger, the more he found he +had to lie and fight mercilessly against his competitors. He had come to +realize that wanting more and more made no sense, and that he only had to +reduce the size of his business for competition to become less fierce so he +would be able to carry out his work honestly. I found his testimony very +true. + -- H.H. the Dalai Lama, in "365 Dalai Lama: Daily Advice from the Heart", + edited by Matthieu Ricard, translated by Christian Bruyat and Dominique + Messent +~ +I--What Is a Treasure? + According to the Nyingma School, the Treasures are most often comprised of +spiritual instructions concealed by enlightened beings for the purpose of +discovery at a later predestined time when their message will invigorate the +Buddhist teaching and deepen spiritual understanding. Central to this process +is the figure of the Treasure revealer (gter ston) -the person who acts as a +medium for the re-emergence of this inspired material into the human world. +Accordingly, beginning in the eleventh century and continuing into the +present, the Nyingma School identifies a large number of Treasure revealers +and grants authoritative status to their discoveries. + The idea that religious truth lies concealed within the world of phenomena +awaiting discovery by spiritually gifted people is by no means a concept +exclusive to the Nyingma School or Tibetan Buddhism as a whole. Throughout +Buddhist literature there are numerous descriptions of teachings being +inherently present in the phenomenal world ready to be perceived by +individuals possessing inspired levels of consciousness and, accordingly, +spiritual revelations have surfaced on numerous occasions throughout the +course of Buddhist history. + -- Andreas Doctor, in "Tibetan Treasure Literature: Revelation, Tradition, + and Accomplishment in Visionary Buddhism", Snow Lion Publications +~ +Patricia Churchland: But do you think that there is something, I am not sure +what to call it--a kind of awareness that can exist independently of the +brain? For example, something that survives death? + +Dalai Lama: Generally speaking, awareness, in the sense of our familiar, day- +to-day mental processes, does not exist apart from or independent of the +brain, according to the Buddhist view. But Buddhism holds that the cause of +this awareness is to be found in a preceding continuum of awareness, and that +is why one speaks of a stream of awareness from one life to another. Whence +does this awareness arise initially? It must arise fundamentally not from a +physical base but from a preceding continuum of awareness. + +The continuum of awareness that conjoins with the fetus does not depend upon +the brain. There are some documented cases of advanced practitioners whose +bodies, after death, escape what happens to everyone else and do not decompose +for some time--for two or three weeks or even longer. The awareness that +finally leaves their body is a primordial awareness that is not dependent upon +the body. There have been many accounts in the past of advanced practitioners +remaining in meditation in this subtle state of consciousness when they died, +and decomposition of their body was postponed although the body remained at +room temperature. + -- H.H. the Dalai Lama, in "Consciousness at the Crossroads: Conversations + with the Dalai Lama on Brain Science and Buddhism", edited by Zara + Houshmand, Robert B. Livingston and B. Alan Wallace, Snow Lion + Publications +~ +The real source of my suffering is self-centeredness: my car, my possession, +my well-being. Without the self-centeredness, the suffering would not arise. +What would happen instead? It is important to imagine this fully and to focus +on examples of your own. Think of some misfortune that makes you want to lash +out, that gives rise to anger or misery. Then imagine how you might respond +without suffering. Recognize that we need not experience the misery, let +alone the anger, resentment, and hostility. The choice is ours. + +Let's continue with the previous example. You see that there is a dent in the +car. What needs to be done? Get the other driver's license number, notify +the police, contact the insurance agency, deal with all the details. Simply +do it and accept it. Accept it gladly as a way to strengthen your mind +further, to develop patience and the armor of forbearance. There is no way to +become a Buddha and remain a vulnerable wimp. Patience does not suddenly +appear as a bonus after full enlightenment. Part of the whole process of +awakening is to develop greater forbearance and equanimity in adversity. +Santideva, in the sixth chapter of his Guide to the Bodhisattva's Way of Life, +eloquently points out that there is no way to develop patience without +encountering adversity, and patience is indispensable for our own growth on +the path to awakening. + -- B. Alan Wallace, in "The Seven-Point Mind Training", edited by Zara + Houshmand, published by Snow Lion Publications +~ +In all things of nature, there is something of the marvelous. -- Aristotle +~ +Now the kilt was only for day-to-day wear. In battle, we donned a full-length +ball gown covered in sequins. The idea was to blind your opponent with luxury. + -- Groundskeeper Willie +~ +Dayata (Om) Gate Gate Paragate Parasamgate Bodhi Swaha. + +A mantra is that which protects the mind. Through this mantra, which is the +perfection of wisdom itself, we can overcome the demon of ignorance that +possesses us and find unsurpassable happiness. It protects the minds of those +who practice it from all fears and describes how to make the transition from +worldly existence to the supreme state beyond sorrow. It is a mantra of great +knowledge because it saves us from the poison of ignorance and its imprints. +It is an unsurpassable mantra because it frees us from suffering and its +causes as no other path of insight can. The incomparable is the state beyond +suffering. Since it helps us to attain that state, it is comparable to the +incomparable. It totally pacifies suffering because it rids us of all the +troubles of the world and their causes. The world here refers to ordinary +beings like us. Our troubles are many but foremost are birth, aging, +sickness, and death. This mantra does not deceive us and it is true because +wisdom sees things as they actually are without any error or deception. It is +therefore transcendent. + -- from "The Heart Sutra: An Oral Teaching by Geshe Sonam Rinchen", + translated and edited by Ruth Sonam, published by Snow Lion Publications +~ +I am interested not in converting other people to Buddhism but in how we +Buddhists can contribute to human society, according to our own ideas. I +believe that other religious faiths also think in a similar way, seeking to +contribute to the common aim.... + +Just as Buddha showed an example of contentment, tolerance, and serving others +without selfish motivation, so did Jesus Christ. Almost all of the great +teachers lived a saintly life--not luxuriously like kings or emperors but as +simple human beings. Their inner strength was tremendous, limitless, but the +external appearance was of contentment with a simple way of life. + +...the motivation of all religious practice is similar--love, sincerity, +honesty. The way of life of practically all religious persons is contentment. +The teachings of tolerance, love, and compassion are the same. A basic goal +is the benefit of humankind--each type of system seeking in its own unique +ways to improve human beings. If we put too much emphasis on our own +philosophy, religion, or theory, are too attached to it, and try to impose it +on other people, it makes trouble. Basically all the great teachers, such as +Gautama Buddha, Jesus Christ, or Mohammed, founded their new teachings with a +motivation of helping their fellow humans. They did not mean to gain anything +for themselves nor to create more trouble or unrest in the world. + +Most important is that we respect each other and learn from each other those +things that will enrich our own practice. Ever if all the systems are +separate, since they each have the same goal, the study of each other is +helpful. + -- H.H. the Dalai Lama, in "The Dalai Lama: A Policy of Kindness", compiled + and edited by Sidney Piburn, published by Snow Lion Publications +~ + Our teacher, Sakyamuni Buddha, is one among the thousand Buddhas of this +aeon. These Buddhas were not Buddhas from the beginning, but were once +sentient beings like ourselves. How they came to be Buddhas is this. + Of body and mind, mind is predominant, for body and speech are under the +influence of the mind. Afflictions such as desire do not contaminate the +nature of the mind, for the nature of the mind is pure, uncontaminated by any +taint. Afflictions are peripheral factors of a mind, and through gradually +transforming all types of defects, such as these afflictions, the adventitious +taints can be completely removed. This state of complete purification is +Buddhahood; therefore, Buddhists do not assert that there is any Buddha who +has been enlightened from the beginning. + -- H.H. the Dalai Lama, in "The Buddhism of Tibet: The Dalai Lama", + translated and edited by Jeffrey Hopkins, published by Snow Lion + Publications +~ + ...everbody thinks that compassion is important, and everyone has +compassion. True enough, but the Buddha gave uncommon quintessential +instructions when he taught the methods for cultivating compassion, and the +differences are extraordinarily important. + Generally, everyone feels compassion, but the compassion is flawed. In +what way? We measure it out. For instance, some feel compassion for human +beings but not for animals and other types of sentient beings. Others feel +compassion for animals and some other types of sentient beings but not for +humans. Others, who feel compassion for human beings, feel compassion for the +human beings of their own country but not for the human beings of other +countries. Then, some feel compassion for their friends but not for anyone +else. Thus, it seems that we draw a line somewhere. We feel compassion for +those on one side of the line but not for those on the other side of the line. +We feel compassion for one group but not for another. That is where our +compassion is flawed. What did the Buddha say about that? It is not +necessary to draw that line. Nor is it suitable. Everyone wants compassion, +and we can extend our compassion to everyone. + -- Kenchen Thrangu Rinpoche, in "Lectures on Kamalashila's 'Stages of + Meditation in the Middle Way School' ", translated by Jules B. Levinson, + published by Snow Lion Publications +~ +4. Seven-Limbed Practice + + The seven parts of the practice are encompassed by two practices -the +purification of negativities and the enhancing of the store of merit. When +you engage in the practice, it is very important to understand that each and +every one of the seven limbs has its individual purpose and significance, and +only with such knowledge can you engage properly in the practice. The seven +limbs are: prostration, offering, confession, rejoicing, requesting to turn +the wheel of the dharma, entreating not to enter into nirvana, and dedication +of merit. + ...For the practice of confession, which is the third of the seven limbs, +it is very important to have the factor of regret; without this factor there +is no possibility of purifying the negativities.... The great yogi Milarepa +said: "When I examined whether or not confession could purify the +negativities, I found that it is regret that cleanses them." In order to +generate regret, it is important to see the destructive nature of negative +actions and also to understand the law of causality. + Based on a disciplined mind, we experience happiness; based on an +undisciplined, untamed mind, we undergo suffering. We should think that if we +are not able to make any progress from our present state of mind, which always +indulges in negative thoughts, there is not much hope for us. So, if we are +able to think in such terms, we will be able to really see the destructive +nature of negative actions, and also that the store of negative actions that +we have is inexhaustible, like a rich person's bank balance. Without the +recognition of the destructive nature of the negative forces, we will never be +able to develop the deep factor of regret from the depth of our hearts. + If we do not engage in a proper practice of dharma, it seems that we may +expend all our store of merit in mundane pleasures. It is very important to +have this faculty of regret in our practice of purification and confession. + -- H.H. the Dalai Lama (Tenzin Gyatso), in "The Path to Bliss: A Practical + Guide to Stages of Meditation", translated by Geshe Thubten Jinpa, + edited by Christine Cox, published by Snow Lion Publications +~ +9. Glorious Lama (excerpt) + +...Those with faith have a refuge. +Those with compassion have an altruistic attitude. +Those with incisive knowledge have realization. +Those with respect and devotion have blessings. + +Those with shame avoid carelessness. +Those who avoid carelessness have a guarded mind. +Those who have a guarded mind have vows and samaya. +Those who have vows and samaya have spiritual attainments. + +Calm and self-control are signs of listening to the Dharma; +Few passions, signs of meditation; +Harmony with everyone is the sign of a practitioner; +Your mind at ease, the sign of accomplishment. + +The root of phenomena is your own mind. +If you tame your mind, you are a practitioner. +If you are a practitioner, your mind is tamed. +When your mind is tamed, that is liberation. + -- Dudjom Jigdral Yeshe Dorje, in "Wisdom Nectar: Dudjom Rinpoche's Heart + Advice", translated by Ron Garry, published by Snow Lion Publications +~ +The Special Features of Dzogchen + + In the early translation school of the Nyingma, a system of nine yanas is +taught. Three of these -the paths of the Sravaka, pratyekabuddha, and +bodhisattva -constitute the sutra tradition, while the tantric tradition +consists of six levels -the three outer tantras and the three inner tantras. +The tradition of Dzogchen, or Atiyoga, is considered to be the pinnacle of +these nine yanas. The other, lower, yanas are said to be philosophical +systems that depend on ordinary consciousness, and so the path is based on +that ordinary consciousness. Here the distinction being made is between +ordinary mind -sem -and pure awareness -rigpa. The ninth yana, the most +majestic, is beyond ordinary consciousness, for its path is based on rigpa, +not on the ordinary mind. + Throughout beginningless time, there has always been present, within us +all, a pure awareness -that in-dwelling rigpa which in Atiyoga is evoked in +all its nakedness, and which constitutes the practice. + ...The ground for all the phenomena of samsara and nirvana is the +fundamental innate mind of clear light, and these phenomena are its radiance +or display. While we are following the path, in order for all the impure +aspects of our experience to be purified on the basis of that rigpa--or, you +can say, that fundamental innate mind of clear light--there is no other means +apart from that fundamental and innate state itself, which is therefore the +very essence of the path. Finally, when the fruition is made fully evident, +it is just this fundamental innate mind of clear light itself, free from +obscuration, that constitutes the attainment of fruition. + ...Any given state of consciousness is permeated by the clear light of +rigpa's pure awareness. However solid ice may be, it never loses its true +nature, which is water. In the same way, even very obvious concepts... arise +within the expanse of rigpa and that is where they dissolve. On this point, +Dodrupchen Jikme Tenpe Nyima says that all objects of knowledge are permeated +by clear light, just as a sesame seed is permeated by its oil. Therefore, +even while the coarser states of the six consciousnesses are functioning, +their subtle aspect -that of clear light -can be directly introduced by means +of those states themselves, through blessings and through pith instructions. + Here lies the extraordinary and profound implication of the Dzogchen +teachings. When you are basing your path on the fundamental innate mind of +clear light, you will employ skilful means to block the coarse and subtle +states of energy and mind, as a result of which the state of clear light +becomes evident, and on this you base your path. But in Dzogchen, even while +the six consciousnesses are fully functioning, by means of those very states +you can be directly introduced to their subtle aspect of clear light in your +immediate experience, and you then meditate by focusing one-pointedly on that +aspect. As you meditate in this way, resting in this non-conceptual state, +gradually your experience of clear light becomes increasingly profound, while +coarser thoughts and concepts dwindle away. + The most difficult task is to differentiate between ordinary mind and +rigpa. It is easy enough to talk about it. You can say, for example, that +rigpa has never been confused, while ordinary mind has fallen under the +influence of concepts and is mired in confusion. But to be introduced to the +direct experience of the essence of rigpa is far from being easy. And so +Dodrupchen says that although your arrogance might be such that you assume you +are meditating on the ultimate meaning of rigpa, there is a danger that "you +could end up meditating on the clear, empty qualities of your ordinary mind, +which even non-Buddhist practitioners are capable of doing." He is warning us +to be careful. + -- H.H. the Dalai Lama, in "Dzogchen: The Heart Essence of the Great + Perfection", translated by Geshe Thubten Jinpa and Richard Barron + (Chokyi Nyima), edited by Patrick Gaffney, pub. by Snow Lion Publications +~ + Patience is like a beautiful ornament. When you become a person with +great patience, it brings a certain element of charm to your life. You are +loved by others, and you give no problems to your friends. You bring an +element of joy, happiness, and calmness to other people's lives -your friends, +your family, and the community. You do not have to ask to be accepted; +everyone longs for your presence. Everyone looks up to you and respects you, +not because you have worked for that or expected it, not because you were +competing for their favor, but simply because of the nature of patience. You +are respected and trusted, and you acquire dignity with the practice of +patience. When you are honored, it is with sincerity, and it is something you +can live up to. + ...Just hearing about patience does not mean you are experiencing it now +or will easily develop it. To lay the ground for training the mind, you must +first tame the mind. To tame the mind, it is extremely important to do the +basic shamata [tranquility meditation, calm abiding] practice, which develops +calmness and tranquility. Then you can add the practice of patience, +understanding the benefits of patience and reminding yourself to take +advantage of the available antidotes. + -- Ven. Khenpo Karthar Rinpoche, in "Dharma Paths", translated by Ngodup + Burkhar and Chojor Radha, edited by Laura M. Roth, published by Snow Lion + Publications +~ +No tyranny is so irksome as petty tyranny: the officious demands of +policemen, government clerks, and electromechanical gadgets. + -- Edward Abbey +~ +We make a living by what we get, but we make a life by what we give. + -- Winston Churchill +~ +If the government would make up its mind to require for every child a good +education, it might save itself the trouble of providing one. It might leave +to parents to obtain the education where and how they pleased, and content +itself with helping to pay the school fees of the poorer classes of children, +and defraying the entire school expenses of those who have no one else to pay +for them. + -- J. S. Mill +~ +First, God created idiots. +That was just for practice. +Then He created school boards. + -- Mark Twain +~ +Whence it comes to pass, that for not having chosen the right course, +we often take very great pains, and consume a good part of our time in +training up children to things, for which, by their natural constitution, +they are totally unfit. + -- Montaigne +~ +There are two types of education... +One should teach us how to make a living, +And the other how to live. + -- John Adams +~ +We are shut up in schools and college recitation rooms for ten or +fifteen years, and come out at last with a belly full of words and +do not know a thing. + -- Ralph Waldo Emerson +~ + The Sanskrit word for compassion, karuna, has the implication of "that +which blocks or prevents bliss." In general, when we develop compassion, we +develop very strongly the attitude that cannot bear the suffering of other +beings. We wish for it to end and for them to become free. Although we do +not actually experience others' suffering at that time, the strength of the +attitude that cannot bear their suffering causes our mind also to become +unhappy. This is the general sense in which compassion blocks bliss.... + Only the power of a union of method and wisdom--namely the union of +compassion, as a greatly blissful awareness, and the discriminating awareness +of voidness--allows us to attain the total release of supreme nirvana, namely +enlightenment. + -- H.H. the Dalai Lama, in "The Gelug/Kagyu Tradition of Mahamudra", with + Alexander Berzin, published by Snow Lion Publications +~ + There is a tradition of reciting prayers of request to the spiritual +teachers of the lineage at the beginning of each study session, starting with +the Buddha Shakyamuni and ending with our own personal teachers. What do we +request? As we chant each verse, we take to mind the inspiring qualities of +the great masters mentioned in it and ask them to bless us to develop +compassion, wisdom and power similar to their own. Blessings are experienced +in the form of a transformation which affects our body, speech and mind. Our +mind becomes more serviceable and flexible, our way of speaking and acting +more constructive. We become more open to the message of how to bring about +inner transformation that has been handed down through this long line of +spiritual teachers. + Our teachers pass on the instructions they have received from their own +teachers, the knowledge they have culled from their reading and everything +they have understood and experienced as a result of their personal practice, +without holding anything back. They are motivated by compassion and their +deep wish to help us. To communicate it to us they choose whatever means are +most effective--sometimes stern, sometimes gentle. This process must take +place in an atmosphere of mutual trust, something very rare in relationships +today. In the past the relationship between students and teachers was even +closer and more trusting than that between siblings--and in Tibet brothers and +sisters generally enjoyed a close and loving bond. Today this is a dying +tradition, yet a good relationship between student and teacher is vital even +for the communication of secular knowledge, let alone where spiritual wisdom +is concerned. + -- Geshe Sonam Rinchen, in "Atisha's Lamp for the Path to Enlightenment: An + Oral Teaching", translated and edited by Ruth Sonam, published by Snow + Lion Publications +~ + We have happiness of mind and freedom from anxiety to just the degree that +our minds are tamed.... Once we want happiness and do not want suffering, we +should engage in the means to achieve happiness and eliminate suffering. +Practice is based on reasoning, not force; it is up to oneself. + The time for engaging in these techniques is now. Some feel, "I did not +succeed in this lifetime; I will ask a lama for help in my future life." To +think that we will practise in the future is only a hope. It is foolish to +feel that the next life will be as suitable as this. No matter how bad our +condition is now, since we have a human brain, we can think; since we have a +mouth, we can recite mantra. No matter how old one may be, there is time for +practice. However, when we die and are reborn, we are unable even to recite +om mani padme hum. Thus, it is important to make all effort possible at this +time when we have obtained the precious physical life-support of a human. + -- H.H. the Dalai Lama and Jeffrey Hopkins, in "Deity Yoga: In Action and + Performance Tantra by Tsong-ka-pa", published by Snow Lion Publications +~ +How would we feel if one of our children was overpowered by a serious disease +and did some terrible things without knowing what he or she was doing? We +should try to view someone dear who suddenly hurts us in the same light. If +we can see that person is out of control and sick with negative emotions, we +will not feel so much hatred and disgust. There may be resentment, and we may +not be able to love that person more than before, but almost automatically +there will be a certain sympathy that will lessen or end our hatred and allow +us to forgive. + -- Ringu Tulku Rinpoche, in "Daring Steps Toward Fearlessness: The Three + Vehicles of Buddhism", edited and translated by Rosemarie Fuchs, published + by Snow Lion Publications +~ + Compassion is a theme the Dalai Lama returns to over and over again. I +also know he has meditated on compassion every morning without fail for the +past half century. In an interview, I asked the Dalai Lama to give me his +take on compassion. Lhakdor [his translator], as usual, was by his side. + "Compassion is something like a sense of caring, a sense of concern for +others' difficulties and pain," the Dalai Lama said. "Not only family and +friends, but all other people. Enemies also. Now, if we really analyze our +feelings, one thing becomes clear. If we think only of ourselves, forget +about other people, then our minds occupy a very small area. Inside that +small area, even a tiny problem appears very big. But the moment you develop +a sense of concern for others, you realize that, just like ourselves, they +also want happiness; they also want satisfaction. When you have this sense of +concern, your mind automatically widens. At this point, your own problems, +even big problems, will not be so significant. The result? Big increase in +peace of mind. So, if you think only of yourself, only your own happiness, +the result is actually less happiness. You get more anxiety, more fear." + "I was thirty-two years old when I developed a strong experience of +compassion," he told me.... "Often when I reflect on the meaning and benefits +of altruistic mind, tears came." Lhakdor translated: "...When he meditated on +compassion, he would sometimes be filled with joy and appreciation. And there +is a strong sense of concern for others accompanied by a feeling of +sadness.... And when His Holiness reflected on certain profound explanations +on emptiness, this would also trigger a strong emotion." + "I think that strong conviction or strong emotions actually give more +inner strength," the Dalai Lama explained. "So when I face some problems or +criticism, for example, criticism from the Chinese, of course, little +irritation sometimes..." "But then he'll have this feeling of compassion for +them," Lhakdor translated. "He'll regret they're not making positive +connection with him. But his sentiment is, although there is this negativity, +may it also give positive fruit." + "Now, the understanding of emptiness helps a lot toward developing +compassion. There's no doubt it reinforces compassion," the Dalai Lama said. +Lhakdor elaborated: "Emptiness allows us to have an understanding about +ultimate reality. It helps us to appreciate the wisdom of interdependence--a +fundamental law of nature. We gain an appreciation that we are all basically +related. It is because of this interrelatedness that we are able to empathize +with the suffering of others. With empathy, compassion flows naturally. We +develop genuine sympathy for others' suffering and the will to help remove +their pain. Emptiness thus strengthens positive emotions like compassion." + Emptiness and compassion. Wisdom and method. These are the twin pillars +of the Dalai Lama's practice--everything we need to know about spiritual +practice. Both qualities are needed; they strengthen each other. Once we +realize we are all interconnected, it is difficut not to feel some of +compassion for the problems of our fellow human beings. And once we come by +a feeling of compassion, we start to get a glimpse of the timeless truth of +interdependence, of emptiness. + The Dalai Lama looked thoughtful. After some time, he turned to me, "I +think one thing I'm quite sure," he said. "I can tell you, the twin practice +of emptiness and compassion is... effective." Then he lapsed into Tibetan +again. Lhakdor translated. "His Holiness can say with conviction: if you +meditate on emptiness and compassion, so long as you make the effort, then His +Holiness is sure that, day in and day out, you will get tangible benefit. +Your whole attitude will change." "....These things about compassion are +something living--according to my own experience," the Dalai Lama went on. "I +tell some of my experiences to other people, share some of my feelings, then +other people understand: there is something real, something living." + -- from "The Wisdom of Forgiveness: Intimate Conversations and Journeys by" + His Holiness the Dalai Lama and Victor Chan, published by Snow Lion + Publications +~ + ...it is difficult to recognize an authentic teacher, because these +qualities are internal. We can not depend upon external factors, but external +factors are what we see. It is very difficult to see the inner qualities of +another person. A businessman might be friendlier to us than our best friend, +while his unseen motivation is merely to make a sale. Likewise, if a +"teacher" acts in a very kind and loving manner towards us it does not +necessarily mean that he is compassionate and selfless, because we cannot see +his motivation. We also cannot determine a teacher's qualifications based +upon her fame, or whether she has thousands of students. So the seeker is +left with this paradox. + There is no simple solution, but there are things we can do. First, it is +important that we familiarize ourselves with the characteristics* discussed by +Kongtrul Rinpoche. Second, we must maintain awareness of our own motivation +during the process of finding a teacher. Am I seeking a teacher in order to +attain enlightenment for the benefit of all sentient beings, or am I seeking +to fulfill my need to acquire the prestige associated with a famous teacher, +or am I merely attracted to a lama's beautiful retreat land or the social +scene of a hip sangha, and so on. + These motivations need to be acknowledged if we are to recognize an +authentic wisdom teacher, because the teacher you find is related to your +karma, and your karma is intimately connected to your motivation. +Fortunately, there are methods that help us purify our motivation and create +the proper conditions for finding a wisdom teacher, such as bringing our +awareness to our motivations as much as possible, doing daily meditation +practice, and praying to the Triple Gem [Buddha, Dharma, and Sangha] that we +will meet and recognize an authentic wisdom teacher. + -- Jamgon Kongtrul Lodro Thaye, in "The Teacher-Student Relationship: A + Translation of 'The Explanation of the Master and Student Relationship, + How to Follow the Master, and How to Teach and Listen to the Dharma' ", + translated and introduced by Ron Garry, published by Snow Lion Pub. + +* "Whatever lama is followed must definitely have these qualities: He holds a +pure lineage since he has not contradicted the commitments and prohibitions of +the three vows.... He should be very learned... clear about the sutras, +tantras, and shastras. His mind-stream should be so saturated with compassion +that he loves all the limitless sentient beings as his only child. He should +be expert in the outer tripitakas and on the inner level he should be expert +in the ritual of the four classes of tantras of the secret mantra. He should +have manifested the outstanding noble qualities of abandomnent and realization +in his mind-stream by having relied upon practicing the meaning of this. He +gathers fortunate disciples by the four ways of attracting: generosity, +pleasant speech, his conduct should benefit others, and he should act in +accord with the dharma." + -- Patrul Rinpoche +~ +...when we talk about the notion of self in Buddhism, it is important to bear +in mind that there are different degrees or types. There are some types of +sense of self which are not only to be cultivated but also to be reinforced +and enhanced. For instance, in order to have a strong determination to seek +Buddhahood for the benefit of all sentient beings, one needs a very strong +sense of confidence, which is based upon a sense of commitment and courage. +This requires a strong sense of self. Unless one has that identity or sense +of self, one will not be able to develop the confidence and courage to +strongly seek this aim. In addition, the doctrine of Buddha-nature gives us a +lot of encouragement and confidence because we realize that there is this +potential within us which will allow us to attain the perfection that we are +seeking. However, there are different types of sense of self which are rooted +in a belief in a permanent, solid, indivisible entity called "self" or "I." +There is the belief that there is something very concrete or objective about +this entity. This is a false notion of self which must be overcome. + -- His Holiness the Dalai Lama, in "Healing Anger: The Power of Patience + from a Buddhist Perspective", translated by Geshe Thupten Jinpa, + published by Snow Lion Publications +~ +...by respecting and serving your teachers you exhaust karma whose effects you +would otherwise experience in the miserable realms. Your action of serving +the teacher expends these miserable effects and replaces them with only slight +harm to your body and mind in this lifetime, either in actuality or in dreams. +In addition, the benefits of respecting and serving your teachers are +tremendous, such as a collection of virtue which surpasses even the roots of +virtue that you derive from making offerings to limitless buddhas, and so +forth. As the Sutra of Ksitigarbha says: + "Those whom the teachers care for will purify the karma that would +otherwise cause them to wander through the miserable realms for ten million +limitless eons. They purify this karma with harm to their bodies and minds in +this lifetime. This harm includes sickness such as an infectious disease with +fever and calamities such as famine. They may purify their karma by merely +undergoing something as little as a dream or a scolding. They produce more +roots of virtue in one morning than those who give gifts to, worship, or +observe precepts from limitless tens of millions of buddhas. Those who +respect and serve their gurus are endowed with unimaginable good qualities." + -- Tsong-kha-pa, in "The Great Treatise on the Stages of the Path to + Enlightenment: Lam Rim Chen Mo", translated by the Lamrim Chenmo + Translation Committee under editorial guidance of Joshua Cutler and Guy + Newland, foreword by Robert A.F. Thurman, Snow Lion Publications +~ + ...if we remain clinging to this life even for one day, we are misusing +our time. In this way, we can waste months and years on end. Because we +don't know when our lives will finish, we should remain mindful and well +prepared. Then, even if we die tonight, we will do so without regret. If we +die tonight, the purpose of being well prepared is borne out; if we don't die +tonight, there is no harm in being well prepared, because it will still +benefit us. + But when we leave the world of humans, we do so without a protector or +supporter and the total responsibility falls on us. We only have our own +intelligence to rely on at that time, so we must expend our own effort in +order to protect ourselves. As the Buddha said, "I have shown you the path to +liberation; know that liberation depends on you." We must put strenuous effort +into gaining freedom from the lower migrations, liberation from samsara, +freedom from conventional existence and solitary salvation. + The body is compared to a guest house; it is a place to stay for just a +short time and not permanently. At present, the guest of consciousness is +staying in the guest house of the body, like renting a place to stay. When +the day comes for consciousness to leave, the guest house of the body must be +left behind. Not being attached to friends, the body, wealth and possessions +is the practice of the Bodhisattvas. + -- His Holiness the XIV Dalai Lama, in "The Heart of Compassion: A Practical + Approach to a Meaningful Life" +~ + ...the Transcendent Conqueror presented the two truths with respect to all +inner and outer things, like sprouts and everything else. Genuine truth is +described as being simply the authentic object of the noble ones' original +wisdom that sees what is authentic and true; there is no identity actually +established there for conceptual mind to find. Relative truth is the false +object seen from the perspective of the conceptual mind whose eye of wisdom is +completely covered by the cataract of ignorance, as is the case with ordinary +beings. It is therefore posited as being this conceptual mind. The object +perceived does not exist in the way that this mind perceives it to be. + Thus, the Teacher explained that every thing found holds two natures +within: a genuine nature and a relative one. From among these two, the object +of the noble ones' authentic vision is the precise nature of reality, genuine +truth, and the object of false seeing is relative truth. + -- Chandrakirti, in "The Moon of Wisdom: Chapter Six of Chandrakirti's + 'Entering the Middle Way' with Commentary from the Eighth Karmapa Mikyo + Dorje's 'Chariot of the Dagpo Kagyu Siddhas', translated by Ari Goldfield, + Jules Levinson, Jim Scott and Birgit Scott under guidance of Khenpo + Tsultrim Gyamtso Rinpoche, published by Snow Lion Publications +~ + ...we find Agastya, born to a family of Brahmans so illustrious as to be +called "an ornament of the earth," living as an ascetic on the island of Kara +in the Indian Ocean.... "On what accomplishment have you set your hopes?" +Indra asked Agastya the Bodhisattva. "What is the object of your wishes that +has led you to leave your sorrowful friends and relatives, desert a household +and possessions that had been your happiness, and enter this way of life that +destroys all pleasures?" + The Bodhisattva replied according to the Dharma, in a way that immediately +laid Indra's anxiety to rest. "Repeated births tend to great sorrow," he +said. "So do the calamities of old age, sickness and death. All are just a +disturbance to the mind. My vow is to save all sentient beings from these +evils." + Relieved, Indra immediately offered, in return for such candid truth, the +fulfillment of any desire Agastya might name. "May the fire of covetousness +that burns insatiably even after obtaining a beloved wife, children, power and +riches never enter my heart," Agastya said. "Excellent, excellent," applauded +Indra and urged Agastya to request the fulfillment of still another desire. + "May the fire of hatred burn far from me," Agastya said. Pleased by this +game, in which Agastya so ingeniously taught the Dharma while appearing to +request the fulfillment of his desires, Indra urged him to go on. But this +time he was startled to hear Agastya's words. "May I never hear, see, speak +to, nor endure the annoyance and pain of staying with a fool," Agastya said. +"What do you mean?" Indra asked. "Those in distress deserve sympathy; the +root of distress is foolishness. How can you claim to be compassionate when +you abhor the very presence of those most due sympathy?" + Then Agastya reasoned in this way, to prove to Indra that one should +associate not with the foolish but with men of wisdom. "A fool cannot be +cured even by medical treatment," he said. "Habituated to wrong conduct +because of a deficiency in moral education, he urges his neighbors to follow +his impetuous way, inflamed by self-conceit and the affectation of wisdom. +When reprimanded, he becomes angry. There is no help for him." "How true," +Indra said. "Let me hear more jewellike, well spoken sentences." + "May I see, hear, live with and converse with a wise man," Agastya said, +"for these reasons: because the wise man, walking the path of virtue, draws +others along with him, and is never roused to impatience by harsh words spoken +for his own good." Again Indra was delighted. + -- H.H. the Dalai Lama, in "Generous Wisdom: Commentaries by His Holiness the + Dalai Lama XIV on the Jatakamala Garland of Birth Stories", translated by + Tenzin Dorjee +~ +(2) What one has to abandon: how to get rid of arrogance by means of an +antidote-- + "I'm not beyond my karma, the deeds I've done; + I'll still fall ill, age, die, and leave my friends." + Think like this again and yet again + And with this remedy avoid all arrogance. +"I will be sick, I will grow old, I will die, I will be separated from those +I love, my relations and so forth. In such manner, the fully ripened effect +of my actions will come to me and to no one else, and I am therefore not above +depending on what I did in former lives." To think like this again and again +is the antidote to such things as arrogance: make every effort not to become +arrogant by meditating on this antidote. + -- "Nagarjuna's Letter to a Friend: with Commentary by Kangyur Rinpoche + by Nagarjuna", translated by the Padmakara Translation Group, published + by Snow Lion Publications +~ +For every minute you are angry, you lose 60 seconds of happiness. + -- Ralph Waldo Emerson +~ +Inwardly, we defend against death by the very structure of our ego. Our ego +claims to have the ability to provide us with happiness based on its belief in +its own permanent existence. On the one hand, to believe in ego results in +denying death. On the other, to accept death is to question the very nature +of ego as a permanent, on-going structure and to confront very strong defense +mechanisms. As a result, we have strong resistance to contemplating death. +This resistance is also the same resistance that comes up when we meditate. +Please recognize it for what it is and move on. + -- Bruce Newman, in "A Beginner's Guide to Tibetan Buddhism: Notes from a + Practitioner's Journey", published by Snow Lion Publications +~ +Question: Could you please say something on the three kinds of suffering? + +His Holiness: One kind of suffering is like a headache or like yesterday's +flu: discomfort in the nose, watery eyes, and so forth. In short, it includes +all of those kinds of gross physical and mental sufferings that in ordinary +parlance we usually call "suffering." This is the first category. + +Then the second category is as follows. When we feel hungry and begin to take +food, at first we feel very happy. We take one mouthful, then two, three, +four, five... eventually, though it is the same person, the same food, and +the same time period, we begin to find the food objectionable and reject it. +This is what is meant by the "suffering of change." Practically every worldly +happiness and pleasure is in this second category. Compared to other forms of +suffering, at the beginning these more subtle forms of suffering seem +pleasurable; they seem to afford us some happiness, but this is not true or +lasting happiness, for the more we become acquainted with them, the more +involved we become with them, the more suffering and trouble they bring us. +That is the second category. + +Now as for the third category, I think it is fair to say that it is one's own +body. Roughly speaking, this is what it is. It is the body which is the +fruit of afflictions, a body originally created by afflictions. Because the +body is created by such causes, it is of the very nature of suffering. It +comes to act as the basis of suffering. This, then, is the third category. + -- H.H. the Dalai Lama, in "Answers: Discussions with Western Buddhists", + edited by Jose Ignacio Cabezon, published by Snow Lion Publications. +~ + One point I should make here is that some people, especially those who see +themselves as very realistic and practical, are sometimes too realistic and +obsessed with practicality. They may think, "The idea of wishing for the +happiness of all beings, of wanting what is best for every single one, is +unrealistic and too idealistic. Such an unrealistic idea cannot contribute in +any way to transforming the mind or to attaining some kind of mental +discipline, because it is completely unachievable." + ...They feel there is simply no point in thinking about all beings since +there is an infinite number of them. They may conceivably be able to feel +some kind of connection with some fellow human beings on this planet, but they +feel that the infinite number of beings throughout the universe has nothing to +do with their own experience as individuals. + ...What is important here, however, is to grasp the impact of cultivating +such altruistic sentiments. The point is to try to develop the scope of our +empathy in such a way that we can extend it to any form of life with the +capacity to feel pain and experience happiness. It is a matter of recognizing +living organisms as sentient, and therefore subject to pain and capable of +happiness. + ...Such a universal sentiment of compassion is very powerful, and there is +no need to be able to identify, in specific terms, with every single living +being in order for it to be effective. + ...Given patience and time, it is within our power to develop this kind of +universal compassion. + -- Tenzin Gyatso, the Fourteenth Dalai Lama, in "The Compassionate Life", + edited by David Kittelstrom, sponsored by Richard Gere and the Gere + Foundation +~ +Position + Different body postures open or compress particular energetic channels and +influence the flow of subtle energy. We use this understanding to aid +specific processes in the practice. The Tibetan tradition considers the +negative emotions to be more closely associated with the primary channel on +the right side of the body in men and on the left in women. When a man sleeps +on his right side, the channel that carries mostly negative prana is forced a +little closed and the left channel opens. Also the lung, the physical organ, +on that side is a bit compressed so the opposite lung is a little more +responsible for the breath. You are probably already familiar with effects +from lying on your side: when you lie on your right side you find it easier to +breathe through your left nostril. For men, we consider this position +beneficial to the movement of the positive wisdom prana through the left +channel. Women benefit from the reverse, opening the wisdom channel that is +on their right side by sleeping on their left. This affects dreams in a +positive fashion and makes the dream practice easier. Opening the flow of the +wisdom prana is a provisional expedient, as ultimately we want the balanced +prana to move into the central channel. + Furthermore, by paying attention to posture, awareness is kept more stable +during sleep. Where I come from, most people sleep on a three-foot by six- +foot Tibetan carpet. If one moves too much, one falls out of bed. But that +does not usually happen, because when one sleeps on something small, the +position of the body is held in the sleeping mind throughout the night.... +Here, in the big beds of the West, the sleeper can rotate like the hands of a +clock and not fall, but holding the position anyway will help maintain +awareness. + -- Tenzin Wangyal Rinpoche, in "The Tibetan Yogas of Dream and Sleep", + edited by Mark Dahlby, published by Snow Lion Publications +~ +The term emptiness does not carry here any connotation of void or of absolute +nothingness. It should be understood as the naturally open and serene state +of the mind. Thus, to affirm the emptiness of phenomena does not in any way +mean that they do not exist in the way that the horn of a hare or skyflowers +do not exist. Instead, emptiness refers to the insight that, at the ultimate +level, both interior phenomena--sensations, perceptions and the "I"--and +exterior phenomena--all the appearances of the phenomenal world--have no real +existence, although they do appear in different forms. The Heart Sutra +summarizes this as follows: + Form is emptiness, emptiness is form, + Emptiness is not other than form, + Form is not other than emptiness. + -- Jerome Edou in "Machig Labdron and the Foundations of Chod", published + by Snow Lion Publications +~ + Nonviolence does not mean that we remain indifferent to a problem. On the +contrary, it is important to be fully engaged. However, we must behave in a +way that does not benefit us alone. We must not harm the interests of others. +Nonviolence therefore is not merely the absence of violence. It involves a +sense of compassion and caring. It is almost a manifestation of compassion. +I strongly believe that we must promote such a concept of nonviolence at the +level of the family as well as at the national and international levels. Each +individual has the ability to contribute to such compassionate nonviolence. + How should we go about this? We can start with ourselves. We must try to +develop greater perspective, looking at situations from all angles. Usually +when we face problems, we look at them from our own point of view. We even +sometimes deliberately ignore other aspects of a situation. This often leads +to negative consequences. However, it is very important for us to have a +broader perspective. + We must come to realize that others are also part of our society. We can +think of our society as a body, with arms and legs as parts of it. Of course, +the arm is different from the leg; however, if something happens to the foot, +the hand should reach down to help. Similarly, when something is wrong within +our society, we must help. + -- H.H. the Dalai Lama, in "An Open Heart: Practicing Compassion in Everyday + Life", edited by Nicholas Vreeland, afterword by Khyongla Rato and + Richard Gere +~ + According to Highest Yoga Tantra, some persons attain Buddhahood in one +lifetime, and because these persons are not born with a body adorned with the +major and minor marks they must achieve such a body through the practice of +deity yoga. + Meditation on oneself as undifferentiable from a deity is the special +cause... for attaining Buddhahood. If one meditated only on emptiness and +did not cultivate any method--either that of the Perfection or that of the +Mantra Vehicle--one would fall to the fruit of a Hinayana Foe Destroyer. In +order to attain the definite goodness of the highest achievement, Buddhahood, +deity yoga is needed. Also, ...one must view one's body clearly as a divine +body and train in the pride of being a deity. Without deity yoga the Mantra +path is impossible; deity yoga is the essence of Mantra. + -- H.H. Dalai Lama, Tsong-ka-pa and Jeffrey Hopkins, in "Tantra in Tibet", + translated and edited by Jeffrey Hopkins, Snow Lion Publications +~ + Often we see other sentient beings as hassles: "This mosquito is +disturbing me. Those politicians are corrupt. Why can't my colleagues do +their work correctly?" and so on. But when we see sentient beings as being +more precious than a wish-fulfilling jewel, our perspective completely +changes. For example, when we look at a fly buzzing around, we train +ourselves to think, "My enlightenment depends on that fly." This isn't +fanciful thinking because, in fact, our enlightenment does depend on that fly. +If that fly isn't included in our bodhicitta, then we don't have bodhicitta, +and we won't receive the wonderful results of generating bodhicitta--the +tremendous purification and creation of positive potential. + Imagine training your mind so that when you look at every single living +being, you think, "My enlightenment depends on that being. The drunk who just +got on the bus--my enlightenment depends on him. The soldier in Iraq--my +enlightenment depends on him. My brothers and sisters, the teller at the +bank, the janitor at my workplace, the president of the United States, the +suicide bombers in the Middle East, the slug in my garden, my eighth-grade +boyfriend, the babysitter when I was a kid--my enlightenment depends on each +of them." All sentient beings are actually that precious to us. + -- Bhikshuni Thubten Chodron, in "Cultivating a Compassionate Heart: The + Yoga Method of Chenrezig", published by Snow Lion Publications +~ + We think of ourselves as being the child of specific parents, as belonging +to a certain gender and race, as a citizen of a specific nation, and as a +member of a caste, class and community within that country, etc. Our +identification with these transient reference points, be they racial, +linguistic, cultural, conceptual, or gender-specific, can become lifelong love +affairs, hate affairs, or guilt affairs. What Guru Rinpoche's birth +symbolizes for me is the fact that, from the first moment, he recognized the +unborn and undying nature of his mind, primordially pure awareness. He +identified with that, rather than with his body, wherever it may have come +from, be it a womb, a lotus, a stork, or a cabbage patch. Whatever physical, +linguistic, or conceptual worlds he adopted, he wore as ephemeral ornaments on +the infinite expanse of timeless awareness. + ...Guru Rinpoche was not an individual who followed a spiritual path until +illumination. He was an enlightened being who appeared in different guises +entirely as a manifestation to help others, including the guise of an +individual who followed the spiritual path. + -- Ngawang Zangpo, in "Guru Rinpoche: His Life and Times", published by + Snow Lion Publications +~ + ...if a person is experiencing some kind of mental dysfunction, it is +frequently understood that the mind itself has become too withdrawn in upon +itself, and that there is a corresponding physiological process involving the +energies themselves, which are closely associated with consciousness, also +entering into a dysfunctional state. + So, in the Buddhist view, it can happen, for example, that one's mind will +become depressed because of some environmental event. As a result of the mind +becoming depressed, there is a chemical, maybe an electrochemical, +transformation in the brain that has now occurred. The mental dysfunction +will then be aggravated. When that happens, there is a further chemical +response, which then avalanches upon itself. + ...on occasion, without any special external event taking place, there can +simply be a dysfunction or disruption in the balance of the elements within +the body. In that event, the internal circumstances are the dominant, +principal cause. + -- H.H. the Dalai Lama, from "Consciousness at the Crossroads: Conversations + with the Dalai Lama on Brain Science and Buddhism," edited by Zara + Houshmand, Robert B. Livingston and B. Alan Wallace, published by + Snow Lion Publications +~ +We cannot hold a torch to light another's path without brightening our own. + -- Ben Sweetland +~ +You must be the change you wish to see in the world. -- Mahatma Gandhi +~ + Enlightenment is not only... devoid of various types of contaminations, +pollutions, suffering, and afflictive emotions... but is also free from +various dualistic appearances. When you achieve such a state, you are +unfettered from all elaborations in the form of subject-object duality and +appearances of conventionality. + You are free not because the subject-object duality or conventional +appearances are objects of elimination in the sense that they are negative +emotions. Rather, you are free because these elaborations cease to exist when +you reach the state of enlightenment. In such a state, the mind of +enlightenment or omniscience is such that it is totally merged with emptiness. +To such a mind, no elaborations exist. + -- His Holiness the Dalai Lama, "Many Ways to Nirvana: Reflections and + Advice on Right Living", edited by Renuka Singh +~ +Guilt and Shame + When we meditate, things from the past come up, and we have to work with +them. We may remember times when we treated others horribly--hurting their +feelings, deceiving them, repaying their kindness with spite, manipulating +them, cheating them. While regret for these actions is appropriate and +necessary to purify these karmas, we often fall into guilt and shame instead. +Guilt and shame are obstacles to overcome on the path, because they keep us +trapped in our self-centered melodrama entitled "How Bad I Am." Regret, on the +other hand, realizes that we erred, leads us to purify, and motivates us to +refrain from acting like that in the future. + How do we counteract guilt and shame? One way is to recognize that the +person who did that action no longer exists. You are different now. Is the +person who did that action five years ago the same person you are now? If she +were exactly the same person, you would still be doing the same action. The +present "you" exists in a continuum from that person, but is not exactly the +same as her. Look back at the person you were with compassion. You can +understand the suffering and confusion she was experiencing that made her act +in that way. + -- Bhikshuni Thubten Chodron, "Cultivating a Compassionate Heart: The Yoga + Method of Chenrezig", foreword by H.H. the Dalai Lama, published by + Snow Lion Publications +~ +Throughout history it has been the inaction of those who could have acted, +the indifference of those who should have known better, the silence of the +voice of justice when it mattered most, that has made it possible for evil +to triumph. + -- Haile Selassie +~ +If we value the pursuit of knowledge, we must be free to follow wherever +that search may lead us. The free mind is no barking dog to be tethered +on a ten-foot chain. + -- Adlai E. Stevenson +~ +Our relationship with our practice must be based on reason and common sense. +The principal subject to be learned is the nature of the two levels of reality +[conventional and ultimate], the stages of which can be approached through a +combination of hearing, contemplation and meditation. It is very important +always to remember contemplation, which is the analysis and investigation of +the teachings through the use of reason. The two truths are speaking about +reality, not some intellectual fabrication. To investigate the teaching +critically is fully encouraged in the same way that medical students are +encouraged to apply their theories to real life and thus to witness their +validity.... Time may flow on, but the essential nature of the deeper +problems and mysteries that human beings encounter in the course of their +lives remains the same. Contemplation of the teachings of Buddha Shakyamuni +is merely contemplation of certain facets of reality, and it will cause to +unfold within us a deeper understanding of ourselves, our minds, and the +nature of our sense of being. As the teachings are merely pointing out key +facts of life, facts that, if realized, cause one to evolve in wholesome +directions, a critical investigation of them will only inspire trainees with +confidence. Reason well from the beginning and then there will never be any +need to look back with confusion and doubt. + -- H.H. the Dalai Lama, in "The Path to Enlightenment", published by + Snow Lion Publications +~ +b. Keeping bounteousness in mind + + Possessions are ephemeral and essenceless + Know this and give them generously to monks, + To brahmins, to the poor, and to your friends: + Beyond there is no greater friend than gift. + +Having realized that possessions such as food are inconstant and fluctuate, +that in changing and transforming they are devoid of essence, in order to make +them meaningful try to use them properly, giving to those with good qualities +(monks and brahmins), to those who suffer (the poor, the sick, and so forth), +to those who help you (friends) and to those you venerate (spiritual teachers +and parents). Even beyond the world there is no friend more sublime, more +beneficial, than giving, because it gives rise directly and indirectly to +ripened effects that are inexhaustible. + + -- Nagarjuna, "Nagarjuna's Letter to a Friend: with Commentary by Kangyur + Rinpoche", translated by the Padmakara Translation Group, published by + Snow Lion Publications +~ + A goloptious full-up pot, too, + And I don't know where it's got to, + No, I don't know where it's gone-- + Well, its funny. + -- A.A. Milne, "In Which Piglet Meets a Heffalump" +~ +With a determination to accomplish +The highest welfare for all sentient beings +Who surpass even a wish-granting jewel +I will learn to hold them supremely dear. + + Never mind neglecting other sentient beings, you should take them as a +treasure through which temporary and final aims can be achieved and should +cherish them one-pointedly. Others should be considered more dear, more +important than yourself. Initially, it is in dependence upon sentient +beings--others--that you generate the altruistic aspiration to highest +enlightenment. In the middle, it is in relation to sentient beings that you +increase this good mind higher and higher and practice the deeds of the path +in order to achieve enlightenment. Finally, in the end, it is for the sake of +sentient beings that you achieve Buddhahood. Since sentient beings are the +aim and basis of all of this marvelous development, they are more important +than even a wish-granting jewel, and should always be treated with respect, +kindness, and love. + + You might think, "My mind is so full of the afflictive emotions. How +could I possibly do this?" However, the mind does what it is used to. What +we are not used to, we find difficult, but with familiarity, previously +difficult things become easy. Thus Shantideva's Engaging in the Bodhisattva +Deeds says, + + "There is nothing which, with time, you cannot get used to." + + -- The Fourteenth Dalai Lama, His Holiness Tenzin Gyatso, "Kindness, + Clarity, and Insight," edited and translated by Jeffrey Hopkins, + co-edited by Elizabeth Napper, published by Snow Lion Publications +~ + Eventually, through the power of stabilizing meditation in which the +mind is set one-pointedly on its object of observation, an initial mental +pliancy--a serviceability of mind--is generated. As a sign that mental +pliancy is about to be generated, a tingly sensation is felt at the top of the +head. This pleasant feeling is compared to that of a warm hand placed on top +of the head after it has been shaved. When mental pliancy has been generated, +a favorable wind, or energy, circulates in the body, engendering physical +pliancy. Through this wind, or air, pervading the entire body, the +unserviceability of the body such that it cannot be directed to virtuous +activities in accordance with your wishes is removed. The generation of +physical pliancy, in turn, engenders a bliss of physical pliancy, a sense of +comfort throughout the body due to the power of meditative stabilization. + The bliss of physical pliancy induces a bliss of mental pliancy, making +the mind blissful. At first, this joyous mental bliss is a little too +buoyant, but then gradually it becomes more steady; at this point, one attains +an unfluctuating pliancy. This marks attainment of a fully qualified +meditative stabilization of calm abiding. + -- H.H. the Dalai Lama, Dzong-ka-ba and Jeffrey Hopkins, in "Yoga + Tantra: Paths to Magical Feats", published by Snow Lion Publications +~ + The more we generate an attitude of contentment in our lives, the happier +we will be and the more open we will be to engage in genuine Dharma practice. +Letting go of the eight worldly concerns brings mental peace right now. + The defining characteristic of a thought or action being Dharma is whether +or not we're attached to the happiness of this life. The eight worldly +concerns are completely involved with attachment to the happiness of this +life. How can we practice genuine Dharma when our self-centered mind is +fixated on getting our own way and making everyone and everything around us +suit our preferences and needs? + That doesn't mean the happiness of this life is bad or wrong. The Buddha +did not say that we should suffer in this life so that we'll get our reward in +heaven. The objects we're attached to and have aversion for aren't the +problem; there's nothing wrong with experiencing pleasure and happiness. +Those aren't the issue. Rather, attachment to pleasant feelings and to the +people, objects, and situations that cause them, and aversion to unpleasant +ones--it is these emotions that create trouble. They make us unhappy and +propel us to harm others in order to get what we want. The troublemakers of +attachment and hostility are what we want to abandon, not people and things. +There is nothing wrong with being happy. But when we're attached to it, we +actually create more unhappiness for ourselves. + -- Bhikshuni Thubten Chodron, "How to Free Your Mind: Tara the Liberator", + published by Snow Lion Publications +~ +When engaging in hearing, it is important to mix the mind, to familiarize the +mind, with what is being heard. The study of religion is not like learning +about history. It must be mixed with your mental continuum; your mind should +be suffused with it. A sutra says that the practices are like a mirror; your +actions of body, speech, and mind are like a face to be seen in the mirror; +and through the practices you should recognize faults and gradually get rid of +them. As it is said in the oral transmission, "If there is enough space +between yourself and the practices for someone else to walk through, then you +are not implementing them properly." + -- The Fourteenth Dalai Lama, His Holiness Tenzin Gyatso, in "Kindness, + Clarity, and Insight", edited and translated by Jeffrey Hopkins, + co-edited by Elizabeth Napper, published by Snow Lion Publications +~ +Question: What is the relationship of the mind and afflictive emotions? + +DL: The very entity of the mind, its nature of mere luminosity and knowing, is +not polluted by defilements; they do not abide in the entity of the mind. +Even when we generate afflictive emotions, the very entity or nature of the +mind is still mere luminosity and knowing, and because of this we are able to +remove the afflictive emotions. If you agitate the water in a pond, it +becomes cloudy with mud; yet the very nature of the water itself is not dirty. +When you allow it to become still again, the mud will settle, leaving the +water pure. How are defilements removed? They are not removed by outside +action, nor by leaving them as they are; they are removed by the power of +antidotes, meditative antidotes. + -- The Dalai Lama, "A Policy of Kindness: An Anthology of Writings By and + About the Dalai Lama", compiled and edited by Sidney Piburn, published + by Snow Lion Publications +~ + Every single sentient being wishes to be happy and free of suffering. By +no means does Buddhism say this is wrong; rather, this is where we start from. + The very root of this yearning for happiness, this yearning to be free of +suffering, is the fundamental expression of the buddha-nature. If for the +time being we turn our gaze away from the myriad ways that we can stray from +the agenda--trying to find happiness by buying a more luxurious car, or a +bigger house, or getting a better job--and just come back to the primary +desire of wishing to be happy, we find at the very source of our yearning for +happiness the buddha-nature wanting to realize itself. It's like a seed that +wants to spring into the sunlight. Sometimes it gets terribly contorted, when +we want to injure somebody else for the sake of our own happiness, but the +fundamental yearning is something to be embraced. + -- B. Alan Wallace, in "The Four Immeasurables: Cultivating a Boundless + Heart", edited by Zara Houshmand, published by Snow Lion Publications +~ +No bastard ever won a war by dying for his country. He won it +by making the other poor dumb bastard die for his country. + -- George S. Patton +~ + When you meditate with concentration, there are three particular +experiences that arise: bliss, clarity, and nonthought. Sometimes you feel +great joy, sometimes your mind is very clear, and sometimes there is complete +equanimity. To experience these you do not need to meditate for a long time, +although for a beginner these experiences will not last long because of the +limited ability of a beginner's meditation. + The experience of meditative bliss is greater than ordinary worldly +happiness. Sometimes when you are meditating, a feeling of blissfulness +suddenly arises from the subtle state of your mind and pervades your entire +body. This bliss is healthy and brings out your inner qualities. Some people +use drugs to induce blissfulness and visions, but drugs are external supports +that cannot bring lasting happiness. The bliss experienced in meditation can +last for many days, according to your ability to meditate. When you +experience this kind of bliss, on the outside you might look very poor, but +inside you remain very joyful. + The second main experience in meditation is clarity. Sometimes while +meditating you can suddenly feel that your mind is very clear and bright. +Even if you are meditating in the dark, you do not feel heavy or tired. +Sometimes your body feels very light and your mind is very clear, and many +kinds of reflections appear. Clarity brings great wisdom and the ability to +read other people's minds, as well as to see your own past and future lives. + The third main experience is nonthought, or a state of equanimity without +distractions. Beginners can also experience this. Nonthought is more settled +than the experiences of bliss and clarity. If you have thoughts, they +suddenly dissolve and you can remain continuously in meditation. As your +ability to meditate develops, your mind becomes more and more settled, so that +you can meditate for one hour or one week or one month without being +distracted by thoughts. You simply remain in the natural state for as long as +you want. + -- Khenchen Palden Sherab and Khenpo Tsewang Dongyal, in "Opening to Our + Primordial Nature", edited by Ann Helm and Michael White, published by + Snow Lion Publications +~ + ...when you probe deeply you will find that no matter how high an +existence a realm may be, even though it may be the highest state of +existence, as long as it is in this cycle of existence the beings there are in +the nature of sufferings, because they have the sufferings of pervasive +conditioning and are therefore under the influence or command of contaminated +actions and delusions. As long as one is not able to be free from such an +influence, there is no place for permanent peace or happiness. + Generally, the experiences that you normally regard as pleasurable and +happy, such as having the physical comfort of good facilities and so forth, if +they are examined at a deeper level, will be revealed to be changeable and +therefore in the nature of suffering. They provide you with temporary +satisfaction; because of that temporary satisfaction you regard them as +experiences of happiness. But if you keep on pursuing them, they will again +lead to the experience of suffering. Most of these pleasurable experiences +are not really happiness in the true sense of the word, but only appear as +pleasure and happiness in comparison to the obvious sufferings that you have. + -- H.H. the Dalai Lama, Tenzin Gyatso, in "Path to Bliss: A Practical Guide + to Stages of Meditation", translated by Geshe Thubten Jinpa, edited by + Christine Cox, published by Snow Lion Publications +~ + Once the conventional nature of the mind has been identified, then, +through analysing its nature, finally we will gradually be able to identify +the ultimate nature of the mind. If that is done, there is great progress +unlike anything else. + At the beginning we should meditate for half an hour. When we rise from +the session and various good and bad objects appear, benefit and harm are +manifestly experienced. Therefore, we should develop as much as we can the +realisation that these phenomena do not exist objectively and are mere +dependent-arisings of appearances, like illusions [in that they only seem to +be inherently existent]. We should meditate in this way in four formal +sessions: at sunrise, in the morning, afternoon, and evening. + -- H.H. the Dalai Lama, "The Buddhism of Tibet", translated and edited by + Jeffrey Hopkins, published by Snow Lion Publications +~ + Bodhichitta is the one practice we cannot do without. Even if we have been +given the precious oral instructions on realizing the nature of mind, they +will not be the sufficient cause for realization if we have not learned to +generate Bodhichitta. The great Dzogchen yogi Patrul Rinpoche said, + If we have only one thing, the precious Bodhichitta is enough. + If we have nothing else, we must have the method of the precious + Bodhichitta. + We should learn to develop Bodhichitta in a twofold way: through our +aspirations and through our actions. Aspiration Bodhichitta is our initial +wish that all sentient beings be liberated from the vast ocean of samsara's +suffering. Action Bodhichitta requires that we first generate aspiration +Bodhichitta, and practice the Six Paramitas as the method to establish the two +benefits of 1) attaining Buddhahood oneself to 2) be of ultimate benefit to +others. The way to practice aspiration and action Bodhichitta was taught by +the omniscient Patrul Rinpoche, who said, + The instructions for aspiration [Bodhichitta] are to practice the + Four Immeasurables; + The instructions for action [Bodhichitta] are to practice the Paramitas. + -- Anyen Rinpoche, "The Union of Dzogchen and Bodhichitta", translated by + Allison Graboski, published by Snow Lion Publications +~ + What are the techniques for heightening or lowering the mind? To heighten +the mind, you think about something that enlivens it, but not an object that +would generate desire. For instance, you could reflect on the value of +developing the meditative stabilization of calm abiding or on the value of +having attained a life as a human or on the value of having human +intelligence. Through such reflection, your mind will gain courage, thereby +causing its mode of apprehension to become heightened. + If, despite such a technique, laxity is not cleared away, it is better to +end the session and go to a place that is bright or that is high with a vast +view where you can see a great distance. Or, expose yourself to fresh air, or +throw cold water on your face. Then, return to the session. + When the mind becomes too heightened and thus scattered, what will lower +its mode of apprehension? As a technique to withdraw the mind inside, you +should reflect on a topic that sobers the mind, such as the suffering of +cyclic existence, or think "In the past I have been ruined by distraction, and +again now I will be ruined by distraction. If I do not take care now, it will +not be good." This will lower the mode of apprehension of the mind. + Since this is the case, a person who is cultivating calm abiding needs to +be in a state where such reflections will move the mind immediately. +Therefore, prior to working at achieving calm abiding, it is necessary to have +become convinced about many topics--such as those involved in the four +establishments in mindfulness--through a considerable amount of analysis. In +an actual session of cultivating calm abiding one is performing stabilizing +meditation, not analytical meditation, but if one has engaged in considerable +analysis of these topics previously, the force of the previous reflection +remains with the mind and can be recalled. Thus, when you switch to such +topics in order either to elevate or lower the mind, the mind will be +immediately affected. In this way, if ascertainment has been generated +previously, then reflecting on the value of meditative stabilization or the +value of a human lifetime will immediately heighten the mind, and reflection +on sobering topics such as the nature of the body or the ugliness of objects +of desire will immediately lower its mode of apprehension. + ...recognize when laxity and excitement arise and know the techniques for +overcoming them. + -- H.H. the Dalai Lama, "The Dalai Lama at Harvard: Lectures on the + Buddhist Path to Peace", translated and edited by Jeffrey Hopkins, + published by Snow Lion Publications +~ + How is it that mistaken mind overwhelms the unmistaken mind, unmistaken +reality? To give an example, during the day when the sun is shining one does +not see any stars and thus one would think that there are no stars at all, +that they just plain do not exist. Just so, afflictive emotions shine so +brightly and are so powerful that it is as if unmistaken reality, unmistaken +mind, does not exist at all. When you seek out this unmistaken mind from +within, you come to understand that there is an unmistaken mind--a reality of +the mind--that does not die, that does not scurry after pleasure and pain. +This mind that does not follow after pleasure and pain has a mode of being +that is emptiness--but not an emptiness in the sense of an empty house or an +empty vessel; rather, it is endowed with the inconceivable self-effulgence of +unmistaken reality, of pristine wisdom. + When you search for this that is beyond mistaken mind, mistaken mind just +stops; gradually like dawn there comes to be a time when pristine wisdom +manifests a little. With the beginning of dawn there is not just darkness but +some light, and so it is when the self-effulgence, the self-color, the self- +nature of the pristine wisdom shows itself a little; one generates a suspicion +that there is wisdom beyond mistaken mind. As Aryadeva says, "When you +generate doubt thinking that there might be such a reality, cyclic existence +is torn to tatters." How does cyclic existence come to be torn to tatters, or +wrecked, made into a mess by doubt? For instance, if a table is wrecked, +broken up, it cannot perform the function of a table; just so, when the self- +effulgence of unmistaken pristine wisdom begins to dawn with this state of +doubt, cyclic existence is wrecked and torn to tatters. + -- Mi-pam-gya-tso, in "Fundamental Mind: The Nyingma View of the Great + Completeness", practical commentary by Khetsun Sangpo Rinbochay, + translated and edited by Jeffrey Hopkins, published by Snow Lion + Publications +~ + The Jewel in the Crown Sutra states, "Donning the armor of loving- + kindness, while abiding in the state of great compassion, practice + meditative stabilization that actualizes the emptiness possessing + the best of all qualities. What is the emptiness possessing the + best of all qualities? It is that which is not divorced from + generosity, ethics, patience, effort, meditative stabilization, + wisdom, or skillful means." Bodhisattvas must rely on virtuous + practices like generosity as means to thoroughly ripen all sentient + beings and in order to perfect the place, body, and manifold retinue. + -- from "Stages of Meditation" by Kamalashila + +...Note that practice of generosity and the other perfections is essential. +This is because the fully enlightened state of Buddhahood is produced by the +realization of favorable causes and conditions. There is no causeless +production and nothing is produced by contrary causes. A Bodhisattva has many +wonderful advantages to help enhance the welfare of sentient beings; every +virtue performed by such a noble being is very powerful and effective. +Therefore, Bodhisattvas earnestly engage in the practice of the method aspects +of the path, including the six perfections, in order to swiftly actualize the +state of Buddhahood. + -- The Dalai Lama, "Stages of Meditation", translated by Venerable Geshe + Lobsang Jordhen, Losang Choephel Ganchenpa, and Jeremy Russell, + published by Snow Lion Publications +~ + Of the three kayas, the Dharmakaya is linked to our mind, Sambhogakaya to +our speech, or communicative principle, and the Nirmanakaya to our ordinary +body. + The process of the three blendings in brief is as follows. + We experience the clear light of the waking state naturally during cIimax, +and it can also be induced with yogic methods. Moreover, we naturally +experience it at the moment of going to sleep, and at the moment of death. +The principle here is that this clear light mind as experienced in each of the +three occasions (waking, sleep and death) is the highest experience of our +consciousness, and in it we dwell in a mental state of blissful, formless non- +duality similar to that of the Dharmakaya wisdom of a buddha. Thus when we +experience the clear light mind in any of these three occasions we should +blend it with the Dharmakaya. + The first movement from this clear light mind is likened to the +Sambhogakaya experience. In the waking state this occurs in our meditation +when we fall out of the clear light that is induced with yogic techniques and +the conceptual mind is aroused. In sleep it occurs after the clear light of +the moment of entering sleep passes and we begin to dream. At death it occurs +when the clear light flash at the moment of death passes and we leave our body +and enter the bardo realm. + A buddha's Sambhogakaya is only visible to an arya, or saint, and not to +an ordinary being; in the same way our thoughts, dreams, and bardo visions are +not visible to ordinary beings but nonetheless are experiences of form. These +subtle form experiences are to be linked to the natural realization of the +illusory, blissful, and perfect nature of being; they are to be seen as an +illusory theater made manifest for the benefit of the world. In other words, +they are to be blended with the Sambhogakaya. This is the second set of three +blendings. + The third blending is that of blending rebirth with the Nirmanakaya. +Rebirth from the bardo of the waking state occurs every time that we arise +from a meditation session and once more go about our ordinary life; rebirth +from the bardo of the sleep / dream state occurs when we wake up and once more +enter the work-a-day world; and rebirth from the bardo of becoming, or death +bardo, occurs when we complete the unwinding process of the afterlife state +and once again are ready to enter into a new body. + The basic principle underlying these three blendings is that what occurs +to us at the time of death also occurs to us in miniature form at the time of +going to sleep and can be induced in the waking state by means of the inner +heat yogas. + -- "Readings on the Six Yogas of Naropa," translated, edited and introduced + by Glenn H. Mullin, published by Snow Lion Publications +~ +If you want to tell people the truth, +make them laugh, +otherwise they'll kill you. + -- Oscar Wilde +~ +In the beginning there was nothing. +God said, "Let there be light!" +And there was light. +There was still nothing, +but you could see it a whole lot better. + -- Ellen DeGeneres +~ + Now, as [in the past], the concept of a transcendent god as creator has a +powerful and inspiring impact on the lives of those who believe in it. The +sense that their entire destiny lies in the hands of an all-powerful, +omniscient and compassionate being leads them to try to understand the +workings and key message of this transcendent being. Then, when they come to +realise that this transcendent being embodies love and infinite compassion, +they try to cultivate love and compassion towards their fellow beings as the +qualities through which to express love for their creator. They also gain +confidence and inspiration through a sense of intimacy or connectedness to +this loving, transcendent being. + Although, metaphysically speaking, Buddhists reject any notion of a +transcendent creator or god, some individual Buddhists do relate to certain +higher beings, such as the goddess Tara, as an independent and real being with +power over their destiny. For these practitioners Tara is their sole refuge, +their greatest object of veneration and their trusted guardian and protector. +What this suggests is that the inclination to seek refuge in an external +source is something deeply natural for us as human beings. + But it is also clear that for other people the metaphysical concept of a +transcendent being is unacceptable. Questions form in their minds, such as: +who created the creator--in other words--where does the transcendent being +come from? And how can we posit a true beginning? People with this type of +mental disposition look elsewhere for explanations. + -- The Dalai Lama, in "Lighting the Way", translated by Geshe Thupten + Jinpa, published by Snow Lion Publications +~ + Due to misunderstanding each other's needs and concerns, miscommunication +occurs on the international level as well [as the personal level]. In all +these situations--personal and international--freeing ourselves from our +narrow understanding of a situation by seeing it from the other's viewpoint is +an effective remedy for anger. We can ask ourselves, "If I had grown up in +that person's family, society, time in history, and cultural conditions, what +would my needs and concerns be in this situation?" + When we look at the situation from the other person's viewpoint, sometimes +we see that she perceives it differently than we thought she did. Other +times, we realize that we have little idea of how a situation appears to +another person or what her needs and concerns are. Therefore, we need to ask +her; and when she responds, we need to listen, without interrupting. It is +all too easy, when someone explains her view to us, to correct her or tell her +that she should not feel the way she does. This only inflames the other +person, and convinces her, with good reason, that we don't understand. +Rather, we need to listen from our heart to what she says. After she has +fully expressed herself, we can share our perspectives, and generally, a +productive discussion will ensue. + -- Thubten Chodron, in "Working with Anger", published by Snow Lion + Publications +~ + ...it is extremely important to look inward and try to promote the right +kind of attitude, which is based on awareness of reality. A sense of caring +for others is crucial. And it is actually the best way of caring for oneself. +...the moment you think of others, this automatically opens our inner door-- +you can communicate with other people easily, without any difficulties. The +moment you think just of yourself and disregard others, then because of your +own attitude, you also get the feeling that other people also have a similar +attitude toward you. That brings suspicion, fear. Result? You yourself lose +inner calmness. Therefore, I usually say that although a certain kind of +selfishness is basically right--self and the happiness of that self are our +original right, and we have every right to overcome suffering--but selfishness +that leads to no hesitation to harm another, to exploit another, that kind of +selfishness is blind. Therefore, I sometimes jokingly describe it this way: +if we are going to be selfish, we should be wisely selfish rather than +foolishly selfish. + I feel that the moment you adopt a sense of caring for others, that brings +inner strength. Inner strength brings us inner tranquility, more self- +confidence. Through these attitudes, even though your surroundings may not be +friendly or may not be positive, still you can sustain peace of mind. + -- "The Art of Peace: Nobel Peace Laureates Discuss Human Rights, + Conflict and Reconciliation", by the Dalai Lama and other Nobel + Laureates, edited by Jeffrey Hopkins, published by Snow Lion +~ + If we do not uncover [our] problems--and I saw this in myself--we risk +placing a veneer of spirituality over deeply buried emotional wounds from +childhood that do not simply go away. + ...When this happens there is greater potential for our spirituality to +become simply another expression of our personal pathology. We can falsify +the qualities valued in the path without realizing it. Renunciation can +become another level of denial and avoidance; compassion can become a sickly +sentimentality that has no substance to it. Our desire to help others can +come from "compulsive caring," or a compulsion to sacrifice ourselves because +we feel worthless. The Buddhist idea of emptiness can likewise be falsified +by the desire to disappear psychologically and merge or lose ego boundaries. +Lack of identity, formless vagueness, and absence of boundaries do not +exemplify the Buddhist idea of emptiness. My own version of this +misconception was to try to live an ideal of the pure and pious only to find +it was a form of repression I could not ultimately sustain. + At the heart of Buddhist practice is the search for a solution to our +fundamental wounds. Healing the emotional damage we often carry within is +truly the object of this practice. If we wish to resolve these problems, we +need to be open and honest about their reality within us. Only when we do +will any spiritual practice address what we need. The aim of Buddhist +practice is not a spiritual transcendence that dissociates from our suffering. +Nor is it the search for salvation in some form of external divine being that +we hope will save us in our distress. As one of my teachers, Lama Thubten +Yeshe, once said, "Buddhism is very practical; you just have to recognize that +your mind is the cause of suffering. If you change your mind, you can find +liberation." This message is very simple but by no means easy to follow. In +order to do so, however, we must begin to recognize where we are +psychologically wounded. + -- Rob Preece, in "The Wisdom of Imperfection: The Challenge of + Individuation in Buddhist Life", published by Snow Lion Publications +~ +Direct Experience + In the Dzogchen teachings, a teacher explains methods that you can apply +for discovering that state. When you say that you are practicing or following +Dzogchen teachings, it doesn't mean that you are reciting some prayers or +mantras, or doing some visualization. It means that, following a teacher and +using methods, you discover that state. When you have discovered that state, +then you still need many kinds of methods for realizing it. Discovering the +state of your real nature and realizing it are completely different things. + Many people have the idea that when they have had some experience or +discovery, they are enlightened; however, this discovery does not mean they +are enlightened. The state of enlightenment means you have direct knowledge +of what the state of rigpa is, and you are not just learning through +intellectual study. When you follow a teaching in an intellectual way, you +have many ideas at first--thinking, judging, and making analysis. You can +follow or reject these ideas; but when you have many problems, you discover +that perhaps this is not real knowledge. It is like following something +blindly because you haven't had any direct experience. Direct introduction +and discovering our real nature mean we have direct experience through our +senses, and that through these experiences we discover our real nature. + For example, if I show you an object, you can look at it and know its form +and color. Now if I ask you to forget about it, you can't. If I ask you to +change your idea about that object, you can't. Why? Because seeing that +object is your direct experience. Discovering your real nature is similar to +that. + When you are studying in an intellectual way, you are following another +person's idea. For example, you can believe your teacher today, but maybe +what your teacher says will not be true for you tomorrow. You can always +change your ideas. You have this problem because you have not discovered your +state. This is the weak point of intellectual study. + -- Chogyal Namkhai Norbu, "Dzogchen Teachings", edited by Jim Valby and + Adriano Clemente, published by Snow Lion Publications +~ + Howard Cutler: "Have there been situations in your life that you've +regretted?" + Dalai Lama: "Oh, yes. Now for instance there was one older monk who lived +as a hermit. He used to come to see me to receive teachings, although I think +he was actually more accomplished than I and came to me as a sort of +formality. Anyway, he came to me one day and asked me about doing a certain +high-level esoteric practice. I remarked in a casual way that this would be a +difficult practice and perhaps would be better undertaken by someone who was +younger, that traditionally it was a practice that should be started in one's +midteens. I later found out that the monk had killed himself in order to be +reborn in a younger body to more effectively undertake the practice..." + Surprised by this story, I remarked, "Oh, that's terrible! That must have +been hard on you when you heard..." The Dalai Lama nodded sadly. "How did +you deal with that feeling of regret? How did you eventually get rid of it?" + The Dalai Lama silently considered for quite a while before replying, "I +didn't get rid of it. It's still there. But even though that feeling of +regret is still there, it isn't associated with a feeling of heaviness or a +quality of pulling me back. It would not be helpful to anyone if I let that +feeling of regret weigh me down, be simply a source of discouragement and +depression with no purpose, or interfere with going on with my life to the +best of my ability." + At that moment, in a very visceral way, I was struck once again by the +very real possibility of a human being's fully facing life's tragedies and +responding emotionally, even with deep regret, but without indulging in +excessive guilt or self-contempt. The possibility of a human being's wholly +accepting herself or himself, complete with limitations, foibles, and lapses +of judgment. The possibility of recognizing a bad situation for what it is +and responding emotionally, but without overresponding. The Dalai Lama +sincerely felt regret over the incident he described but carried his regret +with dignity and grace. And while carrying this regret, he has not allowed it +to weigh him down, choosing instead to move ahead and focus on helping others +to the best of his ability. + -- His Holiness the Dalai Lama and Howard C. Cutler, M.D., in "The Art of + Happiness: A Handbook for Living" +~ +Train yourself in three hard disciplines. + These are the difficult practices of mindfulness, of expulsion and of +'interrupting the flow.' + As for the first of these, the difficult practice of mindfulness, it is +necessary to recognize afflictive emotions as soon as they arise and it is +hard, at first, to remain sufficiently aware to be able to do this. However, +when negative emotions arise, we should identify them as anger, desire or +stupidity. Even when emotions have been recognized, it is not easy to drive +them out with the antidote. If, for instance, an uncontrollably strong +emotion comes over us, so that we feel helplessly in its power, we should +nevertheless confront it and question it. Where are its weapons? Where are +its muscles? Where is its great army and its political strength? We will see +that emotions are just insubstantial thoughts, by nature empty: they come from +nowhere, they go nowhere, they remain nowhere. When we are able to repel our +defiled emotions, there comes the difficult practice of 'interrupting the +flow.' This means that, on the basis of the antidote described, defiled +emotions are eliminated just like a bird flying through the air: no trace is +left behind. These are practices in which we should really strive. + -- Dilgo Khyentse Rinpoche, in "Enlightened Courage: An Explanation of the + Seven-Point Mind Training", translated by The Padmakara Translation + Group, published by Snow Lion Publications +~ + Howard Cutler: "...am I right in assuming that you would consider solitary +meditation to be a productive activity? Would you consider to be productive +our example of a monk who is a hermit, who has little contact with anybody +else and spends his or her life just in meditation, trying to achieve +liberation?" + Dalai Lama: "Not necessarily. From my viewpoint, there can be both +productive meditation and unproductive meditation." + HC: "What's the difference?" + DL: "[Some] practitioners and other kinds of meditators practice different +techniques, some with closed eyes, sometimes open eyes, but the very nature of +that meditation is to become thoughtless, in a state free of thoughts. But in +a way, this is a kind of retreat, like they are running away from trouble. +When they actually face trouble, carry on their daily life and face some real +life problems, nothing has changed. Their attitudes and reactions remain the +same. So that kind of meditation is just avoiding the problem, like going on +a picnic, or taking a painkiller. It's not actually solving the problem. +Some people may spend many years doing these practices, but their actual +progress is zero. That's not productive meditation. Genuine progress occurs +when the individual not only sees some results in achieving higher levels of +meditative states but also when their meditation has at least some influence +on how they interact with others, some impact from that meditation in their +daily life--more patience, less irritation, more compassion. That's +productive meditation. Something that can bring benefit to others in some +way." + -- His Holiness the Dalai Lama and Howard C. Cutler, M.D., "The Art of + Happiness at Work +~ + As human beings, we are all the same. So there is no need to build some +kind of artificial barrier between us. At least my own experience is that if +you have this kind of attitude, there is no barrier. Whatever I feel, I can +express; I can call you "my old friend". There is nothing to hide, and no +need to say things in a way that is not straightforward. So this gives me a +kind of space in my mind, with the result that I do not have to be suspicious +of others all the time. And this really gives me inner satisfaction, and +inner peace. + So I call this feeling a "genuine realization of the oneness of the whole +of humanity". We are all members of one human family. I think that this +understanding is very important, especially now that the world is becoming +smaller and smaller. In ancient times, even in a small village, people were +able to exist more or less independently. There was not so much need for +others' co-operation. These days, the economic structure has completely +changed.... We are heavily dependent on one another, and also as a result of +mass communication, the barriers of the past are greatly reduced. Today, +because of the complexity of interdependence, every crisis on this planet is +essentially related with every other, like a chain reaction. Consequently it +is worthwhile taking every crisis as a global one. Here barriers such as +"this nation" or "that nation", "this continent", or "that continent" are +simply obstacles. Therefore today, for the future of the human race, it is +more important than ever before that we develop a genuine sense of brotherhood +and sisterhood. I usually call this a sense of "universal responsibility". + -- His Holiness the Dalai Lama, from "Dzogchen: The Heart Essence of the + Great Perfection", translated by Geshe Thupten Jinpa and Richard Barron + (Chokyi Nyima), edited by Patrick Gaffney, published by Snow Lion + Publications +~ + ...several great Kagyu and Sakya masters... have expressed the stages [of +sutra and tantra paths] in terms of the tradition known as "parting ourselves +from the four forms of clinging." + First we part from clinging to this life. Instead of total involvement +with affairs of this life, we involve ourselves with future lives. We +accomplish this by thinking about our precious human life with all its +freedoms and endowments for spiritual growth, how we lose it because of death +and impermanence, and then the karmic laws of behavioral cause and effect that +shape our future lives. Next we part from clinging to future lives and +involve ourselves, instead, in the quest for liberation. By thinking about +all the suffering of uncontrollably recurring rebirth, or samsara, we generate +sincere renunciation of it--the strong determination to be free and attain the +total liberation that is nirvana. + -- His Holiness the Dalai Lama and Alexander Berzin, in "The Gelug/Kagyu + Tradition of Mahamudra", published by Snow Lion Publications +~ +Being the Mirror + When we say that we have knowledge, or that we have discovered our real +nature and we are in this nature, that means that we are "being the mirror." +You see, "being the mirror" or "looking in the mirror" are two completely +different things. If we "are the mirror," then we have no concept of +dualistic vision. + If a reflection manifests in the mirror, why is it manifesting? There are +two reasons. One is because the mirror has the capacity to manifest infinite +reflections. This is the mirror's quality. If there is an object in front of +the mirror, whose capacity it is to reflect, naturally a reflection will +appear in the mirror. Furthermore, the mirror has no idea of checking or +accepting the object it is reflecting. The mirror doesn't need any program +for that. This is what is called its qualification, or infinite potentiality. + In the same way, when we have infinite potentiality, but we are ignorant +of our real nature, then we always conceive that "I am here" and "the object +is there," "I am looking and seeing an object," and so on. We do not discover +that we are like a mirror, and if we never discover this, then of course there +is no way that we can function like the mirror. When you discover that you +are like the mirror, then there is a possibility that you will be the mirror. + When you are the mirror, then you have no problems with reflections--they +can be big, small, nice, ugly, any kind. For you, the reflections are only a +manifestation of your quality, which is like that of a mirror. When you have +no problems with reflections, then you understand self-liberation. You are +not changing or transforming something. You are only being in your real +nature. + -- Chogyal Namkhai Norbu, from "Dzogchen Teachings", edited by Jim Valby + and Adriano Clemente, published by Snow Lion Publications +~ + It is important to recognize the difference between an enlightened +experience and the state of enlightenment. To penetrate the veil is to see +the nature of reality for the first time. This enlightened experience in the +Zen tradition might be called a satori. This is a powerful shift of insight +that shakes our reality. No longer can we live with the delusion we may have +once held. Our solidly held concepts about reality begin to crumble. Samsara +shakes, as Lama Yeshe once put it. This experience may not be comfortable. +To come so close to this existential threshold challenges our secure sense of +identity and can be frightening. Indeed, as a Tibetan lama once said, this +fear is a sign that we are close to the edge. We are beginning to recognize +the lack of substance of our ego-identity. Our "wisdom eye" has opened to a +new truth--an ultimate truth, as opposed to relative truth. + When we penetrate the veil, however, the work is not yet done. We may +have had an enlightened experience, but there is further to travel. As Gen +Jhampa Wangdu once said while I was in retreat, it is not difficult to +experience emptiness; the problem is holding it. For this insight to have its +full effect, the mind needs to be able to sustain awareness for prolonged +periods of time. Tibetan teachers will sometimes say we may hit the nail, but +only with a quality of focused attention can we repeatedly do so. With the +development of tranquil abiding, the veil can be cleared completely in the way +the red ring of fire created by the incense burn[ing] slowly expands and +consumes the entire film of tissue paper. The mind is gradually cleansed of +the emotional turmoil and confusion that is generated by the misconceptions we +have about reality. + -- Rob Preece, in "The Wisdom of Imperfection: The Challenge of + Individuation in Buddhist Life", published by Snow Lion Publications +~ + ...if we see others in trouble, although we cannot immediately take their +suffering upon ourselves, we should make the wish to be able to relieve them +from their misfortunes. Prayers like this will bear fruit eventually. Again, +if others have very strong afflictive emotions, we should think, "May all +their emotions be concentrated in me." With fervent conviction, we should +persist in thinking like this until we have some sign or feeling that we have +been able to take upon ourselves the suffering and emotions of others. This +might take the form of an increase in our own emotions or of the actual +experience of the suffering and pain of others. + This is how to bring hardships onto the path in order to free ourselves +from hopes and fears--hopes, for instance, that we will not get ill, or fears +that we might do so. They will thus be pacified in the equal taste of +happiness and suffering. Eventually, through the power of Bodhichitta, we +will reach the point where we are free even from the hope of accomplishing +Bodhichitta and the fear of not doing so. Therefore we should have love for +our enemies and try as much as possible to avoid getting angry with them, or +harbouring any negative thoughts towards them. We should also try as much as +possible to overcome our biased attachment to family and relatives. If you +bind a crooked tree to a large wooden stake, it will eventually grow straight. +Up to now, our minds have always been crooked, thinking how we might trick and +mislead people, but this [Bodhichitta] practice, as Geshe Langri Tangpa said, +will make our minds straight and true. + -- Dilgo Khyentse Rinpoche, "Enlightened Courage: An Explanation of the + Seven Point Mind Training", translated by the Padmakara Translation + Group, published by Snow Lion Publications +~ +Q: ...what is the nature of the mindstream that reincarnates from lifetime to +lifetime? + +A: ...If one understands the term "soul" as a continuum of individuality from +moment to moment, from lifetime to lifetime, then one can say that Buddhism +also accepts a concept of soul; there is a kind of continuum of consciousness. +From that point of view, the debate on whether or not there is a soul becomes +strictly semantic. However, in the Buddhist doctrine of selflessness, or "no +soul" theory, the understanding is that there is no eternal, unchanging, +abiding, permanent self called "soul." That is what is being denied in +Buddhism. + +Buddhism does not deny the continuum of consciousness. Because of this, we +find some Tibetan scholars, such as the Sakya master Rendawa, who accept that +there is such a thing as self or soul, the "kangsak ki dak" (Tib. gang zag gi +bdag). However, the same word, the "kangsak ki dak," the self, or person, or +personal self, or identity, is at the same time denied by many other scholars. + +We find diverse opinions, even among Buddhist scholars, as to what exactly the +nature of self is, what exactly that thing or entity is that continues from +one moment to the next moment, from one lifetime to the next lifetime. Some +try to locate it within the aggregates, the composite of body and mind. Some +explain it in terms of a designation based on the body and mind composite, and +so on.... One of the divisions of [the "Mind-Only"] school maintains there is +a special continuum of consciousness called alayavijnana which is the +fundamental consciousness. + -- H.H. the Dalai Lama, "Healing Anger: The Power of Patience from a Buddhist + Perspective", translated by Geshe Thupten Jinpa, published by Snow Lion + Publications +~ + In the Great Treatise on the Stages of the Path Je Tsongkhapa says that +when we are training ourselves in any of the perfections, for instance in +generosity, we should make sure that we practice all the other five +perfections--in this case ethical discipline, patience, enthusiastic effort, +concentration, and wisdom--and the six excellent factors. When we perform a +generous action, ethical discipline will be included if we take care to +refrain from doing anything unethical at the same time. In certain +situations, for instance, we may be tempted to speak harshly or +condescendingly as we give. + Generosity gives rise to abundance, and by insuring that our practice is +complete, we create the right environment to use these resources +constructively. Sometimes when we give, people respond ungratefully. If we +can resist getting upset, we are practicing patience. Giving not out of a +sense of obligation or reluctantly nor with a wish to outdo others but with +joy is the practice of enthusiastic effort. Directing our full attention to +an act of generosity is concentration. Discerning and understanding what is +appropriate to give and what is not, and remembering that the giver, the act +of generosity, and the recipient are all interdependent and empty of inherent +existence are the practice of wisdom. Including these different factors in +our actions will bring many excellent results such as a good body and mind, +the resources we need, a pleasant appearance, supportive companions, the +ability to complete what we undertake, and the focus not to be distracted by +the disturbing emotions and so forth. This is how to insure that we will +enjoy many conducive conditions in a future human life. On the other hand, +our miserliness or impatience now could make us face many difficult +circumstances in the future. + -- Geshe Sonam Rinchen, "How Karma Works: The Twelve Links of Dependent + Arising", translated by Ruth Sonam, published by Snow Lion Publications +~ + Shakyamuni Buddha, even when he was a trainee on the path, was solely +concerned in both thought and action with others' welfare. Whenever he found +an opportunity to work for others, no matter what difficulties he faced, he +was never discouraged. He never hated obstacles and hardships encountered on +the way. Instead, the difficult situations facilitated his being more +courageous and determined to accomplish others' welfare. Just because he was +so determined to work for others in the past, even as a trainee on the path, +it is needless to say how much more it is so with him now as a completely +enlightened person. + As the saying goes, "A past life story of a teacher is an enlightening +practice for posterity." + -- "Generous Wisdom: Commentaries of His Holiness the Dalai Lama XIV on + the Jatakamala Garland of Birth Stories," translated by Tenzin Dorjee, + edited by Dexter Roberts +~ +When you get tired, it is appropriate to repeat mantra. However, for a +beginner the main part of the meditation revolves around the six deities*, +which should be cultivated carefully and leisurely. This is because clear +appearance of oneself as a deity must be achieved for the sake of amassing the +two collections of merit and wisdom, achieving firm meditative stabilisation, +and transforming all physical and verbal actions into powerful aids for +others' welfare. Hence, before repeating mantra, the yoga of non-dual +profundity (realisation of emptiness) and manifestation (appearance as a +deity) should be sustained, developing clarity in observing the divine form +and in ascertaining its lack of inherent existence. When, having done this +one-pointedly, you become tired, then for the sake of resting begin repeating +mantra. ...Tsong-ka-pa also says that in the approximation phase meditation +is chief, mantra repetition is secondary. + * ultimate, sound, letter, form, seal, and sign + -- H.H. the Dalai Lama, Tsong-ka-pa and Jeffrey Hopkins, "Deity Yoga In + Action and Performance Tantra", published by Snow Lion Publications +~ +There isn't a single one of us who has never felt hostile and angry, so we +know about the effects of anger. Does it make us feel better or worse? It +stirs us up, makes us miserable and destroys our tranquillity. It is quite +easy to recognize anger as a foe and to see how it harms us because its +destructiveness is apparent. But we find it much harder and are also +reluctant to acknowledge the harm done by attachment because it is a foe +masquerading as a friend. When desire or attachment first arises, it feels +quite pleasurable but eventually it lands us in trouble. It wants to possess +what it has fabricated and we reach out for something which, in fact, does not +exist. Failure to get what we want frustrates us and anger quickly follows. +The third of the poisons, confusion or ignorance, simply stimulates desire and +anger and lies at the root of all the disturbing emotions. + -- Geshe Sonam Rinchen, "Eight Verses for Training the Mind", translated + and edited by Ruth Sonam, published by Snow Lion Publications +~ +The Sevenfold Cause-and-Effect Method + If we have been reborn time after time, it is evident that we have needed +many mothers to give birth to us. ...the first cause bringing about +bodhicitta is the recognition that all beings have been our mother. + The love and kindness shown us by our mother in this life would be +difficult to repay. She endured many sleepless nights to care for us when we +were helpless infants. She fed us and would have willingly sacrificed +everything, including her own life, to spare ours. As we contemplate her +example of devoted love, we should consider that each and every being +throughout existence has treated us this way. Each dog, cat, fish, fly, and +human being has at some point in the beginningless past been our mother and +shown us overwhelming love and kindness. Such a thought should bring about +our appreciation. This is the second cause of bodhicitta. + As we envision the present condition of all these beings, we begin to +develop the desire to help them change their lot. This is the third cause, +and out of it comes the fourth, a feeling of love cherishing all beings. This +is an attraction toward all beings, similar to what a child feels upon seeing +his or her mother. This leads us to compassion, which is the fifth cause of +bodhicitta. Compassion is a wish to separate these suffering beings, our +mothers of the past, from their miserable situation. At this point we also +experience loving-kindness, a wish that all beings find happiness. As we +progress through these stages of responsibility, we go from wishing that all +sentient beings find happiness and freedom from suffering to personally +assuming responsibility for helping them enter this state beyond misery. This +is the final cause. As we scrutinize how best to help others, we are drawn to +achieving the fully enlightened and omniscient state of Buddhahood. + -- The Dalai Lama, "An Open Heart: Practicing Compassion in Everyday Life", + edited by Nicholas Vreeland +~ +Among the seven branches [qualities of Buddhahood]--complete enjoyment, +union, great bliss, non-inherent existence, compassion, uninterrupted +continuity, and non-cessation--three are found only in tantra--complete +enjoyment, union, and great bliss--and the other four are common to both +sutra and tantra, although non-inherent existence can also be put in the group +specific to tantra when it is considered as the object ascertained by a bliss +consciousness.... In Yoga Tantras the bliss arising from holding hands or +embracing is used in the path; in Performance Tantras, from laughing; and in +Action Tantras, from looking. The four tantras are similar in that they all +use desire for the attributes of the desire realm on the path. + -- H.H. the Dalai Lama, Tsong-ka-pa, and Jeffrey Hopkins, "Tantra in Tibet", + published by Snow Lion Publications +~ + How then does the mistaken idea, that things exist from their own side, +operate? Whatever appears to the mind appears as if it existed truly from its +own side. ...Now if the object existed as it appears to you, then, when you +searched for it, you could actually find a real [object]. So, we must ask +ourselves whether or not this object, when searched for, is to be found or +not. If the object is not found when it is searched for, we must conclude +that it does not exist from its own side, that when the label is applied to +its basis, it is not so labeled because the basis somehow bears within it +something which is the object. At this point, one must conclude that the +object does not exist as it appears to, but then, one may wonder whether it +exists at all. + Things, however, are not utterly non-existent. They do exist nominally. +So things do exist, but they do not exist from the side of the basis of the +label. And hence, though they do exist, because they do not exist within the +object itself, they must exist only as they are labeled by the subject (the +conceptual mind, for example). There is no other way for the object to exist +apart from the way it is posited by conceptual thought. This is then what we +mean when we say that all phenomena are merely labeled by conceptual thought. +However, things do not appear to us as if they were mere conceptually labeled +entities. Instead, they appear as if they existed from their own side. +Therefore, it is a mistake to think that things exist as they appear. + -- The Dalai Lama, "Answers: Discussions with Western Buddhists", edited + by Jose Ignacio Cabezon, published by Snow Lion Publications +~ +Meditation is hard work, but it is also the most rewarding thing we can do +with our time. As we begin to see the mechanisms we previously took for +granted and start to understand them, the knots inside our minds begin to +loosen. We feel a tremendous sense of freedom, space and release inside us. +As we begin to understand our warped thinking patterns and our neuroses, we +see them directly. We begin to develop compassion for ourselves, for our pain +and confusion. Now that we start to look with clarity, we can see the pain +and confusion in the eyes of other people, and we naturally develop compassion +for them. It doesn't matter how outwardly successful people may appear, we +can see their pain when we look into their eyes. It is very rare to come +across people whose eyes are truly sparkling with joy. + -- Tenzin Palmo, "Reflections on a Mountain Lake: Teachings on Practical + Buddhism", published by Snow Lion Publications +~ + ...recognizing that we have certain obligations, and recognizing at the +same time that spiritual practice is the core of a meaningful life, what do we +do? There really is an answer. It is not easy, but it is tremendously +fruitful, and it keeps on opening and opening further: transform those actions +that are already obligations by applying Dharma to them. + Take eating, for instance. We have to do it two or three times a day, but +we don't have to wolf down the food. There is no one who cannot sit and pause +first for thirty seconds. Even fast-food is worth the thirty seconds it takes +to recognize the immense number of beings who have provided us with this food. +Pausing like this ties us into the community of life, at least on planet +earth, as we recognize that we are indebted to others. We have received, and +as we take the food, let us do it with the aspiration, "May this be returned. +May I use my abilities to the fullest to serve those who have served me." And +that includes everyone, directly or indirectly. The service may occur on a +very mundane level, but insofar as we mature spiritually, our responsibility +increases according to our abilities. Not because someone tells us, "Now you +have to do this," but simply as we gain insight into the nature and sources of +suffering and of contentment, then we have something all the more valuable to +offer others. + -- B. Alan Wallace, "The Seven-Point Mind Training", edited by Zara + Houshmand, published by Snow Lion Publications +~ +The Paramita of Meditative Concentration + It is said in the Teachings that without taking up the Paramita of +Meditative Concentration, it would be impossible to realize the nature of +mind. We should think of meditative concentration as the practice that brings +stability to our minds, and creates the good conditions to practice unfocused +meditation--in other words, resting in the uncontrived natural state. + If we make a quick examination of our own mind, we can see the reason this +kind of stability is so crucial. Although physics has observed light to be +the fastest traveling phenomenon known to man, actually the speed at which our +minds travel is even faster. We can circle the globe in a matter of seconds, +and our minds generate doubts, emotions, and conceptual thoughts at a speed +that defies that of all other phenomena. Because we lack basic mental +stability, conceptual thoughts arise endlessly. So, if our goal is to realize +the nature of mind, we first have to learn to still our minds, and free +ourselves from distraction. The method for quieting the mind is called +"meditative concentration." Once we have gained some initial mind stability, +it is even more important that we continue our training so that this stability +will increase. Without such stability, it is impossible for us to +successfully learn to abide in the uncontrived view. + -- Anyen Rinpoche, "The Union of Dzogchen and Bodhichitta", translated + by Allison Graboski, published by Snow Lion Publications +~ +5. Your present naked awareness + +How amazing! + +Your present, naked awareness-- +Unspoiled by thoughts of past, present, or future, +Not fettered by mind grasping to so-called "meditation" +Nor falling into a pervasive blankness of so-called "non-meditation"-- +The natural state nakedly sustained, +Is the practice of Great Perfection. + +Regardless of what thoughts arise during that practice, +To reject negative ones or foster positive ones is unnecessary. +Mere recognition liberates them in their own ground. +Take this liberation upon arising as the path's key point. + +Destroy whatever meditative experiences arise, and relax. +A tantric practitioner without fixation is deeply content. +You've reached your goal of contentment right now. +What is the use of numerous enumerations of Buddha's teachings +When you discover Buddha Kuntu Zangpo within yourself? +Keep the meaning of these words close to your heart. + -- Dudjom Jigdral Yeshe Dorje, "Wisdom Nectar: Dudjom Rinpoche's Heart + Advice", translated by Ron Garry, a Tsadra Foundation Series book + published by Snow Lion Publications +~ + The Buddhist view is that in the external world there are some elements +that are material, and some that are nonmaterial. And the fundamental +substance, the stuff from which the material universe arises, is known as +space particles. A portion of space is quantized, to use a modern term; it is +particulate, not continuous. Before the formation of the physical universe as +we know it, there was only space, but it was quantized. And it was from the +quanta, or particles, in space that the other elements arose. This accounts +for the physical universe. + But what brought about that process? How did it happen? It is believed +that there existed other conditions, or other influences, which were +nonmaterial, and these were of the nature of awareness. The actions of +sentient beings in the preceding universe somehow modify, or influence, the +formation of the natural universe. + -- H.H. the Dalai Lama, "Consciousness at the Crossroads: Conversations + with the Dalai Lama on Brain Science and Buddhism", edited by Zara + Houshmand, Robert B. Livingston, and B. Alan Wallace, published by + Snow Lion Publications +~ + ...meditation on emptiness begins with gaining a sense of the inherent +existence of which phenomena are empty, for without understanding what is +negated, you cannot understand its absence, emptiness. + ...Through carefully watching how you conceive your self, or "I," to be +inherently established, you will determine that the "I" appears to be self- +instituting without depending on the collection of the mental and physical +aggregates, which are its basis of designation, or without depending on any of +them individually, even though the "I" appears with those aggregates. Proper +identification of this appearance is the first essential toward realizing +selflessness--ascertaining the object of negation. + -- H.H. the Dalai Lama, Dzong-ka-ba, and Jeffrey Hopkins, "Yoga Tantra: + Paths to Magical Feats", published by Snow Lion Publications +~ + At the moment the world's spiritual traditions have greatly degenerated. +It is very important in such times that the practitioners themselves make +especially strong efforts to gain realization. To permit the lineages of +transmission to disappear is to allow the world to plunge into darkness. The +great Vasubandhu wrote, "Buddha, who is like the eye of the world, is no +longer to be seen. His great successors, who realized the most profound +teachings, also have passed away. Who equals them?" It might be asked, who +is there today to equal the master Vasubandhu? Who practices as well as did +Milarepa? Such people are rare. We should remember that everything but +Dharma is useless at death, and instead of wasting our lives on meaningless +activities, we should blend our mindstreams with the teachings and with +practice. Doing so benefits us as individuals and benefits the world by +strengthening its spiritual basis. + Each of us has to be able to feel the pride that we ourselves can reach +perfection, we ourselves can attain enlightenment. When even one person +indulges in spiritual practice, it gives encouragement to the guardian spirits +of the land, and to the celestial deities who have sworn to uphold goodness. +These forces then have the ability to release waves of beneficial effects upon +humanity. Thus our practice has many direct and indirect benefits. ...If we +practice the teachings and live the ways of Dharma, all the natural forces of +goodness will be behind us. + -- H.H. the Dalai Lama, "The Path to Enlightenment", edited and translated + by Glenn H. Mullin, published by Snow Lion Publications +~ + Karmic potentials give rise to a broad array of impulses that affect our +lives. Collective karmic potentials from previous actions of a huge number of +beings--including ourselves--give rise, for example, to the impulse for a +universe to evolve with specific environments and life forms into which we and +these beings subsequently take rebirth. These collective potentials also give +rise to the impulses that drive the physical and biological laws that govern +that universe--ranging from the weather patterns of its planets to the life- +cycle habits of each species on them. They also account for the impulses +behind the instinctive daily behavior characteristic of each life form. + -- Alexander Berzin, "Taking the Kalachakra Initiation", published by Snow + Lion Publications +~ + Once the body, channels, and wind are balanced, the next step is to keep +your mind in the natural state through meditation. By simply maintaining the +mind as it is, without adding or subtracting anything, one will reach the +inner nature, which is unchanging and indestructible. + The instructions for this type of meditation are very simple. One begins +by sitting with good posture on a cushion, because it is important to stay +straight. Then, one simply maintains the natural clarity of the mind, without +analyzing one's experiences or being disturbed by thoughts. In the dzogchen +style of meditation, there is actually nothing to do except relax in the +mind's nature of clarity and emptiness. Inner awareness is different than +external awareness; it is called clear-light emptiness. It is helpful to use +the sky as an analogy for the true nature of the mind--when you let your mind +mingle with the open space of the sky, you do not need any particular focus. +Simply maintain the mind naturally, without discrimination or judgments, and +experience its nature as being spacious as the sky. + -- Khenchen Palden Sherab Rinpoche and Khenpo Tsewang Dongyal Rinpoche, + "Opening the Door to Our Primordial Nature", Snow Lion Publications +~ +Three things in human life are important. +The first is to be kind. +The second is to be kind. +And the third is to be kind. + -- Henry James +~ + Question: A person, particularly in the West, must have the foundation of +humility, honesty and an ethical way of life. Once one has this foundation, +what else does Your Holiness suggest that one cultivate in one's life, if +there is the foundation of virtue, ethics and humility? + DL: The next thing to be cultivated is mental stabilization. Ethics is a +method to control oneself--it is a defensive action. Our actual enemy, you +see, is within ourselves. The afflicted emotions (pride, anger, jealousy) are +our real enemies. These are the real trouble makers, and they are to be found +within ourselves. The actual practice of religion consists of fighting +against these inner enemies. + As in any war, first we must have a defensive action, and in our spiritual +fight against the negative emotions, ethics is our defense. Knowing that at +first one is not fully prepared for offensive action, we first resort to +defensive action and that means ethics. But once one has prepared one's +defenses, and has become somewhat accustomed to ethics, then one must launch +one's offensive. Here our main weapon is wisdom. This weapon of wisdom is +like a bullet, or maybe even a rocket, and the rocket launcher is mental +stabilization or calm abiding. In brief, once you have a basis in morality or +ethics, the next step is to train in mental stabilization and eventually in +wisdom. + -- H.H. the Dalai Lama, "The Dalai Lama, A Policy of Kindness: An Anthology + of Writings", compiled and edited by Sidney Piburn, Forword by Sen. + Claiborne Pell, published by Snow Lion Publications +~ + 4. The power of abandonment. In this practice what is being abandoned is +self-grasping. We are reminded again that since beginningless time beyond all +imagination, self-grasping has lain at the very core of all mental distortions +and afflictions. It has brought us to unfavorable rebirths and is responsible +for all the undesirable circumstances that we encounter. It is self- +centeredness that obstructs realization and prevents us from deriving the full +benefit from our spiritual practice. Recognize when self-grasping manifests +in daily life. It is important to notice it especially at times of passion, +when we are aroused or irritated, and try not to succumb to it for even a +moment. + To be free of self-centeredness continuously for a whole year may be +difficult, but [rejecting it for] a moment is easy. ...the more of these +moments we can saturate with the cherishing of others, the more we are molding +ourselves into the bodhisattvas that we will become. + -- B. Alan Wallace, "The Seven-Point Mind Training", edited by Zara + Houshmand, published by Snow Lion Publications +~ + As a friend, my request and wish is that... you try to promote a sense of +brotherhood and sisterhood. We must promote compassion and love; this is our +real duty. Government has too much business to have time for these things. +As private persons we have more time to think along these lines--how to make a +contribution to human society by promoting the development of compassion and a +real sense of community. + ...If someone who easily gets angry tries to control his or her anger, in +time it can be controlled. The same is true for a selfish person; first that +person must realize the faults of a selfish motivation and the benefit in +being less selfish. Having realized this, one trains in it, trying to control +the bad side and develop the good. As time goes by, such practice can be +effective. This is the only alternative. + Without love, human society is in a very difficult state; without love, +in the future we will face tremendous problems. Love is the center of human +life. + -- His Holiness Tenzin Gyatso, "Kindness, Clarity & Insight", published by + Snow Lion Publications +~ +Love and Attachment + +People often wonder how to reconcile the Buddha's teachings on non-attachment +with those on love. How can we love others without being attached to them? +Non-attachment is a balanced state of mind in which we cease overestimating +others' qualities. By having a more accurate view of others, our unrealistic +expectations fall away, as does our clinging. This leaves us open to loving +others for who they are, instead of for what they do for us. Our hearts can +open to care for everyone impartially, wishing everyone to be happy simply +because he or she is a living being. The feeling of warmth that was +previously reserved for a select few can now be expanded to a great number +of people. + -- Bhikshuni Thubten Chodron, "Taming the Mind", published by Snow Lion + Publications +~ +According to some scientists, emotion is not necessarily negative. Emotion +is a very strong feeling. While some emotions are destructive, others are +constructive. In a meeting with scientists, we concluded that there are +emotions even in the Buddha's mind. There is a strong sense of caring and +compassion and also the realization of emptiness. In the beginning, there is +just a vague feeling of emptiness. At that level, there is no emotion, but +once you become more familiar with it, then that feeling increases. At a +certain level, the realization of emptiness also becomes a kind of emotion. +Therefore, in the practice of developing wisdom and loving-kindness +/ compassion, you strengthen these inner qualities and then reach a state +where you have an upsurge of feeling called emotion. We can clearly see +this link between intellect and emotions. Thus, the brain and heart can go +side by side. I think this is the Buddhist approach. + -- His Holiness the Dalai Lama, "Many Ways to Nirvana: Reflections and + Advice on Right Living", edited by Renuka Singh +~ +Both mindfulness and discriminative alertness are needed in responding to +sensory input of the three types--attractive, unattractive and neutral. +Once again, in this tradition mindfulness does not mean simply to witness. +It is a more discriminative kind of thing. You are asking yourself, "What +is my response?" and then actively responding by applying the antidotes to +attachment and hostility. The word mindfulness is a little bit different in +different contexts. Here, Mindfulness refers to the mental faculty of being +able to maintain continuity of awareness of an object. Vigilance is concerned +with the quality of mind, watching to see, for example, if the mind is veering +off to other objects. + -- Gen Lamrimpa (Ven. Jampal Tenzin, "Calming the Mind: Tibetan Buddhist + Teachings on Cultivating Meditative Quiescence", translated by B. Alan + Wallace, edited by Hart Sprager, published by Snow Lion Publications +~ + When we say that the ignorant mind is perverse or wrong, we are talking +about the way it misconceives reality. Now the pertinent questions are: What +is reality? How is this mind mistaken about reality? And in what way does +the mind wrongly apprehend reality? Reality or emptiness of true existence is +something that can be established logically. There are sound, or perfect, +reasons to prove the emptiness of inherent existence, and we can gain +conviction in these reasons. On the other hand, there is no logical way to +prove true existence. True existence is what appears to an ordinary, +untrained consciousness. But when it comes under logical scrutiny, true +existence cannot be found. Even in our everyday life we often find +contradictions between the way certain things appear and their actual mode of +existence; that is, the way things actually exist is different from the way +they appear to exist. + ...Our perception of impermanent things like mountain ranges and houses +does not conform to their actual mode of existence. Some of these things have +existed for many centuries, even thousands of years. And our minds perceive +them in just that way--as lasting and permanent, impervious to momentary +change. Yet when we examine these objects on an atomic level, they +disintegrate every moment; they undergo momentary change. Science also +describes a similar pattern of change. These objects appear solid, stable, +and lasting, but in their true nature, they constantly change, not keeping +still even for a moment. + -- The Dalai Lama, "Stages of Meditation", translated by Geshe Lobsang + Jordhen, Losang Choephel Ganchenpa, and Jeremy Russell, published by + Snow Lion Publications +~ +If there is someone who always harms us, and we discover that this person +lives in our own house, we think, "This is too much!" Once we figure out +that he is causing all our hardships, we kick him out; we do not see it as a +laughing matter at all. Here, it is worse: we have been wandering in the six +realms of cyclic existence since beginningless time, undergoing great pain and +confusion. What is the main cause of all this? Self-centeredness and its +basis, self-grasping ignorance. These two are right inside us, in our own +mindstream. How can we continue to tolerate that? It is just too much! We +definitely must evict these sources of harm. When we know the antidotes to +them, we will use them, just as we would go to any length to evict a +troublemaker from our home. With strong determination, we will find out what +harms self-centeredness and self-grasping and then go ahead and destroy them. + -- Geshe Jampa Tegchok, "Transforming Adversity into Joy and Courage: An + Explanation of the Thirty-seven Practices of Bodhisattvas", edited by + Thubten Chodron, published by Snow Lion Publications +~ +No tyranny is so irksome as petty tyranny: the officious demands of +policemen, government clerks, and electromechanical gadgets. + -- Edward Abbey +~ +There are many types of meditative stabilisation, but let us explain calm +abiding (samatha) here. The nature of calm abiding is the one-pointed abiding +on any object without distraction of a mind conjoined with a bliss of physical +and mental pliancy. If it is supplemented with taking refuge, it is a +Buddhist practice; and if it is supplemented with an aspiration to highest +enlightenment for the sake of all sentient beings, it is a Mahayana practice. +Its merits are that, if one has achieved calm abiding, one's mind and body are +pervaded by joy and bliss; one can--through the power of its mental and +physical pliancy--set the mind on any virtuous object one chooses; and many +special qualities such as clairvoyance and emanations are attained. + -- H.H. the Dalai Lama, "The Buddhism of Tibet", translated and edited by + Jeffrey Hopkins, published by Snow Lion Publications +~ +45 + Apart from the perfection of wisdom, + All virtuous practices such as + The perfection of giving are described + As skillful means by the Victorious Ones. + +The first five perfections--giving, ethical discipline, patience, enthusiastic +effort and concentration--as well as meditation on impermanence, on the +connection between actions and their effects and the cultivation of +compassion, love and the altruistic intention are all skillful means. In fact +all positive practices which do not constitute the cultivation of wisdom fall +into the category of skillful means. + +46 + Whoever, under the influence of familiarity + With skillful means, cultivates wisdom + Will quickly attain enlightenment-- + Not just by meditating on selflessness. + +When stability in practices which develop skillful means has been gained, the +Bodhisattva meditates on the selflessness of persons and other phenomena and +thereby overcomes clinging to their true existence. This leads swiftly to +enlightenment. If we confine our efforts only to understanding reality, our +understanding lacks the power to destroy all the obstructions that prevent +omniscience and we may remain locked in a state of solitary peace. +Cultivation of skillful means prevents this and adds such power to our +understanding of reality that, like a blazing fire, it consumes all +obstructions. + + -- "Atisha's Lamp for the Path to Enlightenment", commentary by Geshe Sonam + Rinchen, translated and edited by Ruth Sonam, published by Snow Lion + Publications +~ +As I grow to understand life less and less, +I learn to love it more and more. + -- Jules Renard +~ +The environment where you are doing the meditation should be properly cleaned. +While cleaning, you should cultivate the motivation that since you are engaged +in the task of accumulating great stores of merit by inviting the hosts of +buddhas and bodhisattvas to this environment, it is important to have a clean +place. You should see that all the external dirt and dust around you is +basically a manifestation of the faults and stains within your own mind. You +should see that the most important aim is to purge these stains and faults +from within your mind. Therefore, as you cleanse the environment, think that +you are also purifying your mind. Develop the very strong thought that by +cleaning this place you are inviting the host of buddhas and bodhisattvas who +are the most supreme merit field, and that you will subsequently engage in a +path that will enable you to purge your mind of the stains of delusions. + -- H.H. the Dalai Lama, Tenzin Gyatso, "Path to Bliss: A Practical Guide to + Stages of Meditation", translated by Geshe Thubten Jinpa, edited by + Christine Cox, published by Snow Lion Publications +~ + The entire environment we perceive around us arises, in part, in +dependence upon our sense faculties. The environment we experience does not +truly exist "out there." Sight is dependent upon visual faculties, hearing +is dependent upon auditory faculties, and tactile sensations depend on nerve +endings. Psychologically, all that we experience is dependent upon ourselves. +We do not experience anything purely objectively. The arising and perceiving +of experience is co-emergent between ourselves and the world around us. Yet, +the deep belief persists that the world really exists "out there" now and eons +before we were born. + The Buddhist hypothesis extends beyond the psychological. The Buddhist +hypothesis is this: that which is perceived arises in dependence upon the +perception of it. Things are empty of independent, inherent existence. What +appears to exist "out there" is empty of objective existence from its own +side. This does not mean that nothing exists apart from our perceptions. +Rather, it means that by probing the nature of existence of anything we +experience perceptually or conceptually, we find that nothing exists by its +own independent nature. Another way of phrasing this is that appearance +extends all the way down to the root and there is nothing beyond the +appearances. Appearances extend down to quarks; nothing is there purely +objectively and nothing is there purely subjectively. This is the Buddhist +hypothesis. + -- B. Alan Wallace, "Buddhism with an Attitude: The Seven-Point Mind- + Training" , edited by Lynn Quirolo, published by Snow Lion Publications +~ + I consider it very important for religion to have an influence on +politicians. Politicians need religion much more than pious people who have +withdrawn from the world need it. There is a constant increase in the +scandals in politics and business that can be traced back to the lack of self- +discipline on the part of the responsible parties. In India, the minister- +president of West Bengal once said to me with what he considered a humble +attitude that he was a politician and not a religious person. I responded to +him: politicians need religion more than anyone else. + When hermits in solitude are bad persons, the result is that they harm +themselves alone and no one else. But when such influential people as +politicians are full of bad intentions, they can bring misfortune to many. +This is why religion, as continuous work on our inner maturity, is important +for political rulers. + A politician must have moral principles. I am convinced of this. Seen in +this light, politics and religion belong together. In the United States, +church and state may be separate, but when the president takes office, he +makes a vow in the name of God with his hand on the Bible. This means that +God should be the witness that the president will conscientiously fulfill his +official duties. + -- His Holiness the Dalai Lama with Felizitas Von Schonborn, "Path of Wisdom, + Path of Peace: A Personal Conversation", Foreword by Wei Jingsheng +~ +...many people, critical of Dzogchen, question why we need to practice at all +if, as according to Dzogchen, the primordial state is already the enlightened +state. If our true nature is already Buddhahood, what is the need to +cultivate enlightenment? We cannot side-step these criticisms since, +according to Dzogchen, Buddhahood is indeed our natural state; we do not +create it, but simply discover it through our meditation. But if we simply +agree with our critics, this would mean there is no need to practice. These +are important things to think about. We must answer that although the natural +state of the mind is primordially pure, there are two ways of being pure. +Defilements, or obscurations, are not in the nature of the mind (sems nyid) +but in the moving mind (sems), so they can be purified. It is as in the +Tibetan story of the old beggar woman who slept on a pillow of gold every +night: she was rich, but since she did not appreciate the value of gold, she +thought she was poor. In the same way, the primordial purity of our mind is +of no use to us if we are not aware of it and do not integrate it with our +moving mind. If we realize our innate purity but only integrate with it from +time to time, we are not totally realized. Being in total integration all the +time is final realization. But many people prefer thinking and speaking about +integration to actualizing it. + -- Tenzin Wangyal Rinpoche, "Wonders of the Natural Mind: The Essence of + Dzogchen in the Native Bon Tradition of Tibet", Foreword by H.H. the + Dalai Lama, edited by Andrew Lukianowicz, published by Snow Lion + Publications +~ +External disarmament is very, very important. Already, there is some +movement. My dream is that one day the whole world will be demilitarized, but +we cannot achieve this overnight. Also, we cannot achieve it without a +proper, systematic plan; however, it is important to make the target clear. +Even though it may take one hundred years, or fifty years, that doesn't +matter. Establish a clear idea or clear target; then try to achieve it step +by step. As a first step, we have already started with the elimination of +antipersonnel mines and biological weapons. Also, we are already reducing +nuclear weapons; eventually, there should be a total ban on nuclear weapons. +This is now foreseeable; the idea of its possibility is approaching. These +are great, hopeful signs. + -- His Holiness the Dalai Lama, "The Art of Peace: Nobel Peace Laureates + Discuss Human Rights, Conflict and Reconciliation", edited by Jeffrey + Hopkins, published by Snow Lion Publications +~ + Bare awareness is not easy to develop and maintain because of the mind's +disposition to be constantly preoccupied by thoughts. We easily lose +attention because our mind is so busy. When we do, our emotional life can +creep up on us and take us over. Without mindfulness, the capacity to +maintain attention, disidentification is very difficult, and bare awareness +even more so. Through meditation it is possible to cultivate a quiet, +unintrusive awareness that greatly strengthens our capacity to remain with our +feelings. We simply allow their presence without judging them, or needing to +make them different. + The early stage of meditation focuses attention and cultivates +mindfulness. Mindfulness is our capacity to watch and remain conscious as +emotions, feelings, and thoughts arise. We may begin in meditation by +observing the breath and gradually quietening the mind from the constant +discursive chatter that interrupts our attention. In time a quality of bare +awareness is established free from the conceptual confusion that discriminates +and evaluates what arises and parcels it up in conceptual boxes of good or +bad. Furthermore, this quiet awareness does not become pulled into the +contents of mental activity and drown in their confusion. + -- Rob Preece, "The Psychology of Buddhist Tantra", Foreword by Stephen + Batchelor, published by Snow Lion Publications +~ + In the realm of matter, one and the same object can serve as a cause of +happiness for some living beings, and a cause of suffering for others. +Certain plants, for example, function as medicine for some creatures, but for +other species they can be poisonous. From the point of view of the object +itself there is no difference, but because of the physical constitution and +the material state of the particular living being, that single self-same +object can affect them in different ways. Then, in the sphere of our own +experiences, the same holds true. A certain individual may appear to some as +very friendly, kind and gentle, and so gives them feelings of happiness and +pleasure. Yet to others that same person can appear harmful and wicked, and +so cause them discomfort and unhappiness. + What this kind of example points to is that, although external matter +may act as a cause for our experience of pain and pleasure, the principal +cause that determines whether we experience happiness or suffering lies +within. This is the reason why, when Buddha identified the origin of +suffering, he pointed within and not outside, because he knew that the +principal causes of our suffering are our own negative emotions and the +actions they drive us to do. + -- H.H. The Dalai Lama, "Dzogchen: The Heart Essence of the Great + Perfection", translated by Thupten Jinpa and Richard Barron, + Foreword by Sogyal Rinpoche, edited by Patrick Gaffney, published + by Snow Lion Publications +~ +When you have many excuses not to do your work, ask yourself what guarantee +you have of another chance to do what needs to be done. Time lost is lost for +good. No matter how much you promise to improve, no matter what good +intentions you have for making it up, the time is gone for good. Feeling +sorry about the situation will not bring it back. You can never buy back that +precious piece of time. You may think, "Well, that piece of time has passed, +but I still have a long stretch of time left." No, you do not! What guarantee +is there that you will have another piece of time like this one? Wake up and +stop the excuses; they never made sense before and do not make sense now. +Laziness and procrastination have never worked in a sound and helpful way. It +is only sound and helpful to get things moving. + -- Khenpo Karthar Rinpoche, "Dharma Paths", translated by Ngodup Burkhar + and Chojor Radha, edited by Laura M. Roth, published by Snow Lion + Publications +~ + Any sense of conceit or self-importance gets in the way of cultivating the +genuine altruistic intention, and the most effective remedy against this is +the cultivation of humility. + I can tell you a more recent story to illustrate this point. The great +nineteenth-century Tibetan Dzokchen meditator Dza Patrul Rinpoche always +maintained a demeanour of true humility. At one time, when he was giving a +series of teachings to a large crowd of students, he experienced a forceful +yearning for solitude. So one day he quietly left his residence and +disappeared, dressed like an ordinary pilgrim and carrying a walking staff and +very little else. When he reached a nomadic camp he sought shelter for a few +days with one of the families. While he was staying with them, his hostess +asked him to read some texts and, since he looked just like an ordinary +pilgrim, in return for his food and lodging she asked him to help with the +household chores, which included the disposal of the contents of her chamber +pot. + One day, while he was away from the camp attending to this task, some of +his well-dressed monk students came looking for him. When his hostess heard +their description of him, she suddenly realised this was the same person she +had asked to throw away the contents of her chamber pot. (It is said she was +so embarrassed that she just ran away!) Such was the humility of this great +teacher, who had many thousands of students. + ...great practitioners of the altruistic intention also possess a +tremendous courage grounded in real inner strength.... This combination of a +total lack of conceit yet possessing great depth of courage is what is +required in a true practitioner of bodhicitta, the altruistic mind of +awakening. + -- H.H. the Dalai Lama, "Lighting the Way", translated by Geshe Thupten + Jinpa, published by Snow Lion Publications +~ +Rejoicing in the actions of others is the major antidote to jealousy. When +we admire the virtuous deeds of ourselves and of others, a great increase of +merit is created. Jealousy is very harmful, and must be destroyed by +rejoicing. If we rejoice in the virtue of someone whose understanding is less +than our own, we gain greater merit than that person. If we rejoice in the +merit of someone with understanding equal to ours, we gain equal merit. If we +rejoice in the realization or virtue of someone more highly realized than we +are, we accumulate some fraction of the merit that they do. We must rejoice +in virtue because we have taken bodhisattva vows. If other beings practice +well it helps us; therefore we should rejoice in their positive actions. This +is the easiest way to accumulate merit with little hardship. With consistent +effort the practice of rejoicing becomes very powerful and is greatly praised +by many masters. + -- Kyabje Zong Rinpoche, "Chod on the Ganden Tradition: the Oral + Instructions of Kyabje Zong Rinpoche", edited by David Molk, published + by Snow Lion Publications +~ +If you hear a voice within you say "you cannot paint," +then by all means paint, and that voice will be silenced. + -- Vincent Van Gogh +~ + ...there are various factors that contribute to attaining that level of +joy and happiness which we conventionally also recognize as sources of +happiness, such as good physical health, ...the wealth that we accumulate, +...and a circle of friends we trust and with whom we can relate emotionally. + Now all of these are, in reality, sources of happiness, but in order for +one to be able to fully utilize them with the goal of enjoying a happy and +fulfilled life, one's state of mind is crucial. If one harbors hateful +thoughts within, or strong or intense anger somewhere deep down, then it ruins +one's health, so it destroys one of the factors. Even if one has wonderful +possessions, when one is in an intense moment of anger or hatred, one feels +like throwing them--breaking them or throwing them away. So there is no +guarantee that wealth alone can give one the joy or fulfillment that one +seeks. Similarly, when one is in an intense state of anger or hatred, even +a very close friend appears somehow "frosty," cold and distant, or quite +annoying. + What this indicates is that our state of mind is crucial in determining +whether or not we gain joy and happiness. So leaving aside the perspective of +Dharma practice, even in worldly terms, in terms of our enjoying a happy day- +to-day existence, the greater the level of calmness of our mind, the greater +our peace of mind, and the greater our ability to enjoy a happy and joyful +life. + -- The Dalai Lama, "Healing Anger: The Power of Patience from a Buddhist + Perspective", translated by Geshe Thupten Jinpa, published by Snow + Lion Publications +~ +Question: Is there a buildup of awareness that happens by the practice of + recognizing or looking for your own basic nature so that, over time, it + dispels the fear of these emotions? + +Rinpoche: Yes, awareness is developed through the discipline of meditation. + Beginning with shamatha meditation, we develop lots of awareness and + mindfulness on the path. Then, in Mahamudra and Dzogchen, we emphasize a + different aspect of mindfulness and awareness. Mindfulness and awareness + come from the discipline of meditation, which continues in our everyday + life. Therefore, formal sitting practice is very important for us. For + that reason, many teachers tell us to sit at least 10-15 minutes every day. + That helps us to generate this continuity of awareness in our everyday life. + There is no easy solution for manifesting awareness or mindfulness in our + everyday life without some discipline in practice. The only problem is + that when a student hears a teacher say that they must sit every day, + that's the time students usually begin to change their guru! + + -- Dzogchen Ponlop Rinpoche, "Penetrating Wisdom: The Aspiration of + Samantabhadra", published by Snow Lion Publications +~ +We ordinary individuals share the characteristic of having our attempts to +gain happiness thwarted by our own destructive self-centeredness. It is +unsuitable to keep holding onto the self-centered attitude while ignoring +others. If two friends find themselves floundering in a muddy swamp they +should not ridicule each other, but combine their energies to get out. Both +ourselves and others are in the same position of wanting happiness and not +wanting suffering, but we are entangled in a web of ignorance that prevents us +from achieving those goals. Far from regarding it as an "every man for +himself" situation, we should meditate upon the equality of self and others +and the need to be helpful to other beings. + -- Ven. Lobsang Gyatso, "Bodhicitta: Cultivating the Compassionate Mind of + Enlightenment", translated by Ven. Sherab Gyatso, published by Snow Lion + Publications +~ +Dalai Lama: "In the traditional [Tibetan] society, most people automatically +did the types of work their families did--nomads, farmers, merchants, and so +on. But some people still engaged in work that was not in keeping with the +principle of nonharm, because there are butchers, metal smiths who make +swords, and so on. But these kinds of work were also generally hereditary." + +Howard Cutler: "Speaking of work and the implementation of the concept of +nonharm, ...you mentioned that there was a rule in Tibet that any new +invention had to guarantee that it was beneficial or at least not harmful for +at least seven generations." + +DL: "...there do seem to be certain practices and policies that successive +Tibetan governments adopted in Tibet that reflect putting into practice +certain Buddhist ideals, such as the Buddhist principle of respecting the +natural world, particularly the animal world. For example, all the +communities living near the Yamdrok Lake used to rely heavily on fishing in +the past. Recently I heard about a policy that was adopted during the Fifth +Dalai Lama's time where they were discouraged from fishing, and in order to +compensate them, some other communities would band together and provide them +an equivalent value in grain, so that they would be compensated against their +loss. Similarly, in the area near Mount Kailash, around Lake Manasarovar +during a particular season, a lot of waterfowl migrate there. They lay their +eggs on the shores and apparently there was a government policy that during +the egg-laying season, they would appoint people to watch over the eggs to +make sure they were safe. Of course, there might be individuals who in +addition to taking the salary probably ate some of the eggs as well. These +things happen. But overall there is this kind of attitude of nonharm. "So, +even though in Tibet, people didn't always follow the principle of nonharm in +their work ...this principle was still deeply ingrained in the people. "In +general, I think this could be applied in the West. Although not everybody +has options about the work that they do, at least I think it is good to give +serious thought to the kind of work one does, and the impact it has on others. +I think it is best to choose work that does not cause harm to others, that +does not exploit or deceive others, either directly or indirectly. I think +that's the best way." + -- His Holiness the Dalai Lama, and Dr. Howard C. Cutler, M.D., "The Art + of Happiness at Work" +~ + If we are feeling very nervous all the time, the first step toward doing +something to remedy the situation is to take ourselves and the quality of our +life seriously. Suppose we are walking down the street and we step on a bug +and partially crush but have not actually killed it. If we continue walking +and ignore the bug's experience of its leg being crushed or severed, we do so +because we do not take the insect and its life seriously. We have no respect +for it. If we treat ourselves no better than we do a bug and ignore our +innermost pains and anguish, that is most unfortunate. + Taking ourselves seriously means actually looking at how we are +experiencing our life and, if there is something unsatisfactory about it, +admitting it to ourselves. Our tension and stress do not go away by denying +them or avoiding taking an honest look. And admitting that something is amiss +is not the same as complaining about it and feeling sorry for ourselves. Nor +does it imply that something is fundamentally wrong with us and we are guilty +of being a bad person because we are nervous. Being objective, not +melodramatic, and remaining non-judgmental are essential for any healing, +spiritual process. + -- His Holiness the Dalai Lama, with Alexander Berzin, "The Gelug/Kagyu + Tradition of Mahamudra", published by Snow Lion Publications +~ +Wouldn't life be boring without attachment? + + No. In fact it's attachment that makes us restless and prevents us from +enjoying things. For example, suppose we're attached to chocolate cake. Even +while we're eating it, we're not tasting it and enjoying it completely. We're +usually either criticizing ourselves for eating something fattening, comparing +the taste of this chocolate cake to other cakes we've eaten in the past, or +planning how to get another piece. In any case, we're not really experiencing +the chocolate cake in the present. + On the other hand, without attachment, we can think clearly about whether +we want to eat the cake, and if we decide to, we can eat it peacefully, +tasting and enjoying every bite without craving for more or being dissatisfied +because it isn't as good as we expected. As we diminish our attachment, life +becomes more interesting because we're able to open up to what's happening in +each moment. + -- Thubten Chodron, "Buddhism for Beginners", published by Snow Lion + Publications +~ +My Grandmother is over eighty and still doesn't need glasses. +Drinks right out of the bottle. + -- Henny Youngman +~ +We are bits of stellar matter that got cold by accident, +bits of a star gone wrong. + -- Sir Arthur Eddington +~ +Hell hath no fury like a bureaucrat scorned. -- Milton Friedman +~ +I have the worst memory ever so no matter who comes up to me-- +they're just, like, "I can't believe you don't remember me!" +I'm like, "Oh Dad I'm sorry!" + -- Ellen DeGeneres +~ +Question: When a practitioner of the Great Vehicle vows not to enter into +nirvana until all beings are liberated, how is it possible to fulfill this +vow? + +Answer: Three modes of generating an altruistic intention to become +enlightened are described--like a king, like a boatman, and like a shepherd. +In the first, that like a king, one first seeks to attain a high state after +which help can be given to others. In the second, like a boatman, one seeks +to cross the river of suffering together with others. In the third, like a +shepherd, one seeks to relieve the flock of suffering beings from pain first, +oneself following afterward. These are indications of the style of the +altruistic motivation for becoming enlightened; in actual fact, there is no +way that a Bodhisattva either would want to or could delay achieving full +enlightenment. As much as the motivation to help others increases, so much +closer does one approach Buddhahood. + -- His Holiness the Dalai Lama of Tibet, "The Dalai Lama at Harvard", + translated and edited by Jeffrey Hopkins, published by Snow Lion + Publications +~ + A bodhisattva is someone who says from the depth of his or her heart, "I +want to be liberated and find ways to overcome all the problems of the world. +I want to help all my fellow beings to do likewise. I long to attain the +highest state of everlasting peace and happiness, in which all suffering has +ceased, and I want to do so for myself and for all sentient beings." +According to the Buddha's teaching, anyone who makes this firm and heartfelt +commitment is a bodhisattva. We become bodhisattvas from the moment we have +this vast and open heart, called bodhichitta, the mind bent on bringing +lasting happiness to all sentient beings. + Buddhist literature defines three types of bodhisattvas: the kinglike +bodhisattva, the captainlike bodhisattva, and the shepherdlike bodhisattva. A +kinglike bodhisattva is like a good king who first wants everything luxurious +for himself, like a big palace, a large entourage, a beautiful queen, and so +on. But once his happiness has been achieved, he also wants to help and +support his subjects as much as possible. Accordingly, a kinglike bodhisattva +has the motivation, "First, I want to free myself from samsara and attain +perfect enlightenment. As soon as I have reached buddhahood, I will help all +other sentient beings to become buddhas as well." + A captainlike bodhisattva would say, "I would like to become a buddha, and +I will take all other sentient beings along with me so that we reach +enlightenment together." This is just as the captain of a ship crosses the +sea, he takes his passengers with him, and they reach the far shore +simultaneously. + A shepherdlike bodhisattva is inspired by thinking, "I want to help all +sentient beings to reach enlightenment and see the truth. Only when this is +achieved and samsara is emptied will I become a buddha myself." In actual +fact it may not happen this way, but anyone who has this motivation is called +a "shepherdlike bodhisattva." In the old days, sheep were not kept in fenced +pastures, and the shepherds had to bring them down from the mountains to +protect them from wolves. They would follow behind the sheep, guiding them +into their pen and lock them in. A shepherd would take care of his sheep +first, and only then would he go home and eat. + The bodhisattva Avalokiteshvara developed this shepherdlike motivation and +is therefore considered to be the most courageous and compassionate of beings. +He vowed, "I will not attain complete enlightenment until I have led all +sentient beings to liberation without leaving a single one behind." + -- Ringu Tulku Rinpoche, "Daring Steps Toward Fearlessness: The Three + Vehicles of Buddhism", edited and translated by Rosemarie Fuchs, + published by Snow Lion Publications +~ + The practice of compassion begins at home. We have our parents, our +children, and our brothers and sisters, who perhaps irritate us the most, and +we begin our practice of loving-kindness and compassion with them. Then +gradually we extend our compassion out into our greater community, our +country, neighbouring countries, the world, and finally to all sentient beings +equally without exception. + Extending compassion in this way makes it evident that it is not very easy +to instantly have compassion for "all sentient beings." Theoretically it may +be comfortable to have compassion for "all sentient beings," but through our +practice we realize that "all sentient beings" is a collection of individuals. +When we actually try to generate compassion for each and every individual, it +becomes much more challenging. But if we cannot work with one individual, +then how can we work with all sentient beings? Therefore it is important for +us to reflect more practically, to work with compassion for individuals and +then extend that compassion further. + -- Dilgo Khyentse Rinpoche, "Trainings in Compassion", translated by the + Padmakara Translation Group, published by Snow Lion Publications +~ +It wouldn't be bad if you didn't have statues, but it has become indispensable +to have Buddhist texts which deal with the structured path to train our mind. +If you have Buddhist texts, read them for yourselves and to friends who visit. +That way you can help others to understand Buddhist ideas. For instance, it +is interesting to read Milarepa's life story and songs. We find in them many +enlightening lessons. Buddha's image alone will not purify us of karmic +obscuration.... It is very important to study the scriptures. They are not +to be just stacked up on the altar. They must be cultivated in our mind. +...[we] take great interest in having the symbolic representations of Buddha's +body, speech and mind. I feel it is more important to acquire and read +scriptures, the symbolic representations of his speech. You can pay homage to +them, you can make offerings to them; above all, you should study them. + -- H.H. the Dalai Lama, "Generous Wisdom: Commentaries by H.H. the Dalai + Lama XIV on the Jatakamala", translated by Tenzin Dorjee, edited by + Dexter Roberts, Snow Lion Publications +~ +From the Buddhist point of view, being in a depressed state, in a state of +discouragement, is seen as a kind of extreme that can clearly be an obstacle +to taking the steps necessary to accomplish one's goals. A state of self- +hatred is even far more extreme than simply being discouraged, and this can be +very, very dangerous. For those engaged in Buddhist practice, the antidote to +self-hatred would be to reflect upon the fact that all beings, including +oneself, have Buddha Nature--the seed or potential for perfection, full +Enlightenment--no matter how weak or poor or deprived one's present situation +may be. So those people involved in Buddhist practice who suffer from self- +hatred or self-loathing should avoid contemplating the suffering nature of +existence or the underlying unsatisfactory nature of existence, and instead +they should concentrate more on the positive aspects of one's existence, such +as appreciating the tremendous potential that lies within oneself as a human +being. And by reflecting upon these opportunities and potentials, they will +be able to increase their sense of worth and confidence in themselves. + -- His Holiness the Dalai Lama and Howard C. Cutler, M.D., "The Art of + Happiness: A Handbook for Living" +~ +An instruction that matches examples and their meanings to show how the +absolute nature permeates everything. (*SG) + +These various examples give a general idea of the absolute nature. (*DK) + +...there are four examples and their meanings. Take the example of a +Sugata's body: whichever way one looks at it, it is beautiful. (*Z) + +Similarly, everything a realized being does, since it is permeated with the +realization of the unborn nature, is bliss, for he does not have ordinary +attachment and aversion. (*Z & SG) + +Whether one looks at a Sugata's face or any other part of his body, one never +feels one has looked enough. It is an example of ultimate beauty. Similarly, +those for whom everything is backed by the realization of the unborn nature no +longer have ordinary attachment and aversion, and such persons can therefore +act like enlightened beings: whatever they do is bliss. Since they have fully +realized the absolute nature, there is no question of telling them, "This is +the right thing to do; that is something you should not do." They have no +concepts or limits, so they can act as they wish. Everything they do will be +nothing but bliss. (DK) + +* Note: + Z: Zurchungpa's root text + DK: Dilgo Khyentse Rinpoche's commentary + SG: Shechen Gyaltsap's notes + + -- Dilgo Khyentse Rinpoche, "Zurchungpa's Testament: A Commentary on + Zurchung Sherab Trakpa's Eighty Chapters of Personal Advice", translated + by the Padmakara Translation Group, published by Snow Lion Publications +~ +Unless someone like you cares a whole lot, +nothing is going to get better. It's not. + -- The Lorax, Doctor Seuss +~ +We do what we must, and call it by the best names. -- Ralph Waldo Emerson +~ +These days an income is something you can't live without--or within. + -- Tom Wilson +~ +Everything is vague to a degree you do not realize +until you have tried to make it precise. + -- Bertrand Russell +~ +The truth does not change according to our ability to stomach it. + -- Flannery O'Connor +~ +The initial period of deity yoga is called prior approximation because one is +accustoming to a deity through becoming closer and closer to its state, +whereupon the deity grants the feat, either directly or in the sense of +bestowing a capacity to the mind. Actually effecting the achievement of feats +is done by way of carrying out prescribed burnt offerings or repetition of +mantra, etc., after the approximation has been completed. These feats are +then used for the welfare of others in the third stage, which involves +activities of (1) pacification such as overcoming plague or relieving others +of demons, (2) increase of lifespan, intelligence, wealth, and so forth, (3) +control of resources, persons harmful to others' welfare, etc., and (4) +ferocity, such as expelling or confusing harmful beings. + -- His Holiness the Dalai Lama, Tsong-ka-pa, and Jeffrey Hopkins, "Deity Yoga + in Action and Performance Tantra", published by Snow Lion Publications +~ +The bodhisattva, as the personification of individuation, discovers a unique +capacity to awaken his or her potential to work for the welfare of others in +whichever way most suits his or her individual disposition. When I consider +my own teachers, one thing I particularly value is their capacity to be +authentically themselves. They each have their unique personality and quality +that is a genuine expression of their individuality. There is no +contradiction between our Western need to be individuals and the Buddhist +path. Buddhism does not demand that we become clones of some ideal. Rather, +it asks us to respond to who we are and awaken our full potential, expressing +it within our particular individual capacity. My Tibetan teachers have +supremely individualistic personalities, something I love and value deeply. +They respond to me as an individual with my own personality, which they would +never ask me to relinquish. The fact that they were each on their own unique +journey within the Buddhist path was, for me, a sublime example of the +bodhisattva as an individuated person who has truly responded to the inner +call to awaken. + -- Rob Preece, "The Wisdom of Imperfection: The Challenge of Individuation + in Buddhist Life", published by Snow Lion Publications +~ +The view is to believe in and understand the buddha nature, the essence of all +the buddhas. If one knows the buddha nature, then that is to know the +unchanging essence which is free from any limitation, the original primordial +nature of the mind as it is. This is not like a light bulb that suddenly +comes on or something that is newly acquired. It is the nature as it has +always been and always will be: primordially perfect. To recognize the buddha +nature is the view. To fail to recognize the buddha nature is to deviate into +confusion. If you recognize your buddha nature, this is the same as having an +audience with all the buddhas. You will meet face to face with all your root +teachers. + -- Venerable Gyatrul Rinpoche, "Meditation, Transformation, and Dream Yoga", + translated by B. Alan Wallace and Sangye Khandro, published by Snow Lion + Publications +~ + ...when we ask, what is the substantial cause of the material universe way +back in the early history of the universe, we trace it back to the space +particles which transform into the elements of this manifest universe. And +then we can ask whether those space particles have an ultimate beginning. The +answer is no. They are beginningless. Where other philosophical systems +maintain that the original cause was God, Buddha suggested the alternative +that there aren't any ultimate causes. The world is beginningless. Then the +question would be: Why is it beginningless? And the answer is, it is just +nature. There is no reason. Matter is just matter. + Now we have a problem: What accounts for the evolution of the universe as +we know it? What accounts for the loose particles in space forming into the +universe that is apparent to us? Why did it go through orderly processes of +change? Buddhists would say there is a condition which makes it possible, and +we speak of that condition as the awareness of sentient beings. + -- H.H. the Dalai Lama, "Consciousness at the Crossroads: Conversations with + the Dalai Lama on Brain Science and Buddhism", edited by Zara Houshmand, + Robert B. Livingston, and B. Alan Wallace, published by Snow Lion + Publications +~ +Q: Certain associates can say things to me that spark an aggressive reaction. +Why is it so easy to spark this feeling of negativity if there is not an +accumulation of energy behind it? + +A: This is because of your pattern of clinging to the idea that you should +have all the good things, and nothing that bothers you should ever happen, as +I explained earlier. This is wishful thinking, because the nature of the +world is not like that at all. The ego game you have planned is itself the +explanation for how easily your anger is sparked. Because you have planned +such a delicate, impossible game, and there are many things that can happen, +anything that jeopardizes the plan of your ego upsets you. It is not an +accumulation of energy but the pattern of clinging that is at fault. + + -- Venerable Khenpo Karthar Rinpoche, "Dharma Paths", translated by Ngodup + Burkhar and Chojor Radha, edited by Laura M. Roth, published by Snow + Lion Publications +~ + Suppose... you try to convert someone from another religion to the +Buddhist religion, and you argue with them trying to convince them of the +inferiority of their position. And suppose you do not succeed, suppose they +do not become Buddhist. On the one hand, you have failed in your task, and on +the other hand, you may have weakened the trust they have in their own +religion, so that they may come to doubt their own faith. What have you +accomplished by all this? It is of no use. When we come into contact with +the followers of different religions, we should not argue. Instead, we should +advise them to follow their own beliefs as sincerely and as truthfully as +possible. For if they do so, they will no doubt reap certain benefits. Of +this there is no doubt. Even in the immediate future, they will be able to +achieve more happiness and more satisfaction. + ...When I meet the followers of different religions, I always praise them, +for it is enough, it is sufficient, that they are following the moral +teachings that are emphasized in every religion. It is enough, as I mentioned +earlier, that they are trying to become better human beings. This in itself +is very good and worthy of praise. + -- H.H. the Dalai Lama, "Answers: Discussions with Western Buddhists", + edited by Jose Ignacio Cabezon, published by Snow Lion Publications +~ + Only a Buddha has extinguished all faults and gained all attainments. +Therefore, one should mentally go for refuge to a Buddha, praise him with +speech, and respect him physically. One should enter the teaching of such a +being. + A Buddha's abandonment of defects is of three types: good, complete, and +irreversible. Good abandonment involves overcoming obstructions through their +antidotes, not just through withdrawing from those activities. Complete +abandonment is not trifling, forsaking only some afflictions or just the +manifest afflictions, but forsaking all obstructions. Irreversible +abandonment overcomes the seeds of afflictions and other obstructions in such +a way that defects will never arise again, even when conditions favourable to +them are present. + -- H.H. the Dalai Lama, Tsong-ka-pa and Jeffrey Hopkins, "Tantra in Tibet", + published by Snow Lion Publications +~ +In commenting on the first instruction spoken by Manjushri, we considered the +question of attachment to this lifetime and the faults that come from this +attachment. However, the question of attachment goes deeper. It is not just +a matter of giving up attachment to this life's rewards but of losing our +taste and affinity for the whole of worldly existence. This is why it is +necessary to contemplate and meditate upon the faults of conditioned +existence. Otherwise, we may imagine that samsara possesses any manner of +attractive qualities. Pondering the shortcomings of samsara should bring +forth in us a tangible sense of disgust, as we are confronted with our own +misguided pursuit of worldly ends. + -- Chogye Trichen Rinpoche, "Parting from the Four Attachments: Jetsun + Drakpa Gyaltsen's Song of Experience on Mind Training and the View", + commentary translated by Thubten Choedak, Root Text and Lineage Prayer + translated by H.H. Sakya Trizin and Jay Goldberg, compiled and edited + by John Deweese, published by Snow Lion Publications +~ + Another problem we face today is the gap between rich and poor. In this +great country of America, your forefathers established the concepts of +democracy, freedom, liberty, equality, and equal opportunity for every +citizen. These are provided for by your wonderful Constitution. However, the +number of billionaires in this country is increasing while the poor remain +poor, in some cases getting even poorer. This is very unfortunate. On the +global level as well, we see rich nations and poor ones. This is also very +unfortunate. It is not just morally wrong, but practically it is a source of +unrest and trouble that will eventually find its way to our door. + ...one of my elder brothers, who is no longer alive, would tell me of his +experiences living in America. He lived a humble life and told me of the +troubles, the fears, the killings, theft, and rape that people endured. These +are, I think, the result of economic inequality in society. It is only +natural that difficulties arise if we must fight day by day in order to +survive while another human being, equal to us, is effortlessly living a +luxurious life. This is an unhealthy situation; as a result, even the +wealthy--the billionaires and millionaires--remain in constant anxiety. I +therefore think that this huge gap between rich and poor is very unfortunate. + ...So, for those of you who are poor, those who come from difficult +situations, I strongly urge you to work hard, with self-confidence, to make +use of your opportunities. The richer people should be more caring toward the +poorer ones, and the poor should make every effort, with self-confidence. + -- The Dalai Lama, "An Open Heart: Practicing Compassion in Everyday Life", + edited by Nicholas Vreeland, afterword by Khyongla Rato and Richard Gere +~ +Not to be busy + Tibetans say that once upon a time all the yaks that live in Tibet were +living in India as water buffalo. It was very, very hot in India so some of +them decided if they were to keep walking to the north they would get to a +place that would be nice and cool. So they climbed up in the mountains, and +as they were climbing their hair started to grow. Because of this the water +buffalo in India often turn their head and look out expectantly and they are +waiting for their brothers who have wandered off. In a similar way at one +time all the buffalo of samsara and nirvana were living together and one day +some of them wandered off and came into samsara. They keep looking around to +see who else is there and where the other half is, because the basic quality +of our ordinary sense of self is that it is very lonely. Something is missing +in our lives and we don't quite know what it is, but we keep looking and +looking to find this missing part. We can look for it in terms of +possessions, we can look for it in terms of the form of our body, trying to +change it through dieting or hair style or whatever. You can look in terms of +friends. Anything. And this keeps us very, very busy. Sometimes the +busyness can be very exhausting, but when we stop then we feel lonely. So we +get busy again. Dharma is very helpful here if you want distraction because +there are many kinds of ways to be busy in the dharma. You can focus on +having lots of dharma possessions. You can focus on learning the text by +heart, on the mantras and mudras, on serving the tsog, on doing meditations. +There is always something to be busy with. + In Tibet many, many people practiced dharma but not so many seem to get +enlightened. There are many kinds of dharma and if we practice in a way that +doesn't focus on the essential point but on secondary and tertiary levels it +is easy to get lost. It is really important, given that we have limited time, +to focus on what is essential. Many people when they get a plate of food will +eat the things they don't like so much first and leave the special thing to +the end. But when when we apply this to life we can make a big mistake. The +time for deep practice is now. You can learn all about Padmasambhava and what +his clothes mean and what his hair style means but if you don't know the +nature of your own mind then knowledge about Padmasambhava is just some more +concepts. + -- "Being Right Here: A Dzogchen Treasure Text of Nuden Dorje entitled + 'The Mirror of Clear Meaning' ", with commentary by James Low, published + by Snow Lion Publications +~ +Linguistics is arguably the most hotly contested property in the academic +realm. It is soaked with the blood of poets, theologians, philosophers, +philologists, psychologists, biologists, anthropologists, and neurologists, +along with whatever blood can be got out of grammarians. + -- Russ Rymer +~ +What is undistracted calm abiding? It is meditative absorption free of the +six types of distraction. What are these six? + + (1) Inherent distraction refers to the eye consciousness and the other +four collections of consciousness. Because they are naturally directed +outward, they [cause one to] emerge from meditative absorption. + (2) External distraction refers to a mental consciousness that reaches out +towards or engages objects. + (3) Internal distraction concerns dullness and agitation, as well as +savoring one's meditative absorption. + (4) The distraction of marks occurs when, trusting in meditative +absorption, one apprehends marks of it and becomes attached. + (5) Distraction brought about by negative tendencies is when directing the +mind involves the apprehending of an ego. This is said to refer to the mental +act of pridefully believing oneself to be superior to others, or [simply any +mental act] that involves apprehending an "I". + (6) The distraction of directing the mind occurs when one is caught up in +the mindset of, and directs the mind in the style of, the Lesser Vehicle. + +The undistracted calm abiding that is determined by the elimination of +those six is the unique calm abiding of the Great Vehicle. This is a state of +one-pointed inner rest, a flawless calm abiding. In it, there is no +apprehension of marks, as is the case when inner absorption alone is believed +to bring liberation. Neither does it involve the ego apprehension that occurs +in the concentrations of non-Buddhists. Further, one does not direct the mind +as one would when cultivating the supports for the inferior paths [to +liberation]. This is how the wise should understand the calm abiding of the +Great Vehicle. + -- "Middle Beyond Extremes: Maitreya's 'Madhyantavibhaga' with commentaries + by Khenpo Shenga and Ju Mipham", translated by the Dharmachakra + Translation Committee, published by Snow Lion Publications +~ + Calm abiding is predominantly stabilizing meditation, in which the mind is +kept on a single object, rather than analytical meditation, in which a topic +such as impermanence or emptiness is analyzed with reasoning. The purpose of +developing calm abiding is that, since a mind that is scattered to external +objects is relatively powerless, the mind needs to be concentrated in order to +become powerful. If you do not have concentration in which the mind is +unfluctuatingly stable and clear, the faculty of wisdom cannot know its +object, just as it is, in all its subtleties. Therefore, it is necessary to +have a highly focused mind. + ...In order to set the mind steadily on an object of observation, it is +necessary initially to use an object of observation suited to counteracting +your own predominant afflictive emotion, since its force remains with your +mind now and can easily interrupt any attempt to concentrate the mind. +Therefore, Buddha described many types of objects for purifying behavior: + + For someone whose predominant afflictive emotion is desire, ugliness is +a helpful object of meditation. Here, "ugliness" does not necessarily refer +to distorted forms; the very nature of our body--composed of blood, flesh, +bone, and so forth--might seem superficially to be very beautiful with a good +color, solid and yet soft to touch, but when it is investigated, you see that +its essence is quite different--substances like bone, blood, urine, feces, +and so forth. + + For someone who has predominantly engaged in hatred, the object of +meditation is love. + + For someone who was predominantly sunk in obscuration, the meditation is +on the twelve links of the dependent-arising of cyclic existence because +contemplating its complexity promotes intelligence. + + For someone whose predominant afflictive emotion is pride, the +meditation could be on the divisions of the constituents because, when +meditating on the many divisions, you get to the point where you realize that +there are many things you do not know, thereby lessening an inflated sense of +yourself. + + Those dominated by conceptuality can observe the exhalation and +inhalation of the breath because, by tying the mind to the breath, +discursiveness diminishes. + A particularly helpful object for all personality types is a Buddha body, +since concentration on a Buddha's body causes your mind to mix with virtuous +qualities. No matter what the object is, this is not a case of meditating +within, looking at an external object with your eyes, but of causing an image +of it to appear to the mental consciousness. + -- H.H. the Dalai Lama, Dzong-ka-ba and Jeffrey Hopkins, "Yoga Tantra: Paths + to Magical Feats", translated and edited by Jeffrey Hopkins, published + by Snow Lion Publications +~ +...an understanding of the doctrine of emptiness is fundamental to tantric +practice. Every sadhana begins with, is structured around, and ends with +meditation upon emptiness. To practice Vajrayana without the wisdom of +emptiness can be very dangerous. For example, a main tantric technique is the +cultivation of a subtle divine pride, a confidence that one is an enlightened +tantric deity, the Lord of the Mandala. One's mind is the Wisdom Body of a +Buddha, one's speech is the Beatific Body, one's form is the Perfect Emanation +Body, and the world and its inhabitants are seen as a mandala inhabited by the +various forms of tantric deities. Thus we have to utterly change our sense of +"I." To do so involves the subject of emptiness. To practice the yoga of +divine pride without an understanding of emptiness will not only be useless, +but could lead to identity problems and other undesirable psychological +effects. Therefore it is said that although the Vajrayana is a quick path +when correctly practiced on the proper spiritual basis, it is dangerous for +the spiritually immature. This type of danger area is one of the reasons why +the Vajrayana must be practiced under the supervision of a qualified vajra +acharya. + -- H.H. the Dalai Lama, "The Path to Enlightenment", edited and translated + by Glenn H. Mullin, published by Snow Lion Publications +~ + Self-discipline brings us into relationship with one of the six +perfections of the bodhisattva, that of enthusiastic perseverance, which +implies the willingness to engage in a process with effort and enthusiasm over +a prolonged period. No material or spiritual qualities are gained without +some degree of effort. Perseverance enables the practitioner to carry on and +trust in the process, even when it feels hopeless. It makes it possible to +face difficulties and obstacles in the path with confidence and courage, +rather than giving up because it feels too hard. Self-discipline helps us +remain in the vessel and not run away. + My Tibetan retreat guide described the maintenance of self-discipline over +time like keeping a pot heating on a stove. If we continually remove it from +the heat the pot never boils. Similarly he felt that when someone enters into +the discipline of retreat, it should be maintained as rigorously as possible. +In doing so the alchemical vessel will be maintained, and the "cooking" can +take place. Transformation only occurs when the vessel is maintained in this +way. + -- Rob Preece, "The Psychology of Buddhist Tantra", foreword by Stephen + Batchelor, published by Snow Lion Publications +~ +Installation programs are where programming +and the IT department meet... and fight. + -- fred t. hamster +~ +Compassion without attachment is possible. Therefore, we need to clarify the +distinctions between compassion and attachment. True compassion is not just +an emotional response but a firm commitment founded on reason. Because of +this firm foundation, a truly compassionate attitude toward others does not +change even if they behave negatively. Genuine compassion is based not on our +own projections and expectations, but rather on the needs of the other: +irrespective of whether another person is a close friend or an enemy, as long +as that person wishes for peace and happiness and wishes to overcome +suffering, then on that basis we develop genuine concern for their problem. +This is genuine compassion. For a Buddhist practitioner, the goal is to +develop this genuine compassion, this genuine wish for the well-being of +another, in fact for every living being throughout the universe. + -- Tenzin Gyatso, the Fourteenth Dalai Lama, "The Compassionate Life" +~ + Buddha traveled and taught widely for some forty-five years after his +enlightenment, and his audiences were diverse. Even though India at the time +was a highly literate society, nothing of what he said was written down during +his lifetime. Instead, various individuals were entrusted with memorizing the +gist of each discourse. The work of transcribing his words took place only +with the passage of generations. + Tibetans believe that this reluctance on the part of the Buddha and his +immediate followers to commit the enlightenment teachings to paper, and +instead to preserve them as oral traditions, was a purposeful strategy gauged +to maintain the maximum fluidity and living power of the enlightenment +experience. It only became necessary to write things down when the darkness +of the changing times threatened the very survival of the legacy. An oral +tradition becomes lost to history should its holders pass away without first +passing on their lineages. + This intended fluidity, and the according safeguard against the +establishment of an "enlightenment dogma" is perhaps best demonstrated by a +verse that the Buddha himself said shortly before his death: + Do not accept any of my words on faith, + Believing them just because I said them. + Be like an analyst buying gold, who cuts, burns, + And critically examines his product for authenticity. + Only accept what passes the test + By proving useful and beneficial in your life. + This simple statement empowered future generations of Buddhist teachers to +accept and reject at will anything said by Buddha himself as well by his early +disciples. If something that was said by them did not pass the test of +personal analysis, one could simply discard it as being limited in application +to particular times, people, or situations, and therefore as only contextually +valid. + -- H.H. the Dalai Lama, "The Dalai Lamas on Tantra", translated, edited and + introduced by Glenn H. Mullin, published by Snow Lion Publications +~ + Many places have been totally changed through the use of police force and +the power of guns--the Soviet Union, China, Burma, the Philippines, many +communist countries, countries in Africa and South America. But eventually, +you see, the power of guns and the power of the will of ordinary human beings +will change places. I am always telling people that our century is very +important historically for the planet. There is a big competition between +world peace and world war, between the force of mind and the force of +materialism, between democracy and totalitarianism. And now within this +century, the force of peace is gaining the upper hand. Still, of course, the +material force is very strong, but there is a kind of dissatisfaction about +materialism and a realization or feeling that something is missing. + ...entering the twenty-first century, I think the basic concerns are human +values and the value of truth. These things have more value, more weight now. + -- "The Dalai Lama, A Policy of Kindness: An Anthology of Writings By and + About the Dalai Lama", compiled and edited by Sidney Piburn, Foreword by + Sen. Claiborne Pell, published by Snow Lion Publications +~ +An inexpressible, self-arisen expanse +Without the names "samsara" and "nirvana." + + Here, "self-arisen" means the primordial state. It is not something we +can fully express with words or concepts. It's beyond words or concepts. The +nature of all is not biased; it is not restricted to one or another. The +nature of all exists in one identical state. That ground, that nature, does +not have any name such as samsara or nirvana. That is the foundation, that is +the ground. It is beyond samsara and nirvana. Not knowing the ground means +wandering in samsara. If you recognize this ground, if you truly experience +this ground, buddhahood is attained. That is the fruition. That is the +result of our practice and our path. + ...The ground, that fundamental state of simplicity, is the origin of all +elaborations. This pure basic state is like a simple artist's canvas. We +paint different images on this canvas. We can paint the image of a buddha, +and it becomes very pure, beautiful, and inspiring to look at. We can also +paint a devil on the same canvas, which can create our fundamental suffering, +our basic pain. However, the basis of both is the same simple state of canvas +that is completely pure and totally free from the images we project on it. It +is totally free, whether that image is a buddha or a devil. That is the +origin. + -- The Dzogchen Ponlop Rinpoche, "Penetrating Wisdom: The Aspiration of + Samantabhadra", published by Snow Lion Publications +~ +Weeds are flowers too, once you get to know them. + -- Eeyore from Winnie the Pooh +~ +Setting a Specific Intention for Our Practice + We should think about how we can make the best use of our practice so that +we get the most out of it in the short time we have in this life. We do not +have the leisure of wasting our time here by delaying the benefits of our +practice. We have to use these situations as effectively as we can. + Before you begin any practice, first think very carefully about your +motivation. When we are engaged in the threefold process of study, +contemplation and meditation, we should be very specific, very clear about why +we are doing it. We should remind ourselves, "I am doing this to transcend my +negative emotions and my ego-clinging." This is a general example of a +specific intention. However, to be more precise, we need to consider the +unique make-up of our own individual kleshas [intense states of suffering, and +ignorance]. Once we have identified our strongest emotion, then we can focus +on the practices that will alleviate it. We begin with whichever emotion is +strongest for us and then we move on to the next strongest, followed by the +next, and so on. + It is important for us to prioritize our practice in this way. We have +to keep our intention very clear in all three phases--in our study, in our +contemplation and in our meditation. During shamatha or other practices, when +thoughts come up, we recall that our purpose is to overcome our disturbing +emotions and kleshas. We have to have a sense of willpower or determination +in our minds. In order for the remedy to work, we must tell ourselves, "Yes, +I am going to transcend this anger. I am going to work with it." Otherwise, +if we do not have a clear idea, if we simply sit there with an indefinite or +vague intention, then the effect also will be vague. We may have sat for one +hour and although that time will not have been wasted, because it was not +directed in an intentional way, the experience will not be so sharp, to the +point or effective. + -- Dzogchen Ponlop, "Mind Beyond Death", published by Snow Lion + Publications +~ + ...particularly in Buddhism while we practice we must use the brain as +well as the heart. On the ethical side, we must practice the quality of a +good and warm heart; also, since Buddhism is very much involved in reasoning +and logic--the wisdom side--intelligence is important. Thus, a combination of +mind and heart is needed. Without knowledge, without fully utilized +intelligence, you cannot reach the depths of the Buddhist doctrine; it is +difficult to achieve concrete or fully qualified wisdom. There may be +exceptions, but this is the general rule. + It is necessary to have a combination of hearing, thinking, and +meditating. The Kadampa teacher Dromton ('brom ston pa, 1004-1064) said, +"When I engage in hearing, I also make effort at thinking and meditating. +When I engage in thinking, I also search out more hearing and engage in +meditation. And when I meditate, I don't give up hearing and don't give up +thinking." He said, "I am a balanced Kadampa," meaning that he maintained a +balance of hearing, thinking, and meditating. + -- The Fourteenth Dalai Lama His Holiness Tenzin Gyatso, "Kindness, Clarity, + and Insight", edited and translated by Jeffrey Hopkins, co-edited by + Elizabeth Napper, published by Snow Lion Publications +~ +If through listening to this explanation of the Seven Point Mind Training we +come to recognise how important Bodhichitta is, this will be an infallible +cause of our enlightenment. Of all the eighty four thousand different +sections of the doctrine, the precious Bodhichitta is the very essence. By +hearing the words of such a teaching, it is impossible even for demons, whose +nature it is to kill and to do harm, not to have positive thoughts! Kham, a +region in East Tibet, was haunted in the past by many ghosts and evil spirits, +and this was one of the reasons why Patrul Rinpoche used to explain the +Bodhicharyavatara continually to his disciples. Before long, there were no +more ghosts--or at least, no one came to any more harm. Such is the hidden +power of Bodhichitta! + --Dilgo Khyentse Rinpoche, "Enlightened Courage: An Explanation of the Seven + Point Mind Training", translated by the Padmakara Translation Group, + published by Snow Lion Publications +~ + Sometimes we face certain situations where, although we have done +something good for others, we may not be able to reap the consequences within +this lifetime. When we are talking about the law of causality, we are not +limiting its operation to the confines of this life alone, but rather are +taking into account both this lifetime and the future. Occasionally people +who do not have a proper knowledge of karmic law say that such and such a +person is very kind and religious and so forth, but he always has problems, +whereas so and so is very deceptive and negative, frequently indulging in +negative actions, but always seems very successful. Such people may think +that there is no karmic law at all. There are others who go to the other +extreme and become superstitious, thinking that when someone experiences +illness, it is all due to harmful spirits.... It is also possible for very +negative people to experience their positive karma ripening immaturely due to +the very strong force of negative actions, and thus to exhaust the potentials +of their virtuous actions. They experience a relative success in this life, +while others who are very serious practitioners, as a result of the force of +their practices, bring upon this lifetime the consequences of karmic actions +which might have otherwise thrown them into rebirth in lower realms of +existence in the future. As a result, they experience more problems and +illnesses in this life. + Just resolving not to indulge in a negative action is not enough. It +should be accompanied by the understanding that it is for your own benefit and +sake that you must live with awareness of the law of karma: if you have +accumulated the causes, you will have to face the consequences; if you desire +a particular effect, you can work to produce its causes; and if you do not +desire a certain consequence, you can avoid engaging in actions that will +bring it about. You should reflect upon the law of causality as follows: that +there is a definite relation between causes and effects; that actions not +committed will never produce an effect; and that once committed, actions will +never lose their potentiality simply through the passage of time. So, if you +wish to enjoy desirable fruits, you should work for the accumulation of the +appropriate causes, and if you want to avoid undesirable consequences, you +should not accumulate their causes.... [Karma] is a natural law like any +other natural law. + -- H.H. the Dalai Lama, Tenzin Gyatso, "Path to Bliss: A Practical Guide to + Stages of Meditation", translated by Geshe Thubten Jinpa, edited by + Christine Cox, published by Snow Lion Publications +~ + Generally speaking, there are two forms of meditation on emptiness. One +is the space-like meditation on emptiness, which is characterised by the total +absence or negation of inherent existence. The other is called the illusion- +like meditation on emptiness. The space-like meditation must come first, +because without the realisation of the total absence of inherent existence, +the illusion-like perception or understanding will not occur. + For the illusion-like understanding of all phenomena to occur, there needs +to be a composite of both the perception or appearance and the negation, so +that when we perceive the world and engage with it we can view all things and +events as resembling illusions. We will recognise that although things appear +to us, they are devoid of objective, independent, intrinsic existence. This +is how the illusion-like understanding arises. The author of the "Eight +Verses for Training the Mind" indicates the experiential result when he +writes: "May I, recognising all things as illusion, devoid of clinging, be +released from bondage." + When we speak of cultivating the illusion-like understanding of the nature +of reality, we need to bear in mind the different interpretations of the term +'illusion-like'.... For example, the Buddhist realist schools explain the +nature of reality to be illusion-like in the sense that, although we tend to +perceive things as having permanence, in reality they are changing moment by +moment and it is this that gives them an illusion-like character. + -- H.H. the Dalai Lama, "Lighting the Way", translated by Geshe Thupten + Jinpa, published by Snow Lion Publications +~ + Actually, we are all part of the community of humanity. If humanity is +happy, has a successful life, a happy future, automatically, I will benefit. +If humanity suffers, I too will suffer. Humanity is like one body, and we are +part of that body. Once you realize this, once you cultivate this kind of +attitude, you can bring about a change in your way of thinking. A sense of +caring, commitment, discipline, oneness with humanity--these are very relevant +in today's world. I call this secular ethics, and this is the first level to +counter negative emotions. + The second level in this connection is taught by all major religious +traditions, whether Christian or Muslim or Jewish or Hindu. They all carry +the message of love, compassion, forgiveness, tolerance, contentment, and +discipline. These are countermeasures for negative emotions. When anger is +about to surface, when hatred is about to flare up, think of tolerance. It is +important to stop any mental dissatisfaction when we feel it because it leads +to anger and hatred. + Patience is the countermeasure for mental dissatisfaction. Greed and its +self-centeredness bring unhappiness, and also destruction of the environment, +exploitation of others, and increases the gap between the rich and the poor. +The countermeasure is contentment. So practicing contentment is useful in our +daily lives. + ...All religious traditions talk about methods of compassion and +forgiveness. If we accept religion, we should take the religious methods +seriously and sincerely and use them in our daily lives. Then, a meaningful +life can develop. + -- His Holiness the Dalai Lama, "Many Ways to Nirvana: Reflections and + Advice on Right Living", edited by Renuka Singh +~ + Actually, if we look around, people whom we don't like and people who harm +us are in the minority. Let's say we're at work, at a social gathering, or at +a Dharma center with thirty people. How many of them do we really dislike? +We may have problems with a few people here and there, but we manage to stay +in a room together, don't we? It's not like we despise them and they hate us. +The number of people we can't stand in this world is actually very small. +These people are rare. To practice patience we need the people that we don't +like. We can't practice patience with our friends or with people who are kind +to us. Finding people that we don't like or who threaten us is not so easy. +So, when we finally find them, they are a precious treasure! They are rare to +find. When we meet them, we can think, "Fantastic, I get to practice patience +now." + They say that high-level bodhisattvas pray to meet disgusting, +uncooperative people because they want to practice patience. Of course, when +you really want to meet obnoxious people, they don't show up! Why don't they +turn up for high-level bodhisattvas? Because high-level bodhisattvas don't +have any anger. We could be sitting in a room with many people whom we +consider unbearable, but high-level bodhisattvas don't see them that way at +all. To them, these people appear lovable. Bodhisattvas have such a hard +time finding detestable people, whereas we come across them so easily! So, +when we find people whom we don't like, feel threatened by, or find +despicable, we should recognize that there aren't so many of them around. +Therefore, we should cherish them and take the opportunity to practice +patience with them. + -- Bhikshuni Thubten Chodron, "Cultivating a Compassionate Heart: The Yoga + Method of Chenrezig", foreword by H.H. the Dalai Lama, published by + Snow Lion Publications +~ +Whether you think you can or you think you can't, you're right. + -- Henry Ford +~ +...to have greater self-awareness or understanding means to have a better +grasp of reality. Now, the opposite of reality is to project onto yourself +qualities that are not there, ascribe to yourself characteristics in contrast +to what is actually the case. For example, when you have a distorted view of +yourself, such as through excessive pride or arrogance, because of these +states of mind, you have an exaggerated sense of your qualities and personal +abilities. Your view of your own abilities goes far beyond your actual +abilities. On the other hand, when you have low self-esteem, then you +underestimate your actual qualities and abilities. You belittle yourself, you +put yourself down. This leads to a complete loss of faith in yourself. So +excess--both in terms of exaggeration and devaluation--are equally +destructive. lt is by addressing these obstacles and by constantly examining +your personal character, qualities, and abilities, that you can learn to have +greater self-understanding. This is the way to become more self-aware. + -- His Holiness the Dalai Lama and Howard C. Cutler, M.D., "The Art of + Happiness at Work" +~ +Phowa is a practice which should be done repeatedly, all through our lives so +that we can do it naturally and purposefully at the time of death. I have +heard a story about a Tibetan who was dying and his family called the lama to +be with him. The lama sat beside him and told him to think only of his root +guru and forget everything else. He said, "I can't recall my guru, I can only +think of a sizzling sausage being warmed in the ashes of a fire." The lama was +very skillful: "That is excellent!" he said. "Dewachen, the paradise of the +Amitaba Buddha, is full of sausages; they grow on every tree. You only have +to open your mouth and you will have all the sausages you want. The color of +Amitaba is like the embers of a fire, so think of him and you will go to his +realm." It is said that the man went straight to the pure land of Dewachen. + -- Ringu Tulku, "Mind Training", edited by B.M. Shaughnessy, published + by Snow Lion Publications +~ +"Yogis should at all times avoid fish, meat, and so forth, +should eat with moderation, and avoid foods that are not +conducive to health." --Kamalashila + + Meditators need to be physically healthy. Therefore, proper diet is +essential. On the other hand, their minds should be clear and strong and this +will also contribute to physical health. For these reasons, it is recommended +that they give up eating fish, meat, garlic, onions, etc. Appropriate food +should be eaten in moderation, for indigestion can cause havoc with +meditation. What's more, those who overeat can hardly stay awake. + ...If a vegetarian diet does not result in protein deficiency, it is a +wholesome way of living. Even if you cannot be a strict vegetarian, at least +moderating the amount of meat you eat is beneficial. Within the southern +schools of Buddhism eating meat is not strictly prohibited, but the meat of +certain animals, such as those that are not cloven-hoofed or those that have +been slaughtered specifically for your consumption, is forbidden. This means +that meat bought casually in the market is acceptable.... However, certain +scriptures... strictly prohibit eating meat at all times, whereas other +scriptures... seem to permit it. + -- H.H. the Dalai Lama, "Stages of Meditation", root text by Kamalashila, + translated by Geshe Lobsang Jordhen, Losang Choephel Ganchenpa, and + Jeremy Russell, published by Snow Lion Publications +~ +It would be wrong to say, as some do, that if we don't recite prayers while +being aware of their meaning and merely repeat the words mindlessly, it has no +benefit whatsoever--like prayer flags flapping in the wind. However, there +are indeed differences in the level of benefits and blessings derived from +prayers according to the way we recite them. Therefore, keeping this in mind, +at the beginning of the practice, generate bodhicitta. During the main +practice, some will use an object of concentration and some will practice +without an object of concentration; each person should do what is best +according to their level. At the end, one should dedicate the merit in a way +that is pure from the three conceptual spheres to the best of one's ability. +The most important and essential thing in making [prayer] meaningful is to +depend on those three stages of practice--generation of bodhicitta, the main +practice and dedication of merit. All must do the complete three stages of +practice. + -- Chatral Rinpoche, "Compassionate Action", edited, introduced, and + annotated by Zach Larson, published by Snow Lion Publications +~ + "When the thought of the internal + And the external as 'I' and 'mine' + Has perished, grasping ceases + And through that cessation birth ceases. + + When actions and afflictions cease, there is liberation; + They arise from false conceptions, these arise + From the elaborations [of false views on inherent + Existence]; elaborations cease in emptiness." + -- Nagarjuna + +Inherent existence has never been validly known to exist; therefore, it is +impossible for there to be any phenomenon that exists through its own power. +Since it is experienced that mere dependent-arisings, which are in fact empty +of inherent existence, do cause all forms of help and harm, these are +established as existent. Thus, mere dependent-arisings do exist. Therefore, +all phenomena exist in the manner of appearing as varieties of dependent- +arisings. They appear this way without passing beyond the sphere or condition +of having just this nature of being utterly non-inherently existent. +Therefore, all phenomena have two entities: one entity that is its superficial +mode of appearance and one entity that is its deep mode of being. These two +are called respectively conventional truths and ultimate truths. + -- H.H. the Dalai Lama, "The Buddhism of Tibet", translated and edited by + Jeffrey Hopkins, published by Snow Lion Publications +~ +The cloistered environment stands in stark contrast to the uncontrolled +environment of everyday active life in the modern world. When I was a +graduate student living in a family housing unit at Stanford University, I +meditated early in the morning. At about 7:00 outside our window, a group of +little girls would begin shrieking and driving their plastic tractors and +tricycles across the bricks. I was meditating and these girls were disturbing +my peace. I got to feeling pretty sorry for myself so I phoned my lama, +Gyatrul Rinpoche, and asked for advice. He gave me a one-liner, "Just view +it." This was not just Rinpoche's way of telling me to quit whining, but a +reminder of the more encompassing teaching to embrace obstacles in practice. +And carry on. We can't always control our environment, but we can embrace it, +the good, the bad, and the loud, and integrate it into Dharma practice. + -- B. Alan Wallace, "Buddhism with an Attitude: The Tibetan Seven-Point + Mind-Training", edited by Lynn Quirolo, published by Snow Lion Pub. +~ +He who throws mud only loses ground. -- Fat Albert (Bill Cosby) +~ +Grow old along with me! +The best is yet to be. + -- Robert Browning +~ +It isn't the mountain ahead that wears you out; +it's the grain of sand in your shoe. + -- Anonymous +~ +If scientific reasoning were limited to the logical processes of arithmetic, +we should not get very far in our understanding of the physical world. One +might as well attempt to grasp the game of poker entirely by the use of the +mathematics of probability. + -- Vannevar Bush +~ +I have a rock garden. Last week three of them died. --Richard Diran +~ +Hegel was right when he said that we learn from history +that man can never learn anything from history. + -- George Bernard Shaw +~ +America believes in education: the average professor earns more +money in a year than a professional athlete earns in a whole week. + -- Evan Esar +~ +We are more ready to try the untried +when what we do is inconsequential. +Hence the fact that many inventions +had their birth as toys. + -- Eric Hoffer +~ +Do not fear to be eccentric in opinion, +for every opinion now accepted was once eccentric. + -- Bertrand Russell +~ +Man is most nearly himself when he achieves +the seriousness of a child at play. + -- Heraclitus +~ + If we have been reborn time after time, it is evident that we have needed +many mothers to give birth to us.... the first cause bringing about +bodhicitta is the recognition that all beings have been our mother. + The love and kindness shown us by our mother in this life would be +difficult to repay. She endured many sleepless nights to care for us when we +were helpless infants. She fed us and would have willingly sacrificed +everything, including her own life, to spare ours. As we contemplate her +example of devoted love, we should consider that each and every being +throughout existence has treated us this way. Each dog, cat, fish, fly, and +human being has at some point in the beginningless past been our mother and +shown us overwhelming love and kindness. Such a thought should bring about +our appreciation. + ...if all other sentient beings who have been kind to us since +beginningless time are suffering, how can we devote ourselves to pursuing +merely our own happiness? To seek our own happiness in spite of the suffering +others are experiencing is tragically unfortunate. Therefore, it is clear +that we must try to free all sentient beings from suffering. + -- H.H. the Dalai Lama, "An Open Heart: Practicing Compassion in Everyday + Life", edited by Nicholas Vreeland, afterword by Khyongla Rato and + Richard Gere +~ +3.2.4 The way in which experiences dawn through practicing [Mahamudra] + +In beginners, this is similar to water [gushing down] a gorge. +In between, it is the gentle flow of the river Ganga. +Finally, all waters meet like a mother and her child. -- Tilopa + + The meditative equipoise of beginners entails intense movement of +thoughts, similar to water gushing down a narrow gorge. The reason for this +is as follows. Though there is some slight resting in equipoise, thoughts +proliferate. Right at that point, through the remedy of alertness and by +considering that you like resting in meditative concentration and dislike not +resting in it, you rest in meditative equipoise again. Through such an +approach, your mind becomes somewhat uplifted. + The meditative equipoise of those who have attained a little bit more +stability than that and are of middling faculties is similar to the gentle +flow of the river Ganga. The reason is as follows. Even if some thoughts +come up, a little here and there, their own face is immediately recognized, so +that the movement of thoughts does not run wild. Without various notions that +chase after these [subtle thoughts] or any physical and mental effort, all +thoughts that come up will dawn slowly. There is also no need to make great +effort in [applying] their remedies. Rather, these happen naturally or of +their own accord. + Finally, in the meditative equipoise of those with highest faculties, +neither thoughts to be relinquished arise nor is there any need to newly +create some remedial wisdom, because there is nothing to be relinquished. +Since existence and peace have become one taste, mother and child luminosity +blend, or, expanse and wisdom have become indifferentiable. Once the +tributary waters and the ocean have become one taste, like a mother and her +child meeting, they cannot be disturbed. + -- The Fifth Shamarpa Goncho Yenla, "Straight from the Heart: Buddhist Pith + Instructions", translated and introduced by Karl Brunnholzl, published + by Snow Lion Publications +~ + The theory of interdependence allows us to develop a wider perspective. +With wider mind, there is less attachment to destructive emotions like anger, +therefore more forgiveness. In today's world, every nation is heavily +interdependent, interconnected. Under these circumstances, destroying your +enemy--your neighbor--means destroying yourself in the long run. You need +your neighbor. More prosperity in your neighbor, you'll get the benefit. + Now, we're not talking about the complete removal of feelings like anger, +attachment, or pride. Just reduction. Interdependence is important because +it is not a mere concept; it can actually help reduce the suffering caused by +these destructive emotions. + We can say the theory of interdependence is an understanding of reality. +We understand that our future depends on global well-being. Having this +viewpoint reduces narrow-mindedness. With narrow mind, one is more likely to +develop attachment, hatred. I think this is the best thing about the theory +of interdependence--it is an explanation of the law of nature. It affects +profoundly, for example, the environment. + -- His Holiness the Dalai Lama and Victor Chan, "The Wisdom of Forgiveness: + Intimate Conversations and Journeys" +~ +The Suffering of Change + Feelings of suffering change into those of happiness. Feelings of +happiness change into suffering. Both arise in dependence upon internal and +external causes which change. For example, we see food as pleasurable, but if +we eat too much, then it causes suffering. When we are young, we see our +bodies as a source of pleasure. As we become older, the same body becomes a +source of suffering. + Just as a wave is always changing, so the nature of suffering is always to +change. It may be experienced as pleasure or as suffering, but it arises from +the same source. Pleasure arises from suffering. Seeing pleasure as +happiness constitutes suffering. + ...Pain and pleasure are of the same nature. Although they look different +at different times, they both arise from the same sea of delusion and karmic +action. Pleasure or pain, one or the other, arises and then falls back into +the ocean. Thus we can conclude that pleasure and pain within the ocean of +samsara are basically suffering, and dissolve into suffering. + This becomes evident in the wide variety of sudden changes of experience +depicted in films. Love and hatred, happiness and family strife, peace and +war, follow each other in rapid succession. The continuous change, although +exaggerated in films, is characteristic of life in general. + -- Ven. Gen Lobsang Gyatso, "The Four Noble Truths", translated by Ven. + Sherab Gyatso, published by Snow Lion Publications +~ + We feel money and power can bring happiness and solve problems, but they +are not definite causes of those desired states. If that were so, it would +follow that those who have wealth would necessarily have happiness, and those +who do not have wealth would always experience suffering. Money and power +facilitate, but it is clear that they are not the primary causes of, happiness +and solving our problems. It is justified for us to make material and +financial development for building our nation and providing shelter, etc. for +ourselves; we need to do that. But we also need to seek inner development. +As we can see, there are many people who have wealth and power who remain +unhappy, due to which their health declines, and they are always taking +medicines. On the other hand, we find people who live like beggars but who +always remain peaceful and happy. + Therefore, in our daily life a certain way of thinking makes us happy, and +a certain way of thinking makes us unhappy. In other words, there are certain +states of mind which bring us problems, and they can be removed; we need to +make an effort in that direction. Likewise, there are certain states of mind +that bring us peace and happiness, and we need to cultivate and enhance them. + -- H.H. the Dalai Lama XIV, "Generous Wisdom: Commentaries", the + Jatakamala translated by Tenzin Dorjee edited by Dexter Roberts +~ +Focusing the Mind on the Deity + + Those with superior mental capacity should refine their ability by +practicing the development stage without any sense of clinging or fixation. +In this approach, the appearance of the deity and its ornaments are visualized +in such a way that they are totally complete, vivid, and distinct from the +very beginning. This is the form of great wisdom, the union of development +and completion. Beyond being an identifiable entity with a precise nature, it +appears clearly yet is devoid of any essence. In other words, clarity and +emptiness are indivisible. Like the reflection of the moon in a lake, its +very nature is to appear in a distinct manner, down to the pupils of the eyes, +while in reality it is empty. + Those with moderate mental capacity should begin their meditation with a +sudden recollection of the deity's complete appearance. The next step is to +meditate on the clear appearance of the head, and, once this is stable, to +then meditate progressively on the right arm, left arm, torso, right and left +legs, and finally on the complete form of the deity and its seat. Training in +the development stage of illusory clear appearance keeps one from straying +into the view of nihilism. When one grows weary of this, the practitioner +should recollect purity and refine his or her ability in the essence of this +process, the vajra-like absorption. This key point keeps one from straying +into the belief in permanence. + For beginners with less mental capacity, it may be difficult to visualize +in either of these ways. When not yet familiar with this process, one's +ability should be refined using a permanent form. Take a consecrated and +well-formed representation of the yidam deity, such as a painting or clay +statue made by a skilled artisan, and place it before you. Without +intentionally meditating, look directly at it from top to bottom without +blinking. This is referred to as the auxiliary practice of setting +mindfulness into motion. At first, the agitated movement of conditioned +thought patterns will be experienced. This is the experience of movement, +which is said to be "like water cascading off a cliff." + -- Jigme Lingpa, Patrul Rinpoche, and Getse Mahapandita, "Deity, Mantra, + and Wisdom: Development Stage Meditation in Tibetan Buddhist Tantra", + translated by the Dharmachakra Translation Committee, fore. by Trulshik + Rinpoche, fore. by Chokyi Nyima Rinpoche, published by Snow Lion + Publications +~ + The reason why we find so much discussion of epistemology, or how to +define something as a valid cognition, in Buddhist writings is because all our +problems, suffering and confusion derive from a misconceived way of perceiving +things. This explains why it is so important for a practitioner to determine +whether a cognitive event is a misconception or true knowledge. For it is +only by generating insight which sees through delusion that we can become +liberated. + Even in our own experience we can see how our state of mind passes through +different stages, eventually leading to a state of true knowledge. For +instance, our initial attitude or standpoint on any given topic might be a +very hardened misconception, thinking and grasping at a totally mistaken +notion. But when that strong grasping at the wrong notion is countered with +reasoning, it can then turn into a kind of lingering doubt, an uncertainty +where we wonder: "Maybe it is the case, but then again maybe it is not". That +would represent a second stage. When further exposed to reason or evidence, +this doubt of ours can turn into an assumption, tending towards the right +decision. However, it is still just a presumption, just a belief. When that +belief is yet further exposed to reason and reflection, eventually we could +arrive at what is called 'inference generated through a reasoning process'. +Yet that inference remains conceptual, and it is not a direct knowledge of the +object. Finally, when we have developed this inference and constantly +familiarized ourselves with it, it could turn into an intuitive and direct +realization--a direct experience of the event. So we can see through our own +experience how our mind, as a result of being exposed to reason and +reflection, goes through different stages, eventually leading to a direct +experience of a phenomenon or event. + -- H.H. the Dalai Lama, "Dzogchen: The Heart Essence of the Great + Perfection", translated by Thupten Jinpa and Richard Barron, foreword by + Sogyal Rinpoche, edited by Patrick Gaffney, published by Snow Lion + Publications +~ +Not recognizing their own face, but letting them run wild, one thought leads +to many kinds of [other] thoughts. If you fall into letting this continue, +you wander around in confusion. Through directly looking at the face of +whatever thought that comes up at the very start [of a potential train of +thought], without being able to stand its own ground, just like a rainbow +fading away in space, this thought vanishes into emptiness. Since you arrive +at such within the previous experience of stillness, if you become familiar +with it, the stream of confusion is severed through thoughts coming to rest on +their own and vanishing on their own. Hence, if you know how to sustain this, +even if you regard movement as a flaw and [try to] stop it, you need neither +stop it nor [apply] any other remedy for the movement of thoughts. Rather, by +sustaining the state of realizing their own essence, you realize the essential +point that all the various appearances of happiness and suffering emerge from +the mind and dissolve back into the mind. Through this, you realize the +essential point that all of cyclic existence and nirvana is produced by the +mind, the mind resting naturally settled without being affected by thoughts +about the three times. + -- "Straight From the Heart: Buddhist Pith Instructions", translated and + introduced by Karl Brunnholzl, published by Snow Lion Publications +~ +It's a dangerous business going out your front door. -- JRR Tolkien +~ +The only way to entertain some folks is to listen to them. -- Kin Hubbard +~ +Never face facts; if you do, +you'll never get up in the morning. + -- Marlo Thomas +~ +I hate television. +I hate it as much as I hate peanuts. +But I can't stop eating peanuts. + -- Orson Welles +~ +One cannot always be a hero, but one can always be a human. -- Goethe +~ +The opposite of talking isn't listening. +The opposite of talking is waiting. + -- Fran Lebowitz +~ +Holding onto anger is like grasping a hot coal +with the intent of throwing it at someone else; +you are the one who gets burned. + -- Shakyamuni Buddha +~ +Question: Western religions use the term "God", and Buddhism does not. Could +emptiness or nirvana be considered God? If the afflictive obstruction that is +the conception of inherent existence is eliminated, does one realize that +everything is God? + +Dalai Lama: If God is interpreted as an ultimate reality or truth, then +selflessness may be considered as God and even as a creator in the sense that +within the nature of emptiness things appear and disappear. In this sense, +emptiness is the basis of everything; because of emptiness, things can change, +and things can appear and disappear. Thus, voidness--emptiness, +selflessness--is this kind of basis. + + -- H.H. the Dalai Lama of Tibet, "The Dalai Lama at Harvard: Lectures on the + Buddhist Path to Peace", translated and edited by Jeffrey Hopkins, + published by Snow Lion Publications +~ +Immeasurable Joy + + Because such love and compassion have not arisen in their mindstreams, +people don't understand that all sentient beings are their kind mothers. They +hold on to them as friends or foes, and the power of bad karmic action causes +them to experience the immeasurable suffering of cyclic existence. "Wouldn't +it be a joy if I could carry the suffering of those mothers, and if all beings +could have all of my happiness and virtue? In order to establish these +mothers in happiness, what a joy it would be if, until cyclic existence is +empty, their suffering and the cause and effect of suffering, their sins and +the cause and effect of sins, would all ripen in me and these mothers would +become abundantly happy. I give my body, enjoyments, power, prestige, and +roots of virtue in all times for the sake of these mothers. I won't pursue my +own peace and happiness for even a moment. I will work for the welfare of +beings without regard for life or limb. These mothers must have the entire +range of happiness and the causes of happiness." With that thought, meditate +on joy. + "Furthermore, I will not shrink away from the specific harm done to +sentient beings, or any kind of sickness, suffering, misfortune, enemies, and +obstructions that happen to me for their sakes. What a joy if all the +suffering of beings ripened in me, so that I would have that kind of +suffering. And even a greater joy when those suffering beings are free of +suffering and dwell in exceptional happiness." + Generate an extraordinary attitude with that thinking. It is important +that such joy does not stray into any kind of bias. And if you know it all to +be like a dream or an illusion, free of fixation to true existence, it is +called immeasurable joy. + -- "Machik's Complete Explanation: Clarifying the Meaning of Chod (Dharma)", + translated and edited by Sarah Harding, a Tsadra Foundation Series + book published by Snow Lion Publications +~ +Curiosity is the very basis of education +and if you tell me curiosity killed the cat +I say only that the cat died nobly. + -- Arnold Edinborough +~ +Knowledge will forever govern ignorance +and a people who mean to be their own governors +must arm themselves with the power knowledge gives... + -- James Madison +~ +Question: What should you say to a loved one who is talking about a third +person with hatred or anger? On the one hand, you want to show compassion for +the feelings being experienced by the loved one. On the other hand, you don't +want to reinforce or lend approval to that hatred. What might one say? + +Dalai Lama: Here I would like to tell a story. Once there was a Kadampa +master called Gampowa who had many responsibilities. One day he complained to +the Kadampa master Dromtonpa that he had hardly any time for his meditation or +for his Dharma practice. So Dromtonpa responded by agreeing with him, "Yes, +that's right. I don't have any time either." Then once an immediate affinity +was established, Dromtonpa skillfully said, "But, you know what I am doing is +for the service of the Dharma. Therefore, I feel satisfied." Similarly, if +you find one of your beloved ones speaking against someone out of anger or +hatred, maybe your initial reaction should be one of agreement and sympathy. +Then once you have gained the person's confidence, you can say, "But...." + -- H.H. the Dalai Lama, "Healing Anger: The Power of Patience from a + Buddhist Perspective", translated by Geshe Thupten Jinpa, published + by Snow Lion Publications +~ + ...if you have not purified ordinary appearances into emptiness, how could +you possibly meditate on the mandala circle? The fact that all phenomena are +emptiness, that samsara and nirvana are inseparable, is the very reason we are +able to actualize this by meditating on the mandala circle. In other words, +emptiness is the basis for the development stage. As it is said, "For the one +to whom emptiness is possible, anything is possible." If all phenomena were +not empty and ordinary appearances were truly present, development stage +meditation would be impossible, as the following quotation points out: "Even +though one might empower wheat to be rice, rice won't actually appear." +However, even if all phenomena are realized to be empty in this way, without +the momentum of great compassion you will not be able to manifest the +rupakayas to benefit others. This is similar to the listeners and solitary +buddhas, who enter into a state of cessation and do not benefit others with +rupakaya emanations. + Once one understands this point, it will be like the following saying: +"All these phenomena are like an illusion and birth is like taking a stroll +in a park...." Said differently, one will no longer dwell in existence, while +through compassion, one will not get caught up in a state of peace either. +This is the great, universal path of the offspring of the victorious ones. +For all these reasons, making sure the three absorptions are not isolated from +one another is a vitally important point. + -- Jigme Lingpa, Patrul Rinpoche, and Getse Mahapandita, "Deity, Mantra, + and Wisdom: Development Stage Meditation in Tibetan Buddhist Tantra", + translated by the Dharmachakra Translation Committee, fore. by Trulshik + Rinpoche, fore. by Chokyi Nyima Rinpoche, published by Snow Lion Pub. +~ + We can experience things without confusion and without being tense. Even +the most disturbed, nervous person has moments of clarity and calmness--even +if only when he or she is peacefully asleep and dreaming pleasant or innocuous +dreams. This demonstrates that confusion and tension are not integral parts +of the nature of mind. Thus confusion can be removed. Not only can it be +removed, but since confusion cannot be validated and can be totally replaced +by understanding, which can be verified, confusion can be eliminated forever. +Thus it is possible for a total absence of confusion to exist. Furthermore, +since confusion limits mind from using its full potentials, once confusion is +gone, a utilization of all potentials can also exist. Therefore, since we all +have a mind, and all minds have the same nature of being able to experience +whatever exists, we can all realize and experience the definitive Three +Precious Gems. + Thus, if we aim to remove our confusion and realize our potentials as +indicated by the Buddhas, their achievement, their teachings, what they have +built up along the path and those who are progressing along it, we are +traveling through life with a safe, reliable and positive direction. Taking +refuge, then, means to put this realistic, safe direction in our life. +Without it, our practice of mahamudra either has no direction and leads +nowhere, or an unsound direction leading to more confusion and trouble. In +addition, the further we travel in this safe direction through the mahamudra +techniques--in other words, the more we realize the nature of mind and its +relation to reality--the more confident we become in the soundness of this +direction and our ability to reach its goal. The stronger our confidence, the +further we progress along the path. + -- H.H. the Dalai Lama and Alexander Berzin, "The Gelug/Kagyu Tradition of + Mahamudra", published by Snow Lion Publications +~ + The Rolling Stones have a song that goes, + "Wild horses couldn't drag me away. + Wild, wild horses, we'll ride them some day." +That is the level that we have to reach, where wild horses cannot drag us +away from the present moment of awareness. Once we have reached that level of +training, then even in the bardos of death we will be able to guide our mind +steadily past all difficulties toward awakening, toward freedom from samsara. + There is another well-known image; it compares our wild minds to a mad +elephant in a china shop. When untamed, this elephant can very easily destroy +many things in the shop, and even the shop itself. With one move, the +elephant can destroy a wall; and with another move, another wall. In only +four moves, this elephant can destroy the whole structure. In the same way, +if our minds are not tamed, they can easily destroy our whole collection of +virtue--all the merit and wisdom we have accumulated through the +accomplishment of countless positive deeds. + Vipashyana meditation is the process of taming and training our minds. +How do we do it? We catch our minds with shamatha and we train them with +vipashyana. Then we ride our minds with mindfulness while remaining aware of +the greater environment. Following these methods, we will reach our goal +quite quickly--especially when we remember the thought of impermanence, which +works like a whip. + -- Dzogchen Ponlop, "Mind Beyond Death", published by Snow Lion Publications +~ +With or without religion, you would have good people doing +good things and evil people doing evil things. But for good +people to do evil things, that takes religion. + -- Steven Weinberg +~ +Whatever you do will be insignificant, but it is very important that you do it. + -- Mahatma Gandhi +~ +I was born not knowing and have had only a little +time to change that here and there. + -- Richard Feynman +~ +As for me, except for an occasional heart attack, +I feel as young as I ever did. + -- Robert Benchley +~ + It is possible to understand the Buddhist teachings as a method of +psychological healing, comparable to psychotherapy, that teaches us how we can +master destructive forces like anger, envy, and greed. Human beings seem to +be a bundle of different qualities and psychological processes. We should +attentively examine our qualities and be alertly aware of our experiences in +order to recognize what we truly feel and think. At the same time, the +personality of human beings is not seen as a unified whole. According to +these teachings, the heart of consciousness is composed of various elements, +the five types of attachment, or skandhas: body, sensations, perceptions, +instinctual forces, and consciousness. + These inner forces impart the false concept of an ego-consciousness. The +basic problem of emotional disorders therefore lies in a false concept of +identity. This I-blindness should therefore be abolished through self- +study.... The goal is not self-realization but selflessness. + -- His Holiness the Dalai Lama with Felizitas Von Schonborn, "Path of + Wisdom, Path of Peace", foreword by Wei Jingsheng +~ +Life would be unbearable if everything stayed the same because human beings +find situations that are fixed and predictable very hard to tolerate. Even in +small matters, we become uneasy if we feel there is no end in sight. I know +of couples who live harmoniously together for ten years then marry and are +divorced within a year. As soon as they feel bound to each other for the rest +of their lives, they begin to fight. Impermanence removes our reasons for +quarrelling with each other. Arguments only break out if we imagine that our +relationships are endless. When we appreciate that our time with our +families, partners, and friends may be shorter than we think, we get on better +with each other. Awareness of impermanence gives us extraordinary inner +strength and resilience. + -- Ringu Tulku, "Mind Training", edited by B.M. Shaughnessy, published by + Snow Lion Publications +~ +Be like a postage stamp. Stick to one thing until you get there. + -- Josh Billings +~ + The ability to look at events from different perspectives can be very +helpful. Then, practicing this, one can use certain experiences, certain +tragedies, to develop a calmness of mind. One must realize that every +phenomenon, every event, has different aspects. Everything is of a relative +nature. For example, in my own case, I lost my country. From that viewpoint, +it is very tragic--and there are even worse things. There's a lot of +destruction happening in our country. That's a very negative thing. But if I +look at the same event from another angle, I realize that as a refugee, I have +another perspective. As a refugee there is no need for formalities, ceremony, +protocol. If everything were status quo, if things were okay, then on a lot +of occasions you merely go through the motions; you pretend. But when you are +passing through desperate situations, there's no time to pretend. So from +that angle, this tragic experience has been very useful to me. Also, being a +refugee creates a lot of new opportunities for meeting with many people. +People from different religious traditions, from different walks of life, +those whom I may not have met had I remained in my country. So in that sense +it's been very, very useful. + It seems that often when problems arise, our outlook becomes narrow. All +of our attention may be focused on worrying about the problem, and we may have +a sense that we're the only one that is going through such difficulties. This +can lead to a kind of self-absorption that can make the problem seem very +intense. When this happens, I think that seeing things from a wider +perspective can definitely help--realizing, for instance, that there are many +other people who have gone through similar experiences, and even worse +experiences. This practice of shifting perspective can even be helpful in +certain illnesses or when in pain. At the time the pain arises it is of +course often very difficult, at that moment, to do formal meditation practices +to calm the mind. But if you can make comparisons, view your situation from a +different perspective, somehow something happens. If you only look at that +one event, then it appears bigger and bigger. If you focus too closely, too +intensely, on a problem when it occurs, it appears uncontrollable. But if you +compare that event with some other greater event, look at the same problem +from a distance, then it appears smaller and less overwhelming. + -- His Holiness the Dalai Lama and Howard C. Cutler, M.D., "The Art of + Happiness: A Handbook for Living" +~ +Three Forms of Compassion + + Chandrakirti explained three types of compassion: compassion aimed at +suffering, aimed at phenomena, and unaimed. With the first, we look at +animate beings in light of their suffering and develop the wish for them to be +free from both that suffering and its causes. One source of their suffering +is their unawareness that they even have any problems, let alone their not +knowing the causes of their problems. For example, our friend becomes upset +at the slightest thing that goes wrong and sees this as normal. He or she +does not understand that hypersensitivity is to blame and that something can +be done to remedy this. When we see this sad situation, our compassion for +our friend becomes even stronger. + Compassion aimed at phenomena looks at beings in light of their moment-to- +moment changes. With it, we wish others to be free of suffering and its +causes based on the understanding that these both are impermanent. We also +see that others are unaware of this fact and so, when depressed, for example, +they make their sufferings worse by imagining that they will last forever. +Realizing this further enhances compassion for them. + Unaimed compassion looks at beings in terms of their voidness. It has the +same wish as the other two forms, but is based on not identifying others +concretely with their suffering. Seeing that others do not have this insight +and that consequently they identify themselves with their problems intensifies +our compassion for them even more. + -- Alexander Berzin, "Developing Balanced Sensitivity: Practical Buddhist + Exercises for Daily Life", published by Snow Lion Publications +~ +Don't be confused; the point of Scrum and XP and other agile methodologies is +the same point behind any management technique of the past 100 years--squeeze +every drop of work out of your workers until they are empty husks and can be +thrown away. If you're a programmer and you think Scrum is going to "empower" +you, you could not be more mistaken. Management will hide behind the +principles of Scrum when it suits them and then still do whatever they want +otherwise. When it's pointed out to them that they're violating the agile +principles, they will simply claim that this is how the system is tailored +for our company's "needs" and that we shouldn't blindly follow it. But if you +need to break with an agile practice, oh no, the process is *God* and we must +follow it to the exact letter. + -- John Nesmith +~ + I always believe that each individual human being has some kind of +responsibility for humanity as a whole.... + Through my own profession, I try my best to contribute as much as I can. +This proceeds without my being concerned whether another person agrees with +my philosophy or not. Some people may be very much against my belief, my +philosophy, but I feel all right. So long as I see that a human being suffers +or has needs, I shall contribute as much as I can to contribute to their +benefit. + -- Dalai Lama XIV, "Consciousness at the Crossroads", edited by Zara + Houshmand, Robert B. Livingston, and B. Alan Wallace, published by + Snow Lion Publications +~ +A Song on Impermanence + +With this spouse and these near and dear ones you desire to live together, +Inseparable for all times, but there is no doubt that you will be separated. + +From this excellent home you would like to be inseparable forever +And take root in it, but you will surely depart. + +From this happiness, well-being, and wealth you want to be inseparable forever +So you can relish them, but it is certain you will lose them. + +From this supreme human body with its freedoms and riches you wish to be + inseparable +And own it until the end of times, but there is no way that you won't die. + +From this really great teacher you yearn to be inseparable +And listen to the dharma for all eternity, but there is no question that you + will be separated. + +From these good friends you wish to be inseparable forever +So you can hang out together, but it's a sure thing that you will be parted. + +Therefore, from today on, don your armor of vigor-- +The time has come to travel to the land of inseparable great bliss. +You friends who have developed weariness from the depths of your hearts, +I, a dharma-beggar, request you to do so. + + -- The Omniscient Longchen Rabjam, "Straight from the Heart: Buddhist Pith + Instructions", translated and introduced by Karl Brunnholzl, published + by Snow Lion Publications +~ + The wisdom that realizes emptiness, that has gained insight into the +nature of reality, is of varying kinds, depending upon the level of subtlety +of the consciousness perceiving the emptiness. In general, there are rough +levels of consciousness, more subtle levels, and then the innermost subtle +level of consciousness. It is the uncommon characteristic of Tantric practice +that through it one can evoke this most subtle consciousness at will and put +it to use in a most effective way. For example, when emptiness is realized by +this subtlest level of mind, it is more powerful, having a much greater effect +on the personality. + In order to activate or make use of the more subtle levels of +consciousness, it is necessary to block the rougher levels--the rougher or +grosser levels must cease. It is through specifically Tantric practices, such +as the meditations on the chakras and the channels, that one can control and +temporarily abandon the rougher levels of consciousness. When these become +suppressed, the subtler levels of consciousness become active. And it is +through the use of the subtlest level of consciousness that the most powerful +spiritual realizations can come about. Hence, it is through the Tantric +practice involving the most subtle consciousness that the goal of +enlightenment can most quickly be realized. + -- H.H. the Dalai Lama, "Answers: Discussions with Western Buddhists", + edited by Jose Ignacio Cabezon, published by Snow Lion Publications +~ +By building up good habits of the mind in meditation, our behavior in daily +life gradually changes. Our anger decreases, we are better able to make +decisions, and we become less dissatisfied and restless. These results of +meditation can be experienced now. But we should always try to have a broader +and more encompassing motivation to meditate than just our own present +happiness. If we generate the motivation to meditate in order to make +preparation for future lives, to attain liberation from the cycle of +constantly recurring problems, or to reach the state of full enlightenment for +the benefit of all beings, then naturally our minds will also be peaceful now. +In addition, we'll be able to attain those high and noble goals. + -- Thubten Chodron, "Buddhism for Beginners", published by Snow Lion + Publications +~ + ...all four tantra sets make use of deity yoga, the special tantric means +for amassing the collections of merit and wisdom quickly. Highest Yoga Tantra +has, in addition, techniques for generating subtler minds that realise +emptiness and for using the winds or currents of energy that are the mounts of +these subtler minds as the substantial cause of an actual divine body. +Through this enhancement of the wisdom consciousness the obstructions to +omniscience are quickly removed and Buddhahood is attained. + In the three lower tantras--Action, Performance, and Yoga--deity yoga is +used to bring about the speedy achievement of many common feats and to come +directly under the care of Buddhas and high Bodhisattvas, receiving their +blessings, and so forth. This is done through a threefold process known as +prior approximation, effecting the achievement of feats, and using the feats +in the performance of activities for the welfare of others. + -- H.H. the Dalai Lama, Tsong-ka-pa and Jeffrey Hopkins, "Deity Yoga in + Action and Performance Tantra", published by Snow Lion Publications +~ +If you are able to think about the meaning of cyclic existence in general and +human life in particular, then it is possible to discipline the mind through +religious practice which is the process of becoming peaceful and anxiety-free. +Otherwise, if too much emphasis is put on the sufferings of the hells and the +imminence of death, there is a chance of falling into paralysing fear. There +is a story in Tibet about an abbot of a monastery who went to give a +discourse. A fellow asked the abbot's servant where the abbot had gone, and +the servant said, "He has gone to frighten old men and women." If you fulfil +the value of a human lifetime through engaging in religious practice, then +there is no point in worrying about death. + -- H.H. the Dalai Lama, Tsong-ka-pa and Jeffrey Hopkins, "Tantra in Tibet", + published by Snow Lion Publications +~ +...if a fire which consumes +One house moves to another, +It is right to throw out anything +Like straw which could ignite. + +Likewise anything to which the mind +Is attached ignites the fire of anger. +Fearing our merit will be consumed, +It should be discarded at once. + + If a house is on fire and the fire is spreading, we need to clear away straw, +wood or anything else which is highly flammable and could cause a conflagration +that would consume our entire home and property. Similarly, one way to prevent +desire and attachment is to avoid contact with the objects that stimulate it. +If anything comes between us and what we desire or if the thing to which we're +attached is harmed or threatened, we instantly feel angry. This destroys the +positive energy we've created. + Another way is not to avoid the objects but to contemplate their unappealing +aspects, because desire results from focusing only on their attractive side. +The third way is to contemplate their lack of true existence, since desire and +clinging are based on seeing them as very real and objectively existent. +Whichever technique we employ, the aim is to prevent desire and attachment, +since they bring many other problems. + -- Geshe Sonam Rinchen, "The Thirty-Seven Practices of Bodhisattvas", + translated and edited by Ruth Sonam, published by Snow Lion Publications +~ + Usually when we breathe, we breathe in and, as soon as we have finished +breathing in, we immediately start breathing out. And as soon as we have +finished breathing out, we start breathing in again. There is never any space +or gap in between the in-breath and the out-breath. Now, many different ways +of focusing the mind on the breathing have been taught.... There are +basically six methods taught in the abhidharma. But here we have something +different from any of those. This is called gentle threefold breathing. It +is called gentle because there is no particular attempt to manipulate the +breathing, except that instead of breathing in and then immediately breathing +out, after breathing in, you wait before you breathe out...here the duration +of the inhalation, of the retention, and of the exhalation should all be +equal, three equal periods within each complete breath. + In doing this, some people combine the phases of the breath with the +mental repetition of the three mantra syllables: OM AH HUM (HUNG)--OM +coordinated with the in-breath, AH with the retention of the breath, and HUM +(HUNG) with the out-breath. But what is most important here is simply to +recollect, as they occur, the inhalation, retention, and exhalation, so that, +while you are inhaling, you are aware that you are doing so; while you are +retaining the breath, you are aware that you are doing so; and while you are +exhaling, you are aware that you are doing so. In the beginning, it is +recommended that beginners start with doing, for example, twenty-one of these +breaths as a series, and it is important to practice with enough mindfulness +so that, while you breathe in, and so forth, you maintain an awareness of what +part of the breathing process you are in. + -- Khenchen Thrangu Rinpoche, "The Ninth Karmapa's Ocean of Definitive + Meaning", edited, introduced and annotated by Lama Tashi Namgyal, + published by Snow Lion Publications +~ +Life is fragile, like the dew hanging delicately on the grass, crystal +drops that will be carried away on the first morning breeze. + -- Dilgo Khyentse Rinpoche +~ + ...though the emptiness of an impure phenomenon and the emptiness of a +pure phenomenon are the same, there is a difference. What is the difference? +The continuum of an impure substratum will later cease, not existing in +Buddhahood, whereas a pure substratum's continuum of similar type will exist +right through Buddhahood. Since the deity as whom you are imaginatively +meditating yourself is a divine figure that exists in the state of Buddhahood +when all defilements have been abandoned, this substratum is, for your +imagination, pure. + Hence, it is important when doing deity yoga to put great effort into: + * working at realizing emptiness as much as you can, + * then imagining that the wisdom realizing emptiness appears itself + as a compassionately directed divine body with a face, arms, and + so forth, + * and then taking this divine figure as the substratum and + continuously meditating on its emptiness. + -- H.H. the Dalai Lama, Dzong-ka-ba and Jeffrey Hopkins, "Yoga Tantra: Paths + to Magical Feats", translated and edited by Jeffrey Hopkins, published by + Snow Lion Publications +~ +...when you explain or hear the teachings, if your mind and the teachings +remain separate, then whatever is explained will be inconsequential. Hence, +listen in such a way that you determine how these teachings apply to your +mind. For example, when you want to find out whether or not there is some +smudge, dirt, or whatever, on your face, you look in a mirror and then remove +whatever is there. Similarly, when you listen to the teachings, your faults +such as misconduct and attachment appear in the mirror of the teachings. At +that time, you regret that your mind has become like this, and you then work +to clear away those faults and establish good qualities. Hence, you must +train in the teachings. + --Tsong-kha-pa, "The Great Treatise on the Stages of the Path to + Enlightenment: The Lamrim Chenmo", translated by the Lamrim Chenmo + Translation Committee, Joshua Cutler, Editor in Chief, published + by Snow Lion Publications +~ + if god exists, then god doesn't need me to believe in god. it will not +harm god if i do not believe, because god's existence is not dependent on +my belief. god could contact me directly if god existed and actually cared +what i believed, thus i see no reason to believe in god until then. since +i don't need to believe in god, this frees me from wasting any mental +effort *believing* when that belief will not create god if god doesn't +exist, and when my disbelief will not destroy god if god does exist. +i can't change the state of god's existence one way or the other--i have +realized this limitation in my capabilities. + instead, god needs to believe in me. it's my existence that presumably +requires god's participation in some way, if god does exist. but regardless +of any effort to placate a deity, i feel that i need to be the kind of person +who is worth believing in, by god or by anyone else. my seeking perfection +should not come from fear of an ignored and needy deity. i must build the +best moral and ethical system that i can come up with for myself in this life. +it is my own self-control and my behavior towards others that is the final +word on whether i am worth believing in. + -- fred t. hamster +~ +Sometimes doing nothing is doing something. + -- Sir Thomas Robert Dewar +~ +Enjoy now, another now is coming. + -- Sir Thomas Robert Dewar +~ +Less pain, more gain. + -- Sir Thomas Robert Dewar +~ +The pause is a part of the walk. + -- Sir Thomas Dewar +~ +Go ahead, look around. + -- Sir Thomas Dewar +~ +You better wait for the unexpected. + -- Sir Thomas Dewar +~ +Making a mistake is also an achievement. + -- Lord Dewar +~ +Leave for tomorrow what you can't do today. + -- Lord Dewar +~ +The further you are from a problem, the smaller it gets. + -- Lord Dewar +~ +If you think you know it all, you are missing something. + -- Lord Dewar +~ +Proper rules are not written. + -- Sir Thomas Dewar +~ +Sometimes a step forward needs a step back. + -- Lord Dewar +~ +If you get to the top on your own, who'll take the picture? + -- Sir Thomas Robert Dewar, aka Lord Dewar +~ +When a man says his word is as his bond--get his bond. + -- Sir Thomas Robert Dewar, aka Lord Dewar +~ +Never invest in a going concern until you know which way it is going. + -- Lord Dewar +~ +The quality of the article should be its greatest achievement. + -- Sir Thomas Dewar +~ +If you do not advertise, you fossilize. + -- Sir Thomas Dewar +~ +Yesterday's success belongs to yesterday. + -- Lord Dewar +~ +In charity there is no excess. + -- Lord Dewar +~ +The greatest mistake you can make is to be +continually fearing you will make one. + -- Lord Dewar +~ +Life is a one-way street and you're not coming back. + -- Sir Thomas Robert Dewar +~ +Fish stimulates the brain, but fishing stimulates the imagination. + -- Lord Dewar +~ +Respectability is the state of never being caught +doing anything which gives you pleasure. + -- Lord Dewar +~ +Of two evils, choose the more interesting. + -- Lord Dewar +~ +There is no traffic congestion on the straight and narrow path. + -- Lord Dewar +~ +We have a great regard for old age when it is bottled. + -- Lord Dewar +~ +A philosopher is a man who can look at an empty glass with a smile. + -- Sir Thomas Dewar +~ +A teetotaller is one who suffers from thirst instead of enjoying it. + -- Sir Thomas Dewar +~ +Experience is what you get when you're looking for something else. + -- Lord Dewar +~ +Ability without enthusiasm is like a rifle without a bullet. + -- Lord Dewar +~ +You can send a boy to college but you can't make him think. + -- Lord Dewar +~ +If we are here to help others, I often wonder what the others are here for. + -- Lord Dewar +~ +Don't question your wife's judgment; look who she married. + -- Lord Dewar +~ +I pray for all of us, oppressor and friend, that together we may succeed in +building a better world through human understanding and love, and that in +doing so we may reduce the pain and suffering of all sentient beings. + -- His Holiness the 14th Dalai Lama's Nobel Peace Prize Acceptance Speech, + Oslo, December 1989, "The Pocket Dalai Lama", compiled and edited by + Mary Craig +~ +Benefiting living beings is my main practice, and I would like to give a brief +introduction to the three qualities that are its basis: pure love, compassion, +and bodhichitta, the awakened mind. Pure love is the desire that all living +beings have happiness and its causes. Compassion is the desire that living +beings be free of suffering and its causes, such as unwholesome actions. +Bodhichitta is the desire that all living beings be free of suffering and that +we will be able to place them on the unsurpassed level of awakening, or +buddhahood. + -- "Music in the Sky: The Life, Art, and Teachings of the 17th Gyalwa + Karmapa, Ogyen Trinley Dorje", by Michele Martin, published by Snow + Lion Publications +~ +How can we eliminate the deepest source of all unsatisfactory experience? +Only by cultivating certain qualities within our mindstream. Unless we +possess high spiritual qualifications, there is no doubt that the events life +throws upon us will give rise to frustration, emotional turmoil, and other +distorted states of consciousness. These imperfect states of mind in turn +give rise to imperfect activities, and the seeds of suffering are ever planted +in a steady flow. On the other hand, when the mind can dwell in the wisdom +that knows the ultimate mode of being, one is able to destroy the deepest root +of distortion, negative karma and sorrow. + -- His Holiness the Dalai Lama, "The Path to Enlightenment", translated and + edited by Glenn H. Mullin, published by Snow Lion Publications +~ +It is important to realize that there is nobody else who can wake us up and +save us from samsara. There is no such thing in Buddhism. That may be +Buddhism's biggest drawback, and at the same time its greatest advantage. +This view shows us that there is nobody else in control of our lives, our +experiences, our freedom or our bondage. Who is responsible? Who is in +control? It is us. We are in control. We can bind ourselves further in +samsara or we can free ourselves from it right now. It is all up to us. We +are the ones who have to keep looking at our thoughts, looking for the nature +of our mind. There is no guru, deity, buddha or bodhisattva out there to look +for it for us. Although they would happily do this, it would not help us; it +would only help them. We have to do it for ourselves. That is the key point. + -- Dzogchen Ponlop, "Mind Beyond Death", published by Snow Lion Publications +~ +...mistakenly apprehending inherent existence in all phenomena +serves as the root of all other delusions... + + The opponent force powerful enough to eliminate the delusions should be a +wisdom which combines calm abiding and special insight. In order to cultivate +an advanced meditative stabilization that is free of both subtle mental +sinking and mental excitement, first of all there should be a basis: the +practice of morality, an abstaining from negative actions. Therefore, the +path leading to liberation is comprised of the three higher trainings: the +training of morality as the foundation, the training of meditative +stabilization as the complementing factor, and the actual path which is the +training of wisdom. By enhancing the practice of wisdom and by developing it +to its fullest extent, you will be able to eliminate totally the delusions, +particularly ignorance which misapprehends the mode of being of phenomena. + -- H.H. the Dalai Lama, "The Path to Bliss", translated by Geshe Thupten + Jinpa, edited by Christine Cox, published by Snow Lion Publications +~ + There is considerable ongoing debate regarding the traditional view of the +guru-disciple relationship, which asserts that seeing the guru as Buddha, +impeccable and without failings, is vital to ripen the disciple's potential to +attain the fruits of the path. This is reinforced by the admonition that to +see faults in one's own guru will result in karmic downfalls and future +suffering for the disciple. Any faults in the teacher should be seen as the +disciple's aberrations projected outside. The tantric teachings insist this +pure view should be held at all times to protect the disciple from accruing +negative karma. + However, underlying this is also the need to preserve the integrity, +authority, and status of the teacher. This leads to a great deal of confusion +when students begin to see evident flaws in teachers, and it would be folly to +explain them away as the students' impure perception. Consequently it has +become necessary to cultivate a less dogmatic, more pragmatic view. A teacher +may not be a perfect carrier of the projection, but this does not contradict +the tantric view that essentially the guru, an inner phenomenon projected +outside, is Buddha. + If we literalize this principle of the teacher as the embodiment of +perfection, we are in danger of blinding ourselves to the reality that most +teachers are human, and therefore not perfect. An individual can have deep +insights into the nature of reality and still have human failings, a shadow +that has not been fully eradicated. According to the teachings on the Ten +Grounds or Stages of the Bodhisattva, until the final ground is reached, there +are still subtle obscurations to full enlightenment that can manifest in +flawed behavior. Believing without question that the outer guru is Buddha +also traps the teacher in an unrealistic, unconscious position. The Dalai +Lama has commented that too much deference harms the teacher, because we never +challenge him or her. + When disciples become devoted to teachers, considerable power and +authority is entrusted to them. While a teacher's role is to support and +empower disciples to discover their own potential, sometimes this does not +happen. Some teachers become caught in the powerful position they have been +endowed with and are unaware of their own desire for power and authority. +They may begin to enjoy their power too much and take advantage of it for +their own needs. This keeps their disciples disempowered, and ultimately does +not allow growth and individual responsibility to emerge. Teachers may be +unconsciously afraid to empower their disciples and allow them to gain a sense +of their own authority and autonomy. They may try to hold on to their +disciples, when to genuinely empower them could lead to their leaving to +engage in their own journey. + --Rob Preece, "The Psychology of Buddhist Tantra", foreword by Stephen + Batchelor, published by Snow Lion Publications +~ + Attachment increases desire, without producing any satisfaction. There +are two types of desire, unreasonable and reasonable. The first is an +affliction founded on ignorance, but the second is not. To live, you need +resources; therefore, desire for sufficient material things is appropriate. +Such feelings as, "This is good; I want this. This is useful," are not +afflictions. It is also desirable to achieve altruism, wisdom, and +liberation. This kind of desire is suitable; indeed, all human development +comes out of desire, and these aspirations do not have to be an affliction. + ...when you have attachment to material things, it is best to desist from +those very activities that promote more attachment. Satisfaction is helpful +when it comes to material things, but not with respect to spiritual practice. +Objects to which we become attached are something to be discarded, whereas +spiritual progress is something to be adopted--it can be developed +limitlessly, even in old age. + -- H.H. the Dalai Lama, "How to Expand Love", translated and edited by + Jeffrey Hopkins +~ + Some people find it helpful to set a determination of a reasonable period +of time during which they will sit in meditation without moving. If you do +this, do not make it into a contest in which you grit your teeth in pain just +to say that you sat without moving for a certain length of time. That isn't +conducive for focusing with wisdom on the object of meditation. On the other +hand, avoid moving whenever you feel the slightest bit of restlessness or +discomfort. Doing that isn't conducive for developing concentration either. +Rather, note when there is the urge to move but don't move. Observe the +sensation: Is it really pain or is it simply restless energy in the body? +Learn to differentiate between these two. Learn, also, to differentiate +between pain and discomfort. Watch and study both of those when they arise in +your field of experience. + In general, when attachment, anger, jealousy, or other distracting +emotions arise, observe them without getting involved in their stories. +Experience the feeling, rather than repeat the story to yourself again and +again. Be aware of what it feels like in your body when you are angry, +jealous, arrogant, or clingy. Be aware of the feeling tone in your mind when +one of these emotions is present. Observe how the feeling changes, never +remaining the same. + ...It is important to avoid criticizing yourself when your mind is +distracted or dull. Do not fall into discouraging thoughts or self-hatred +because these are unproductive and are to be abandoned on the path. Remember +that internal transformation takes time and rejoice in your opportunity to +learn and practice the Dharma. "Slowly, slowly," as Lama Thubten Yeshe used +to say. Learn to be satisfied with what you are able to do now while you +aspire to improve in the future. + -- Ven. Thubten Chodron, "Guided Meditations on the Stages of the Path", + foreword by H.H. the Dalai Lama, published by Snow Lion Publications +~ +A family is a place where minds come in contact with one another. If these +minds love one another the home will be as beautiful as a flower garden. +But if these minds get out of harmony with one another it is like a storm +that plays havoc with the garden. + -- Shakyamuni Buddha +~ +You will not be punished for your anger, you will be punished by it. + -- Shakyamuni Buddha +~ +Buddhists take a vow of morality in the context of first taking refuge--in +Buddha, in the states of realization, and in the spiritual community. Refuge +is the foundation for the practice of morality. Buddha teaches us how to find +refuge from suffering and limitation, but the chief refuge, or source of +protection, is found in the states of realization achieved through practicing +morality, concentrated meditation, and wisdom. ...A lama from the Drukpa +Kagyu tradition and I were very close. We met frequently and always used to +joke, teasing each other back and forth. On one occasion I asked him about +his spiritual experience. He told me that when he was young, he was staying +with his lama who had him perform the preliminary practice of making a hundred +thousand prostrations to the Buddha, the doctrine, and the spiritual +community. Early in the morning and late in the evening he had to make +prostrations on a low platform the length of his body. His lama was +meditating in the dark in the next room; so to trick him into thinking he was +making prostrations he would tap with his knuckles on the prostration +platform. Years later, after his lama passed away, he was taking a meditation +retreat in a cave, during which he recalled his lama's great kindness over +years of training him, and he wept and wept. He almost fainted, but then +experienced the clear light, which he continuously practiced. Subsequently, +after successful meditations he occasionally would remember past lives in +vivid reflections before him. + --His Holiness the Dalai Lama, "How to Practice: The Way to a Meaningful + Life", translated and edited by Jeffrey Hopkins, + http://www.snowlionpub.com/search.php?isbn=HOPRPA +~ + A practitioner needs faith, or trust.... Guru Rinpoche said that we +should meditate in the same way that a sparrow enters a nest. A sparrow +spends some time investigating whether or not it is safe to enter. Once his +examination is over, he then enters unhesitatingly. That's a wonderful +metaphor for practice. First clear up all your doubts about your technique, +then throw yourself into the technique with no separation or self- +consciousness. Of course, it's easy to say, but that is the direction toward +which we should be moving. + Another necessary quality is determination. It's easy to gear oneself up +for counting mantras or prostrations. For some, physical discipline is also +easy. But the determination of the meditator is different. We must be +determined to strive to purify our obscurations until they're completely +gone--in other words, until our buddha-nature unobstructedly shines through. +When we sit, we decide to do our best not to be swayed by our negativity. We +should cultivate this attitude at the beginning of our session. Otherwise, no +matter how much we practice, we will daydream a lot and our meditation will +always be wishy-washy. I know this from experience--I may do my session of +meditation, but it is tepid. Why? I don't have that inner strength to remain +unmoved by the arising of the various mental contents. + -- Bruce Newman, "A Beginner's Guide to Tibetan Buddhism", published by + Snow Lion Publications +~ +This is the way of peace: + "Overcome evil with good, + falsehood with truth, and + hatred with love." + -- Peace Pilgrim +~ +The seeing of Truth cannot be dualistic (a "thing" seen). It cannot be seen +by a see-er, or via a see-er. There can only be a seeing which itself is +Truth. "All Else is Bondage; Non-Volitional Living" + -- Wei Wu Wei +~ +An economist is a surgeon with an excellent scalpel and a rough-edged lancet, +who operates beautifully on the dead and tortures the living. + -- Nicholas Chamfort +~ +A joke's a very serious thing. + -- Charles Churchill +~ +You have to know how to accept rejection and reject acceptance. + -- Ray Bradbury +~ + The five subtler aggregates* will eventually be transformed into the +Buddhas of the five lineages. They are now as if accompanied by mental +defilements. When the defilements are removed, these factors do not become +any coarser or subtler; their nature remains, but [when they] become separated +from the faults of mental pollution, they become the Buddhas of the five +lineages. So if you ask whether the Buddhas of the five lineages are present +now in our continuums, these factors are currently bound by faults, and since +there cannot be a Buddha who has a fault, they are not Buddhas. One is not +yet fully enlightened, but that which is going to become a Buddha is present; +therefore, these factors presently existent in our continuums are Buddha seeds +and are called the Buddha nature, or the essence of the One Gone Thus +(Tathagatagarbha). + * The five consituents that are included within a person's continuum--earth, + water, fire, wind, and space--that will be purified into the five Buddha + lineages [the exalted manifestations of these constituents]. + -- The Fourteenth Dalai Lama, His Holiness Tenzin Gyatso, "Kindness, + Clarity, and Insight", translated and edited by Jeffrey Hopkins, + co-edited by Elizabeth Napper, published by Snow Lion Publications +~ + ...an inherently existent "I" appears to us, but instead of assenting to +that appearance and holding it to be true, we analyze how the "I" actually +exists. + At those times in our life when there's a very solid feeling of "I," it's +helpful to examine how that "I" appears. I remember the first time I stayed +out all night in college and my mother didn't know. I came home the next day +with this feeling that "I" really existed: "I did this and my mother doesn't +know!" The feeling of "I" was just enormous, incredibly solid, because I did +something I wasn't supposed to do. + Examine how that "I" appears, that big "I," especially when you have a +strong emotion. Get familiar with that sense of "I." When somebody criticizes +us or accuses us of doing something that we didn't do, this feeling comes up +very quickly. Usually, we're focused not on the feeling of "I," but on +attacking the other person or escaping from him. But if we can step back, +it's an incredible opportunity to study the feeling of "I." The person who +irritates us the most can be our best Dharma asset, because he gives us an +opportunity to look at this sense of "I." + -- Bhikshuni Thubten Chodron, "Cultivating a Compassionate Heart: The Yoga + Method of Chenrezig", foreword by H.H. the Dalai Lama, published by + Snow Lion Publications +~ +One time when I was giving an exposition on Nagarjuna's "Fundamentals of the +Middle Way," which deals explicitly with the topic of emptiness, one student +who did not have a prior background of learning in great treatises made a +comment to another colleague. He said: 'Today's teaching was a little +strange. His Holiness began with the presentation of the Buddha's path and +built up the edifice one layer at a time. Then, all of a sudden, he started +talking about emptiness and the absence of inherent existence, so that this +whole edifice he had spent much time building was completely dismantled.' +He couldn't really see the point. There is that danger. However, if we +understand the importance of the need to generate wisdom into emptiness as a +means of bringing about the cessation of the afflictions, particularly +fundamental ignorance, then we recognise the value of deepening our +realisation of emptiness. Also, as Dharmakirti points out, emotions such as +loving kindness and compassion cannot directly challenge fundamental +ignorance. It is only by cultivating insight into no-self that we can +directly overcome our fundamental ignorance. + -- H.H. the Dalai Lama, "Lighting the Way", translated by Geshe Thupten + Jinpa, published by Snow Lion Publications +~ +Having committed yourself to certain practices, be steadfast and never +transgress the promises you have made. Let go of everything that could tempt +you to do so and devote yourself entirely and single-mindedly to the +accomplishment of your aims. For six years the Buddha did not waver from his +practice of the meditative stabilization known as "Pervading Space." This +meditation focuses on the fundamental nature of phenomena, which is present +wherever there is space. Everywhere throughout space there are suffering +living beings on whom this meditation also focuses with the compassionate wish +to relieve their suffering and the loving wish to give them happiness. Thus +it combines essential wisdom and skillful means. + -- Geshe Sonam Rinchen, "The Three Principal Aspects of the Path: An Oral + Teaching", translated and edited by Ruth Sonam, published by Snow Lion + Publications +~ +If you're not scared or angry at the thought of a human brain being controlled +remotely, then it could be this prototype of mine is finally starting to work. + -- John Alejandro King +~ +Only fools are positive. -- Moe Howard +~ +Just once, I wish we would encounter an alien menace +that wasn't immune to bullets. + -- Unknown +~ +We are formed and molded by our thoughts. Those whose minds are shaped by +selfless thoughts give joy when they speak or act. Joy follows them like a +shadow that never leaves them. + -- Buddha +~ +If you find a good companion, who is following the same spiritual +path, travel together, overcoming obstacles as they arise. + -- Buddha +~ +Man is harder than rock and more fragile than an egg. + -- Yugoslav Proverb +~ +That in man which cannot be domesticated is not his evil but his goodness. + -- Antonio Porchia, Voces, 1943, translated from Spanish by W.S. Merwin +~ +Man is the only creature that refuses to be what he is. + -- Albert Camus +~ +A human being: an ingenious assembly of portable plumbing. + -- Christopher Morley, Human Being +~ +The universe may have a purpose, but nothing we know suggests that, if so, +this purpose has any similarity to ours. + -- Bertrand Russell +~ +Ocean: A body of water occupying two-thirds of a +world made for man--who has no gills. + -- Ambrose Bierce +~ +Man is harder than iron, stronger than stone and more fragile than a rose. + -- Turkish Proverb +~ +Man is the only kind of varmint sets his own trap, baits it, then steps in it. + -- John Steinbeck, Sweet Thursday +~ +In nature a repulsive caterpillar turns into a lovely butterfly. But with +humans it is the other way around: a lovely butterfly turns into a repulsive +caterpillar. + -- Anton Chekhov +~ +Man is an intelligence in servitude to his organs. + -- Aldous Huxley +~ +We are perverse creatures and never satisfied. + -- Nan Fairbrother +~ +Modern man is the missing link between apes and human beings. + -- Author Unknown +~ +Human consciousness arose but a minute before midnight on the geological +clock. Yet we mayflies try to bend an ancient world to our purposes, ignorant +perhaps of the messages buried in its long history. Let us hope that we are +still in the early morning of our April day. + -- Stephen Jay Gould, "Our Allotted Lifetimes," The Panda's Thumb, 1980 +~ +Such is the human race, often it seems a pity +that Noah... didn't miss the boat. + -- Mark Twain +~ +There are too many people, and too few human beings. + -- Robert Zend +~ +It would indeed be a tragedy if the history of the human race proved to be +nothing more than the story of an ape playing with a box of matches on a +petrol dump. + -- David Ormsby Gore +~ +Only on paper has humanity yet achieved glory, beauty, truth, knowledge, +virtue, and abiding love. + -- George Bernard Shaw +~ +The disastrous history of our species indicates the futility of all attempts +at a diagnosis which do not take into account the possibility that homo +sapiens is a victim of one of evolution's countless mistakes. + -- Arthur Koestler, Janus: A Summing Up +~ +Men! The only animal in the world to fear. + -- D.H. Lawrence +~ +The chief obstacle to the progress of the human race is the human race. + -- Don Marquis +~ +Man embraces in his makeup all the natural orders; +he's a squid, a mollusk, a sucker and a buzzard; +sometimes he's a cerebrate. + -- Martin H. Fischer +~ +Men are cruel, but Man is kind. + -- Rabindranath Tagore, Stray Birds, 1916 +~ +Humanity is on the march, earth itself is left behind. + -- David Ehrenfeld, The Arrogance of Humanism, 1978 +~ +Human nature, if healthy, demands excitement; and if it does not obtain its +thrilling excitement in the right way, it will seek it in the wrong. God +never makes bloodless stoics; He makes no passionless saints. + -- Oswald Chambers +~ +Cabbage: a familiar kitchen-garden vegetable about as +large and wise as a man's head. + -- Ambrose Bierce, The Devil's Dictionary +~ +Monkeys are superior to men in this: +When a monkey looks into a mirror, he sees a monkey. + -- Malcolm de Chazal +~ +It is human nature to stand in the middle of a thing. + -- Mariane Moore, "A Grave," Collected Poems, 1951 +~ +The human race is a race of cowards; and I am not only marching in that +procession but carrying a banner. + -- Mark Twain, "Reflections on Being the Delight of God." +~ +Adam ate the apple, and our teeth still ache. + -- Hungarian Proverb +~ +Why was man created on the last day? So that he can be told, when pride +possesses him: God created the gnat before thee. + -- The Talmud +~ +Man--a creature made at the end of the week's work when God was tired. + -- Mark Twain +~ +I sometimes think that God in creating man somewhat overestimated His ability. + -- Oscar Wilde +~ +O poor mortals, how ye make this earth bitter for each other. + -- Thomas Carlyle, The French Revolution, vol. I, book II, chapter 1 +~ +God pulled an all-nighter on the sixth day. + -- Author Unknown +~ +Zoo: An excellent place to study the habits of human beings. + -- Evan Esar +~ +The progress of evolution from President Washington to +President Grant [is] alone enough to upset Darwin. + -- Henry Adams, Education, 1907 +~ +Man--a being in search of meaning. -- Plato +~ +Ultimately, aren't we all just talking monkeys with an attitude problem? + -- "Uncle" Ben, as seen on quotes-r-us.org +~ +The more humanity advances, the more it is degraded. + -- Gustave Flaubert +~ +Nothing feebler does earth nurture than man, +Of all things breathing and moving. + -- Homer, Odyssey +~ +Everyone is as God made him, and often a good deal worse. + -- Miguel de Cervantes +~ +Man is a strange animal, he doesn't like to read the handwriting +on the wall until his back is up against it. + -- Adlai Stevenson +~ +It is easier to denature plutonium than to denature the evil spirit of man. + -- Albert Einstein +~ +God doesn't measure His bounty, but oh how we do! + -- Mignon McLaughlin, The Second Neurotic's Notebook, 1966 +~ +The belief in a supernatural source of evil is not necessary; +men alone are quite capable of every wickedness. + -- Joseph Conrad, Under Western Eyes, 1911 +~ +The human race is governed by its imagination. + -- Napoleon +~ +Man uses his intelligence less in the care of his own species than he does in +his care of anything else he owns or governs. + -- Abraham Meyerson +~ +Human beings cling to their delicious tyrannies and to their exquisite +nonsense, till death stares them in the face. + -- Sydney Smith +~ +Why should man expect his prayer for mercy to be heard by What is above him +when he shows no mercy to what is under him? + -- Pierre Troubetzkoy +~ +The small percentage of dogs that bite people is monumental proof that the dog +is the most benign, forgiving creature on earth. + -- W.R. Koehler, The Koehler Method of Dog Training +~ +Man was created a little lower than the angels, +and has been getting lower ever since. + -- Josh Billings +~ +We have no choice but to be guilty. +God is unthinkable if we are innocent. + -- Archibald MacLeish, JB, 1958 +~ +Human beings invent just as many ways to sabotage their lives +as to improve them. + -- Mark Goulston, Get Out of Your Own Way: Overcoming Self-Defeating + Behavior, 1996 +~ +As I know more of mankind I expect less of them, and am ready now to call a +man a good man upon easier terms than I was formerly. + -- Samuel Johnson +~ +What is man's greatest bane? His brother man alone. + -- Bias of Priene, Maxims +~ +Acedia is not in every dictionary; just in every heart. + -- Mignon McLaughlin, The Second Neurotic's Notebook, 1966 +~ +The study of crime begins with the knowledge of oneself. + -- Henry Miller, The Air-Conditioned Nightmare, 1945 +~ +Is man a savage at heart, skinned o'er with fragile Manners? Or is savagery +but a faint taint in the natural man's gentility, which erupts now and again +like pimples on an angel's arse? + -- John Barth, The Sot-Weed Factor, 1960 +~ +God has given a great deal to man, but man would like something from man. + -- Antonio Porchia, Voces, 1943, translated from Spanish by W.S. Merwin +~ +I was surprised just now at seeing a cobweb around a knocker; +for it was not on the door of heaven. + -- Augustus William Hare and Julius Charles Hare, Guesses at Truth, + by Two Brothers, 1827 +~ +I sometimes think of what future historians will say of us. +A single sentence will suffice for modern man: +He fornicated and read the papers. + -- Albert Camus +~ +Man, when he is merely what he seems to be, is almost nothing. + -- Antonio Porchia, Voces, 1943, translated from Spanish by W.S. Merwin +~ +Give a man secure possession of a bleak rock, and he will turn it +into a garden; give him nine years' lease of a garden, and he will +convert it into a desert. + -- Arthur Young, Travels in France, 1792 +~ +Our behavior is human with a sliver of animal, +our souls animal with a sliver of human. + -- Carrie Latet +~ +Occident: The part of the world lying west (or east) of the Orient. +It is largely inhabited by Christians, a powerful subtribe of the +Hypocrites, whose principal industries are murder and cheating, which +they are pleased to call "war" and "commerce." These, also, are the +principal industries of the Orient. + -- Ambrose Bierce, The Devil's Dictionary +~ +Man is the only animal that laughs and weeps; for he is the only animal +that is struck with the difference between what things are and what +they ought to be. + -- William Hazlitt, The English Comic Writers, 1819 +~ +Nature is neutral. Man has wrested from nature the power to make the world a +desert or to make the deserts bloom. There is no evil in the atom; only in +men's souls. + -- Adlai Stevenson +~ +A simple and irrefutable argument to knock creationism on its ass: (1) Humans +are a mistake -subproof: opposable thumbs and enlarged brain capacity are the +combined number one factor in the increasingly speedy destruction of planet +Earth. (2) God doesn't make mistakes. (3) Therefore, God couldn't have +created people. + -- Cassus Garrulitas +~ +Man talks about everything, and he talks about everything as though the +understanding of everything were all inside him. + -- Antonio Porchia, Voces, 1943, translated from Spanish by W.S. Merwin +~ +My dog is usually pleased with what I do, because she is not infected +with the concept of what I "should" be doing. + -- Lonzo Idolswine +~ +Man will do many things to get himself loved; +he will do all things to get himself envied. + -- Mark Twain, Following the Equator, 1897 +~ +We are all parasites; we humans, the greatest. + -- Martin H. Fischer +~ +Suppose some mathematical creature from the moon were to reckon up the human +body; he would at once see that the essential thing about it was that it was +duplicate. A man is two men, he on the right exactly resembling him on the +left. Having noted that there was an arm on the right and one on the left, a +leg on the right and one on the left, he might go further and still find on +each side the same number of fingers, the same number of toes, twin eyes, twin +ears, twin nostrils, and even twin lobes of the brain. At last he would take +it as a law; and then, where he found a heart on one side, would deduce that +there was another heart on the other. And just then, where he most felt he +was right, he would be wrong. + -- Gilbert Keith Chesterton, "The Paradoxes of Christianity," Orthodoxy +~ +It is the nature of mortals to kick a fallen man. + -- Aeschylus, Agamemnon +~ +God is less careful than General Motors, for He floods the world +with factory rejects. + -- Mignon McLaughlin, The Neurotic's Notebook, 1960 +~ +Man's highest merit always is, as much as possible, to rule external +circumstances and as little as possible to let himself be ruled by them. + -- Johann Wolfgang von Goethe +~ +So there he is at last. Man on the moon. The poor magnificent bungler! He +can't even get to the office without undergoing the agonies of the damned, but +give him a little metal, a few chemicals, some wire and twenty or thirty +billion dollars and vroom! there he is, up on a rock a quarter of a million +miles up in the sky. + -- Russell Baker, New York Times, 21 July 1969 +~ +Man is the only animal for whom his own existence is +a problem which he has to solve. + -- Erich Fromm, Man for Himself, 1947 +~ +When freedom from want and freedom from fear are achieved, +man's remains will be in rigor mortis. + -- Martin H. Fischer +~ +Man is nature's sole mistake. + -- W.S. Gilbert +~ +The average man's judgment is so poor, he runs a risk every time he uses it. + -- E.W. Howe +~ +Man -a reasoning rather than a reasonable animal. + -- Alexander Hamilton +~ +We must, however, acknowledge, as it seems to me, that man with all +his noble qualities, still bears in his bodily frame the indelible +stamp of his lowly origin. + -- Charles Darwin, Descent of Man, 1871 +~ +We're animals. We're born like every other mammal and we live our +whole lives around disguised animal thoughts. + -- Barbara Kingsolver, Animal Dreams +~ +Nature is trying very hard to make us succeed, but nature does not +depend on us. We are not the only experiment. + -- R. Buckminister Fuller +~ +Be a good animal, true to your animal instincts. + -- David Herbert Lawrence, White Peacock, 1911 +~ +The question is this: Is man an ape or an angel? +I am on the side of the angels. + -- Benjamin Disraeli +~ +I viewed my fellow man not as a fallen angel, but as a risen ape. + -- Desmond Morris, The Naked Ape +~ +Man desired concord; but nature knows better what is good for his species; she +desires discord. Man wants to live easy and content; but nature compels him +to leave ease... and throw himself into roils and labors. + -- Immanuel Kant, Idea for a Universal History with a Cosmopolitan + Purpose, 1787 +~ +The thief and the murderer follow nature just as much as the philanthropist. + -- T.H. Huxley, "Evolution and Ethics," 1893 +~ +Many people believe that they are attracted by God, or by Nature, +when they are only repelled by man. + -- William Ralph Inge +~ +People are like birds: on the wing, all beautiful; +up close, all beady little eyes. + -- Mignon McLaughlin, The Second Neurotic's Notebook, 1966 +~ +Evolution is individual -devolution is collective. + -- Martin H. Fischer +~ +I think I could turn and live with animals, they are so placid and self- +contain'd. I stand and look at them long and long. They do not sweat and +whine about their condition.... Not one is dissatisfied, not one is demented +with the mania of owning things, not one kneels to another, nor to his kind +that lived thousands of years ago, not one is respectable or unhappy over the +whole earth. + -- Walt Whitman, Leaves of Grass +~ +As long as people believe in absurdities +they will continue to commit atrocities. + -- Voltaire +~ +I demand of you, and of the whole world, that you show me +a generic character... by which to distinguish between Man and Ape. +I myself most assuredly know of none. + -- Carl Linnaeus, 1788 +~ +In creating the human brain, evolution has wildly overshot the mark. + -- Arthur Koestler +~ +Evolution: that last step was a doozy! + -- Astrid Alauda +~ +We have a world for each one, but we do not have a world for all. + -- Antonio Porchia, Voces, 1943, translated from Spanish by W.S. Merwin +~ +Evolution: one small step for man, one giant leap backward for mankind. + -- The Quote Garden +~ +I do not value any view of the universe into which man and the institutions of +man enter very largely and absorb much of the attention. Man is but the place +where I stand, and the prospect hence is infinite. + -- Henry David Thoreau, journal, 2 April 1852 +~ +Nature does not deceive us; it is we who deceive ourselves. + -- Jean-Jacques Rousseau, Emile, 1762 +~ +It is not titles that honor men, but men that honor titles. + -- Niccolo Machiavelli +~ +Nobody knows the age of the human race, but everybody agrees +that it is old enough to know better. + -- Author Unknown +~ +Question: How can one work with deep fears most effectively? + +DL: There are quite a number of methods. The first is to think about actions +and their effects. Usually when something bad happens, we say, "Oh, very +unlucky," and when something good happens, we say, "Oh, very lucky." Actually, +these two words, lucky and unlucky, are insufficient. There must be some +reason. Because of a reason, a certain time became lucky or unlucky, but +usually we do not go beyond lucky or unlucky. The reason, according to the +Buddhist explanation, is our past karma, our actions. + +One way to work with deep fears is to think that the fear comes as a result of +your own actions in the past. Further, if you have fear of some pain or +suffering, you should examine whether there is anything you can do about it. +If you can, there is no need to worry about it; if you cannot do anything, +then there is also no need to worry. + +Another technique is to investigate who is becoming afraid. Examine the +nature of your self. Where is this I? Who is I? What is the nature of I? +Is there an I besides my physical body and my consciousness? This may help. + +Also, someone who is engaging in the Bodhisattva practices seeks to take +others' suffering onto himself or herself. When you have fear, you can think, +"Others have fear similar to this; may I take to myself all of their fears." +Even though you are opening yourself to greater suffering, taking greater +suffering to yourself, your fear lessens. + -- "The Dalai Lama, A Policy of Kindness: An Anthology of Writings By and + About the Dalai Lama", compiled and edited by Sidney Piburn, Foreword + by Sen. Claiborne Pell, published by Snow Lion Publications +~ +Object of Negation + + When a subject is analyzed, the object to be negated is determined to be +either an appearance or something imagined. It is not logical, [however,] to +negate momentary appearances, because reasonings cannot negate them. To take +an example: for people with eye diseases, the appearances of floaters [bits of +optical debris], double moons, and the like do not stop as long as their +eyesight is impaired. Similarly, as long as beings are not free from +unafflicted ignorance, illusionlike appearances [manifesting] to the six modes +of consciousness do not stop. + It is not necessary to negate [appearances], because our mistakes do not +come from appearances: they arise from fixating on those [appearances]. This +is the case because if we do not fixate on appearances, we are not bound--we +are like a magician who, having conjured up a young woman, has no attachment +towards her. [On the other hand, if,] like naive beings attached to an +illusory young woman, we fixate intensely [on appearances], our karma and +mental afflictions will increase. + To intentionally negate appearances would be wrong because, if they were +negated, emptiness would come to mean the [absolute] nonexistence of things. +Another reason this would be a mistake is that yogins and yoginis meditating +on emptiness would fall into the extreme of nihilism since they would be +applying their minds to a negation that [equals] the [absolute] nonexistence +of everything. + Thus, [Madhyamikas] set out to negate only what is imagined, because that +is what can be negated. Like a rope mistaken for a snake, what is imagined +does not conform to facts: it is simply the mind's fixations. + -- Kongtrul Lodro Taye, "The Treasury of Knowledge, Book Six, Part Three: + Frameworks of Buddhist Philosophy", translated by Elizabeth M. Callahan, + published by Snow Lion Publications +~ +Even though Mac Users may be only 10% of the market, +always remember that we are the top 10%. + -- Douglas Adams +~ +While I don't claim to be a great programmer, I try to imitate one. An +important trait of the great ones is constructive laziness. They know that +you get an A not for effort but for results. + -- Eric S. Raymond +~ +We've heard that a million monkeys at a million keyboards could produce the +complete works of Shakespeare; now, thanks to the Internet, we know this is +not true. + -- Robert Wilensky +~ +C makes it easy to shoot yourself in the foot; C++ makes it harder, but when +you do, it blows away your whole leg. + -- Bjarne Stroustrup +~ +The question of whether computers can think is like the question of whether +submarines can swim. + -- Edsger Dijkstra +~ +Consistently separating words by spaces became a general custom about the +tenth century A.D., and lasted until about 1957, when FORTRAN abandoned the +practice. + -- Sun FORTRAN Reference Manual +~ +UNIX was never designed to keep people from doing stupid things, because that +policy would also keep them from doing clever things. + -- Doug Gwyn +~ +Anyone who slaps a "this page is best viewed with Browser X" label on a Web +page appears to be yearning for the bad old days, before the Web, when you had +very little chance of reading a document written on another computer, another +word processor, or another network. + -- Tim Berners-Lee +~ +If you were plowing a field, which would you rather use? +Two strong oxen or 1024 chickens? + -- Seymour Cray +~ +The competent programmer is fully aware of the strictly limited size of his +own skull; therefore he approaches the programming task in full humility; and +among other things he avoids clever tricks like the plague. + -- Edsger Dijkstra +~ +A programming language is a tool that has profound influence +on our thinking habits. + -- Edsger Dijkstra +~ +The Answer to the Great Question of Life, the Universe and Everything is +forty-two. + -- Deep Thought, 2nd greatest Computer in the Universe of Time and Space +~ +If Windows is the solution, can we please have the problem back? + -- unknown +~ +This is a fascinating property: Writing texts in programming languages can not +only be as creative as poetry, the creations more than in poetry, belong to +the real world as soon as run through the machine. + -- Heinz Zemanek +~ +Any problem in computer science can be solved with another layer of +indirection. But that usually will create another problem. + -- Davin Wheeler +~ +Simplicity is prerequisite for reliability. -- Edsger Dijkstra +~ +It is practically impossible to teach good programming to students that have +had a prior exposure to BASIC: as potential programmers they are mentally +mutilated beyond hope of regeneration. + -- Edsger Dijkstra +~ +Good artists copy. Great artists steal. -- Pablo Picasso +~ +I am always doing that which I cannot do, in order I may learn how to do it. + -- Pablo Picasso +~ +Serious people have few ideas. People with ideas are never serious. + -- Paul Valery +~ +Anyone who has never made a mistake has never tried anything new. + -- Albert Einstein +~ +A thing is not necessarily true because a man dies for it. + -- Oscar Wilde +~ +The debt is like a crazy aunt we keep down in the basement. All the neighbors +know she's down there, but nobody wants to talk about her. + -- Ross Perot +~ +Loyalty to petrified opinion never yet broke a chain or freed a human soul. + -- Mark Twain +~ +Where true religion has prevented one crime, false religions have afforded a +pretext for a thousand. + -- Charles Caleb Colton +~ +Whatever limits us, we call Fate. -- Ralph Waldo Emerson +~ +The surest way to corrupt a youth is to instruct him to hold in higher regard +those who think alike than those who think differently. + -- Friedrich Nietzsche +~ +Dream no small dreams for they have no power to move the hearts of men. + -- Johann Wolfgang von Goethe +~ +Men show their characters in nothing more clearly than in what they think +laughable. + -- Johann Wolfgang von Goethe +~ +It often takes more courage to change ones opinion than to keep it. + -- Willy Brandt +~ +Let's be grateful for those who give us happiness; they are the charming +gardeners who make our soul bloom. + -- Marcel Proust +~ +It is better to have a permanent income than to be fascinating. + -- Oscar Wilde +~ +There are only two forces that unite men -fear and interest. + -- Napoleon Bonaparte +~ +If there were in the world today any large number of people who desired their +own happiness more than they desired the unhappiness of others, we could have +a paradise in a few years. + -- Bertrand Russel +~ +The difference between fiction and reality is that fiction has to make sense. + -- Tom Clancy +~ +If people are good only because they fear punishment, and hope for reward, +then we are a sorry lot indeed. + -- Albert Einstein +~ +Your character must be above suspicion and +you must be truthful and self controlled. + -- Mahatma Gandhi +~ +Either you live or you are consequential. -- Erich Kaestner +~ +Pedaled curd gets wide -not strong. -- Johann Wolfgang von Goethe +~ +Do not follow where the path may lead. +Go instead where there is no path and leave a trail. + -- Ralph Waldo Emerson +~ +She wore too much rouge last night and not quite enough clothes. Thats always +a sign of despair in a woman. + -- Oscar Wilde +~ +Live as if you were to die tomorrow. Learn as if you were to live forever. + -- Mahatma Gandhi +~ +First they ignore you, then they laugh at you, then they fight you, then you +win. + -- Mahatma Gandhi +~ +Wherever it has been established that it is shameful to be involved with +sexual relationships with men, that is due to evil on the part of the rulers, +and to cowardice on the part of the governed. + -- Plato +~ +We want to be poets of our life--first of all in the smallest +most everyday matters. + -- Friedrich Nietzsche +~ +Without music, life would be an error. -- Friedrich Nietzsche +~ +Joseph LaGrange believed that a mathematician has not thoroughly understood +his own work till he has made it so clear that he can go out and explain it +effectively to the first man he meets on the street. + -- E. T. Bell +~ +There is no such thing as an inevitable war. If war comes it will be from +failure of human wisdom. + -- Andres Bonar Law +~ +Perfect nonviolence is the highest bravery. -- Mahatma Gandhi +~ +There never was a good war or a bad peace. -- Benjamin Franklin +~ +There's no honorable way to kill, no gentle way to destroy. There is nothing +good in war. Except its ending. + -- Abraham Lincoln +~ +I object to violence because when it appears to do good, the good is only +temporary; the evil it does is permanent. + -- Mahatma Gandhi +~ +An eye for an eye makes the whole world blind. -- Mahatma Gandhi +~ +Trust no one in whom the desire to punish is strong. -- Fyodor Dostoevsky +~ +If we steal thoughts from the moderns, it will be cried down as plagiarism; if +from the ancients, it will be cried up as erudition. + -- Charles Caleb Colton +~ +The artist is nothing without the gift, but the gift is nothing without work. + -- Emile Zola +~ +Ability is nothing without opportunity. -- Napoleon Bonaparte +~ +Never work just for money or for power. +They won't save your soul or help you sleep at night. + -- Marian Wright Edelman +~ +Who is the happiest of men? He who values the merits of others, and in their +pleasure takes joy, even as though it were his own. + -- Johann Wolfgang von Goethe +~ +Whatever you can do, or dream you can, begin it. Boldness has genius, power, +and magic in it. + -- Johann Wolfgang von Goethe +~ +Censorship cannot eliminate evil, it can only kill freedom. -- unknown +~ +Freedom is the right to be wrong, not the right to do wrong. + -- John G. Riefenbaker +~ +People demand freedom of speech to make up for +the freedom of thought which they avoid. + -- Soren Aabye Kierkegaard +~ +Expose yourself to your deepest fear; after that, fear has no power, and the +fear of freedom shrinks and vanishes. You are free. + -- Jim Morrison +~ +If you are a phenomenologist, you can talk about this cocktail and make +philosophy out of it. + -- Raymond Aron +~ +I should only believe in a God that would know how to dance. + -- Friedrich Nietzsche +~ +I would not know what the spirit of a philosopher might wish more +to be than a good dancer. + -- Friedrich Nietzsche +~ +Faith means not wanting to know what is true. -- Friedrich Nietzsche +~ +True love is like ghosts, which everybody talks about and few have seen. + -- Francois, Duc de la Rochefoucauld +~ +This war, like the next war, is a war to end war. -- David Lloyd George +~ +How good bad music and bad reasons sound when one marches against an enemy! + -- Friedrich Nietzsche +~ +When I was a kid my parents moved a lot, but I always found them. + -- Rodney Dangerfield +~ + When your mind is trained in self-discipline, even if you are surrounded +by hostile forces, your peace of mind will hardly be disturbed. On the other +hand, if your mind is undisciplined, your mental peace and calm can easily be +disrupted by your own negative thoughts and emotions. The real enemy is +within, not outside. Usually we define our enemy as a person, an external +agent, whom we believe is causing harm to us or to someone we hold dear. But +such an enemy is dependent on many conditions and is impermanent. One moment, +the person may act as an enemy; at yet another moment, he or she may become +your best friend. This is a truth that we often experience in our own lives. +But negative thoughts and emotions, the inner enemy, will always remain the +enemy. They are your enemy today, they have been your enemy in the past, and +they will remain your enemy in the future as long as they reside within your +mind. + This inner enemy is extremely dangerous. The destructive potential of an +external enemy is limited when compared to that of its inner counterpart.... +In a time when every country is a potential target for the nuclear weapons of +others, human beings still continue to develop defense systems of greater and +greater sophistication. I do not know if it will ever be possible to create a +defense system capable of guaranteeing worldwide protection against all +external forces of destruction. However, one thing is certain: as long as +those destructive internal enemies of anger and hatred are left to themselves +unchallenged, the threat of physical annihilation will always loom over us. +In fact, the destructive power of an external enemy ultimately derives from +the power of these internal forces. The inner enemy is the trigger that +unleashes the destructive power of the external enemy. + Shantideva tells us that as long as these inner enemies remain secure +within, there is great danger. Shantideva goes on to say that even if +everyone in the world were to stand up against you as your enemies and harm +you, as long as your own mind was disciplined and calm, they would not be able +to disturb your peace. Yet a single instance of delusion arising in your mind +has the power to disturb that peace and inner stability. + -- Tenzin Gyatso, the Fourteenth Dalai Lama, from "The Compassionate Life" +~ +(Each day before breakfast the founder and abbess of Sravasti Abbey, Thubten +Chodron, gives a morning motivation for residents and guests. We were moved +by these inspiring words, and hope you will be, too.) + +Morning Motivation + + Let's recall our motivation in the morning and think that today, the most +important thing I have to do is to guard my body, speech and mind so that I +don't harm anybody through what I do with my body, through what I say, or even +through what I think. That's the most important thing, more important than +anything else today. + The second most important thing is, as much as possible, to be of benefit +to others. Thoroughly cultivate that as your motivation simply for being +alive today. Our purpose for being alive isn't just to keep this body alive, +to eat and sleep, and have pleasure. We have a higher purpose, a higher +meaning: to really work for the benefit of living beings. If the purpose of +our life is simply to keep the body alive and have pleasure, then at the end +of life, we have nothing to show for it. The body dies and all the pleasures, +like last night's dream, have gone. But if we work for a higher motivation, a +higher purpose, to really do what's beneficial for all living beings, then +there's happiness and benefit now. + At the end of the life, the benefit that we've given to others continues, +as do all the imprints of the attitude of kindness, the attitude of care +towards others. All the imprints of having generated that positive mind go on +with us into the next life. So even at the time of death, that kind heart +brings incredible benefit and carries through into the next life. + And then let's also generate a third motivation--a really long-term +motivation--to become fully enlightened. In other words, to have the wisdom, +compassion, and skill so that in the long term, we'll be able to be of the +greatest benefit to all living beings, even being able to lead them on the +path to enlightenment. That's our really long-term purpose. + As we change and develop a kind heart, that influences every single living +being we encounter in a positive way. Then, through the influence on them, it +spreads out to all the people they know. So, just spending one day with a +positive, long-term motivation may seem like a small thing, but when we think +of the ripple effect it has now, and the benefit it has in future lives and +for progressing along the path to liberation and enlightenment, we see that +even one day spent with this motivation of kindness, directly and indirectly +benefiting sentient beings, has tremendous outcomes--many, many good results. + -- Thubten Chodron, who is the author of many books, including her latest + work, "Guided Meditations on the Stages of the Path", published by + Snow Lion Publications +~ + ...We can't blame one individual for what happens in our world. I think +we should blame our entire society. Society produces our leaders and +politicians, and if we try to develop a more compassionate and affectionate +society, we will have human beings with a more peaceful nature. Leaders, +politicians, and businesspeople coming from such a society would offer hope +for a better world. Our long-term responsibility--everyone's responsibility, +whether they are believers or nonbelievers--is to find ways to promote a +peaceful and compassionate society. + I think one way is quite simple. Each individual must try to ensure peace +and compassion in his [or her] family. Put together ten peaceful, +compassionate homes, or one hundred, and that's a community. The children in +such a society would receive affection in their family and in their schools +from the educators concerned. We might have one or two setbacks, but +generally I think we could develop a sensible society. Sensible here means a +sense of community, a sense of responsibility, and a sense of commitment. + -- His Holiness the Dalai Lama, "Many Ways to Nirvana: Reflections + and Advice on Right Living", edited by Renuka Singh, published by + Snow Lion Publications +~ + We can see that there are many ways in which we actively contribute to our +own experience of mental unrest and suffering. Although, in general, mental +and emotional afflictions themselves can come naturally, often it is our own +reinforcement of those negative emotions that makes them so much worse. For +instance when we have anger or hatred towards a person, there is less +likelihood of its developing to a very intense degree if we leave it +unattended. However, if we think about the projected injustices done to us, +the ways in which we have been unfairly treated, and we keep on thinking about +them over and over, then that feeds the hatred. It makes the hatred very +powerful and intense. Of course, the same can apply to when we have an +attachment towards a particular person; we can feed that by thinking about how +beautiful he or she is, and as we keep thinking about the projected qualities +that we see in the person, the attachment becomes more and more intense. But +this shows how through constant familiarity and thinking, we ourselves can +make our emotions more intense and powerful. + We also often add to our pain and suffering by being overly sensitive, +overreacting to minor things, and sometimes taking things too personally. We +tend to take small things too seriously and blow them up out of proportion, +while at the same time we often remain indifferent to the really important +things, those things which have profound effects on our lives and long-term +consequences and implications. + So I think that to a large extent, whether you suffer depends on how you +respond to a given situation. + -- His Holiness the Dalai Lama and Howard C. Cutler, M.D., "The Art of + Happiness: A Handbook for Living" +~ + Realizations come only if we practice joyfully, with confidence and +courage. Realization doesn't grow within a timid or weak state of mind--it +blossoms in the mind free of doubt and hesitation. Realization is fearless. +When we see the true nature of reality, there's nothing hidden, nothing left +to fear. At last we're seeing reality as it is, full of joy and peace. + Our habitual patterns can only be removed by understanding the great +emptiness aspect of true nature, that which is named the Mother of all the +buddhas. Emptiness is freedom; emptiness is great opportunity. It is +pervasive and all phenomena arise from it. As the great master Jigme Lingpa +said, "The entire universe is the mandala of the dakini." The Mother's mandala +is all phenomena, the display of the wisdom dakini. + Without this ultimate great emptiness, the Mother of the buddhas, the +universe would be without movement, development, or change. Because of this +great emptiness state of the Mother, we see phenomena continually arising. +Each display arises, transforms, and radiates, fulfilling its purpose and then +dissolving back into its original state. This dramatic dance of energy is the +activity, ability, or mandala of the wisdom dakini. Thus, the combination of +the great emptiness or openness state, together with the activities of love +and compassion, is both the ultimate Mother and the ultimate wisdom dakini. + -- Khenchen Palden Sherab Rinpoche and Khenpo Tsewang Dongyal Rinpoche, + "Tara's Enlightened Activity: An Oral Commentary on 'The Twenty-one + Praises to Tara' ", published by Snow Lion Publications +~ +Just as your innermost wish is to be free from suffering and to abide in +happiness, so too is it the aspiration of all other beings. But, they, like +you, encounter sufferings and problems in their lives, and often their +difficulties are much worse than your own. Examine your capacity to help +them. At this time your ability to help them is quite limited, but if you +reduce your own ignorance, anger, attachment, and other faults, and increase +your good qualities such as generosity, patience, loving-kindness, compassion, +and wisdom, you will be of greater benefit. If you become fully enlightened, +you will be of the greatest possible benefit to all beings. Thus generate the +altruistic intention to become a Buddha in order to benefit all sentient +beings most effectively. + -- Thubten Chodron, "Guided Meditations on the Stages of the Path", + foreword by H.H. the Dalai Lama, published by Snow Lion Publications +~ + ...at Bodh Gaya, [Shakyamuni] displayed the ways of becoming fully +enlightened. Then in stages he turned the three renowned wheels of doctrine. + In the first period, at Varanasi, Buddha turned the wheel of doctrine that +is based on the four noble truths; he did this mainly in consideration of +those having the lineage of Hearers (Sravaka). In the middle period, at +Grdhrakuta, he set forth the middle wheel of doctrine, which is based on the +mode of non-inherent existence of all phenomena; he did this mainly in +consideration of trainees of sharp faculties who bear the Mahayana lineage. +In the final period, at Vaisali, he set forth the final wheel [which is based +on discriminating between those phenomena that do and those that do not truly +exist]; he did this mainly in consideration of trainees of middling and lower +faculties who bear the Mahayana lineage. The teacher Buddha also appeared in +the body of Vajradhara, setting forth tantric doctrines. + -- H.H. the Dalai Lama, "The Buddhism of Tibet", translated and edited by + Jeffrey Hopkins, published by Snow Lion Publications +~ +There are different levels of faith. First, clear faith refers to the joy and +clarity and change in our perceptions that we experience when we hear about +the qualities of the Three Jewels and the lives of the Buddha and the great +teachers. Longing faith is experienced when we think about the latter and are +filled with a great desire to know more about their qualities and to acquire +these ourselves. Confident faith comes through practicing the Dharma, when we +acquire complete confidence in the truth of the teachings and the +enlightenment of the Buddha. Finally, when faith has become so much a part of +ourselves that even if our lives were at risk we could never give it up, it +has become irreversible faith. + -- Dilgo Khyentse Rinpoche, "The Excellent Path to Enlightenment", + translated by The Padmakara Translation Group, published by Snow + Lion Publications +~ + ...when seeking work, or if you already have a job, it is important to +keep in mind that a human being isn't meant to be some kind of machine +designed only for production. No. Human life isn't just for work, like [a +socialistic] vision where everyone's purpose is just to work for the state, +and there is no individual freedom, where the state even arranges the person's +vacations and everything is planned out for the individual. That is not a +full human life. Individuality is very important for a full human life, and +then accordingly some leisure time, a bit of holiday, and time spent with +family or friends. That is the means to a complete form of life.... If your +life becomes only a medium of production, then many of the good human values +and characteristics will be lost--then you will not, you cannot, become a +complete person. + So if you're looking for work and have a choice of a job, choose a job +that allows the opportunity for some creativity, and for spending time with +your family. Even if it means less pay, personally I think it is better to +choose work that is less demanding, that gives you greater freedom, more time +to be with your family, or to do other activities, read, engage in cultural +activities, or just play. I think that's best. + -- His Holiness the Dalai Lama, and Dr. Howard C. Cutler, M.D., "The + Art of Happiness at Work" +~ + Our painful experiences have brought the five poisons* right into our +world. Our heavy sense of being a separate person has led to an anxiety about +our safety in the world. This leads us to aversion and attachment, as we long +to predict and control our relation with the environment. From this all the +other fixed and defensive positions arise. And so the world that we encounter +is covered over and suffused with many subtle moods of hopes and fears, +doubts, jealousies, pride. So even here on a dharma retreat, as we look +around the room, we have a complex sense of whose faces we can look at, and +who we might have to look away from. This is not at all a neutral place. The +force of projections, interpretations and impulsive reactions keeps us busy in +trying to stay ahead of the game.... + However in dzogchen we are trying to get to the essential point where +nirvana and samsara separate. This is like a great weed killer: If you spray +it once all the weeds, all the confusion, all the pain and suffering will +vanish. You don't need to pluck out each weed by itself. Believing that you +are a bad person is very unhelpful for the practice of dzogchen. Also +believing that you are a good person is not very helpful in the practice of +dzogchen. You are not a person! Resting in the unborn state we are a pure +awareness free of the least defilement. When you give up your ego identity, +your samsara citizenship, you tear up your identity card and all the problems +and sins and police records linked to that identity vanish immediately. + * Five poisons (dug nga)--the five poisonous mental afflictions are + desire, aggression, ignorance, pride, and jealousy. (Penetrating Wisdom) + -- "Being Right Here: A Dzogchen Treasure Text of Nuden Dorje Entitled 'The + Mirror of Clear Meaning' ", with commentary by James Low, published by + Snow Lion Publications +~ +And just as men depend upon +A boat for traversing the sea, +So does the mental body need +The matter-body for occurrence. +And as the boat depends upon +The men for traversing the sea, +So does the matter-body need +The mental body for occurrence. +Depending each upon the other +The boat and men go on the sea. +And so do mind and matter both +Depend the one upon the other. + -- Visuddhimagga (XVIII, 36) +~ + There is a Buddhist practice in which one imagines giving joy and the +source of all joy to other people, thereby removing all their suffering. +Though of course we cannot change their situation, I do feel that in some +cases, through a genuine sense of caring and compassion, through our sharing +in their plight, our attitude can help alleviate their suffering, if only +mentally. However, the main point of this practice is to increase our inner +strength and courage. + I have chosen a few lines that I feel would be acceptable to people of all +faiths, and even to those with no spiritual belief. When reading these lines, +if you are a religious practitioner, you can reflect upon the divine form that +you worship. Then, while reciting these verses, make the commitment to +enhance your spiritual values. If you are not religious, you can reflect upon +the fact that, fundamentally, all beings are equal to you in their wish for +happiness and their desire to overcome suffering. Recognizing this, you make +a pledge to develop a good heart. It is most important that we have a warm +heart. As long as we are part of human society, it is very important to be a +kind, warm-hearted person. + May the poor find wealth, + Those weak with sorrow find joy. + May the forlorn find new hope, + Constant happiness and prosperity. + + May the frightened cease to be afraid, + And those bound be free. + May the weak find power, + And may their hearts join in friendship. + -- H.H. the Dalai Lama, "An Open Heart: Practicing Compassion in Everyday + Life", edited by Nicholas Vreeland, afterword by Khyongla Rato + and Richard Gere +~ + Although there are as many categories of emptiness* as there are types of +phenomena, when you realize the emptiness of one specific phenomenon, you also +realize the emptiness of all phenomena. The ultimate nature, or emptiness, of +all phenomena is of equal taste and of the same undifferentiable nature. Even +though the nature of emptiness of all phenomena is the same, and all the +different aspects of phenomena, such as whether they are good or bad, or the +way they change, arise from the sphere of emptiness, you should understand +that emptiness cannot be found apart from the subject or the object. + Emptiness refers to an object's being free of intrinsic existence. Things +depend on causes and conditions. This very dependence on causes and +conditions signifies that phenomena lack independent, or intrinsic, existence. +It also demonstrates how all the diverse aspects of things that we experience +arise because they are by nature empty. When we talk about emptiness, we are +not dealing with those different aspects, we are dealing with phenomena's +ultimate reality. + -- H.H. the Dalai Lama, "Stages of Meditation", trans. by Ven. Geshe + Lobsang Jordhen, Losang Choephel Ganchenpa, and Jeremy Russell, + published by Snow Lion Publications +~ +The Treasure Discoverers + Most of the influential terma [hidden treasures or teachings] were +purportedly secreted by Padmasambhava or his immediate disciples, and specific +instructions were also laid down for each terma at the time of its +concealment. The theory behind this system is that certain teachings would be +especially effective at particular points in the future, and so they were +hidden in a "time release" system which assured that at the appropriate time a +terton would locate the teaching and disseminate it. When Padmasambhava hid +these treasures, he prophesied the circumstances for the discovery of each +terma and the terton who would find it. He predicted that there would be +three "grand" tertons, eight "great" ones, twenty-one "powerful" ones, one +hundred eight "intermediate," and one thousand "subsidiary" tertons. Most of +these were to be recognized as emanations of Padmasambhava or his chief +disciples. + ...Many hidden treasures still remain undiscovered, awaiting the proper +time for their dissemination. They continue to reinvigorate the Nyingma +tradition, and a number have been incorporated into other lineages. The +institution of terma serves as a link with the past of the tradition, a link +that periodically revitalizes the present and points the way to the future. +The system reflects the Mahayana ideal of skill in means, the ability to adapt +teachings to changing circumstances. + -- John Powers, "Introduction to Tibetan Buddhism", published by Snow Lion + Publications +~ + Practicing compassion will bring about the recognition of emptiness* as +the true nature of the mind. When you practice virtuous actions of love and +compassion on the relative level, you spontaneously realize the profound +nature of emptiness, which is the absolute level. In turn, if you focus your +meditation practice on emptiness, then your loving-kindness and compassion +will spontaneously grow. + These two natures, the absolute and the relative, are not opposites; they +always arise together. They have the same nature; they are inseparable like a +fire and its heat or the sun and its light. Compassion and emptiness are not +like two sides of a coin. Emptiness and compassion are not two separate +elements joined together; they are always coexistent. + In Buddhism, emptiness does not mean the absence of apparent existence. +Emptiness is not like a black hole or darkness, or like an empty house or an +empty bottle. Emptiness is fullness and openness and flexibility. Because of +emptiness it is possible for phenomena to function, for beings to see and +hear, and for things to move and change. It is called emptiness because when +we examine things we cannot find anything that substantially and solidly +exists. There is nothing that has a truly existent nature. Everything we +perceive appears through ever-changing causes and conditions, without an +independent, solid basis. Although from a relative perspective things appear, +they arise from emptiness and they dissolve into emptiness. All appearances +are like water bubbles or the reflection of the moon in water. + -- Khenchen Palden Sherab Rinpoche and Khenpo Tsewang Dongyal Rinpoche, + "Opening to our Primordial Nature", published by Snow Lion Publications +~ +[Preceding story: Before reaching enlightenment, the Buddha was born as Prince +Visvantara, who, despite facing many challenges and adversity, brought all of +his heart and courage to bear against a single enemy--human suffering.] + +In giving we not only find wealth while in cyclic existence but we achieve the +zenith of prosperity in supreme enlightenment. Therefore we all have to +practice giving. A Bodhisattva's giving is not just overcoming miserliness +and being generous to others; a pure wish to give is cultivated, and through +developing more and more intimacy with it, such giving is enhanced infinitely. +Therefore it is essential to have the firm mind of enlightenment rooted in +great love and compassion and, from the depths of one's heart, to either give +one's body, wealth and virtues literally to sentient beings as infinite as +space, or to dedicate one's body, wealth and virtues for them while striving +in all possible ways to enhance the wish to give infinitely. As mentioned in +Engaging in Bodhisattva Activities and in The Precious Garland, we should +literally give material help to the poor and needy, give teaching to others, +and give protection to them, even the small insects, as much as we can. In +the case of things which we are not able to part with, we should cultivate the +wish to give them away and develop more and more intimacy with that wish. + -- "Generous Wisdom: Commentaries by H.H. the Dalai Lama XIV on the + Jatakamala", translated by Tenzin Dorjee, edited by Dexter Roberts +~ + Mad yogins are known in virtually every tradition in Tibet, but most often +in the Nyingma and Kagyu lineages, and also in the Shije (Pacification) and +Chod traditions. The Nyingma, Kagyu, and Chod traditions are the three with +which Tangtong Gyalpo had the closest ties. One of the texts in Tangtong's +Oral Transmission, a collection of teachings originally passed down from +Tangtong, quotes the great yogini Machik Labdron's statement concerning proper +yogic conduct following realization. In response to a question by one of her +sons, Machik recommended that a practitioner act like a child with unfeigned +spontaneity, like a lunatic with no regard for what is conventionally +acceptable, like a leper with no attachment to his or her own physical health, +and like a wild animal wandering in isolated and rough terrain. + ...Guru Padmasambhava himself prophesied that Tangtong Gyalpo would care +for living beings by means of unpredictable actions. Tangtong's unusual +conduct began to manifest at an early age, and resembled traits noted in the +lives of other mad yogins. He was first called insane by his father and the +members of his village when, as a child, he subdued a malicious spirit +responsible for an epidemic. Several other early incidents are mentioned in +the biographies. When he went to take scholastic examinations at the renowned +monastery of Sakya he earned the nickname Tsondru Nyonpa (Crazy Tsondru) +because of his disinterest in explaining the scriptural definitions of the +highest states of realization. He preferred to spend his time absorbed in +actually experiencing these states. When he was later practicing deliberate +behavior secretly in a vast and empty wasteland, the dakinis gave him five +names indicating his high realization, one of which was Lungtong Nyonpa +(Madman of the Empty Valley). + -- Cyrus Stearns, "King of the Empty Plain: The Tibetan Iron Bridge + Builder Tangtong Gyalpo", a Tsadra Foundation book, published by + Snow Lion Publications +~ + ...In the Buddhist teachings, when we search for the causes of suffering, +we find what is called 'the truth of the origin of suffering', namely that +negative actions--karma--and the negative emotions that induce such actions +are the causes of suffering. + Talking about causes, if we take a step further and investigate more +deeply, we find that the cause alone is not sufficient for bringing about the +results. Causes themselves have to come in contact with co-operative +circumstances or conditions. For instance, say we search for a material or +substantial cause for this plant, we will find that it has a continuity +stretching back into beginningless time. + There are certain Buddhist texts that speak of space particles, existing +before the evolution of this present universe. According to these texts, the +space particles serve as the material and substantial cause for matter, such +as this plant. Now if the essential and substantial cause for matter is +traced to these space particles, which are all the same, how do we account for +the diversity that we see in the material world? It is here that the question +of conditions and circumstances comes into play. When these substantial +causes come in contact with different circumstances and conditions, they give +rise to different effects, that is, different kinds of matter. So we find +that the cause alone is not sufficient for bringing about a result. What is +required is an aggregation of many different conditions and circumstances. + Although you can find certain differences among the Buddhist philosophical +schools about how the universe came into being, the basic common question +addressed is how the two fundamental principles--external matter and internal +mind or consciousness--although distinct, affect one another. External causes +and conditions are responsible for certain of our experiences of happiness and +suffering. Yet we find that it is principally our own feelings, our thoughts +and our emotions, that really determine whether we are going to suffer or be +happy. + -- H.H. The Dalai Lama, "Dzogchen: The Heart Essence of the Great + Perfection", translated by Thupten Jinpa and Richard Barron, Foreword + by Sogyal Rinpoche, edited by Patrick Gaffney, published by Snow Lion + Publications +~ + Right now many of us wish for liberation, yet sometimes we cannot keep +ourselves from creating the causes for cyclic existence. When we understand +true suffering well, our wish for liberation will become firm. At present our +resolve to reach liberation is not firm because we think of suffering, but not +deeply. The deluded attitude believing that the unsatisfactoriness of change +is true happiness easily arises in us because we are not yet deeply convinced +that all happiness in cyclic existence is contaminated and is in fact only a +variety of suffering. To remedy this, we should meditate on true suffering +more often and explore its meaning deeply. Then our wish for liberation will +become firm. + We consider many things--clothes, food, good health, nice possessions, +financial security, the higher rebirths--as true happiness. As a result, we +are attached to them and create more causes for suffering in cyclic existence +in order to gain them. Thinking that the human birth is something marvelous, +we work at creating the causes that propel us toward it. In fact all we are +doing is creating the cause for yet another rebirth in cyclic existence, +together with all the problems that such a rebirth involves. + If we understand that by its nature, cyclic existence is unsatisfactory, +we will have a deep aversion to it. If we do not have a deep aversion to it, +we will not be determined to be free, and therefore will not be able to +destroy our self-grasping ignorance, which is the root of cyclic existence. +In that case, we will not be able to attain liberation. However, when we +deeply feel the extent to which we suffer in cyclic existence, we will +automatically want to abandon the true origin of suffering, attain the true +cessation, and meditate on the true path. Having realized true suffering, we +will easily realize the other three of the four noble truths. Thus it is +said: suffering is to be known. The origin is to be abandoned. The cessation +is to be attained. The path is to be practiced. The determination to be free +is the wish for ourselves to be free of cyclic existence. When we wish others +to be free, that is compassion. + -- Geshe Jampa Tegchok, "Transforming Adversity into Joy and Courage: + An Explanation of the Thirty-seven Practices of Bodhisattvas", edited by + Thubten Chodron, published by Snow Lion Publications +~ + Mantras are invocations to buddhas... prayers, or a combination of these. +Tantric practitioners repeat them in order to forge karmic connections between +themselves and meditational deities and to effect cognitive restructuring +through internalizing the divine attributes that the mantra represents. A +person who wishes to develop greater compassion, for instance, might recite +the mantra of Avalokitesvara, who embodies this quality: om mani padme hum... +[a] mantra [that] is well known to Tibetans. It represents for them the +perfect compassion of Avalokitesvara, who they believe has taken a special +interest in the spiritual welfare of the Tibetan people. He epitomizes +universal compassion that is unsullied by any trace of negative emotions or +mental afflictions. + Among ordinary beings there are, of course, many acts of compassion, but +these are generally tinged by self-interest, pride, or desire for recognition. +Avalokitesvara's compassion, by contrast, is completely free from all +afflictions and is so vast that it encompasses all sentient beings without +exception and without distinction. People who wish to develop such a +perspective recite Avalokitesvara's mantra over and over, meditating on its +significance, and in so doing they try to restructure their minds in +accordance with the cultivation of his exalted qualities. According to the +Dalai Lama, + 'mani'... symbolizes the factors of method--the altruistic intention to +become enlightened, compassion, and love. Just as a jewel is capable of +removing poverty, so the altruistic mind of enlightenment is capable of +removing the poverty, or difficulties, of cyclic existence and of solitary +peace.... The two syllables, 'padme'... symbolize wisdom. Just as a lotus +grows forth from mud but is not sullied by the faults of mud, so wisdom is +capable of putting you in a situation of non-contradiction whereas there would +be contradiction if you did not have wisdom.... Purity must be achieved by an +indivisible unity of method and wisdom, symbolized by the final syllable +'hum', which indicates indivisibility.... Thus the six syllables, 'om mani +padme hum', mean that in dependence on a path which is an indivisible union of +method and wisdom, you can transform your impure body, speech, and mind into +the pure exalted body, speech, and mind of a Buddha. + -- John Powers, "Introduction to Tibetan Buddhism", published by Snow + Lion Publications +~ + "Now let's look at ultimate reality," the Dalai Lama said, pointing a +little finger to his mug. "What exactly is it? We're seeing color, shape. +But if we take away shape, color, material, what is mug? Where is the mug? +This mug is a combination of particles: atoms, electrons, quarks. But each +particle is not 'mug.' The same can be said about the four elements, the +world, everything. The Buddha. We cannot find the Buddha. So that's the +ultimate reality. If we're not satisfied with conventional reality, if we go +deep down and try to find the real thing, we ultimately won't find it." + Thus, the Dalai Lama was saying, the mug is empty. The term "mug" is +merely a label, something we use to describe everyday reality. But each mug +comes into existence because of a complex web of causes and conditions. It +does not exist independently. It cannot come into being by itself, of its own +volition. + For example: suppose I decide to make a black mug. To do this, I mix +black clay and water, shape it to my liking, and fire the resulting mixture in +an oven. Clay plus water turns into a mug because of my actions. But it +exists because of the myriad different ways that atoms and molecules interact. +And what about me, the creator of the black mug? If my parents had never met, +the black mug might never have existed. + Therefore the mug does not exist independently. It comes into being only +through a complex web of relationships. In the Dalai Lama's own words, and +this is the key concept in his worldview, the mug is "dependently originated." +It came to be a mug because of a host of different factors, not under its own +steam. It is empty. "Empty" is shorthand for "empty of intrinsic, inherent +existence." Or to put it another way, empty is another word for +interdependent. + -- His Holiness the Dalai Lama and Victor Chan, "The Wisdom of + Forgiveness: Intimate Conversations and Journeys" +~ + Buddha teaches that one should not practice extremes.... As Nagarjuna's +"Precious Garland of Advice" says, + Practice is not done + By mortifying the body, + Since you have not forsaken injuring others + And are not helping others. + When you disregard the basic needs of the body, you harm the many sentient +organisms that live within the body. You should also avoid the opposite +extreme of living in great luxury. It is possible to make use of good food, +clothing, residence, and furnishings without producing afflictive emotions +such as attachment, pride, and arrogance. The crucial point is the control of +internal factors such as lust and attachment; external factors are not in and +of themselves good or bad. It is not suitable if attachment increases toward +even mediocre food, clothing, and so forth. + Contentment is the key. If you have contentment with material things, you +are truly rich. Without it, even if you are a billionaire, you will not have +happiness. You will always feel hungry and want more and more and more, +making you not rich but poor. If you seek contentment externally, it will +never happen. Your desire will never be fulfilled. Our texts speak of a king +who gained control over the world, at which point he began thinking about +taking over the lands of the gods. In the end his good qualities were +destroyed by pride. Contentment is necessary for happiness, so try to be +satisfied with adequate food, clothing, and shelter. + -- H.H. the Dalai Lama, "How to Expand Love: Widening the Circle of Loving + Relationships", translated and edited by Jeffrey Hopkins +~ +There is another way of speaking about the two types of meditation. In this +case, they are differentiated into 1) meditation that perceives the object and +2) meditation in which our mind is transformed into a specific affective +state. An example of the former is meditating on impermanence and emptiness. +These are subtle objects that we must use analytical meditation to perceive. +An example of the latter is meditation on the four immeasurables +(brahmaviharas)--love, compassion, joy, and equanimity. Here we are not +trying to perceive a subtle object, but are practicing to transform our minds +into those mental states. For example, everyone admires the quality of love, +but we cannot just say, "I should love everyone," and expect our deepest +feelings to change. First, we must free our minds from the gross obstacles of +attachment to friends, hostility to people who threaten or harm us, and apathy +towards strangers. On this basis, we then train our mind to recognize the +kindness of others, which arouses in us a natural wish to reciprocate and +share our kindness with them. After this we meditate on love and cultivate a +genuine wish for all sentient beings to have happiness and its causes. +Initially that feeling will arise in us but will not be stable. Anger may +still flash into our mind making our good feelings towards others disappear. +We need to cultivate love continuously and do so with a focused mind. The +greater our concentration, the more stable and penetrative the experience +will be. + -- Thubten Chodron, "Guided Meditations on the Stages of the Path", + foreword by H.H. the Dalai Lama, published by Snow Lion Publications +~ + ...for the Christian practitioner, the Creator and the acceptance of the +Creator as almighty, is a very important factor within that tradition in order +to develop self-discipline, compassion, or forgiveness and to increase them in +one's intimate relationship with God. That's something very essential. In +addition, when God is seen as absolute and almighty, the concept that +everything is relative becomes a little bit difficult. However, if one's +understanding of God is in terms of an ultimate nature of reality or ultimate +truth, then it is possible to have a kind of unified approach. + ...As to one's personal religion, I think this must be based on one's own +mental disposition.... Generally speaking, I think it is better to practice +according to your own traditional background, and certainly you can use some +of the Buddhist techniques. Without accepting rebirth theory or the +complicated philosophy, simply use certain techniques to increase your power +of patience and compassion, forgiveness, and things like that. + -- H.H. the Dalai Lama, "Healing Anger: The Power of Patience from a + Buddhist Perspective", translated by Thupten Jinpa, published by Snow + Lion Publications +~ +When we compare two ancient spiritual traditions like Buddhism and +Christianity, what we see is a striking similarity between the narratives of +the founding masters: in the case of Christianity, Jesus Christ, and in the +case of Buddhism, the Buddha. I see a very important parallel: in the very +lives of the [founders] the essence of their teachings is demonstrated. For +example... the essence of the Buddha's teaching is embodied in the Four Noble +Truths: the truth of suffering, the truth of the origin of suffering, the +truth of the cessation of suffering, and the truth of the path leading to this +cessation. These Four Noble Truths are very explicitly and clearly +exemplified in the life of... the Buddha himself. I feel [it] is the same +with the life of Christ. If you look at the life of Jesus, you will see all +the essential practices and teachings of Christianity exemplified. And in the +lives of both Jesus Christ and the Buddha, it is only through hardship, +dedication and commitment, and by standing firm on one's principles that one +can grow spiritually and attain liberation. That seems to be a central and +common message. + -- H.H. the Dalai Lama, "The Pocket Dalai Lama", compiled and edited + by Mary Craig +~ + With the achievement of quiescence, the attention is drawn inwards and is +maintained continuously, single-pointedly upon its object. Tsongkhapa +emphasizes that genuine quiescence is necessarily preceded by an experience of +an extraordinary degree of mental and physical pliancy, which entails an +unprecedented sense of mental and physical fitness and buoyancy. + In the state of meditative equipoise, only the aspects of awareness, +clarity, and joy of the mind appear, and all one's other sense faculties +remain dormant. Thus, while one's consciousness seems as if it has become +indivisible with space, one lacks any sensation of having a body; and when +rising from that state, it seems as if one's body is suddenly coming into +being. When genuine quiescence is achieved, one's attention can effortlessly +be maintained for hours, even days, on end, with no interference by either +laxity or excitation. + -- B. Alan Wallace, "Balancing the Mind: A Tibetan Buddhist Approach + to Refining Attention", published by Snow Lion Publications +~ + In Mahayana Buddhism, when one takes the bodhisattva vow, one pledges to +work tirelessly in this life and all future lives to awaken oneself and purify +oneself in order to help all other beings attain freedom from suffering +through spiritual enlightenment. One vows to help beings whenever possible, +and a profound way of doing this is to give a being the gift of life through +an act of kindness. This can take the form of helping an animal in danger +cross the road to safety before being struck by a vehicle or freeing an animal +that is in captivity before it is killed by buying it from the captor and +letting it roam free. If one is in a position to help save another's life-- +whether a human or an animal--one must practice fearless kindness to help the +other being in danger. + In Tibetan Buddhism, it is believed that due to the countless incarnations +all beings have undergone throughout time, at one point or another any given +living creature has been one's mother in a past life. Therefore, it is viewed +as an obligation to repay the kindness of those who are referred to as "mother +sentient beings." If your own mother in this life were in danger, you would +certainly do whatever you could to save her life. Similarly, dedicated +holders of the bodhisattva vow feel this kind of urgency to save the lives of +all "mother sentient beings." + -- Chatral Rinpoche, "Compassionate Action", edited, introduced and + annotated by Zach Larson, published by Snow Lion Publications +~ + In the frenzy of modern life we lose sight of the real value of humanity. +People become the sum total of what they produce. Human beings act like +machines whose function is to make money. This is absolutely wrong. The +purpose of making money is the happiness of humankind, not the other way +around. Humans are not for money, money is for humans. We need enough to +live, so money is necessary, but we also need to realize that if there is too +much attachment to wealth, it does not help at all. As the saints of India +and Tibet tell us, the wealthier one becomes, the more suffering one endures. + ...Eating, working, and making money are meaningless in themselves. +However, even a small act of compassion grants meaning and purpose to our +lives. + -- H.H. the Dalai Lama, "How to Practice: The Way to a Meaningful Life", + translated and edited by Jeffrey Hopkins +~ +Unlike the Lesser Vehicle tenet systems, which teach only a selflessness of +persons, the Great Vehicle tenet systems teach that the most profound reality, +the most subtle and important type of selflessness, is a selflessness, or +emptiness, that is a quality of all phenomena. They hold that the bodhisattva +trains in altruistically motivated meditation on the emptiness of all +phenomena, thus preparing for the omniscience of buddhahood. Some Great +Vehicle systems maintain that Lesser Vehicle practitioners do not realize the +profound emptiness of phenomena at all and are therefore unable to overcome +the obstructions to omniscience. However, the highest system, the Middle Way +Consequence system, holds that persons on Lesser Vehicle paths do realize +emptiness, but are unable to achieve omniscience on their paths because their +wisdom is not empowered by association with altruism and altruistically +motivated actions of giving, ethics, patience, etc. + -- Guy Newland, "Appearance and Reality: The Two Truths in the Four + Buddhist Tenet Systems", published by Snow Lion Publications +~ +Selflessness in Context: Ultimate Bodhichitta + Let us return for a moment to the beginning of [the Heart] Sutra where the +Buddha enters into the meditative absorption called "appearance of the +profound" and Avalokiteshvara beholds the practice of the profound perfection +of wisdom. Generally speaking, the expression "appearance of the profound" +refers to the bodhisattva deeds, which are encompassed in the practice of the +six perfections. Here, however, the expression refers particularly to the +perfection of wisdom, known in Sanskrit as prajnaparamita. What the text +means by "perfection of wisdom" is a direct, unmediated realization of +emptiness that is also called "ultimate bodhichitta." This is not the direct +realization of emptiness alone; rather it is this direct realization in union +with bodhichitta--the aspiration to become a buddha in order to free all +beings. This union of wisdom and method constitutes the first bhumi, or level +of bodhisattva attainment. + The importance of this altruistic aspiration cannot be overstated. +Bodhichitta is not only important as a motivating factor at the beginning of +practice, it is also important as a complementary and a reinforcing factor +during every stage of the path. The bodhichitta aspiration is twofold, +comprised both of the wish to help others and of the wish to become +enlightened so that one's assistance will be supremely effective. + --from Essence of the Heart Sutra: The Dalai Lama's Heart of Wisdom +Teachings by H.H. the Dalai Lama, translated & edited by Thupten Jinpa +~ +From "The Prayer Requested by Namke Nyingbo" by Padmasambhava + +All these things of the outer environment and the beings therein +That come into sight as the objects of your eyes like this, +They may appear, but leave them in the sphere free from clinging to a self. +Since they are pure of perceiver and perceived, they are the luminous-empty + body of the deity. +I pray to the guru in whom attachment is self-liberated, +I pray to Padmasambhava from Uddiyana. + +All these sounds, taken as pleasant or unpleasant, +That resound as the objects of your ears like this, +Leave them in the sphere of inconceivable, empty resonance. +Empty resonance, unborn and unceasing, is the Victor's speech. +I pray to the words of the Victor that resound and yet are empty, +I pray to Padmasambhava from Uddiyana. + +However these thoughts of afflictions' five poisons, +Which stir as objects in your mind like this, may appear, +Do not mess around with them through a mind that rushes ahead into the future + or lingers in the past. +Through leaving their movement in its own place, they uncoil as the + dharmakaya. +I pray to the guru whose awareness is self-liberated, +I pray to Padmasambhava from Uddiyana. + +Grant your blessings that the mind stream of someone like me is liberated + Through the compassion of the Tathagatas of the three times, +So that objects, appearing as if perceived outside, become pure, +That my very mind, perceiving as if inside, becomes liberated, +And that, in between, luminosity will recognize its own face. + + -- "Straight from the Heart: Buddhist Pith Instructions", translated and + introduced by Karl Brunnholzl, published by Snow Lion Publications +~ + Some people feel that although it may be right to curb feelings of intense +hatred which can cause us to be violent and even to kill, we are in danger of +losing our independence when we restrain our emotions and discipline the mind. +Actually, the opposite is true. Like their counterparts of love and +compassion, anger and the afflictive emotions can never be used up. They +have, rather, a propensity to increase, like a river flooding in summer when +the snow melts, so that far from being free, our minds are enslaved and +rendered helpless by them. When we indulge our negative thoughts and +feelings, inevitably we become accustomed to them. As a result, gradually we +become more prone to them and more controlled by them. And we become +habituated to exploding in the face of displeasing circumstances. + Inner peace, which is the principal characteristic of happiness, and anger +cannot coexist without undermining one another. Indeed, negative thoughts and +emotions undermine the very causes of peace and happiness. In fact, when we +think properly, it is totally illogical to seek happiness if we do nothing to +restrain angry, spiteful, and malicious thoughts and emotions. Consider that +when we become angry, we often use harsh words. Harsh words can destroy +friendship. Since happiness arises in the context of our relationships with +others, if we destroy friendships, we undermine one of the very conditions of +happiness itself. + -- His Holiness the Dalai Lama, "Ethics for the New Millennium", edited by + Alexander Norman, translated by Dr. Thupten Jinpa +~ +Two Senses of Self + + Psychologists talk about people who are co-dependent because they don't +have a sense of self. What psychologists mean when they say a person has no +sense of self is very different from what the Buddha meant by no-self or +selflessness. People with psychological problems actually have a very strong +sense of self in the Buddhist sense, although they may not in the +psychological sense of the word. Psychologically, they don't see themselves +as efficacious individuals in the world, but they still have a very strong +sense of "I": "I am worthless." When somebody criticizes them, they don't +like it. They get into co-dependent relationships to protect or to please +this "I." When they fall into self-pity, their sense of an inherently +existent "I" is very strong. Thus they still have self-grasping even though +they lack a psychologically healthy sense of self. + Buddhism recognizes two kinds of sense of self. There's one sense of self +that is healthy and necessary to be efficacious on the path. The object of +this sense of self is the conventionally existent "I." The other sense of +self grasps at an inherently existent self that never has and never will +exist. Within Buddhism, when we talk about realizing emptiness, we're +negating the false self, this self that appears inherently existent to us. + -- Thubten Chodron, "Cultivating a Compassionate Heart: The Yoga Method of + Chenrezig", foreword by H.H. the Dalai Lama, published by Snow Lion + Publications +~ + Question: If a person views the self and other phenomena as being empty of +any inherent existence, is it then, in that state, possible for them to take +any animate or inanimate phenomenon as their object, and through the power of +imputation or words, enable that object to actually take on a manifesting role +with the qualities which we view objects to have? + + His Holiness: This is an instance of not properly understanding the +meaning of "lack of inherent existence." If we think that "emptiness" means +things cannot function, then, with an improper understanding of the view of +emptiness, one will have fallen into nihilism. So, because one has failed to +reconcile emptiness and the fact that things work, this view is incorrect. +That is why it is said that the meaning of emptiness is to be understood in +terms of dependent arising. + Now, since the meaning of emptiness is to be explained in terms of +dependent arising, we can only explain something as arising dependently if +there is a basis, that is, some thing that is dependent. Hence, such a basis +must exist. We see then that when we speak of dependent arising, we are +indicating that things work. Dependent arising proves that things have no +inherent existence, through the fact that things work in dependence on each +other. The fact that things work and the fact that they do so in dependence, +one on the other, eliminates the possibility of their being independent. This +in turn precludes the possibility of inherent existence, since, to inherently +exist means to be independent. Hence, the understanding of emptiness, of the +the emptiness of a kind of inherent existence that is independent, boils down +to understanding dependent arising. + -- H.H. the Dalai Lama, "Answers: Discussions with Western Buddhists", + edited by Jose Ignacio Cabezon, published by Snow Lion Publications +~ + In the Tibetan Buddhist tradition, the most profound and commonly +practiced teachings are those of the Vajrayana. Within this powerful system +of skillful means, the supreme view and most potent methods are found in the +teachings and practices of Dzogchen, the Great Perfection. These instructions +are regarded as the pinnacle of the teachings and as the most direct path to +realizing the nature of mind and the reality of the world. + The instructions of the Dzogchen lineage are used to directly point out +the nature of mind and bring the experience of enlightenment into our ordinary +life. These teachings are known as "pith instructions," the pure, +quintessential knowledge that cuts through all confusion and gets straight to +the point. There is a saying, "Don't beat around the bush," meaning, "Get to +the point." That is Dzogchen. + In many ways, these teachings go beyond scripture and the formality of +spiritual techniques. These two do have their place, since it is important to +study scripture and meditate in a step-by-step manner. Yet, at some point we +also must connect directly with the nature of mind. We have to strike the +crucial point, the enlightened state, and leap directly into experiencing and +realizing the true nature of our mind. + -- The Third Dzogchen Rinpoche, "Great Perfection: Outer and Inner + Preliminaries", trans. by Cortland Dahl, intro. by Dzogchen Ponlop, + published by Snow Lion Publications +~ + Dzogchen teaches that practice conducted with contriving, rough, fleeting +minds cannot bring enlightenment. Only practice with the deep awareness of +non-contriving rigpa--pure awareness--can bring us to the state of a Buddha. +We can understand this in the same way as we do the statement that practice of +the yoga class of tantras and below cannot bring us enlightenment by itself. +The ultimate, deepest reason why it cannot is that the pathways of practice of +these levels of teaching cannot by themselves make manifest the deep awareness +of subtlest clear light mind. Without the manifestation of the deep awareness +of clear light mind, we do not have the perpetrating causes for an +enlightening body and enlightening mind of a Buddha--causes that are in the +same uncommon category of phenomena as a Buddha's body and mind. Therefore, +no matter how much we practice with pathway minds of yoga tantra and below, we +are never able to attain to enlightenment on their basis alone. + ...when we make clear light mind of deep awareness prominent or enhanced +through techniques presented in the anuttarayoga tantra texts, and then +transform it into the nature of being a pathway mind, only then do we have +what can actualize an enlightening body and enlightening mind of a Buddha. + -- H.H. the Dalai Lama and Alexander Berzin, "The Gelug/Kagyu Tradition of + Mahamudra", published by Snow Lion Publications +~ + Whenever you consider there is bliss, and the objective conditions for +bliss occur, if you fall under the control of that by becoming arrogant or +conceited, then that will fester as an obstruction to the spiritual path. +Rather than thinking about what has caused this happiness, which most probably +is the accumulation of merit or the removal of obscurations, as soon as the +bliss occurs, you think, "That's my nature." Based on that, you become +arrogant or lazy, thinking, "Well, I've accomplished it." This is the +greatest obstacle to the spiritual path. This is what creates the realms +of deva-gods. Oftentimes it is said that people can handle only a little bit +of felicity, but they can handle a lot of adversity. This is because +happiness on the spiritual path is the most difficult thing to handle. Once +it arises, that's where the path stops. + This does not mean that it is necessary to give it all up. Giving up +happiness is not the practice. The main point is not to become mesmerized by +happiness as the end result. You realize that, "Ah, now, the good quality of +this is that I am fortunate, and this is another result of the great fortune +of the path and the result of the accumulation of merit and wholesome deeds. +Even more than ever, I will carry on with the work at hand to achieve +liberation from cyclic existence." So with more diligence and more courage, +you continue listening to teachings, contemplating, meditating, and +appreciating this precious human rebirth. + -- Venerable Gyatrul Rinpoche, "Meditation, Transformation, and Dream Yoga", + translated by Sangye Khandro and B. Alan Wallace, published by Snow + Lion Publications +~ + As to what might be the mechanism through which karma plays a causal role +in the evolution of sentience, I find helpful some of the explanations given +in the Vajrayana traditions, often referred to by modern writers as esoteric +Buddhism. According to the Guhyasamaja tantra, a principal tradition within +Vajrayana Buddhism, at the most fundamental level, no absolute division can be +made between mind and matter. Matter in its subtlest form is prana, a vital +energy which is inseparable from consciousness. These two are different +aspects of an indivisible reality. Prana is the aspect of mobility, dynamism, +and cohesion, while consciousness is the aspect of cognition and the capacity +for reflective thinking. So according to the Guhyasamaja tantra, when a world +system comes into being, we are witnessing the play of this energy and +consciousness reality. + ...Despite the success of the Darwinian narrative, I do not believe that +all the elements of the story are in place. To begin with, although Darwin's +theory gives a coherent account of the development of life on this planet and +the various principles underlying it, such as natural selection, I am not +persuaded that it answers the fundamental question of the origin of life. +Darwin himself, I gather, did not see this as an issue. Furthermore, there +appears to be a certain circularity in the notion of "survival of the +fittest." The theory of natural selection maintains that, of the random +mutations that occur in the genes of a given species, those that promote the +greatest chance of survival are most likely to succeed. However, the only +way this hypothesis can be verified is to observe the characteristics of +those mutations that have survived. So in a sense, we are stating simply +this: "Because these genetic mutations have survived, they are the ones that +had the greatest chance of survival." + From the Buddhist perspective, the idea of these mutations being purely +random events is deeply unsatisfying for a theory that purports to explain the +origin of life. ...One empirical problem in Darwinism's focus on the +competitive survival of individuals, which is defined in terms of an +organism's struggle for individual reproductive success, has consistently been +how to explain altruism, whether in the sense of collaborative behavior, such +as food sharing or conflict resolution among animals like chimpanzees or acts +of self-sacrifice. There are many examples, not only among human beings but +among other species as well, of individuals who put themselves in danger to +save others. + ...From the scientific view, the theory of karma may be a metaphysical +assumption--but it is no more so than the assumption that all of life is +material and originated out of pure chance. + -- H.H. the Dalai Lama, "The Universe in a Single Atom: Convergence of + Science and Spirituality" +~ + Praising others should be part of our daily life and a component of our +Dharma practice. Imagine what our life would be like if we trained our minds +to dwell on others' talents and good attributes. We would feel much happier +and so would they! We would get along better with others, and our families, +work environments, and living situations would be much more harmonious. We +plants the seeds from such positive actions on our mindstream, creating the +cause for harmonious relationships and success in our spiritual and temporal +aims. + An interesting experiment is to try to say something nice to or about +someone every day for a month. Try it. It makes us much more aware of what +we say and why. It encourages us to change our perspective so that we notice +others' good qualities. Doing so also improves our relationships +tremendously. + A few years ago, I gave this as a homework assignment at a Dharma class, +encouraging people to try to praise even someone they didn't like very much. +The next week I asked the students how they did. One man said that the first +day he had to make something up in order to speak positively to a fellow +colleague. But after that, the man was so much nicer to him that it was easy +to see his good qualities and speak about them! + -- Thubten Chodron, "Taming the Mind", published by Snow Lion Publications +~ +Do not regret growing older. It is a privilege denied to many. -- Unknown +~ +To the world you may be one person, but to one person you may be the world. + --Unknown +~ +He that is good for making excuses is seldom good for anything else. + -- Benjamin Franklin +~ + If, after having performed a virtuous action and accumulated its potency, +that potency remained without degenerating until its fruit issued forth in +either this or a future life, it would not be so fragile. But that is not the +case. Rather, the generation of a strong nonvirtuous state of mind, such as +anger, overpowers the capacity of a virtuously established potency so that it +cannot issue forth, much like scorching a seed. Conversely, the generation of +a strong virtuous attitude overpowers potencies established by nonvirtues, +making them unable to issue their effects. Thus it is necessary not only to +achieve many powerful constructive causes but also to avoid contrary forces +that would cause those beneficial causes to degenerate. + The good actions required for accumulating these causes, or potencies, +arise from a tamed mind, whereas bad actions arise from an untamed mind. +Common beings like us have been accustomed to an untamed mind since +beginningless time. Given this predisposition, we can conclude that actions +performed with an untamed mind are more powerful for us and actions performed +with a tamed mind are weaker. It is important to appreciate that this +excellent life support of a human body that we now possess is a wholesome +result of many powerful good actions from a tamed mind in the past. It was +very difficult to gain, and, since it is very rare, you must take care to use +it well, making sure that it is not wasted. + ...If this human endowment, so difficult to attain, were stable and +permanent--not prone to deterioration--there would be time later to make use +of it. However, this life-support system is fragile and easily disintegrates +from many external and internal causes. Aryadeva's "Four Hundred Stanzas on +the Yogic Deeds of Bodhisattvas" says that once the body depends on the four +elements of earth, water, fire, and wind, which themselves oppose each other, +physical happiness is just an occasional balance of these elements, not an +enduring harmony. + ...So this human body is a precious endowment, potent and yet fragile. +Simply by virtue of being alive, you are at a very important juncture, and +carry a great responsibility. Powerful good can be achieved for yourself and +others, so becoming distracted by the minor affairs of this lifetime would be +a tremendous waste. You should make wishes to use this lifetime in this body +effectively and make petitions to your guru, the three refuges, and other +sources of help. In doing so, urge yourself on from the inside and seek +assistance from the outside..... + In sum, since this human body, which supports your life, is beneficial, +was difficult to gain, and easily disintegrates, you should use it for your +benefit and that of others. Benefits come from a tamed mind: When your mind +is peaceful, relaxed, and happy, external pleasures such as good food, +clothing, and conversation make things even better, but their absence does not +overpower you. If your mind is not peaceful and tamed, no matter how +marvelous the external circumstances are, you will be burdened by frights, +hopes, and fears. With a tamed mind, you will enjoy wealth or poverty, health +or sickness, you can even die happily. With a tamed mind, having many friends +is wonderful, but if you have no friends, it is all right, too. The root of +your own happiness and welfare rests with a peaceful and tamed mind. + -- His Holiness the Dalai Lama, "Mind of Clear Light: Advice on Living Well + and Dying Consciously", translated and edited by Jeffrey Hopkins, Ph.D. +~ + One of the reasons there is a need to adopt a strong countermeasure +against someone who harms you is that, if you let it pass, there is a danger +of that person becoming habituated to extremely negative actions, which in the +long run will cause that person's own downfall and is very destructive for the +individual himself or herself. Therefore a strong countermeasure, taken out +of compassion or a sense of concern for the other, is necessary. When you are +motivated by that realization, then there is a sense of concern as part of +your motive for taking that strong measure. + ...One of the reasons why there is some ground to feel compassionate +toward a perpetrator of crime or an aggressor is that the aggressor, because +he or she is perpetrating a crime, is at the causal stage, accumulating the +causes and conditions that later lead to undesirable consequences. So, from +that point of view, there is enough ground to feel compassionate toward the +aggressor. + -- H.H. the Dalai Lama, "Healing Anger: The Power of Patience from a + Buddhist Perspective", translated by Geshe Thupten Jinpa, published + by Snow Lion Publications +~ + What do you think would be the chief obstacle in recognizing that each +individual person has been kind to you? In my case, I was afraid of having to +return the kindness, because then I'd be under the control of these people. I +didn't want to do what my parents wanted me to do, although they gave me a lot +of slack--I left college after my first year, went to the woods of Vermont, +went to Tahiti, all on my own with whatever cash I earned. I didn't fit into +the upper-middle-class community where we lived. I didn't want their control; +the lifestyle they were pushing on me was completely unappealing. Therefore, +I refused to recognize their kindness. + However, assuming a debt with respect to every sentient being differs +greatly from having a debt to a few. In this meditation, you start with +friends, then neutral persons, and then enemies and contemplate: "I will +return the debt of kindness that I have with this person through helping her +or him achieve happiness." It is easy to determine that the response to all +sentient beings' kindness cannot be to do everything they want, since, with so +many people, what they want from you would be at cross-purposes. You cannot +even do everything your mother of this lifetime wants you to do, though you +know her advice is, for the most part, motivated by kindness.... + Those who help us--our parents, for instance--often attain power over us +for that very reason: "Do as I say because I have helped you." Thus, for some, +it becomes almost a mental habit to refuse to recognize those who have helped +us, because they otherwise would attain some power over us. Still, we know we +should return their many kindnesses. That is one reason why the practice of +reflecting, "This person has helped me in many intimate ways and thus I must +do something in return," gets to be uncomfortable, but when it is extended to +more and more beings, we have to find a way of intending to return their +kindness without coming under their misguided influence. + ...one cannot do everything all those sentient beings want. There are so +many of them, and they want such contradictory things. Besides, to fulfill +what they temporarily want may not be the best way to help them. The greatest +of all ways to return their kindness is to help them become free from all +suffering and to assist in the process of becoming liberated from cyclic +existence and attaining the bliss of Buddhahood. It is important to realize +here in the step of developing an intention to return others' kindness that +acknowledging a debt does not mean that you must do what they say. Otherwise, +you might hold back from the truth of their attentive care. + --Jeffrey Hopkins, "A Truthful Heart: Buddhist Practices for Connecting + with Others", foreword by the Dalai Lama, published by Snow Lion + Publications +~ + ...Nagarjuna's Fundamental Treatise says, "That which arises dependently +we explain as emptiness. This [emptiness] is dependent designation; this is +the middle way." His Refutation of Objections says, "I bow down to the +Buddha, the unequaled, supreme teacher, who taught that emptiness, dependent +arising, and the middle way hold a single meaning." + For Tsong-kha-pa, the compatibility of emptiness and dependent arising is +the very heart of the Madhyamaka view and the key to the path. Dependent +arising means that things come into being in dependence upon causes and +conditions. Understanding dependent arising correctly refutes the idea that +things exist in and of themselves--because they must depend on other things. +In the same moment, it also refutes the nihilist extreme--because it shows +that things do arise, they do come into existence, and they affect one +another. Thus, Tsong-kha-pa advises that if you think that you may have found +the profound view of emptiness, you should check to see if you have negated +too much. Can this "emptiness" you have discovered be reconciled with the +mere existence of things that arise interdependently? If not, then you are +certainly mistaken. + ...The point is that one cannot become a buddha without both compassionate +action and nondual wisdom--and one cannot have these two types of path without +both of the two truths, conventional and ultimate. If only emptiness existed +and there were, in fact, no conventional truths, then there would be no living +beings, no suffering to relieve; thus there would be no compassionate action; +and thus there would be no buddhahood. Therefore, maintaining the +compatibility of the two truths--the compatibility of emptiness and dependent +arising--is crucial to the whole of the Dharma. + -- Guy Newland, "Introduction to Emptiness: As Taught in Tsong-kha-pa's + Great Treatise on the Stages of the Path", published by Snow Lion + Publications +~ +Anything that can be done chemically can be done by other means. + -- William S. Burroughs +~ + ...let us discuss true sources of suffering. The fact that sufferings are +not always produced but are produced in some places at some times and cease at +some times and in some places indicates that they are caused. Logically, it +can be said that sufferings are caused because of being produced occasionally. +If sufferings were produced causelessly, either they would never exist or they +would always exist. + Since sufferings are caused, one needs to look into what their causes are. +In the Buddhist systems, the causes are explained to be contaminated actions +and afflictive emotions.... + For instance, if I had an angry feeling, this could serve as a motivating +force that would lead to a harsh attitude, harsh speech, and harsh physical +gestures. Since the anger that serves as the motivating factor is a +defIlement--an afflictive emotion--the physical and verbal actions done +through that motivating force are negative karmas, negative actions. Through +them, the atmosphere immediately changes into one of tension. Right away, I +might not feel the effects of those actions, perhaps even feeling that I had +gained a victory over someone, even shouting, "I have won." However, later I +will feel very sorry and shy, deep down experiencing a guilty conscience. +Similarly, those around me would immediately lose their tranquility and peace. +These are painful results of actions impelled by a bad motivation. This is +the law of karma--motivation, action, result. + Conversely, a good, open, sincere motivation such as compassion with a +deep respect for others impels verbal and physical actions that immediately +create a peaceful, harmonious, enjoyable atmosphere. Due to that, I feel +happy and calm, enjoying that atmosphere, and others around me also enjoy the +same. Therefore, bad motivation creates problems, suffering, and pain, +whereas good motivation creates happiness and peacefulness--something good. + This is the general explanation. On a deeper level, right at the time of +an action, predisposing potencies are instilled in the consciousness. The +performance of an action establishes a predisposing potency in the mind that, +in the future, will serve as the causal condition for one's experiencing a +good or bad effect. + -- H.H. the Dalai Lama, "The Dalai Lama at Harvard: Lectures on the + Buddhist Path to Peace", translated and edited by Jeffrey Hopkins, + published by Snow Lion Publications +~ +(Each day before breakfast the founder and abbess of Sravasti Abbey, Thubten +Chodron, gives a morning motivation for residents and guests. Below is a +teaching given during March 2008.) + +Quiet Place + + Have you ever had this experience? You walk outside, and all of a sudden +the silence strikes you--it's in such sharp contrast to the chatter that's +going on in the mind. + We live in a very quiet place. We walk outside and it's pretty quiet--a +few birds chirping, sun shining. Then suddenly the chatter in the mind stops +because we see that it's just chatter. It's in such stark contrast to the +silence that's outside. + We want to learn to notice that chatter before we even have to walk +outside. And we want to be able to find that quiet place inside ourselves and +keep it with us, so that even when we're in a place where there is a lot of +noise, the mind can be quiet. + All that mental chatter is basically negative conceptualization. If we +were thinking about emptiness or developing compassion with that kind of +mental activity, fine! Continue that outside, inside, everywhere. But most +of the time what's going on is, "I like this. I don't like this. I want +this. I don't want that. Why does this person do this? Why don't they do +that?" That kind of mental activity makes the mind quite stressful as well +as accumulates negative karma and wastes a great deal of time. + As soon as we can catch it and be aware of what's going on in our mind, +and come back to that silent space inside, the more peaceful we'll be. Our +lives will be more productive in terms of having the Dharma grow in our +hearts, and we’ll be more focused in whatever daily activities we're doing. +We won't be quite so distracted. + -- Thubten Chodron, author of numerous books, including Buddhism for + Beginners; Taming the Mind; Open Heart, Clear Mind; and Working + with Anger +~ + ...if people have compassion, naturally that's something they can count +on; even if they have economic problems and their fortune declines, they still +have something to share with fellow human beings. World economies are always +so tenuous and we are subject to so many losses in life, but a compassionate +attitude is something that we can always carry with us. + ...Of course, in attempting to explain to someone the importance of +compassion, in some cases, you might be dealing with a very hardened, +individualistic, and selfish person, someone concerned only with her or his +own interests. And it is even possible that there are people who may not have +the capacity to empathize with even someone whom they love or who may be close +to them. But even to such people, it is still possible to present the +importance of compassion and love on the grounds that it's the best way to +fulfill their self-interests. They wish to have good health, live a longer +life, and have peace of mind, happiness, and joy. And if these are things +that they desire, I've heard that there is scientific evidence that these +things can be enhanced by feelings of love and compassion. + ...educating someone about these facts and scientific studies could +certainly encourage some people to cultivate a more compassionate state of +mind. But I think that, even aside from scientific studies, there are other +arguments that people could understand and appreciate from their own practical +or direct everyday experience. For example, you could point out that lack of +compassion leads to a certain ruthlessness. There are many examples +indicating that at some level deep down, ruthless people generally suffer from +a kind of unhappiness and discontent, people like Stalin and Hitler. Such +people suffer from a kind of nagging sense of insecurity and fear. Even when +they are sleeping I think that sense of fear remains... these people lack +something that you can find in a more compassionate person--a sense of +freedom, a sense of abandonment, so when you sleep you can relax and let go. +Ruthless people never have that experience. Something is always gripping +them; there is some kind of hold on them, and they aren't able to experience +that feeling of letting go, that sense of freedom. + ...There are always different degrees of benefit that one might receive +from practicing various methods and techniques, depending on one's particular +circumstances.... First, through learning, thoroughly understanding the value +of compassion--this gives you a feeling of conviction and determination. +Then, employing methods to enhance empathy, such as using your imagination, +your creativity, to visualize yourself in another's situation. And certain +exercises or practices that you can undertake, such as Tong-Len, serve to +strengthen your compassion. But I think it's important to remember that these +techniques... were developed to help as many as possible, at least some +portion of the human population. But it was never expected that these +techniques could help 100 percent of people, the entire human population. + ...the main point really, if we are talking about various methods to +develop compassion, the important thing is that people make a sincere effort +to develop their capacity for compassion. If they make their best efforts to +be kinder, to cultivate compassion and make the world a better place, then at +the end of the day they can say, "At least I've done my best!" + -- His Holiness the Dalai Lama and Howard C. Cutler, M.D., "The Art of + Happiness: A Handbook for Living" +~ + In ascertaining luminous clarity at the time of the path, the general +technique is to rest evenly in the very essence of luminous clarity. Telopa +said: + + Rest relaxed within the uncontrived native state; + Bonds are released and freedom is sure. + + This and other such instructions are expressed unanimously by the mighty +adepts. Accordingly, with the body in the seven-point posture of meditative +stability, the mind rests without support, relaxed and uncontrived. This will +create the unerring yogic direct perception of emptiness. This is the +ultimate esoteric instruction of the completion phase found in the profound +tantras. The reason is that once the vital points of the vajra body, which is +the support, are bound, the mind, eyes, and energy currents remain in a state +of nonthought. Because of the special interconnection between body and mind, +the movement in the right and left channels is stopped and immobilized within +the central channel, causing the direct experience of mahamudra, emptiness +with aspects. + Therefore the luminous mind, which is the supported, is realized as empty +appearance arising as the mahamudra of forms of emptiness. This, again, +depends on the dissolution of the energy currents of the right and left +channels in the central channel, the supreme support. There is no more +profound method for affecting this dissolution than resting the mind once it +is uncontrived and relaxed. Therefore, in all the esoteric instructions of +highest tantra, this is called "the esoteric instruction of withdrawal" in the +presentations. + -- Jamgon Kongtrul Lodro Taye, "The Treasury of Knowledge, Book Eight, Part + Four: Esoteric Instructions, A Detailed Presentation of the Process of + Meditation in Vajrayana", trans. and annot. by Sarah Harding, foreword + by Khenchen Thrangu Rinpoche, published by Snow Lion Publications +~ +Happiness is when what you think, what you say, and what you do are in harmony. + -- Mahatma Gandhi +~ +Dignity does not consist in a silk dress. -- A saying from the Orient +~ + To solve the problems humanity is facing, we need to organize meetings of +scholars, educators, social workers, neuroscientists, physicians, and experts +from all fields to discuss the positive and negative sides of what we have +done thus far, as well as what needs to be introduced and what needs to be +changed in our educational system. Proper environment plays a crucial role in +the healthy growth of a child. All problems, including terrorism, can be +overcome through education, particularly by introducing concern for all others +at the preschool level. + Living in society, we must share the suffering of our fellow citizens and +practice compassion and tolerance not only toward our loved ones but also +toward our enemies. This is the test of our moral strength. We must set an +example by our own practice. We must live by the same high standards of +integrity we seek to convey to others. The ultimate purpose is to serve and +benfit the world. + -- His Holiness the Dalai Lama, "How to See Yourself As You Really Are", + translated and edited by Jeffrey Hopkins, Ph.D. +~ + "When Tibet was still free, we cultivated our natural isolation, +mistakenly thinking that we could prolong our peace and security that way. +Consequently, we paid little attention to the changes taking place in the +world outside. Later, we learned the hard way that in the international +arena, as well as at home, freedom is something to be shared and enjoyed in +the company of others, not kept to yourself." Budapest, 1994 + "I believe that Tibet will be free only when its people become strong, and +hatred is not strength. It is a weakness. The Lord Buddha was not being +religious, in the popular sense of the term, when he said that hatred does not +cease by hatred. Rather, he was being practical. Any achievement attained +through hatred [can only invite] trouble sooner or later." Statement, 10 March +1971 + -- H.H. the Dalai Lama, "The Pocket Dalai Lama" compiled and edited by + Mary Craig +~ + Persons from orally oriented cultures, writes Ong, tend to project their +sensibilities, to see them expressed in the world around them. More widely +literate cultures create persons who tend to withdraw for insight into their +own personal psyches. Orally oriented peoples may thus be more inclined than +persons in print-dominated cultures to set their feelings or experiences in +the space around them, including the invisible spirits presumed to occupy that +space, and less likely to project these feelings and experiences onto +individual persons. In Tibet lineages or sects are the most likely targets of +negative projections. Western print-oriented persons are more likely to +project their feelings onto other individuals, especially people in +significant relationships with them. Unlike Tibet, or the premodern West, the +contemporary West tends to identify the mind as the exclusive locus of ideas, +feelings, and values. With this localization, the mind becomes "psychic" in a +new sense, distinct from bodily soma and from the larger world. + This very different configuration of personhood affects the way Westerners +are likely to understand the Great Bliss Queen practice. For example, there +is a tendency among Westerners for "visualization" to be a more disembodied +practice than it is for Tibetans. The point in imagining oneself as the Great +Bliss Queen is not just to replace one visual image of oneself with another, +as if observing a changing scene in a movie theater, but to experience a +physical as well as mental shift from deep inside the body. + -- Anne Carolyn Klein, "Meeting the Great Bliss Queen: Buddhists, Feminists, + and the Art of the Self", published by Snow Lion Publications +~ + ...when you recognize how kind someone has been to you, you are using an +ordinary worldly attitude to help keep you from responses of hatred. For +instance, if someone gave me a grant with a blank check to form a team of +translators of Tibetan thought, I would be more than extremely pleased. Now +if the person who gave me the money came by someday and gave me a hard time, I +would feel a measure of restraint due to reflecting on the person's kindness. +I would seek other means to work things out with the person. When you reflect +how kind every person has been, there is that restraint to the point where, +believe it or not, trained Buddhists will look at a fly or an ant walking +across the table and think, "This is someone who bore me in her womb in a +former lifetime, who took care of me." + If you watch how mothers take precautions for a child in the womb, it is +clear that they do a great deal to help it. They eat nourishing foods and +avoid harmful substances like coffee, alcohol, nicotine, and drugs. If you +reflect on how such a mother takes care of the child in the womb and extend +this reflection to all sentient beings, I think that because your field of +awareness is no longer just a few sentient beings but is gradually expanding +to more and more, you can reflect on the mother's kindness without doing it +merely because you were helped. The staggering debt deflates your sense of +exaggerated importance. The boil is pricked. + -- Jeffrey Hopkins, "A Truthful Heart: Buddhist Practices for Connecting + with Others", foreword by His Holiness the Dalai Lama, published by + Snow Lion Publications +~ +He that can have patience can have what he will. -- Benjamin Franklin +~ +One generation plants the trees, and another gets the shade. + -- Chinese proverb +~ +Most men are within a finger's breadth of being mad. -- Diogenes the Cynic +~ +Fashion is something that goes in one year and out the other. -- Unknown +~ +I'm a born-again atheist. -- Gore Vidal +~ + Buddha's teachings on non-manifest phenomena, such as the extremely subtle +presentations of actions and their effect--which are very hidden phenomena-- +cannot be proved with reasoning. How then can they be verified? + There is no need to verify manifest phenomena through reasoning because +they appear directly to the senses. The slightly hidden, however, can be +proved with reasoning that generates inferential understanding, and since +emptiness is very profound but only slightly hidden, it is accessible to +reasoning. + ...very hidden phenomena cannot be proved with reasoning, and it seems +that Buddha can say whatever he likes. However, through our own experience we +can confirm Buddha's teachings on more important topics such as emptiness, the +altruistic mind of enlightenment, love, and compassion, for no matter who +analyses--Buddhist or non-Buddhist--or how much one analyses, if the person is +not biased through desire or hatred, these teachings can bear analysis and +serve as powerful sources of thought. When you see that Buddha does not err +with regard to these more important phenomena, you can accept his other +presentations. + ...The process of cyclic existence and the eradication of it can be proved +by the reasoning that establishes the misconception of inherent existence as +its root cause and establishes the wisdom cognising emptiness as its antidote. + -- H.H. the Dalai Lama, Tsong-ka-pa and Jeffrey Hopkins, "Tantra in + Tibet", published by Snow Lion Publications +~ +Meditation on [or cultivation of] the six deities is like faith or love +meditation in that the mind is being generated into the entity of the object +meditated. When faith or love are meditated, those two are not the object +observed but the entity into which the consciousness is being generated. +Meditation on impermanence or emptiness, on the other hand, means to take +these as the object and meditate on them. Thus, there are two types of +meditation--of a subjective aspect and on an objective aspect. Meditation on +the six deities is the former, for first one generates a wisdom consciousness +knowing the sameness in suchness of oneself and the deity--the ultimate--and +then causes it to appear as the sounds, letters, and finally the form of the +deity. + --His Holiness the Dalai Lama, Tsong-ka-pa, and Jeffrey Hopkins, + "Deity Yoga in Action and Performance Tantra", published by Snow + Lion Publications +~ + Once we realize emptiness, all phenomena are included within this reality, +which is not separate from the cause and effect of karma and which is free of +mental constructs. On this ultimate level of realization, it is possible to +state that there is no wholesome or unwholesome action. When we have realized +the nature of all phenomena, negative actions naturally subside and positive +ones are spontaneously accomplished. Until this time, however, we would be +slipping into nihilism if we said that the phenomena of relative truth, such +as positive and negative actions or karma, do not exist. + Just knowing this authentic view, however, is not enough. For others to +be able to experience it, we must also know the scriptures and reasonings so +that we can teach. Without the support of this knowledge, it will be +difficult for others to trust what we say, and so Milarepa speaks of scripture +and reasoning as an adornment to realization. + + Dissolving thoughts into the dharmakaya-- + Is this not meditation naturally arising? + Join it with experience + To make it beautifully adorned. + + One way to understand meditation is to see it as a practice of working +with the many thoughts that arise in our mind. With realization they arise as +mere appearances of the dharmakaya, the natural arising of mind's essential +nature. Being clear about this true nature of thought is called "attaining +the level of natural arising." At this point, there is no difference in any +thought that may arise, because we see the nature of each thought to be +emptiness, arising as the dharmakaya. Meditation could be defined as +realizing the dharmakaya of the Buddha. + -- Michele Martin, "Music in the Sky: The Life, Art & Teachings of the + 17th Karmapa Ogyen Trinley Dorje", published by Snow Lion Publications +~ + Who is the supreme friend + always helpful in times of need? + Mindfulness of the spiritual instructions + learned through study and contemplation. + -- The Seventh Dalai Lama + + Ordinary friends desert us when we fall on hard times or become an +inconvenience in their lives. Others simply disappear into their own +destinies. Even our spiritual teachers eventually die and leave us behind. + Our practice of the Dharma, however, that has been cultivated by means of +study, contemplation and meditation, is the one sure anchor that keeps our +ship stable when the seas become choppy. In fact, the more difficult the +situation we encounter, the more helpful it is to us. + When the Buddha had become very old and was preparing to pass away, +several of his disciples were overcome with grief. They asked him, "What +will we do after you are gone?" He replied, "Whenever you rely upon my +teachings, at that time I am there with you." + The Second Dalai Lama wrote, "When we know how to rely on the Dharma, we +are able to be happy in every situation. Where could one find a more +trustworthy and reliable friend?" + -- Glenn H. Mullin, "Gems of Wisdom from the Seventh Dalai Lama", + published by Snow Lion Publications +~ + I always believe that each individual human being has some kind of +responsibility for humanity as a whole. Particularly, I always believe that +as scientists, you have a special responsibility. Besides your own +profession, you have a basic motivation to serve humanity, to try to produce +better, happier human beings. Whether we understand consciousness or not, we +must produce warm-hearted persons. That is important. I want to express +that. Whenever I meet scientists, I always have to say this. + Through my own profession, I try my best to contribute as much as I can. +This proceeds without my being concerned whether another person agrees with my +philosophy or not. Some people may be very much against my belief, my +philosophy, but I feel alright. So long as I see that a human being suffers +or has needs, I shall contribute as much as I can to contribute to their +benefit. Scientists and medically qualified people can contribute especially. +That's different; that's a particular context. A human being needs to be +cared for according to your professional calling. You can contribute; that's +your shared professional responsibility + -- H. H. the Dalai Lama, "Consciousness at the Crossroads: Conversations + with the Dalai Lama on Brain Science and Buddhism", edited by Zara + Houshmand, Robert B. Livingston, and B. Alan Wallace, published by + Snow Lion Publications +~ + ...while walking in a park the body may be in the park while the mind is +off working in the office, or at home, or talking to a distant friend, or +making a list of groceries. That means the mind has disconnected from the +body. Instead, when looking at a flower, really look at it. Be fully +present. With the help of the flower, bring the mind back to the park. +Appreciation for sensory experience reconnects mind and body. When the +experience of the flower is felt throughout the body, a healing occurs; this +can be the same when seeing a tree, smelling smoke, feeling the cloth of your +shirt, hearing a bird call, or tasting an apple. Train yourself to vividly +experience sensory objects without judgment. Try completely to be the eye +with form, the nose with smell, the ear with sound, and so on. Try to be +complete in experience while remaining in just the bare awareness of the +sensory object. + When this ability is developed, reactions will still occur. Upon seeing +the flower, judgements about its beauty will arise, or a smell may be judged +to be foul. Even so, with practice the connection to the pure sensory +experience can be maintained rather than continuing to become lost in the +mind's distraction. Being distracted by a cloud of concepts is a habit and it +can be replaced with a new habit: using bodily sensual experience to bring us +to presence, to connect us to the beauty of the world, to the vivid and +nourishing experience of life that lies under our distractions. This is the +underpinning of successful dream yoga. + -- Tenzin Wangyal Rinpoche, "The Tibetan Yogas of Dream and Sleep", + edited by Mark Dahlby, published by Snow Lion Publications +~ + To succeed in practicing any form of tantra, it is necessary first to +train in developing the altruistic intention to become enlightened. Dzong- +ka-ba says that this needs to be done "in accordance with the quintessential +instructions," these being found in his Great Exposition of the Stages of the +Path to Enlightenment. Specifically, such an altruistic intention is +generated by way of the seven cause and effect quintessential instructions or +the equalizing and switching of self and other. To do those, it is necessary +to identify what liberation is and to develop an awareness seeking liberation, +for which it is necessary to reflect on the three types of suffering and +develop an intention to turn away from over-emphasizing the appearances of +this life and then to turn away from over-emphasizing the appearances of +future lives, developing an intention to leave such cyclic existence entirely, +whereupon it is possible to reflect on how others suffer and develop +compassion. Done continuously over a long period of time, at best one should +develop a fully qualified altruistic intention to become enlightened, and at +least one should develop such an intention from the depths of the heart. + With such altruism as your basic motivation it is possible to receive +initiation and take the pledges that lay out a type of behavior conducive to +enlightenment. + ...nowadays some people look on the practice of religion as if it were +something that causes them to lose their freedom. Opposite to this, rules +[for controlling counter-productive ill-deeds and overcoming afflictive +emotions] are for the sake of utilizing your freedom to develop the limitless +qualities of Buddhahood, in the quest for which you should never be satisfied. +Toward material things, which necessarily have a limit, it is best to be +satisfied with what you have, but with regard to the limitless development of +spiritual qualities, you should never be satisfied with a mere portion, but +continually seek higher development. The rules themselves make your mind +conducive to such progress, so there is no reason to be uptight about them. + -- H.H. the Dalai Lama, Dzong-ka-ba and Jeffrey Hopkins, "Yoga Tantra: + Paths to Magical Feats", translated and edited by Jeffrey Hopkins, + published by Snow Lion Publications +~ + The practice of Dzogchen may begin with doing fixation on an object, in +order to calm one's thoughts. Then one relaxes the fixation, dissolving the +dependence on the object, and one fixes one's gaze in open space. Then, when +one succeeds in making the calm state stable, it is important to work with the +movement of one's thoughts and one's energy, integrating this movement with +the presence of contemplation. At this point one is ready to apply +contemplation in one's daily life. The system of practice just described is +characteristic of the Series of the Nature of the Mind, but that is not to say +that in Dzogchen one must necessarily begin with fixation and meditation on a +calm state. In the Series of Primordial Space, and the Series of Secret +Instructions, for example, one enters directly into the practice of +contemplation. Particularly in the former, there are very precise +instructions on how to find the pure state of contemplation. In the latter, +on the other hand, the explanations are mainly concerned with how one +continues in contemplation in all circumstances. + The practice of contemplation is concisely explained in the line that +reads, "but vision nevertheless manifests: all is good." Even if the +condition of "what is" cannot be grasped with the mind, the whole +manifestation of the primordial state, including our karmic vision, does +nevertheless exist. All the various aspects of forms, colours, and so on, +continue to arise without interruption. When we find ourselves in +contemplation, this doesn't mean that our impure vision just disappears +and pure vision manifests instead. If we have a physical body, there is a +karmic cause for that, so there would be no sense in trying to abandon or +deny the situation we find ourselves in. We just need to be aware of it. +If we have a vision of the material, physical level of existence, which is +the cause of so very many problems, we need to understand that this vision +is only the gross aspect of the colours, which are the essence of the elements. + -- Chogyal Namkhai Norbu, "Dzogchen: The Self-Perfected State", edited by + Adriano Clemente, translated by John Shane, published by Snow Lion Pub. +~ +Don't think there are no crocodiles because the water is calm. + -- Malayan Proverb +~ + A tantric yogi who has gained control of the subtle energies of the body +and the subtle levels of consciousness will have control over the inner and +outer elements and consequently can transform his or her ordinary samsaric +form into a joyous rainbow body. But until we can do this, we have to accept +the fact that our physical basis is a magnet attracting every kind of +discomfort and pain. + ...This samsaric body keeps us running all of our lives. We have to run +to fulfill its endless needs, to keep it away from things that may harm it, +and to protect it from anything unpleasant. We have to give it pleasure and +comfort. We become ordained, and at first this is very satisfactory; but soon +our body makes it so difficult for us that we think our practice would be less +disturbed if we were to live as a layperson. So we give up and return to +ordinary life; but then we end up with a family to support, leaving us with no +time or energy for meditation. We have the pressing tasks of feeding, +clothing, and sheltering our children, and of arranging their education and so +forth. Our lives are spent alternating between work and worry, with +occasional short periods of pleasure, and then we have to die; but even this +we cannot do in peace, for, when we lie down to die, our last thoughts are +worried ones concerning the family we are leaving behind. Such is the nature +of worldly existence. + ...To care for our old people--these ones who have given us our body, our +life, and our culture--is a sacred duty of humanity. But most humans act more +like animals than people, and often we see old people who have been abandoned +by their families. Family units were very strong in Tibet, and old people +were usually cared for directly by relatives. The national care for the old +that we see in the West is something very good, a healthy sign, although +perhaps here the spiritual and psychological basis is somewhat lacking. + The suffering of old age is something we all must face, unless we die +prematurely. There is nothing we can do about it. Gone will be that false +sense of personal ability and strength that made us so proud when we were +young. Instead, helpers or friends will bathe us, dress us, spoonfeed us, and +have to take us to the toilet. Rather than live under the delusion of +permanence, we should engage in spiritual training so that we can enter old +age at least with the grace of wisdom. + ...So we can see that this body indeed causes us much grief in this life +and, sadly, in their quest to satisfy its many needs, most people just collect +an endless stream of negative karmic instincts that will lead them to lower +rebirths in the future. These are the sufferings of the human world. + ...The important point here is to become aware of the third type of +suffering, the subtle suffering that pervades all imperfect existence, the +all-pervading misery concomitant with having a perishable, samsaric base.... +[All are] enmeshed in suffering because the nature of their body and mind is +bound with compulsive cyclic processes. Until we develop the wisdom that is +able to free the mind from these compelling forces, there is no doubt that we +shall experience suffering throughout our lives, and that we shall continue to +wander endlessly in the wheel of birth, life, death, and rebirth where the +presence of misery can always be felt. + -- H.H. the Dalai Lama, "The Path to Enlightenment", edited and translated + by Glenn H. Mullin, published by Snow Lion Publications +~ +Observing the Mind Itself + + The primary meditative technique of great perfection is remaining in the +state of pure awareness. This is accomplished by calming the mind and then +abiding in comprehension of its basic clear light nature. The meditative +practice involves being cognizant of the arising and passing away of feelings, +emotions, sensations, etc., but understanding them within the context of pure +awareness. The more one does this, the more one realizes that all phenomena +arise from mind and remerge into it. They are of the nature of pure awareness +and are a projection of luminosity and emptiness. Through cultivating this +understanding, mental phenomena of their own accord begin to subside, allowing +the clear light nature of mind to become manifest. They appear as reflections +on the surface of a mirror and are perceived as illusory, ephemeral, and +nonsubstantial. + Those who succeed in this practice attain a state of radical freedom: +there are no boundaries, no presuppositions, and no habits on which to rely. +One perceives things as they are in their naked reality. Ordinary beings view +phenomena through a lens clouded by concepts and preconceptions, and most of +the world is overlooked or ignored. The mind of the great perfection adept, +however, is unbounded, and everything is possible. For many beginners, this +prospect is profoundly disquieting, because since beginningless time we have +been constricted by rules, laws, assumptions, and previous actions. One who +is awakened, however, transcends all such limitations; there is no ground on +which to stand, no limits, nothing that must be done, and no prohibitions. +This awareness is bottomless, unfathomable, immeasurable, permeated by joy, +unboundedness, and exhilaration. One is utterly free, and one's state of mind +is as expansive as space. Those who attain this level of awareness also +transcend physicality and manifest the "rainbow body" ('ja lus), a form +comprising pure light that cannot decay, which has no physical aspects, and +which is coterminous with the nature of mind. + -- John Powers, "A Concise Introduction to Tibetan Buddhism", published by + Snow Lion Publications +~ +Traditions are group efforts to keep the unexpected from happening. + -- Barbara Tober +~ +In a democracy it's your vote that counts; +in feudalism, it's your Count that votes. + -- Mogens Jallberg +~ +You cannot shake hands with a clenched fist. -- Indira Gandhi +~ +Luck is what you have left over after you give 100 percent. + -- Langston Coleman +~ +Every child is an artist. The problem is how to +remain an artist once he grows up. + -- Pablo Picasso +~ + Among Tibetans, at least traditionally, the economic conditions are such +that this nine-to-five daily employment isn't really an important part of +[working life]. In Tibet, either you are a farmer or a nomad or a merchant. +The work is seasonal.... During the season they work very hard, and when they +finish they come back and don't have any employment. + ...in modern society, and particularly in industrialized nations, the +issue of unemployment is a very difficult situation. There are no easy +answers. One has no choice but to try to cope, and make one's best effort to +find new work. There is just no other solution. + However, the basic attitude of the individual plays a very significant +role, and can make a big difference in how someone responds. While we may not +have control over our situation, our attitude is something that we have some +control over. So first, what we need to realize is that uncertainty and +change are very much a part of the modern economy, particularly with regard to +employment. That is a serious problem, but a fact that we have to accept. +There is no guarantee that there will be a job tomorrow if you are working +today. So, if we understand this ahead of time, it may change how we respond +when that happens. Then we won't feel so surprised, as if we are singled out. +We understand that the loss of a job has many factors, the result of many +causes and conditions. We will understand that, in many cases, it may even +have roots in global economic issues. This way, we won't become so upset by +taking it personally, or looking around us for someone to blame for our +problems. This alone may help reduce our mental agitation. Of course, here +we are talking about unemployment due to some wider causes or layoffs, not due +to being fired because of one's own incompetence. + So there might be different ways in which individuals will respond to the +challenges of change. What is important is to acknowledge this fact and try +to work out how best to cope with the immediate problem itself. For example, +if you need employment as a means of your livelihood and if you become +unemployed, then all your efforts should be put into looking for new +employment so that your livelihood will be secure. But there are two +different responses. One person may feel demoralized and become sort of +paralyzed, thinking, There is no hope, I lost my job, what am I supposed to +do? But another individual in the same situation might look at it as an +opportunity to make some changes. As a challenge. So that is the more +positive way, the more proactive way of dealing with this problem. But of +course it is not easy. + There may also be other ways that might help at least reduce the mental +anxiety of dealing with the situation, so that a person can use all their +mental energy to find new work. For Buddhists, there are certain thought +processes and considerations that help--for example, the belief in karma +[one's actions] and ultimately taking responsibility for one's own karma. +Although this kind of mental attitude may not have any effect in physically +resolving the situation, at least it will help ease the individual from the +psychological effect of losing the job, and so on. And of course, believers +in other religious systems can also take some consolation in their own +beliefs. + -- His Holiness the Dalai Lama, and Dr. Howard C. Cutler, M.D., + "The Art of Happiness at Work" +~ + Compassion and love are not man-made. Ideology is man-made, but +compassion and love are produced by nature. It is important to recognize +natural qualities, especially when we face a problem and fail to find a +solution. For example... in religious business, sometimes even due to +religion, we create a problem. If we try to solve that problem using +religious methods, it is quite certain that we will not succeed. So I feel +that when we face those kinds of problems, it is important to return to our +basic human quality. Then I think we will find that solutions come easier. +Therefore, I usually say that the best way to solve human problems is with +human understanding. + It is very important to recognize the basic nature of humanity and the +value of human qualities. Whether one is educated or uneducated, rich or +poor, or belongs to this nation or that nation, this religion or that +religion, this ideology or that ideology, is secondary and doesn't matter. +When we return to this basis, all people are the same. Then we can truly say +the words brother, sister; then they are not just nice words--they have some +meaning. That kind of motivation automatically builds the practice of +kindness. This gives us inner strength. + ...Next, let us talk about the human being as a social animal. Even if we +do not like other people, we have to live together. Natural law is such that +even bees and other animals have to live together in cooperation. I am +attracted to bees because I like honey--it is really delicious. Their product +is something that we cannot produce, very beautiful, isn't it? I exploit them +too much, I think. Even these insects have certain responsibilities, they +work together very nicely. They have no constitution, they have no law, no +police, nothing, but they work together effectively. This is because of +nature. Similarly, each part of a flower is not arranged by humans but by +nature. The force of nature is something remarkable. We human beings, we +have constitutions, we have law, we have a police force, we have religion, we +have many things. But in actual practice, I think that we are behind those +small insects. + Sometimes civilization brings good progress, but we become too involved +with this progress and neglect or forget about our basic nature. Every +development in human society should take place on the basis of the foundation +of the human nature. If we lose that basic foundation, there is no point in +such developments taking place. + -- "The Dalai Lama, A Policy of Kindness: An Anthology of Writings By and + About the Dalai Lama" compiled and edited by Sidney Piburn, Foreword by + Sen. Claiborne Pell, published by Snow Lion Publications +~ + Emphasizing neither renunciation nor transformation, though incorporating +both into its preparatory practices, the Great Completeness provides a +method know as "self-liberation" (rang 'grol), sometimes described as +"liberation in its own spot" (rang sar 'grol). Liberation takes place in the +situation just as it is, because one's mind and all things are, despite +powerful appearances to the contrary, primordially pure. If one has not yet +made this essential discovery, the Great Bliss Queen ritual can prepare one +for it. If one is familiar with the Great Completeness perspective, one +performs the visualization and recitation of the Great Bliss Queen ritual +entirely within an experience of innate awareness. In either case, the ritual +encompasses the three nondualisms already discussed. + One way of accessing the primordial purity so important to the Great +Completeness tradition is a practice known as "pure vision." This involves +visualizing companions, family, surroundings, and so forth as creations of +light, the habitat of an enlightened being. From the viewpoint of the Great +Completeness, such pure vision is not an imaginative overlay, but a move +toward understanding things as they are. As Khetsun Sangpo taught it, this +practice allows you to understand that apparently ordinary things and persons +have "been [primordially pure] from the beginning" so that "you are +identifying their own proper nature. Your senses normally misrepresent what +is there, but through this visualization you can come closer to what actually +exists." In short, by identifying one's body, companions, and world with +those of the Great Bliss Queen, one develops the ability to discover what has +always been there. This being so, there is no need to renounce or change +anything, only to see it more completely. This is the Great Completeness +tradition's special mix of ontological and cognitive nondualisms. Unlike the +tantric traditions, in which it is necessary to cease the coarse sense and +mental consciousness in order for the most subtle mind of clear light to +appear, the Dalai Lama observes that "in the Old [Nyingma] Translation School +of the Great Completeness it is possible to be introduced to the clear light +without the cessation of the six operative consciousnesses." Hence the +possibility of "discovering" what is already in our midst. Such discovery +reveals a spontaneous presence (yon dan hlun gyis grub ba) of collateral +qualities such as clarity and spontaneous responsiveness. Thus, comments +Longchen Rabjam, "primordially pure primordial wisdom is free in the face of +thought and the primordial wisdom, with a nature of spontaneity, abides as +primordial radiance, and profound clarity." + -- Anne Carolyn Klein, "Meeting the Great Bliss Queen: Buddhists, Feminists, + and the Art of the Self", by published by Snow Lion Publications +~ + Why is it that meeting our yidam deity directly and receiving the deity's +blessing are so important? If we are studying texts and wish to become great +scholars, there are an inconceivable number of the Buddha's teachings along +with the treatises that comment on them. All these have to be studied +diligently so that we can come to a basic understanding of their meaning; +beyond this, it is extremely difficult to enter into the more subtle levels. +In all of this practice and study, it is our own mind that is central. +Without a great blessing or without awakening the generative power of previous +habitual patterns, it will be extremely difficult to realize primordial +wisdom. + Lord Maitreya stated that bodhisattvas abiding on the various levels are +not able to attain omniscience immediately, and he also affirmed that we do +not need to become expert in all five traditional Buddhist sciences. Among +these are all classifications of the inner science that deals with the mind. +In the practice of the Secret Mantrayana, it is said that as long as objects +continue to arise in our minds, so long will the classifications of the Secret +Mantrayana last. As long as we have not realized the simultaneity of concepts +and liberation, as long as we have not been blessed with the knowledge that +knowing the nature of one phenomenon liberates us into knowing the nature of +all, we need to train from lifetime to lifetime in the many aspects of the +teachings. If we try to become expert in all five sciences or try to know all +the objects of knowledge, our training will be endless. For these reasons, it +is extremely important to seek accomplishments and blessings from the yidam +deity, for through the blessing of the deity, our positive habitual patterns +from the past will be awakened and the doubts that cloud our minds will be +cleared away. + -- "Music in the Sky: The Life, Art and Teachings of the Seventeenth + Karmapa, Ogyen Trinley Dorje" by Michele Martin, published by Snow Lion + Publications +~ + Verse 6 + When someone whom I have helped + Or in whom I have placed great hopes + Mistreats me in extremely hurtful ways + May I regard him still as my precious teacher. + -- Shantideva, "Guide to the Bodhisattva's Way of Life" + + According to worldly norms of human behaviour, when we help someone and +place great trust in them and they mistreat us in return, it is seen as +reasonable to be angry with them because we have been hurt. However, +practitioners of bodhicitta must not give in to this type of conventional +thinking. Instead, we should learn to view such people in a special way, as +objects for our practice of forbearance and loving kindness. We must in fact +recognise these people as our spiritual teachers. + -- H.H. the Dalai Lama, "Lighting the Way", translated by Geshe Thupten + Jinpa, published by Snow Lion Publications +~ + Advanced meditators develop the ability to create environments of their +own choosing, and they are able to transcend the sufferings that seem so real +to ordinary beings who are bound by mundane conceptions. According to Tsong +Khapa, for one who attains advanced levels of meditation, painful cognitions +no longer occur, no matter what external experiences one encounters. All of +one's cognitions are a union of bliss and emptiness. One recognizes that +nothing is inherently what it appears to be. Whatever occurs is perceived by +one's unshakably blissful consciousness as the sport of luminosity and +emptiness, and so, + + "for a Bodhisattva who has attained the meditative stabilisation of + bliss pervading all phenomena, only a feeling of pleasure arises with + respect to all objects; pain and neutrality do not occur, even though + [pieces from his body] the size of a small coin are cut or even though + his body is crushed by elephants, only a discrimination of bliss is + maintained." + -- Tsong-ka-pa on Ratnarakshita's Commentary + + Tantric texts stress that such bodhisattvas are not creating a delusional +system in order to hide from the harsher aspects of reality. Rather, they are +transforming reality, making it conform to an ideal archetype. Since all +phenomena are empty of inherent existence, they have no fixed nature. No one +ever apprehends an object as it is in its true nature, because there is no +such nature. Even if phenomena had fixed essences, we would still never be +able to perceive them, since all we ever experience are our cognitions of +objects, which are overlaid with conceptions about them. All our perceptions +are ideas about things, and not real things. These ideas are also empty, +arising from nothingness and immediately dissolving again into nothingness, +leaving nothing behind. Tantric adepts develop the ability to reconstitute +"reality," which is completely malleable for those who train in yogas +involving blissful consciousnesses realizing emptiness. The sense of bliss +pervades all their cognitions, and their understanding of emptiness allows +them to generate minds that are manifestations of bliss and emptiness. + -- John Powers, "A Concise Introduction to Tibetan Buddhism", published + by Snow Lion Publications +~ +Beware the flatterer; he feeds you with an empty spoon. -- Cosino DeGregrio +~ +Republicans are for both the man and the dollar, but in case of conflict +the man before the dollar. + -- Abraham Lincoln +~ +Labor is prior to, and independent of, capital. Capital is only the fruit of +labor, and could never have existed if labor had not first existed. Labor is +the superior of capital, and deserves much the higher consideration. + -- Abraham Lincoln +~ + For those of you who are not able to devote all your time to meditation, +there is nevertheless the possibility of engaging in practice in a serious +way. For example, the students at the monastic universities in South India +can, with some effort, do meditations during the prayers. When you recite the +prayers, you can mentally do the contemplation. The lifestyle and daily +routine at these monasteries have been structured by the great masters of the +past in a way that is most conducive to individual practice as well as to the +flourishing of the dharma. + If you find that your mind is in a very fluctuating emotional state-- +displaying anger, hatred, attachment and so forth--then you should first try +to calm down that state of strong emotion. This should be done by first +transforming it into a neutral state of mind, because there is no way that one +can switch directly from a negative state of mind to a positive one. +Therefore, you should first reduce the force of these emotions and +fluctuations and try to bring about some sort of calmness, using any means-- +such as taking a stroll or concentrating on the inhalation and exhalation of +the breath--that will enable you to forget what you are immediately feeling. +This will help you to reduce the force of strong emotion, thereby giving you +the calmness necessary for the practice of dharma. Like a white piece of +cloth which could be dyed any color that you desire, such a neutral state of +mind could then be transformed into a virtuous state of mind. + You could also engage in the preliminary practices of performing 100,000 +prostrations, recitations of the Vajrasattva mantra, and so forth. When you +undertake these practices, you should do them properly, not being only +concerned about the number. Many great masters of the past of all traditions +have emphasized the importance of these preliminary practices--they will +enable you to have a very firm start. If through them you can acquire a +fertile mind, then when the seed of meditation is planted, it will readily +bear the fruits of realizations. + Having successfully neutralized the emotional fluctuations within your +mind and having restored a reasonable degree of calmness, engage in the +practice of taking refuge and generating the altruistic aspiration to attain +full enlightenment. Taking refuge in the Three Jewels is the factor that +distinguishes one's practice from that of an erroneous path, and the +generation of the altruistic mind makes it superior to the paths aiming at +individual liberation. + -- H.H. the Dalai Lama, "The Path to Bliss", translated by Geshe Thupten + Jinpa, edited by Christine Cox, published by Snow Lion Publications +~ + ...if you consider just the subtlest mind and the wind or energy that +serves as its mount, the mere factor of luminosity and knowing of the subtlest +mind itself as well as the energy associated with it are what will be +transformed into the mind and body of a Buddha. This is the mind that will +turn into an omniscient consciousness--a Buddha's mind; it is this mind which +will be transformed, not some other mind coming from the outside. In other +words, the Buddha nature is inherent; it is not imported from somewhere else. + This is true because the very entity of the mind, its nature of mere +luminosity and knowing, is not polluted by defilements; they do not abide in +the entity of the mind. Even when we generate afflictive emotions, the very +entity or nature of the mind is still mere luminosity and knowing, and because +of this we are able to remove the afflictive emotions. + -- H.H. the Fourteenth Dalai Lama, His Holiness Tenzin Gyatso, "Kindness, + Clarity, and Insight", edited and translated by Jeffrey Hopkins, + co-edited by Elizabeth Napper, published by Snow Lion Publications +~ + The recognition that worldly attainments just do not provide enduring +happiness, and that we need to work on the internals, rather than the +externals, is an important motivation. It is also the basis of achieving +nirvana, often represented by the lotus flower. It is no accident that most +statues of Buddha have him sitting on cushions resting on a lotus flower--the +symbol of renunciation. + But what if we achieve nirvana? What if, through extreme diligence, we +attain its supreme peace and happiness? Would that be enough, or is there a +more profound level of motivation still? + Some years ago a number of tourists were kidnapped by terrorists in the +Philippines, and held hostage in the jungle for many months. Finally they +were released in small groups. I will never forget the reaction of one +hostage who was interviewed at the airport on his way home to join his wife, +who had been freed just days earlier. + You would think that after months of extreme privation and the constant +threat of uncertainty and death, returning safely to one's wife, home, and +family would be a cause for joyful celebration. But the hostage, while +relieved, could only think of the group of hostages he'd left behind. Those +who, in the preceding months, had been his fellow prisoners, whom he now knew +better than anyone else, and with whom in several cases, he had formed unique +and profound bonds of attachment. His overriding concern was to ensure that +those still being held captive would be safely released to experience the same +freedom he had now. Only then would he really be able to celebrate. + -- David Michie, "Buddhism for Beginners: Finding Happiness in an Uncertain + World", published by Snow Lion Publications +~ + The term "meditation" carries with it a burden of trendy, pseudo-mystical +connotations. The biggest mistake people make is to think that they will "get +something" out of meditation. It would be more accurate to think they will be +getting rid of something. Awareness practice undermines our unwitting +subjugation to hypermentation. It cuts through the cascade of thoughts and +feelings that distract us from the present moment where life actually happens. +The inner newsreel, with its imagined or distorted dramas, becomes less urgent +and seductive. The unexamined hopes and fears that have thrown us into +automatic or reflexive behavior lose their power to toss us about. What we +get rid of, initially, is a great deal of compelling noise with no point or +real substance to it. Even by becoming aware of its nature we de-reify it, +render it less solid and intractable. + ...How can we sort out our neuroses when the mind is a wild, chaotic mess +of fragmented thought? How can we work with our anger when we experience it +as a deluge of highly charged, urgent impulses, all mixed in with fleeting +bits of narrative, physical sensations, whispers of memory, rushes of fear, +and the visceral press to act? We can't. Every beginning meditator discovers +very quickly that the mind has a mind of its own. No beginner sits down, +says, "Peace! Be still!" and accomplishes enlightenment. It's enough at the +start just to see, discover, and acknowledge the chatter. That, in itself, is +a great step towards self-awareness. Chogyam Trungpa Rinpoche taught that the +awareness of our confusion is the first step towards clarity. + Over time, we can learn to just take note of whatever arises without being +pushed and pulled emotionally. We can sit still and not respond reflexively +to our hypermentation. We can allow ourselves to rest, to gently release +thoughts, to find a quiet space apart from the discursive jumble. We can +choose to be simply and quietly aware. In these quiet moments, experiences +arise much more clearly and distinctly. Only then can we discover the source +of our suffering and our anger. + I once attended a conference between a highly esteemed Tibetan lama, +Jamgon Kongtrul Rinpoche, and a group of psychiatrists. Someone asked +Rinpoche: "What is meditation?" Rinpoche looked playfully puzzled, pretended +not to understand, and after a brief consultation with his translator, +answered: "Meditation? Meditation? I don't know what that means. We have +another word for it which means 'paying attention to.' " Whatever the style, +to meditate is to pay attention. + -- Ron Leifer, M.D., "Vinegar into Honey: Seven Steps to Understanding and + Transforming Anger, Aggression, and Violence", published by Snow Lion + Publications +~ + ...practice must be carried out in terms of one's own thought. If one +knows how to bring the teachings into one's own thought, all physical and +verbal deeds can be made to accord with practice. If one does not know how to +bring them into one's own thought, even though one might meditate, recite +scriptures, or spend one's life in a temple, it will not help; thought is +therefore important for practice. Thus, taking refuge in the Three Jewels +(Buddha, his Doctrine and the Spiritual Community), taking into account the +relationship between actions and their effects, and generating an attitude of +helping others, are most important. + + Formerly in Tibet there was a famous lama called Drom. One day Drom saw a +man walking around a reliquary. 'Walking around a reliquary is good,' he +said. 'Practice is even better.' The man thought, 'Then, reading a holy book +would be good.' He did so, and one day while he was reading, Drom saw him and +said, 'Reading a holy book is good; practice is even better.' + + The man thought, 'This also does not seem to be sufficient. Now if I do +some meditation, that will certainly be practice.' Drom saw him in meditation +and said, 'Meditation is good; practice is even better.' The man was amazed +and asked, 'How does one practise?' Drom answered, 'Do not be attached to this +life; cause your mind to become the practices.' Dram said this because +practice depends on thought. + + -- H.H. the Dalai Lama, "The Buddhism of Tibet", translated and edited by + Jeffrey Hopkins, published by Snow Lion Publications +~ +Dampa said, "If these practitioners want buddhahood, they must reverse their +present behavior!" + +[Kunga] asked, "What is wrong with their present behavior?" + +He said: + + They practice thinking that what are in actuality obstacles are +attainments! + They meet the liberating path, but doubting and striving, they part from +it! + Doubting if they should refrain from their ill-omened actions, they +suffer! + The speech of those without experience has become Dharma--supposedly the +view. + Kunga is never parted from his prayers for the three village girls! + Now, draw your own conclusions! + +... + +Dampa said: + + When I see people clinging to illusions as real, compassion arises with a +force. + If one considers the sufferings of the six realms in terms of oneself, one +has no time to remain ordinary. + When one sees that the characteristic of samsara is suffering, a mind +wanting nothing whatsoever is born! + When one sees the various bases as rootless, self-grasping is not born! + When impermanence is born in the mind, faith and perseverance will come +together! + Those who grasp at permanence will not destroy persistent grasping at +things as real! + Kunga! Internalize truthlessness and throw the kitchen sauce into the +water! + -- Lion of Siddhas: The Life and Teachings of Padampa Sangye, translated + by David Molk, with Lama Tsering Wangdu Rinpoche, published by Snow + Lion Publications +~ +Books have the same enemies as people: +fire, humidity, animals, weather, and their own content. + -- Paul Valery +~ + In Buddhism there are basically two types of practices: Sutra and +Tantra.... The special purpose of Tantra is to provide a faster path so that +qualified practitioners can be of service to others more quickly. In Tantra +the power of imagination is harnessed to meditation in a practice called deity +yoga. In this practice you imagine 1) replacing your mind as it ordinarily +appears, full of troubling emotions, with a mind of pure wisdom motivated by +compassion; 2) substituting your body as it ordinarily appears (composed of +flesh, blood, and bone) with a body fashioned from compassionately motivated +wisdom; 3) developing a sense of a pure self that depends on purely appearing +mind and body in an ideal environment, fully engaged in helping others. As +this distinctive practice of Tantra calls for visualizing yourself with a +Buddha's body, activities, resources, and surroundings, it is called "taking +imagination as the spiritual path." + Let us consider a qualm about this practice. You are considering yourself +to have Buddha qualities which you presently do not have. Is this, then, a +correct type of meditative consciousness? Yes. Your mind is involved in +understanding reality, out of which you are appearing as a deity. Therefore, +your mind, from this viewpoint, is correct. Also, you are purposely imagining +yourself as having a divine body even if you do not presently possess one. +This is an imaginative meditation; you are not convinced from the depths that +you actually have pure mind, body, and selfhood. Rather, based in clear +imagination of ideal body and mind, you are cultivating the sense of being a +deity, compassionately helping others. + ...to engage in Tantra at any level demands a powerful intention to become +enlightened for the sake of others, and a feeling that this needs to be done +very quickly. + -- His Holiness the Dalai Lama, "How to Practice: The Way to a Meaningful + Life", translated and edited by Jeffrey Hopkins +~ +Why do we want to be wise and compassionate? If it's because we would simply +like to be wise and compassionate, we are off course, because the "I" cannot +attain wisdom and compassion. Wisdom and compassion can only be revealed once +the "I" has disappeared. When we reach this level, we will be able to benefit +others. In the meantime, it is the blind leading the blind. All true +religions seek to gain access to that level of consciousness which is not ego- +bound. In Buddhism, it is called the unconditioned, the unborn, the +deathless. You can call it anything you like. You can call it atman. You +can call it anatman. You can call it God. The fact is, there is a subtle +level of consciousness which is the core of our being, and it is beyond our +ordinary conditioned state of mind. We can all experience this. Some people +experience it through service, others through devotion. Some even think they +can experience it through analysis and intellectual discipline. Buddhists +usually try to access it through meditation. That's what we are doing. +Breaking through to the unconditioned in order to help others break through to +the unconditioned. But we have to start where we are, from right here. We +start with these minds, these bodies, these problems, these weaknesses, and +these strengths. + -- Venerable Tenzin Palmo, "Reflections on a Mountain Lake: Teachings + on Practical Buddhism", published by Snow Lion Publications +~ + Over the last few days I have been meeting scientists, mainly specialists +on the brain, as well as psychologists and psychotherapists.... The majority +of them agreed... that the key cause of the mental unrest and depression so +prevalent today... is lack of sympathy and affection. + I think you might find the following story quite interesting. A few weeks +ago I met someone whose mind, I was told, is severely disturbed. At the +beginning, I used all of my reasoning to encourage him, explaining that, as a +human being, there was no need for him to be discouraged, because we have such +a good human brain and intelligence. I pointed out how, with determination, +we can solve all our human problems and overcome all obstacles, and so there +is no reason to worry or be discouraged or depressed. Personally, I always +find this kind of reasoning is quite effective, but this time it failed. He +was not impressed by this line of thinking. On the contrary, instead of +showing any appreciation, he developed a rather contrary attitude. After +listening to what I had to say, he became even more agitated, and asked me, +"Why are you concerned about my problem? How do I know if your attitude is +sincere or not?" I felt really sad. I was quite moved as well, and as I was +explaining something or other, my hand reached out and caressed his arm. It +was a natural gesture, a sincere expression of how I felt. Gradually, his +mood altered; I could see his face beginning to change, and finally a smile +began to appear. Then as I gained confidence, I increased that expression of +affection. At last a big smile spread right across his face. + I told him, "Please consider me as an old friend. Any time, you can come +to see me. Whatever I can do to help you, I am ready to do. I am at your +service." When I said this, then his mood, it was clear, became very happy +and joyful. The following day he came to see me again. When he arrived, he +already had a happy air about him, but nevertheless he was trying to pretend +otherwise and was not smiling. Anyway, what this incident really gave me was +another confirmation of how powerful genuine compassion, love, or altruism can +be, to affect other people's minds. And how they can remove fear and +suspicion, and alleviate feelings of insecurity and mistrust. + So I always consider compassion as the key, not only for achieving and +maintaining our own mental calmness, stability and happiness, but also as +something extraordinarily useful for creating a healthy human society. By +that I mean a happier and less harmful human society. Therefore--whether it +be in individual cases, on a family level, a national level, or an +international level--altruism, love and compassion are the basis for success, +for happiness, and for a happy environment. + -- H.H. the Dalai Lama, "Dzogchen: The Heart Essence of the Great + Perfection", translated by Thupten Jinpa and Richard Barron, + Foreword by Sogyal Rinpoche, edited by Patrick Gaffney, published + by Snow Lion Publications +~ +According to Tibetan Buddhism, ordinary beings are born into life situations +in which they are destined to suffer and die. This is the result of former +contaminated actions and afflictions, which have been accumulated since +beginningless time. Because of this process, physical and mental afflictions +are deeply rooted in sentient beings, and so it is generally considered +necessary to prepare oneself for tantric practice by engaging in the +"preliminary practices," or ngondro (sngon 'gro, purvagama), in order to begin +to reverse one's negative conditioning. These practices combine physical +movements with visualization in order to transform the mind from one that is +fixated on mundane concerns and desires into one that is primarily oriented +toward religious practice for the benefit of others. Some teachers consider +these preparatory trainings to be so essential to successful tantric practice +that they will not give tantric initiations to those who have not completed +them, and even teachers who are willing to waive them generally stress their +importance. The preliminary practices are: (1) taking refuge; (2) +prostration; (3) Vajrasattva meditation; (4) mandala offering; and (5) guru +yoga. + -- John Powers, "A Concise Introduction to Tibetan Buddhism", + published by Snow Lion Publications +~ + [It] is quite clear to me is that the moment you think only of yourself, +the focus of your whole reality narrows, and because of this narrow focus, +uncomfortable things can appear huge and bring you fear and discomfort and a +sense of feeling overwhelmed by misery. The moment you think of others with a +sense of caring, however, your view widens. Within that wider perspective, +your own problems appear to be of little significance, and this makes a big +difference. + If you have a sense of caring for others, you will manifest a kind of +inner strength in spite of your own difficulties and problems. With this +strength, your own problems will seem less significant and bothersome to you. +By going beyond your own problems and taking care of others, you gain inner +strength, self-confidence, courage, and a greater sense of calm. This is a +clear example of how one's way of thinking can really make a difference. + One's own self-interest and wishes are fulfilled as a byproduct of +actually working for other sentient beings. As the well-known fifteenth- +century master Tsongkhapa points out in his Great Exposition of the Path to +Enlightenment, "The more the practitioner engages in activities and thoughts +that are focused and directed toward the fulfillment of others' well-being, +the fulfillment or realization of his or her own aspiration will come as a +byproduct without having to make a separate effort." Some of you may have +actually heard me remark, which I do quite often, that in some sense the +bodhisattvas, the compassionate practitioners of the Buddhist path, are +"wisely selfish" people, whereas people like us are the "foolishly selfish." +We think of ourselves and disregard others, and the result is that we always +remain unhappy and have a miserable time. + ...we find that kindness and a good heart form the underlying foundation +for our success in this life, our progress on the spiritual path, and our +fulfillment of our ultimate aspiration, the attainment of full enlightenment. +Hence, compassion and a good heart are not only important at the beginning but +also in the middle and at the end. Their necessity and value are not limited +to any specific time, place, society, or culture. + Thus, we not only need compassion and human affection to survive, but they +are the ultimate sources of success in life. Selfish ways of thinking not +only harm others, they prevent the very happiness we ourselves desire. The +time has come to think more wisely, hasn't it? This is my belief. + -- Tenzin Gyatso, the Fourteenth Dalai Lama, from "The Compassionate Life" +~ + ...all apparent phenomena are nothing but delusion and there is, moreover, +no freedom from delusion to be achieved by dispelling delusion. Delusion is, +by its own essence, completely pure and, hence, enlightened. All phenomena +are, in this way, primordially, fully, and completely enlightened. Phenomena +appearing as various attributes are, therefore, indeed the mandala of vajra +body, speech, and mind. They are like the Buddhas of the three times, never +transcending the essence of complete purity. Sentient beings and Buddhas are +not differentiated in terms of their essence. Just like distinct causes and +results appearing in a dream, they are nothing but perceptions of individual +minds brought forth by the power of imputation. + Here the issue might be raised, "although the scriptures do teach this, +there is no certainty whether it is to be taken at face value or requires +interpretation. Therefore the essential purity of phenomena may well be +established, but it is unreasonable to say that precisely the nature of that +which appears as subjects with attributes is primordially enlightened. For, +if it were that way, thorough affliction and samsara would be entirely absent. +There can't be a reasoning that establishes such a philosophy." The +conceptual mind that takes objects that appear in the experience of sentient +beings as valid is, since beginningless time, deluded. It accepts or negates +with reference to the way things appear to it. With such dialectics it is, +indeed, not possible to establish the vast and profound meaning. Nevertheless, +since the nature of phenomena is inconceivable, it is not the case that there +is no way to realize it by means of discriminating knowledge. Thus it is not +in any way a mistake if one, rather than that, is inclined to approach simply +by faith, regarding the scriptures and oral instructions as valid. One will +then gain access through trust. + One may object, "Well, if one cannot prove [the primordial mandala] with +reasoning, one cannot gain access to it either." We can prove it as follows: +That phenomena are fully enlightened as the mandala of vajra body, speech, and +mind is proven with the reasoning of the intrinsic nature. Just as it is +stated in a sutra, "Form is empty by nature. Why is that? It is so because +that is its nature." All phenomena are pure by their intrinsic nature and, +therefore, there is not a single phenomenon that is impure. This is the +intrinsic nature of phenomena. Complete purity is, therefore, also the +intrinsic nature of body, speech, and mind, and their complete purity is +enlightenment. Therefore, body, speech, and mind, distinguished by their +complete purity, are inseparable, free from mental constructs, and perfectly +pervasive. One must in this way understand them to be the mandala of vajra +body, speech, and mind. + -- Heidi I. Koppl, "Establishing Appearances as Divine: Rongzom + Chozang on Reasoning, Madhyamaka, and Purity", published by + Snow Lion Publications +~ + Because spirits can be positive or negative in relation to humans, it is +wise to be careful with practices that connect the practitioner to a spirit. +It is currently popular for people to take drum journeys in their imaginations +and to look for guardian spirits and power animals and so on. Although +usually this is beneficial, or at least harmless, there really are beings with +whom the rare individual will connect. Not all of them are beings anyone +should want to connect with. There seems to be little regard for who the +being is; this can be a dangerous practice. People are much more careful +about choosing a business partner or a roommate than they seem to be about +choosing a non-physical being for a guide or guardian. + -- Tenzin Wangyal Rinpoche, "Healing with Form, Energy and Light: + The Five Elements in Tibetan Shamanism, Tantra and Dzogchen", + published by Snow Lion Publications +~ + ...reflect upon the negative consequences of our strong attachment to +friends and hostility toward enemies. Our feelings for a friend or a loved +one sometimes blind us to certain of his or her aspects. We project a quality +of absolute desirability, absolute infallibility, upon that person. Then, +when we see something contrary to our projections, we are stunned. We swing +from the extreme of love and desire to disappointment, repulsion, and +sometimes even anger. Even that sense of inner contentment and satisfaction +in a relationship with someone we love can lead to disappointment, +frustration, and hatred. Though strong emotions, like those of romantic love +or righteous hatred, may feel profoundly compelling, their pleasure is +fleeting. From a Buddhist point of view, it is far better not to be in the +grip of such emotions in the first place. + What are the repercussions of becoming overpowered by intense dislike? +The Tibetan word for hatred, shedang, suggests hostility from the depth of +one's heart. There is a certain irrationality in responding to injustice or +harm with hostility. Our hatred has no physical effect on our enemies; it +does not harm them. Rather, it is we who suffer the ill consequences of such +overwhelming bitterness. It eats us from within. With anger we slowly begin +to lose our appetite. We cannot sleep at night and often end up just rolling +back and forth, back and forth, all night long. It affects us profoundly, +while our enemies continue along, blissfully unaware of the state we have been +reduced to. + Free of hatred or anger, we can respond to actions committed against us +far more effectively. If we approach things with a cool head, we see the +problem more clearly and judge the best way to address it. For example, if a +child is doing something that could be dangerous to himself or others, such as +playing with matches, we can discipline him. When we behave in such a +forthright manner, there is a far greater chance that our actions will hit the +mark. The child will respond not to our anger but to our sense of urgency and +concern. + This is how we come to see that our true enemy is actually within us. It +is our selfishness, our attachment, and our anger that harm us. Our perceived +enemy's ability to inflict harm on us is really quite limited. If someone +challenges us and we can muster the inner discipline to resist retaliating, it +is possible that no matter what the person has done, those actions do not +disturb us. + -- H.H. the Dalai Lama, "An Open Heart: Practicing Compassion in + Everyday Life", edited by Nicholas Vreeland, afterword by Khyongla + Rato and Richard Gere +~ + "All phenomena should be understood as lacking an end and a middle, just + as the mind does not have an end or a middle. With the knowledge that the + mind is without an end or a middle, no identity of the mind is perceived. + What is thoroughly realized by the mind, too, is realized as being empty. + By realizing that, the very identity, which is established as the aspect + of the mind, like the identity of physical form, and so forth, is also + ultimately not perceived. In this way, when the person does not ultimately + see the identity of all phenomena through wisdom, he will not analyze + whether physical form is permanent or impermanent, empty or not empty, + contaminated or not contaminated, produced or non-produced, and existent + or non-existent. Just as physical form is not examined, similarly feeling, + recognition, compositional factors, and consciousness are not examined. + When the object does not exist, its characteristics also cannot exist. + So how can they be examined?" + -- Stages of Meditation by Kamalashila + +The above passage deals with ultimate reality; its meaning is that in the +ultimate sense the object of imputation is not findable. In this context we +find in the Heart Sutra phrases like: "There is no physical form, no sound, no +smell, no taste, and no object of touch." The mind, too, is not findable in +the ultimate sense. Since in the ultimate sense such things are non-existent, +there is no point examining whether they are permanent or impermanent. +Ultimately all phenomena, including the aggregates and so forth, are devoid of +true existence. Within the notion of ultimate reality, things are devoid of +true existence. In the same way, suchness, which is an attribute of +phenomena, is also devoid of true existence. This is important. Even when we +understand that phenomena like physical form and so forth are devoid of true +existence, there is a danger of thinking that ultimate reality may have true +existence. + -- H.H. the Dalai Lama, "Stages of Meditation", root text by Kamalashila, + translated by Geshe Lobsang Jordhen, Losang Choephel Ganchenpa, and + Jeremy Russell, published by Snow Lion Publications +~ + We usually discriminate strongly between someone who intends to harm us +and someone who doesn't. We think, "That's all right; he didn't mean it"; or +the person who has harmed us can say, "Why do you blame me so much? I didn't +mean to." But we get really angry when we know people mean to harm us. How +could we possibly see such people as intimate, close, dear--as dear as our +best of friends? + If you can retain a little compassion when people harm you +unintentionally, you have made progress. But if you retain it when someone +intends to harm you, you are really successful. It's not that you think, +"This person is marvelous; she's trying to rob me," but you don't take these +facts as reasons for hating the person. You recognize the intention and put +your wallet in your front pocket. You take such measures, but the conditions +that prompted them no longer serve as reasons for hatred. Our wish to love +everyone and the actual attitudes we have under pressure are in constant +conflict. That's just the way we are. We've been wandering in cyclic +existence since beginningless time, because of desire and hatred, and it's +going to take a lot of familiarization to change this. Be relaxed about it. +Don't put pressure on yourself, thinking things like, "Oh, I'm a scumbag +because I hate so deeply." Rather, try this attitude: "I have to admit it. +As much as my ideals say I should love so-and-so--or at least be neutral--I +have to face the fact that I don't." Go easy on yourself. + -- Jeffrey Hopkins, "A Truthful Heart: Buddhist Practices for Connecting + with Others", foreword by His Holiness the Dalai Lama, published by + Snow Lion Publications +~ + Many types of valid consciousnesses derive from basic, natural, and +obvious perception. All of us have an innate "I," although if we try to +locate this "I," we get into a lot of difficulties. This sense of "I" gives +us a well-founded aspiration to happiness and a wish not to suffer. + There are different levels of happiness and different kinds of suffering. +Material things usually correspond to physical happiness, whereas spiritual +development corresponds to mental happiness. Since our "I" has these two +aspects--physical and mental--we need an inseparable combination of material +progress and internal, or spiritual, progress. Balancing these is crucial to +utilizing material progress and inner development for the good of human +society. + Big schemes for world development arise from this wish to gain happiness +and relieve suffering. But there are higher levels of happiness beyond these +worldly forms, in which one seeks something longer-term, not just confined to +this lifetime. Just as we need a long-range perspective that protects the +environment, we need an internal long-range perspective that extends to future +lifetimes. + -- H.H. the Dalai Lama, "How to Expand Love: Widening the Circle of + Loving Relationships", translated and edited by Jeffrey Hopkins +~ + 128. + Desire is painful because of not getting, + Anger is painful through lack of might, + And confusion through not understanding. + Because of this, these are not recognized. + + Desire produces suffering when one does not encounter what one badly +wants. Anger produces suffering when one lacks might to crush the strong. +Confusion* induces suffering when one fails to understand a subtle matter +thoroughly. The inability to recognize these forms of suffering when one is +overwhelmed by desire and so forth is great suffering indeed. Therefore, +persevere in getting rid of the disturbing emotions. It is like a poor man's +son who suffered because he wanted a queen. + A certain poor man wanted a queen, but kings keep their queens heavily +guarded, and because he could not get her, his desire made him suffer. He +felt anger toward the king for guarding his queens well, and since he could +not do the slightest harm to the king, he suffered acutely on account of his +anger. Blinded by desire and anger his confusion grew, and unable to +understand the situation properly, he was tormented by the suffering it caused +him. + +* confusion's function is to feed desire and anger. + + -- "Aryadeva's Four Hundred Stanzas on the Middle Way: with Commentary by + Gyel-tsap by Aryadeva and Gyeltsap, additional commentary by Geshe Sonam + Rinchen", translated by Ruth Sonam, published by Snow Lion Publications +~ +Never hope more than you work. -- Rita Mae Brown +~ +To lead the people, walk behind them. -- Lao Tzu +~ + To consider those things which are existent, there are many phenomena +which are produced only occasionally. For example, certain plants grow only +during certain seasons, not all the time. That shows that they have been +produced by their causes and conditions. On the other hand, certain phenomena +exist permanently. Those are the two types of phenomena. In the case of +phenomena which arise only occasionally for a certain period of time then +cease to exist, their production is evidence used to prove their dependence on +their causes and conditions. But permanent phenomena are not dependent on +causes and conditions. Generally speaking, almost all phenomena which are +beneficial or harmful to us belong to the category of the occasional, the +dependent--the impermanent. Even our mind, which is to be disciplined and +subdued, belongs to that category. + Within the kind of phenomena which are existent, we can talk about +different types: those which are animate and those which are inanimate; those +with form and those formless; visible and invisible; audible and inaudible. +And there are phenomena which definitely exist but can be experienced only by +our mind, not our sense perceptions; in other words, we can talk about two +types of phenomena, external matter and internal consciousness. When we talk +about subduing mind, we refer to internal consciousness, that which has +clarity and cognitive power and is capable of experiencing objects. Although +our mind has arisen depending upon its causes and conditions, we need to find +out to what extent it can be transformed, for it is through the transformation +of our mind that we can subdue it. The way of transformation is to pacify the +mind's faults and to cultivate and enhance its good qualities. Although there +are certain phenomena which, having arisen from their causes and conditions, +remain as they are and cannot be changed by any means, there are others, +including our mind, which can be. To establish that kind of distinction, the +reasons provided in the Lam-rim section on analytical meditation to generate +special insight are especially important and useful. + -- "Generous Wisdom: Commentaries", H.H. the Dalai Lama XIV on the + Jatakamala translated by Tenzin Dorjee edited by Dexter Roberts +~ +Whatever appears, nothing has moved from the absolute nature. + +Decide that nothing is extraneous to the absolute nature, taking the +example of gold jewelry. + Once we know how to remain in the absolute nature, the manifold thoughts +that arise in the mind are no different from gold jewelry. One can make all +sorts of things out of gold, such as earrings, bracelets, and necklaces, but +although they have a variety of different shapes, they are all made of gold. +Likewise, if we are able to not move from the absolute nature, however many +thoughts we might have, they never depart from the recognition of the absolute +nature. A yogi for whom this is the case never departs from that realization, +whatever he does with his body, speech, and mind. All his actions arise as +the outer display or ornament of wisdom. All the signs one would expect from +meditating on a deity come spontaneously without him actually doing any formal +practice. The result of mantra recitation is obtained without his having to +do a large number of recitations. In this way everything is included in the +recognition that nothing is ever extraneous to the absolute nature. + In that state one does not become excited at pleasant events or depressed +by unpleasant ones. + -- Dilgo Khyentse Rinpoche, "Zurchungpa's Testament: A Commentary on + Zurchung Sherab Trakpa's 'Eighty Chapters of Personal Advice'", based + on Shechen Gyaltsap's Annotated Edition, translated by the Padmakara + Translation Group, published by Snow Lion Publications +~ +crashola in second life: +due to having been in a vehicle which crossed into forbidden land and got +taken away, my avatar was left in an indetermine and very unhealthy state. +i was unable to move, deep underground, and i saw this object off down to +my left, so i clicked on it and picked 'edit'. i realized as i was doing +it, that this was my disembodied hair, which had flown off for some reason. +once i clicked edit, *crunch*, no more second life. +~ +It makes no sense to brood anxiously on the harmful actions we have committed +in the past to the point where we become paralyzed. They are done, it is +over. If the person is a believer in God, the appropriate action is to find +some means of reconciliation with Him. So far as Buddhist practice is +concerned, there are various rites and practices for purification. When the +individual has no religious beliefs, however, it is surely a matter of +acknowledging and accepting any negative feelings we may have in relation to +our misdeeds and developing a sense of sorrow and regret for them. But then, +rather than stopping at mere sorrow and regret, it is important to use this as +the basis for resolve, for a deep-seated commitment never again to harm others +and to direct our actions all the more determinedly to the benefit of others. +The act of disclosure, or confession, of our negative actions to another-- +especially to someone we really respect and trust--will be found to be very +helpful in this. We are quite wrong if we merely acknowledge the gravity of +our actions inwardly and then, instead of confronting our feelings, give up +all hope and do nothing. This only compounds the error. Above all, we should +remember that as long as we retain the capacity of concern for others, the +potential for transformation remains. + -- His Holiness the Dalai Lama, "Ethics for the New Millennium" +~ + According to Buddhism, compassion is an aspiration, a state of mind, +wanting others to be free from suffering. It's not passive--it's not empathy +alone--but rather an empathetic altruism that actively strives to free others +from suffering. Genuine compassion must have both wisdom and lovingkindness. +That is to say, one must understand the nature of the suffering from which we +wish to free others (this is wisdom), and one must experience deep intimacy +and empathy with other sentient beings (this is lovingkindness). Let's +examine these two elements. + The suffering from which we wish to liberate other sentient beings, +according to Buddha's teachings, has three levels. The first level includes +the obvious physical and mental sensations of pain and discomfort that we can +all easily identify as suffering. This kind of suffering is primarily at the +sensory level--unpleasant or painful sensations and feelings. The great +Tibetan master Panchen Losang Chokyi Gyaltsan, tutor to the fifth Dalai Lama, +reminds us that even animals seek to avoid physical suffering and pain. + The second level of suffering is the suffering of change. Although +certain experiences or sensations may seem pleasurable and desirable now, +inherent within them is the potential for culminating in an unsatisfactory +experience. Another way of saying this is that experiences do not last +forever; desirable experiences will eventually be replaced by a neutral +experience or an undesirable experience. If it were not the case that +desirable experiences are of the nature of change, then, once having a happy +experience, we would remain happy forever! In fact, if desirability were +intrinsic to an experience, then the longer we remained in contact with it, +the happier we would become. However, this is not the case. In fact, often, +the more we pursue these experiences, the greater our level of +disillusionment, dissatisfaction, and unhappiness becomes. + ...But the third level of suffering is the most significant--the pervasive +suffering of conditioning. This refers to the very fact of our unenlightened +existence, the fact that we are ruled by negative emotions and their +underlying root cause, namely our own fundamental ignorance of the nature of +reality. Buddhism asserts that as long as we are under the control of this +fundamental ignorance, we are suffering; this unenlightened existence is +suffering by its very nature. + If we are to cultivate the deepest wisdom, we must understand suffering at +its deepest, most pervasive level. In turn, freedom from that level of +suffering is true nirvana, true liberation, the true state of cessation. +Freedom from the first level of suffering alone--merely being free of +unpleasant physical and psychological experiences--is not true cessation of +suffering. Freedom from the second level is again not true cessation. +However, freedom from the third level of suffering--being completely free from +the very source of suffering--that is genuine cessation, genuine liberation. + -- H.H. the Dalai Lama, "Essence of the Heart Sutra: The Dalai Lama's Heart + of Wisdom Teachings", translated & edited by Geshe Thupten Jinpa +~ + The only conclusion that can legitimately be reached is that the self is a +fiction, a mere label superimposed onto the aggregates, a concept created and +reified by the mind but lacking any substantial reality. This reasoning +process alone does not eliminate the idea, however; it merely weakens it. +Because it is so deeply ingrained, the idea of self is only eliminated through +repeated meditation on the reasonings of no-self, which enable the yogin to +become progressively more familiar with the understanding that no self or +essence exists. The Dalai Lama concludes that "when such a realization is +maintained and reinforced through constant meditation and familiarization, you +will be able to develop it into an intuitive or direct experience." (From Path +to Bliss.) + Many Westerners reject this notion, contending that it would be a sort of +cognitive suicide. The idea that the self (which is assumed even by people +who reject religions that propound the idea) does not exist is profoundly +disturbing to many non-Buddhists, but in Buddhist thought the denial of self +is not seen as constituting a loss, but rather is viewed as a profoundly +liberating insight. Since the innate idea of self implies an autonomous, +unchanging essence, if such a thing were in fact the core of one's being, it +would mean that change would be impossible, and one would be stuck being just +what one is right now. Because there is no such self, however, we are open +toward the future. One's nature is never fixed and determined, and so through +engaging in Buddhist practice one can exert control over the process of change +and progress in wisdom, compassion, patience, and other good qualities. One +can even become a buddha, a fully awakened being who is completely liberated +from all the frailties, sufferings, and limitations of ordinary beings. But +this is only possible because there is no permanent and static self, no soul +that exists self-sufficiently, separated from the ongoing process of change. + -- John Powers, "A Concise Introduction to Tibetan Buddhism", published + by Snow Lion Publications +~ + I often encounter people in and our of my office who seem to be lost in +thought. I sometimes ask them what they are thinking about. They are usually +startled by the question. They look at me blankly and are often surprised to +hear themselves admit with embarrassment that they don't know or can't say. +Or they describe one small, fleeting fragment of disconnected thought. The +"normal" human state of mind is constant, incessant thinking--an enigmatically +linked stream of consciousness, sensations, memories, feelings, desires, +fears, and chatter. And at the center of the narrative, the star of the show +is always--ME! This is why the first leg of the journey requires courage. To +become familiar with the chaotic, egotistical, and often nonsensical narrative +of our own mind stream is disconcerting and painful. To discover directly +that we are literally "lost in thought" can be frightening. But this is where +we are and where we must begin. + It's consoling to remember that everyone is neurotic, each one of us. The +"normal" mind suffers from a complex of conflicting desires and aversions. +The best we can do is to become aware of our neuroses, to become wiser in our +thinking and our conduct of life. In my experience, meditation is the most +direct and efficient method for developing self-awareness. Self-awareness is +not a steady state because experience is not a steady state. Through the +practice of meditation, we can learn to watch our ever-fluctuating mental +processes from a more detached, aerial perspective. Without necessarily +understanding ourselves in some intellectual way, we can directly discover how +the mind works. The mind has its causes and effects, its motivations and +intentions, and its awareness and evaluation of their possible consequences. + -- Ron Leifer, M.D., "Vinegar Into Honey: Seven Steps to Understanding and + Transforming Anger, Aggression, and Violence", published by Snow Lion + Publications +~ + "The best thing for being sad," replied Merlin, beginning to puff and blow, +"is to learn something. That's the only thing that never fails. You may grow +old and trembling in your anatomies, you may lie awake at night listening to +the disorder of your veins, you may miss your only love, you may see the world +about you devastated by evil lunatics, or know your honour trampled in the +sewers of baser minds. There is only one thing for it then--to learn. Learn +why the world wags and what wags it. That is the only thing which the mind can +never exhaust, never alienate, never be tortured by, never fear or distrust, +and never dream of regretting. Learning is the only thing for you. Look what +a lot of things there are to learn." + -- T. H. White, "The Once and Future King" +~ + In Chapter 4 we looked at the when and where of meditation. Whatever +works best for you, given your personal circumstances and temperament, the +important thing is to do it regularly, preferably every day. + I would also recommend that you keep the session to a length of time that +feels comfortable. This is because in the early stages of meditation it's +easy to become discouraged and have thoughts along the lines of: "This might +work for other people, but I don't have the right personality/mind/lifestyle/ +partner for meditation." Or: "I've been doing this for six months and my +concentration is no better than when I started." With thoughts like these, you +may start to resent the time you spend meditating and consider giving up. + Much better to keep your practice light and easy to begin with; short +sessions, and concentrated attention, especially towards the end of your +practice so that you "finish like a winner" and feel encouraged for the next +day. Better to end a short session thinking you could have gone on longer +than keep glancing at your watch with the thought that has passed through the +mind of every meditator at some stage--"My watch must have stopped. It's been +longer than two minutes--surely?!" + Having reviewed the meditation practices outlined in the previous chapter, +you may decide you quite like the sound of several of them. On what basis +should they be practiced? My own preference is to have a simple calendar of +activity so that, for example, Mondays, Wednesdays, and Fridays are breath- +counting days; Tuesdays, Thursdays, and Saturdays are visualization days; and +Sundays are for whatever I'm in the mood to do. + On this point, I once asked a high-ranking Tibetan lama which of a number +of meditation practices I should focus on. He gave me an indulgent smile and +said simply, "Whichever you enjoy the most." D'oh! + -- David Michie, "Hurry Up and Meditate: Your Starter Kit for Inner Peace + and Better Health", published by Snow Lion Publications +~ +"Always be sustained by cheerfulness." + + The effectiveness of our practice can be measured by looking at our mood. +If we are in better spirits, the practice is working. We can take heart +because we have a purpose, to exchange whatever sadness we meet for joy. The +smallest personal damage can be put to use to dissolve great suffering and do +away with negativity. If there is a way, we try to stop unfortunate things +from happening, but when unhappy events occur we meet them optimistically. We +never let negativity discourage us or injure our ability to help. + Setting out on any adventure demands determination. We may have to toil +and struggle with setbacks along the way but the trials we face are short- +lived. We can endure them because we have a great end in mind: to benefit all +sentient beings. Remaining good-natured and enthusiastic shows that our +efforts are succeeding. Being cheerful is the sign of a good practitioner. + -- Ringu Tulku, "Mind Training", edited by B.M. Shaughnessy, published + by Snow Lion Publications +~ + Once we take ourselves and the quality of our life seriously, and +acknowledge the difficulties we may be experiencing, the next step is to have +confidence that (1) it is possible to overcome them, (2) there is a way to +accomplish this, and (3) we are capable of achieving it [Buddha-nature]. This +bring us to the topics of refuge and Buddha-nature. + Taking refuge is not a passive act of placing ourselves in the hands of a +higher power that will do everything for us, as the English word "refuge" +might imply. It is an active process of putting a safe, reliable and positive +direction in our life. That direction is indicated by the Buddhas, the Dharma +and the Sangha--the Three Precious Gems. They are precious in the sense that +they are both rare and valuable.... + In short, the definitive level of the Three Precious Gems of Buddha, +Dharma and Sangha presents the goal we would like to achieve. Their +interpretable level indicates what we rely on, externally, to bring ourselves +there. But we also have internal factors that we need to rely on as well. +These refer to our Buddha-nature. + We are capable of eliminating our problems and achieving the definitive +Three Precious Gems because everyone has Buddha-nature, namely the various +factors or working materials that make it possible. Of all our natural +resources, the most important is mind. We all have a mind which, in its +nature, is unhampered by anything from experiencing whatever exists. No +matter what happens--no matter how confused, stressed or unhappy we may be--we +experience it. Even death is something that we experience when it occurs. +Therefore, because we have a mind that allows us to experience whatever +exists, we have the basic resource that allows us to experience a total +absence of confusion and a utilization of all possible good qualities for +helping others--provided that such a total absence and utilization actually +exist. In other words, if we can establish that it is possible for these two +things to exist--and that they are not just objects of nice but totally +unrealistic wishes--we can be confident that we are capable of attaining them, +simply because we have a mind. + -- H.H. the Dalai Lama and Alexander Berzin, "The Gelug/Kagyu Tradition + of Mahamudra", published by Snow Lion Publications +~ + Actually, we Buddhists are supposed to save all sentient beings, but +practically speaking, this may be too broad a notion for most people. In any +case, we must at least think in terms of helping all human beings. This is +very important. Even if we cannot think in terms of sentient beings +inhabiting different worlds, we should nonetheless think in terms of the human +beings on our own planet. To do this is to take a practical approach to the +problem. It is necessary to help others, not only in our prayers, but in our +daily lives. If we find we cannot help another, the least we can do is to +desist from harming them. We must not cheat others or lie to them. We must +be honest human beings, sincere human beings. + On a very practical level, such attitudes are things which we need. +Whether one is a believer, a religious person, or not, is another matter. +Simply as an inhabitant of the world, as a member of the human family, we need +this kind of attitude. It is through such an attitude that real and lasting +world peace and harmony can be achieved. Through harmony, friendship, and +respecting one another, we can solve many problems in the right way, without +difficulties. + -- H.H. the Dalai Lama, "Answers: Discussions with Western Buddhists", + edited by Jose Ignacio Cabezon, published by Snow Lion Publications +~ +We may have all come on different ships, but we're all in the same boat now. + -- Dr. Martin Luther King, Jr. +~ +...Maitreya, in his text the Sublime Continuum, gives three reasons on the +basis of which one can conclude that the essence of Buddhahood permeates the +minds of all sentient beings. First, he says that the Buddha's activities +radiate in the heart of all sentient beings. Now this can be understood in +two different ways: one is that we can understand that in every sentient being +there is a seed of virtue, and one could see the seed of virtue as an act of +the completely enlightened, compassionate Buddha. But one could also see it +in deeper terms, that is, that all sentient beings possess the potential for +perfection. Therefore, there is a kind of perfected being inherent within all +sentient beings, radiating. So one can understand it in these ways. Second, +so far as the ultimate nature of reality is concerned, there is total equality +between the samsaric state and nirvana. Third, we all possess a mind which +lacks intrinsic reality and independent existence, which allows us to then +remove the negativities and delusory states that obscure it. For these three +reasons, Maitreya concludes that all sentient beings possess the essence of +Buddhahood. + -- H.H. the Dalai Lama, "Healing Anger: The Power of Patience from a + Buddhist Perspective", translated by Geshe Thupten Jinpa, published by + Snow Lion Publications +~ + ...Compassion diminishes fright about your own pain and increases inner +strength. It gives you a sense of empowerment, of being able to accomplish +your tasks. It lends encouragement. + Let me give you a small example. Recently, when I was in Bodh Gaya, I +fell ill from a chronic intestinal infection. On the way to the hospital, the +pain in my abdomen was severe, and I was sweating a great deal. The car was +passing through the area of Vulture Peak (Buddha taught here) where the +villagers are extremely poor. In general, Bihar State is poor, but that +particular area is even more so. I did not even see children going to or +coming from school. Just poverty. And sickness. I have a very clear memory +of a small boy with polio, who had rusty metal braces on his legs and metal +crutches up to his armpits. It was obvious that he had no one to look after +him. I was very moved. A little later on, there was an old man at a tea +stop, wearing only a dirty piece of cloth, fallen to the ground, left to lie +there with no one to take care of him. + Later, at the hospital, my thoughts kept circling on what I had seen, +reflecting on how sad it was that here I had people to take care of me but +those poor people had no one. That is where my thoughts went, rather than to +my own suffering. Though sweat was pouring out of my body, my concern was +elsewhere. + In this way, though my body underwent a lot of pain that prevented sleep +(a hole had opened in my intestinal wall), my mind did not suffer any fear or +discomfort. It would only have made the situation worse if I had concentrated +on my own problems. This is an example from my small experience of how an +attitude of compassion helps even oneself, suppressing some degree of physical +pain and keeping away mental distress, despite the fact that others might not +be directly helped. + Compassion strengthens your outlook, and with that courage you are more +relaxed. When your perspective includes the suffering of limitless beings, +your own suffering looks comparatively small. + -- His Holiness the Dalai Lama, "Mind of Clear Light: Advice on Living + Well and Dying Consciously", translated and edited by Jeffrey Hopkins, + Ph.D. +~ + ...blissful light, with a Chenrezig on the tip of each ray, streams out of +you and touches each and every sentient being--those whom you like, those whom +you don't, and those you don't know. When this glowing light touches each +sentient being, it performs two functions: it purifies them of their +negativities, and it inspires them to realize all the stages of the path to +enlightenment. We may start imagining the light touching the beings in the +room and gradually spreading out to those in the area, the country, the +continent, the world, and the universe. Or we can start with our friends and +family, then radiate light to strangers, and finally to those who have harmed +us or of whom we're afraid. Or, we can first radiate light to human beings, +then animals, hungry ghosts, hell beings, demi-gods, and gods. We can use our +creativity and imagination when doing this visualization. Each meditation +session can have a different emphasis. + It's very easy to love sentient beings in a general way. But it's more +effective to be specific in our visualizations. Send light to the guy who cut +you off on the highway. Send light to the IRS employee who questioned your +tax return. Send light to the terrorist who thinks that killing others in the +name of God will cause him to be reborn in heaven. Send light to government +leaders who think that bombing others solves problems. Send light to your +teenager who leaves his room a mess and gets mad when you comment on it. Send +light to specific people you know and care about, people who are having +problems, strangers, and people you don't like. Send it to hospitals, the +Middle East, the inner cities, and Beverly Hills. There's suffering +everywhere. The light frees sentient beings from their suffering. + -- Thubten Chodron, "Cultivating a Compassionate Heart: The Yoga Method + of Chenrezig", foreword by H.H. the Dalai Lama, published by Snow Lion + Publications +~ + In Tibetan drenpa means "mindfulness," and sheshin means "awareness." +Drenpa also means "mindfulness and memory." It means that one is mindful of +what one is doing and remembers what one has to do whether one is meditating, +whether one has lost the power of concentration, and so on. Mindfulness is +like a causal condition and awareness is like the result. If one has very +concentrated mindfulness, one immediately notices a thought arising and this +becomes awareness, which becomes sheshin, and one knows what is occurring. +Normally, one does not know what is in one's mind or what one is thinking, so +there is no awareness. But if one has mindfulness, then it is said to the +extent that mindfulness brings mental stability, one has awareness. So when +one has mindfulness, it is through one's awareness of what is happening. + At this level of pacification we become aware of the negative qualities of +distraction. Santideva explains this by saying that when the mind is +distracted, it is between the fangs of the wild animal of the kleshas +[emotional obscurations], and from mental distractions come all the +difficulties and mental hardships of this and future lives. Being in a state +of distraction will increase the negative qualities of the mind more and more. +However, being aware of the negative qualities motivates us to meditate. + -- Khenchen Thrangu Rinpoche, "The Practice of Tranquillity and Insight: + A Guide to Tibetan Buddhist Meditation", translated by Peter Roberts, + published by Snow Lion Publications +~ + Developing a flexible approach to living is not only instrumental in +helping us cope with everyday problems--it also becomes the cornerstone for a +key element of a happy life: balance. + Settling comfortably into his chair one morning, the Dalai Lama explained +the value of leading a balanced life. + "A balanced and skillful approach to life, taking care to avoid +extremes, becomes a very important factor in conducting one's everyday +existence. It is important in all aspects of life. For instance, in planting +a sapling of a plant or a tree, at its very early stage you have to be very +skillful and gentle. Too much moisture will destroy it, too much sunlight +will destroy it. Too little will also destroy it. So what you need is a very +balanced environment where the sapling can have a healthy growth. Or, for a +person's physical health, too much or too little of any one thing can have +destructive effects. For example, too much protein I think is bad, and too +little is bad. + "This gentle and skillful approach, taking care to avoid extremes, +applies to healthy mental and emotional growth as well." + -- His Holiness the Dalai Lama and Howard C. Cutler, M.D., "The Art of + Happiness: A Handbook for Living" +~ + ...Idle talk is usually considered a destructive action because it wastes +our time. But if our friend is depressed and can't listen to wise advice, we +can joke, tell silly stories, and use small talk to lighten his mood. Because +our motivation is kind, our joking and chatting are positive. + Laughing and having a good time aren't in opposition to Dharma. The more +we leave behind attachment, anger, jealousy, and pride, the more we'll enjoy +whatever we're doing. Our hearts will open to others and we can laugh and +smile with ease. The holy beings I've been fortunate to meet have a wonderful +sense of humor and are very friendly. + In Buddhist groups, it's important for people to get to know each other +and have a sense of fellowship. We can share experiences with our Dharma +friends and encourage each other on the path. Buddhism isn't an isolated +path, and it's important for Buddhists to cultivate group unity and +companionship. + It's not beneficial to retreat inside ourselves, thinking, "Every time I +talk to someone I'm motivated by attachment. Therefore I'll concentrate on +meditation and chanting and won't socialize with others." One of the +fundamental principles of Buddhism is care and compassion for others. +Although at times we may need to distance ourselves from others in order to +settle our own minds, whenever possible we should actively develop genuine +love for others. To do this, we must be aware of what's happening in others' +lives, care about them as we do ourselves, and offer help whenever possible. +Our ability to act with love develops with time and practice, and it has to be +balanced with our need for private contemplation. + -- Bhikshuni Thubten Chodron, "Taming the Mind", published by Snow Lion + Publications +~ +Young men and young women may work systematically six days in the week and +rise fresh in the morning, but let them attend modern dances for only a few +hours each evening and see what happens. The Waltz, Polka, Gallop and other +dances of the same kind will be disastrous in their effects to both sexes. +Health and vigor will vanish like the dew before the sun. It is not the +extraordinary exercise which harms the dancer, but rather the coming into +close contact with the opposite sex. It is the fury of lust craving +incessantly for more pleasure that undermines the soul, the body, the sinews +and nerves. Experience and statistics show beyond doubt that passionate +excessive dancing girls can hardly reach twenty-five years of age and men +thirty-one. Even if they reached that age they will in most instances be +broken in health physically and morally. This is the claim of prominent +physicians in this country. + -- Quote from a 1910 periodical. +~ +Why I Can't Go Out With You: I'd LOVE to, but... + -- I'm trying to see how long I can go without saying yes. + -- I'm attending the opening of my garage door. + -- The monsters haven't turned blue yet, and I have to eat more dots. + -- I'm converting my calendar watch from Julian to Gregorian. + -- I have to fulfill my potential. + -- I don't want to leave my comfort zone. + -- It's too close to the turn of the century. + -- I have to bleach my hare. + -- I'm worried about my vertical hold knob. + -- I left my body in my other clothes. +~ + I remember most vividly my first lesson on epistemology as a child, when I +had to memorize the dictum "The definition of the mental is that which is +luminous and knowing." Drawing on earlier Indian sources, Tibetan thinkers +defined consciousness. It was years later that I realized just how +complicated is the philosophical problem hidden behind this simple +formulation. Today when I see nine-year-old monks confidently citing this +definition of consciousness on the debating floor, which is such a central +part of Tibetan monastic education, I smile. + These two features--luminosity, or clarity, and knowing, or cognizance-- +have come to characterize "the mental" in Indo-Tibetan Buddhist thought. +Clarity here refers to the ability of mental states to reveal or reflect. +Knowing, by contrast, refers to mental states' faculty to perceive or +apprehend what appears. All phenomena possessed of these qualities count as +mental. These features are difficult to conceptualize, but then we are +dealing with phenomena that are subjective and internal rather than material +objects that may be measured in spatiotemporal terms. Perhaps it is because +of these difficulties--the limits of language in dealing with the subjective-- +that many of the early Buddhist texts explain the nature of consciousness in +terms of metaphors such as light, or a flowing river. As the primary feature +of light is to illuminate, so consciousness is said to illuminate its objects. +Just as in light there is no categorical distinction between the illumination +and that which illuminates, so in consciousness there is no real difference +between the process of knowing or cognition and that which knows or cognizes. +In consciousness, as in light, there is a quality of illumination. + -- H.H. the Dalai Lama, "The Universe in a Single Atom: Convergence + of Science and Spirituality" +~ + False conceptions are exaggerated modes of thought that do not accord with +the facts. Even if an object--an event, a person, or any other phenomenon-- +has a slightly favorable aspect, once the object is mistakenly seen as +existing totally from its own side, true and real, mental projection +exaggerates its goodness beyond what it actually is, resulting in lust. The +same happens with anger and hatred; this time a negative factor is +exaggerated, making the object seem to be a hundred percent negative, the +result being deep disturbance. Recently, a psychotherapist told me that when +we generate anger, ninety percent of the ugliness of the object of our anger +is due to our own exaggeration. This is very much in conformity with the +Buddhist idea of how afflictive emotions arise. + At the point when anger and lust are generated, reality is not seen; +rather, an unreal mental projection of extreme badness or extreme goodness is +seen, evoking twisted, unrealistic actions. All of this can be avoided by +seeing the fuller picture revealed by paying attention to the dependent- +arising of phenomena, the nexus of causes and conditions from which they arise +and in which they exist. + Looked at this way, the disadvantages of afflictive emotions are obvious. +If you want to be able to perceive the actual situation, you have to quit +voluntarily submitting to afflictive emotions, because in each and every +field, they obstruct perception of the facts..... + Love and compassion also involve strong feelings that can even make you +cry with empathy, but they are induced not by exaggeration but by valid +cognition of the plight of sentient beings, and the appropriateness of being +concerned for their well-being. These feelings rely on insight into how +beings suffer in the round of rebirth called "cyclic existence," and the depth +of these feelings is enhanced through insight into impermanence and +emptiness.... Though it is possible for love and compassion to be influenced +by afflictive emotions, true love and compassion are unbiased and devoid of +exaggeration, because they are founded on valid cognition of your relationship +to others. The perspective of dependent-arising is supremely helpful in +making sure that you appreciate the wider picture. + -- His Holiness the Dalai Lama, "How to See Yourself As You Really Are", + translated and edited by Jeffrey Hopkins, Ph.D. +~ +The Prajna Paramita is a very profound philosophical doctrine, and I will just +outline the main ideas in it in order to clarify the Chod. First we start off +with the confused egocentric state of mind. This state of mind causes us to +suffer, and so, to alleviate the suffering, we start to practice meditation. +What happens in meditation is that the speedy mind begins to slow down and +things begin to settle, like the mud sinking to the bottom of a puddle of +water when it is left undisturbed. When this settling has occurred, a kind of +clear understanding of the way things work in the mind takes place. This +understanding is prajna, profound cognition. Then, according to Buddhist +doctrine, through the use of this prajna, we begin to see that, in fact, +although we think that we have a separate and unique essence, or self, which +we call the "ego," when we look closely, we are a composite of form, sense- +perceptions, consciousness, etc., and are merely a sum of these parts. This +realization is the understanding of sunyata, usually translated as emptiness, +or voidness. It means there is no self-essence, that we are "void of a self." +If we are void of a self, there is no reason to be egocentric, since the whole +notion of a separate ego is false. Therefore we can afford to be +compassionate, and need not continually defend ourselves or force our desires +onto others. + -- Tsultrim Allione, "Women of Wisdom", published by Snow Lion Publications +~ + There is a film called "Groundhog Day," which is really a Buddhist movie +because this is exactly what the plot is about. For those of you who haven't +seen it, it's about somebody who had to relive the same day again and again +until he got it right. He started out with an extremely negative attitude, +and so throughout the first day he created a lot of negative causes. People +related back to him from his own level of negativity, and so he had a very bad +day. Then the next day he had to experience the same day all over again. +Then again, and again. He became desperate to find a way out. He attempted +suicide many times, but the next morning, there he was again in the same room +and the same bed. The date hadn't changed, and the same song was playing on +the radio. His attitude underwent many, many changes, until in the end he +spent most of his time trying to help people. He forestalled tragedies he +knew were going to happen because he had lived the day over so many times, and +his whole attitude gradually turned around into working out ways to help +others. As his inner attitude transformed, the day gradually got better and +better. Finally, he was able to break through to a new day. + The important thing is how we respond to our situation. We can transform +anything if we respond in a skillful way. This is precisely what karma is +about. If we greet situations with a positive attitude, we will eventually +create positive returns. If we respond with a negative attitude, negative +things will eventually come our way. Unlike the scenario in the movie, it +doesn't always happen right away. We can be very nice people but still have +lots of problems. On the other hand, we can be awful people and have a +wonderful time. But from a Buddhist perspective, it's just a matter of time +before we receive the results of our conduct. And usually it is true that +people with a positive attitude encounter positive circumstances. Even if the +circumstances do not appear positive, they be transformed through a positive +view. On the other hand people with negative minds complain even when things +are going well. They also transform circumstances, but they transform +positive ones into negative ones! + Both our present and our future depend on us. From moment to moment, we +are creating our future. We are not a ball of dust tossed about by the winds +of fate. We have full responsibility for our lives. The more aware we +become, the more capable we are of making skillful choices. + -- Venerable Tenzin Palmo, "Reflections on a Mountain Lake: Teachings + on Practical Buddhism", published by Snow Lion Publications +~ + Why is endeavor necessary? If we consider material progress, we see that +research started by one person can always be continued by another. But this +is not possible with spiritual progress. The realization we talk about in the +Buddhadharma is something that has to be accomplished by the individual. No +one else can do it for us. Of course, it would be wonderful if in the future +we could attain realization through some sort of new injection or by means of +a new generation of computers, without having to go through any difficulties. +If we could be absolutely certain that such a time would come, we could simply +lie back and wait to get enlightened. But I doubt that this will ever happen. +It is better to make an effort. We have to develop endeavor. + I. + Thus with patience I will practice diligence, + For it is through zeal that I will reach enlightenment. + If there is no wind, then nothing stirs; + Neither is there merit where there is no diligence. + We can be patient in various ways, such as by not thinking ill of those +who harm us or by accepting suffering as the path. Of these two, the latter +is the more important for generating endeavor, and it is endeavor that enables +us to attain enlightenment. As Shantideva says, "It is through zeal that I +will reach enlightenment." In the same way that protecting a lamp from the +wind allows the flame to burn without flickering, endeavor enables the +virtuous mind to grow undisturbed. + What is endeavor? It is finding joy in doing what is good. To do that, +it is necessary to remove anything that counteracts it, especially laziness. +Laziness has three aspects: having no wish to do good, being distracted by +negative activities, and underestimating oneself by doubting one's ability. +Related to these are taking undue pleasure in idleness and sleep and being +indifferent to samsara as a state of suffering. + -- Tenzin Gyatso, His Holiness the Fourteenth Dalai Lama, "A Flash of + Lightning in the Dark of Night: A Guide to the Bodhisattva's Way of + Life", translated by The Padmakara Translation Group. +~ + Western women emerging from crisis situations often choose to live alone, +intuitively knowing that the confrontation with oneself that this brings will +lead to a deeper understanding. These women in our society (which sees them +as pitiable and unfortunate) can take strength from the stories of Tibetan +yoginis. + These Western women also seek the support of other women or +psychotherapists to help them to emerge from their descents, just as the +yoginis sought the guidance of their teachers and spiritual friends, and the +Greeks needed the help of the "therapeutes" [helpers] to make sense of the +memories they brought back from the oracle cave. + Speaking of the descent myth in terms of her experiences in controlled +therapeutic regressions, Jungian analyst M.L. Von Franz describes the descent +process in relation to the story of "The Handless Maiden": + In the Middle Ages there were many hermits, and in Switzerland there + were the so-called Wood Brothers and Sisters. People who did not want + to live a monastic life but who wanted to live alone in the forest had + both a closeness to nature and also a great experience of spiritual + inner life. Such Wood Brothers and Sisters could be personalities on + a high level who had a spiritual fate and had to renounce active life + for a time and isolate themselves to find their own inner relation to + God. It is not very different from what the shaman does in the Polar + tribes, or what medicine men do all over the world, in order to seek + an immediate personal religious experience in isolation. + ...If we avoid the descent because of fear of what we will discover about +ourselves in the "underworld," we block ourselves off from a powerful +transformative process. This process has been recognized by modern +psychologists and ancient mystery religions alike. + -- Tsultrim Allione, "Women of Wisdom", published by Snow Lion Publications +~ + The modes of thought in pride and in courageous thought are entirely +different. + Depression caused by disintegration of the ego probably comes from not +being able to posit a conventionally existent I. Still, when some +understanding of emptiness develops, you have a different feeling of I than +that to which you previously were accustomed. Our usual feeling is that the I +is something solid, really independent, and very forceful. Such no longer +remains, but at the same time there is a sense of a mere I that accumulates +karma and performs actions. Such a sense of self is not at all a source of +depression. + If you have difficulty positing a merely nominal I as well as merely +nominal cause and effect of actions--if you get to the point where if you +assert selflessness, you cannot posit dependent-arising--then it would be +better to assert dependent-arising and give up selflessness. Indeed, there +are many levels of understanding selflessness, and Buddha, out of great +skillfulness in method, taught many different schools of tenets that posit +coarser levels of selflessness for those temporarily unable to understand the +more subtle levels. It is not the case that only if the most profound level +is immediately accessible, it is suitable, and if it is not accessible, the +whole endeavor should be thrown away. You have to proceed step by step with +whatever accords with your level of mind. Between emptiness and dependent- +arising, you should value dependent-arising more highly. + -- H.H. the Dalai Lama of Tibet, "The Dalai Lama at Harvard: Lectures on the + Buddhist Path to Peace", translated and edited by Jeffrey Hopkins, + published by Snow Lion Publications +~ + Puba Supoche asked, "Dampa, tell me what it's like when you really +practice sincerely! I understand neither heads nor tails of it!" + Dampa said, "View is the destruction of extreme ideas regarding things! +Cutting pride of self with confidence is realization! Being without support +in luminosity is meditation! In insight, absence of recognition is the +innate! Finding nowhere to place the mind among shifting phenomena is +subsequent attainment! In their absence, there is no antidote but natural +intensity! Naked awareness without grasping is dharmakaya! Disappearance +without being anything is experience! Don't you wonder whether all this truly +exists?" + -- Padampa Sangye, "Lion of Siddhas: The Life and Teachings of Padampa + Sangye", translated by David Molk, with Lama Tsering Wangdu Rinpoche, + published by Snow Lion Publications +~ +[do you have any thoughts about how a person could go about increasing their +feeling of autonomy or freedom at work?] + + ...it will completely depend on the person's individual circumstances, +what position they are in. Let's take the example of a prisoner. Now of +course it is best not to be in prison, but even in that situation, where a +person may be deprived of freedom, he or she may discover small choices that +they are able to make. And even if somebody is in prison, with very rigid +rules, they can undertake some spiritual practices to try to lessen their +mental frustrations, try to get some peace of mind. So they can work on +internal development...if people can do this under the extreme conditions of +prison, in the workplace people may try to discover small things, small +choices that they can make in how to go about their work. And of course, +somebody may work on an assembly line with little variation in how to do their +tasks, but they still have other kinds of choices in terms of their attitudes, +how they interact with their co-workers, whether they utilize certain inner +qualities or spiritual strengths to change their attitude at work even though +the nature of the work may be difficult. Isn't it? So, perhaps that would +help. + Of course, when you are talking about rigid rules and lack of freedom, +that doesn't mean that you are required to blindly follow and accept +everything others tell you. In instances where the worker might be exploited, +where the employer thinks of nothing but profit and pays a small salary and +demands a lot of overtime, or where one may be asked to do things that are not +appropriate or are unethical, one should not simply think, "Well, this is my +karma," and take no action. Here it is not enough to think, "I should just be +content." + If there is injustice, then I think inaction is the wrong response. The +Buddhist texts mention what is called "misplaced tolerance," or "misplaced +forbearance." So...misplaced patience or forbearance refers to the sense of +endurance that some individuals have when they are subject to a very +destructive, negative activity. That is a misplaced forbearance and +endurance. Similarly, in the work environment, if there is a lot of injustice +and exploitation, then to passively tolerate it is the wrong response. The +appropriate response really is to actively resist it, to try to change this +environment rather than accept it. One should take some action...perhaps one +could speak with the boss, with the management, and try to change these +things. One needs to actively resist exploitation. And in some cases, one +may simply need to quit and to look for other work. + -- His Holiness the Dalai Lama, and Dr. Howard C. Cutler, M.D., + "The Art of Happiness at Work" +~ + The purpose of Buddha's coming to the world was for the sake of sentient +beings' attaining the wisdom that he achieved. The paths that he taught are +only a means leading to Buddhahood; he does not lead sentient beings with a +low vehicle that is not a method leading to Buddhahood. He establishes +sentient beings in the powers and so forth that exist in his own state. + + "Manjushri, all the doctrines that I teach to sentient beings are for + the sake of attaining omniscient wisdom. Flowing into enlightenment + and descending into the Mahayana, they are means of achieving + omniscience, leading completely to one place. Therefore, I do not + create different vehicles." + -- from "Chapter of the True One Sutra". + + -- H.H. the Dalai Lama, Tsong-ka-pa and Jeffrey Hopkins, "Tantra in Tibet", + published by Snow Lion Publications +~ + We are aiming to develop a strong feeling of love and compassion with +respect to everyone, but this cannot be done without first seeing an equality +of all beings throught meditatively cultivating equanimity. Otherwise, you'll +easily be able to generate love and compassion for friends and may be able to +extend a little of this to neutral people, but even minor enemies will remain +a huge problem. Thus at first it is necessary to recognize how friends, +neutral persons, and enemies are equal. + This is done in two ways. One way to break down rigid classifications of +people is by reflecting first with respect to friends, then neutral persons, +and then enemies: + "Just as I want happiness and don't want suffering, so this friend + wants happiness and doesn't want suffering. And equally, this neutral + person wants happiness and doesn't want suffering. And equally, this + enemy wants happiness and doesn't want suffering." + Another way is to reflect on what your relationships have been with others +over the course of lifetimes, beginning with neutral persons, then friends, +and finally enemies. An enemy in this lifetime wants to do you in, but over +the course of lifetimes was this person just an enemy? No. If you do not +believe in rebirth, utilize the rebirth game, the rebirth perspective, as a +technique for making your mind more flexible. + Either of these techniques will work: + - Reflecting on the similarity of yourself and others in the basic + aspiration to gain happiness and be rid of suffering. + + - Reflecting on the changeability of relationships over the course + of lifetimes. + -- Jeffrey Hopkins, "A Truthful Heart: Buddhist Practices for Connecting + with Others", foreword by His Holiness the Dalai Lama, published by + Snow Lion Publications +~ + What is wisdom? It is as explained in the perfection of supreme knowledge +teachings: all phenomena are free from elaborations, and when the perceiving +subject as well becomes equally free from elaborations, that is wisdom. In +particular, the wisdom of the buddha consists in the pacification of the +elaborations and their habitual tendencies in relation to suchness. It is the +inseparability of the expanse and wisdom. It is free from singularity and +multiplicity, quality and qualified. It realizes the nonduality of subjects +and objects. In it all phenomena--samsara and nirvana, faults and qualities, +and so on--are always undifferentiable and equal. Outside of that, there is +no way to posit wisdom. + In a nonanalytical context of repeating what others accept, we Followers +of the Middle Way describe knowable objects as existing. The wisdom of the +buddhas is the same. Since we speak of all phenomena as existing from the +perspective of others (even though from our own perspective they are free of +the elaborations of existence and nonexistence), it is unreasonable to debate +solely about the existence or nonexistence of the wisdom of buddhas. + -- "The Karmapa's Middle Way: Feast for the Fortunate by the Ninth Karmapa, + Wangchuk Dorje", trans. by Tyler Dewar, published by Snow Lion + Publications +~ +Well timed silence hath more eloquence than speech. -- Martin Fraqhar Tupper +~ +A diplomat is a man who says you have an open mind, +instead of telling you that you have a hole in the head. + -- Anonymous +~ +To achieve great things, two things are needed; +a plan, and not quite enough time. + -- Leonard Bernstein +~ +To bend a bamboo, start when it is a shoot. -- Malaysian Proverb +~ +Things turn out best for people who make the best of the way things turn out. + -- John Wooden +~ +No shade tree? Blame not the sun, but yourself. -- Chinese Proverb +~ +'Tis better to be silent and be thought a fool, +than to speak and remove all doubt. + -- Abraham Lincoln +~ +Art is either plagiarism or revolution. -- Paul Gauguin +~ + When this world initially formed, there seem to have been two types of +events or entities, one sentient, the other insentient. Rocks, for instance, +are examples of nonsentient entities. You see, we usually consider them to +have no feelings: no pains and no pleasures. The other type, sentient beings, +have awareness, consciousness, pains and pleasures. + But there needs to be a cause for that. If you posit there is no cause +for consciousness, then this leads to all sorts of inconsistencies and logical +problems. So, the cause is posited, established. It is considered certain. + The initial cause must be an independent consciousness. And on that basis +is asserted the theory of continuation of life after death. It is during the +interval when one's continuum of awareness departs from one's body at death +that the subtle mind, the subtle consciousness, becomes manifest. That +continuum connects one life with the next. + -- H.H. the Dalai Lama, "Consciousness at the Crossroads: Conversations + with the Dalai Lama on Brain Science and Buddhism", edited by Zara + Houshmand, Robert B. Livingston, and B. Alan Wallace, published by + Snow Lion Publications +~ + Channels and cakras represent the inner structure of the human body, +referred to in the tantric teachings as the 'vajra body'. 'Vajra' means +'indestructible', and 'vajra body' refers to the dimension of the three +fundamental components: the channels and cakras, the prana that flows through +them, and the bindu or thigle, the white and red seed-essences of the physical +body that form the basis for practices such as the Tummo. + In the tantras of the Upadesa section of Dzogchen, it is explained that +after the conception of a human being the first thing to develop is the navel +cakra. Then from this, through a channel, the head cakra develops followed by +the other main cakras of the throat and the heart. This channel or meridian, +known as the life-channel, develops into the spinal cord and spine. At the +same time it remains as the fundamental energy of the central channel. + The central channel, known as Uma in Tibetan, is connected with the two +lateral channels called Roma and Kyangma. The Roma channel, which is white +and corresponds to lunar energy, is on the right side in men and on the left +in women. Ro means 'taste', and the main function of this channel is to give +the sensation of pleasure. The Kyangma channel, red and corresponding to +solar energy, is on the left side in men and on the right in women. Kyang +means 'sole', and unlike the Roma, this channel is not connected with many +secondary channels. Control of this channel is fundamental in order to +cultivate the experience of emptiness. These are the characteristic features +of the two channels, which are related to the two principles of upaya or +method, and of prajna or energy. Method denotes everything pertaining to the +visible or material dimension; while 'prajna', which generally means +discriminating wisdom, in this context denotes the energy of emptiness that is +the base of any manifestation. + -- Chogyal Namkhai Norbu, "Yantra Yoga: The Tibetan Yoga of Movement", + translated by Adriano Clemente, published byy Snow Lion Publications +~ +Flowers leave some of their fragrance in the hand that bestows them. + -- Chinese proverb +~ +The taller the bamboo grows, the lower it bends. + -- Chinese proverb +~ +You can't depend on the man who made the mess to clear it up. + -- Indian proverb +~ +You will never be alone with a poet in your pocket. -- John Adams +~ +Kissing is like drinking salted water; you drink and your thirst increases. + -- Chinese Proverb +~ +The eye is a menace to clear sight, the ear is a menace to subtle hearing, the +mind is a menace to wisdom, every organ of the senses is a menace to its own +capacity.... Fuss, the god of the Southern Ocean, and Fret, the god of the +Northern Ocean, happened once to meet in the realm of Chaos, the god of the +center. Chaos treated them very handsomely and they discussed together what +they could doto repay his kindness. They had noticed that, whereas everyone +else had seven apertures, for sight, hearing, eating, breathing and so on, +Chaos had none. So they decided to make the experiment of boring holes in +him. Every day they bored a hole, and on the seventh day, Chaos died. + -- Chuang Tzu +~ + Everybody loves to talk about calm and peace, whether in a family, +national, or international context. But without inner peace how can we make +real peace? World peace through hatred and force is impossible. Even in the +case of individuals, there is no possibility to feel happiness through anger. +If in a difficult situation one becomes disturbed internally, overwhelmed by +mental discomfort, then external things will not help at all. However, if +despite external difficulties or problems, internally one's attitude is of +love, warmth, and kind-heartedness, then problems can be faced and accepted. + ----- + The necessary foundation for world peace and the ultimate goal of any new +international order is the elimination of violence at every level. For this +reason the practice of non-violence surely suits us all. It simply requires +determination, for by its very nature non-violent action requires patience. +While the practice of non-violence is still something of an experiment on this +planet, if it is successful it will open the way to a far more peaceful world +in the next century. + -- H.H. the Dalai Lama, "The Pocket Dalai Lama", compiled and edited by + Mary Craig +~ + On the tenth night of the twelfth month, Gyal Dawa, the girl, came again. +She said, "Don't neglect my request for a prayer. It is very important." +That's the dream she had. I thought, "I'll write it on the full moon day." +So on the night of the fourteenth I prayed with single-pointed devotion to +Guru Rinpoche to grant blessing that the prayer would be beneficial and then +fell asleep. Early in the morning of the fifteenth I dreamed I was sitting in +front of the shrine in a very large building that looked like a temple. +Suddenly a young white man dressed in white with his hair falling loosely over +his shoulder appeared at the entrance. He was playing the cymbals melodiously +and dancing the swirling, joyous dance of the Ging. He came closer and +closer, singing: + + If you want to establish the dharma, + Establish it in your mind. + In the depth of mind, you will find Buddha. + If you wish to visit the buddha fields, + Purify ordinary deluded attachment. + The perfectly comfortable buddha field is close by. + Develop the joyful effort to practice, + That is the essence of the teaching. + Without practice, who can gain the siddhis? + It is hard to see one's faults, + But to see them nakedly is powerful advice. + In the end when faults have been cleared away, + The enlightened qualities increase and shine forth. + + At the end of this he rolled his cymbals. Then he crashed them together, +and I awoke. After I woke up, I did not forget what he had said. I +understood it to have been advice on practicing what to accept and what to +reject. I was sad that although I had actually seen the face of my only +father, Guru Padmasambhava, I had not recognized him. + I, Jigdral Yeshe Dorje, old father of the Nyingma, wrote this from my own +experience. Sarva Mangalam [May all be auspicious]. + -- Khenpo Tsewaang Dongyal Rinpoche, "Light of Fearless Indestructible + Wisdom: The Life and Legacy of His Holiness Dudjom Rinpoche", published + by Snow Lion Publications +~ +The modern economy has no national boundaries. When we talk about ecology, +the environment, when we are concerned about the ozone layer, one individual, +one society, one country cannot solve these problems. We must work together. +Humanity needs more genuine cooperation. The foundation for the development +of good relations with one another is altruism, compassion, and forgiveness. +For small arguments to remain limited, in the human circle the best method is +forgiveness. Altruism and forgiveness are the basis for bringing humanity +together. Then no conflict, no matter how serious, will go beyond the bounds +of what is truly human. + -- "The Dalai Lama, A Policy of Kindness: An Anthology of Writings By and + About the Dalai Lama", compiled and edited bby Sidney Piburn, Foreword + by Sen. Claiborne Pell, published by Snow Lion Publications +~ + Creation in four vajra steps entails meditation on emptiness; generating a +moon, sun, and seed-syllable from which light emanates and then converges; the +full manifestation of the deity resulting from the convergence of the light +and transformation of the seed-syllable; and visualization of three syllables +at the deity's three places. [The syllables om, ah, and hum are imagined at +the forehead, throat, and heart, respectively.] + ...Kongtrul explains that all the varieties of the creation phase +incorporate the four key elements of form, imagination, result, and +transformative power. "Form" means meditating on forms that represent the +aspects of awakening and generating clear images of these forms, thereby +stopping impure appearances. "Imagination" means using the force of creative +imagination to convert the visualized forms of awakening into reality. +"Result" means meditating on the result, that is, the very goal to be +attained, and thereby achieving that goal. "Transformative power" means +turning the ordinary body and mind into pristine awareness by relying on the +transformative powers of awakened beings. Among these, Kongtrul points out, +the most important element for realization of the path is the transformative +power of the vajra master combined with one's own devotion to that master. + -- Jamgon Kongtrul Lodro Taaye, "The Treasury of Knowledge, Book Eight, + Part Three: The Elements of Tantric Practice", translated by Ingrid + Loken McLeod and Elio Guarisco, published by Snow Lion Publications +~ +From The Prayer Requested by Namke Nyingbo +by Padmasambhava + +All these things of the outer environment and the beings therein +That come into sight as the objects of your eyes like this, +They may appear, but leave them in the sphere free from clinging to a self. +Since they are pure of perceiver and perceived, they are the +luminous-empty body of the deity. +I pray to the guru in whom attachment is self-liberated, +I pray to Padmasambhava from Uddiyana. + +All these sounds, taken as pleasant or unpleasant, +That resound as the objects of your ears like this, +Leave them in the sphere of inconceivable, empty resonance. +Empty resonance, unborn and unceasing, is the Victor's speech. +I pray to the words of the Victor that resound and yet are empty, +I pray to Padmasambhava from Uddiyana. + +However these thoughts of afflictions' five poisons, +Which stir as objects in your mind like this, may appear, +Do not mess around with them through a mind that rushes ahead into the + future or lingers in the past. +Through leaving their movement in its own place, they uncoil as the dharmakaya. +I pray to the guru whose awareness is self-liberated, I pray to + Padmasambhava from Uddiyana. + +Grant your blessings that the mind stream of someone like me is liberated +Through the compassion of the Tathagatas of the three times, +So that objects, appearing as if perceived outside, become pure, +That my very mind, perceiving as if inside, becomes liberated, +And that, in between, luminosity will recognize its own face. + + -- "Straight from the Heart: Buddhist Pith Instructions", translated + and introduced by Karl Brunnholzl, published by Snow Lion Publications +~ + The actual method of cultivating the correct attitudes towards the +spiritual master is to practice contemplative meditation upon the guru's good +qualities and the beneficial effects that he or she introduces into one's +life. By reflecting again and again on the great kindness the guru performs, +a confidence suitable for spiritual training under him or her is born. This +process of reflecting on the role of the guru is important in the beginning as +well as in the higher practices, for as we sit in contemplation we become +faced with a stream of reactions, which if understood at an early stage can +clear the mind of much doubt, confusion and superstition. + The spiritual master is the source of all spiritual progress. In this +context, Geshe Potowa once said, "If even those who want to learn a common +worldly trade must study under a qualified teacher, how much more so must we +who seek enlightenment? Most of us have come from the lower realms and have +no background or experience in the paths and stages to enlightenment; and, if +we wish to gain this experience, why should we not study with someone +qualified to teach us the methods that develop it?" + In the beginning of his Great Exposition, Lama Tsongkhapa writes, "The +root of spiritual development is to cultivate an effective relationship with a +master." This means that we must cultivate the correct attitudes and then +demonstrate them correctly in action. This is the root that, if made strong, +supports the trunk, branches, leaves and flowers of practice. When the roots +of a tree are strong, the entire tree becomes strong, whereas when the roots +are weak, the entire tree will remain weak. + ...We should engender respect such that we see the guru as a Buddha. If +we can do this, then we experience the guru as we would a Buddha and +consequently are sufficiently inspired to practice what he or she teaches. +The instruction to see the guru as a Buddha is not unreasonable, for in many +ways the spiritual master is Buddha himself. + -- H.H. the Dalai Lama, "The Path to Enlightenment", edited and translated + by Glenn H. Mullin, published by Snow Liion Publications +~ + ...when you start practicing, you should not expect too much. We live in +a time of computers and automation, so you may feel that inner development is +also an automatic thing for which you press a button and everything changes. +It is not so. Inner development is not easy and will take time. External +progress, the latest space missions and so forth, have not reached their +present level within a short period but over centuries, each generation making +greater developments based on those of the previous generation. However, +inner development is even more difficult since internal improvement cannot be +transferred from generation to generation. Your past life's experience very +much influences this life, and this life's experience becomes the basis for +the next rebirth's development, but transference of inner development from one +person to another is impossible. Thus, everything depends on yourself, and it +will take time. + I have met Westerners who at the beginning were very enthusiastic about +their practice, but after a few years have completely forgotten it, and there +are no traces of what they had practiced at one time. This is because at the +beginning they expected too much. Shantideva's Engaging in the Bodhisattva +Deeds emphasizes the importance of the practice of patience--tolerance. This +tolerance is an attitude not only towards your enemy but also an attitude of +sacrifice, of determination, soo that you do not fall into the laziness of +discouragement. You should practice patience, or tolerance, with great +resolve. This is important. + -- The Fourteenth Dalai Lama, His Holiness Tenzin Gyatsoo, "Kindness, + Clarity, and Insight 25th Anniversary Edition", edited and translated + by Jeffrey Hopkins, co-edited by Elizabeth Napper, published by Snow + Lion Publications +~ + I take refuge until I am enlightened in the Buddhas, + the Dharma, and the Sangha. + By the positive potential I create by practicing generosity + and the other far-reaching attitudes (ethical discipline, + patience, joyous effort, meditative stabilization, and wisdom), + may I attain Buddhahood in order to benefit all sentient beings. + + It takes only a few moments to think in this way and to recite the prayer, +yet doing so has a significant effect on the rest of our day. We'll be more +cheerful and will be sure of our direction in life. Especially if we don't do +a regular meditation practice, starting the day in this way is extremely +beneficial. + In the evening, after reviewing the day's activities and freeing our minds +from any remaining afflictions that may have arisen during the day, we again +take refuge and generate the altruistic intention. + Before going to sleep, we can envision the Buddha, made of light, on our +pillow. Placing our head in his lap, we fall asleep amidst the gentle glow of +his wisdom and compassion. Instead, we can learn the guidelines and try to +implement them as much as we can, reviewing them periodically to refresh our +minds. We may choose one guideline to emphasize this week in our daily lives. +Next week, we can add another, and so on. In that way, we'll slowly build up +the good habits of practicing all of them. + -- Ven. Thubten Chodron, "Taming the Mind", published by Snow Lion + Publications +~ +Compassion is the wish for another being to be free from suffering; +love is wanting them to have happiness. + -- H.H. the Dalai Lama, "The Compassionate Life" +~ +The secret of health for both mind and body is not to mourn for the past, +worry about the future, or anticipate troubles, but to live in the present +moment wisely and earnestly. + -- Buddha +~ +Our patience will achieve more than our force. -- Edmund Burke +~ +The best measure of a man's honesty isn't his tax return, +it's the zero adjust on his bathroom scale. + -- Arthur C. Clarke +~ + Despite all the material progress in this and the last century we still +experience suffering, especially in relation to mental well-being. In fact, +if anything, the complex way of life created by modernisation or globalisation +is causing new problems and new causes of mental unrest. Under these +circumstances I feel that the various religious traditions have an important +role to play in helping to maintain peace and the spirit of reconciliation and +dialogue, and therefore harmony and close contact between them is essential. +Whether we are believers or non-believers and, within the category of the +believers, whether we hold this or that belief, we must respect all the +traditions. That's very important. + I always tell people in non-Buddhist countries that followers of other +religions should maintain their own tradition. To change religion is not +easy, and people can get into trouble as a result of confusion. So it is much +safer to keep to one's own tradition, while respecting all religions. I'm +Buddhist--sometimes I describe myself as a staunch Buddhist--but, at the same +time, I respect and admire the works of other traditionss' figures such as +Jesus Christ. Basically, all the religious traditions have made an immense +contribution to humanity and continue to do so, and as such are worthy of our +respect and admiration. + When we contemplate the diversity of spiritual traditions on this planet +we can understand that each addresses the specific needs of different human +beings, because there is so much diversity in human mentality and spiritual +inclination. Yet, fundamentally, all spiritual traditions perform the same +function, which is to help us tame our mental state, overcome our negativities +and perfect our inner potential. + -- H.H. the Dalai Lama, "Lighting the Way", translated by Geshe Thupten + Jinpa, published by Snow Lion Publications +~ + ...For one who abides in thought + Feats do not arise. + Therefore abandon thought + And think a mantra form. + +'Abandon thought' refers to the eradication of thought conceiving self +[inherent existence] through the wisdom of selflessness; it does not mean to +stop any and all types of thought. 'Think a mantra form' means to meditate on +a deity. The measure of firmness in deity yoga is indicated by 'whether +going, standing, or sitting is always immovable though moving about'. When +one has attained the capacity to hold the mind on the divine body in all types +of behaviour--whether in meditative equipoise or not--without moving to +something else, one has the capacity to remove the pride of ordinaariness. + -- His Holiness the Dalai Lama, Tsong-ka-pa, and Jeffrey Hopkins, + "Deity Yoga in Action and Performance Tantra", published by Snow + Lion Publications +~ + A person who is liberated, who has freed his or her mind of all mental +afflictions, still experiences physical suffering. The difference between us +and an arhat, a person who has freed the mind from mental affliction, is that +an arhat doesn't identify with pain. Arhats experience physical pain vividly +but don't grasp onto it; they can take action to avoid or alleviate pain, but +whether they do so or not, the physical pain doesn't come inside. What an +arhat does not experience is mental suffering. A buddha, one who is perfectly +spiritually awakened, has gone a further step. A buddha has no mental +suffering of his or her own, but is vividly and non-dually aware of the +suffering of others. + Superficially, the arhat who is free from mental suffering can seem to us +who lack this realization as numb and detached, in a state of existential +anesthesia. A buddha, one who is fully awakened, presents the paradox of +being free from suffering and also non-dually present with other people's joys +and sorrows, hopes and fears. A buddha taps into immutable bliss, the +ultimate ground state of awareness beyond the dichotomy of stimulus-driven +pain and pleasure. The mind of a buddha has been purified of all obscuration +and from its own nature there naturally arises immutable bliss, like a spring +welling up from the earth. With the unveiling of the buddha-nature of +unconditioned bliss, there is also a complete erosion of an absolute +demarcation between self and other. The barrier is gone. This is why buddhas +are vividly and non-dually aware of the suffering of others, their hopes and +fears, the whole situation, and at the same time are not disengaged from the +purity and bliss of their own awareness. The mind of a buddha doesn't block +out anything and nothing is inhibited, and this is why the awareness of an +awakened being is frequently described as "unimaginable." + -- B. Alan Wallace, "Buddhism with an Attitude: The Tibetan Seven-Point + Mind Training", published by Snow Lion Publications +~ + The fifth Tara is known as Wangdu Rigje Lhamo. She is Kurukulle in +Sanskrit and Rigjema or Rigje Lhamo in Tibetan. Wangdu means power of +"gathering, summoning," or "magnetizing." We can think of it as attracting +everything beneficial, to benefit all beings. Rigjema means "she who +precisely understands everything" and Lhamo is "divine lady." So she is known +as the Tara who precisely understands the power of magnetizing. + Kurukulle's practice is very extensively taught throughout Tibetan +Buddhism. She is often named the "Red Tara" because of her color. Her Praise +is: + CHAG TSHAL TUT TA RA HUNG YI GE + Homage, Mother, filling all regions, sky, and the realm of desire + + DO DANG CHOG DANG NAM KHA GANG MA + With the sounds of TUTTARA and HUNG, + + JIG TEN DUN PO ZHAB CHI NEN TE + Trampling the seven worlds with her feet, + + LU PA ME PAR GUG PAR NU MA + Able to summon all before her. + -- Khenchen Palden Sherab annd Khenpo Tsewang Dongyal, "Tara's Enlightened + Activity: An Oral Commentary on 'The Twenty-one Praises to Tara'", + published by Snow Lion Publications +~ +The best way to find yourself is to lose yourself in the service of others. + -- Gandhi +~ +Always bear in mind that your resolution to succeed +is more important than any one thing. + -- Abraham Lincoln +~ +Empty your mind, be formless, shapeless-like water. Now you put water into a +cup, it becomes the cup, you put water into a bottle, it becomes the bottle, +you put it in a teapot, it becomes the teapot. Now water can flow or it can +crash. Be water, my friend. + --Bruce Lee +~ +All fixed set patterns are incapable of adaptability or pliability. +The truth is outside of all fixed patterns. + --Bruce Lee +~ +A wise man can learn more from a foolish question +than a fool can learn from a wise answer. + --Bruce Lee +~ +The remembrance of youth is a sigh. -- Oriental Maxim +~ +Youth would be an ideal state if it came a little later in life. + -- Herbert Henry Asquith +~ +The freethinking of one age is the common sense of the next. + -- Matthew Arnold (1822-1888) +~ +What we play is life. -- Louis Armstrong +~ +Fight for your opinions, but do not believe that they contain +the whole truth, or the only truth. + -- Charles Dana +~ +Some say the glass is half empty, +some say the glass is half full, +I say, are you going to drink that? + -- Lisa Claymen +~ +Who looks outside, dreams; who looks inside, awakes. -- Carl Jung +~ +Since a politician never believes what he says, +he is quite surprised to be taken at his word. + -- Charles de Gaulle +~ +On the one hand information wants to be expensive, because it's so valuable. +The right information in the right place just changes your life. On the +other hand, information wants to be free, because the cost of getting it out +is getting lower and lower all the time. So you have these two fighting +against each other. + -- Stewart Brand at the first Hackers' Conference in 1984 +~ +Nature provides a free lunch, but only if we control our appetites. + -- William Ruckelshaus +~ +Those who get too big for their briches will be exposed in the end. -- Anon. +~ +We're not talking about the same thing," he said. "For you the world is weird +because if you're not bored with it you're at odds with it. For me the world +is weird because it is stupendous, awesome, mysterious, unfathomable; my +interest has been to convince you that you must accept responsibility for being +here, in this marvelous world, in this marvelous desert, in this marvelous +time. I wanted to convince you that you must learn to make every act count, +since you are going to be here for only a short while, in fact, too short for +witnessing all the marvels of it. + -- Don Juan, Yaqui Shaman +~ +Always and never are two words you should always remember never to use. + -- Wendell Johnson +~ +People prefer to believe what they prefer to be true. -- Francis Bacon +~ +The greatest glory in living lies not in never falling, +but in rising every time we fall. + -- Nelson Mandela +~ +Stapp's Ironical Paradox: +The universal aptitude for ineptitude makes any +human accomplishment an incredible miracle. + -- Col. John P. Stapp +~ +When the only tool you own is a hammer, +every problem begins to resemble a nail. + -- Abraham Maslow +~ +Peace is not a little white dove. It is you and me. -- Rigoberta Menchu Tum +~ +If you want to go quickly, go alone. If you want to go far, go together. + -- African proverb +~ +The reserve of modern assertions is sometimes pushed to extremes, +in which the fear of being contradicted leads the writer to strip +himself of almost all sense and meaning. + -- Sir Winston Churchill +~ +Why not go out on a limb? Isn't that where the fruit is? -- Frank Scully +~ +Money often costs too much. -- Ralph Waldo Emerson +~ +The pen is mightier than the sword, and considerably easier to write with. + -- Marty Feldman +~ +The true meaning of life is to plant trees +under whose shade you do not expect to sit. + -- Nelson Henderson +~ +The first human being who hurled an insult instead +of a stone was the founder of civilization. + -- Sigmund Freud +~ +When the weight of the world has got you down + and you want to end your life. +Bills to pay, a dead-end job, + and problems with the wife. +But don't throw in the tow'l, + 'cuz there's a place right down the block... +Where you can drink your misery away... +At Flaming Moe's.... (Let's all go to Flaming Moe's...) +When liquor in a mug (Let's all go to Flaming Moe's...) + can warm you like a hug. (Flaming Moe's...) +And happiness is just a Flaming Moe away... +Happiness is just a Flaming Moe away... + -- Flaming Moe's Theme Song, The Simpsons. +~ +Stop living for what's around the corner +and start enjoying the walk down the street. + -- Grant L. Miller +~ +Doubt is the beginning, not the end, of wisdom. -- George Iles +~ +One day Ananda, who had been thinking deeply about things for a while, turned +to the Buddha and exclaimed: "Lord, I've been thinking--spiritual friendship +is at least half of the spiritual life!" The Buddha replied: "Say not so, +Ananda, say not so. Spiritual friendship is the whole of the spiritual life!" + -- Samyutta Nikaya, Verse 2 +~ +There are moments when one feels free from one's own identification with human +limitations and inadequacies. At such moments one imagines that one stands on +some spot of a small planet, gazing in amazement at the cold yet profoundly +moving beauty of the eternal, the unfathomable; life and death flow into one, +and there is neither evolution nor destiny; only Being. + -- Albert Einstein +~ +A conference is a gathering of important people who singly can do nothing, but +together can decide that nothing can be done. + -- Fred Allen +~ +An undefined problem has an infinite number of solutions. + -- Robert A. Humphrey +~ +The most erroneous stories are those we think we know best -and therefore +never scrutinize or question. + -- Stephen Jay Gould +~ +41. One's own awareness, fresh and uncontrived + One's own awareness, fresh and uncontrived, + Is the primordially present ultimate Lama + From whom you have not been separated for even an instant. + This meeting with the original abiding nature--how amazing! +I, Jnana, wrote this in response to Changchub Palmo's request. + -- "Wisdom Nectar: Dudjom Rinpoche's Heart Advice", translated by + Ron Garry, a Tsadra Foundation Series book, published by Snow Lion +~ + I hunted up statistics, and was amazed to find that after all the glaring +newspaper headings concerning railroad disasters, less than three hundred +people had really lost their lives by those disasters in the preceding twelve +months. The Erie road was set down as the most murderous in the list. It had +killed forty-six.or twenty-six, I do not exactly remember which, but I know +the number was double that of any other road. But the fact straightway +suggested itself that the Erie was an immensely long road, and did more +business than any other line in the country; so the double number of killed +ceased to be matter for surprise. + By further figuring, it appeared that between New York and Rochester the +Erie ran eight passenger trains each way every day.sixteen altogether; and +carried a daily average of 6,000 persons. That is about a million in six +months.the population of New York city. Well, the Erie kills from thirteen +to twenty-three persons out of its million in six months; and in the same +time 13,000 of New York's million die in their beds! My flesh crept, my hair +stood on end. + "This is appalling!" I said. "The danger isn't in travelling by rail, but +in trusting to those deadly beds. I will never sleep in a bed again." + --Mark Twain on Risk Analysis, 1871 +~ +The activities of this degenerate age are like a madman's performance of dance. +No matter what we do, there is no way to please others. +Think about what is essential. +This is my heart's advice. + --Bhande Dharmaradza + + In any group of people, there is always some misunderstanding. You cannot +satisfy everyone, no matter what you do. The Bodhicaryavatara says that every +individual has a different way of thinking. Thus, it is very difficult to +please everyone. Even the Buddha could not do it, so how can we? Instead of +trying to please others, please yourself by applying yourself fully to +bodhicitta. + Investigate your situation carefully, according to the Dharma. For us, it is +more important to know what is best than to know how to please everyone. Know +what is right, and on the basis of your own wisdom and skill, just do it. +Don't expect that other people will be pleased with you or that they will be +happy about what you do. Rather, do what's best, what's helpful for yourself +and for others. If they are happy about it, that's fine. If they are not +happy, what can you do? + -- "A Complete Guide to the Buddhist Path", by Khenchen Konchog Gyaltshen, + edited by Khenmo Trinlay Chodron, published by Snow Lion Publications +~ +Lochen Gyurme Dechen, nephew of the great accomplished master Tangtong +Gyalpo, sang this song, a prayer of the Six Doctrines, called "The Rain +of Great Bliss": + +Nama Shri Jnana Daki Nigupta-ye! + + Lady of the celestial realms, compassionate one, + Chief of wisdom dakinis, Niguma, + When I, your child, pray fervently to you, + In your expanse free from formulations, please think of me. + Lady who reveals the sacred circle of great secrets, + Bestow now the empowerment of the four joys! + Lady who opens the door to the unborn state, + Clear away now my negative acts and obscurations with the purification + practice! + Lady who emits fire from the short Ah, + Burn now my soiled aggregates and sense elements! + Lady who draws great bliss from the syllable Ham, + Bestow now coemergent wisdom! + Lady who reveals the natural experience of illusion, + Destroy now my attachment to the reality of anger and desire! + Lady who emanates and transforms during lucid dreams, + Lady who makes spontaneous luminosity arise, + Dispel now the darkness of my stupidity! + Lady who leads above at the time of departure, + Guide me now to the celestial realms! + Lady who overcomes the appearances of delusion in the intermediate state, + Grant me now the invincible body of enlightenment's perfect rapture. + +This prayer was sung by the religious teacher Gyurme Dechen. + + -- "Timeless Rapture: Inspired Verse of the Shangpa Masters", compiled by + Jamgon Kongtrul, translated & edited by Ngawang Zangpo, a Tsadra + Foundation Series book, published by Snow Lion Publications +~ + ...ngondro, the foundational practices, are ways to bring body, speech, or +energy, and all aspects of mind into increasingly effortless harmony with the +oceanic expanse central to Dzogchen teachings. This expanse is another name +for reality, the heart of our being, and thus for mind-nature. Its vastness +challenges the cramped and reified self-images that temporarily obstruct our +view of the whole. Finitudes of any kind--the sense of being small and +contained, the familiar urgent rush of business, passions, or plans--are +simply conceptions. These conceptions are both cause and effect of energetic +holdings in the body. The foundational practices illuminate these holdings +and, in the end, lead to their dissolution into the expanse. As Khetsun +Sangpo Rinpoche has said, "Like a fire that burns fuel, the mind consumes +thought by working with it." + In the Tibetan traditions, teaching and practice sessions typically open +with a reference, brief or extensive, to the foundational practices. Every +lineage has its own variations, but the basic structure and principles of +these practices are virtually identical. After an acknowledgment of one's +guru or lineage and the intention to benefit all beings, the sequence usually +begins with the four thoughts. These are reflections on (1) the preciousness +of one's own life, (2) the fragility of life and the uncertainty of death's +timing, (3) the inexorable nature of karma, and (4) the impossibility of +avoiding suffering so long as ignorance holds one in samsara. In addition, +there are two other contemplations: (5) the benefits of liberation compared to +life in samsara and (6) the importance of a spiritual guide. These six are +known as the outer foundational practices. + These six are combined with five inner practices, each of which is repeated +one hundred thousand times. The first inner foundational practice is refuge. +Refuge, writes Adzom Drukpa, is the cornerstone of all paths. Without it, he +adds, quoting Candrakirti, all vows come to nothing. Most succinctly, refuge +helps us cultivate a quality vital to the path and to human interaction in +general: this is the quality of trust, the ability to fruitfully rely on +someone or something other than oneself. Adzom Paylo Rinpoche once said that +whereas relying on others in the context of samsara generally leads us astray, +relying on the Dharma increases our good qualities. + -- "Heart Essence of the Vast Expanse: A Story of Transmission", by Anne C. + Klein, foreword by Adzom Paylo Rinpoche, preface by Tulku Thondup + Rinpoche, published by Snow Lion Publications +~ +1.18 Fruition of the Seed of Enlightenment + + When we engage in virtuous actions, we realize they are beneficial not only +for others, but also for ourselves. Our good deeds can earn the praise and +appreciation of others, and the benefits of our work come back to us through +others. When we are involved with virtuous works, people respect us and hold +us in high esteem. And we know we must be doing something good, because we +experience a wholesome, pleasant feeling about our life's work. We quickly +begin to see the short-term benefits of our involvement in virtuous action as +our bodies and minds become more peaceful in our daily lives. + This serenity in turn increases our longevity as our body and mind become +more harmonized. Even after our death, we will be reincarnated in higher +realms of existence as a result of our involvement with virtuous works during +this life. Yet a higher rebirth is merely a short-term benefit, a temporary +relief from the sufferings of samsara, for until we achieve liberation from +samsara we remain trapped in the cycle of suffering, and "whatever goes up, +must come down!" + Within the mundane world, when our evil deeds are common knowledge, no one +sings their praises. If such deeds are remembered at all, it is in infamy. +However, when a being lives with a mind of true bodhichitta and does great +works of pure altruism, their deeds are remembered for centuries. Of such +cases we have many examples within the Kagyu lineage alone: the historical +Buddha, Guru Rinpoche, Milarepa, the Karmapas, and countless others. Yet it +is also important to remember that virtuous action eventually leads us to the +liberation of buddhahood; this is the ultimate long-term benefit of planting +the seed of enlightenment of which we speak. Hence, as we make this journey +towards liberation, it is extremely important for us to learn to recognize +which of our actions are virtuous and which are not. + -- Lama Dudjom Dorjee, "Heartfelt Advice", Snow Lion Publications +~ +Dilgo Khyentse Rinpoche, addressing those who have or will undertake a +retreat, gives this advice: + + "You will fall sick, experience pain, and encounter many adverse +circumstances. At such times do not think, 'Although I am practicing the +Dharma, I have nothing but trouble. The Dharma cannot be so great. I have +followed a teacher and done so much practice, and yet hard times still befall +me.' Such thoughts are wrong views. You should realize that through the +blessing and power of the practice, by experiencing sickness and other +difficulties now, you are purifying and ridding yourself of negative +actions.... By purifying them while you have the chance, you will later go +from bliss to bliss. So do not think, 'I don't deserve this illness, these +obstacles, these negative influences.' Experience your difficulties as +blessings...when you do experience such difficulties, you should be very happy +and avoid having adverse thoughts like, 'Why are such terrible things +happening to me.'" + + As Rinpoche advises, relating to hardship properly depends on the strength +of one's view. In general, having a view is knowing exactly where you want +to go and how to get there. It is the vision of knowing what you want. For +example, if you have the view to become a doctor, your vision guides you +through financial burdens, physical and emotional difficulties, and obstacles +that get in your way. You know it will be difficult and involve sacrifice, +but with a strong view, you forge to the finish line. + Similarly, if you want to become spiritually awakened, it is the power of +your view that gets you there. If you are having a hard time getting to the +meditation cushion, or engaging in the necessary study, it is because your +view is not strong enough or is incomplete. A partial view, in this case, is +one that doesn't include hardship. You can strengthen your view and +accelerate progress by understanding how you lose your view in the fog of +hardship, and therefore lose sight of your path. + -- Dr. Andrew Holecek, "The Power and the Pain: Transforming Spiritual + Hardship into Joy", published by Snow Lion Publications +~ +Chod is a very powerful path to buddhahood. Its power comes from working +with the practitioner's afflictive emotions. Chod is purposely performed in +frightening places to help the practitioner heighten his fear so that he has +the opportunity to cut through it. + -- adapted from Chod Practice in the Bon Tradition, by Alejandro Chaoul, + published by Snow Lion Publications +~ + Under the heading of the great way's [Mahayana's] perspective, we read of how +the Buddha merely demonstrates the process of enlightenment in this world, +something he has done and will do repeatedly. Kongtrul quotes the Buddha in +an important discourse: + + "In the past, countless ages ago, in a world-system that united as many +realms as there are grains of sand in the Ganges, I attained enlightenment as +Transcendent Buddha Crown of the Powerful One, aided beings, and transcended +sorrow. Then once again, from that point until the present age, I have +repeatedly demonstrated the inconceivable process of enlightenment. + "I will continue, until cyclic existence is empty, to demonstrate [this +process of] enlightenment beginning with the initial development of the mind +of awakening as an ordinary being." + + While such statements do not help us grasp the nature of the Buddha's +enlightenment, they do underline the fact that enlightenment is a specific +experience, the result of a known and knowable process that the Buddha +deliberately demonstrates time and again so that we might follow his example, +no guesswork involved. + -- Jamgon Kongtrul Lodro Taye, "Treasury of Knowledge, Books Two, Three, + and Four: Buddhism's Journey to Tibet" translated and introduced by + Ngawang Zangpo, published by Snow Lion Publications +~ +Yang Gonpa says: + + The essence of thoughts that suddenly arise is without any nature. Do not +inhibit their appearance in any way, and without thinking of any essence, let +them arise clearly, nakedly, and vividly. Likewise, if one thought arises, +observe its nature, and if two arise, observe their nature. Thus, whatever +thoughts arise, let them go without holding onto them. Let them remain as +fragments. Release them unimpededly. Be naked without an object. Release +them without grasping. This is close to becoming a Buddha. This is the self- +extinction of samsara, samsara is overwhelmed, samsara is disempowered, and +samsara is exhausted. Knowledge of the path of method and wisdom, appearances +and emptiness, the gradual stages, the common and special paths, and the 84, +000 entrances to the Dharma is made perfectly complete and fulfilled in an +instant. This is self-arisen, for it is present like that in the very nature +[of awareness]. Natural liberation is the essence of all the stainless paths, +and it bears the essence of emptiness and compassion. + -- Karma Chagme, "A Spacious Path to Freedom: Practical Instructions on the + Union of Mahamudra and Atiyoga", commentary by Gyatrul Rinpoche, trans. + by B. Alan Wallace, published by Snow Lion Publications +~ + We must learn to trust ourselves when we practice the doctrines of the Buddha. +In time, we come to trust the infallibility of karmic cause and effect and of +the interdependence of all actions. We must come to know and trust the +importance of the accumulation of merit and wisdom, in the same way we know +and trust that even the smallest drops of water falling into a bucket will +eventually fill it. + We must learn to trust that our own dharma practice will remove our entire +jungle of kleshas [unwholesome qualities], much like knowing a raging wildfire +can clear an entire forest from the earth. All of our negativities can be +swept away by the firestorm of our compassionate wisdom. We must trust that +all of our happiness and sadness is completely dependent on, and a result of, +our previous karma; when we trust this process we can begin the accumulation +of virtuous actions immediately. + No one achieves perfection in anything meaningful the very first time they +try; however, we've heard the phrase over and over again that "practice makes +perfect." It is true that with multiple repetitions and patience everyone can +achieve perfection over time. I don't know of anyone who has sat down to +meditate for the very first time and immediately attained enlightenment, but +just like the drops of water that we trust will eventually fill our bucket, +consistent dharma practice will eventually lead us to liberation. + -- Lama Dudjom Dorjee, "Heartfelt Advice", published by Snow Lion +~ + Unbroken practice is like a watchful guard. + It is simply unscattered and is free from acceptance or rejection. + There is no duality of things to be abandoned and their antidotes. + This is my heart's advice. + +This verse and the following instructions concern how to continue with +Mahamudra practice. Once we have received instructions, we have to accomplish +them and perfect the practice. Continuity of practice is essential for the +perfection of enlightenment. + Unbroken practice means that one is mindful all the time, like a watchful +guard. Thieves and robbers may come at any time, so the guard of a mansion +containing great treasure must be alert twenty-four hours a day. In the same +way, it is important to watch our mind since the thieves of attachment, +desire, anger, and forgetfulness can come at any time and steal the wealth of +our compassion and wisdom, along with our realization of Mahamudra. + Once mindfulness is continuously established, an unscattered mind is "just +there," on the spot, whether we are walking, eating, driving, or performing +other activities. We can watch the mind and see how our mental state shapes +our world. But when we watch it, we should just relax. Milarepa advises us +in a vajra song: + + Rest naturally, like a small child. + Rest like an ocean without waves. + Rest with clarity, like a candle flame. + Rest without self-concern, like a corpse. + Rest unmoving like a mountain. + + -- Khenchen Konchog Gyaltshen, "A Complete Guide to the Buddhist Path", + edited by Khenmo Trinlay Chodron, published by Snow Lion Publications +~ + Every time we begin to practice, it helps not to plunge in right away. +Instead, take a few moments to stop your ordinary chain of thoughts. This is +especially relevant if you are very busy and have only five minutes for your +daily practice, but even ordinarily we have this constant stream of thoughts. +Suppose that just before practice you have a fight with your fianc�. This +will probably trigger a chain of thoughts about what you want to say to your +partner. If you start your practice in the midst of all this, it is not going +to go so well. This is why it helps to put a stop to this chain of thoughts +for just a few moments. + I have found this to be very, very useful. There are actually countless +methods for stopping the chain of thoughts, but for me, before I practice, I +just sit for a while. Every time a thought comes along, I try to stop it by +cultivating a sense of renunciation, and I do this over and over again. I +think about how I am now forty-years-old and, even if I live to be eighty, I +only have half of my life left. I think that out of this forty years, I am +going to sleep the equivalent of twenty years. So now there are only twelve +hours a day that could actually be termed living. If we then factor in +watching at least one movie a day, eating, and gossip, we have maybe five +hours or so left. Out of forty years that means eight years remain, and most +of that will go to indulging our paranoia, anxiety, and all that.... There is +actually very little time for practice! + This should give you an idea of how to stop the chain of thoughts. Don't +immediately throw yourself into the practice; instead, just watch yourself, +watch your life, and watch what you are doing. If you are doing ten minutes +of practice every day, you should try to stop the chain of thoughts for at +least two to three minutes. We do this to transform the mind by invoking a +sense of renunciation. When we think, "I am dying. I am coming closer to +death" and other such thoughts, it really helps. + -- Dzongsar Khyentse Rinpoche in the commentary to "Entrance to the Great + Perfection: A Guide to the Dzogchen Preliminary Practices", compiled, + translated, and introduced by Cortland Dahl, published by Snow Lion + Publications +~ +Courage is not the absence of fear, but rather the judgement that +something else is more important than fear. + -- Ambrose Redmoon +~ +Good timber does not grow with ease; +the stronger the wind, the stronger the trees. + -- Douglas Malloch +~ +Q: Does every kind of desire lead to pain? + +A: Not all desire leads directly to pain. However, the very word expresses +the sense of sticking to something. It does not permit freedom. It binds. +When attached and fastened to something, we cannot move far away. It is as if +the desired object pulls us back, and we cannot free ourselves from it. For +this kind of desire we use a term meaning attachment. So long as we are +attached, we stick there and cannot achieve liberation. However, this does +not necessarily entail chaos and pain. + +Q: Does that mean that some desire is actually beneficial? + +A: In the Tibetan language, desire names an attachment that harms ourselves +and others. The source of benefit for ourselves and others receives a +different name; we call that "longing." + + -- "Essential Practice: Lectures on Kamalashila's Stages of Meditation in + the Middle Way School by Khenchen Thrangu Rinpoche", translated and + introduced by Jules B. Levinson, published by Snow Lion Publications +~ +He who plants a tree plants hope. -- Lucy Larcom +~ +We cross our bridges when we come to them and burn them behind us, with +nothing to show for our progress except a memory of the smell of smoke, and a +presumption that once our eyes watered. + -- Tom Stoppard +~ +The great thing about democracy is that it gives every voter a chance to do +something stupid. + -- Art Spander +~ +It is our earth, not yours or mine or his. We are meant to live on it, +helping each other, not destroying each other. + -- J. Krishnamurti +~ +When one has the feeling of dislike for evil, when one feels tranquil, one +finds pleasure in listening to good teachings; when one has these feelings and +appreciates them, one is free of fear. + -- Buddha +~ +In Buddhism, there is a teaching called the "three bodies" (sanjin), also +called the "three properties" or the "three enlightened properties". These +are the three kinds of form that a Buddha may manifest as: 1) the Dharma Body +(dharmakaya or hosshin) is the form in which a Buddha transcends physical +being and is identical with the undifferentiated unity of being or Suchness +(Skt. tathata, Jp. shinnyo); 2) the Bliss or Reward Body (sambhogakaya or +hojin) is obtained as the "reward" for having completed the bodhisattva +practice of aiding other beings to end their suffering and having penetrated +the depth of the Buddha's wisdom. Unlike the Dharma Body, which is +immaterial, the Bliss Body is conceived of as an actual body, although one +that is still transcendent and imperceptible to common people; 3) the +Manifested Body (nirmanakaya or ojin) is the physical form in which the Buddha +appears in this world in order to guide sentient beings. It is considered +that the historical Buddha, Shakyamuni, is nirmanakaya. Honen believed that +Amida is sambhogakaya. +~ + Sometimes we put our glasses in our pockets or on our heads and later we ask, +"Where are my glasses?" This is quite common. We look everywhere else without +finding our glasses. That is why we need the guru, who can say to us, "There +are your glasses." That is all that the Mahamudra and Dzogchen teachers do: +they simply point out. What they are pointing out is something that you +already have. It is not something that they give you. They do not give you +new glasses. They cannot afford to give you new glasses, but they can afford +to point out where you can find your own glasses. + When we receive pointing-out instructions from our root teacher, we are +being introduced directly and nakedly to the reality of mind's nature. These +instructions become very effective if we have prepared ourselves to receive +them. + ...Pointing-out is similar to pointing to the sky when it is very cloudy and +saying to someone, "There is the blue sky." The person will look up and say, +"Where?" You may reply, "It is there--behind the clouds." The person to whom +you are pointing out the blue sky will not see it at first. However, if even +a patch of blue sky appears, then you can say, "Look--the blue sky is like +that." The person then gets a direct experience. He or she knows +experientially that there is blue sky, which will be fully visible when the +clouds are gone. + -- The Dzogchen Ponlop Rinpoche, "Mind Beyond Death", published by Snow + Lion Publications +~ +In general, clear light is of two types--the objective clear light that is +the subtle emptiness [of inherent existence], and the subjective clear light +that is the wisdom consciousness realizing this emptiness. + -- Lati Rinbochay, "Death, Intermediate State and Rebirth" +~ + [At the time of Buddha, a farmer asked to be ordained as a monk. Shariputra +did not see his merit. But, with a great, compassionate mind, the Buddha took +his hand and said, "I will give you ordination. You do have a seed to attain +arhatship...."] + The Buddha explained, "Thousands and thousands of kalpas ago, this man was +born as a fly. He was sitting on a pile of cow dung when a sudden rush of +water caught the cow dung, along with the fly, and sent them into the river. +Downstream, someone had placed a prayer wheel in the water, and that cow dung +and fly swirled around and around it. Because of that circumambulation, this +man now has a seed to attain arhatship in this lifetime." + Cause and result are so subtle that only omniscient wisdom can perceive +every detail. That is why we must be very careful that our actions are truly +beneficial. + Reciting just one mantra, protecting the life of even one small bug, giving +a small thing--we should not ignore such actions by saying, "This is nothing; +it makes no difference if I do it or not." Many small actions will gather and +swell like the ocean. These are not merely Buddhist beliefs; these are the +causes that create our world no matter who we are. Our study and practice +give us the opportunity to understand this and to be sincere with ourselves +even in small things. + -- Khenchen Konchog Gyaltshen, "A Complete Guide to the Buddhist Path", + edited by Khenmo Trinlay Chodron, published by Snow Lion Publications +~ +Never let your sense of morals get in the way of doing what's right. + -- Isaac Asimov +~ +What a blessing it would be if we could open and shut our ears as easily as we +open and shut our eyes! + -- Georg Christoph Lichtenberg +~ +Honest criticism is hard to take, particularly from a relative, a friend, an +acquaintance, or a stranger. + -- Franklin P. Jones +~ +Your worst enemy cannot harm you as much as your own unguarded thoughts. +Develop the mind of equilibrium. You will always be getting praise and blame, +but do not let either affect the poise of the mind: follow the calmness, the +absence of pride. + -- Sutta Nipata +~ +Whosoever has heard the law of virtue and vice is as one who has eyes and +carries a lamp, seeing everything and will become completely wise. + -- Buddha +~ +...in Dzogchen, one applies specific practices in order to create a variety of +sensations, so that the practitioner is more clearly enabled to distinguish +the state of presence--which always remains the same--from the sensations +which change according to the practice being carried out. This obviously +enables one to 'no longer remain in doubt' as to what the state of pure +presence is. The practices known as the twenty-one Semdzin found in the +Dzogchen Mennagde, or Upadesha, series, have this particular function, +enabling the practitioner to separate the ordinary, reasoning mind from the +nature of the mind. + -- Chogyal Namkhai Norbu, "The Crystal and the Way of Light: Sutra, Tantra + and Dzogchen", compiled and edited by John Shane, published by Snow Lion + Publications +~ +People often wonder how to reconcile the Buddha's teachings on non-attachment +with those on love. How can we love others without being attached to them? +Non-attachment is a balanced state of mind in which we cease overestimating +others' qualities. By having a more accurate view of others, our unrealistic +expectations fall away, as does our clinging. This leaves us open to loving +others for who they are, instead of for what they do for us. Our hearts can +open to care for everyone impartially, wishing everyone to be happy simply +because he or she is a living being. The feeling of warmth that was +previously reserved for a select few can now be expanded to a great number of +people. + -- Ven. Thubten Chodron, "Taming the Mind", published by Snow Lion Pub. +~ + Even in this world, and even now, there are said to be many hidden yogis or +discreet yogis, called bepay naljor in Tibetan. It means those realized ones +who are not generally recognized as great spiritual sages or saints, but have +deeply tasted the fruit of enlightenment, and are living it. Perhaps they are +anonymously doing their good works here among us right now! + The infinite vast expanse is one's own inconceivable nature. Who can say +who has realized it and who hasn't? When we travel around the world or +experience other dimensions, there are so many beings who have tasted it. We +can see it in their behavior, in their countenance, and in stories that are +told--not just in the Dzogchen tradition or the Buddhist tradition, but in any +tradition, and in our Western world too. + This true nature is so vast and inconceivable that even some birds and +animals and beings in other unseen dimensions can be said to have realized it, +as in some of the ancient Indian Jataka stories and other teaching tales. It +is always said that everything is the self-radiant display of the primordial +Buddha Samantabhadra. There are infinite numbers of Buddhas and infinite +numbers of beings. Who can say who is excluded from it? + -- Nyoshul Khenpo Rinpoche and Lama Surya Das, "Natural Great Perfection: + Dzogchen Teachings and Vajra Songs", published by Snow Lion Publications +~ + Many spiritual seekers are not yet ready to become the disciples of +spiritual mentors. Their present levels of commitment may suit working only +with Buddhism professors, Dharma instructors, or meditation or ritual +trainers. Even if they are ready to commit themselves to the Buddhist path +and to spiritual mentors, they may not yet have found properly qualified +mentors. Alternatively, the spiritual teachers available to them may be +properly qualified and may even have shown them great kindness. Yet, none +seem right to be their mentors. They feel they can relate to them only as +their Buddhism professors. Nevertheless, the Kadam style of guru-meditation +may still help such seekers to gain inspiration from these teachers at the +present stages of their spiritual paths. + Unless our spiritual teachers are total charlatans or complete scoundrels, +all of them have at least some good qualities and exhibit at least some level +of kindness. Our Buddhism professors, Dharma instructors, or meditation or +ritual trainers may lack the qualities of great spiritual mentors. Still, +they have some knowledge of the Dharma, some insight from applying the Dharma +to life, or some technical expertise in the practice. Our teachers are kind +to instruct us, even if their motivations contain the wish to earn a living. +If we correctly discern and acknowledge whatever qualities and levels of +kindness that our professors, instructors, or trainers in fact possess, we may +derive inspiration, through guru-meditation, by focusing on them with +conviction and appreciation. + -- Dr. Alexander Berzin, "Wise Teacher, Wise Student: Tibetan Approaches to + a Healthy Relationship", published by Snow Lion Publications +~ + A tenth-century Bengali pandita named Palden Atisha reintroduced Buddhism +into Tibet. He had a servant who was really awful. He was abusive to Atisha, +disobedient, and generally a big problem. The Tibetans asked Atisha what he +was doing with such an awful guy who was so completely obnoxious. They said, +"Send him back. We'll take care of you." Atisha replied, "What are you +talking about? He is my greatest teacher of patience. He is the most +precious person around me!" + Patience does not mean suppression, and it doesn't mean bottling up our +anger or turning it in on ourselves in the form of self-blame. It means +having a mind which sees everything that happens as the result of causes and +conditions we have set in motion at some time in this or past lives. Who +knows what our relationship has been with someone who is causing us +difficulties now? Who knows what we have have done to him in another life! +If we respond to such people with retaliation, we are just locking ourselves +into that same cycle. We are going to have to keep replaying this part of +the movie again and again in this and future lifetimes. The only way to break +out of the cycle is by changing our attitude. + -- "Reflections on a Mountain Lake: Teachings on Practical Buddhism by + Venerable Tenzin Palmo", published by Snow Lion Publications +~ +[Understanding through the merging of sound and meaning takes place when one +immediately understands the meaning of a teaching through hearing the sound of +the words.] + + One might ask what these words are in the key instructions on the Three +Words That Strike the Vital Point. The sound and the word are the same. For +example, the word "mother" can be understood as indicating someone who is very +kind. If one says "mother," the meaning of what that word expresses is +pointed out. What is known as "the three words" is like that. + What are the three words? "View," "meditation," and "action." What does it +mean to "strike the vital point" with these three words? If one wants to kill +a man and strikes his heart with a weapon, the man will not live another hour. +He will die immediately. What vital point do these three words strike? Just +as oil is present in a mustard seed, all of us, all sentient beings, have +buddha nature. Though it is present, we do not recognize it, because our +minds are obscured by delusions. When, as a result of the view, meditation, +and action, we come to recognize these delusions, we can get rid of them in a +moment. In one day sentient beings can be transformed into buddhas--that is +the ultimate view, meditation, and action of dzogchen. Such a power of +transformation is called "striking the vital point." + -- Dilgo Khyentse, "The Collected Works of Dilgo Khyentse", edited by + Matthieu Ricard and Vivian Kurz, excerpt from Volume 3: "Primordial + Purity", published by Snow Lion Publications +~ + If we can attain nondual, nonconceptual awareness in meditation, we are +engaged in profound political activity, even though we may lose this awareness +during the times we are not formally meditating (the buddha's awareness in +post-meditation is the same as during meditation). Meditating in nondual, +nonconceptual awareness, which is meditating on the dharmadhatu, immediately +begins systematically to destroy in ourselves the structure of dualistic +consciousness with all its attendant cognitive obscurations and emotional +affiictions. From the standpoint of duality, since this dualistic +consciousness also involves other sentient beings as the other pole of our +duality, our activity in dissolving this consciousness has a profound impact +on them as well. + While our nondualistic, nonconceptual meditation is purifying our own +obscurations and afflictions and thereby transforming our personal experience +of others, it is also becoming a spark of buddha activity in those others. As +our meditation becomes effective, the attitude of others towards us begins to +change, and they themselves begin to turn inward and to search with greater +conscientiousness through the stuff of their own minds and lives for spiritual +solutions to their own problems. And as the power of our meditation +increases, this effect reaches ever-widening concentric circles of sentient +beings with whom we have karmic interdependence, which in this day and age +includes not only our immediate family and friends, working associates, and +local communities, but also everyone with whom we are connected through all +the media of our lives. + --Khenchen Thrangu Rinpoche, "The Ninth Karmapa's Ocean of Definitive + Meaning", edited, introduced and annotated by Lama Tashi Namgyal, + published by Snow Lion Publications +~ + Realizations come only if we practice joyfully, with confidence and courage. +Realization doesn't grow within a timid or weak state of mind--it blossoms in +the mind free of doubt and hesitation. Realization is fearless. When we see +the true nature of reality, there's nothing hidden, nothing left to fear. At +last we're seeing reality as it is, full of joy and peace. + ...Thinking of Tara will bring total calm, peace, and protection from all +fears and all frightening situations. Tara's practice removes the two +obscurations: negative emotions and subtle conceptual thinking. It will +increase the two merits: accumulation merit and wisdom merit. From the moment +you start praying to and practicing Tara, your life will be always under the +protection of the Great Mother. From then on rebirth in the lower realms will +be prevented. If you do this prayer for others, it will bring them the same +benefits; it will protect them in their lifetimes as well as uproot future +births in the lower realms. So there is great benefit. + -- Khenchen Palden Sherab and Khenpo Tsewang Dongyal in "Tara's Enlightened + Activity: An Oral Commentary on 'The Twenty-one Praises to Tara'", + published by Snow Lion Publications +~ + Realizations come only if we practice joyfully, with confidence and courage. +Realization doesn't grow within a timid or weak state of mind--it blossoms in +the mind free of doubt and hesitation. Realization is fearless. When we see +the true nature of reality, there's nothing hidden, nothing left to fear. At +last we're seeing reality as it is, full of joy and peace. + ...Thinking of Tara will bring total calm, peace, and protection from all +fears and all frightening situations. Tara's practice removes the two +obscurations: negative emotions and subtle conceptual thinking. It will +increase the two merits: accumulation merit and wisdom merit. From the moment +you start praying to and practicing Tara, your life will be always under the +protection of the Great Mother. From then on rebirth in the lower realms will +be prevented. If you do this prayer for others, it will bring them the same +benefits; it will protect them in their lifetimes as well as uproot future +births in the lower realms. So there is great benefit. + -- Khenchen Palden Sherab and Khenpo Tsewang Dongyal in "Tara's Enlightened + Activity: An Oral Commentary on 'The Twenty-one Praises to Tara'", + published by Snow Lion Publications +~ +6. Meditation on the Buddha + Begin by observing your breath for a few minutes to calm the mind. + Think of the qualities of infinite love, compassion, wisdom, skillful means, +and other wonderful qualities you aspire to develop. What would it feel like +to have those qualities? Get a sense of the expansiveness and peace of having +a wise and kind heart that reaches out impartially to work for the benefit of +all beings. + Those qualities of love, compassion, wisdom, skillful means, and so on now +appear in the physical form of the Buddha, in the space in front of you. He +sits on an open lotus flower, and flat sun and moon disks. His body is made +of radiant, transparent light, as is the entire visualization. His body is +golden and he wears the robes of a monk. His right palm rests on his right +knee and his left is in his lap, holding a bowl of nectar, which is medicine +to cure our afflictions and other hindrances. The Buddha's face is very +beautiful. His smiling, compassionate gaze looks at you with total acceptance +and simultaneously encompasses all sentient beings. His eyes are long, +narrow, and peaceful. His lips are red and his earlobes long. + Rays of light emanate from each pore of the Buddha's body and reach every +part of the universe. These rays carry countless miniature Buddhas, some +going out to help beings, others dissolving back into the Buddha after having +finished their work. + The Buddha is surrounded by the entire lineage of spiritual teachers, all +meditational deities, innumerable other Buddhas, bodhisattvas, arhats, dakas, +dakinis, and Dharma protectors. To the side of each spiritual master is an +elegant table upon which are arranged volumes of Dharma teachings. + Surrounding you are all sentient beings appearing in human form, with your +mother on your left and your father on your right. The people you do not get +along with are in front of you. All of you are looking to the Buddha for +guidance. + -- Bhikshuni Thubten Chodron, "Guided Meditations on the Stages of the + Path", foreword by His Holiness the Dalai Lama, published by Snow Lion + Publications +~ + If we investigate on a deeper level, we will find that when enemies inflict +harm on us, we can actually feel gratitude toward them. Such situations +provide us with rare opportunities to put to test our own practice of +patience. It is a precious occasion to practice not only patience but the +other bodhisattva ideals as well. As a result, we have the opportunity to +accumulate merit in these situations and to receive the benefits thereof. + The poor enemy, on the other hand, because of the negative action of +inflicting harm on someone out of anger and hatred, must eventually face the +negative consequences of his or her own actions. It is almost as if the +perpetrators of the harm sacrifice themselves for the sake of our benefit. +Since the merit accumulated from the practice of patience was possible only +because of the opportunity provided us by our enemy, strictly speaking, we +should dedicate our merit to the benefit of that enemy. This is why the Guide +to the Bodhisattva's Way of Life speaks of the kindness of the enemy. + --Tenzin Gyatso, the Fourteenth Dalai Lama, "The Compassionate Life" +~ + If our practice does not diminish self-grasping, or perhaps even enhances +it, then no matter how austere and determined we are, no matter how many hours +a day we devote to learning, reflection, and meditation, our spiritual +practice is in vain. + A close derivative of self-grasping is the feeling of self-importance. Such +arrogance or pride is a very dangerous pitfall for people practicing Dharma. +Especially in Tibetan Buddhism, with its many levels of practice, the exalted +aspirations of the bodhisattva path, and the mystery surrounding initiation +into tantra, we may easily feel part of an elite. Moreover, the philosophy of +Buddhism is so subtly refined and so penetrating that, as we gain an +understanding of it, this also can give rise to intellectual pride. + But if these are the results of the practice, then something has gone awry. +Recall the well-known saying among Tibetan Buddhists that a pot with a little +water in it makes a loud noise when shaken, but a pot full of water makes no +noise at all. + People with very little realization often want to tell everyone about the +insights they have experienced, the bliss and subtleties of their meditation, +and how it has radically transformed their life. But those who are truly +steeped in realization do not feel compelled to advertise it, and instead +simply dwell in that realization. They are concerned not to describe their +own progress, but to direct the awareness of others to ways in which their own +hearts and minds can be awakened. + -- B. Alan Wallace, "The Seven-Point Mind Training", edited by Zara + Houshmand, published by Snow Lion Publications +~ + What is method, within the context of the unity of method and wisdom? It is +a dedicated heart of bodhichitta, based on love and compassion. It apprehends +its object, enlightenment, with the intention to achieve it in order to +benefit others. Compassion, as its basis, apprehends its object, the +suffering of others, with the wish to remove it. + Wisdom, on the other hand, is a correct view that understands voidness--the +absence of fantasized, impossible ways of existing. Even if it is aimed at +the same object as method, it apprehends that object as not existing in an +impossible way. + The ways wisdom and compassion each apprehend their object are not at all +the same. Therefore, we need to actualize these two, as method and wisdom, +first separately and then together. + Even if we speak about the mahamudra* that is method and wisdom, inseparable +by nature in the ultimate tantric sense, the first stage for its realization +is understanding the abiding nature of reality. + -- H.H. the Dalai Lama and Alexander Berzin, "The Gelug/Kagyu Tradition of + Mahamudra", published by Snow Lion Publications +~ + In Buddhism we speak of three types of phenomena: First, there are evident +phenomena that are perceived directly. + Second, there are slightly hidden phenomena, which are not accessible to +immediate perception. There are differences of opinion on this even within +Buddhist philosophy. Generally speaking, we think this second type of +phenomena can be known indirectly by inference. + One example of something known by inference is that anything arising in +dependence upon causes and conditions is itself subject to disintegration and +momentary change. This momentary change is not immediately evident to your +senses. You can look at something with your eyes, and it does not appear to +be changing right now, but by inference you can know that it is momentarily +changing. This is an example of the second category of phenomena. + Third, there are very concealed phenomena, which cannot be known by either +of the two preceding methods. They can be known only by relying upon +testimony of someone such as the Buddha. + -- "Consciousness at the Crossroads: Conversations with the Dalai Lama on + Brain Science and Buddhism", edited by Zara Houshmand, Robert B. + Livingston, and B. Alan Wallace, published by Snow Lion Publications +~ +I would rather be a superb meteor, every atom of me in +magnificent glow, than asleep and permanent as a planet. + -- Jack London +~ + Sometimes our life can feel devoid of meaning even though we may try in +different ways to put meaning into it.... Meaning comes when we go deeply +within, wait, listen, and open. It begins to come when we genuinely open to +the suffering of those around us with a compassionate heart. Equally, it +comes as we respond to the environment within which we live with care and +concern. + The meaning or purpose to be found in bodhichitta is less associated with +what we do than with the quality we bring to what we engage in. Small, simple +aspects of our life can be profoundly meaningful and have deep impact both for +ourselves and others. Meaning lies in the quality of heart that we put into +what we do. + It is not, therefore, the outer manifestation of what we can achieve that is +the root of meaning. It is the undercurrent of bodhichitta's intention or +purpose and meaning that flows within. What bodhichitta implies is that in +attuning to our buddha nature or buddha potential, we touch a source of +meaning in ourselves that will come through whatever we do. + This root of meaning gives the bodhisattva the capacity to live a relatively +ordinary life and transform adverse circumstances into the path. Even small +things become meaningful, like the way we respond to someone's distress or a +gesture of friendliness that lifts someone's day. This deeper sense of +purpose is reflected in the care we give to our relationships and the +environment. + Being present and responsive to what arises may mean that the eventual goal +of our sense of purpose is less crucial. We are seldom, if ever, able to see +fully where our path will take us, and once we are open to the meaning present +in bodhichitta, the ego must surrender ambitions and allow the journey to +unfold. + -- Rob Preece, "The Courage to Feel: Buddhist Practices for Opening to + Others", published by Snow Lion Publications +~ + We have to admit impermanence into our lives. It's important to live with +impermanence as a frame of reference so that we can approach each moment or +each day with a sense of humility about what we are able to do and what we are +not able to do and relinquish control over things we cannot have control over. +It is important to live as if things are as permanent as stone. + You have to invest yourself in love and concern for people, accept people's +love as if that's the only thing that exists. The commitment to living as if +everything is always there forever with the acceptance that nothing is going +to survive. + --David Hodge and Hi-Jin Kang Hodge, "Impermanence: Embracing Change", + published by Snow Lion Publications +~ +We are the people our parents warned us about. -- Jimmy Buffett +~ +To succeed in the world it is not enough to be stupid, +you must also be well-mannered. + -- Voltaire +~ +I won't take my religion from any man who never works except with his mouth. + -- Carl Sandburg +~ +Hatred does not cease through hatred at any time. +Hatred ceases through love. +This is an unalterable law. + -- Shakyamuni Buddha +~ + The Tibetan controversies about instantaneous enlightenment through +recognition of the nature of the mind have been studied by David Jackson. +As he shows, it is mainly members of the Kagyu traditions in Tibet who have +maintained this doctrine, although it is certainly common in Chinese Ch'an +Buddhism and in the teachings of the Great Perfection in Tibet. Dolpopa +quotes the position that is the object of his refutation: "Recognizing the +very essence naturally purifies them, without rejection." This expresses the +view that through recognition of the essence of the thoughts as the dharmakaya +they are purified or dissolved into the dharmakaya, and also the idea that any +affliction that arises is actually a manifestation or self-presencing of +primordial awareness itself. Thus there is no need to reject thoughts or +afflictions, which are naturally purified by means of the recognition. This +type of viewpoint is widespread in Tibetan Buddhism. + In contrast to these views, Dolpopa claims that the definition of an +ordinary sentient being or a buddha, and of samsara or nirvana, is determined +by the presence or absence of the incidental and temporary obscurations that +veil the true nature of reality. It is not determined solely by recognition +of the nature of the mind or the thoughts. + ...While the ground buddhahood of the dharmakaya and the resultant +buddhahood of the dharmakaya have not the slightest difference in essence, +they are distinguished as ground and result by means of the presence or +absence of incidental stains. + -- Cyrus Stearns, "The Buddha from Dolpo: A Study of the Life and Thought + of the Tibetan Master Dolpopa Sherab Gyaltsen", a Tsadra Foundation + Series book, published by Snow Lion Publications +~ + We live in an ocean of cyclic existence whose depth and extent cannot be +measured. We are troubled again and again by the afflictions of desire and +hatred as if repeatedly attacked by sharks. + Our mental and physical aggregates are impelled by former contaminated +actions and afflictions and serve as a basis for present suffering as well as +inducing future suffering. While such cyclic existence lasts, we have various +thoughts of pleasure and displeasure: 'If I do this, what will people think? +If I do not do this, I will be too late; I won't make any profit.' When we see +something pleasant we think, 'Oh, if I could only have that!' + ...Day and night, night and day we spend our lives in the company of the +afflictions, generating desire for the pleasant and anger at the unpleasant, +and continue thus even when dreaming, unable to remain relaxed, our minds +completely and utterly mixed with thoughts of desire and hatred without +interruption. + To what refuge should we go? A source of refuge must have completely +overcome all defects forever; it must be free of all faults. It must also +have all the attributes of altruism--those attainments which are necessary for +achieving others' welfare. For it is doubtful that anyone lacking these two +prerequisites can bestow refuge; it would be like falling into a ditch and +asking another who is in it to help you out. You need to ask someone who is +standing outside the ditch for help; it is senseless to ask another who is in +the same predicament. A refuge capable of protecting from the frights of +manifold sufferings cannot also be bound in this suffering but must be free +and unflawed. + Furthermore, the complete attainments are necessary, for if you have fallen +into a ditch, it is useless to seek help from someone standing outside it who +does not wish to help or who wishes to help but has no means to do so. + -- H.H. the Dalai Lama, Tsong-ka-pa and Jeffrey Hopkins, "Tantra in Tibet", + published by Snow Lion Publications +~ + If we were forced to choose between a sense of practical application and +learnedness, a sense of practical application would be more important, for one +who has this will receive the full benefit of whatever he knows. The mere +learnedness of one whose mind is not tamed can produce and increase bad states +of consciousness, which cause unpleasantness for himself and others instead of +the happiness and peace of mind that were intended. One could become jealous +of those higher than oneself, competitive with equals and proud and +contemptuous towards those lower and so forth. It is as if medicine had +become poison. + Because such danger is great, it is very important to have a composite of +learnedness, a sense of practical application and goodness, without having +learnedness destroy the sense of practical application or having the sense of +practical application destroy learnedness. + -- The Dalai Lama, "A Policy of Kindness: An Anthology of Writings By and + About the Dalai Lama", compiled and edited by Sidney Piburn, Foreword + by Sen. Claiborne Pell, published by Snow Lion Publications +~ + A bodhisattva, having generated a sincere and spontaneous desire to attain +full enlightenment for the sake of all sentient beings, enters the Mahayana +path of accumulation. Here the bodhisattva cultivates the four mindfulnesses +and develops mental quiescence, then passes on to the path of application, +where she or he strives for a conceptual insight into emptiness. When +quiescence and insight are combined in examining emptiness, the bodhisattva +attains a direct, non-conceptual realization of emptiness, and thus becomes an +arya, on the path of seeing. + The path of seeing corresponds to the first of the ten bhumis, i.e. stages, +levels, or grounds said to be traversed by a bodhisattva. The other nine +bodhisattva stages are coextensive with the path of development, during the +course of which the disciple completely eliminates not only the defilements +that are obstacles to liberation but even the traces of defilement, which are +obstacles to full enlightenment. + When the path of development is completed, the disciple is ready to enter +the path of no-more-training; this marks the attainment of full enlightenment, +the dharmakaya, sambhogakaya, and nirmanakaya of an omniscient, compassionate, +and powerful buddha. + -- Geshe Lhundub Sopa, Roger Jackson, John Newman, "The Wheel of Time: The + Kalachakra in Context", edited by Beth Simon, published by Snow Lion + Publications +~ +"If you fear you are running after the objects of the six senses, hold +yourself with the hook:" + 'Employ the watchman that is mindfulness.' + + Someone who has been captured with a hook has no option but to go wherever +he is led. In the same way, if we catch hold of our mind--which risks being +distracted by the objects of the six senses--with the hook of mindfulness, and +with vigilance and carefulness, this will be of enormous benefit. We should +use this watchman to constantly check how many positive or negative thoughts +and actions we produce during the day. When we are able to control our minds +through mindfulness, everything that appears in samsara and nirvana becomes an +aid in our practice and serves to confirm the meaning of the teachings. All +appearances are understood as being dharmakaya. We perceive everything in its +natural purity, and there is nothing we can call impure. + -- Dilgo Khyentse Rinpoche, "Zurchungpa's Testament: A Commentary on + Zurchung Sherab Trakpa's 'Eighty Chapters of Personal Advice'", based on + Shechen Gyaltsap's Annotated Edition, translated by the Padmakara + Translation Group, published by Snow Lion Publications +~ +A committee can make a decision that is dumber than any of its members. + -- David Coblitz +~ +Art is science made clear. -- Jean Cocteau +~ +A specification that will not fit on one page of 8.5x11 inch paper +cannot be understood. + -- Mark Ardis +~ +A vacuum is a hell of a lot better than some of the stuff +that nature replaces it with. + -- Tennessee Williams +~ +The trouble with being poor is that it takes up all of your time. + - Willem de Kooning +~ + We need to understand the essential nature of the broad diversity of +phenomena. For example, if we are obliged to be involved frequently with a +man who exhibits a personality that is true only on the surface, as well as +another basic personality, it is important for us to know both of them. To +engage in a relationship with this person that does not go awry, we must know +both aspects of his personality. To know only the facade that he presents is +insufficient; we need to know his basic disposition and abilities. Then we +can know what to expect from him; and he will not deceive us. + Likewise, the manifold events in the world are not non-existent; they do +exist. They are able to help and hurt us--no further criterion for existence +is necessary. If we do not understand their fundamental mode of existence, we +are liable to be deceived, just as in the case of being involved with a person +whose basic personality we do not know. + -- H.H. the Dalai Lama, "Transcendent Wisdom", translated, edited and + annotated by B. Alan Wallace, published by Snow Lion Publications +~ + One has to be cautious when one is very successful since at that time there +is danger of becoming prideful and becoming involved in the non-religious, and +one has to be cautious also when one has undergone lack of success and so +forth since there is a danger of becoming discouraged--the life of the mind, +so to speak, dying--due to which harm to one's practice could be incurred. + ...Specifically, in situations of low self-esteem, Shantideva recommends +reflection this way: + "Even very tiny bugs and worms have the Buddha nature and thus, when they + encounter certain conditions, through the power of effort they can achieve + the non-abiding nirvana of a Buddha. Now, I have been born as a human with + the capacity to understand what is to be adopted in practice and what is to + be discarded; thus, there is no reason for me to be discouraged. The great + saints and so forth of the past who achieved a high level were people with + a life-basis such as I have, not something separate." + Through such reflection, a resurgence of will can be generated. + -- "The Dalai Lama at Harvard: Lectures on the Buddhist Path to Peace by + H.H. the Dalai Lama of Tibet, Tenzin Gyatso", translated and edited by + Jeffrey Hopkins, published by Snow Lion Publications +~ +When we understand the evolution of our unsatisfactory experiences in cyclic +existence, we will see that meditating on emptiness is their antidote. All +knowable things--people and phenomena--appear to our minds to be inherently +existent. We then grasp at them as existing inherently. Our inappropriate +attention focuses on them, and that gives rise to the various disturbing +attitudes of anger, attachment, and so on. These disturbing attitudes +motivate our actions, which in turn leave karmic imprints on our mindstreams. +When these imprints ripen, we meet with suffering. + -- Geshe Jampa Tegchok, "Transforming Adversity into Joy and Courage: An + Explanation of the Thirty-seven Practices of Bodhisattvas", edited by + Thubten Chodron, published by Snow Lion Publications +~ + The essential point about this condition of potentiality is that, although +there is a causal relationship between the physical world and the world of +mental phenomena, in terms of their own continuum one cannot be said to be the +cause of the other. A mental phenomenon, such as a thought or an emotion, +must come from a preceding mental phenomenon; likewise, a particle of matter +must come from a preceding particle of matter. + Of course, there is an intimate relationship between the two. We know that +mental states can influence material phenomena, such as the body; and, +similarly, that material phenomena can act as contributory factors for certain +subjective experiences. This is something that we can observe in our lives. +Much of our gross level of consciousness is very closely connected to our +body, and in fact we often use terminology and conventions which reflect this. + For example, when we say 'human mind' or 'human consciousness' we are using +the human body as the basis to define a particular mind state. Likewise, at +the gross levels of mind such as our sensory experiences, it is very obvious +that these are heavily dependent upon our body and some physiological states. +When a part of our body is hurt or damaged, for instance, we immediately +experience the impact on our mental state. Nevertheless, the principle +remains that mental phenomena must come from preceding phenomena of the same +kind, and so on. + If we trace mental phenomena back far enough, as in the case of an +individual's life, we come to the first instant of consciousness in this life. +Once we have traced its continuum to this point of beginning, we then have +three options: we can either say that the first instant of consciousness in +this life must come from a preceding instant of consciousness which existed in +the previous life. Or we can say that this first instant of consciousness +came from nowhere--it just sort of 'popped up'. Or we can say that it came +from a material cause. + From the Buddhist point of view, the last two alternatives are deeply +problematic. The Buddhist understanding is that, in terms of its continuum, +consciousness or mind is beginningless. Mental phenomena are beginningless. +Therefore, the person or the being--which is essentially a designation based +on the continuum of the mind--is also devoid of beginning. + -- H.H. the Dalai Lama, "Lighting the Way", translated by Geshe Thupten + Jinpa, published by Snow Lion Publications +~ +If someone wants a sheep, then that means that he exists. + -- Antoine de Saint-Exupery +~ +If I were two-faced, would I be wearing this one? -- Abraham Lincoln +~ +There is something about the outside of a horse +that is good for the inside of a man. + -- Sir Winston Churchill +~ + Life is a party on death row. Recognizing mortality means we are willing to +see what is true. Seeing what is true is grounding. It brings us into the +present and, eventually, into presence. It also brings us into our bodies, +especially if we combine meditation on impermanence with an energetic +awareness at the base of the spine. At first, the important thing about +impermanence seems to be the limited time we have in this precious life. This +is crucial and foundational, and yet it is not the whole story. + The teachings on impermanence concern the death of a self that never +existed. Our sense of such a false and finite self, which initially is +inseparable from our wish to practice, can dissolve. Understanding +impermanence, Khetsun Rinpoche says, will lead you into the natural clarity of +your own mind. To know impermanence is thus not only a path leading to what +Dzogchen traditions speak of as "unbounded wholeness" (thigle nyag cig), it is +also integral to that wholeness. + -- Anne C. Klein, "Heart Essence of the Vast Expanse: A Story of + Transmission", foreword by Adzom Paylo Rinpoche, preface by Tulku + Thondup Rinpoche, published by Snow Lion Publications +~ + Now, you see, world peace through mental peace is an absolute. It is the +ultimate goal. But as for the method, there are many factors that must be +taken into consideration. Under a particular set of circumstances, a certain +approach may be useful while under other circumstances another may be useful. +This is a very complicated issue which compels us to study the situation at a +particular point of time. We must take into account the other side's +motivation, etc., so it is a very complex matter. + But we must always keep in mind that all of us want happiness. War, on the +other hand, only brings suffering--that is very clear. Even if we are +victorious, that victory means sacrificing many people. It means their +suffering. Therefore, the important thing is peace. But how do we achieve +peace? Is it done through hatred, through extreme competition, through anger? +It is obvious that through these means it is impossible to achieve any form of +lasting world peace. Hence, the only alternative is to achieve world peace +through mental peace, through peace of mind. World peace is achieved based +only on a sense of brotherhood and sisterhood, on the basis of compassion. +The clear, genuine realization of the oneness of all mankind is something +important. It is something we definitely need. Wherever I go, I always +express these views. + -- H.H. the Dalai Lama, "Answers: Discussions with Western Buddhists", + edited by Jose Ignacio Cabezon, published by Snow Lion Publications +~ +koan for dependency inversion: + + High-level modules should not depend upon low-level modules. + Both should depend upon abstractions. + + Abstractions should not depend upon details. + Details should depend upon abstractions. +~ +People harm others only when they are unhappy. No one wakes up in the morning +and says, "I feel so great today! I think I'll go out and harm someone!" When +we can allow ourselves to know the depth of the pain and confusion felt by +those who have harmed us, compassion--the wish that they be free from such +suffering--can easily arise. Thinking in this way does not mean whitewashing +or denying harm that was done. Rather, we acknowledge it, but go beyond +amassing resentment, because we know that grudges help neither ourselves nor +others. + -- Thubten Chodron, "Working with Anger", published by Snow Lion Publications +~ + The reason why the qualities of a teacher are described at such length in +the scriptures is because we should know what to look for when seeking a guru +capable of opening up the Buddhist paths within us. To take up training under +an unqualified teacher can be disastrous. It is said in the tantric +scriptures that one is not unwise to examine a guru for twelve years before +accepting that person as one's teacher. The choice of teachers is an +important one and must be made carefully. + Not only does the guru perform the work of the Buddhas and thus equal them +in activity, in terms of kindness the guru surpasses them. Of all Buddhas of +the past who have manifested as universal teachers, it is said that Buddha +Shakyamuni is kindest to us; for it is with his teachings that we have come +into contact...even though Buddha Shakyamuni is most kind of the past Buddhas, +still we are unable to receive teachings from him or witness his inspiring +presence. + Were all the Buddhas and lineage masters of the past to manifest before us +at this very moment, we would not be able to recognize them as enlightened +beings. Due to our not having a sufficiently strong karmic connection with +them, they would be unable to affect us. The guru performs the great kindness +of coming to us in an ordinary form which we can perceive and to which we can +relate, and carries out the work of the Buddhas in our lives. The fact that a +donkey like us is brought into the family of spiritual beings is purely due to +the kindness of the guru. The Buddhas can only come to us through him or her. +Thus if we do not respect the guru and heed his or her teachings, what hope do +we have? We should meditate upon the guru's unexcelled kindness and give +birth to profound appreciation. + The reason why we have been wandering unceasingly in cyclic existence since +time immemorial is because we have not met a spiritual master before; or even +if we have met one we did not cultivate an effective relationship with him or +her. We should determine to take the opportunities afforded by our present +human situation and cultivate a spiritual practice under the guidance of a +master. + -- H.H. the Dalai Lama, "The Path to Enlightenment", edited and translated + by Glenn H. Mullin, published by Snow Lion Publications +~ +There was once a Spartan boy, +who, one night, stole a fox. +In order not to be caught, +he hid it under his cloak. +He stood stock still as his +elder looked him over, +but all the while the fox +chewed away at his insides. +The boy bit his lip against +the pain until he inevitably fell, +dead to the ground. + -- Plutarch +~ +An exalting task for all humankind + The West is fascinated by efficiency. And there is no doubt that in many +areas its efficiency is quite admirable. That is why I would like to ask this +question, which seems natural to me: why not apply this technical efficiency +to protect all forms of life? This would be an exalting task for all +humankind, especially as we seem to lack a truly large-scale project or ideal. +It is difficult, yet it is absolutely necessary. If the question of human +survival is not solved, there will be nobody left to solve the problem. And +Buddhism can help here. + -- "The Dalai Lama's Little Book of Inner Peace: The Essential Life and + Teachings by His Holiness the Dalai Lama" +~ + If you continue to practice meditation, then your experience will gradually +increase and there will be greater and greater stability and greater and +greater lucidity. However, the experiences that can arise in meditation can +take various different forms. And in spite of the fact that the person has a +real recognition of the mind's nature, there is still the possibility or +probability of fluctuation in experience even after that. + Sometimes you may feel that you have amazing, tremendous meditation, and at +other times you may feel that you have no meditation at all. This +characterizes meditation experience, which fluctuates a great deal. +Realization, which is distinct from experience, does not change, but +experiences can fluctuate a great deal or alternate between good and bad. +There will still be times when you will have what you regard as good +experiences and, in contrast, what you regard as bad experiences. When that +occurs, just keep on looking. Don't get distracted or sidetracked by the +experience. Whatever meditation experience arises, you should recognize that +it is transitory. As is said, "meditation experience is like mist, it will +surely vanish." + Experiences are different from the actual fact of the recognition itself. +Because they are ephemeral experiences, they aren't worth investing in. So if +you have a bad meditation experience, do not be alarmed, because it too will +vanish. If you have a good meditation experience, you need to continue; if +you have a bad meditation experience, you need to continue. In either case, +you simply need to continue to rest in this recognition of the mind's nature. + -- Khenchen Thrangu Rinpoche, "Pointing Out the Dharmakaya", published by + Snow Lion Publications +~ + The person witnessing another person's suffering has only one appropriate +response: "How can I help?" When karma comes to fruition and causes suffering, +the response should never be, "This is your karma. It's your destiny, so I +can't help." Your own karma may very well present itself as an opportunity to +help a suffering person. Misunderstanding actions and their consequences can +be disastrous. + From the Buddhist perspective, the type of fortune we encounter, happiness +or sorrow, is not due to somebody doing something to us. If I win the +lottery, it is not because Buddha selected me for a bonus. No god or buddha +is responsible for what happens to us....This does not imply that a suffering +person is morally degenerate any more than suffering the consequences of +eating contaminated food does. The suffering we experience is due to karma +accumulated under the influence of delusion and mental afflictions. This is +true for all sentient beings. + The Buddhist response to the non-virtues we all commit while strapped to the +wheel of samsara can be inspiring and encouraging. The Buddhist teaching is +that it is possible to neutralize negative karmic seeds embedded in the stream +of consciousness. Deeds cannot be undone, but it is possible to purify one's +mind-stream so that the impact of karmic seeds will be nullified. + The method used to purify the mind-stream is the "four remedial powers" +[remorse, reliance, resolve, and purification]. The metaphor for the +effectiveness of the four remedial powers is that of burning a seed. Karma, +like a seed, can be scorched in the fire of purification so that it will not +sprout. The seed won't vanish, but it will not sprout. + -- B. Alan Wallace, "Buddhism with an Attitude: The Tibetan Seven-Point + Mind Training", published by Snow Lion Publications +~ + ...it is said that through the power of believing one's own body, speech, +and mind to be undifferentiable from the deity's exalted body, speech, and +mind all one's physical actions and movements are seals and all one's speech +is mantra. In this way the Vajrapani Initiation Tantra says: + "A son or daughter of lineage* who has seen a mandala, who generates the +mind of enlightenment, who is compassionate, skilled in means and in teaching +the ways of letters--the door of Secret Mantra--should think thus, 'Separate +from speech, there is no mind. Separate from mind, there is no speech. +Separate from mind, there is no divine form. Mind itself is speech; speech +itself is mind; divine form itself is also mind, and speech itself is also +divine form.' + "If a mantra practitioner believes in this way that these are +undifferentiable, he attains purity of mind. At those times when he has a +pure mind, he always views in all ways his own body to be the same as the +deity's body, his own speech to be the same as the deity's speech, and his own +mind to be the same as the deity's mind; then, he is in meditative equipoise." +* One who is a suitable vessel for the teaching. + -- "Deity Yoga in Action and Performance Tantra", by His Holiness the Dalai + Lama, Tsong-ka-pa, and Jeffrey Hopkins, published by Snow Lion Pub. +~ +My fellow Americans. As a young boy, I dreamed of being a baseball, but +tonight I say, we must move forward, not backward, upward not forward, +and always twirling, twirling, twirling towards freedom. + -- Kodos gives a speech, "Treehouse of Horror VII" +~ + Mind is empty like an illusion; + Separate from body, it cannot achieve. + Body also is like a wall; + Separate from mind it is without activity. + Similarly the yoga of seals.... + --Shriparamadhya + + Mind separate from body cannot openly display activities; body separate from +mind is the same. Rather, actions must be done upon the aggregation of mind +and body. Just so, here when practicing techniques for transforming basic--or +ordinary--body, speech, mind, and activities into those of the fruit stage of +Buddhahood, it is necessary both to cultivate (1) internal meditative +stabilization on a divine body, on a vajra on a moon disc at the heart, on a +seed syllable on a vajra in the throat, and on a vajra at the heart, and (2) +at the same time to construct the appropriate gesture, or seal, with the +hands. These must be done in unison, for it is said in Yoga Tantra ritual +texts that if you fail to construct seals with the hands, the rite is +nullified. Unlike on other occasions, it is not just that if the hand-seals +are constructed, it is better, but if not, there is no fault of nullifying the +rite. Here, they must be done. + -- H.H. the Dalai Lama, Dzong-ka-ba and Jeffrey Hopkins, "Yoga Tantra: Paths + to Magical Feats", translated and edited by Jeffrey Hopkins, published by + Snow Lion Publications +~ + Following the Vajrayana teachings, we do not give up or reject anything; +rather we make use of whatever is there. We look at our negative emotions and +accept them for what they are. Then we relax in this state of acceptance. +Using the emotion itself, it is transformed or transmuted into the positive, +into its true face. + When, for instance, strong anger or desire arises, a Vajrayana practitioner +is not afraid of it. Instead he or she would follow advice along the +following lines: Have the courage to expose yourself to your emotions. Do not +reject or suppress them, but do not follow them either. Just look your +emotion directly in the eye and then try to relax within the very emotion +itself. There is no confrontation involved. You don't do anything. +Remaining detached, you are neither carried away by emotion nor do you reject +it as something negative. Then, you can look at your emotions almost casually +and be rather amused. + When our usual habit of magnifying our feelings and our fascination +resulting from that are gone, there will be no negativity and no fuel. We can +relax within them. What we are trying to do, therefore, is to skillfully and +subtly deal with our emotions. This is largely equivalent to the ability of +exerting discipline. + -- Ringu Tulku, "Daring Steps: Traversing the Path of the Buddha", edited and + translated by Rosemarie Fuchs, published by Snow Lion Publications +~ + Although we did not have the fortune to see Buddha Shakyamuni himself in +person, we do have the great fortune of having access to his own precious +teachings, which is actually superior to seeing him in person. The same is +the case with masters like Nagarjuna and his immediate disciples. If we make +the necessary effort, and undertake the practice and study, we can fully enjoy +a benefit equal to that of having met them in person. + ...So visualize in space, in front of you, all the exalted masters, +including Buddha Shakyamuni, Nagarjuna, Aryadeva, eighty mahasiddhas, the +Nyingma masters, Atisha, the Kadampa masters, the five great masters of the +Sakya tradition, the lineages of Lamdre practice, the great masters of the +Kagyu lineage, such as Marpa, Milarepa, and also the great masters of the +Gelugpa lineage, Lama Tsongkhapa, and all of their followers. + Around you also are the protectors who have taken the oath in the presence +of Buddha Shakyamuni to safeguard and protect the precious doctrine of Buddha. +Visualize as well the harmful spirits--actually an embodiment of your own +delusions--from which you are being protected by the guardians. Also +visualize various emanations of the buddhas actively working for the benefit +of all living beings. Surrounding you are all sentient beings...undergoing +the sufferings of their individual realms of existence. Now generate a strong +force of compassion directed towards all these sentient beings, particularly +your enemies. + Having created this mental image, question yourself as to how all these +objects of refuge, the buddhas and the masters of the past, achieved such a +high state of realization and reached a state where they can provide +protection to all living beings. You will find that it is because of their +having made effort in the practice of dharma in general and, in particular, +the practice of bodhicitta*. Think as follows: "I shall, from today, follow +in the footsteps of these great masters, and take the initiative of generating +bodhicitta." + * The aspiration to achieve enlightenment for the sake of all beings. + -- H.H. the Dalai Lama, "The Path to Bliss", translated by Geshe Thupten + Jinpa, edited by Christine Cox, published by Snow Lion Publications +~ +To forget how to dig the earth and tend the soil is to forget ourselves. + -- Gandhi +~ +I arise in the morning torn between a desire to improve the world and a desire +to enjoy the world. This makes it hard to plan the day. + -- E.B. White +~ + The question then is "How do we cultivate and develop this bodhicitta, the +mind of enlightenment?" The key, and the root, is great compassion. +Compassion here refers to a state of mind that makes it utterly unbearable for +us to see the suffering of other sentient beings. The way to develop this is +through understanding how we feel about our own suffering. When we become +conscious of our own suffering, we have a spontaneous wish to be free from it. +If we are able to extend that feeling to all other beings, through realizing +the common instinctive desire we all have to avoid and overcome suffering, +then that state of mind is called 'great compassion'. + All of us have the potential to develop that kind of compassion, because +whenever we see people who are suffering, especially those close to us, we +immediately feel empathy towards them, and witness a spontaneous response +within our minds. So all we have to do is to bring that potential out, and +then to develop it to become so impartial that it can include all sentient +beings within its embrace, whether friend or foe. + To cultivate this great compassion within ourselves, first of all we need to +develop what is called loving-kindness, a feeling of connectedness or +closeness with all living creatures. This closeness and intimacy should not +be confused with the kind of feeling we normally have toward our loved ones, +which is tainted by attachment...ego and selfishness. On the contrary, we are +seeking to develop a feeling of closeness towards other sentient beings, and +affection for them, by reflecting on the fact that suffering is inherent in +their very nature, on the helplessness of their situation, and on the +instinctive desire they all have to overcome suffering. + The greater the force of our loving kindness towards other beings, the +greater the force of our compassion. And the greater the force of our +compassion, the easier it will be for us to develop a sense of responsibility +for taking upon ourselves the task of working for others. The greater that +sense of responsibility, the more successful we will be in generating +bodhicitta, the genuine altruistic aspiration to attain buddhahood for the +benefit of all. + -- His Holiness the Dalai Lama, "Dzogchen: The Heart Essence of the Great + Perfection", translated by Thupten Jinpa and Richard Barron, Foreword by + Sogyal Rinpoche, edited by Patrick Gaffney, published by Snow Lion Pub. +~ + Understanding the power of the path provides the inspiration that keeps us +going forward; exploring its pain provides the understanding of what holds us +back. It doesn't take long to discover the power, nor to feel the pain. +Waking up hurts. And if we don't understand why, we will run from the pain +and abandon the path. There are countless people who have become spiritual +dropouts, or who are lost in detours because they have not understood +hardship. + When your arm falls asleep, it prickles and burns as it returns to life. +Frozen fingers sting when they thaw; we jolt awake when the alarm clock rings. +But physical instances of anesthesia are mild compared to the anesthesia born +of ignorance, and so is the level of discomfort upon awakening. The longer +something has been asleep, the more painful it is to wake it up. If your +fingers are merely cold, it is easy to warm them up. But if your fingers are +frozen solid, it hurts like hell when they thaw. According to the traditions, +unless one is already a buddha, an "awakened one," one has been snoring from +beginningless time, and it can really hurt before we completely wake up. +Mingyur Rinpoche writes, + "I'd like to say that everything got better once I was safely settled among +the other participants in the three-year retreat.... On the contrary, +however, my first year in retreat was one of the worst in my life. All the +symptoms of anxiety I'd ever experienced--physical tension, tightness in the +throat, dizziness, and waves of panic--attacked in full force. In Western +terms, I was having a nervous breakdown. In hindsight, I can say that what I +was actually going through was what I like to call a 'nervous breakthrough'." + -- Dr. Andrew Holecek, "The Power and the Pain: Transforming Spiritual + Hardship into Joy", published by Snow Lion Publications +~ +Through our eyes the universe is perceiving itself, and through our ears the +universe is listening to its harmonies. We are the witness through which the +universe becomes conscious of its glory, of its magnificence. + -- McMurdo Station Forklift Driver paraphrasing Alan Watts +~ + As human beings we are deeply insecure and we do not know who we truly are. +Of course this problem does not show on the surface of our lives. We are +always telling ourselves who we are, based on this notion that we are separate +from everything else. This sense that "I am separate" is the ground of our +sense of self. It is reinforced by various false identities that we cling to, +notions that "I am this" or "I am that." Whatever beliefs we have about +ourselves are just another extension. Most of the time when we look around, +we immediately see that our surroundings are validating these false +identities. For this very reason, it is a challenging endeavor to deconstruct +this illusion of self. + Every time we look into our mirror we might have some thought about +ourselves. Each of these thoughts adds up. They become the conceptual bricks +we use to keep building this illusory castle of self. Yet, there is a +suspicion that this notion of self might be very fragile and transient, and +this thought is silently lurking somewhere in our consciousness. Most of the +time this suspicion is not brought into the light of awareness, but if it is, +some deep, inner wisdom will arise without choice. + Our suspicion of the fragility of this false notion of self can go in one of +two directions. In general it becomes a source of fear, anxiety, and +insecurity. We often see people who are fearful and overly defensive when it +comes to their own identity. We ourselves tend to become fearful if our +identity is threatened. But at other times the suspicion can go another way. +When that happens, it can be a life-changing revelation that can lead us to +the realization of the highest level of truth. This idea is not some new, +lofty theory. It is timeless wisdom that has been realized by many people in +human history. Buddha taught this wisdom, and in his tradition it is called +anatman or "no self." Anatman, or "no self," is the term used to mean that one +has seen through this false sense of self. One has seen that this false sense +of self is merely an identification with one's roles in life. It is just a +mask, not the truth. + -- Anam Thubten, "No Self, No Problem", edited by Sharon Roe, published by + Snow Lion Publications +~ + It is common worldly knowledge that by believing untrue information to be +true we fall into confusion and are harmed. Similarly, by believing phenomena +to be inherently existent when in fact they are not inherently existent, we +are also harmed. For example, with respect to the different ways in which +there can be a consciousness of 'I', there is a definite difference between +the way the 'I' is apprehended when desire, hatred, pride and so forth are +generated based on this 'I', and the way the 'I' is apprehended when we are +relaxed without any of those attitudes being manifest. + Similarly, there is the mere consciousness that apprehends an article in a +store before we buy it, and there is the consciousness apprehending that +article after it has been bought, when it is adhered to as 'mine' and grasped +with attachment. Both these consciousnesses have the same object, and in both +cases the mode of appearance of the article is the appearance of it as +inherently existent. However, there is the difference of the presence or +absence of our adhering to it as inherently or independently existent. + ...a consciousness conceiving inherent existence precedes any bad +consciousness, leading it on by the nose, and also accompanies, or aids, many +other bad consciousnesses as well. Thus, if there were no ignorance +conceiving inherent existence, then there would be no chance for desire, +hatred and so forth to be generated. + -- H.H. the Dalai Lama, "The Buddhism of Tibet", translated and edited by + Jeffrey Hopkins, with Anne Klein, published by Snow Lion Publications +~ +Avoid Sidetracks + + As practitioners we will face many obstacles and sidetracks on our path to +liberation, and these will provide us with many challenges along the way. We +shouldn't allow our practice to become interrupted due to these obstacles and +sidetracks, such as the appearance and disappearance of the many friends we +will have over the course of our lives. + We also shouldn't allow our practice to become interrupted by a change in +the availability or quality of food and shelter. And we shouldn't allow our +practice to become interrupted by the obstacles and sidetracks presented by +the many distractions of mind that are readily available in the mundane world +of our external environment. We shouldn't allow our practice to be +interrupted by obstacles and sidetracks that arise due to the desire and +attachment we feel for loved ones, or our aversion to enemies, or our +indifference towards others. Finally, we should not allow our practice to +become interrupted by our desire to accumulate wealth, or by our attachment to +our material possessions. Only an advanced practitioner, motivated by deep +bodhichitta, can get through these obstacles and avoid these sidetracks to +reach their goal of liberation from samsara. + -- Lama Dudjom Dorjee, "Heartfelt Advice", published by Snow Lion Pub. +~ + Cultivating an aspiration to help other sentient beings becomes a cause for +wanting to achieve Buddhahood for the sake of all sentient beings. These are +the two levels of the awakening mind of bodhichitta. + Such a mind cannot be cultivated in a mere few months or years, but this +does not mean it cannot be cultivated at all. If you continue your practice +to cultivate bodhichitta, a time will come when you will be successful. For +example, in the initial stage you may not even understand the meaning of the +word bodhichitta. You might wonder how you could ever cultivate such a mind. +But through repeated practice and familiarity, you will gradually come closer +to such a mind. + It is the nature of conditioned things that they change depending on causes +and conditions. So it is important to recall the advantages and benefits of +such a mind and cultivate a strong determination to achieve it. Make ardent +prayers. Whether you sleep, walk, or sit, you should think: "How good it +would be if I could cultivate such a mind." Try to cultivate bodhichitta even +on an aspirational level. If you spend your days in such repeated and +persistent practice, you can definitely develop it. Make the determination to +cultivate it even if it will take many aeons. As Shantideva prays in his +Guide to the Bodhisattva's Way of Life: + + As long as space endures + And as long as sentient beings remain, + May I too abide + To dispel the sufferings of all sentient beings. + + -- H.H. the Dalai Lama, "Stages of Meditation", root text by Kamalashila, + translated by Geshe Lobsang Jordhen, Losang Choephel Ganchenpa, and + Jeremy Russell, published by Snow Lion Publications +~ + The theory of dependent-arising can be applied everywhere. One benefit of +applying this theory is that viewing a situation this way gives you a more +holistic picture, since whatever the situation is--good or bad--it depends on +causes and conditions. An event is not under its own power but depends on +many present causes and conditions as well as many past causes and conditions. +Otherwise, it could not come into being. + When you think from this viewpoint, you can see much more of the whole +picture, and from this wider perspective, you can see the reality of the +situation, its interdependence. With the help of this relational outlook, the +action that you take will be realistic. + ...Failure to look at the whole picture means realism is lost. The attitude +that money alone is sufficient leads to unforeseen consequences. Money is +certainly necessary; for instance, if you thought that religious retreat in +meditation alone was sufficient, you would not have anything to eat. Many +factors have to be considered. With awareness of the fuller picture, your +outlook becomes reasonable, and your actions become practical, and in this way +favorable results can be achieved. + -- His Holiness the Dalai Lama, "How to See Yourself As You Really Are", + translated and edited by Jeffrey Hopkins, Ph.D. +~ +Fear and Fearlessness. + + Perhaps the first reaction we have to our own suffering is fear. Fear +arises in us almost automatically when we experience strong emotions or pain. +We don't have to sit there and generate fear--it just arises. When we +experience a disturbing emotion such as jealousy we think, "No, I don't want +this." We would rather not experience it. However, if we examine fear +closely, we see that it is a thought to which we have been habituating our +mind for a very long time. We have repeated this thought pattern of fear for +many years, and from a Buddhist point of view, many lifetimes. + In just the same way, when we habituate our minds to being fearless, to +being brave and open towards our emotions, fearlessness will also arise +naturally. In order for this to happen, we must train in applying antidotes +to our thought patterns that are caught up in fear. In this way, we transcend +fear first through a conceptual process, which later becomes nonconceptual, a +natural fearlessness. In order to become fearless in this way, we need +determination and the willingness to face our emotions. With that strong +determination and courage, fearlessness will arise effortlessly. + -- "Trainings in Compassion: Manuals on the Meditation of Avalokiteshvara", + trans. by Tyler Dewar under the guidance of The Dzogchen Ponlop Rinpoche, + published by Snow Lion Publications +~ + There is both a reason and a purpose for cultivating the meditative +stabilization observing exhalation and inhalation of the breath. The reason +is mainly to purify impure motivations. What exactly is to be purified? The +main of these are the three poisons--desire, hatred, and obscuration. Even +though we have these at all times and even though the meditator will still +retain them, she or he is seeking to suppress their manifest functioning at +that time. The specific purpose for cleansing impure motivations before +meditation is to dispel bad motivations connected with this lifetime, such as +having hatred toward enemies, attachment to friends, and so forth. + In terms of the practice I am explaining here, even the thought of a +religious practitioner of small capacity is included within impure +motivations; such a person engages in practice mainly for the sake of a good +future lifetime. Similarly, if on this occasion one has the motivation of a +religious practitioner of middling capacity--that of only oneself escaping +from cyclic existence, this is also impure. + What is a pure motivation? To take as one's aim the welfare of all sentient +beings. This is the motivation of a religious practitioner of great capacity. +Meditators should imagine or manifest their own impure motivation in the form +of smoke, and with the exhalation of breath should expel all bad motivation. +When inhaling, they should imagine that all the blessings and good qualities +of Buddhas and Bodhisattvas, in the form of bright light, are inhaled into +them. This practice is called purification by way of the descent of ambrosia. +There are many forms of this purification, but the essence of the practice is +as just indicated. + -- Geshe Gedun Lodro, "Calm Abiding and Special Insight: Achieving Spiritual + Transformation Through Meditation", translated and edited by Jeffrey + Hopkins, published by Snow Lion Publications +~ + Tsongkhapa pays homage to the "foremost holy lamas," for it is in dependence +upon a qualified lama that the three principal aspects of the path are +realized. + The high title "lama" alone does not qualify someone as a lama; the good +qualities associated with the title must also be present. The three words-- +foremost, holy, and lama--set forth the three qualities of a lama. + "Foremost" describes a person who has diminished emphasis on this lifetime +and is primarily concerned with future lifetimes and deeper topics. Such a +person has a longer perspective than the shortsighted one of those who mainly +look to the affairs of this life and thus, in relation to common beings whose +emphasis is mainly on this life, is the foremost, or a leader. + "Holy" refers to one who, as a result of developing renunciation for all +forms of cyclic existence, is not attached to any of its marvels and is +seeking liberation. A holy person has turned his or her mind away from +attachment outside to the better things of cyclic existence and focused it +within. + In the word "lama", "la" means high, and "ma" is a negative, which indicates +that there is none higher; this is a person who has turned away from self- +cherishing to cherishing others, has turned away from the lower concern for +personal benefit in order to achieve the higher purpose of attaining benefit +for others. + -- "Kindness, Clarity, and Insight 25th Anniversary Edition", by The + Fourteenth Dalai Lama, His Holiness Tenzin Gyatso, edited and translated + by Jeffrey Hopkins, co-edited by Elizabeth Napper, published by Snow Lion + Publications +~ +...beginning with an attitude +Of love for all living creatures, +Consider beings, excluding none, +Suffering in the three bad rebirths, +Suffering birth, death and so forth. + + The" attitude of love" to which the text refers is the affection which sees +all living beings as lovable. The stronger our affection the more easily +compassion arises and the more intense and steadfast it is. Compassion can +arise without it, but it will not be consistent. Unless we see all living +beings as near, dear, appealing and beloved, we won't care what happens to +them. On the contrary, we may even wish more suffering on those we dislike. +That affection is what a doting mother feels for the apple of her eye, what a +dog-owner feels for a beloved pet--a warm feeling that makes you want to hug +and pat and say, "Adorable!" + At present our feelings of affection are restricted to those we like and, +even then, vanish quite quickly if they do something that goes against our +wishes. It's a tall order to ask us to feel affection toward all living +beings. It doesn't come naturally, which is why we need to train ourselves to +see them in a new way. + -- "Atisha's Lamp for the Path to Enlightenment", commentary by Geshe Sonam + Rinchen, translated and edited by Ruth Sonam, published by Snow Lion + Publications +~ + Why should one work so hard to please people, doing all sorts of things for +others in order to make them feel happy? If one can't bear one's enemy's +happiness, then why should one do all sorts of things to make anyone else +happy? + Shantideva explains an inconsistency regarding this issue. He notes that +when praise is directed toward oneself, when people speak highly of oneself, +one not only feels happy but also expects others to be happy when they hear +this praise. However, this is totally inconsistent with one's attitude toward +others. When people praise others, then not only does one disapprove of +others' happiness but one's own peace of mind and happiness are destroyed as +well. So there seems to be an inconsistency when it comes to relating to +praise directed toward oneself and praise directed toward others. + Then, especially for a Bodhisattva practitioner who has dedicated his or her +life to bringing about joy and happiness in others and leading them to the +ultimate state of happiness, to be jealous of others' happiness and joy is +totally inappropriate. In fact, one should feel that if other sentient beings +of their own accord, from their own efforts, gain any little experience of +happiness and joy here and there, we should be all the more grateful, because +without our helping them, they have been able to achieve these joyful +experiences and happiness. + -- H.H. the Dalai Lama, "Healing Anger: The Power of Patience from a + Buddhist Perspective", translated by Geshe Thupten Jinpa, published by + Snow Lion Publications +~ + What is meant by going for refuge is that you are seeking refuge from some +fear. All the objects [Buddha, lama, guru, etc.] in front of you are what is +known as the causal refuge, because they serve as the cause for bringing about +the resultant refuge within you. You should entrust yourself to these objects +from the depth of your heart, and you should see the objects as protectors. +The resultant state of your own future realizations, becoming an arya being +and attaining buddhahood--which depends on your own actualization of the path- +-is called the resultant refuge. Someone in difficulty seeking the assistance +of a high official is analogous to someone seeking refuge in the causal +refuge. + But depending upon others' protection forever is not a courageous way of +life; therefore, one has to try to achieve a state where one is no longer +dependent upon such a refuge, and this is likened to taking refuge in the +resultant buddha, dharma, and sangha. That is the process of taking refuge by +a person of high faculty and courage. This practice should be done not for +the sake of oneself alone but rather for the sake of all other sentient +beings. When you cultivate such an aspiration focused toward the achievement +of the omniscient state, it is very much like the generation of the +bodhichitta mind. + -- H.H. the Dalai Lama, "The Union of Bliss and Emptiness: Teachings on the + Practice of Guru Yoga", translated by Thupten Jinpa, published by Snow + Lion Publications +~ + Many of the methods of practicing Dharma that are learned during waking can, +upon development of dream awareness, be applied in the dream condition. In +fact, one may develop these practices more easily and speedily within the +Dream State if one has the capacity to dream lucidly. There are even some +books that say that if a person applies a practice within a dream, the +practice is nine times more effective than when it is applied during the +waking hours. + The dream condition is unreal. When we discover this for ourselves within +the dream, the immense power of this realization can eliminate obstacles +related to conditioned vision. For this reason, dream practice is very +important for liberating us from habits. We need this powerful assistance in +particular because the emotional attachments, conditioning, and ego +enhancement which compose our normal life have been strengthened over our +many, many years. + In a real sense, all the visions that we see in our lifetime are like the +images of a dream. If we examine them well, the big dream of life and the +smaller dreams of one night are not very different. If we truly see the +essential nature of both, we will find that there really is no difference +between them. If we can finally liberate ourselves from the chains of +emotions, attachments, and ego by this realization, we have the possibility of +ultimately becoming enlightened. + -- Chogyal Namkhai Norbu, "Dream Yoga and the Practice of Natural Light", + ed. & intro. by Michael Katz, published by Snow Lion Publications +~ +Cultivating Memory and Joyful Effort + + [This] foundational practice is engaged upon awaking in the morning. It +further cultivates strong intention and also strengthens the capacity to +remember the events of the night. + Begin by reviewing the night. The Tibetan term for this preparation is +literally "remembering." Did you dream? Were you aware that you were in a +dream? If you dreamt but did not attain lucidity, you should reflect, "I +dreamt but did not recognize the dream as a dream. But it was a dream." +Resolve that next time you enter a dream you will become aware of its true +nature while still in the dream. + If you find it difficult to remember dreams, it can be helpful, throughout +the day and particularly before sleep, to generate a strong intention to +remember dreams. You can also record dreams in a notepad or with a tape +recorder, as this will reinforce the habit of treating your dreams as +something valuable. The very act of preparing the notebook or recorder at +night serves to support the intention to recall the dream upon waking. It is +not difficult for anyone to remember dreams once the intention to do so is +generated and sustained, even over just a few days. + If you did have a lucid dream, feel joy at the accomplishment. Develop +happiness relative to the practice and resolve to continue to develop the +lucidity the following night. Keep building intention, using both successes +and failures as occasions to develop ever stronger intent to accomplish the +practice. And know that even your intention is a dream. + --Tenzin Wangyal Rinpoche, "The Tibetan Yogas of Dream and Sleep", + published by Snow Lion Publications +~ + On some occasions, people faint. Even when your breath temporarily stops, +during that moment, there is a reduced level of consciousness. Consciousness +is most reduced late in the course of dying. Even after all physical +functions cease, we believe that the "I," or "self," still exists. Similarly, +just at the beginning of life, there must be a subtle form of consciousness to +account for the emergence of consciousness in the individual. + We must explore further the point at which consciousness enters into a +physical location. At conception, the moment when and the site where +consciousness interacts with the fertilized egg is something to be discovered, +although there are some reference to this in the texts.... The Buddhist +scriptures do deal with it, but I am interested to see what science has to say +about this. During this period we believe that without the subtle +consciousness, there would be a life beginning without consciousness. If that +were the case then no one could ever recollect experiences from their past +life. It is also in terms of Buddhist beliefs relating to this topic that +Buddhism expounds its theory of cosmology: how the universe began and how it +later degenerates. + Based on this metaphysical reasoning and other arguments, and based on the +testimony of individuals who are able to recollect their experiences in past +lives very vividly, Buddhists make this claim. I am a practitioner, so based +on my own limited experiences, and the experiences of my friends, I cannot say +with one hundred percent certainty that there is a subtle consciousness. + Scientists don't posit consciousness in the same sense that Buddhists do. +At the moment of conception, however, there has to be something that prevents +the sperm and egg from simply rotting, and causes it to grow into a human +body. When does that occur? Why does that occur? + -- H.H. the Dalai Lama, "Consciousness at the Crossroads: Conversations with + the Dalai Lama on Brain Science and Buddhism", edited by Zara Houshmand, + Robert B. Livingston, and B. Alan Wallace, published by Snow Lion +Taking the reins is the key to happiness +~ + The state of mind of a Buddhist practitioner should be stable, and should +not be subject to too many conflicting events. Such a person will feel both +joy and pain, but neither will be too weak or too intense. Stability is +developed through discipline. The heart and mind become more full of energy, +more resolute, and therefore less susceptible to being blown about by outside +events. + Deep within the human being abides the wisdom that can support him or her in +the face of negative situations. In this way, events no longer throw him +because he is holding the reins. Similarly, when something good happens it is +also possible to rein it in. Taking the reins is the key to happiness. In +Tibet we have a saying: "If you are beside yourself with joy, tears are not +far behind." This shows how relative what we call joy and pain are. + -- His Holiness the Dalai Lama, "The Dalai Lama's Little Book of Inner + Peace: The Essential Life and Teachings" +~ + From a Buddhist perspective, busying ourselves with worldly activities is a +form of laziness, because we're lax in self-cultivation. Our lives are so +busy in modern society: Our appointment books are completely full and we're +always running here and there. We often complain there isn't enough time for +the Dharma. + However, whenever we have a spare moment, we work overtime or call some +friends to fill in the gap. We always have time to eat, but we hardly ever +have time to nourish ourselves spiritually by attending Dharma classes or +meditating. When the temple has entertainment and free meals, we go; but when +there is meditation or lessons, we're busy. + This hindrance to spiritual progress comes because we're attached to worldly +pleasures: food, money, reputation, amusement, and friends. The harm comes +from our inappropriate way of relating to them. Attached, we selfishly +indulge in them. However, these things in and of themselves aren't bad. +Through pacifying our afflictions, we can enjoy these things with a good +motivation--to improve ourselves for the benefit of others. + -- Ven. Thubten Chodron, "Taming the Mind", published by Snow Lion Pub. +~ + It is important to note that we should make sure that our meditation suits +our mind. If we feel comfortable doing analytical meditation on the various +topics in a progressive way, we should go ahead with it. If, on the other +hand, we find it difficult and it is not compatible with our mind, we should +meditate on whatever topic we like. + If we enjoy meditation on emptiness, we should go ahead with this. If it +suits us and we derive pleasure from meditating principally on the altruistic +intention, we can emphasize this. At some point if we find that we cannot +really get into whatever analytical meditation we have been doing, but doing +prostrations, chanting mantra, visualizing a meditation deity, or reciting +aspirational prayers brings peace and pleasure to our mind, we should do that +practice. + -- Geshe Jampa Tegchok, "Transforming Adversity into Joy and Courage: An + Explanation of the Thirty-seven Practices of Bodhisattvas", edited by + Thubten Chodron, published by Snow Lion Publications +~ + Attaining realization is not such a long path once we become able to +integrate all our movements of energy in our practice, because then every +action is governed by presence and becomes a step on the path and an +expression of virtue. + Practice is not only sitting in meditation, reciting mantras, or chanting. +It is the application of practice in daily life that is most difficult, +working with our energy in every life situation, with every sense perception, +with every person we meet, whether we want to encounter that person or not. + -- Tenzin Wangyal Rinpoche, "Wonders of the Natural Mind: The Essence of + Dzogchen in the Native Bon Tradition of Tibet", published by Snow Lion +~ +When we focus our attention on the passage of breath, we break the usually +continuous flow of thoughts of attachment, hostility and so forth, whatever +they might be. This causes such thoughts to subside for the moment. Thus, by +occupying the mind with our breath, we cleanse it of all positive and negative +conceptual thoughts and thus remain in a neutral state of mind unspecified as +either constructive or destructive. This is the meaning of the line in the +root text, "Thoroughly clean out your state of awareness." This unspecified or +neutral state of mind, cleaned out of all positive and negative conceptual +thoughts, is the most conducive one to work with. Because an unspecified +state of mind like this is unburdened and supple, it is relatively easy to +generate it into a constructive state. + -- H.H. the Dalai Lama and Alexander Berzin, "The Gelug/Kagyu Tradition of + Mahamudra", published by Snow Lion Publications +~ +76. + "You may ask: If there is no sentient being, whose is the goal? We grant + that desire [for liberation, etc.] is indeed delusive. Still, in order to + eradicate suffering, effective delusion, whose result [is understanding of + the ultimate] is not prevented." + -- Shantideva + +Objection: + If sentient beings do not exist, who is it that attains the fruition of the +spiritual path--full awakening? And while on the path, for whom does one +cultivate compassion? + +Response: + Sentient beings do exist. It is for them that compassion is felt, and +compassion is cultivated by existent people. Whatever is designated by +delusion is to be acknowledged. Due to cultivating compassion while on the +spiritual path, the fruition of full awakening is attained. Who attains +awakening? That, too, is to be established conventionally, without [ultimate] +examination or analysis. In order to pacify the suffering of oneself and +others, impure appearances that arise due to ignorance are not to be rejected. + -- H.H. the Dalai Lama, "Transcendent Wisdom", translated, edited and + annotated by B. Alan Wallace, published by Snow Lion Publications +~ + The object of meditation this time is emotion. In other words, we +specifically focus on the emotions that arise from our feelings of good, bad, +and indifferent. In the first of the equanimity meditations, we made the +choice to not follow up these emotions. This time we make the choice to +meditate on them. We might choose to meditate on sensations and feelings that +arise in our immediate, present environment. We might also choose to meditate +on an event or person that sets off strong sensations, feelings, and emotions. + Let's say you choose to base your meditation on an event such as a family +argument. This time you contemplate an aspect of that event and try to +disentangle the sensations, feelings, and emotions. Sensations are what you +feel with your body. Feelings assess whether that sensation is nice, nasty, +or neutral. What emotions arise as a result of those sensations and feelings? + As we now know, equanimity means not getting caught in further +exaggerations: "Oh, I am so bad because this is what I did," "Look how good I +am," "How could anyone love someone like me?" and so on. In this meditation, +equanimity means not judging whether we are good or bad people, but just +noting what happened. + -- Chonyi Taylor, "Enough! A Buddhist Approach to Finding Release from + Addictive Patterns", published by Snow Lion Publications +~ + Everyone tries to remove superficial pain, but there is another class of +techniques concerned with removing suffering on a deeper level--aiming at a +minimum to diminish suffering in future lives and, beyond that, even to remove +all forms of suffering for oneself as well as for all beings. Spiritual +practice is of this deeper type. + These techniques involve an adjustment of attitude; thus, spiritual practice +basically means to adjust your thought well. In Sanskrit it is called dharma, +which means "that which holds." This means that by adjusting counterproductive +attitudes, you are freed from a level of suffering and thus held back from +that particular suffering. Spiritual practice protects, or holds back, +yourself and others from misery. + From first understanding your own situation in cyclic existence and seeking +to hold yourself back from suffering, you extend your realization to other +beings and develop compassion, which means to dedicate yourself to holding +others back from suffering. It makes practical sense...by concentrating on +the welfare of others, you yourself will be happier. + -- His Holiness the Dalai Lama, "Mind of Clear Light: Advice on Living Well + and Dying Consciously", translated and edited by Jeffrey Hopkins, Ph.D. +~ +Fool me once, shame on you. Fool me twice, shame on me. Fool me eight +times, I must be a f**king idiot. + -- Jon Stewart, on the last eight presidents vowing to end America's + addiction to foreign oil +~ +A lot of disappointed people have been left standing on the street corner +waiting for the bus marked "Perfection". + -- Donald Kennedy +~ + In his closing discussion on loving-kindness, Buddhaghosa asks: "What is the +proximate cause of loving-kindness?" The answer is the observation of +lovableness in the person to whom you are attending. + Bring to mind right now someone whom you find lovable. It could be a person +you have a romance with, or a child, or a dear friend, or a great teacher-- +someone to whom your heart would leap like a deer in the forest if this person +were to walk through the door, someone whose presence is so lovable that a +gladness arises on seeing him or her. If you can sense that in a dear friend, +then try to seek out the lovableness of a neutral person. Then, finally, when +you break down all the barriers, see it in a person who has done you injury. + It's a great key if you can seek out something to love, even in the enemy. +Bear clearly in mind that this does not endorse or embrace evil. The crucial +point here is to be able to slice through like a very skilled surgeon, +recognizing vicious behavior that we would love to see annihilated as separate +from the person who is participating in it. The doctor can be optimistic. A +cure is possible: the person is not equivalent to the action or the +disposition. Moreover there is something there that we can hold in affection, +with warmth. That really seems to be a master key that can break down the +final barrier and complete the practice. + One way of approaching this is to look at the person you hold in contempt, +and try to find any quality he might share with someone you deeply admire and +respect. Is there anything at all noble to be seen, anything that would be +akin to what a truly great spiritual being would display? Focus on that: +There is something there that you can love. The rest is chaff, that hopefully +will be blown away quickly, to everyone's benefit. It is as if you could see +a little ray of light from within, knowing that its source is much deeper than +the despicable qualities on the outside. That light is what you attend to. +(p. 112) + -- B. Alan Wallace, "The Four Immeasurables: Practices to Open the Heart", + edited by Zara Houshmand, published by Snow Lion Publications +~ +Question: I have the strong wish to be reborn in any of the realms in a +position to truly help other sentient beings in that realm. Is it wrong for +me in these circumstances not to have the strong wish to leave the wheel of +cyclic existence? + +Answer: Your wish to stay in order to help is certainly right. One of +Shantideva's prayers, roughly translated is, "As long as there is space, I +will remain with sentient beings, to serve and help them." Therefore, I also +am trying to practice this. Helping others is the real purpose of life; it +will bring the most satisfaction. The one action of helping others out of a +sincere motivation brings two results--satisfaction for yourself and benefit +to others. It is most beautiful. + One might ask whether there is a contradiction between a Bodhisattva's +developing a determination to leave cyclic existence by viewing it as faulty +and a Bodhisattva's wishing to remain in cyclic existence in order to help +others. An answer to this is given in Bhavaviveka's Heart of the Middle Way: +...because of being under the influence of love and compassion, one is not +captivated by the idea of retreating into solitary peace and, with an attitude +of seeking to bring about the welfare of other sentient beings, remains in +cyclic existence. This attitude is really marvelous. Though you are really +fed up with cyclic existence, still because of a willingness and a +determination to serve others, you voluntarily accept to remain. + However, as is indicated by the frequently cited example of a lotus that is +produced from mud but not polluted by it, a Bodhisattva stays in cyclic +existence but is not affected by its faults. It would indeed be hypocritical +to claim from one's mouth that one had taken up the practice of a Bodhisattva +but actually to be happily stuck in cyclic existence with great attachment. +(p. 91) + -- H.H. the Dalai Lama of Tibet Tenzin Gyatso, "The Dalai Lama at Harvard: + Lectures on the Buddhist Path to Peace", translated and edited by Jeffrey + Hopkins, published by Snow Lion Publications +~ + Sutras, tantras, esoteric instructions, and experiences teach + The vital point of deathlessness, awakening without meditating: + How this body of karma fully ripening + Arises as a naturally pure awareness body. + Visualize the fully ripening karmic body as the deity's form + And meditate without fixation on it. + It is itself inseparable from mind. + No essence of mind is established, + So where is something that dies? + "Death" is just a concept. + The hosts of concepts are nonexistent phenomena of samsara and nirvana. + -- Mokchokpa + + According to this and other statements, since one's own mind in essence has +no real existence whatsoever, it was always unborn. Therefore the great +natural liberation of deathlessness is attained. As for this body of fully +ripening karma, since it is a conglomeration of inert matter, it is not a +basis on which to attribute the designations of birth or death. In fact, the +body even arises as a mere appearance of mind. + When one gains confidence in the realization that the mind is unborn and +undying, then the body appears as the deity's form in mahamudra and one +becomes bound to basic space without erring into the path of deluded +appearance. By this kind of instruction one discovers the kaya of union in +this lifetime. Even just hearing it can cause one to get enlightened in the +intermediate state as the sambhogakaya of the victors. Of the Five Golden +Dharmas, it is said to be like the ripened fruit. (p. 248) + -- Jamgon Kongtrul Lodro Taye, "The Treasury of Knowledge, Book Eight, Part + Four: Esoteric Instructions, A Detailed Presentation of the Process of + Meditation in Vajrayana", translated by Sarah Harding, published by Snow + Lion Publications +~ + Buddhas and Bodhisattvas see clearly that our neglect of others, our self- +preoccupation and our disregard for the connection between actions and their +effects are responsible for all our miseries. The feeling that it doesn't +matter what we do as long as we can get away with it kills our chances of +liberation and enlightenment. Our selfishness robs us of worldly and +supramundane good qualities, leaving us naked and empty-handed. It separates +us from happiness now and in the future and fetters us to suffering. + Resolve never again to let yourself be dominated by this mean and selfish +way of thinking and do everything in your power to combat it. Your happiness +begins the moment you recognize self-cherishing as your chief foe. There are +many good reasons why cherishing others makes sense. Shantideva says: + The state of Buddhahood is accomplished + Equally through living beings and Victorious Ones. + What kind of behavior then is it to revere + Victorious Ones but not living beings? + ...If we truly want to please Buddhas and Bodhisattvas and all those noble +beings in the world whom we admire and whose sole guiding principles are their +affection, love and compassion for others, we can do nothing better than to +cherish living beings. (p. 100) + -- "The Three Principal Aspects of the Path", an oral teaching by Geshe + Sonam Rinchen, translated and edited by Ruth Sonam, published by Snow + Lion Publications +~ +How Purification Works + + During Nyungne [fasting] practice, true purification is possible primarily +because of the power of Chenrezig's compassion and blessing, as well as our +faith, devotion, and correct motivation to do the practice. When such causes +and conditions come together, a result inevitably occurs, and this result is +understood as the interdependently-arising nature of all phenomena. + For the most part, enlightened and unenlightened phenomena all arise due to +this interdependently-arising nature. As a spiritual practitioner, the basic +qualities one must bring to the practice are faith, devotion, and a trust in +the power of the practice and Chenrezig. These qualities stem from our own +pure nature of mind, a purity that is identical to Chenrezig's heart, that is, +unceasing love and compassion. When these two things are combined together, +our devotion and faith and Chenrezig's love and compassion, one could say +miracles happen; a true purification takes place. + It has been said that when one is sitting before the mandala of Chenrezig, +one should believe that although Chenrezig is not physically visible to us, in +fact he is really there in front of us. Just as we would be very careful of +our thoughts and behavior if we were in the presence of a powerful and +clairvoyant enlightened guru, in the same way we must generate vigilance so +that we don't act shamefully in front of this great being. If we develop such +vigilance and noble habit, then our negativities will automatically decrease. +(p.11) + -- Wangchen Rinpoche, "Buddhist Fasting Practice: The Nyungne Method of + Thousand-Armed Chenrezig", published by Snow Lion Publications +~ + Reflect on the basic pattern of our existence. In order to do more than +just barely survive, we need shelter, food, companions, friends, the esteem of +others, resources, and so on; these things do not come about from ourselves +alone but are all dependent on others. Suppose one single person were to live +alone in a remote and uninhabited place. No matter how strong, healthy, or +educated this person were, there would be no possibility of his or her leading +a happy and fulfilling existence.... Can such a person have friends? Acquire +renown? Can this person become a hero if he or she wishes to become one? I +think the answer to all these questions is a definite no, for all these +factors come about only in relation to other fellow humans. + When you are young, healthy, and strong, you sometimes can get the feeling +that you are totally independent and do not need anyone else. But this is an +illusion. Even at that prime age of your life, simply because your are a +human being, you need friends, don't you? This is especially true when we +become old and need to rely more and more on the help of others: this is the +nature of our lives as human beings. + In at least one sense, we can say that other people are really the principal +source of all our experiences of joy, happiness, and prosperity, and not only +in terms of our day-to-day dealings with people. We can see that all the +desirable experiences that we cherish or aspire to attain are dependent upon +cooperation and interaction with others. It is an obvious fact. + Similarly, from the point of view of a Buddhist practitioner, many of the +high levels of realization that you gain and the progress that you make on +your spiritual journey are dependent upon cooperation and interaction with +others. Furthermore, at the stage of complete enlightenment, the +compassionate activities of a buddha can come about spontaneously only in +relation to other beings, for those beings are the recipients and +beneficiaries of those enlightened activities. (p.5) + -- Tenzin Gyatso, the Fourteenth Dalai Lama, "The Compassionate Life" +~ + The difficulty with a purely materialistic interpretation of life is that, +in addition to ignoring an entire dimension of the mind, it does not deal +effectively with the problems of this life. A materialistic mind is an +unstable mind, for its happiness is built on transient, physical +circumstances. Mental disease is as high among the affluent as it is among +the poor, which is a clear indication of the limitations of the approach. + Although it is essential to maintain a reasonable material basis on which to +live, the emphasis in one's life should be on cultivating the mental and +spiritual causes of happiness. The human mind is very powerful and our +worldly needs are not so great that they must demand all of our attention, +especially in light of the fact that materialistic success solves so few of +the many challenges and problems that confront men and women throughout their +lives, and it does nothing for them at death. + On the other hand, if one cultivates spiritual qualities such as mental +harmony, humility, non-attachment, patience, love, compassion, wisdom and so +forth, then one becomes equipped with a strength and intelligence able to deal +effectively with the problems of this life; and because the wealth one is +amassing is mental rather than material, it will not have to be left behind at +death. There is no need to enter the after-death state empty-handed. (31) + -- H.H. the Dalai Lama, "The Path to Enlightenment", edited and translated + by Glenn H. Mullin, published by Snow Lion Publications +~ + The bardo* of this life does not last forever. We know that, like a guest +in a hotel, our mind is only temporarily sheltered in this body. As we face +the challenges of this life and the impending challenges of the bardos to +come, how does engaging in the three-stage process of study, contemplation and +meditation help us? By applying ourselves to these three, we acquire the +skills to stabilize our mind and we develop actual insight into how our mind +functions. First we gain an understanding of the nature of mind; then, we +experience that nature; and finally, we arrive at the ultimate benefit, which +is fully realizing that nature. + When we practice these stages of the path, it is like accumulating the exact +things we will need to take with us on our trip. When we are ready to pack +our suitcase, we will have what we need without looking further. We will not +have to go out at the last minute and buy a map or a guidebook. We will not +have to worry about whether we are forgetting something crucial. + We have knowledge and experience that has blossomed into realization; +therefore we can handle any situation. We have confidence in ourselves, in +the teachings, and the guidance of our lineage teachers. At this point, we +can let go of all our doubt and hesitation. We can simply relax and be who we +are, wherever we are. (p.58) + * in-between state, interval + -- The Dzogchen Ponlop Rinpoche, "Mind Beyond Death", published by Snow + Lion Publications +~ + The thoughts that in this year and month + I will put right all my tasks and plans + And then start a perfect dharma practice + Is in fact the devil which brings all downfalls. + + The lack of death awareness prevents one from undertaking the practice of +dharma. This is very true: If one is not aware of the eventuality of death, +one will be totally concerned and preoccupied with the affairs of this +lifetime alone, and with actions that are just for the benefit of this +lifetime. Such ventures may take all one's time and energy, but no matter how +important they appear to be, since they are directly related to this lifetime +alone, their benefits are limited--once one leaves the present body, their +benefit ends. Even though one might have a best friend, when one has to leave +the body, one cannot take the friend along. + ...Think that after twenty or thirty years even the Dalai Lama will also be +no more. While I am alive, there will be people who are, from the depths of +their hearts, prepared to give their lives for my sake, but on the day when I +have to leave, I cannot take even one among them with me. Neither will I be +able to take any of my possessions, even the body which I have always +preserved and protected. This also will be left behind. At that time of my +death, what will benefit is only the positive seeds that are imprinted upon my +consciousness. No other factors will help at that time. (p.106) + -- H.H. the Dalai Lama, "The Path to Bliss", translated by Geshe Thupten + Jinpa, edited by Christine Cox, published by Snow Lion Publications +~ + At the beginning of the process of deity meditation and mantra repetition +one meditates on emptiness, settling the non-inherent existence of oneself and +the deity through a reasoning such as that of dependent-arising--the fact that +both oneself and the deity arise in dependence on their respective bases of +designation. One's own final nature and the final nature of the deity are the +same, an emptiness of inherent existence. + To perform deity yoga one does not just withdraw ordinary appearances and +then appear as a deity but causes the mind realising emptiness itself to +appear as a deity. Thus, it is essential initially to meditate on emptiness, +cleansing all appearances in emptiness. One then uses that wisdom +consciousness realising emptiness as the basis of emanation of a divine body. +This must be done at least in imitation of a consciousness actually doing +this, for meditation on a truly existent divine body, instead of helping, will +only increase adherence to inherent existence. Meditated properly, the +appearance of a divine figure is the sport of the ultimate mind of +enlightenment, first in imitation and later in fact. (p.39) + -- His Holiness the Dalai Lama, Tsong-ka-pa, and Jeffrey Hopkins, "Deity + Yoga in Action and Performance Tantra", published by Snow Lion Pub. +~ + If one could attain the state of perfect buddhahood that is free from all +faults, that sees directly all aspects of the qualities to be cultivated and +faults overcome in the quest for enlightenment, and that is physically adorned +with the marks and signs of perfection, the mere perception of which is +beneficial, then one would be beyond the distinction of feeling attraction or +aversion toward the infinite sentient beings. One would regard all beings +with an equal compassion, and would have the ability to really benefit them. +Think, "I should make every effort to attain this all-beneficial state." + In brief, the motivation should be, "For the ultimate benefit of the +sentient beings, who are as infinite as the sky is vast, I must attain the +state of a peerless, perfect, pure buddha." This is the aspirational aspect of +the bodhimind. + -- Glenn Mullin, "The Practice of Kalachakra", foreword by H.H. the Dalai + Lama, published by Snow Lion Publications +~ +After suffering severe puncture wounds without shooting any goop on my +opponent, I realized I was simply outgunned. Never bring caulk to a +nailgun fight. + -- Stephanie S. Thompson +~ +My cat Elea added a trick to her repertoire this morning. +I sleep in a bit on the weekend, and both cats protest by +visiting the bed every few minutes until I get up. +Elea, being toothless, is afflicted with a mean case of drool when +she's hungry. So this morning I got a "drool shower" since she +sat next to my face and shook her head vigorously, +flinging drool everywhere (especially onto my face). +I know this wasn't just a fluke because a few minutes later +she came back and did it again. + -- fred t. hamster +~ +On Arcadia Asylum, pioneering creator in Second Life... + "Nope, she can't log into Arcadia, Aley Arai, nor Lora Lemon. She's great + at building, but she doesn't think she should have to pay the Lindens for + anything, so she doesn't, and they get mad about that and close her + accounts. :D" +~ +The important thing is the obvious thing nobody is saying. + -- William S. Burroughs +~ + Our fundamental nature--what we term 'the buddha nature', the very nature of +our mind, is inherently present within us as a natural attribute. This mind +of ours, the subject at hand, has been going on throughout beginningless time, +and so has the more subtle nature of that mind. On the basis of the +continuity of that subtle nature of our mind rests the capacity we have to +attain enlightenment. This potential is what we call 'the seed of +buddhahood', 'buddha nature', 'the fundamental nature', or 'tathagatagarbha'. + We all have this buddha nature, each and every one of us. For example, this +beautiful statue of Lord Buddha here, in the presence of which we are now +sitting, is a representation that honours someone who attained buddhahood. He +awakened into that state of enlightenment because his nature was the buddha +nature. Ours is as well, and just as the Buddha attained enlightenment in the +past, so in the future we can become buddhas too. + ...In any case, there dwells within us all this potential which allows us to +awaken into buddhahood and attain omniscience. The empowerment process draws +that potential out, and allows it to express itself more fully. When an +empowerment is conferred on you, it is the nature of your mind--the buddha +nature--that provides a basis upon which the empowerment can ripen you. +Through the empowerment, you are empowered into the essence of the buddhas of +the five families. In particular, you are 'ripened' within that particular +family through which it is your personal predisposition to attain buddhahood. +(p.29) + -- His Holiness the Dalai Lama, "Dzogchen: The Heart Essence of the Great +Perfection", translated by Thupten Jinpa and Richard Barron, Foreword by +Sogyal Rinpoche, edited by Patrick Gaffney, published by Snow Lion Pub. +~ +For achieving calm abiding... your mind must have two qualities: + -great clarity of both the object and the consciousness itself + -staying one-pointedly on the object of observation. + Two factors prevent these from developing--laxity and excitement. Laxity +prevents the development of clarity, and excitement prevents the stability of +staying with the object. + That which interferes with the steadiness of the object of observation and +causes it to fluctuate is excitement, which includes any scattering of the +mind to an object other than the object of meditation. To stop that, withdraw +your mind more strongly inside so that the intensity of the mode of +apprehension of the object begins to lower. If you need a further technique +to withdraw the mind, it helps to leave the object of meditation temporarily +and think about something that makes you more sober, such as the imminence of +death. Such reflections can cause your heightened mode of apprehension of the +object, the mind's being too tight, to lower or loosen somewhat, whereby you +are better able to stay on the object of observation. + It is not sufficient just to have stability; clarity is also needed. That +which prevents clarity is laxity, which is a case of the mind's becoming too +relaxed, too loose, lacking intensity--the tautness of the mind having become +weak, caused by over-withdrawal inside. Heaviness of mind and body can lead +to becoming lax, which can lead to a type of lethargy in which, losing the +object of observation, you have as if fallen into darkness; this can lead even +to sleep. When this begins to occur, it is necessary to raise, to heighten, +this excessive declination of the mind by making it more taut, more tight. To +accomplish this, it helps to brighten the object of meditation or, if that +does not work, to leave the object of meditation temporarily and think on +something that makes you joyous, such as the wonderful opportunity that a +human lifetime affords for spiritual practice. If that does not work, you can +even leave off meditating and go to a high place or where there is a vast +view. Such techniques cause your deflated mind to heighten, to sharpen. + While holding the object of observation with mindfulness, investigate with +introspection from time to time to see whether the mind has come under the +influence of laxity or excitement and determine the best practice for lowering +or heightening it. In time, your will develop a sense of the proper level of +tautness of the mind such that you will be able to catch laxity and excitement +just before they arise and prevent their arising. (p.50) + -- H.H. the Dalai Lama, Dzong-ka-ba and Jeffrey Hopkins, "Yoga Tantra: Paths +to Magical Feats", translated and edited by Jeffrey Hopkins, published by Snow +Lion Publications +~ +The position of the body [during meditation] is very important because the +channels within the body will follow the external disposition of the body. +The way the body is placed will set the channels; and the winds, of course, +flow inside the channels, so if they are properly set, the winds will flow +properly. Mind follows the wind. To focus the mind properly, the winds must +also be functioning properly. (p.39) + -- Drikung Kyabgon Chetsang Rinpoche, "The Practice of Mahamudra", translated + by Robert Clark, ed. by Ani K. Trinlay Chodron, published by Snow Lion +~ + The real source of my suffering is self-centeredness: my car, my possession, +my well-being. Without the self-centeredness, the suffering would not arise. +What would happen instead? It is important to imagine this fully and to focus +on examples of your own. Think of some misfortune that makes you want to lash +out, that gives rise to anger or misery. Then imagine how you might respond +without suffering. Recognize that we need not experience the misery, let +alone the anger, resentment, and hostility. The choice is ours. + Let's continue with an example. You see that there is a dent in the car. +What needs to be done? Get the other driver's license number, notify the +police, contact the insurance agency, deal with all the details. Simply do it +and accept it. Accept it gladly as a way to strengthen your mind further, to +develop patience and the armor of forbearance. There is no way to become a +Buddha and remain a vulnerable wimp. + Patience does not suddenly appear as a bonus after full enlightenment. Part +of the whole process of awakening is to develop greater forbearance and +equanimity in adversity. Santideva, in the sixth chapter of his Guide to the +Bodhisattva's Way of Life, eloquently points out that there is no way to +develop patience without encountering adversity, and patience is indispensable +for our own growth on the path to awakening. (p.66) + -- B. Alan Wallace, "The Seven-Point Mind Training", edited by Zara + Houshmand, published by Snow Lion Publications +~ + While the great adept [Tangtong Gyalpo] did not stray from vajralike +meditative concentration on the peak of glorious Riwoche, the ornamental wheel +of his inexhaustible enlightened body, speech, and mind manifested in three +great regions of Kham. + At Gyalmorong, a person who had received the Path with the Result at Sakya, +and who meditated single-mindedly on the Time of the Path during four sessions +and on the Profound Path Guruyoga, saw the great adept to be Vajradhara, the +lord of all spiritual families, and made countless prostrations. + ...A person who recited a thousand of the heart-mantra of Tara every day, +declared, "This isn't Avalokiteshvara. It's Tara." + Also, a person said, "This is the Great Adept of Iron Bridges. O great +adept, why do different visual manifestations appear to us?" The great adept +replied: + By bringing the vital winds + and mind under control, + taking control of how things appear to myself, + overwhelming how things appear to others, + and positioning magical bodies, + I display whatever will tame sentient beings + according to their various inclinations. + (p.410) + -- Cyrus Stearns, "King of the Empty Plain: The Tibetan Iron Bridge Builder + Tangtong Gyalpo", a Tsadra Foundation Series book, published by Snow Lion +~ + When I was a boy, Ling Rinpochay, who was then my junior tutor, was always +very stern; he never smiled, not even a little. This bothered me a lot. By +wondering why he was so humorless, I examined more and more what I was doing +in my own mind. This helped me develop self-awareness with regard to my +motivation. By my early twenties when I had matured, Ling Rinpochay +completely changed; he always had a big smile when we were together. + Effective practice of the morality of individual liberation depends upon +sound, long-term motivation. For example, one should not become a monk or a +nun to avoid having to work at a worldly job for food and clothing. Also, it +is not sufficient merely to seek to avoid difficulty in this lifetime. To be +motivated by such trifling purposes does not help to achieve freedom from +cyclic existence--the ultimate reason to practice the morality of individual +liberation. + This is confirmed by Buddha's life story. One day Shakyamuni slipped +outside the palace wall to experience life for himself. For the first time he +saw a sick person, an old person, and a corpse. Deeply troubled by the +suffering of sickness, aging, and death, he came to the conclusion that +worldly life is without substance. Later, inspired by several religious +practitioners, Buddha became captivated by the possibility of a higher, more +meaningful, spiritual life. At that point he escaped from the palace, leaving +his ordinary life behind to pursue that vision. + What does this teach us? Like Buddha we need to begin by becoming concerned +about the suffering of cyclic existence and by turning away from temporary +distractions. Influenced by this new attitude, we must take up a system of +morality by renouncing cyclic existence and by taking vows of pure behavior +through seeking to avoid the ten nonvirtues. (p.29) + -- His Holiness the Dalai Lama, "How to Practice: The Way to a Meaningful + Life", translated and edited by Jeffrey Hopkins +~ + Tsong-ka-pa's intention in praising Buddhism is not to insult other +teachers. Statements of the greatness of Buddhism are made in order to +develop one-pointedness of mind toward practice, for one who is able to +practise Buddhism must generate effort to do so. It is necessary for him to +have confidence in Buddha's teaching from the round orb of his heart. + There is a Tibetan saying that one cannot sew with a two-pointed needle or +achieve aims with a two-pointed mind. Similarly, if a practitioner is +hesitant, he will not put great force into the practice of any one system. +Tsong-ka-pa states that Buddhism is the best in order that persons who would +be helped more through engaging in the Buddhist path than through another +system might not be diverted to another path. (p.48) + -- H.H. the Dalai Lama, Tsong-ka-pa and Jeffrey Hopkins, "Tantra in Tibet", + published by Snow Lion Publications +~ + At the level of conventional truth we all naturally possess both the desire +and the potential to overcome suffering and to attain happiness. In this +context, we can reflect upon the Buddha's teachings on the Four Noble Truths +and the Two Truths, and on the basis of such reflection we gradually develop +an understanding of how we can gain freedom from suffering and of the +potential we possess within ourselves for accomplishing such a goal. + We can reflect further that: 'Just like me, all other sentient beings +possess this same desire and potential to be happy and overcome suffering', +and ask ourselves: 'If I continue to be guided by my own self-centredness and, +through my single-pointed concern for my own well-being, continue to ignore +the well-being of others, what will the consequences be?' + Then we can reflect: 'From beginningless lifetimes I have harboured this +self-cherishing attitude and have grasped onto the notion of an intrinsically +real, enduring self. I have nurtured these two thoughts of self-cherishing +and self-grasping deep in my heart as if they are twin jewels. But where has +this way of being led me? By pursuing the dictates of my self-grasping and +self-centredness, have I actually managed to attain the fulfilment of my self- +interest? If it were possible, surely by now I should have achieved my goal. +But I know that this is not the case.' + We should then compare ourselves to enlightened beings such as the Buddha +Shakyamuni who achieved total victory over all defilements and perfected all +qualities of goodness. We should then ask ourselves: 'How did the Buddha +accomplish this?' Through contemplation we will come to recognise that, at a +certain point in his existence, the Buddha reversed the normal way of thinking +and being. In the place of self-cherishing he cultivated the thought of +cherishing the well-being of other sentient beings, and in place of self- +grasping he cultivated the wisdom realising the absence of self-existence. In +this way he attained full awakening. (p.30) + -- H.H. the Dalai Lama, "Lighting the Way", translated by Geshe Thupten + Jinpa, published by Snow Lion Publications +~ +In the history of the Nyungne tradition, many practitioners have been able to +overcome incurable disease through the practice of Nyungne. We could say +miracles like this literally do take place, although in the Buddhist +understanding, overcoming great obstacles and disease would be considered +blessings. A miracle is something else. It is the enlightened power that is +demonstrated by enlightened masters. A true miracle in the Buddhist sense +would be like the miracle of Milarepa entering into a little horn while his +student, Rechungpa, sees him in his usual size yet he is inside the horn. Or +like the miracle of Milarepa sitting on a lake and people seeing that he +hasn't become any larger nor has the lake shrunk in size, yet he is completely +covering it. These are real, enlightened miracles.(p.13) + -- Wangchen Rinpoche, "Buddhist Fasting Practice: The Nyungne Method of + Thousand-Armed Chenrezig", published by Snow Lion Publications +~ + [In listening to teachings one] of the defects is to listen in a way that is +like a container with holes. This means that even though we are listening to +the teachings, we do not retain their contents. In this case we lack +mindfulness and memory. Practice of Dharma means that we should be able to +benefit from what we have heard. It is not a pastime, like listening to a +story. The teachings give us guidance on how to live meaningful lives and how +to develop proper attitudes. So in order to benefit from the teachings, we +must retain them with mindfulness. + In all kinds of learning processes, listening, reading, etc., we must pay +full attention and should endeavor to remember their contents. When our +interest is halfhearted, we only remember half the points, and we retain them +for only a short time. We should reflect and think about whatever we have +heard, over and over again. In this way, the knowledge will stay in our mind +for a long time. Another technique for remembering instructions is debate as +it is practiced in the traditional debating schools. (p.22) + -- H.H. the Dalai Lama, "Stages of Meditation", root text by Kamalashila, + translated by Geshe Lobsang Jordhen, Losang Choephel Ganchenpa, and + Jeremy Russell, published by Snow Lion Publications +~ + We are duped by maya. The whole display of our senses has tricked us into +believing it and thus seduces us into the world of suffering. And the +illusionist is that old trickster, one's own mind. But when this illusory +nature is recognized to be just that, one is released from the bondage of the +magic show, at which time it becomes a wonderful spectacle, even a display of +the unimpeded creativity and freedom of mind. Then maya itself is both the +medium for this realization and the expression of it. + This conscious and intentional method of relating to all phenomena as +illusion is thus cast in a totally positive light on the spiritual path, a +complete turn-around from the original negative valuation of it as deceit. +Now illusion is seen as illumination and opportunity. The nature of our +relationship with it is the salient point, rather than its own nature, which +certainly does not exist anyway, in any way. + Aryadeva says: + Since everything is an illusory display, + it is possible to attain enlightenment. + The transformation of the maya concept from something to escape to something +to engage may be loosely correlated with the shift of emphasis on +understanding emptiness that emerged in the mahayana teachings. A further +development may be seen in the vajrayana teachings with the esoteric +instruction known as Illusory Body (sgyu lus). This occurs as one of the Six +Dharmas of Niguma and in other configurations of completion stage practices in +many lineages. (p.40) + -- Sarah Harding, "Niguma, Lady of Illusion", a Tsadra Foundation Series + book, published by Snow Lion Publications +~ + Afflictions are classed as peripheral mental factors and are not themselves +any of the six main minds [eye, ear, nose, tongue, body and mental +consciousnesses]. However, when any of the afflicting mental factors becomes +manifest, a main mind [a mental consciousness] comes under its influence, goes +wherever the affliction leads it, and 'accumulates' a bad action. + There are a great many different kinds of afflictions, but the chief of them +are desire, hatred, pride, wrong view and so forth. Of these, desire and +hatred are chief. Because of an initial attachment to oneself, hatred arises +when something undesirable occurs. Further, through being attached to oneself +the pride that holds one to be superior arises, and similarly when one has no +knowledge of something, a wrong view that holds the object of this knowledge +to be non-existent arises. + How do self-attachment and so forth arise in such great force? Because of +beginningless conditioning, the mind tightly holds to 'I, I' even in dreams, +and through the power of this conception, self-attachment and so forth occur. +This false conception of 'I' arises because of one's lack of knowledge +concerning the mode of existence of things. The fact that all objects are +empty of inherent existence is obscured and one conceives things to exist +inherently; the strong conception of 'I' derives from this. Therefore, the +conception that phenomena inherently exist is the afflicting ignorance that is +the ultimate root of all afflictions. (p.26) + -- H.H. the Dalai Lama, "The Buddhism of Tibet", translated and edited by + Jeffrey Hopkins, with Anne Klein, published by Snow Lion Publications +~ + With regard to awareness of the present moment, our mind is utterly +insubstantial and yet has this characteristic of luminosity (Tib. salwa). +"Luminosity" here simply means the cognitive capacity, the fact that our mind +can know, experience, feel, and so on. This awareness always occurs in the +present. When we are not thinking of the past or thinking of the future, when +we're letting our mind simply rest in the direct experience of the present +moment, then this awareness or lucidity emerges as an unfabricated +intelligence. + Initially we do this very briefly, for one moment, two moments, and so on, +but as we work with this, it starts to take on a momentum. However, it's +important not to interfere with the naturalness of this awareness by +appraising what is occurring, which means that we shouldn't think, "Well, this +is happening, that is happening, I'm aware of this, I'm aware of that." Nor +should we judge what's happening by thinking, "Well, this is good, this is +what's supposed to be happening," or, "This is bad, this isn't what's supposed +to be happening." + On the other hand, we do need to "plant the watchman of mindfulness and +alertness," which means that we maintain some intentional awareness of what is +occurring. Here, mindfulness means a simple, direct recollection of what +we're trying to do. In other words, mindfulness is recollecting that we are +trying to rest in a direct experience of the present moment. Alertness then +is that faculty of mind that becomes aware when we become distracted from this +present experience. However, this watchfulness or, this watchman, has to be +very relaxed and gentle. It can't be too heavy-handed, otherwise the whole +thing becomes a conceptual judgement. The technique of mind is to rest in +this awareness of the present moment with a gentle watchman of mindfulness and +alertness. (p.36) + -- Khenchen Thrangu Rinpoche, "Pointing Out the Dharmakaya", foreword by + the Dalai Lama, introduction by Lama Tashi Namgyal, published by Snow + Lion Publications +~ +The future is here. It's just not widely distributed yet. -- William Gibson +~ + Complete spiritual fulfillment requires the ability to act compassionately, +and that involves making practical distinctions. Therefore, Tsong-kha-pa +insists upon the clarifying power of analysis that is not ultimate, analysis +that operates within the constraints and boundaries of conventional fact and +language so as to illuminate what does and what does not exist, what is and +what is not helpful. + Not all useful analysis need immediately reduce everything to emptiness. In +other words, we can learn valuable, practical things by analyzing which car is +good to drive, which action is good to do, which seed is good to plant, +without at each step interrogating the final ontological status of the car, +action, or seed. + ...A pervasive sense that things are real and solid and exist just as they +appear is woven right into the fabric of the world as we experience it. While +tables do exist, we have yet to see them just as they are. Our very +perception of them--while a valid source of information--is at the same time +contaminated with a layer of distortion. That distortion is the appearance of +the table as something that is able to be there on its own power, something +that exists in and of itself. + Thus when we begin to see, or even to suspect, that things lack essence and +are not at all as we had supposed, we may feel terrified, as though our world +is coming apart at the seams or evaporating beneath our feet. We calm those +fears by again remembering that it is not that there is nothing. There is +dependent arising, just as there has always been. Analysis threatens nothing +but the false overlay, the distorting superimposition, which has caused us and +others so much misery. (p.43) + -- Guy Newland, "Introduction to Emptiness: As Taught in Tsong-kha-pa's + Great Treatise on the Stages of the Path", published by Snow Lion Pub. +~ + The play of this divine mind, + The union of bliss, the supreme father, and emptiness, + Is unlimited and thus beyond concept. + -- The Seventh Dalai Lama, Kelsang Gyatso + + Cultivate a state of mind focused on bliss and emptiness as forcefully as +possible. The wisdom of bliss and emptiness is compared to space, which is +non-obstructive and expansive. Because offerings are the manifestation of the +wisdom of bliss and emptiness, these substances are called "offerings of +Samantabhadra (All-Good)." + Generally speaking, a bodhisattva named Samantabhadra is renowned for his +elaborate offerings to the buddhas and bodhisattvas. But here the term all- +good (samantabhadra) refers most appropriately to the wisdom of bliss and +emptiness. It is all-good from the viewpoint of emptiness and also from the +viewpoint of bliss. This emptiness is the ultimate truth and also the +ultimate virtue. And the wisdom of great bliss is the clear light wisdom: +With a feeling of joy, imagine that offerings having such a nature pervade +entire space. (p.64) + -- H.H. the Dalai Lama, "The Union of Bliss and Emptiness: Teachings on the + Practice of Guru Yoga", translated by Thupten Jinpa, published by Snow + Lion Publications +~ + The spiritual path is truly simple. It is simple because it is not about +acquiring, accumulating, or achieving anything. It is all about giving up +what we don't need. It's about giving up what isn't useful instead of +acquiring things with the idea of going somewhere or achieving something. +That was the old game. That game which we have been playing for a long time +is like a vicious circle. It has no end. + Sometimes the spiritual search itself prevents us from seeing the truth that +is always one with us. We have to know when to stop the search. There are +people who die while they are searching for the highest truth with +philosophical formulas and esoteric techniques. For them spiritual practice +becomes another egoic plot which simply maintains and feeds delusions. +Amazing! Buddha, God, truth, the divine, the great mystery, whatever you have +been searching for, is here right now. (p.37) + -- Anam Thubten, "No Self, No Problem", edited by Sharon Roe, published + by Snow Lion Publications +~ + Practice of the morality of individual liberation, whether lay or monastic, +leads to contentment.... Examine your attitudes toward food, clothes, and +shelter. By reducing expectations you will promote contentment. The extra +energy which is released should be devoted to meditation and to achieving +cessation of problems, corresponding to the fourth and third noble truths. In +this way, contentment is the basis, and the resulting action is called "liking +meditation and abandonment." + We should be contented in material areas, for those are bound by limitation, +but not with regard to the spiritual, which can be extended limitlessly. +Though it is true that a discontented person who owned the whole world might +want to own a tourist center on the moon, that person's life is limited, and +even the amount that can be owned is limited. It is better right from the +beginning to be contented. + However, with regard to compassion and altruism there is no limit, and thus +we should not be content with the degree that we have. We are just the +opposite; in the spiritual field we are content with slight amounts of +practice and progress, but materially we always want more and more. It should +be the other way around. Everyone needs to practice this, whether lay or +monastic. (p.67) + -- His Holiness the Dalai Lama, "How to Practice: The Way to a Meaningful + Life", translated and edited by Jeffrey Hopkins +~ + If we view the world's religions from the widest possible viewpoint and +examine their ultimate goal, we find that all of the major world religions, +whether Christianity or Islam, Hinduism or Buddhism, are dedicated to the +achievement of permanent human happiness. They are all directed toward that +goal. All religions emphasize the fact that the true follower must be honest +and gentle, in other words, that a truly religious person must always strive +to be a better human being. To this end, the different world religions teach +different doctrines which will help transform the person. In this regard, all +religions are the same, there is no conflict. This is something we must +emphasize. We must consider the question of religious diversity from this +viewpoint. And when we do, we find no conflict. + ...Different kinds of food have different tastes: one may be very hot, one +may be very sour, and one very sweet. They are opposite tastes, they +conflict. But whether a dish is concocted to taste sweet, sour, or hot, it is +nonetheless made in this way so as to taste good. Some people prefer very +spicy, hot foods with a lot of chili peppers. Many Indians and Tibetans have +a liking for such dishes. Others are very fond of bland tasting foods. It is +a wonderful thing to have variety. It is an expression of individuality; it +is a personal thing. Likewise, the variety of the different world religious +philosophies is a very useful and beautiful thing. (p.13) + -- "Answers: Discussions with Western Buddhists by the Dalai Lama", edited + by Jose Ignacio Cabezon, published by Snow Lion Publications +~ + The basic principles and precepts of all true religions are very pure. What +you see as impure is simply the inability of those who adhere to them. So as +Buddhists, for instance, if you fail to embrace and internalize the basic +principles and precepts of the practice, then your mind is always going to be +overrun by the five mental afflictions. These negative afflictions are +desire, hatred, jealousy, pride, and ignorance. They are the basic obstacles +which impede you from making any true progress on the path. It is, in fact, +the function of the preliminary training to prepare the field of the mind so +that you are actually able to put to rest the gross delusions and give rise to +your innermost qualities. This allows you to actualize your true bodhicitta +nature, the mind which cares about others more than self. + Leaving aside the idea of the so-called spiritual path, or religion, if you +are able to uproot these delusions, the stones and boulders, from the field of +your mind, then you will become an honorable person, respected in the world, +with an easier, flexible attitude toward yourself and others. If you are +able, through your development of wisdom and skillful means, to unite the +teachings with your life, then true results will be achieved. (p.96) + -- Ven. Gyatrul Rinpoche, "Meditation, Transformation, and Dream Yoga", + trans. by B. Alan Wallace and Sangye Khandro, published by Snow Lion +~ + What premises or grounds do we have for accepting that mental afflictions +can be ultimately rooted out and eliminated from our mind? In Buddhist +thought, we have three principal reasons for believing that this can happen. +One is that all deluded states of mind, all afflictive emotions and thoughts, +are essentially distorted in their mode of apprehension, whereas all the +antidotal factors such as love, compassion, insight, and so on not only are +undistorted, but they also have grounding in our varied experience and in +reality. + Second, all these antidotal forces also have the quality of being +strengthened through practice and training. Through constant familiarity, one +can enhance their capacity and increase their potential limitlessly. So the +second premise is that as one enhances the capacity of these antidotal forces +and increases their strength, one is able to correspondingly reduce the +influences and effects of delusory states of mind. + The third premise is that the essential nature of mind is pure; in other +words, there is the idea that the essential nature of mind is clear light or +Buddha-nature. + So it is on these three premises that Buddhism accepts that delusions, all +afflictive emotions and thoughts, can be ultimately eliminated through +practice and meditation. (p.38) + -- H.H. the Dalai Lama, "Healing Anger: The Power of Patience from a + Buddhist Perspective", translated by Geshe Thupten Jinpa, published + by Snow Lion Publications +~ + As we become aware of the working of our mind, we'll find ourselves +grappling with an inner trickster. Pay attention! The mind in which anger +arises is also the mind that holds it, hides it, fans it, justifies it, or +suppresses it. That's why this first step is crucial--before we can +understand, befriend, tame, and transform our anger, we have to recognize it +clearly and acknowledge it frankly. This is no small task. + Self-awareness is a precondition for understanding and healing our anger. +If we become aware of the workings of our mind we can discover the means by +which we create our anger and the key to healing it. If we become aware that +we are harboring irrational beliefs, ideas with false premises, mistaken +assumptions or flawed logic, we can examine them and correct them. If we +discover that we cherish ideas which are not in harmony with the realities of +life and nature we can learn to relax into existence. If we find that we +harbor desires, hopes, and expectations which cannot be achieved we have the +option of letting them go. + ...To develop awareness is to take a journey within--into the heart of our +being. (p.33) + -- Ron Leifer, M.D., "Vinegar into Honey: Seven Steps to Understanding and + Transforming Anger, Aggression, and Violence", published by Snow Lion +~ + A kind heart is the essential cause of happiness. Being kind to others is +the nicest thing we can do for ourselves. When we respect others and are +considerate of their needs, opinions and wishes, hostility evaporates. It +takes two people to fight, and if we refuse to be one of them, there is no +quarrel. + ...A kind heart is the root of harmony and mutual respect. It prevents us +from feeling estranged or fearful of others. It also protects us from +becoming angry, attached, closed-minded, proud or jealous. When opportunities +arise to help others we won't lack courage or compassion. If political +leaders had impartial minds and kind hearts, how different our world would be! + As all problems arise from the self-cherishing attitude, it would be wise +for each of us, as individuals, to exert ourselves to subdue it. World peace +doesn't come from winning a war, nor can it be legislated. Peace comes +through each person eliminating his or her own selfishness and developing a +kind heart...we can each do our part beginning today. The beneficial result +in our own lives will immediately be evident. (p.76) + -- Thubten Chodron, "Open Heart, Clear Mind", foreword by His Holiness + the Dalai Lama, published by Snow Lion Publications +~ + In order to have strong consideration for others' happiness and welfare, it +is necessary to have a special altruistic attitude in which you take upon +yourself the burden of helping others. In order to generate such an unusual +attitude, it is necessary to have great compassion, caring about the suffering +of others and wanting to do something about it. In order to have such a +strong force of compassion, first you must have a strong sense of love which, +upon observing suffering sentient beings, wishes that they have happiness-- +finding a pleasantness in everyone and wishing happiness for everyone just as +a mother does for her sole sweet child. + In order to have a sense of closeness and dearness for others, you first +train in acknowledging their kindness through using as a model a person in +this lifetime who was very kind to yourself and then extending this sense of +gratitude to all beings. Since, in general, in this life your mother was the +closest and offered the most help, the process of meditation begins with +recognizing all other sentient beings as like your mother. (p.44) + -- The Fourteenth Dalai Lama, His Holiness Tenzin Gyatso, "Kindness, + Clarity, and Insight 25th Anniversary Edition", edited and translated + by Jeffrey Hopkins, co-edited by Elizabeth Napper, published by Snow + Lion Publications +~ + Most people feel cozy enough in samsara. They do not really have the +genuine aspiration to go beyond samsara; they just want samsara to be a little +bit better. It is quite interesting that "samsara" became the name of a +perfume. And it is like that. It seduces us into thinking that it is okay: +samsara is not so bad; it smells nice! The underlying motivation to go beyond +samsara is very rare, even for people who go to Dharma centers. There are +many people who learn to meditate and so forth, but with the underlying motive +that they hope to make themselves feel better. And if it ends up making them +feel worse, instead of realizing that this may be a good sign, they think +there is something wrong with Dharma. We are always looking to make ourselves +comfortable in the prison house. We might think that if we get the cell wall +painted a pretty shade of pale green, and put in a few pictures, it won't be a +prison any more. + ...There are two basic reasons we follow a spiritual path and look for +liberation. One reason is that we want to be free. Let's take the +traditional example of a burning house: your whole house is on fire, and you +run out from it. But all your family--your partner, your children, your +parents, even your pet dog--are all still inside. What are you going to do? +You don't just say, "Well, I'm out. So too bad. Do your best to get out, +too." Naturally this leads to the second basic reason for following a +spiritual path: we will try to pull them out as well. (p.71) + -- Jetsunma Tenzin Palmo, "Into the Heart of Life", foreword by H.H. the + Gyalwang Drukpa, published by Snow Lion Publications +~ + "Form is emptiness, emptiness is form." + + We are empty, or rather the matter of which we are composed is empty. But I +must emphasize that emptiness does not mean nothingness. Some commentators +have been mistaken when they have accused Buddhism of being nihilistic. We +believe that the world in which we live is part of a flux, a stream of events. +This does not mean it is nothing. Everything depends on everything else. +Nothing exists on its own. On account of all the influences that come to bear +upon them, things appear, exist, and disappear, and then reappear again. But +they never exist independently. Form is therefore empty, by which we mean it +is not separate and independent. Form depends on a multitude of different +factors. And emptiness is form because all forms emerge from emptiness, from +this absence of independent existence. Emptiness exists only to give rise to +form. (p.341) + -- His Holiness the Dalai Lama, "The Dalai Lama's Little Book of Inner + Peace: The Essential Life and Teachings". +~ + When we understand the empty nature of our own mind, then the consequences +of merit and sin will not be realized. In the state of emptiness, there +exists no objective merit or sin. + ...The nature of the mind is like a mirror; merits and sins are like the +reflections in this mirror; and reflections in no way affect or modify the +nature of the mirror. When we are in a state of contemplation, we are living +in the condition of the mirror. At the time when all phenomena are exhausted +and pass into the nature of reality, then our virtuous and vicious deeds will +cause no benefit or harm to us. There is no basis for effect--all +limitations, all frames of reference, all solid ground having been eliminated. +But if we do not understand the nature of the mind and intrinsic awareness +through direct personal experience, it will be a very dangerous situation for +us. + Indeed, it is not sufficient merely to understand these teachings +intellectually; one must first practice and attain realization from this +practice. Otherwise the virtuous and the vicious acts we commit in this life +will create and accumulate karma, leading us again inevitably into +transmigration. From the present time until we realize the ultimate +exhausting of all phenomena into the nature of reality, our behavior must be +refined; it must be heedful and scrupulous. Otherwise our view is only so +much empty intellectual talk. (p.66) + -- "Self-Liberation through Seeing with Naked Awareness", translation and + commentary by John Myrdhin Reynolds, foreword by Namkhai Norbu, published + by Snow Lion Publications +~ + The attainment of shamata is a serenely stilled state of mind, settled on +mind itself. Although the attainment of such a meditational state focused on +mind is the foundation for developing the highest attainments and is, of +course, very excellent, by itself it is insufficient for reaching those goals. + When we achieve a mind focused on mind with the perfect placement of +absorbed concentration, free from all faults of dullness or flightiness, we +increasingly experience an element of bliss accompanying our meditation. When +we experience serene joy, on both a physical and mental level, brought on by +the force of total absorption of mind on mind, we achieve a meditational state +that fulfills the definition of shamata. + Our ordinary mind is like raw iron ore that needs to be made into a steel +sword. Progressing through the stages for attaining shamata is like forging +the iron into steel. All the materials are there at our disposal. But since +the mind wanders after external objects, then although it is the material for +attaining shamata, it cannot yet be used as this product. We have to forge +our mind through a meditational process. It is like putting the iron ore into +fire. + To fashion the steel into a sword, or in this analogy to fashion the mind +into an instrument that understands voidness, our serenely stilled and settled +mind needs to come to decisive realization of voidness as its object. Without +such a weapon of mind, we have no opponent with which to destroy the +disturbing emotions and attitudes. (p.142) + -- H.H. the Dalai Lama and Alexander Berzin, "The Gelug/Kagyu Tradition of + Mahamudra", published by Snow Lion Publications +~ + The Yolmo Valley has many different aspects that are beneficial to +practitioners. Ian Baker writes: + Chatral Rinpoche said that specific [places] in Yolmo are conducive to +particular kinds of practice. Places with waterfalls inspire reflection on +impermanence. Places with steep cliffs where the rocks are dark and jagged +are good for meditating on wrathful deities. Places with rolling hills and +flowering meadows support meditation on peaceful deities.... + Chatral Rinpoche clarified that the beyul [hidden lands] that Padmasambhava +established in Tibet are not literal arcadias, but paradises for Buddhist +practice, with multiple dimensions corresponding to increasingly subtle levels +of perception. Beyond Yolmo's visible terrain of mountains, streams, and +forests, he said, lies an inner level, corresponding to the flow of intangible +energies in the physical body. Deeper still, the subtle elements animating +the environment merge with the elements present within the practitioner--the +secret level. + Finally, at the beyul's innermost level--yangsang--lies a paradisiacal, or +unitary dimension revealed through an auspicious conjunction of person, place, +and time.... Chatral Rinpoche contended that yangsang is not merely a +metaphor for the enlightened state, but an ever-present, if hidden, reality. +(p.62) + -- Chatral Rinpoche, "Compassionate Action", edited and annotated by Zach + Larson, published by Snow Lion Publications +~ + Distinguishing between constructive and destructive emotions is right there +to be observed in the moment when a destructive emotion arises--the calmness, +the tranquillity, the balance of the mind are immediately disrupted. Other +emotions do not destroy equilibrium or the sense of well-being as soon as they +arise, but in fact enhance it--so they would be called constructive. + Also there are emotions that are aroused by intelligence. For example, +compassion can be aroused by pondering people who are suffering. When the +compassion is actually experienced, it is true that the mind is somewhat +disturbed, but that is more on the surface. Deep down there is a sense of +confidence, and so on a deeper level there is no disturbance. A consequence +of such compassion, aroused by intelligent reflection, is that the mind +becomes calm. + The consequences of anger--especially its long-term effects--are that the +mind is disturbed. Typically, when compassion moves from simply being a +mental state to behavior, it tends to manifest in ways that are of service to +others, whereas when anger goes to the point of enactment it generally, of +course, becomes destructive. Even if it doesn't manifest as violence, if you +have the capacity to help, you would refrain from helping. That too would be +a kind of destructive emotion. (p.158) + -- "Destructive Emotions: How Can We Overcome Them?" A Scientific Dialogue + with the Dalai Lama narrated by Daniel Goleman +~ +I love smiles. That is a fact. How to develop smiles? There are a variety +of smiles. Some smiles are sarcastic. Some smiles are artificial-diplomatic +smiles. These smiles do not produce satisfaction, but rather fear or +suspicion. But a genuine smile gives us hope, freshness. If we want a +genuine smile, then first we must produce the basis for a smile to come. + —- H.H. the Dalai Lama +~ + Suffering is something very concrete, which everyone knows and wants to +avoid if possible, and the Buddha therefore began his teaching by talking +about it in his famous formulation of the Four Noble Truths. + The first truth draws our attention to the fact that we suffer, pointing out +the existence of the basic dissatisfaction inherent in our condition; the +second truth explains the cause of dissatisfaction, which is the dualistic +state and the unquenchable thirst (or desire) inherent in it: the subject +reifies its objects and tries to grasp them by any means, and this thirst (or +desire) in turn affirms and sustains the illusory existence of the subject as +an entity separate from the integrated wholeness of the universe. + The third truth teaches that suffering will cease if dualism is overcome and +reintegration achieved, so that we no longer feel separate from the plenitude +of the universe. Finally, the fourth truth explains that there is a Path that +leads to the cessation of suffering, which is the one described by the rest of +the Buddhist teachings. + All the various traditions are agreed that this basic problem of suffering +exists, but they have different methods of dealing with it to bring the +individual back to the experience of primordial unity. (p.47) + -- Chogyal Namkhai Norbu, "The Crystal and the Way of Light: Sutra, Tantra, + and Dzogchen", compiled and edited by John Shane, published by Snow Lion +~ + To disciples of increasing purity, ability, and rarity the Buddha gave more +private guidance in the subtle mysteries. It appears that such teachings are +included in the Mahayana sutras. There is no certainty, however, that all of +the tantras were taught while the historical Buddha was alive. To an +extremely small number of pure disciples the Buddha could appear today. They +could encounter Vajradhara, the King of the Tantras, and he could reveal +tantras and quintessential guidance to them. + This is possible even though more than twenty-five hundred years have gone +by since the historical Buddha passed away. There is no possibility, after +the Buddha's death, of additions being made to his public discourses. But I +think that teachings to disciples of pure action do not necessarily have to be +given during the historical Buddha's lifetime. (pg.44) + -- H.H. the Dalai Lama, "Transcendent Wisdom", translated, edited and + annotated by B. Alan Wallace, published by Snow Lion Publications +~ + 30. + Since the five perfections without wisdom + Cannot bring perfect enlightenment, + Along with skillful means cultivate the wisdom + Which does not conceive the three spheres [as real] + This is the practice of Bodhisattvas. + -- Gyelsay Togmay Sangpo + + Practice of the five perfections without the understanding of reality +remains contaminated, and though it may yield boundless happiness, it doesn't +lead to omniscience. Love and compassion without the understanding of reality +cannot help us to escape from worldly existence. + On the other hand, we may easily remain trapped in a state of personal peace +if we have understood reality but lack enough love and compassion. It is +therefore the practice of Bodhisattvas to combine the two--skillful means and +wisdom. Which of us can say we don't want to possess knowledge, kindness and +pure conduct? Our text is a manual of instruction on how to gain these +qualities and become a fully developed human being. (p.70) + -- Geshe Sonam Rinchen, "The Thirty-Seven Practices of Bodhisattvas", + translated and edited by Ruth Sonam, published by Snow Lion Publications +~ + "The Buddhas have already achieved all their own goals, but remain in the +cycle of existence for as long as there are sentient beings. This is because +they possess great compassion. They also do not enter the immensely blissful +abode of nirvana like the Hearers. Considering the interests of sentient +beings first, they abandon the peaceful abode of nirvana as if it were a +burning iron house. Therefore, great compassion alone is the unavoidable +cause of the non-abiding nirvana of the Buddha." + -- Kamalashila + + Compassion's importance cannot be overemphasized. Chandrakirti paid rich +tribute to compassion, saying that it was essential in the initial, +intermediate, and final stages of the path to enlightenment. + Initially, the awakening mind of bodhichitta is generated with compassion as +the root, or basis. Practice of the six perfections and so forth is essential +if a Bodhisattva is to attain the final goal. + In the intermediate stage, compassion is equally relevant. Even after +enlightenment, it is compassion that induces the Buddhas not to abide in the +blissful state of complacent nirvana. It is the motivating force enabling the +Buddhas to enter non-abiding nirvana and actualize the Truth Body, which +represents fulfillment of your own purpose, and the Form Body, which +represents fulfillment of the needs of others. Thus, by the power of +compassion, Buddhas serve the interests of sentient beings without +interruption for as long as space exists. This shows that the awakening mind +of bodhichitta remains crucial even after achieving the final destination. +(p.44) + -- H.H. the Dalai Lama, root text by Kamalashila, "Stages of Meditation", + translated by Geshe Lobsang Jordhen, Losang Choephel Ganchenpa, and + Jeremy Russell, published by Snow Lion Publications +~ + [During sleep and waking states] there are physiological processes that +correspond to different mental states, and these are associated with +subjectively experienced energies in the body. + In the waking state, these energies tend to be drawn into a locus in the +center of the head, at the level of the forehead. In the dreaming stage, +these energies will be even more drawn to a point in the throat. In the deep +sleep state, these energies are more drawn into the heart. The location is +not the physical heart, the organ, but the heart center which is right in the +center of the chest. + Certain events are experienced in meditation that seem to corroborate this +theory. For example, in meditation, it is possible to bring your awareness +into the heart cakra, and sometimes when this happens, the person will faint. +On other occasions, the meditative awareness, finely concentrated, may be +brought into the area of the navel. And at this juncture, it has been found +experientially that heat is produced by such concentration. If you look at +the anatomy of the body, you don't find these cakra points. (p.106) + -- H.H. the Dalai Lama, "Consciousness at the Crossroads: Conversations with + the Dalai Lama on Brain Science and Buddhism", edited by Zara Houshmand, + Robert B. Livingston, and B. Alan Wallace, published by Snow Lion Pub. +~ + When you meditate with concentration, there are three particular experiences +that arise: bliss, clarity, and nonthought. + The experience of meditative bliss is greater than ordinary worldly +happiness. Sometimes when you are meditating, a feeling of blissfulness +suddenly arises from the subtle state of your mind and pervades your entire +body. This bliss is healthy and brings out your inner qualities. Some people +use drugs to induce blissfulness and visions, but drugs are external supports +that cannot bring lasting happiness. The bliss experienced in meditation can +last for many days, according to your ability to meditate. When you +experience this kind of bliss, on the outside you might look very poor, but +inside you remain very joyful. + The second main experience in meditation is clarity. Sometimes while +meditating you can suddenly feel that your mind is very clear and bright. +Even if you are meditating in the dark, you do not feel heavy or tired. +Sometimes your body feels very light and your mind is very clear, and many +kinds of reflections appear. Clarity brings great wisdom and the ability to +read other people's minds, as well as to see your own past and future lives. + The third main experience is nonthought, or a state of equanimity without +distractions. Beginners can also experience this. Nonthought is more settled +than the experiences of bliss and clarity. If you have thoughts, they +suddenly dissolve and you can remain continuously in meditation. As your +ability to meditate develops, your mind becomes more and more settled, so that +you can meditate for one hour or one week or one month without being +distracted by thoughts. You simply remain in the natural state for as long as +you want. + Bliss, clarity, and nonthought are the main qualities of concentration. +However, it is important not to be attached to them or concerned about whether +they arise or not; one should simply continue to practice. (p.29) + -- Khenchen Palden Sherab Rinpoche and Khenpo Tsewang Dongyal Rinpoche, + "The Buddhist Path: A Practical Guide from the Nyingma Tradition of + Tibetan Buddhism", published by Snow Lion Publications +~ + The Buddha's teachings can be divided into two main categories: the +scriptures and realization. A verse states: + + The teachings of the Teacher have two aspects: + Scripture and realization presented as they truly are. + There is nothing else to do but + Sustain them, speak of them, and practice them. + + When we practice listening, reflecting, and meditating, the teachings will +free us from the heavy darkness of suffering. They are like a never-setting +sun whose luminous rays reach to the farthest corners of this world. Among +the eighty-four thousand teachings of the Buddha are those found in Tibet that +maintain the unity of the sutra and mantra traditions. These teachings are +like a tree trunk with numerous branches: a variety of lamas hold lineages +within diverse traditions. + ...In showing how to cut through the delusion of duality, these teachings +open up to every living being the possibility of attaining true mastery over +the immense and profound gates to the eighty-four thousand teachings. They +are precious because they make nonconceptual wisdom manifest and bring forth +the amrita of all-pervading emptiness. Like placing a perfect fruit in the +palm of our hand, these teachings bring about two kinds of wisdom: the wisdom +that sees the multitude of all phenomena distinctly and the wisdom that sees +clearly into their nature. + Relying on an appropriate path allows the fruition of practice to manifest. +This result is possible because buddha nature is found in the mindstream of +all living beings. (p.160) + -- "Music in the Sky: The Life, Art and Teachings of the Seventeenth + Karmapa, Ogyen Trinley Dorje", by Michele Martin, published by Snow Lion +~ + All is neither real nor delusive-- + Held to be like [a reflection of] the moon on water by the learned. + Just this ordinary mind + Is called "dharmadhatu" and "Heart of the victors." + --Venerable Rangjung Dorje + + ...Thus, seeming reality consists of the adventitious stains that are like +[mistakenly seeing] a [white] conch as being yellow. Ultimate reality is the +tathagata heart, which is like the [natural] white of the conch. Except for +the mere appearances from the perspective of a mistaken [perceiving] subject, +within the object--the conch--there is nothing white or yellow to be added or +to be removed. Therefore, the pith instruction is to rest naturally and +uncontrived. + In brief, what are called "samsara" and "nirvana" are set up from the point +of view of mere seeming appearances, while the nature of both--luminosity free +from reference points--is called tathagata heart. Consequently, in terms of +the definitive meaning, mere appearances and their nature cannot be separated, +just like fire and its heat. For this reason, the mother [sutras] say: + "Form is emptiness. Emptiness is form. Emptiness is nothing other than +form. Form is nothing other than emptiness." (p.165) + -- "Gone Beyond: The Prajnaparamita Sutras, The Ornament of Clear + Realization, and Its Commentaries in the Tibetan Kagyu Tradition", Volume One + translated and introduced by Karl Brunnholzl, a Tsadra Foundation Series book, + published by Snow Lion Publications +~ + In the Mahayana 'cause and effect' refer to totally supreme emptiness and +supreme immutable bliss. The Brief Explication of Initiations (Shekhoddesha) +[included in the Kalachakra cycle] says: + + That bearing the form of emptiness is the cause, + That bearing immutable compassion is the effect. + Emptiness and compassion indivisible + Are called the mind of enlightenment. + + The indivisibility of these two is a Cause Vehicle in the sense of being the +means by which one progresses, and it is an Effect Vehicle in the sense of +being that to which one is progressing. Such a Vajra Vehicle has reference to +Highest Yoga Tantra and cannot occur in the lower tantras. For the supreme +immutable bliss can only arise when one has attained the branch of meditative +stabilisation (in the system of the Kalachakra) and thus the branches of +mindfulness and those below must be the means of achieving it. The three +lower tantras do not have all the factors that are included in these causal +branches. (p.107) + -- H.H. the Dalai Lama, Tsong-ka-pa and Jeffrey Hopkins, "Tantra in Tibet", + published by Snow Lion Publications +~ +...one of the things you can learn from history is that men have learned +to live with machinery at least as well as, and probably a good deal better +than, they have learned to live with one another. + -- E. E. Morison, from “Computers and the World of the Future” +~ +Everything is perfect in the universe--even your desire to improve it. + -- Dr. Wayne Dyer +~ +On Practice Space + + I encourage you to conduct your own research on the results of practicing in +various environments. Tibetan yogis are especially attracted to places with +an enormous amount of open space and distant vistas. I have greatly enjoyed +meditating in the high desert of the eastern Sierra Nevada range, where the +views extend to peaks sixty miles away. The ability to direct the attention +to such distant points gives a very expansive feeling to the intervening +space. + In such a spacious environment, allow your awareness to come out, with your +eyes open and your gaze resting vacantly in the space in front of you. The +experience in a vast space is very different from that in a tiny room. Gazing +up at a clear night sky studded with stars is a wonderful way to experience +the sheer enormity of space. + It is important to distinguish between the contents of a space and the space +itself. Colors and shapes constitute the contents of visual space. These are +aspects or representations of ordinary phenomena in the visual field. +Attending to the space of the mind means attending to that space from which +all such contents emerge, in which they are present, and into which they +dissolve; it is the space that lingers in between discrete events. (p.220) + -- B. Alan Wallace, "Minding Closely: The Four Applications of Mindfulness", + published by Snow Lion Publications +~ + "Through analytical meditation, you come to a point of clarity and decisive +insight, and at this point it is beneficial to abide in that revelation. Your +insight will grow gradually like a sprout. Simply be present and settle your +mind in the absolute nature of reality. Remain in a state of meditative +equipoise, and do not think of this as a waste of your time. If you think you +should rather be actively engaged in such practices as circumambulations or +the stage of generation, it is the time for you to be simply present in +meditative equipoise. But do not just sit and space out." + --Karma Chagme + + In some scholarly discursive meditations in the sutra tradition, one +continually seeks out the mind, and there is a tradition in which +investigation is needed. Here, in the tradition of Mahamudra and Atiyoga, it +is enough to seek and investigate during this phase of Dharma practice, but +afterwards it is not necessary to continue the search. In the Katok +tradition, the investigation of the mind is said to takes months, for one +examines for three days each of the points of the mind's color and shape as +well as the exterior and the interior of the body. Our tradition does not +take so long, so it is important for you to seek out the mind without even a +moment's distraction. (p.100) + -- Karma Chagme, "A Spacious Path to Freedom: Practical Instructions on the +Union of Mahamudra and Atiyoga", commentary by Gyatrul Rinpoche, trans. by B. +Alan Wallace, published by Snow Lion Publications +~ + When the root of duality--dualistic clinging, dualistic perceptions, deluded +perceptions--is severed, all the leaves, the branches, and even the tree trunk +of samsara and nirvana naturally wither on their own and topple in their own +time. Then this great spreading tree of samsara and nirvana, of duality, of +worldliness, of conditioned being, does not need to be chopped down: it is +already as if dead. We can relax; done is what had to be done, as the Buddha +sang. + This is the whole point of the Dharma, of spiritual awakening, of +Buddhahood; this is its ultimate evolution or unfolding. If we aspire to +experience such an awakening, there is nothing else to do except recognize the +true nature of our primordial awareness, our own essential being, our own +birthright, which is within. This is the intrinsic nature of our own heart- +mind, also known as bodhicitta or bodhi-mind. It is our own being, our own +nature, this renowned buddha-nature. It is not a Buddha anywhere else. (p.103) + -- Nyoshul Khenpo Rinpoche and Lama Surya Das, "Natural Great Perfection: + Dzogchen Teachings and Vajra Songs", published by Snow Lion Publications +~ + A common [Tibetan Buddhist] motif is the "Wheel of Life," symbolizing the +workings of cyclic existence. This is frequently found at the doorway to a +main assembly hall and serves to remind the inhabitants of the dangers of +mundane existence. This striking image has a large central circle divided +into two halves. The top half has three sections, representing the three +"happy transmigrations"--humans, demi-gods, and gods. The lower half also has +three sections, indicative of the three bad transmigrations--animals, hungry +ghosts, and hell beings. A pigeon symbolizes the mental affliction of desire, +a snake represents hatred, and a pig--symbol of ignorance--holds the tails of +the first two in its mouth. These three afflictions are the primary factors +that bind people to cyclic existence, causing them to transmigrate helplessly +from birth to birth. + The theme of cause and effect is further illustrated by twelve sections +around the rim of the wheel, symbolizing the twelve links of dependent arising +(a summary of the process of transmigration). The whole wheel is held in the +jaws of the Lord of Death, indicating that death is inevitable for those who +are caught up in this cycle. Outside of the wheel are buddhas and +bodhisattvas, often shown teaching the dharma, which provides an avenue of +escape for those who are perceptive enough to recognize this and follow their +instructions. (p.239) + -- John Powers, "Introduction to Tibetan Buddhism", published by Snow Lion + Publications +~ +Is Anger Beneficial? + + We generally consider something beneficial if it promotes happiness. But +when we ask ourselves, "Am I happy when I'm angry?" the answer is undoubtedly +no. We may feel a surge of physical energy due to physiological reasons, but +emotionally we feel miserable. Thus, from our own experience, we can see that +anger does not promote happiness. + In addition, we don't communicate well when we're angry. We may speak +loudly as if the other person were hard of hearing or repeat what we say as if +he had a bad memory, but this is not communication. Good communication +involves expressing ourselves in a way that the other person understands. It +is not simply dumping our feelings on the other. Good communication also +includes expressing our feelings and thoughts with words, gestures, and +examples that make sense to the other person. Under the sway of anger, +however, we neither express ourselves as calmly nor think as clearly as usual. + Under the influence of anger, we also say and do things that we later +regret. Years of trust built with great effort can be quickly damaged by a +few moments of uncontrolled anger.... If we could tame our anger, such +painful consequences could be avoided.(p.23) + -- Thubten Chodron, "Working with Anger", published by Snow Lion Pub. +~ + Why should we want to help our enemies or to give them happiness? Here are +various useful ideas to consider. One approach is to think that the harm they +have done us is, in fact, the result of our own past negative actions through +which we have set ourselves up as a target for their harm. We could also +consider how those who harm us are totally driven by their disturbing +emotions. + If someone in our family, someone we love dearly, becomes insane and tries +to harm us, we wouldn't think of taking revenge but would try to help them +regain a normal state of mind. Living beings, our mothers, are crazed by +their disturbing emotions. Those who harm us are in particular need of our +love and compassion. (p.52) + -- Geshe Sonam Rinchen, "The Bodhisattva Vow", translated and edited by + Ruth Sonam, published by Snow Lion Publications +~ + On top of the sufferings of birth, aging, sickness, and death, we encounter +the pains of facing the unpleasant, separating from the pleasant, and not +finding what we want. The basic problem lies with the type of mind and body +that we have. Our mind-body complex serves as a basis for present sufferings +in the form of aging, sickness, and death, and promotes future suffering +through our usual responses to painful situations. + By reflecting on birth and on the nature of mind and body, you will be moved +from the depths of your heart to seek relief, thinking, "If I could only be +free from a life driven by afflictive emotions and karma!" Without such +reflection on pain, your knowledge of your own condition will be limited, +which itself will put a limit on your compassion. As Tsonghkapa says: + "If you do not cultivate a genuine sense of disenchantment with cyclic +existence--whose nature is a mind-body complex under the sway of afflictive +emotions and karma--you will have no chance to develop a genuine attitude +intent on liberation, and there will be no way to develop great compassion for +beings wandering in cyclic existence. Therefore, it is crucial to reflect on +your situation." (p.151) + -- His Holiness the Dalai Lama, "Becoming Enlightened", trans. and ed. by + Jeffrey Hopkins, PhD +~ + When I was a young boy, Tantra was just a matter of blind faith. At age +twenty-four I lost my own country, and then after coming to India started +really reading Tsongkhapa's explanations on emptiness. Then, after moving to +Dharamsala, I put more effort into the study and practice of the stages of the +path, emptiness, and Tantra. So it was only in my late twenties after gaining +some experience of emptiness that deity yoga made sense. + One time in the main temple in Dharamsala I was performing the ritual of +imagining myself as a deity of Highest Yoga Tantra, called Guhyasamaja. My +mind continuously remained on the recitation of the ritual text, and when the +words "I myself" came, I completely forgot about my usual self in relation to +my combination of mind and body: Instead, I had a very clear sense of "I" in +relation to the new, pure combination of mind and body of Guhyasamaja that I +was imagining. Since this is the type of self-identification that is at the +heart of Tantric yoga, the experience confirmed for me that with enough time I +could definitely achieve the extraordinary, deep states mentioned in the +scriptures. (p.188) + -- His Holiness the Dalai Lama, "How to Practice: The Way to a Meaningful + Life", translated and edited by Jeffrey Hopkins +~ + In Buddhism, one speaks of three different levels of understanding, which +are sequential--an understanding arrived at through learning and studying, an +understanding developed as a result of deep reflection and contemplation, and +an understanding acquired through meditative experience. + There is a definite order in the sequence of this three. So on the basis of +study and learning--which is the first level--we deepen our understanding of a +given topic by constantly reflecting upon it until we arrive at a point where +we gain a high degree of certainty or conviction that is firmly grounded in +reason. At this point, even if others were to contradict our understanding +and the premises upon which it is based we would not be swayed, because our +conviction in the truth has arisen through the power of our own critical +reflection. This is the second level of understanding which, however, is +still at the level of the intellect. + If we pursue this understanding further and deepen it through constant +contemplation and familiarity with the truth, we reach a point where we feel +the impact at the emotional level. In other words, our conviction is no +longer at the level of mere intellect. This is the third level of +understanding, which is experiential, and this is referred to in the Buddhist +texts as an understanding derived through meditative experience.... You will +need to deepen your understanding still further by engaging in regular +meditation so that you can progress to the third level of understanding. + -- H.H. the Dalai Lama, "Lighting the Way", translated by Geshe Thupten + Jinpa, published by Snow Lion Publications +~ + Shortly after attaining enlightenment under the bodhi tree, the Buddha gave +a sermon in Varanasi sharing the fruits of his realization. This sermon is +referred to as the "first turning of the wheel of Dharma." The word Dharma +here refers to the Buddha's teachings themselves. It was this sermon in which +the Buddha developed what would become the framework for the entirety of his +teachings: the four noble truths. + These four truths are the truth of suffering, the truth of its origin, the +truth of the possibility of its cessation, and the truth of the path that +leads to that cessation. In essence, the four noble truths say that we all +naturally desire happiness and do not wish to suffer--and that the suffering +we wish to avoid comes about as a result of a chain of causes and conditions +begun even before our birth. If we are to pursue our aspiration to gain +freedom from suffering, we need to clearly understand the causes and +conditions that give rise to suffering and strive to eliminate them. +Additionally, we must clearly understand the causes and conditions that give +rise to happiness as well, and actively practice them. This is the essence of +the four noble truths. + -- H.H. the Dalai Lama, "Essence of the Heart Sutra: The Dalai Lama's + Heart of Wisdom Teachings", translated & edited by Geshe Thupten Jinpa +~ + In Dzogchen, while thoughts are active, rigpa permeates them all, so that +even at the very moment when powerful thoughts like attachment and aversion +are arising, there remains a pervasive quality of clear light rigpa. +Dodrupchen says, "in Dzogchen, since the clear light's natural way of being is +like the sun and its rays, inseparable, if you are able, through this, to +bring out the radiance of genuine mind, you will be able to maintain the +experience of clear light in meditation, without it fluctuating, or coming and +going." + Longchen Rabjam speaks of self-arising wisdom, which is in fact rigpa: +"Self-arising wisdom is rigpa that is empty, clear and free from all +elaboration, like an immaculate sphere of crystal. Its very being is such +that it never explores objects of the senses." + This "self-arising wisdom" is rigpa, which in essence is primordially pure. +Longchenpa describes it as "empty and clear". To call it empty is to refer to +its essence, primordially pure. To call it clear is to speak of its nature, +spontaneously present. As such, it is "free from all elaboration", and free +from the elaborations of adventitious phenomena. So it is like a flawless +crystal sphere, and truly "its very being is such that it never explores +objects of the senses". (p.180-5) + -- H.H. the Dalai Lama +~ + There is an Indian saying: if you are struck by a poisonous arrow, it is +important first to pull it out, there is no time to ask who shot it, what sort +of poison it is and so on. First handle the immediate problem, and later we +can investigate. Similarly, when we encounter human suffering, it is +important to respond with compassion rather than question the politics of +those we help. Instead of asking whether their country is enemy or friend, we +must think, "These are human beings, they are suffering, and they have a right +to happiness equal to our own." + Our attitude towards suffering is very important because it can affect how +we cope with it when it arises. Our usual attitude consists of an intense +aversion and intolerance of our own pain and suffering. However, if we can +transform our attitude, adopt an attitude that allows us greater tolerance of +it, this can do much to help counteract feelings of mental unhappiness, +dissatisfaction and discontent. (p.92) + -- H.H. the Dalai Lama, "The Pocket Dalai Lama", compiled and edited by + Mary Craig +~ + Why should we want to help our enemies or to give them happiness? Here are +various useful ideas to consider. One approach is to think that the harm they +have done us is, in fact, the result of our own past negative actions through +which we have set ourselves up as a target for their harm. We could also +consider how those who harm us are totally driven by their disturbing +emotions. + If someone in our family, someone we love dearly, becomes insane and tries +to harm us, we wouldn't think of taking revenge but would try to help them +regain a normal state of mind. Living beings, our mothers, are crazed by +their disturbing emotions. Those who harm us are in particular need of our +love and compassion. (p.52) + -- Geshe Sonam Rinchen, "The Bodhisattva Vow", translated and edited by + Ruth Sonam, published by Snow Lion Publications +##If you are totally concerned and preoccupied with the affairs of this +lifetime, there is a great danger of causing your own downfall. If by such +concern you were able to achieve the desired happiness, that is okay, but this +is not the case. +~ + We all let ourselves be caught in this web of preoccupation with the +activities and confusion of this lifetime. Having too much worldly +involvement ends in confusion. We spend our whole lives thinking that this +might be better than that, I should do this, or perhaps something else is +better and I should do that. If you reflect upon the underlying +dissatisfaction, then you will be able to find that, well, after all, whatever +they might be, the affairs of this lifetime are not that important, because +they yield a limited benefit. This does not mean that you should not work for +your own livelihood, but it does indicate that you should not be preoccupied +with that alone. (p.107) + -- H.H. the Dalai Lama, "The Path to Bliss", translated by Geshe Thupten + Jinpa, edited by Christine Cox, published by Snow Lion Publications +~ + Do you understand who the enemy is? You do not need to beat anyone up, and +you do not need a weapon to kill your enemy. You do not need money to buy a +weapon. It is all very easy. + How is liberation accomplished? The offering of liberation is accomplished +by abandoning the dualistic mind of discursive thoughts. The sharp weapon of +primordial wisdom, which completely annihilates the dualistic mind, is the +means for achieving this separation. This "weapon" has been part of your +continuum for a long time now. With this weapon you can completely devastate +the dualistic mind, leaving not even a trace behind, thus liberating the mind +into the sphere of unborn truth. The enemy will never return. This is called +great liberation. + I must emphasize that primordial wisdom is not something you can buy, get +from your best friend or have handed to you by a buddha in heaven. It is not +something that someone else has but you do not. Abandon such concepts. +Primordial wisdom does not come from an external source. It is simply your +true nature. It is something that you and everyone else have as the very +essence of your mind. + You should know what your qualities and capabilities are. (p.79) + -- Gyatrul Rinpoche, "The Generation Stage in Buddhist Tantra", published + by Snow Lion Publications +~ + With regard to ordinary study, except for the fact that there is a limit to +our lifetime, it is not that you arrive at a point where there is no more room +in your brain. No matter how much you study, even if you study a hundred +thousand million words, the mind can still retain them. This indicates that +the basis of these qualities, consciousness, is stable and continuous. + The other day, I made a joke to someone who was asking about the brain. I +said that if, like a computer, you needed a cell for each moment of memory, +then as you become more and more educated, your head would have to get bigger +and bigger! + Because of these reasons--that compassion, wisdom, and so forth are +qualities that depend on the mind, and the mind is stable and continuous--they +can be developed to a limitless degree. + It is from this point of view that it is said that the conception of +inherent existence can be extinguished. When one removes the conception of +inherent existence, one thereby also ceases the afflictive emotions generated +in dependence upon that ignorance. Also, since the ignorance that drives +contaminated actions has ceased, this class of actions ceases. Once the +motivator of the action and the actions cease, the results of those actions +will cease. That is how the third noble truth--true cessation--comes to be. +(p.103) + -- H.H. the Dalai Lama of Tibet, Tenzin Gyatso, "The Dalai Lama at + Harvard: Lectures on the Buddhist Path to Peace", translated and + edited by Jeffrey Hopkins, published by Snow Lion Publications +~ + "Accumulating merit" can be approached from a psychological perspective that +lends itself to experiential verification or from a spiritual dimension that +requires some faith. "Merit" can be understood as "spiritual power" that +manifests in day-to-day experience. When merit, or spiritual power, is +strong, there is little resistance to practicing Dharma and practice itself is +empowered. + Tibetans explain that people who make rapid progress in Dharma, gaining one +insight after another, enter practice already having a lot of merit. By the +same theory, it is possible to strive diligently and make little progress. +Tibetans explain this problem as being due to too little merit. Merit is the +fuel that empowers spiritual practice. + How do you accumulate merit? Engaging in virtue of any sort, with your +mind, your speech, or your body results in merit. Just as merit can be +accumulated, it can also be dissipated by doing harm. In general, mental +afflictions dissipate merit. The mental affliction that is like a black hole +sucking up merit, worse than all the others, is anger. Attachment or sensual +craving can get you in a lot of trouble, but it doesn't have the debilitating +impact upon spiritual practice that anger does. Remember the warrior +metaphor--standing at the gateway of the mind, vigilant, spear ready. The +spear is for mental afflictions, especially anger. Nip anger in the bud. (p.208) + -- B. Alan Wallace, "Buddhism with an Attitude: The Tibetan Seven-Point Mind + Training", published by Snow Lion Publications +~ + It is necessary to alternate stabilising meditation and analytical +meditation... by merely cultivating non-conceptuality and non-analysis it is +impossible to enter into the yoga of signlessness. + Even after emptiness has been realised, powerful and repeated analysis is +needed. Merely to set one's mind on the meaning of emptiness is the mode of +cultivating calm abiding observing emptiness; in order to cultivate special +insight it is necessary to analyse again and again. These two modes of +meditation--stabilising and analytical--are alternated until analysis itself +induces even greater stablisation, at which point stabilisation and wisdom are +of equal strength, this being a union of calm abiding and special insight. + In Performance as well as in Action Tantra the meditative stabilisation +which is a union of calm abiding and special insight is used to gain feats for +the sake of aiding sentient beings and accumulating merit quickly. (p.42) + -- His Holiness the Dalai Lama, Tsong-ka-pa, and Jeffrey Hopkins, "Deity + Yoga in Action and Performance Tantra", published by Snow Lion Pub. +~ +Seeking a Place of Refuge. + + A spiritual aspirant requires a model, something he or she can look up to as +an ideal and thus find guidance and inspiration. In Buddhism this is the +Triple Gem, or the Three Jewels of Refuge: the Buddhas, Dharma and Sangha. + When we think of the fully enlightened Buddhas--the beings who have purified +their minds of all stains and obscurations and who have expanded their wisdom +to the limits of existence--we feel very attracted and awed; but somehow there +always seems to be a great distance between the Buddhas and us. Therefore, +there is the refuge of Sangha, the community of spiritual aspirants, the +assembly of practitioners dwelling in the various stages of practice and +attainment. + These beings provide us with a perspective on the path. We have to look up +to the Sangha, but not as far as to the Buddhas. The Sangha make us think, +"This person is not that far ahead of me. If I just make a bit more +effort...." They give us confidence for spiritual practice. Sometimes they +make us feel like we can even race them to enlightenment. These are the +Sangha of spiritual friends. + Thoughts of the Buddhas make us numb with admiration; thoughts of the Sangha +cause us to jump to it and to apply ourselves with zeal to the spiritual path. +This path and the methods for traversing it are the third Jewel of Refuge, the +Dharma. This is the collection of teachings to be practiced and the +realizations to be attained. (p.97) + -- H.H. the Dalai Lama, "The Path to Enlightenment", edited and translated + by Glenn H. Mullin, published by Snow Lion Publications +~ + Suppose there is this religious group building thousands of childcare +facilities and hospices.... Although these religious workers are doing a lot +of caring work, there is no wish to enlighten sentient beings. Their aim is +just to provide food and education. At the same time, imagine there is one +hermit living somewhere in the mountains of the Himalayas who is doing none of +this. In fact, within close range of him, there are a lot of babies dying, +yet outwardly he is doing nothing about it. Inwardly, however, he is actually +meditating, "May all sentient beings be enlightened!" and he continues to do +this every day. Purely because of the enlightenment aspect, this person is +worthier of homage than the first group. Why? Because it is so difficult to +truly and genuinely wish for the enlightenment of others. It is much easier +to give people food and educate them. + Most of us don't really appreciate this fact. We have never before +genuinely wished for someone else to achieve enlightenment. Likewise, if +someone were to come over and say to us: "Here you go, you have a ticket for +enlightenment. There is only one ticket." I don't think we would even think +about giving it to someone else! We'd grab it and go for it. Enlightenment +is such a valuable thing. + Actually, enlightenment is much too large a subject, so let's not take that +as an example. Instead, let's say someone comes along with a potion that +promises you clairvoyance or omniscience. We would drink it ourselves, not +even sharing half of it with others! + Just think how often we are jealous when someone is a better practitioner. +How often do we get jealous when someone receives a better or a higher +teaching than we do? If you have genuine bodhichitta, you should be happy, +shouldn't you? After all, isn't that what you wished for? Their getting +enlightenment means your wish is at last coming true. Their receiving higher +teachings, or becoming better practitioners, means that your aspiration is +finally being fulfilled! But we don't feel this way, instead we feel jealous +or envious. Some of us may be so-so Dharma practitioners, so we don't really +feel jealous or envious, but we still feel left behind. Who cares? If you +are a genuine bodhisattva, you shouldn't care about these things. (p.123) + -- "Entrance to the Great Perfection: A Guide to the Dzogchen Preliminary + Practices", compiled, translated, and introduced by Cortland Dahl, + published by Snow Lion Publications +~ + It is very difficult to help somebody overcome his or her problems when the +problems are unstructured, when in a certain way this person does not have any +problems, though deep inside all the problems are there. It is very difficult +for a human being whose problem is confused, whose ego is ill-defined and +without foundation, to really purify, clarify, and develop anything. + The same principle applies to praying. As long as we have our self, our +ego, we pray to the Buddha: "Please bless me so that my prayers for the +benefit of all sentient beings be fulfilled." Otherwise our prayer does not +follow any line or direction. It would be like going to a big five-star hotel +with five hundred rooms and not knowing your room number, or taking an +elevator without knowing which floor to go to--this would be a big problem. + This is the reason for calling upon the great compassion of the Buddha and +asking him to consider our prayers. The reason is not that the Buddha only +listens to someone who prays to him; rather, without praying to the Buddha we +are not developed enough to have the condition necessary to receive his +blessing. Rain might be falling for ten thousand years, yet if our cup is +upside down it will remain empty. Through praying we open up, we turn our cup +to let the water get inside. (p.48) + -- XII Khentin Tai Situpa Rinpoche, "The Third Karmapa's Mahamudra Prayer", + translated and edited by Rosemarie Fuchs, published by Snow Lion Pub +~ +Catproof is an oxymoron, childproof nearly so. -- unknown +~ + In Tibetan there is no word for "emotion." + Bearing in mind that the fundamental goal of Buddhist practice is the +achievement of nirvana, when you study the mind what you're really concerned +with is what specific mental states impede the accomplishment of that end. +That's what the six primary states and twenty derivative states (the +unwholesome mental factors) all have in common. Some are emotions and some +are not, but it doesn't really matter. What's important is they all share +that common factor of being impediments. + In contrast, modern psychology does not have the aim of nirvana. My +conjecture, in terms of trying to understand why the West places such a strong +emphasis on identifying emotion, is that, going back to the Enlightenment, +even as far back as Aquinas, there is an enormous priority placed on reason +and intelligence. What can impede reason? Emotion. + You have two categories that are set in opposition to each other. The fact +that there is a specific term for emotion in Western thought does not +necessarily imply that there was a special emphasis placed on understanding +the nature of emotion. Perhaps initially the motive for labeling something as +emotion was to enhance reason by identifying something that is unreasonable, +something that is irrational. (p.159) + -- "Destructive Emotions: How Can We Overcome Them? A Scientific Dialogue + with the Dalai Lama", narrated by Daniel Goleman, foreword by the Dalai + Lama +~ +The solution of this problem is trivial and is left as an exercise for the reader. + -- Standard textbook cookie +~ +Question: + Your Holiness and other teachers tell us to be sincerely joyful about +others' worldly achievements, happiness, and acquisitions. But if we know +with certainty that a person has acquired or achieved something through +unskillful or non-virtuous means, such as lying, stealing, cheating, harming, +in what manner should that happiness for them be experienced and expressed? + +Dalai Lama: + One's attitude toward superficial successes that are achieved through wrong +means of livelihood such as lying, stealing, cheating, and so on, should not +be the same as for achievements and happiness which are genuine. However, +here you must bear in mind that if you examine this carefully, you will find +that although the immediate circumstances that gave rise to a person's joy and +happiness may be a wrong means of livelihood, that is merely the immediate +circumstance: the actual cause of that happiness is the individual's merit in +the past. + So one has to see the difference between immediate circumstances and long- +term causes. One of the characteristics of karmic theory is that there is a +definite, commensurate relationship between cause and effect. There is no way +that negative actions or unwholesome deeds can result in joy and happiness. +Joy and happiness, by definition, are the results or fruits of wholesome +actions. So, from that point of view, it is possible for us to admire not so +much the immediate action, but the real causes of joy. (p.119) + -- H.H. the Dalai Lama, "Healing Anger: The Power of Patience from a + Buddhist Perspective", translated by Geshe Thupten Jinpa, published by + Snow Lion Publications +~ + At all times, do not lose courage in your inner awareness; uplift yourself, +while assuming a humble position in your outer demeanor. Follow the example +of the life and complete liberation of previous accomplished masters (siddha). +Do not blame your past karma; instead, be someone who purely and flawlessly +practices the Dharma. Do not blame temporary negative circumstances; instead, +be someone who remains steadfast in the face of whatever circumstances may +arise. + In brief, taking your own mind as witness, make your life and practice one, +and at the time of death, with no thought of anything left undone, do not be +ashamed of yourself. This itself is the pith instruction of all practices. + Eventually, when the time of death arrives, completely give up whatever +wealth you possess, and do not cling to even one needle. Moreover, at death, +practitioners of highest faculty will be joyful; practitioners of middling +faculty will be without apprehension; and practitioners of the lowest faculty +will have no regrets. When realization's clear light becomes continuous day +and night, there is no intermediate state (bardo): death is just breaking the +enclosure of the body. + If this is not the case, but if you have confidence that you will be +liberated in the intermediate state, whatever you have done in preparation for +death will suffice. Without such confidence, when death arrives, you can send +your consciousness to whichever pure land you wish and there traverse the +remaining paths and stages to become enlightened. (p.58) + -- "Wisdom Nectar: Dudjom Rinpoche's Heart Advice", translated by Ron Garry, + a Tsadra Foundation Series book, published by Snow Lion Publications +~ + There are many different forms of bodywork that can purify and heal in the +context of preparing for tantric practice. Trauma held in the body from early +experiences is cleared only when we are able to work therapeutically in the +body. Whether it is body-centered therapy or the various practices of +acupuncture, osteopathy, homeopathy, and so on, if the practice releases and +transforms trauma, then it is beneficial as a preliminary to any further +tantric practice. I often suggest to people I teach that they follow some +form of body-energy healing in order to further their release of trauma. +Also, after trauma has been released, it is often extremely useful to then +explore some form of psychotherapy. + We should not assume that the traditional practices will do it all for us. +It is simply idealistic and naive to think that all our ills can be resolved +by doing the traditional preliminary practices or, indeed, by classical +"dharma practice" alone. We should consider a healthy body-mind-life +relationship as a necessary part of our practice. When we get this balance +right, we create the basis for a sound dharma practice. + The practice of tantra in particular needs this healthy, balanced basis +because when we work with tantric practices, we stimulate processes in the +body that are often very powerful. If we have a sound base for practice and +have a level of emotional and energetic maturity, then the effects of tantra +can be held and grounded without creating the potential for problems to arise. +Without a sound relationship to the body, the practice of tantra has no real +base from which to unfold.(p.53) + -- Rob Preece, "Preparing for Tantra: Creating the Psychological Ground + for Practice", published by Snow Lion Publications +~ +During a visit to America, Winston Churchill was invited to a buffet luncheon +at which cold fried chicken was served. Returning for a second helping, he +asked politely, "May I have some breast?" "Mr. Churchill," replied the +hostess, "in this country we ask for white meat or dark meat." Churchill +apologized profusely. The following morning, the lady received a magnificent +orchid from her guest of honor. The accompanying card read: "I would be most +obliged if you would pin this on your white meat." +~ +The Three Refuges + + What are the methods for causing one's own mind to become the practices? +Initially, one should take refuge and think about actions and their effects. +The refuge is the Three Jewels: Buddha, his Doctrine and the Spiritual +Community. + + [Buddha] When a sentient being purifies the taints of his own mind as well +as their latent predispositions, he is free of all defects that act as +obstructions. Thus, he simultaneously and directly knows all phenomena. Such +a being is called a Buddha, and he is a teacher of refuge, like a physician. + + [Dharma] The Doctrine jewel is the superior paths--the chief right paths +which remove the taints as well as their latent predispositions--and the +absences which are states of having removed what is to be removed. The +Doctrine is the actual refuge, like medicine. + + [Sangha] The Spiritual Community jewel is all persons, whether lay or +ordained, who have generated a superior path in their continuum. They are +friends helping one to achieve refuge, like nurses. + + The three refuges that have been achieved and presently exist in other +beings' continuums are one's own causal refuge; one relies on a protector just +as a weak person takes refuge in a stronger person. The three refuges that +one will attain in the future are one's own effect refuge. One who relies on +the Three Jewels from the point of view of knowing that he is to attain them, +must cause them to be generated in his own continuum.(p.35) + -- H.H. the Dalai Lama, "The Buddhism of Tibet", translated and edited by + Jeffrey Hopkins, with Anne Klein, published by Snow Lion Publications +~ + Buddhism was extremely helpful to me during the process of my sister's +lingering death two years ago. She was forty-five years old and had very few +spiritual aspirations. She was actually fearful and closed to any suggestions +that she might find comfort in expanding her degree of awareness and +understanding. At first I was extremely upset by her attitude, but then I +realized it was not for me to decide what she should or should not do with the +last few months of her life. I was with her for support and comfort and not +to force her to view her life in a way which was foreign and threatening to +her. + Enabling a person to accomplish a sense of having lived purposefully and +with significance is a major goal of caregivers and loved ones. Being able to +support someone during their dying trajectory, regardless of what they are +thinking or feeling is probably one of the most valuable services one person +can offer to another. But, it is difficult to stay close to someone who is +dying. Not trying to evade an open encounter with the intense psychic pain +that usually accompanies the recognition of impending death is one of the most +valuable contributions that a nurse or any other caregiver or loved one can +make to the patient who wishes to discuss his or her circumstances. Facing +forthrightly the situation of dying, however, requires feeling comfortable +with one's own feelings about death and the frailty of being human. + Buddhism has taught me that death need not be approached only as a tragedy; +it is also an event from which a profound understanding can unfold. (p.44) + -- "Buddhism through American Women's Eyes", edited by Karma Lekshe Tsomo, + published by Snow Lion Publications +~ + Offerings should not be influenced by fluctuations of motivation and they +should not be procured by devious means--offerings procured through wrong +means are not good offerings. They should be arranged with proper motivation. +As explained in the precepts of refuge, you should make offerings of the first +portion of your food or drink of the day, whether it be food, milk or tea. + Offerings should be made of what is edible; it is not helpful to arrange a +torma that could not be eaten and then to say OM AH HUM, OM AH HUM. If you +can in reality transform something into delicious food just by reciting OM AH +HUM three times, then it is alright! On the other hand, if your offerings +remain as mere tsampa (roasted barley flour) after having repeated OM AH HUM a +thousand times, it will not help much. The offerings should be the best you +can afford. At least you can offer the first portion of your daily food, as +no one can live without food! Our offerings should be something which is +edible.... [Even] if you make water offerings in a proper manner, you can +generate great merit.(p.35) + -- H.H. the Dalai Lama, "The Union of Bliss and Emptiness: Teachings on the + Practice of Guru Yoga", translated by Thupten Jinpa, published by Snow + Lion Publications +~ + Hundreds of people may be more popular, powerful, and wealthy than we are, +but from the point of view of the Dharma, no one is more fortunate. We have a +very precious opportunity to make the best of our lives by working toward the +attainment of buddhahood. We have obtained this precious human birth and have +come in contact with the teachings and spiritual friends. All the favorable +conditions are available--we could not ask for more. Yet this is only for a +very short period of time. Within this very short time, the best thing we can +do for ourselves is commit ourselves fully and wholeheartedly to practicing +the disciplines, which are an essential part of the practice of the teachings. + ...The practice of discipline is very profound. In terms of the +effectiveness of the practice of the Dharma, there is a hundredfold difference +between someone who follows some level of discipline and someone who does not. +Whether visualizing a deity, practicing basic meditation, or reciting mantras, +the benefit is a hundredfold greater when we have the ground of discipline. + The teachings of the Buddha say that if we take dust from the footprint of a +person who embodies discipline and put it on our heads, it is a blessing. +Even the king of the devas would do that, because of the sacredness of +discipline. There is a tradition, followed to this day in India, of touching +the feet of a holy person or touching the doorstep before entering his or her +door, and then touching our foreheads. This is not merely a cultural +tradition, but is acknowledging something very profound.(p.73) + -- Khenpo Karthar, "Dharma Paths" 2nd Edition, translated by Ngödup Burkhar + and Chöjor Radha, edited by Laura M. Roth, published by Snow Lion + Publications +~ + According to the lower schools of Buddhist thought, when a being, like +Sakyamuni Buddha, attains mahaparinirvana and passes away, he ceases to exist, +there is no further continuity of consciousness. Therefore, according to the +Vaibhasika school, for example, after this point there is no more being, no +more consciousness. Only the name remains. And yet, they believe that this +being who has now disappeared can influence the course of those who follow him +due to the virtues that he created in the past. + This is not accepted by the higher schools of thought, however, that instead +believe that there are two kinds of bodies, those that are pure in nature and +those that are impure. The latter is more gross, whereas a body that has been +purified is more subtle. Now, for example, when Sakyamuni Buddha gave up his +body, there still remained the more subtle one. So, according to these +schools of thought, at the stage of Buddhahood, there are two bodies: a mental +body and a physical one. I don't know whether the English word "body" is the +most appropriate one. In Sanskrit, the words used to signify these two bodies +of the Buddha are dharmakaya and rupakaya. The first is of the nature of +mind, whereas the latter is material. So when the Buddha passes away, there +is still this more subtle body, which is of the nature of mind, and since the +mental continuum is also present, we can say that the personality is still +there. Even today, the Buddha remains as a living being. I think this is +better, don't you?(p.91) + -- H.H. the Dalai Lama, "Answers: Discussions with Western Buddhists", + edited by Jose Ignacio Cabezon, published by Snow Lion Publications +~ + Blame everything on one thing. It simplifies life incredibly, and yet it +truly is not simplistic. If we believe from our hearts that all of our +misfortunes can be attributed to self-centeredness, this must radically +transform our lives. + Do we have reservations? Isn't there some part of the mind that says, +"Self-centeredness is not such a bad idea. It got me my job, a good salary, +my house and car. How can this be my enemy?" On the surface self-centeredness +may seem like an aide who looks after our interests. There is one powerful +answer to this: insofar as self-centeredness dominates our lives, it brings us +into conflict with virtually everyone else. Because most people are dominated +by self-centeredness, their interests are at odds with our own. There is +bound to be conflict, and conflict gives rise to suffering. + Imagine what life would be like without self-centeredness. Would we give +away all our possessions, waste away from malnutrition, and die prematurely of +disease? No. This would be a partial lack of self-centeredness combined with +a large part of stupidity. If we are to serve others effectively, we must +take care of ourselves. A bodhisattva has no self-centeredness, but there +have been people in all stations of life, including kings, who are +bodhisattvas. If we free ourselves of self-centeredness and really concern +ourselves with the cherishing of others, then our own welfare comes as a kind +of echo.(p.67) + -- B. Alan Wallace, "The Seven-Point Mind Training", edited by Zara + Houshmand, published by Snow Lion Publications +~ + When we achieve a mind focused on mind with the perfect placement of +absorbed concentration, free from all faults of dullness or flightiness, we +increasingly experience an element of bliss accompanying our meditation. When +we experience serene joy, on both a physical and mental level, brought on by +the force of total absorption of mind on mind, we achieve a meditational state +that fulfills the definition of shamata. + Our ordinary mind is like raw iron ore that needs to be made into a steel +sword. Progressing through the stages for attaining shamata is like forging +the iron into steel. All the materials are there at our disposal. But since +the mind wanders after external objects, then although it is the material for +attaining shamata, it cannot yet be used as this product. We have to forge +our mind through a meditational process. It is like putting the iron ore into +fire. + To fashion the steel into a sword, or in this analogy to fashion the mind +into an instrument that understands voidness, our serenely stilled and settled +mind needs to come to decisive realization of voidness as its object. Without +such a weapon of mind, we have no opponent with which to destroy the +disturbing emotions and attitudes.(p.142) + -- H.H. the Dalai Lama and Alexander Berzin, "The Gelug/Kagyu Tradition of + Mahamudra", published by Snow Lion Publications +~ + Focusing the mind on the object of meditation is like planting a seed for +the arisal of the realization.... Even in the beginning stages one might +become impatient, thinking, "I really want to get this done quickly." One +might think that by exerting more effort, by adding more and more stuff, by +changing things this way or that way the process can be made to go faster. + The good gardener knows that too much water or fertilizer is harmful, not +helpful. The mature meditator must understand this as well. The Kadampa +masters of old gave this counsel: First, pay great heed to getting the proper +causes and conditions together. Next, engage in the practice without +agitation and without anxiety. Then, with the mind at ease, carry on to the +end.(p.20) + --Gen Lamrimpa, "How to Practice Shamatha Meditation: The Cultivation of + Meditative Quiescence", translated by B. Alan Wallace, published by Snow + Lion Publications +~ +The Need for Reasoning + + All Buddhist schools agree that the analytical reasoning process which leads +to an inference (a conceptual realization) derives from basic, shared, direct +perception. As an example let us consider the following reasoning: + + A plant does not inherently exist because of being a dependent-arising. + + You begin by reflecting on the fact that a plant is a dependent-arising +because its production depends on certain causes and conditions (such as a +seed, soil, sunlight, and water), but eventually the reasoning process must be +supported by direct perception, or it cannot stand. We can see with our eyes +that plants change; they grow; mature, and finally dry up. In this sense, +inference is blind, since it must eventually rely on direct perception. +Inference depends on reasoning, which in turn rests on basic, shared, +indisputable experience through direct perception. (p.153) + -- His Holiness the Dalai Lama, "How to Practice: The Way to a Meaningful + Life", translated and edited by Jeffrey Hopkins +~ + The root of all qualities of the Bodhisattva vehicle is caring for sentient +beings. We admire and respect the Buddha because he has reached the state +free of all faults and possessing all good qualities, knows the method to +reach that state, and teaches it to us. If we do as the Buddha did, by +meditating on love and compassion for all sentient beings, not harming or +getting angry with them, we too can become a Buddha. + Our enlightenment depends on the Buddhas and on sentient beings, and from +this point of view, they are equally important to us. Thus when we look at +any sentient being, we should recognize that she is indispensable to our +attainment of enlightenment. Our enlightenment comes from cherishing sentient +beings; it does not come from cherishing only ourselves. Understanding this, +whenever we encounter people in our lives, it becomes easy to feel, "May this +person be happy and free from suffering." + Caring for sentient beings means freeing them from the suffering of +unfortunate rebirths and of cyclic existence in general, teaching the Dharma +to those who want to hear it, providing the means for them to eliminate the +causes which bring suffering temporarily and ultimately, not harming them, not +lying to them, not creating discord among them, not speaking harshly to them, +and so on. Through caring about them now, excellent results will follow, for +us and for them.(p.179) + -- Geshe Jampa Tegchok, "Transforming Adversity into Joy and Courage: An + Explanation of the Thirty-seven Practices of Bodhisattvas", edited by + Thubten Chodron, published by Snow Lion Publications +~ + The term 'karma' literally means 'action', and more specifically refers to +the process of cause and effect, where the intention of an agent or being is +involved. So here karma means an intentional act committed or carried out by +a being who possesses a sentient nature and who is also capable of having a +sentient experience. + ...Buddhist texts state that only a buddha's omniscient mind can penetrate +the subtlest aspects of the workings of karma, and know at the most +microscopic level which specific causes and conditions give rise to which +specific consequences. At our level, we can only recognise that an intimate +relationship exists between the external elements of the material world and +the internal elements of our mental world; and, based on that, we can learn to +detect varying levels of subtlety within our mental and emotional experiences. +(p.13) + -- H.H. the Dalai Lama, "Lighting the Way", translated by Geshe Thupten + Jinpa, published by Snow Lion Publications +~ + All attachment and aversion come from what we have mentally created. We +have made an image and that is our mind as we normally experience it. In +order to solve this problem in a more profound and permanent way, we have to +look at our mind and see its true nature. In our innate, unfabricated nature, +which is the basic state of our mind, there is no problem. We make all our +problems by creating concepts and all kinds of mental conditioning. + Seeing the true nature of mind means experiencing the way the mind is when +we do not fabricate and contrive anything. We need to look at our mind when +it is devoid of our creations and free from mental elaborations. If we can +see this state of mind, there is no grasping, no grasped object, and no +subject doing the grasping. There is simply perception or seeing, which in +itself does not cause a problem. + When the true nature of mind is seen, there are just appearances without any +evaluation. One thing arises in the mind and then another thing arises. The +arising that is pleasant is no better than the one that is unpleasant. They +are simply different manifestations of the mind. There is no need to grasp +one and reject the other. Once this is seen clearly, we see the true nature +of mind. This is something that we need to experience directly. When we see +the truth, we become liberated from our struggle within the nets of aversion +and attachment.(p.97) + --Ringu Tulku, "Daring Steps: Traversing the Path of the Buddha", edited and + translated by Rosemarie Fuchs, published by Snow Lion Publications +~ + In general, most non-Buddhist religions meditate on the deity as being +outside the physical body. In these cases the deity takes the form of a +refuge, or of a protector or messenger. Thus do they meditate, and of course +this is fine. In the Buddhist tradition, however, the deity is not meditated +on as being outside the physical body. One meditates on the deity as being +one's own essence expressing itself through oneself arising as the deity. One +therefore thinks, "I am the deity," and with this conviction one meditates. + Why is it justifiable to meditate in this manner? As previously seen, the +five afflictions are actually self-expressions of the five kinds of primordial +awareness; thus our own mind is in essence exactly the same as the mind of a +Buddha. In the philosophical treatises this is sometimes referred to as +'sugatagarbha' or 'buddha-nature'. + Because all beings possess this innately pure buddha-nature, they are pure +by nature and not at all impure. Being pure by nature it is perfectly +justified to meditate that you are the deity, because this is exactly how it +is! (p.95) + --Khenchen Thrangu Rinpoche, "Everyday Consciousness and Primordial + Awareness", translated and edited by Susanne Schefczyk, published by Snow + Lion Publications +~ + In day to day life if you lead a good life, honestly, with love, with +compassion, with less selfishness, then automatically it will lead to +nirvana....We must implement these good teachings in daily life. Whether you +believe in God or not does not matter so much; whether you believe in Buddha +or not does not matter so much; as a Buddhist, whether you believe in +reincarnation or not does not matter so much. You must lead a good life. + And a good life does not mean just good food, good clothes, good shelter. +These are not sufficient. A good motivation is what is needed: compassion, +without dogmatism, without complicated philosophy; just understanding that +others are human brothers and sisters and respecting their rights and human +dignity. That we humans can help each other is one of our unique human +capacities. We must share in other peoples' suffering; even if you cannot +help with money, to show concern, to give moral support and express sympathy +are themselves valuable. This is what should be the basis of activities; +whether one calls it religion or not does not matter.... In my simple +religion, love is the key motivation.(p.20) + -- The Fourteenth Dalai Lama, His Holiness Tenzin Gyatso, "Kindness, + Clarity, and Insight 25th Anniversary Edition", edited and translated by + Jeffrey Hopkins, co-edited by Elizabeth Napper, published by Snow Lion +~ + All beings suffer in the same way as we do, and some are even more deeply +immersed in sorrow. Yet all of these beings wish to experience only happiness +and to avoid all suffering, frustration, and pain. They wish lasting +happiness but do not know how to cultivate its causes, and they wish to avoid +misery but automatically collect only causes of further misery. As Shantideva +said, "Although seeking happiness, they destroy their own causes of happiness +as they would an enemy. And although seeking to avoid misery, they treat its +causes as they would a close friend." + Were the countless sentient beings unrelated to us, or were they not to mind +their sufferings, perhaps there would be no need for us to bother with their +welfare. In reality, however, all are related to us and not one of them +wishes to suffer. Over the billions of lifetimes that we have experienced +since beginningless time, we have known all the living beings again and again. +Sometimes they have been parents to us, sometimes friends or mates, sometimes +enemies. Without exception, each of them has been even a mother to us again +and again, performing all the kindnesses of a mother. How can we be +indifferent to them? + Wishing them to have only happiness and its causes and to be free of +suffering and its causes, we ourselves should generate a sense of +responsibility for their well-being. Finally, as only an omniscient +Enlightened One is effectively able to benefit beings in deep, lasting, and +ultimate ways, we must quickly attain enlightenment. This is the wishing +bodhimind, the inner basis of Mahayana practice.(p.136) + -- H.H. the Dalai Lama, "The Path to Enlightenment", edited and translated + by Glenn H. Mullin, published by Snow Lion Publications +~ + Recalling our interconnectedness, we begin to recognize our total +interdependence and that whatever we enjoy in our life comes through others-- +through their efforts, their work, their hardships. + It does not necessarily require that others had a specific intention to +enable us to enjoy the things of our life. If we think of this in terms of +the obvious examples like food and clothing, we can immediately see the global +meaning of this contemplation. Our food comes from all over the world and if +we consider the people and other creatures involved in its production, +picking, packaging, transportation, and selling so that we can enjoy it, the +numbers are vast. It is through their labor, their efforts, their struggles +that we enjoy what we eat. Often their lives are terribly hard, and to feed a +family they must work for very little--yet we enjoy the fruits of their labor. +This is something to feel a huge gratitude for. + If we begin to look more closely at our Western life, we can see how much we +are dependent upon people in considerably poorer circumstances all over the +world for what we consume. What we often don't consider is the impact of this +consumption on those who produce it. In this meditation, it can be very +useful to spend some time dwelling upon this so that we really feel the +profound depth of appreciation for our interdependence upon others for our +lives. This can counter the tendency to take our good fortune for granted and +can open up a sense of gratitude for the kindness of those around. If guilt +arises, it can be used to increase our awareness of the responsibility we have +globally. + Gradually, we may begin to see the complete interdependent nature of our +relationship with the countless other beings around us. We cannot overlook +this connectedness to others and the kindness and benefit we have gained +through them. When we come to feel this deeply, we will be able to hold +others dear and automatically respond to others with a greater sense of care +and concern.(p.80) + -- Rob Preece, "The Courage to Feel: Buddhist Practices for Opening to + Others", published by Snow Lion Publications +~ +If you cannot stop worrying over something in the past or what might happen in +the future, shift your focus to the inhalation and exhalation of your breath. +Or recite this mantra: om mani padme hum (pronounced "om mani padmay hum"). +Since the mind cannot concentrate on two things simultaneously, either of +these meditations causes the former worry to fade.(p.133) + -- His Holiness the Dalai Lama, "How to Practice: The Way to a Meaningful + Life", translated and edited by Jeffrey Hopkins +~ +When serving society or others in general, it is very important to set a +proper motivation at the start of each day. When we wake up each morning, we +reflect, 'Today I am not going to come under the power of either attachment or +hostility. Today I am going to be of benefit and help to others.' Thus we +consciously set the tone for the entire day so that we go through it within +the context of a pure, altruistic motivation and attitude. + -- H.H. the Dalai Lama, excerpted from "The Gelug/Kagyu Tradition of + Mahamudra", published by Snow Lion Publications +~ + We all have a certain style for doing things--how we drive, how we cook, how +we dress. Some of us are shy or cautious, others assertive or flamboyant. +We've refined that style over the years based on how successful it is, but +it's not usually something of which we're completely aware. As long as it +gets the job done, as long as we get the appropriate feedback from others, our +style goes unnoticed, and when questioned we'll say, "That's just the way I +am." + When we begin meditation, it is inevitable that we will meditate with the +same style with which we do everything else, because it's who we think we are. +Furthermore, this style has proven to be reasonably successful in our other +activities. However, in this case, it is not at all appropriate. If there is +any style, there is a hidden agenda and an implicit judgment of the various +phenomena of meditation. There is not the true detachment or choiceless +awareness of real meditation. Our style contains our unacknowledged attitudes +toward meditation. + ...What's the problem in meditating with an attitude? First, a large amount +of energy goes into maintaining the attitude. To make this clearer, if we are +trying to be aware of our breathing, 100 percent of our attention should be on +our breathing. If we're thinking, "I'm a shy person and I'm a little afraid +of what's going on here," even if we're not consciously aware of that thought, +it will be taking our energy away from the breathing and keeping it tied up in +the world of ego. Consequently, this energy is not available for our +practice. And your evaluation of your practice and progress will be based on +your agenda rather than on the Buddha's teaching. + Of course, no one is a perfect meditator. It's not like we have to wait +until we have a perfect attitude before we begin. If that were the case, we +would never start..With time, the purity of your attitude will grow...refining +one's approach is a lifetime's work and is at the same time the practice +itself.(p.72) + -- Bruce Newman, "A Beginner's Guide to Tibetan Buddhism", published by + Snow Lion Publications +~ + Developing a sense of good cheer in the face of adversity, you can +specifically use adversity as the support for refuge and true spiritual +development. I am discussing how you relate to your suffering, how you relate +to your adversity, as it affects you in life and on the path. + Now, as you know, whenever you are suffering by way of the body, speech, and +mind, be it physical illness or a mental affliction, this is a very big deal +to you. Usually it appears as something major. Even if it's minor, you make +it into some great distress. If you lose a little money or if someone speaks +nastily to you, it invokes a strong reaction. This is called "appearances +arising as the enemy." When your habituation to adversity reaches such a +point that you actually fall prey to appearances arising as the enemy, it +means that you no longer have patience for suffering. + ...If you can't bear the minor aspects of adversity in this, the best +rebirth in cyclic existence, the precious human rebirth, what will you do when +you're reborn in the three lower realms? Samsara is so vast, so deep and +limitless, and the number of sentient beings within samsara are equal to that. +All of them want to be free; all of them desire liberation. You should +consider then how unnecessary or pointless it is to think that your small +problems in this fortunate life are so great, when in fact they really are +not. + Any rebirth in this ocean of cyclic existence will by nature bring this type +of discontent or suffering. Since you've been in this cycle of rebirths from +beginningless time until now and you are still not free, it points out the +fact that help is needed. Refuge is necessary. Adversity then becomes the +support for training in refuge, which demonstrates that adversity is used to +your advantage.(p.44) + -- Ven. Gyatrul Rinpoche, "Meditation, Transformation, and Dream Yoga", + trans. by B. Alan Wallace and Sangye Khandro, published by Snow Lion Pub. +~ + The practice of Dharma is to pacify the afflictions and concepts that fill +our minds. When we blend the teachings with our minds, the power of the +Dharma can act upon and pacify afflictions and concepts. If on the outside we +look like Dharma practitioners while on the inside our Dharma practice has not +diminished our afflictions or concepts, we merely call ourselves practitioners +without actually being one. This is not to say that outer behavior, our +reflection in the world, is not important, but what is crucial is to train in +taming our minds. + What we tame are the three main afflictions: ignorance, attachment, and +aversion. Ignorance, the root of the two others, is defined as the continual +fixation on our self that we assume to be permanent and independent. This +ego-clinging is the main cause for our cycling in samsara. We wish to be in +paradise for our own advantage; we wish to erase all suffering for our own +advantage. We cling to this "I" of ours, thinking that it is so special that +we should not be bothered with problems but enjoy wealth, power, and charisma. +If we honestly look into our minds, it is quite easy to see this kind of +coarse and obvious grasping to a self. + There are also subtle forms of fixating on the self ("I") and what belongs +to it ("mine"), like the quick thought of ourselves before another one comes. +When practicing Dharma, we are taming this coarse and subtle clinging to an +ego. If this does not happen, we will merely be able to suppress the +afflictions temporarily, distancing ourselves for the time being. To cut +through them completely, we must steadily apply ourselves to practice.(p.187) + -- "Music in the Sky: The Life, Art and Teachings of the Seventeenth + Karmapa, Ogyen Trinley Dorje", by Michele Martin, published by Snow + Lion Publications +~ + "The mantra of the perfection of wisdom--the mantra of great knowledge, + the unexcelled mantra, the mantra equal to the unequalled, the mantra + that quells all suffering--is true because it is not deceptive." + -- The Heart Sutra + + The perfection of wisdom is called "the mantra of great knowledge" because +thoroughly understanding its meaning eliminates the three poisons of craving, +hatred, and delusion. It is called the "unexcelled mantra" because there is +no greater method than the perfection of wisdom for saving one from the +extremes of cyclic existence and the isolated peace of individual nirvana. It +is called the mantra "equal to the unequalled" because the Buddha's +enlightened state is unequalled, and, through the deepest realization of this +mantra, one attains a state equal to that state. Finally, the perfection of +wisdom is known as the "mantra that quells all suffering" because it quells +manifest sufferings and also removes all propensities for future suffering. + The perfection of wisdom is the ultimate truth, thus the statement "it is +true." In the realm of the ultimate truth, there is no disparity, as there is +in conventional reality, between appearance and reality, and thus this +manifest ultimate truth is "not deceptive." This nondeceptiveness also +suggests that, through actualization of this mantra, the perfection of wisdom +can enable one to attain total freedom from suffering and its causes. From +this perspective too, we can say that it is the truth. + "The mantra of the perfection of wisdom is proclaimed: tadyatha gate gate +paragate parasamgate bodhi svaha! Shariputra, the bodhisattvas, the great +beings, should train in the perfection of wisdom in this way." + -- H.H. the Dalai Lama, "Essence of the Heart Sutra: The Dalai Lama's Heart + of Wisdom Teachings", translated & edited by Geshe Thupten Jinpa +~ +For as long as space endures +And for as long as sentient beings remain, +Until then may I too abide, +To dispel the misery of the world. + -- Shantideva, A Guide to the Bodhisattva Way of Life +~ +Global Responsibility + + Occasionally I notice that people are making a convenient distinction +between ethics on the personal level and ethics on the wider social level. To +me, such attitudes are fundamentally flawed, as they overlook the +interdependence of our world. + That individual ethics--or rather their absence--can have an impact on the +lives of many is powerfully demonstrated by the global financial crisis that +began in 2008, the repercussions of which are still being felt around the +world. It revealed the way unbridled greed on the part of a few can adversely +affect the lives of millions. So, just as in the wake of the 9/11 attacks we +started to take the dangers of religious extremism and intolerance seriously, +so too, in the wake of the financial crisis, should we take the dangers of +greed and dishonesty seriously. When greed is seen as acceptable, even +praiseworthy, there is clearly something wrong with our collective value +system. + In this age of globalization, the time has come for us to acknowledge that +our lives are deeply interconnected and to recognize that our behavior has a +global dimension. When we do so, we will see that our own interests are best +served by what is in the best interests of the wider human community. By +contrast, if we concentrate exclusively on our inner development and neglect +the wider problems of the world, or if, having recognized these, we are +apathetic about trying to solve them, then we have overlooked something +fundamental. Apathy, in my view, is itself a form of selfishness. For our +approach to ethics to be truly meaningful, we must of course care about the +world. This is what I mean by the principle of global responsibility, which +is a key part of my approach to secular ethics.(p.84) + -- His Holiness the Dalai Lama, "Beyond Religion: Ethics for a Whole + World", trans. by Thupten Jinpa Langri +~ + Speech that is not harmful is the meaning of "right speech." It is wise +speech. Wise people can still be quite firm and decisive when that is what is +needed. It means finding generous and productive ways of saying things. +There are times when we need to be strict, but we do not have to denigrate or +harm the person or child who is out of line. Firm speech can also be wise +speech. + Wise speech is another tool that can be practiced. We can begin by +practicing wise speech to ourselves--replacing the inner voice of guilt that +is putting us down and opening a space to listen to our deeper needs. + What can I say which will be helpful to someone? What tone of voice will I +use? And when is it wise to say nothing? Imagine yourself actually saying +something helpful and supportive. Imagine the difference it would make in +your life if you could say just one helpful thing to one person. Imagine your +life if your speech always came from wisdom.(p.136) + -- Chonyi Taylor, "Enough! A Buddhist Approach to Finding Release from + Addictive Patterns", published by Snow Lion Publications +~ +The famous nineteenth-century dzogchen master Paltrul Rinpoche explained self- +liberation concretely and precisely: + + The practitioner of self-liberation is like an ordinary person as far as + the way in which the thoughts of pleasure and pain, hope and fear, + manifest themselves as creative energy. However, the ordinary person, + taking these really seriously and judging them as acceptable or rejecting + them, continues to get caught up in situations and becomes conditioned by + attachment and aversion. + + Not doing this, a practitioner, when such thoughts arise, experiences + freedom: initially, by recognizing the thought for what it is, it is freed + just like meeting a previous acquaintance; then it is freed in and of + itself, like a snake shedding its skin; and finally, thought is freed in + being unable to be of benefit or harm, like a thief entering an empty + house. + +...Freeing or liberating thought does not mean ignoring, letting go of, being +indifferent to, observing, or even not having thoughts. It means being +present in hope and fear, pain and pleasure, not as objects before us, but as +the radiant clarity of our natural state. Thus anger, for example, when +experienced dualistically, is an irritation which we may indulge in or reject, +depending on our conditioning. Either way we are caught up in it and act out +of it. But when aware of anger as a manifestation of clarity, its energy is a +very fresh awareness of the particulars of the situation. However, these +particulars are no longer irritating.(p.77) + + -- Longchenpa, "You Are the Eyes of the World", translated by Kennard Lipman + and Merrill Peterson, introduction by Namkhai Norbu, published by Snow + Lion Publications +~ + In this practice one recollects negativity, contemplates its nature, +generates apprehension of its karmic implications, and resolves to purify +one's mind of the negative traces. On the basis of this resolve one takes +refuge, develops the bodhimind and enters the Vajrasattva meditation or +whatever method is being used. One can also do exercises such as prostrations +and so forth. This concentration of purifying energies destroys the potency +of negative karmic imprints like the germ of a barley seed roasted in a fire. + Here it is important to begin the meditation session with a contemplative +meditation and then to transform this into settled meditation for a prolonged +period of time. One abides in the settled meditation until it begins to lose +intensity, and then temporarily reverts to contemplative meditation in order +to invigorate the mind, returning to fixed meditation once a contemplative +atmosphere has been restored. + Generally our mind is habituated to directing all of our energies into +things that benefit this life alone, things of no spiritual consequence. By +performing these types of meditations, our natural attachment to the +meaningless activities of this life subsides and we begin to experience an +inner appreciation for spiritual values. When spontaneously one's mind +appreciates spiritual rather than mundane goals one has become an active +practitioner of initial perspective.(p.117) + -- H.H. the Dalai Lama, "The Path to Enlightenment", edited and translated + by Glenn H. Mullin, published by Snow Lion Publications diff --git a/database/patterns/vi/pattern_auto_replacement b/database/patterns/vi/pattern_auto_replacement new file mode 100644 index 00000000..6794d440 --- /dev/null +++ b/database/patterns/vi/pattern_auto_replacement @@ -0,0 +1,9 @@ + + +pattern that automates making changes in a lot of files with vi. +system_config is to be changed to path_config. substitute as needed. + +:1,$s/system_config/path_config/g +:wn + + diff --git a/database/patterns/vi/pattern_fix_commented_formal_parameters.txt b/database/patterns/vi/pattern_fix_commented_formal_parameters.txt new file mode 100644 index 00000000..2544473d --- /dev/null +++ b/database/patterns/vi/pattern_fix_commented_formal_parameters.txt @@ -0,0 +1,6 @@ + +:1,$s/\/\*\([a-zA-Z_][a-zA-Z0-9_]*\)\*\//formal(\1)/g +:wn + + + diff --git a/database/pictures/celtic_destiny14.jpg b/database/pictures/celtic_destiny14.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2b0679428f00f4e943bf3bf747593f2aa34c7e69 GIT binary patch literal 44866 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 RZe6INifR#E4BX_8|JmNJ5xxKb literal 0 HcmV?d00001 diff --git a/database/pictures/clamblock2.jpg b/database/pictures/clamblock2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..91901860f125d942d807839aa38c33b6656d9e8e GIT binary patch literal 75805 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

      + + + + + + + +

      +
      + no matches +
      +
      +(thanks to +Karlsville +for this beautiful photo of matches which we have defaced horribly.
      +check out the original and other images on his +photography page.) +
      +
      + + diff --git a/database/pictures/no_matches.jpg b/database/pictures/no_matches.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3ed20fe52c8c6581ceb402a88f488fde0184a7ac GIT binary patch literal 35664 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 + + + + + + + + YETIcode Bash Scripts + + + + +

       

      +
      + + + + + + +
      +
      +

      YETIcode Open Source Bash +Scripts

      +

      GPL-Licensed Bash Code
      +

      +
      +
      Contributed by Chris Koeritz (Koeritz@Gruntose.COM)
      +See the GNU Public +License for details of licensing.
      +
      +

      Caveats: please refer to the Perl +Scripts page which shares the same constraints as these Bash +Scripts do.
      +

      + Direct Cognition: +View the scripts directory itself rather than navigating with the links +below: scripts. +
        +
      +
      +
      +

       

      +
      + + + + + + +
      +

      Assorted Bash Script Files

      +
      +

      uhh.sh

      +
      This documentation page isn't quite ready yet.
      +In fact, this file is still in its very first few iterations and is +pretty +limited so far...
      +
      +

      cvs_importer.sh

      +
      This script eliminates the need to use the cvs +import command to pull in a whole new sub-hierarchy.  Given a +sub-folder under an existing hierarchy, it will add all the +subdirectories and all of the files in those subdirectories.  The +script avoids adding any files found in the internally used CVS +directories.
      +For example, suppose you have a folder called "hoople" that's a +top-level project in CVS.  You want to add a new folder under the +existing hierarchy "hoople/source/lib_src" called "pasta".  The +"pasta" hierarchy has hundreds of subdirectories and thousands of +files.  Assuming that you've already moved the new pasta source +folder into place, the command would be:
      +    cvs_importer hoople/source/pasta
      +If you don't have the Yeti aliases set up, then you may need to type +this instead:
      +    bash ~/yeti/scripts/cvs_importer.sh +hoople/source/pasta
      +
      +

      +

      movie_seeker.sh +/ movie_stripper.sh +/ show_stripper.sh
      +

      +
      Some manipulation methods for a +particular type of movie and TV show database.  These expect a db +in the form of a CSV file with three fields per line: (1) index number, +(2) show or movie name and (3) show episode title.  The third +field is defined as blank for movies.  The movie_seeker finds a +given movie (or pattern) in the database.  The movie_stripper +pulls out all the unique movie or show names in the database.  The +show_stripper pulls out the episode names for a particular show name +(or pattern).
      +
      +

      call_movie_seeker.sh +/ call_movie_stripper.sh +/ call_show_stripper.sh
      +

      +
      These are CGI correspondents to +the above movie database searches.  They're used on the Gruntose +web site.
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + + diff --git a/docs/binaries_note.txt b/docs/binaries_note.txt new file mode 100644 index 00000000..e4191f65 --- /dev/null +++ b/docs/binaries_note.txt @@ -0,0 +1,22 @@ + +There are a few pre-built binaries included with hoople. +They originate from a few different sources. Feel free to not trust them. + +These apps are external to the HOOPLE build... + +build/msys hierarchy: + This is copied directly from the msys project of MingW. + It has had zip.exe, unzip.exe and short_path.exe added to it. + + unzip.exe & zip.exe: + The zip and unzip tools are from the info-zip project. They are somewhat + antiquated but still work great on the various 32-bit windows platforms. + These utilities are self-identified as: "Zip 2.3 (November 29th 1999)" + To get a more recent version of the zip utilities or build them yourself, + they can be found at the site: http://www.info-zip.org/ + + short_path.exe: + Built by the hoople bootstrapping process, but unfortunately this tool + is desperately needed for windows builds to work nicely with clam and + bash. It can be replaced by any newer working version from the bootstrap. + diff --git a/docs/clam_manual/clam_docs.html b/docs/clam_manual/clam_docs.html new file mode 100644 index 00000000..8bfa888b --- /dev/null +++ b/docs/clam_manual/clam_docs.html @@ -0,0 +1,2334 @@ + + + + + + + CLAM Reference Manual + + +
      + +

      CLAM: Coordinated Librarian &

      +

      Automatic Maker

      +
      +
      +
      + +

      Tutorial and Reference Manual

      +
      +
      +
      + +

      +

      +
      +
      + +

      Table of Contents

      +
      +
        + +
      1. Executive Summary
      2. + +
      3. Preparing Your Computer to Use +CLAM
      4. + +
          + +
        1. Necessary Steps
        2. + +
        + +
      5. CLAM Tutorial
      6. + +
          + +
        1. Caveats
        2. + +
        3. Basics
        4. + +
        5. Common Files
        6. + +
        7. Important Variables
        8. + +
        9. Variable Assignment
        10. + +
        11. Optional Variables
        12. + +
        13. Writing Your Own Rules
        14. + +
        15. Conclusion
        16. + +
        + +
      7. CLAM Reference
      8. + +
          + +
        1. Language Independent +Variables
        2. + +
        3. Language Independent +Rules
        4. + +
        5. Language Independent +Targets
        6. + +
        7. Language Independent +Files
        8. + +
        9. C++ Specific Variables
        10. + +
            + +
          1. Directory Structure +Variables
          2. + +
          3. Compiler Dependent Flags
          4. + +
          5. Microsoft Visual C++ Only
            +
          6. + +
          7. Support for +Compilation +Extensions
          8. + +
          + +
        11. C++ Specific Rules
        12. + +
        13. C++ Specific Targets
        14. + +
        15. C++ Specific Files
        16. + +
        + +
      9. Example CLAM Makefiles
      10. + +
      11. CLAM Hints and Troubleshooting
      12. + +
      13. Acknowledgements
      14. + +
      +
      + +

      +

      +
      +
      + +

      Executive Summary

      +
      +    The CLAM system is a set of macros and rules +for the GNU make program +that +simplifies the creation of executable programs and code +libraries.  +Most makefiles that use the CLAM system are ten lines long or +less.  +Makefiles are stated in terms of a set of special variable names that +CLAM +interprets in order to issue the correct sequence of compilation +directives.  +This document presents a tutorial on the variable names and simple +rules +that need to be used with CLAM.  Several example makefiles and the +full +reference manual for CLAM are also included.
      +    CLAM is part of the HOOPLE libraries (http://hoople.org/) and can be +downloaded from there or through a sourceforge mirror site.
      +    In the remainder of the document, we will often +refer to CLAM as just "clam".
      +  +
      +
      +

      +

      +
      +
      + +

      Preparing Your Computer to Use +CLAM

      +
      +

      Necessary Steps:

      +
        + +
      1. Setting environment variables for clam:
      2. + +
          +
        1. YETI_DIR:
        2. + +
            + +
          1. +**REVISE** out of date... + +This variable has been needed since clam became +part of the YETIcode project (at http://yeticode.org).
          2. + +
          3. The default location for clam is under the YETI_DIR +in a +folder named clam, although the version of clam shipped with the HOOPLE +code (http://hoople.org) actually lives under the root of the hoople +hierarchy.
            +
          4. + +
          5. If the yeti root directory is in $HOME/yeti +already, then the +default for YETI_DIR will work and it doesn't need to be declared.
          6. +
          7. Setting the variable:
            +
          8. + +
              + +
            1. On Unix (with the bash shell): export MAKEFLAGS="-I $HOME/yeti/clam"
            2. + +
            3. On win32: set +MAKEFLAGS="-I c:/yeti/clam"  (or set this in the System +control panel, under the advanced tab, in environment variables)
              +
            4. + +
            5. Note that the use of +forward slashes is mandatory in the clam directory in MAKEFLAGS.
              +
            6. + +
            + +
          + +
        3. MAKEFLAGS:
        4. + +
            + +
          1. This variable is required to be set in the +environment before using clam with gnu-make.  It tells make where +to find the clam definitions and scripts.
          2. + +
          3. Setting the variable:
            +
          4. + +
              + +
            1. On Unix (assuming bash as shell): export MAKEFLAGS="-I $YETI_DIR/clam"
            2. + +
            3. On win32: set +MAKEFLAGS="-I %YETI_DIR%/clam"
            4. + +
            5. This variable also requires forward slashes +instead of +backslashes.
            6. + +
            + +
          + +
        + +
      3. Required Tools:
      4. + +
          + +
        1. The compiler itself:
          +
        2. + +
            + +
          1. If you are running GNU/Linux (or almost any other +Posix-compliant operating system), then the GNU C/C++ compiler +suite is pretty much all that's needed.
          2. + +
          3. The +GNU C/C++ compiler (included in the MinGW +toolkit) should be all that's needed for +compilation, +but the Microsoft Visual Studio 6.0-8.0 compilers can be used if +available.  Compatibility is only guaranteed for vc8 +however.
            +
          4. + +
          + +
        3. Win32 Unix Tools:
        4. + +
            + +
          1.  If you are running a win32-based product +(windows NT, +windows +2000, +windows xp, etc) then a few additional tools are required...
            +
          2. + +
          3. The recommended GNU utilities are available for +win32 in the +MingW MSYS +package (http://www.mingw.org/).
          4. +
          5. The MSYS tools are actually included in the source safe +archive for +hoople. The version provided by hoople is recommended +because a +couple of missing tools have been added back in.
          6. +
          7. They can also be downloaded the mingw web site: MSYS +package: msys_bins.tar.gz
          8. +
          9. Note that you will need to add the +binaries +directory from MSYS +to +your path.  The PATH variable can be accessed under MS-NT type +OSes through the +"control panel | system | advanced | environment variables" menu +trail.  If you plan to use msys outside of clam, then ensure that +the MSYS bin (l:/msys/bin) directory is prior +to the +windows system directory in your path; this causes the Unix "find" +command to be used instead of the NT version.
          10. +
          11. Alternatively, a similar set of GNU utilities is +available +in the Cygwin +package, although these tools are no longer recommended and are, in +fact, actively deprecated.
          12. + +
              + +
            + +
          + +
        5. makedep and version_stamper tools:
        6. + +
            + +
          1. The hoople/bin directory in the archive has +pre-built +versions of tools used by clam during a build.
            +
          2. + +
          3. If you would rather rebuild them from source, then +running +the script "bin/bootstrap_build.sh" will +recreate all of these internal tools.
            +
          4. + +
          + +
        + +
      5. Third Party Tools Used By or Supported Within clam:
      6. + +
          + +
        1. wx widgets:
        2. + +
            + +
          1. home page: http://www.wxwidgets.org/
          2. + +
          3. As far as the clam team is concerned, this is the +premier +portable (and open source) library for graphical user interfaces.
            +
          4. + +
          + +
        3. OpenSSL:
        4. + +
            + +
          1. home page: http://www.openssl.org/
          2. + +
          3. This is the team's most favorite library for SSL +(Secure +Sockets Layer) and general encryption needs.
            +
          4. + +
          + +
        5. cURL:
        6. + +
            + +
          1. home page: http://curl.haxx.se/
          2. + +
          3. The curl library rocks(!) and provides a very +powerful set of +tools for programmatically interacting with live web pages.
            +
          4. + +
          + +
        + +
      7. Other clam Preconditions:
      8. + +
          + +
        1. Linux platforms:
        2. + +
            + +
          1. The standard source code repository is a directory +called +"hoople" +in the user's home directory.  If you decompress the hoople +library archive in your home directory, you should be all set to +perform a build.
          2. + +
          3. See the HOOPLE website for more details about +downloading that codebase (http://hoople.org).
            +
          4. + +
              + +
            + +
          + +
        3. Win32 platforms:
        4. + +
            + +
          1. The standard repository for source code is a substituted drive l:, which is where all the other hierarchies start.  This +drive can be mapped to any folder desired using the "subst" command +(for example, "subst l: c:\build_dir"). + All +objects and final products will be generated to the l: drive.
          2. + +
          3. Using MS Visual Studio as the Compiler:
          4. + +
              + +
            1. VS80COMNTOOLS/VS90COMNTOOLS/VS100COMNTOOLS variable:
            2. + +
                + +
              1. This variable should be automatically created by +Visual Studio upon installation.  If it isn't, then Microsoft has +a bug or you need to restart your current prompt or your computer.
                +
              2. +
              3. The paths that clam uses to find compiler binaries +is calculated based on this variable.
              4. +
              5. Older versions of visual studio are currently +unsupported because Microsoft constantly rearranges their folders and +tools in a non-maintainable way.
                +
              6. +
              +
            3. Several other environment variables are required +by Visual +Studio.  They can be set up for your current command prompt by +running "vcvars32.bat" or "vsvars32.bat" (found under +the +compiler's common directory, which varies depending on the version of +visual studio).
              +
            4. + +
            + +
          + +
        + +
      +
      + +

      +

      +
      +
      + +

      CLAM Tutorial

      +
      +     This section provides an overview of +how clam +works and how you can make it work for you.  It is quite brief, +but +should suffice for most common cases of makefiles.  For more +detailed +usage, consult the CLAM Reference section of this document. + +

      Caveats

      +
        + +
      • Most of the Unix tools employed in the make process are +case-sensitive.  +This means that they will probably not find any of the clam support if +the files have been changed to upper-case names.  It also means +that +all code files must match their descriptions in makefiles, letter for +letter.  +And any batch files or executables invoked also need to be in +lower-case +as clam expects them to be.
      • + +
      • A corollary case requirement is that the makefile must +be named +either +"makefile" or "Makefile".  These are the Unix standard names and +GNU make +looks for these by default.  If you are willing to type "make -f makefile_name", +then you can run any makefile.  However, the build-ready makefiles +should be named according to the standard, since the build process will +look for these automatically.
      • + +
      +

      +Basics

      +     The C++ Library +Automatic Maker system (or CLAM) is defined as a set of +variable +(or macro) definitions.  These variable definitions are +manipulated in +order to compile and link programs.  By setting the variables' +values +appropriately, specific products can be generated from the target rules +defined +in clam.  Both variables and rules are extensible.  The +general +procedure for building a clam-based Makefile has four user-defined +steps: + +
        + +
      1. loading the default variables for clam,
      2. + +
      3. redefining the default variables where necessary,
      4. + +
      5. loading the default rule set for clam,
      6. + +
      7. defining rules that are local to the user's Makefile.
      8. + +
      +Step 4 can usually be omitted unless the project creates +components +whose types are not supported by clam.
      +     clam is structured as a directory hierarchy +where the root of clam +supports +the most general makefile activities.  Activities such as +recursing +into subdirectories and providing support for cleaning up after a make +are +provided at this level.   In the remainder of the document, we +will +designate this location with a "$" character to clarify what part of +the clam hierarchy we are describing.
      +     The root clam +support files are mostly language independent, since they are used by +all +varieties of language dependent derived versions of clam.  These +files +are generally not of concern unless one is designing a new derived +version +of clam for a language not yet supported.
      +     The subdirectories off of the clam root +provide +"derived" makefile services, such as C++ or Ada compilation.  Each +derived clam service implements at least two files to link into the +rest of the +clam system: a variables file and a rules file.  The variables +file defines +the options for the derived make process; by changing the values of +these, +different types of targets can be created.  The rules file +implements +creation of the targets relevant to the programming language being +supported.
      +     It may be worth noting that clam can be used to +drive +any kind of programmatic process--not just compilation.  Currently +though, +program compilation is the primary goal. +
      +

      Common Files

      +     The top-level file called +"$/variables.def" +contains +definitions and descriptions of the variables used throughout the clam +system.  For a non-derived type of make (using only base clam +support), +this file should be included near the start of the user's +Makefile.  +The rules file (stored in "$/rules.def") should be included after the +user +has modified the appropriate variables that will dictate how the make +is +performed. +
      +     This scheme of including variables at the +top and then rules at the bottom of the user's makefile is employed in +all clam makefiles.  For example, makefiles for C++ compilation +are structured the +same way.  The user's C++ makefile includes the C++ variables +(stored +in a subdirectory called "$/cpp" under the clam root) at the top of the +makefile and then includes the C++ rules at the bottom.
      +     An example +of a C++ makefile is shown below: +
      +
        + include cpp/variables.def
        +
        +PROJECT = basis
        +TYPE = library
        +SOURCE = chaos.cpp checkup.cpp earth_time.cpp guards.cpp istring.cpp \
        +  log_base.cpp mutex.cpp occurrence.cpp outcome.cpp +outcome_table.cpp \
        +  packable.cpp portable.cpp runtime_history.cpp +system_outcomes.cpp \
        +  utility.cpp version_checker.cpp version_record.cpp
        +TARGETS = basis.lib
        +
        +include cpp/rules.def

        +
        +
      +The interior of the makefile overrides the TYPE, SOURCE +and TARGETS variables for C++ compilation to specify what is to be +built +(basis.lib) and what it consists of (the CPP files mentioned in +SOURCE).  + The PROJECT variable being overridden is actually defined in the +$/variables.def; +a project name is a required feature of all clam makefiles. + +

      Important Variables

      +     +The clam root directory is pointed to by an internal variable called +"CLAM_DIR", +defined in $/variables.def.  This variable is used by the clam +system +to find extra files that might be needed by derived makefile +support.  +It is important to change this to the appropriate value when you are +using the system in a different location.  The CLAM_DIR variable +can either +be directly edited in $/variables.def, or it can be overridden in the +environment +of the shell running the make, or it can be passed on the command line +to +make.
      +     For C++ compilation, the above example +makefile +(for basis.lib) contains examples for most of the required +elements.  Additional elements +will be discussed in the examples section or can be found in the +reference.  +The absolutely required variables for C++ are PROJECT, TYPE, SOURCE and +TARGETS. +
      +

           PROJECT is a variable that +provides the +name +of the project being compiled.  This should be a word that can +also +be used as a directory name and partial component of filenames.  +Thus, +spaces and other unusual punctuation characters are discouraged.  +All of the project's temporary directories will be created based on +this +variable.  This project name should be unique across a full build; +otherwise files generated by compiling identical project names will be +jumbled together. +

      +

           TYPE is a variable that describes +the kind +of project that is being compiled.  This is necessary because it +controls +some aspects of the compilation, such as where the compilation products +are +generated.  All files generated by compilation are stored in the +repository +directory (by default, either "~/hoople" in Linux or "l:\" in +win32).  There are three TYPEs supported so far:

      +
        + +
      • library: indicates that the project will primarily be +creating +static +or +dynamic libraries.
      • + +
      • application: indicates that the project will create +executables.
      • + +
      • test: indicates that the project constructs test +programs.
      • + +
      +     Projects of the "library" type will +be given +an include directory named after the project, such as +"~/hoople/include/basis". + The include directory is created as a copy of the headers in the +project's +directory .  Library projects will also have their final products +copied +to the lib or dll subdirectories of the build directory being created.
      +     Projects that are of type "application" will +have their executables +copied to the executable directory in the repository (such as +"~/hoople/exe").
      +     The "test" type of project +will be promoted to a subdirectory named after the PROJECT that resides +under the test hierarchy in the repository (such as +"~/hoople/tests/turbodog"). +
      +

           SOURCE is a list of files that +are to be +compiled +in order to create the final products of the project.  These can +be +C++ source files (*.cpp), MS-Win32 resource files (*.rc) and other +types +of source files.  The list of objects to create will be determined +by transforming the list of SOURCE files (such as by turning a file +called +"fud.cpp" into an object called "fud.obj"). +

      +

           TARGETS is a list of the products +that are +to be created by compilation and linking.  The suffix of a target +is a well established extension, such as ".lib", ".exe" +or ".dll" for MS-Win32 compilation products. +

      +

      Variable Assignment Policies

      +     +The assignment of variable values is mostly straightforward, but it +might +be valuable to provide a refresher.  In GNU make, a variable +(a.k.a. +macro) can be assigned using the following syntax: + +
        + FRED = a b c +
      +This sets the variable named FRED to the value of "a +b c".  The variable is referred to as $(FRED) when it is being +used, +although its name is just FRED.
      +     This syntax is fine when the variable is to be +defined only once.  +In many cases though, a variable is already defined and needs to be +added +to instead of redefined.  Using the standard equals (=) operator +would +wipe out the previous definition, so a special assignment +is provided: +
      +
        + FRED += d e f +
      +This is quite similar to the C syntax on integers.  It +means that +FRED will be given a value equal to its old value plus the new +contents.  +In our example, FRED would be equal to "a b c d e f".  Note that +one cannot say:
      +
      +          FRED = $(FRED) d e f +      (BAD!)
      +
      +This is not allowed in GNU make because it includes a macro's own value +in its definition.  This causes a badly formed recursive +definition +of the variable; a variable dereferencing operation (such +as $(FRED)) causes the variable's current value to +be resolved, which in turn dereferences any other variables in the +definition. + Thus, the reference to $(FRED) causes infinite recursion when +included +in the definition of FRED.
      +
      +     In the case of variables that must +be defined by the user's makefile, the standard assignment operator +(via the = character) can +be used.  This includes the PROJECT, TYPE, SOURCE, and TARGETS +variables.  +Also, any other variables that are set only by the user's makefile can +use simple assignment.  This category includes LOCAL_LIBS_USED, +LIBS_USED and others of similar nature.
      +     But several variables are defined partially +by clam, then added to within the user's makefile, and then possibly +extended +after the user's makefile is processed (by the clam rules file).  +These variables cannot use +standard assignment and must instead use the incremental assignment +(+=) +operator.  Variables included in this category are DEFINITIONS, +LOAD_FLAG_PREFIX, +CLEANUPS, and many others. +
      +     If you are unsure about the type of variable +you are defining, then the incremental assignment (+=) operator is +preferred +to avoid trashing the variable's previous values.
      +     +Note that when variables are "exported", then any make in a subshell +will +inherit the parent shell's value.  This can induce some weird +behavior +for variables that are incrementally constructed with the += +operator.  +If this seems to be happening, try using the simple assignment operator +for +that variable in the sub-makefile, if this is allowed.  In general +though, +variables are not exported unless they MUST be seen by shell scripts +and +this does not occur overly frequently. +
      +

      Optional Variables

      +     There are several miscellaneous +variables that +are useful, either within one's makefiles or when passed to GNU make on +the command +line.  These are described below. + +

           LOCAL_LIBS_USED is a list of +library names +that are to be linked in with the library or executable being +created.  +These are specially formatted names; they are just the prefix part of +the +full library name.  For example, if you're building a release +executable +and want to link in a data structures library "i_adt.lib" (win32) or +"libi_adt.a" (Linux), you can specify: +
      +        LOCAL_LIBS_USED = i_adt +
      +The appropriate prefix and suffix will be attached. +

      +

           EXTRA_COPIES is a list of files +that should be copied to a project's output folder when it is done +being compiled.  These should be files that are not already +copied as the main products, such as extra data or configuration files +that belong with an application. +

      +

           EXTRA_VERSIONS is a list of +version files +that +also need to be updated to the main build version during a +compilation.  +These are usually needed if a project compiles several executable +files, +and each one performs version checking.  (By default, any project +containing a file called "version.ini" will get a version stamp from +the +main build version.) +

      +

      Writing Your Own Rules

      +     One might need to write new rules +for +processing +file types that are not directly supported by clam.  There are a +number +of features provided for writing rules, but there are also some +requirements +placed on the rules. +
      +     All rules in makefiles need to be prefaced +with one of the provided "launcher" macros.  These are used to +ensure +that the rules can be properly executed on different platforms; +Windoze95 +was especially hard to implement for until these macros were developed +(due to what appear to be basic defects in the command line +support).  +All preaching aside, here are the macros: +
      +
        + +
      • HIDER: Executes a command but hides the +invocation.  Any +output is +still sent to standard out.  If a verbose +build is being done, then all of the invocations become visible again.
      • + +
      • HIDESH: Executes a shell script but hides the +invocation. + Similar to HIDER but supports scripts specifically.
        +
      • + +
      +Here are some examples of using the macros properly.  +Note that +the +command itself must be contained in single quotes:
      +
      +$(HIDER) $(MIDL) crumpet_server.idl
      +
      +
      MIDL is also a provided macro; it executes the +Microsoft +IDL compiler.
      +$(HIDESH) $(CLAM_DIR)/postconditions.sh
      +
      +
      This runs a shell script that handles the end +portion of a +make.
      +

      +Conclusion

      +     This tutorial is intended to raise +awareness +of +basic  usage.  Hopefully the reader will now be able to +create +simple makefiles that use .  For more aggressive compilation +requirements, +the reference section may be needed; it describes every variable and +rule +used in the  system.  However, it is most likely the case +that +your unsupported compilation needs will also be required by others in +the +future, and it is hoped that you will contribute them to the +main-line  support.  Currently, the appropriate way to do +this is just +to +send the makefile code to the library +administrator, who will include them +in the next version of . + +
      +

      +

      +
      +
      + +

      CLAM Reference

      +
      +

      +Language Independent Variables

      +     The language independent variables +are stored +in the file "$/variables.def".  They define the overall structure +of a make and can usually be overridden to customize how the make is +performed. + +

      BUILD_BEFORE

      +     This is a list of projects that need +to be +created +before this project can be created.  The items in the list are +interpreted +as directories that contain a makefile to be run.  For example, if +an item in BUILD_BEFORE is listed as ‘fred’, then the target +"fred.make" +will be executed.  That target changes to the directory 'fred' +before +running the makefile there.  The project in the specified +directory +is created using make if needed (as determined by that directory's +Makefile).  +The projects in BUILD_BEFORE are made immediately after the +FIRST_TARGETS +are made. + +

      ACTUAL_TARGETS, ACTUAL_FIRST_TARGETS, ACTUAL_LAST_TARGETS

      +     See below for TARGETS, FIRST_TARGETS and +LAST_TARGETS.
      +
      +

      BUILD_AFTER

      +     A list of directory names that +should be +recursed +into after this project finishes.  Each listed directory will have +make +started on any makefile found.
      +
      +

      BUILD_BEFORE
      +

      +     A list of directory names that +should be +recursed into before this project +starts.  Each directory listed will have make started on any +makefile found.
      +
      +

      MAKEFILE_NAME

      +     A variable that specifies the name +of the +makefile +for all sub-makes.  It works with BUILD_BEFORE and +BUILD_AFTER and allows the name of the makefile in a +subdirectory to be changed to something other than 'makefile'.  +This +supports different types of builds which are controlled by different +makefile +names. + +

      PARAMETER_FILE

      +     A file name that is +usually found at the root of the repository.  The name is +often "build.ini", but any name can be used as the parameter file. + This file is an extension of the variable set included in +$/variables.def +and can be used to provide compilation paramters without resorting to +the +command line.  This file is associated with a particular build +rather +than the  support, so different releases will have different build +parameter +files.  On systems supporting version information, the build's +version +number is stored here also.
      +
      +

      CATCHER

      +     A sub-program launcher like HIDESH +but this will trap errors it sees and play the build error +CLAM_ERROR_SOUND.
      +
      +

      CLAM_BIN

      +     This is a folder where the helper +binaries for  are located. The default for this is usually +CLAM_DIR/../bin.
      +
      +

      CLAM_DIR

      +     This variable points at the location +where the  definitions and helper scripts are located.  The +default is +"~/yeti/clam", +but this can be overridden for local installations of .
      +
      +

      CLAM_ERROR_SOUND

      +     This is a list of sound files +that should be played when a make stops with an error.  It serves +as +an audible warning that something bad happened.
      +
      +

      CLAM_FINISH_SOUND

      +     This is a list of sound files +that should be played when the make has concluded +successfully.  It should play when the outer-most make +has seen all targets created as intended.
      +
      +

      CLAM_TMP

      +     Specifies the location for temporary +files generated during a make.  The default value usually works +fine. + This directory will be created if it does not already exist.
      +
      +

      CLEANUPS

      +     This is a list of files to be +removed by the +make +clean command.  They are possibly acquired from the TARGETS +defined +in the user's Makefile, or by language dependent rules for +cleaning.  +Additional files can be added to this list by the user's makefile also. + +

      DIRTY_FILE

      +     This variable points at a file that +signifies +that some targets have been remade.  It is not used at the base +level +of clam, but language-specific versions might do something special if +targets +were remade (such as put them in a build repository). + +

      FAILURE_FILE

      +     This file is used as a flag that +indicates +when +a make has failed.  The particular file used depends on the +project +name for this makefile.  It is cleared at both the beginning and +end +of a make. + +

      FIRST_TARGETS

      +     The FIRST_TARGETS are made before +any +libraries +are created and before any executables are compiled.  There must +be +a rule for making every entry in this list, either through implicit +rules +or explicit ones provided by the user's makefile. + +

      FLAG_FILES

      +     This is a list of all the files +that are used for compilation flags.  They are whacked at the +beginning +and end of a make.
      +
      +

      HIDER

      +     This macro is used throughout  +to hide the +commands that are being sent to the operating system.  It can be +disabled to allow a verbose make (see the NOISY macro). + +

      HIDESH

      +     Just like HIDER, but this macro is +specifically +for launching shell scripts.  Some versions of GNU make (like +Cygwin's) +have problems running scripts which don't arise when running executable +files. + Those problems led to the creation of the HIDESH macro for those +specific +cases.  This is not an issue for Unix systems. + +

       LAST_TARGETS

      +     The LAST_TARGETS are made after all +of the +other +standard targets are made.  Their must be a rule for making every +entry in this list, either through implicit rules or explicit ones +provided +by the user's makefile. + +

      NOISY

      +     This variable can be used to cause a +verbose +make.  +If the variable is non-empty, then all commands will be echoed to +standard +output.  Otherwise, the default is to hide the commands that are +issued +and just show the output of running those commands. + +

      OP_SYSTEM

      +     This is a flag that defines the +operating +system +name.  This flag is sometimes used to choose the appropriate tools +per platform or to conditionally compile code for system +dependent interfaces.  The available possibilities so far are +UNIX, +OS2, SYSV (System V Unix), DOS, and WIN32.  Only UNIX and WIN32 +are +currently very functional. + +

       OTHER_CLEANS

      +     These are targets to execute before +performing +the main clean up during "make clean".  These might be targets +that +contain shell commands to execute as part of clean up or they could +contain +the "clean_subdirs" command (defined below). + +

      PROJECT

      +     This is a variable that provides the +name of +the +project being compiled.  This should be a word that can also be +used +as a directory name and as a partial component of filenames.  +Thus, spaces +and other unusual punctuation characters are discouraged.  All of +the project's temporary directories will be created based on this +variable. + +

      REPOSITORY_DIR

      +     Specifies the root directory +for compilation or other  building activities.   The +repository +is also where source code and final products of compilation reside, +unless +the default is over-ridden (see TARGETS_DIR).
      +
      +

      SH & SHELL

      +     These variables both point at a +shell program +that is +used for starting commands.  SHELL is defined by GNU make, whereas +SH is defined by . + +

      SUB_FLAG_FILES

      +     This is a list of the compilation +flag files +which +should be destroyed only at the end of a make.  They are used for +communication +with submakefiles--makefiles that were invoked by "this" makefile.
      +
      +

      SUBMAKE_FLAG

      +     This points to a file whose presence +indicates +that +a "submake" performed some actions.  The flag can be interpreted +by +some language-specific versions of  as a reason to set a flag +using +the +DIRTY_FILE.
      +
      +

      TARGETS

      +     These are the products to be created +by .  +Each item listed in TARGETS should have a rule that knows how to create +that type of file.  The language independent system provides very +few suffix based rules.  TARGETS is filled in by the user in +their  file, but it is not used directly by the  +system.  +Instead, +a generated variable called ACTUAL_TARGETS is used.
      +
      +

      TARGETS_DIR

      +     This folder is where all generated +files are +to +be stored.  It is usually identical to REPOSITORY_DIR but can be +overridden +when the targets should be stored elsewhere.
      +
      +

      Version components: major, minor, revision, build
      +

      +     These four variables specify the +version of +this +particular build.  They are usually stored in the +PARAMETER_FILE. + The major and minor versions are the traditional 2.3, 4.0, etc +style +of release numbers.  The revision number is often used to sequence +the +builds of that particular release, such that build 3.5.127 is the 127th +build +of the 3.5 release.
      +     A version-tagged file (such as an executable or +dynamic +library) with any one of the major, minor or revision numbers differing +from +an installed build is incompatible with the installed build.  An +executable +file or dynamic library will not be allowed to load other dynamic +libraries +where these numbers differ.
      +     The last version component is misleadingly called +"build"; +this number specifies the service pack level for a file.  Files +whose +versions only differ in the last "build" component are intended to be +compatible +with each other.  The understanding is that if only that number +differs, +then the external interface to the file has not changed, although the +interior +implementation may have.
      +
      +

      Language Independent +Rules

      +     The file "$/rules.def" uses the +composite +macros +defined in "$/variables.def" together with a set of make rules to +perform +actions during compilation.  The rules file should be included in +the user's Makefile after the compilation variables have been +initialized +for the project being compiled.  The user's own targets should be +placed after the directive that includes "$/rules.def". + +

      %.halt

      +     These targets cause  to exit, +usually to +avoid +something that it considers catastrophic.  An example of this +would +be when  finds an inappropriate entry in the list of objects to +create; +allowing a "make clean" on this makefile will delete files that are +probably +not intended.  Hence, when  finds this kind of usage, it will +stop the make and issue a complaint. + +

      %.make

      +     Used to compile a makefile in a +subdirectory +named +"%".  This rule is employed by the BUILD_BEFORE macro, but can be +used in the user's makefile targets also. + +

      Language Independent +Targets

      +    The following targets are defined by +"$/rules.def". + +

      all

      +     This is a standard target that is +executed +when +no particular target is specified at the make command line.  It is +an umbrella target that invokes all of the other targets required to +perform +a make.  The order in which the major targets are created is: + +
        + +
      1. FIRST_TARGETS
      2. + +
      3. TARGETS
      4. + +
      5. LAST_TARGETS
      6. + +
      +

      +clean

      +     This causes all of the files in +CLEANUPS to be +removed and also executes all of the targets in OTHER_CLEANS.  The +language dependent system can override some of this behavior or it can +just add more files to the list of CLEANUPS. + +

      clean_subdirs

      +     This is similar to "make_subdirs" in +that it +descends +into the subdirectories in no particular order, but it runs "make +clean" +in each of them.  This allows a directory hierarchy of projects to +be cleaned with one command. + +

      finish

      +     The "finish" target represents the +completion +of a make, whether successful or not.  It reports the time and +date +(and logs them). + +

      rm_links

      +     This target causes all link files in +the +current +directory to be deleted.  This is only applicable on a Unix +operating +system. + +

      make_subdirs

      +     This target allows a makefile to +specify that +all of the subdirectories under the current directory should be scanned +for makefiles and that those makefiles should be executed.  If a +makefile +does not exist, it is skipped.  Note that the subdirectories are +descended +into in no particular order; the order depends on how the operating +system +decides to list the directories.  If the order of make is +important, +use BUILD_BEFORE instead. + +

      start

      +     The "start" target represents the +beginning of +the make.  It reports the time and date (and logs them). + +

      Language Independent +Files

      +

      $(PARAMETER_FILE)

      +     This is a special +file that has at least two purposes in .  It is the source of +the +version number that will be stamped on all the appropriate DLLs and +EXEs +created during a build.  It is also a place where build-wide +compilation +directives can be included so that they do not have to be passed on the +command +line.  For C++ compilation, this is usually an INI file +stored in the +REPOSITORY_DIR under the build folder.  +Here is a sample parameter file: + +
      #\
      + [version]
      + major=14
      + minor=3
      + revision=140
      + build=0
      +

      DEBUG=t
      + OPTIMIZE=t
      +

      +
      +Note the bizarre comment at the top of the makefile; this is +used to +hide +the "[version]" section marker.  The comment is required because +the +build parameter file is pulled directly into the makefile code to set +the +variables after the version stamp.  Without a comment in front of +the section, a syntax error would result.  The "[version]" section +marker is required because this file is also sometimes treated as a +win32 INI file +in order to read the version stamp.
      +     The build version is stored in the first four +entries.  Our interpretation of the stamp is standard for "major" +and "minor".  We treat the "revision" as a build revision number; +within a release, there will be numerous revisions--one for each new +build +that is performed.  We then treat the "build" entry as a patch +level +within that particular build.  When we perform our version +checking, +only the first three entries are compared; the patch level in "build" +is +considered irrelevant. +
      +     This example also specifies that the build +should be a debug style (rather than release) build and that it should +be optimizer.  We can also see that +the flags for bounds checker instrumentation and true time +analysis support are commented out.
      +
      +

      badness_catcher.sh

      +     Runs the command line passed +in as a sub-shell and looks for error conditions.  If an error +occurred, +the build is stopped and the CLAM_ERROR_SOUND is played.
      +
      +

      datestamp.sh

      +     Echoes the time and date.  This +is a +separate +file to make the cross-platform difference less annoying.
      +
      +

      exit_make.sh

      +     Causes the make to stop dead in its +tracks. + +

      postconditions.sh

      +     Invoked at the end of the +language-invariant +portion of a make.
      +
      +

      preconditions.sh

      +     Invoked at the beginning of the +language-invariant portion of a make.
      +
      + +

      starter.sh

      +     This shell script executes a command +that is +passed +to it as its parameters and logs error conditions to standard +output.  +It's used by the CATCHER macro. + + +

      +

      +

      +C++ Specific Variables

      +     These variables are used throughout +the C++ +compilation +support.  They are defined in "$/cpp/variables.def". + + +

      BASE_CPU

      +     Allows specification of the +processor that the +build is targeted for.  This is needed when special actions must +be +taken for different processor types.  Valid values currently +include +m68k (for Motorola 68000 series), m68340 (specifically the 68340), +x86 (intel 386 and upwards), and ppc860 (the PowerPC 860). + +

      BUILD_LIST_FILE

      +     The list of files that must +be rebuilt.  This is only used with compilers that support +compilation +of multiple source files with one invocation of the compiler (currently +only +MS-Visual C++).
      +
      +

      BUILD_WHACK_FILE

      +     A list of object files that must be +destroyed +if +the make fails.  This is only relevant in the same situations as +BUILD_LIST_FILE.
      +
      +

      COMPILER

      +     This variable chooses the specific +flags +needed +for the compiler.  Not all operating system choices above are +suitable +with the COMPILER choices, but generally it is fairly obvious which are +supported.  The current possibilities include BORLAND_DOS, +BORLAND_OS2, +UNIX (default cc), GNU_OS2, GNU_LINUX, OBJECT_CENTER (Saber compiler), +SUN_UNIX, +VISUAL_CPP, and DIAB3. + +

      COMPILER_FLAGS

      +     This is the list of flags passed to +the +preprocessor +and compiler. It is composed of the SYSTEM, the DEFINITIONS, the +SEARCH_DIRS, +and any user-included options. If flags that don't fit one of the +categories +are needed, they can be added here. + +

      CONSOLE_MODE

      +     This causes the program +to be generated as a console application.  This is relevant in +systems +(such as win32) where programs have a split personality depending on +whether +they are to have graphical user interfaces or just console interfaces. + +

      DEBUG_FLAGS

      +     These are flags used for generating +specialized +versions of object files, such as ones that include debugging code +(e.g., +for gdb) or ones that add code for profiling (e.g., gprof). Possible +values +in the Sun CenterLine Compiler environment are -g for debugging code +and +-pg for profiling. + +

      DEFINITIONS

      +     This is a list of compiler flags +that define +the +value of C or C++ macros. These usually have the format of +‘-D<flag>’, +but in this particular variable only the <flag> itself should be +listed +(because the compiler option characters ‘-D’ are added automatically). + +

      DEPENDENCY_ADDITIONS

      +     This is a list of extra flags that +gets passed +to the auto-dependency tool.  The list can vary for each compiler. + +

      DEPS_FILE

      +     This file is where the +auto-dependency +information +is stored.  The "makedep" program is used to generate +auto-dependency +information for the files listed in SOURCE.  During a build, the +DEPS_FILE +is pulled into the actual code of the makefile; this causes the +dependencies +to be automatically included so that they can dictate the files that +need +to be rebuilt. + + +

      EXTRA_VERSIONS

      +     This is a list of version files that +also need +to be updated to the main build version during a compilation.  +These +are usually needed if a project compiles several executable files, and +each one performs version checking.  By default, any project +containing +a file called "version.ini" will get a version stamp from the main +build +version. + +

      LIBRARIAN_FLAGS

      +     This is a list of flags that are +passed to the +library creation tool.  Sometimes this must be overridden for a +particular +compiler. + +

      LIBS_USED

      +     These are code libraries that the +executables +depend upon.  They are searched for in any of the directories +listed +in the LIBRARY_SEARCH_PATH. + +

      LOAD_FLAG_PREFIX & LOAD_FLAG_SUFFIX

      +     These tell the linker and loader how +to deal +with +the files and where to locate library components. The prefix is listed +on the compilation command line before the object files are listed, and +the suffix after. The prefix should contain information such as the +directories +to be searched for code libraries (although they should be added to +LIBRARY_SEARCH_PATH).  +In the suffix definition, actual library loading statements (like +-lmath) +can be included (although they should be listed in a different form in +LIBS_USED or LOCAL_LIBS_USED). + +

      LOCAL_LIBS_USED

      +     The names in this list actually +cause the +OBJECTS +to be recompiled when the libraries listed have changed.  To +accomplish +this, these libraries MUST be located in the STATIC_LIBRARY_DIR rather +than +at some arbitrary place on the LIBRARY_SEARCH_PATH.  These +libraries +also must follow the special naming convention followed by ; if +"basis" +is an entry in this list, then a library called "basis.lib" will be +sought +during the build. + +

      NO_COMPILE

      +     Specifies that no compilation +should be performed.  Nothing in the SOURCE or TARGETS macros will +be +built.
      +
      +

      NO_DEPS

      +     This is an exclusion flag.  If +it is +defined, +then no auto-dependency files will be generated.  This is useful +if +you're missing the makedep tool and trying to compile it.
      +
      +

      OBJECTS

      +     The OBJECTS are all those files that +need to +be +created during compilation.  Usually this list is filled based on +the files in SOURCE. + +

      OPTIMIZE

      +     Causes the make to create optimized +code.  +The default optimization is for speed. + +

      REBUILD

      +     If the REBUILD variable is +non-empty, then all +files listed in the SOURCE variable are touched.  This should +cause +all of those files to be rebuilt during the compilation.  +Occasionally +GNU make will complain that a file is newer than the current time, but +this does not usually cause any problems. + +

      SOURCE

      +     The SOURCE variable is a list of +files that +are +to be compiled in order to create the final products of the +project.  +These can be C++ source files (*.cpp), Win32 resource files (*.rc) +and +other types of source files.  The list of objects to create will +be +determined by transforming the list of SOURCE files (such as by turning +a file called "fud.cpp" into an object called "fud.obj").  More +file +types will be added as they are needed. + +

      STATIC

      +     Causes the make to create statically +linked +targets.  +Executables or dynamic libraries will not link in any compiler supplied +dynamic libraries, nor will they require them during run-time. + +

      TYPE

      +     This is a variable that describes +the kind of +project that is being compiled.  Knowing the type of project is +necessary +because it controls some elements of the compilation and also of the +final +promotion of the compiled products.  There are three TYPEs +supported +so far: + +
        + +
      • library: indicates that the project will be primarily +creating +static +or +dynamic libraries.
      • + +
      • application: indicates that the project will create +executables.
      • + +
      • test: indicates that the project constructs test +programs.
      • + +
      +Projects of the "library" type will follow the special  +rules for +their include directory (which is created as a copy of headers in the +library +directory).  Library projects will also have their final products +copied to the lib or dll subdirectories of the build directory being +created.  +Projects that are "application"s will have their executables copied to +the executable directory in the build.  And "test" projects will +be +promoted to a subdirectory named after the PROJECT that resides under +the +test hierarchy in the build. + +

      C++ Directory Structure +Variables

      +

      BASE_OUTPUT_PATH

      +     This is the parent directory +for object files generated for the specified type of CPU and the style +of +build (e.g. debug or release builds).
      +
      +

      CPU_BUILD_DIR

      +     This variable can be used to +distinguish +directory +names used for output.  It includes the cpu name and the type of +build. + +

      DYNAMIC_LIBRARY_DIR

      +     The directory where dynamic +libraries will be +stored after creation.
      +
      +

      EXECUTABLE_DIR

      +     The directory where executable files +will be +stored after creation.
      +
      +

      FINAL_DIR

      +     This is the name of the directory +where the +finished +compilation products are stored, currently only import libraries for +dynamic libraries.  +It is usually a directory under the OUTPUT_PATH named "final". + +

      HEADER_SEARCH_PATH

      +     This is a list of directories that +will be +searched +for C++ header files (files ending in ‘.h’). + +

      HOOPLE_HEADERS

      +     The two standard places to look for +headers +(the repository and the third party directory) are listed in this +variable. + +

      HOOPLE_LIBRARIES

      +     This is where our libraries are +located.  It is usually a subdirectory called "lib" under the +repository +directory. + +

      LIBRARY_SEARCH_PATH

      +     This is a list of directories that +will be +searched +for C++ library archives (files ending in ".a" or ".lib"). + +

      LOCAL_HEADERS

      +     This variable provides a way to +include +headers +prior to the default locations in the search path.  For example, +if +you are compiling locally and have some headers that are not present in +the build you are using, then you can specify where they are in this +variable. + +

      LOCAL_LIBRARIES

      +     This variable allows other library +directories +to be added prior to the default search locations.  This enables +substitute +static or import libraries to be used instead of the standard ones +present +in the build. + +

      STATIC_LIBRARY_DIR

      +     This is the location where code +libraries are +to be copied during promotion and where they are to be searched for +when +listed in LOCAL_LIBS_USED.  Under Unix, these libraries have a +‘.a’ +suffix and are created with the "ar" program.  Under Win32, +these +libraries have a ‘.lib’ suffix and are created with "link". + +

      OBJECT_DIR

      +     This is where object files will be +stored +during +compilation for the target type being produced. + +

      OUTPUT_DIRECTORY_LIST

      +     This is a list of directories that +need to be +created under the OUTPUT_PATH.  It contains the "final" directory +where all finished products are stored, as well as all the intermediate +directories for objects. + +

      OUTPUT_PATH

      +     This is the temporary file storage +area.  +Any files that are created during the compilation process will be +stored +under here in a subdirectory named after the PROJECT. + +

      OUTPUT_ROOT

      +     This specifies the root portion of +the +OUTPUT_PATH.  +It lets a PC build use drive letters for the root, while a Unix build +can +specify a directory hierarchy. + +

      SEARCH_DIRS

      +     This is a list of directories that +will be +searched +for both C++ header files and for C++ code libraries.  The items +placed +on SEARCH_DIRS will be added to both the LIBRARY_SEARCH_PATH and the +HEADER_SEARCH_PATH.  +The reasoning behind this variable is lost in antiquity. + +

      TESTS_DIR
      +

      +     The directory where test programs +will be +stored after creation.
      +
      +

      THIRD_PARTY_DIR

      +     Third party components are sometimes +used in +the +creation of products.  The directory is expected to have a +structure +containing "include" and "lib" subdirectories where headers and +libraries +are stored. + +

      Compiler Dependent Flags

      +

      +CC

      +     This is the name of the C++ compiler +executable. + +

      COMPILER_HEADER_DIR

      +     This is where the compiler's header +(or +include) +root directory is located.  It is usually based on the root +directory. + +

      COMPILER_LIBRARY_DIR

      +     This is where the code libraries for +the +compiler +are located.  It is usually based on the root directory. + +

      COMPILER_ROOT_DIR

      +     This should automatically be set to +the +appropriate +local directory where the C++ compiler is located. + +

      CREATE_LIBRARY_FLAG

      +     This flag, if required, specifies +the text +that +must precede the name of a library to create.  It is passed to the +library creation tool. + +

      DEF_FILE

      +     This flag only applies to Win32 +programs.  +It specifies the name of a DEF file for all of the products created in +the project. + +

      LIB_PREFIX & LIB_SUFFIX

      +     The portions of a library's name +dictated by the operating system.  For example, on Unix the prefix +is "lib" +and the suffix is ".a", leading to library names like "libbasis.a" for +the +basis library.  On win32, the prefix is "" and the suffix is +".lib", leading +to library names like "basis.lib".
      +
      +

      LIBRARY_NAME_FLAG

      +     This flag contains the text that +specifies a +library +that will be included in a link.  It is often "-l". + +

      LIBRARY_PATH_FLAG

      +     This flag provides the text needed +to add +another +library search path.  Multiple occurrences of this flag followed +by +a directory name are allowed by most compilers. + +

      LIBRARY_TOOL

      +     This is the name of the program +responsible +for +creating libraries. + +

      LINK_TOOL

      +     This is the name of the program that +links.  +This is sometimes the same as the compiler (CC) and sometimes the same +as the librarian (LIBRARY_TOOL). + +

      LINKER_OPTION_SEPARATOR

      +     In some compilers, linker options +need to be +separated +from compiler options that occur on the same command line.  This +flag +serves that purpose. + +

      LINKER_OUTPUT_FLAG

      +     This flag is sometimes required by a +linker +for +specifying the name of the library or executable that it is creating. + +

      OBJECT_NAME_FLAG

      +     This flag is used to specify the +name of an +object +file being created.  It is passed to the compiler to override +whatever +default name would be used. + +

      Microsoft-Visual C++ Only
      +

      +

      USE_MFC

      +     This flag only applies to Visual C++ +and +indicates +that MFC is to be used in creating this project.  This is usually +the case for GUI applications. + +

      VC_ROOT

      +     This is an override that allows the +compiler +root +directory to be customized without changing the  code.  If +VC_ROOT +is set (either in a makefile or as an external variable), then it will +be used in place of the COMPILER_ROOT_DIR.  The best way to use +this +override is as an external environment variable; this allows makefiles +to remain the same despite your local configuration of the compiler. +
      +     Note that this variable should use +forward-slashes, +where DOS/Win32 would use backslashes.  Also, if you have +installed +Visual C++ in a directory path containing space characters, then please +use the 8.3 notation for the directories containing the spaces; this +allows +the name to be passed around successfully.  For example... +
      +
      + + + + + + + + + + + + + + + + + + + + +
      +
      If Visual C++ Is Installed In
      +
      +
      Then VC_ROOT Should Be
      +
      +
      c:\devstudio\vc
      +
      +
      c:/devstudio/vc
      +
      +
      c:\program files\devstudio\vc
      +
      +
      c:/progra~1/devstudio/vc
      +
      +
      +

      VCS_ROOT

      +     Similarly to the VC_ROOT, this +variable points +at the root of the C# support for Visual Studio.Net.
      +
      +

      FRAMEWORK_DIR

      +     This variable specifies the location +of the +.Net framework directory.  On MS-Windows XP, the default should be +fine.  For MS-Windows 2000 or other Win32 OSes, the windows +directory +should be "winnt" instead.  If the operating system is configured +in a non-default way, the framework directory can be specified in an +environment variable.
      +
      +

      VCPP_USE_BASE

      +     Specifies that standard Win32 +libraries should +be linked in.
      +
      +

      VCPP_USE_GUI

      +     Specifies that the MFC libraries +should be +linked in. + +

      VCPP_USE_OLE

      +     Specifies that the COM / OLE +libraries should +be linked in. + +

      VCPP_USE_RPC

      +     Specifies that the MS-RPC libraries +should be +linked in.
      +
      +

      VCPP_USE_SOCK

      +     Specifies that the MS-WinSock +libraries should +be linked in.
      +
      +

      C++ Specific Rules

      +     These types of targets have one +thing in +common; +if any of the items that a target depends on in SOURCE or +LOCAL_LIBS_USED +or included files or whatever have changed since the last time the +target +was created, then it is recompiled. + +

      %.bad

      +     Causes the make to die.  This +is added when an incorrect file type is spotted in a list of targets.
      +
      +

      %.dll

      +     These create dynamically linked +libraries from +the SOURCE. + +

      %.elf

      +     Creates elf-formatted binaries for +use with a +firmware build (a specialized RTOS is the only one currently supported).
      +
      +

      %.exe

      +     This creates an executable program +using all +of +the objects and libraries specified.  It is therefore important in +a  makefile to only have executables that depend on the same group +of object files.  The hidden agenda in the "exe" type of target is +that a file ending in ".cpp" must exist; this is taken as the root of +the +executable.  It should usually contain the main() function (or its +equivalent). + +

      %.lib

      +     This creates static libraries from +the files +listed +in OBJECTS. + +

      %.nil

      +     A blank target for test compiles.
      +
      +

      %.obj

      +     These create object files from C++ +source +files +(files ending in .c or .cpp). + +

      %.res

      +     These create compiled resource files +from RC +files +in the SOURCE list. + +

      C++ Specific Targets

      +

      +check_requirements

      +     This target ensures that certain +characteristics +of the makefile are present.  It complains and aborts the make if +they are missing. + +

      post_compilation

      +     This target finalizes the +compilation by +running +the postconditions script.  If PROMOTE is true, then the final +products +are copied into the repository. + +

      pre_compilation

      +     This target executes the +preconditions script +to set up the compilation's output directories. + +

      rebuild

      +     This target performs the actions of +rebuilding.  +This mainly involves touching all of the files in SOURCE before the +compilation +has really started. + +

      C++ Specific Files

      + +

      postconditions.sh

      +     After a compilation has succeeded, +the +postconditions +script performs the final actions required.  The nature of these +actions +depends on the type of project being made.  For a library project, +the script copies the headers to the project's include directory and +copies +libraries to the appropriate locations.  For application and test +program targets, the script copies the final products to the +appropriate +repository directory. + +

      preconditions.sh

      +     Before any targets are compiled, the +preconditions +script ensures that the appropriate output directories exist for the +project.  +The script also calls the version utilities to update the project's +version +file and to create any required resource files.
      +
      +

      rebuild_oldies.sh

      +     Used for compilers that support +multiple code +files +in one invocation.  This is launched to compile a batch of sources +and +catch any errors.
      +
      +
      +
      +

      CLAM Example Makefiles

      +
      +     These examples show some common +patterns for +how  is used.  The makefiles below are actually used in real +software +projects. + +

      Library-Only Makefile

      +This example creates a dynamic library. + +
        + include cpp/variables.def + +

        PROJECT = mechanisms
        + TYPE = library
        + SOURCE = delayer.cpp eventmgr.cpp event_po.cpp heartbea.cpp +instance.cpp +\
        +   libmain.cpp monitor.cpp semaphor.cpp state_ma.cpp +timer.cpp +time_sta.cpp
        + TARGETS = mechanisms.dll
        + LOCAL_LIBS_USED = basis
        + DEFINITIONS += BUILD_MECHANISMS USE_HOOPLE_DLLS

        + +

        include cpp/rules.def

        + +
      +The dynamic library created here is mechanisms.dll.  The +basis +library is linked +in also.  The file "roller.cpp" will also be copied to the build +directory's +include path, presumably since it is a template code file. + +

      Library Plus Executable Makefile

      +This example shows the basis makefile with a couple of test +programs +also +being generated. + +
        + include cpp/variables.def + +

        PROJECT = basis
        + TYPE = library
        + SOURCE = chaos.cpp checkup.cpp guards.cpp \
        +   istring.cpp itime.cpp logger.cpp matrix.cpp +portable.cpp \
        +   realtime.cpp textdump.cpp timezone.cpp utility.cpp \
        +   version_checker.cpp version_record.cpp
        + TARGETS = basis.lib t_string.exe t_alloc.exe

        + +

        include cpp/rules.def

        + +
      +Note that the executables +"t_string.exe" and "t_alloc.exe" require files called "t_string.cpp" +and +"t_alloc.cpp" to exist.  These files are expected to contain the +"main()" +or "WinMain()" functions (or the MFC application object).  All of +the +files in the SOURCE variable will be included in each final executable. + +

      Executable-Only Makefile

      +This example is produces several test programs that exercise +the +associated +library. + +
        + include cpp/variables.def + +

        PROJECT = t_basis
        + TYPE = test
        + SOURCE = instance.cpp t_basis.rc
        + TARGETS = t_alloc.exe t_chaos.exe t_checku.exe t_dattim.exe \ +
        +   t_matrix.exe t_sequen.exe t_sorts.exe t_string.exe \
        +   t_texdmp.exe
        + LOCAL_LIBS_USED = basis

        + +

        include cpp/rules.def

        + +
      +The programs "t_alloc.exe" and so on will require C++ files +with the +same +prefix (t_alloc.cpp) to contain the main program (as in the previous +example).  +The items in the SOURCE list will be included in each executable, and +the +basis library will be linked in. + +

      CLAM Hints

      +     This section +is devoted to untangling snags that have been encountered in the +past.  +Hopefully problems you encounter will be discussed here.  Please +contribute +any new problems found to the library +administrator. + +

      Problem:

      +     A message like: + +
        + +
          + make: *** No rule to make target +`o:/x86_w32_rel/project/final/myproj.dll', +needed by `all'.  Stop. +
        + +
      +is displayed during a make. + +

      Solution:

      +     The most frequent reason for +receiving a +message +similar to the above is that there is a file listed in SOURCE that +either +does not exist or that is capitalized differently from how it is +listed.  +Check that all the files in SOURCE are in the makefile's directory and +that the exact spelling of those files (including their case) is +correct. +
      +     Another potential cause of this problem is +if a file is included in the SOURCE that  does not +recognize.  +The standard compilable files are supported (*.cpp, *.c, *.rc), but it +is possible that a makefile must handle a non-standard extension (such +as *.idl).  Either the user's makefile must supply a rule for +processing +this type of file or the user must negotiate with the  +administrator +to get that type of target added to the  support. +
      +

      Problem:

      +      Clam is complaining about programs +not being +found +during a build. + +

      Solution:

      +     The most frequent cause of this +problem is a +directory +not being on your path.  The compilation tools bin (~/hoople/bin) +directory must be in +the PATH variable. +
      +     Problems are occasionally seen when the PATH +contains directory names that have spaces in them.  Try using the +shorter 8.3 form of the directory name. +
      +     An even more obscure situation sometimes +occurs: paths with networked drives seem to somehow hide paths with +local drives that +are listed later in the PATH variable.  The cause of this is +unknown, +although it was thought to be caused by NetWare at one point.  To +fix +the situation, move the local paths before the networked ones.
      +
      +
      +
      +
      +

      Acknowledgements

      +
      +
      Thanks to April Bly Monnen for the wonderful cover +art. + +

      Thanks to Kevin Wika for some early help with makefiles. +

      + + +
      +
      +
      +
      + + diff --git a/docs/clam_manual/clam_root.html b/docs/clam_manual/clam_root.html new file mode 100644 index 00000000..89b9889b --- /dev/null +++ b/docs/clam_manual/clam_root.html @@ -0,0 +1,38 @@ + + + + + + + CLAM Home Page + + + +
      +

      clam automagic maker

      +
      +
      +

      +Tutorial and Reference Manual +
      +
      +GNU Make +Manual +
      +
      +CLAM Source +
      +
      +CLAM C++ Support Source +
      +
      +CLAM C# Support Source +
      +
      You can download CLAM from the hoople +site.
      +Please send any comments and contributions to the +Administrator +.
      +
      + + \ No newline at end of file diff --git a/docs/clam_manual/partial_cygwin_for_build.txt b/docs/clam_manual/partial_cygwin_for_build.txt new file mode 100644 index 00000000..0f33a59e --- /dev/null +++ b/docs/clam_manual/partial_cygwin_for_build.txt @@ -0,0 +1,32 @@ + +install these groups: +base - default should be okay +devel - + add make +shells - default should be okay + +optional items: +admin + add shutdown +archive + add sharutils + add zip + add unzip +devel + add cvs +editors + add gvim + add vim + add emacs +interpreters + add perl +net + add openssh +text + add less +utils + add pcre (needed by less) + add cygutils (provides cal and other useful tools) + + + diff --git a/docs/feisty_meow_dox.config b/docs/feisty_meow_dox.config new file mode 100644 index 00000000..34995618 --- /dev/null +++ b/docs/feisty_meow_dox.config @@ -0,0 +1,1219 @@ +# Doxyfile 1.5.0 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = "feisty meow concerns codebase" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 2.108 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, +# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, +# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, +# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = $(REPOSITORY_DIR)/scripts $(REPOSITORY_DIR)/core $(REPOSITORY_DIR)/octopi $(REPOSITORY_DIR)/graphiq $(PRODUCTION_DIR)/setup_src + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = "*/3rdparty/*" "*/clam_bin/*" "*/bin/*" "*/binaries/*" "*/install/*" "*/logs/*" "*/msys/*" "*/objects/*" "*/packages/*" "*/waste/*" + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = $(PRODUCTION_DIR)/code_guide + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = YES + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = $(DEFINITIONS) + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a caller dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = /usr/bin + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/docs/feisty_meow_quick_start.txt b/docs/feisty_meow_quick_start.txt new file mode 100644 index 00000000..6d24456b --- /dev/null +++ b/docs/feisty_meow_quick_start.txt @@ -0,0 +1,74 @@ + +Feisty Meow Concerns Ltd. is a small software shop that concentrates on building high quality, +portable, open source projects in a variety of languages (primarily bash, perl, python, C++, +and Java). Here are some highlights of our main products: + + Bookmark Processing Tools - can take a mozilla bookmark file or arbitrary + web page and extract all the links out of it, building a csv database of + web links. Using that database, a variety of output formats are + provided, including one that outputs a mozilla bookmark file again. + The most useful feature is probably the marks checker that operates on + our csv format and that locates all unreachable links in the file and + separates them out. + + CROMP protocol - supports transmission of objects across the network and + provides a platform independent method for RPC and other types of + communication. + + Octopus design pattern - the underpinning of the CROMP protocol. An octopus + has an arbitrary number of tentacles (no, not just eight) which are each + responsible for consuming a different type of object (or datum). + + Fast Templates and Portable Abstractions - the class libraries of Feisty Meow + provide numerous different data structures and programming language + abstractions (like threads and state machines). There are also some + fairly ancient templates (in use since late 80s) which in many cases + perform faster than their STL analogues. + + CLAM System - Feisty Meow is the home site of the CLAM makefile system. The + CLAM system is a flexible and extensible method for building C++ and + C# files using makefiles. + +Prerequisites: + + Software required to compile under Linux: + curl-devel + openmotif-devel + openssl-devel + wxGTK-devel + + Software required to compiler under MS-windows: + The free Microsoft compiler should build Feisty Meow but it is untested. + The full version of MS Visual Studio 2010 (version 10) is supported. + Gnu C++ should compile Feisty Meow but it is also untested recently. + +Quick Start: + + Run the following commands to bootstrap the Feisty Meow libraries, once you + have downloaded the archive or retrieved them via CVS (assuming that you + have stored the files in ~/feisty_meow): + + bash ~/feisty_meow2/scripts/generator/bootstrap_build.sh + + This should create the 'makedep' dependency checking tool and the version + tagging tool and then go through the rest of the build. + + Once you've got a bootstrapped build, you can clean out all the files with: + + bash ~/feisty_meow2/scripts/generator/whack_build.sh clean + + And if you want to load the build environment for doing makes inside the + feisty_meow hierarchies, you can either run a sub-shell with the environment: + + bash ~/feisty_meow2/scripts/generator/build_variables.sh + + or you can load them into the current shell: + + bv=~/feisty_meow2/scripts/generator/build_variables.sh; source $bv $bv + + (The double reference is required since a sourced script does not get any of + the command-line parameters from the parent script.) + + More information is available at the official site http://feistymeow.org + + diff --git a/docs/makefile b/docs/makefile new file mode 100644 index 00000000..ae493d94 --- /dev/null +++ b/docs/makefile @@ -0,0 +1,17 @@ + +export DEFINITIONS + # ensure that the macros get passed down to the subprocesses. + +include cpp/variables.def + +PROJECT = Source_Documentation +TYPE = application +FIRST_TARGETS = build_doxygen +CLEANUPS = html + +include cpp/rules.def + +build_doxygen: + @echo the defs are $(DEFINITIONS) + doxygen $(wildcard *.config) + diff --git a/docs/perl_tools.html b/docs/perl_tools.html new file mode 100644 index 00000000..6a14c438 --- /dev/null +++ b/docs/perl_tools.html @@ -0,0 +1,372 @@ + + + + + + + + + YETIcode Perl Scripts + + + + +

       

      +
      + + + + + + +
      +
      +

      YETIcode Open Source Perl +Scripts

      +

      Some Hopefully Useful GPL-Licensed Perl Code
      +

      +
      +
      Contributed by Chris Koeritz (Koeritz@Gruntose.COM)
      +See the GNU Public +License for details of licensing.
      +
      +

      Caveats:

      +
        +
      1. No warranty, express, implied or imagined, is offered for +these files.  If you destroy your life by running one of them, I +will have sympathy, but that's about it.
      2. +
      3. Some of these shell scripts depend on environment variables.  +The most frequent case of this is YETI_DIR, which should point to the +top-level +directory where the YETIcode scripts are stored on your +machine.   This variable is set by default when the scripts are in +the ~/yeti folder.  Consult your operating +system documentation if you are +unfamiliar with the concept or management of environment variables.
      4. +
      5. These files are provided as possibly useful shell scripts +rather than as full-fledged reusable and object oriented components.
      6. +
      7. Improvements and contributions are gladly accepted.  +They will be processed as fast as our schedule permits.  Please +send any changes to the Gruntose Curator at fred@gruntose.com .
      8. +
      9. These files are mostly portable between Unix and the +various Windoze OSes, but in many cases you may want to fix the +defaults or path names to make them more suitable for your own needs.
      10. +
      11. Some of the scripts invoke external programs that are +available for most Unixes.  For Windoze users, a set of GNU Unix +utilities is available at "http://www.mingw.org/".
      12. +
      13. An alternative, but not recommended, GNU suite is +at "http://www.cygwin.com/".
      14. +
      15. Direct Cognition: +View the scripts directory itself rather than navigating with the links +below: scripts.
      16. +
      +
      +
      +

       

      +
      + + + + + + +
      +

      Library Files
      +

      +
      +

      diff_lib.pl

      +
      The "differ" utilities can be used to compare two +directories of text or binary files against each other.  The two +directories are presumably close in contents.  This can be useful +when one is revising a set of files and wants to synchronize an older +copy against a newer version.  Support for "differ.pl" +is provided +here.
      +

      filename_helper.pl

      +
      Contains a handy set of utilities for manipulating +filenames.  These can help to make perl scripts portable across +the two well-known types of filename separators ('/' and '\').  +They also provide support for ripping up filenames into their +components.
      +

      inc_num.pl

      +
      This is a simple utility that manages a file with a +number in it.  This is somewhat more useful than it sounds.  +Functions are provided to get the current number and to change the +number.
      +

      +

      shared_snarfer.pl

      +
      This library supports the "snarf" utilities.  +The +utilities manipulate archive files with the ".snarf" extension.  +These +packed +snarf files are compressed chunks of directory hierarchies.  Bob +files +are useful because they track a number per distinct "snarf" types that +is +used to make uniquely named new archives of the appropriate type.  +This +number is an ever increasing integer that's stored in a well-known +(configurable) location.  When a snarf file is unpacked (using the +"unsnarf" tool), the number is updated on the local machine so that the +next generated file will +be one greater than the previous number.  If one is travelling +between +two machines with the same snarf file, this will have the effect of +keeping +the number updated on both sides. +
      +

      zap_the_dir.pl

      +
      Support for the zapdirs utility.  This library +cleans out the directory that it is passed by removing files that are +not considered important (using "filename_helper.pl +").  The list of important files is something you might want to +look at to ensure that you won't get burned by zapdirs.
      +
      +
      +
      +

       

      +
      + + + + + + +
      +

      Applications

      +
      +

      add_cr.pl

      +
      Processes Unix format text files for pcdos by +forcing the line endings to be Carriage Return plus Line Feed (CRLF).
      +

      +

      cgi_display.pl

      +
      Turns the files passed on the command line into a +stream of CGI compatible text output. The javascript show_file method +(see the +source for this page) is preferred since it is lighter weight and +doesn't +need cgi, but there are some situations where cgi is the only option +(older +browsers or requirements of no javascript).
      +

      +

      change_endings.pl

      +
      Replaces the suffix of all filenames in the current +directory +with a different suffix.  Suffix here is defined as the set of +characters +after the last period ('.') in the name.  Note this will not work +for +names without suffices.
      +

      +

      cpdiff.pl

      +
      Copies files from a source directory into a +destination directory.  The files are only copied when they are +missing in the destination or when the destination version has +different contents.  The syntax looks like this:
      +    cpdiff  source  destination
      +The assumption is that the files in the source directory are somehow +better, newer or more complete than the set of files in the destination.
      +
      +

      cpdiffnow.pl

      +
      Similar to cpdiff, but this utility sets the +destination file's time stamp to "now".  This should cause the new +or changed files in the destination directory to be more recent than +anything else in there.  This is helpful sometimes for forcing +compilation of modified source files.
      +
      +

      +

      cvs_fix.pl

      +
      Wraps the cygwin cvs command for pcdos/win32. + Any +unfriendly backward slashes are flipped to be forward slashes.
      +
      +

      +

      differ.pl

      +
      Compares two directory hierarchies and the files +they +contain.  The first parameter is a directory +to compare against "this" directory; +every subdirectory "here" will be traversed in order to build the +output file that shows the differences.  An optional second +argument can be used to specify a different directory than the current +one as the source of the comparison (the first argument is always the +destination of the comparison).
      +

      filedump.pl

      +
      Collects the contents of the files whose names are +passed on the command line into one gigundo stream which is passed to +standard output. The output can be piped into another file as desired.
      +

      generate_aliases.pl

      +
      Performs some useful activities for the YETI shell +environment.  Using the environment variable for SHELLDIR (which +is set in the appropriate startup files to be the shell scripts +directory, where all this stuff lives), generate_aliases will create +all of the aliases files for the combinations of operating systems and +types of shells supported.  Currently this includes Linux, Unix, +PCDOS, OS/2 and MS-WIN32 (9x, NT, 2K, XP, etc) for +supported operating systems.  The shell languages supported are +dos's command, nt's cmd, unix's sh and bash, and perl.  This script will also look for +any files ending in ".sh" or ".pl" and it will create aliases for them +in forms appropriate to the different shells.  The .zz_auto_gen +subdirectory +is created under the home directory (or under TMP in DOS and Win32) as +a storage place for the generated +files.
      +

      +

      goodbye.pl

      +
      A logout script for exiting from a shell; it prints +a message using the nechung oracle for the user's benefit and starts a +byejob before exiting. The byejob will wait for a few seconds, then +clear the screen and print another fortune. It attempts to leave the +screen looking like a +standard login, but with an extra fortune.
      +

      +

      +

      new_sig.pl

      +
      Generates a signature file from the nechung +database +using the 'nechung' application.  See the HOOPLE library for the nechung +application. + The database for nechung resides in the whole +YETI +package in "yeti/database".
      +

      +

      renlower.pl

      +
      Renames all of the files passed on the command line +such that they are only in lower-case.  Useful if you're tired of +passing mistakenly re-capitalized names from a defective 8.3 OS (e.g. +Doze95/98) to +a file system where you care about the case.
      +

      runner.pl

      +
      Finds all executable files in the current directory +(and subdirectories) and runs them.  The output of the programs is +sent to standard output.  Standard error is used to report which +file is being worked on, plus the running programs' own standard error +streams are merged into runner's standard error stream.  This +makes it nice to do something like:
      +       runner >runs.log
      +where the runs.log file will contain the output of each program that +was executed and the console will be sent messages as each program is +started and finished (and errors show up at the console also).
      +
      +

      safedel.pl

      +
      Makes deleting files and directories a little less +nerve-wracking.  If you substitute safedel as an alias for rm or +del or deltree or whatever, it will make a zipped backup of the items +before they are actually deleted.  Safedel keeps track of a number +that is attached to each zip to enforce uniquely numbered +archives.  They are stored in a directory named "zz_del_keep" +that is stored under the temorary directory (specified by the +environment +variable named TMP).  A report of the contents of the compressed +trash +is appended to a file named "zz_safedel.rpt" in the TMP directory.  +Occasional +cleaning of the deleted files folder is recommend, but this utility has +saved +my various parts several times already.
      +

      +

      +

      snarf_linux_config.pl

      +
      A snarf utility that packages up the +important configuration files in a Linux installation.
      +

      +

      snarf_light.pl

      +
      A selective snarf of the source hierarchy.  +This +collects the code that I manage.  As such, this is probably +irrelevant to anyone but CAK.
      +

      snarf_notes.pl

      +
      Gathers all "important" files from the home +directory. + This +is somewhat personally tuned but it includes files and directories that +have +"project", "notes", or "crucial" in their name.
      +

      +

      snarf_src.pl

      +
      A source code grabbing snarfer.  The entire +source +code hierarchy is snarfed.  Note that one should edit the +hierarchy +location to make it appropriate for your local source code.
      +

      +

      snarf_yeti.pl

      +
      A snarfer for the YETI shell scripts and databases.
      +

      summing_dir.pl

      +
      Offers a directory listing along with total file +sizes +and disk free space.
      +
      +
      +

      synch_build.pl

      +
      This is a helper utility that synchronizes the binary +outputs from a build process with an existing installed location.  Given a target directory, the executable +programs and dynamic libraries that exist there will be synchronized +with the build repository's versions.  This is kind of a quickie +upgrade process, as long as the files in the target location are not +locked by other processes.
      +
      +

      unsnarf.pl

      +
      Uses the snarfer utilities to undo a previously +snarfed file.  A folder named "snarf_BASE" is created for the +contents, where BASE +is replaced with the basename of the snarf file (that is, without the +".snarf" +suffix).  The number that tracks the snarf files of this type is +updated +such that the next snarf file will be at least one higher than this +snarf's +sorta +unique number.  The number will be managed correctly if you're +always +unsnarfing the most recent snarf files before creating any new snarfs.
      +

      +

      whack_forever.pl

      +
      Since all of my file deletion commands are aliases +to safedel, it is hard to actually remove a +file.  If I'm really really sure that a file or directory needs to +be +permanently deleted, then this command can be used.  It shows the +names +it is removing also, but it does _not_ ask for confirmation.
      +

      +

      y2038_check.pl

      +
      Tests the system for survival +past the year 2038, which is when the Unix time scale runs out of bits +for the number of seconds since 1970 measured in a 32 bit integer.
      +
      +

      zapdirs.pl

      +
      Removes empty directories and directories +containing only +unimportant crud (see "filename_helper.pl").  +If there are no arguments, then the current directory is cleaned up; +any subdirectories +will be traversed into and removed if it seems appropriate.  +Otherwise, +zapdirs operates on the arguments passed to it as if they are directory +names +to be cleaned.
      +
      +
      +

       

      +
      + + diff --git a/docs/text_examples/chinese_simplified_text.txt b/docs/text_examples/chinese_simplified_text.txt new file mode 100644 index 00000000..1219fd33 --- /dev/null +++ b/docs/text_examples/chinese_simplified_text.txt @@ -0,0 +1,10 @@ + +we will translate this at babelfish: + +Let us see then if we can narrow it down. As I focus my mind upon it, it seems rather less impenetrable. What indications have we as to this book? + +into this: + +让我们然后看如果我们能使它狭窄击倒。因为我聚焦我的头脑在它, 它似乎宁可较不难贯穿。什么征兆有我们至于这本书? + + diff --git a/docs/text_examples/korean_text.txt b/docs/text_examples/korean_text.txt new file mode 100644 index 00000000..52be2983 --- /dev/null +++ b/docs/text_examples/korean_text.txt @@ -0,0 +1,12 @@ + + +we will translate this at babelfish: + +Let us see then if we can narrow it down. As I focus my mind upon it, it seems rather less impenetrable. What indications have we as to this book? + +into this: + +우리들을 그때 우리가 떨어뜨리기 위하여 그것을 좁힐 수 있으면 보는 시키십시요. 나가 그것에 나의 마음을 초점을 맞추기 때문에, 오히려 보다 적게 보인다. 이 책에 관해서는 무슨 표시가 우리가 있는가? + + + diff --git a/docs/text_examples/readme.txt b/docs/text_examples/readme.txt new file mode 100644 index 00000000..0111f86c --- /dev/null +++ b/docs/text_examples/readme.txt @@ -0,0 +1,7 @@ + + +these are example files of other languages in utf-8 format. + +they can be used to test proper handling of the characters in hoople code. + + diff --git a/docs/text_examples/russian_text.txt b/docs/text_examples/russian_text.txt new file mode 100644 index 00000000..4ef6c44b --- /dev/null +++ b/docs/text_examples/russian_text.txt @@ -0,0 +1,4 @@ + +Stanislav Yevgrafovich Petrov (Russian: Станислав Евграфович Петров) (born c. 1939) is a retired Russian Strategic Rocket Forces lieutenant colonel who, on September 26, 1983, averted a potential nuclear war by refusing to believe that the United States had launched missiles against the Soviet Union, despite the indications given by his computerized early warning systems.[1] + + diff --git a/docs/text_examples/tibetan_text.txt b/docs/text_examples/tibetan_text.txt new file mode 100644 index 00000000..321ddace --- /dev/null +++ b/docs/text_examples/tibetan_text.txt @@ -0,0 +1,13 @@ + + +om mani padme hum: +ༀ'མཎི'པདེྨ'ཧཱུྃ ༔ + + +this was scarfed from a tibetan resources page: + + +༄༅།།གངས་ལྗོངས་གློག་རྡུལ་དཔེ་མཛོད་ཁང་ཞེས་པ་འདི་དེང་དུས་ཀྱི་ འཕྲུལ་རྩལ་བེད་སྤྱད་དེ་ཁ་བའི་ལྗོངས་དང་འབྲེལ་ཡོད་ཀྱི་རིག་ གཞུང་ངོ་མཚར་ཅན་རྣམས་འཛམ་གླིང་ཡུལ་གྲུ་རིས་མེད་ཀྱི་སྐྱེ་བོའི་ སྤྱན་ལམ་དུ་བསྟར་བའི་རིན་གོང་མེད་པའི་དྲ་བའི་སྟེགས་བུ་ཞིག་ ཡིན། ངེད་ཚོས་དྲ་ལམ་དེ་བརྒྱུད་སྐད་རིགས་མི་འདྲ་བའི་ཐོག་ནས་ཁུལ་ དེའི་དཔྱད་གཞིའི་ཡིག་ཆ་སྣ་ཚོགས་མཁོ་འདོན་བྱེད་ཀྱིན་ཡོད་པས་ ཚུལ་དེ་ནི་གསར + + + diff --git a/graphiq/library/geometric/angle.h b/graphiq/library/geometric/angle.h new file mode 100644 index 00000000..89696850 --- /dev/null +++ b/graphiq/library/geometric/angle.h @@ -0,0 +1,240 @@ +#ifndef ANGLE_CLASS +#define ANGLE_CLASS + +/*****************************************************************************\ +* * +* Name : angle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include + +#include + +namespace geometric { + +//! Represents a geometric angle. + +//! angles can be measured in degrees or radians in this class. +enum angular_units { DEGREES, RADIANS }; + +template +class angle : public basis::packable +{ +public: + DEFINE_CLASS_NAME("angle"); + + angle(contents inital_rotation = 0, angular_units unit = RADIANS); + //!< constructs a new angle with "initial_rotation" in the "unit". + + void set(contents a, angular_units unit); + //!< sets the angle to a new rotation "a" in the "unit". + contents get(angular_units unit) const; + //!< retrieves the current angular measure. + + angle operator - (void) const; + //!< returns the negation of this angle. + + angle operator + (const angle &to_add) const; + angle operator - (const angle &to_subtract) const; + angle operator * (contents to_multiply) const; + angle operator / (contents to_divide) const; + angle &operator += (const angle &to_add); + angle &operator -= (const angle &to_subtract); + angle &operator *= (contents to_multiply); + angle &operator /= (contents to_divide); + + contents sine() const; + //!< returns the sin function of this angle. + contents cosine() const; + //!< returns the cos function of this angle. + contents tangent() const; + //!< returns the tan function of this angle. + + static angle arctangent(contents opposite, contents adjacent, + basis::outcome &retval); + //!< returns the atan of the angle. + /*!< the outcome will be set to OKAY if the function operated successfully. + otherwise it will be set to BAD_INPUT. */ + static angle arccosine(contents adjacent, contents hypotenuse, + basis::outcome &retval); + //!< returns the acos of the angle. + static angle arcsine(contents opposite, contents hypotenuse, + basis::outcome &retval); + //!< returns the asin of the angle. + + virtual int packed_size() const; + virtual void pack(basis::byte_array &packed_form) const; + //!< packs the angle for shipping in bytes. + virtual bool unpack(basis::byte_array &packed_form); + //!< unpacks the angle from the "packed_form". + +private: + contents _theta; //!< the held angular measure. + + contents to_internal(contents initial, angular_units unit) const; + //!< converts the angle into the units we use inside the class. + contents from_internal(contents initial, angular_units unit) const; + //!< converts the angle from our internal measure into "unit" measure. +}; + +////////////// + +//! double_angle provides a non-templated class for forward declarations. + +class double_angle : public angle +{ +public: + double_angle(double init = 0, angular_units unit = RADIANS) + : angle(init, unit) {} + double_angle(const angle &to_copy) : angle(to_copy) {} +}; + +////////////// + +// implementation of larger methods below. + +template +angle::angle(contents a, angular_units unit) { set(a, unit); } + +template +angle angle::operator - (void) const +{ angle to_return(*this); to_return *= -1; return to_return; } + +template +angle angle::operator + (const angle &a) const +{ angle to_return(*this); to_return += a; return to_return; } + +template +angle angle::operator - (const angle &a) const +{ angle to_return(*this); to_return -= a; return to_return; } + +template +angle angle::operator * (contents to_multiply) const +{ + angle to_return(*this); + to_return *= to_multiply; + return to_return; +} + +template +angle angle::operator / (contents to_divide) const +{ angle to_return(*this); to_return /= to_divide; return to_return; } + +template +angle &angle::operator += (const angle &a) +{ _theta += a._theta; return *this; } + +template +angle &angle::operator -= (const angle &a) +{ _theta -= a._theta; return *this; } + +template +angle &angle::operator *= (contents f) +{ _theta *= f; return *this; } + +template +angle &angle::operator /= (contents f) +{ _theta /= f; return *this; } + +template +contents angle::sine() const { return sin(_theta); } + +template +contents angle::cosine() const { return cos(_theta); } + +template +contents angle::tangent() const { return tan(_theta); } + +template +int angle::packed_size() const +{ + basis::byte_array temp; +//hmmm: inefficient! + pack(temp); + return temp.length(); +} + +template +void angle::pack(basis::byte_array &packed_form) const +{ structures::attach(packed_form, _theta); } + +template +bool angle::unpack(basis::byte_array &packed_form) +{ return structures::detach(packed_form, _theta); } + +template +contents angle::to_internal(contents a, angular_units unit) const +{ + switch(unit) { + case RADIANS: return a; + case DEGREES: return a * PI_APPROX / 180.0; + default: return 0; + } +} + +template +contents angle::from_internal(contents a, angular_units unit) const +{ + switch(unit) { + case RADIANS: return a; + case DEGREES: return a * 180.0 / PI_APPROX; + default: return 0; + } +} + +template +void angle::set(contents a, angular_units unit) +{ _theta = to_internal(a, unit); } + +template +contents angle::get(angular_units unit) const +{ return from_internal(_theta, unit); } + +template +angle angle::arccosine(contents adjacent, + contents hypotenuse, basis::outcome &retval) +{ + contents d = adjacent / hypotenuse; + retval = basis::common::BAD_INPUT; + bounds_return(d, -1.0, 1.0, angle()); + retval = basis::common::OKAY; + return angle(acos(d), RADIANS); +} + +template +angle angle::arcsine(contents opposite, contents hypotenuse, + basis::outcome &retval) +{ + contents d = opposite / hypotenuse; + retval = basis::common::BAD_INPUT; + bounds_return(d, -1.0, 1.0, angle()); + retval = basis::common::OKAY; + return angle(asin(d), RADIANS); +} + +template +angle angle::arctangent(contents opposite, contents adjacent, + basis::outcome &retval) +{ + retval = basis::common::BAD_INPUT; + if ( (adjacent == 0.0) && (opposite == 0.0) ) return angle(); + retval = basis::common::OKAY; + return angle(atan2(opposite, adjacent), RADIANS); +} + +} // namespace. + +#endif + diff --git a/graphiq/library/geometric/cartesian_objects.h b/graphiq/library/geometric/cartesian_objects.h new file mode 100644 index 00000000..e18608e4 --- /dev/null +++ b/graphiq/library/geometric/cartesian_objects.h @@ -0,0 +1,74 @@ +#ifndef CARTESIAN_OBJECTS_GROUP +#define CARTESIAN_OBJECTS_GROUP + +/*****************************************************************************\ +* * +* Name : cartesian objects * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "angle.h" +#include "line.h" +#include "point.h" +#include "rectangle.h" + +namespace geometric { + +//! Provides a geometric point that use double floating points numbers. + +class cartesian_point : public point +{ +public: + cartesian_point(double x = 0, double y = 0) : point(x, y) {} + cartesian_point(double r, double_angle theta) : point(r, theta) {} + cartesian_point(const point &to_copy) : point(to_copy) {} + DEFINE_CLASS_NAME("cartesian_point"); + + static cartesian_point origin() { return cartesian_point(0.0, 0.0); } + //!< the origin of the two-dimensional system. +}; + +////////////// + +//! Provides a geometric line that use double floating points numbers. + +class cartesian_line : public line +{ +public: + cartesian_line(const cartesian_point &endpoint_1, + const cartesian_point &endpoint_2) + : line(endpoint_1, endpoint_2) {} + cartesian_line(double x_1 = 0, double y_1 = 0, + double x_2 = 0, double y_2 = 0) + : line(x_1, y_1, x_2, y_2) {} +}; + +////////////// + +//! Provides a geometric rectangle that use double floating points numbers. + +class cartesian_rectangle : public rectangle +{ +public: + cartesian_rectangle(const cartesian_point &vertex_1, + const cartesian_point &vertex_2) + : rectangle(vertex_1, vertex_2) {} + cartesian_rectangle(double x_1 = 0, double y_1 = 0, + double x_2 = 0, double y_2 = 0) + : rectangle(x_1, y_1, x_2, y_2) {} + cartesian_rectangle(const rectangle &rect) + : rectangle(rect) {} +}; + +} // namespace. + +#endif + diff --git a/graphiq/library/geometric/circle.cpp b/graphiq/library/geometric/circle.cpp new file mode 100644 index 00000000..8423c488 --- /dev/null +++ b/graphiq/library/geometric/circle.cpp @@ -0,0 +1,77 @@ +/*****************************************************************************\ +* * +* Name : circle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "circle.h" +#include "cartesian_objects.h" +#include "line.h" +#include "rectangle.h" + +#include + +#include + +using namespace basis; + +namespace geometric { + +circle::circle() : _radius(1), _center(cartesian_point::origin()) {} + +circle::circle(double a_radius, const cartesian_point &a_center) +: _radius(a_radius), _center(a_center) {} + +circle::~circle() {} + +double circle::area() const { return PI_APPROX * square(_radius); } + +double circle::diameter() const { return 2.0 * _radius; } + +double circle::circumference() const { return 2.0 * PI_APPROX * _radius; } + +cartesian_point circle::location(const double_angle &where) const +{ + double rotation = where.get(RADIANS); + cartesian_point second(cos(rotation) * _radius, sin(rotation) * _radius); + return _center + second; +} + +bool circle::inside(const cartesian_point &where) const +{ + double dist = where.distance(_center); + return dist <= _radius? true : false; +} + +cartesian_rectangle circle::dimensions() const +{ + const double deg0 = 0; + const double deg90 = 0.5 * PI_APPROX; + const double deg180 = PI_APPROX; + const double deg270 = 1.5 * PI_APPROX; + + cartesian_point right(location(deg0)); + cartesian_point top(location(deg90)); + cartesian_point left(location(deg180)); + cartesian_point bottom(location(deg270)); + return cartesian_rectangle(left.x(), bottom.y(), right.x(), top.y()); +} + +double circle::radius() const { return _radius; } + +void circle::radius(double to_set) { _radius = to_set; } + +cartesian_point circle::center() const { return _center; } + +void circle::center(const cartesian_point &to_set) { _center = to_set; } + +} // namespace. + diff --git a/graphiq/library/geometric/circle.h b/graphiq/library/geometric/circle.h new file mode 100644 index 00000000..4e25286d --- /dev/null +++ b/graphiq/library/geometric/circle.h @@ -0,0 +1,71 @@ +#ifndef CIRCLE_CLASS +#define CIRCLE_CLASS + +/*****************************************************************************\ +* * +* Name : circle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "cartesian_objects.h" + +namespace geometric { + +//! Represents a geometric circle. +/*! + A circle is specified by its center and its radius. The angles are + measured in radians. +*/ + +class circle +{ +public: + circle(); + circle(double radius, const cartesian_point ¢er); + ~circle(); + + double area() const; + //!< Returns the area occupied by the circle. + + double circumference() const; + //!< Returns the perimeter for the circle. + /*!< The circumference is the length of a virtual string around the + circle. */ + + double diameter() const; + //!< Returns the length of the circle's bisecting line. + /*!< This is the length of a line segment that is circumscribed by the + circle and which passes through the center of the circle. */ + + bool inside(const cartesian_point &where) const; + //!< Returns true if the point is inside the circle. + + cartesian_point location(const double_angle &where) const; + //!< Returns the point on the circle that is at the angle "where". + + cartesian_rectangle dimensions() const; + //!< Returns a bounding box around the circle. + + double radius() const; //!< Half of the circle's diameter. + void radius(double to_set); //!< Sets the radius of the circle. + + cartesian_point center() const; //!< The point at the center of the circle. + void center(const cartesian_point &to_set); //!< Resets the circle's center. + +private: + double _radius; //!< Records the current radius. + cartesian_point _center; //!< Records the current center. +}; + +} // namespace. + +#endif + diff --git a/graphiq/library/geometric/ellipse.cpp b/graphiq/library/geometric/ellipse.cpp new file mode 100644 index 00000000..c7be48c9 --- /dev/null +++ b/graphiq/library/geometric/ellipse.cpp @@ -0,0 +1,120 @@ + + + +/*****************************************************************************\ +* * +* Name : ellipse * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "cartesian_objects.h" +#include "ellipse.h" +#include "line.h" +#include "rectangle.h" + +#include + +#include + +using namespace basis; + +namespace geometric { + +ellipse::ellipse() +: _center(cartesian_point::origin()), + _width_from_center(1), + _height_from_center(1) +{} + +ellipse::ellipse(const cartesian_point &a_center, double a_width_from_center, + double a_height_from_center) +: _center(a_center), + _width_from_center(a_width_from_center), + _height_from_center(a_height_from_center) +{} + +ellipse::ellipse(const ellipse &to_copy) +: _center(), + _width_from_center(0), + _height_from_center(0) +{ *this = to_copy; } + +ellipse::~ellipse() {} + +ellipse &ellipse::operator = (const ellipse &to_copy) +{ + if (this == &to_copy) return *this; + _center = to_copy._center; + _width_from_center = to_copy._width_from_center; + _height_from_center = to_copy._height_from_center; + return *this; +} + +double ellipse::area() const +{ return absolute_value(PI_APPROX * _width_from_center * _height_from_center); } + +double ellipse::perimeter() const +{ + double w = _width_from_center; + double h = _height_from_center; + double perim_temp = sqrt(square(h) + square(w)) / 2; + return 2.0 * PI_APPROX * perim_temp; +} + +cartesian_point ellipse::location(const double_angle &where) const +{ + double a = _width_from_center; + double b = _height_from_center; + double a_multiplier = square(where.tangent()); + double denom = sqrt(square(b) + square(a) * a_multiplier); + double ab = a * b; + double tango = where.tangent(); + cartesian_point to_return(ab / denom, ab * tango / denom); + + // the following negates the x component if the angle is in the appropriate + // part of the ellipse. + int ang = int(where.get(DEGREES)); + double adjustment = where.get(DEGREES) - double(ang); + ang %= 360; + double adjusted_ang = ang + adjustment; + if ( (adjusted_ang < 270.0) && (adjusted_ang > 90.0) ) + to_return.set(to_return.x() * -1.0, to_return.y()); + to_return += _center; + return to_return; +} + +bool ellipse::inside(const cartesian_point &where) const +{ + double dist = where.distance(_center); + double_angle to_point = double_angle(asin(where.y() / dist), RADIANS); + cartesian_point intersector = location(to_point); + return dist <= intersector.distance(_center)? true : false; +} + +cartesian_point ellipse::center() const { return _center; } + +double ellipse::width_from_center() const { return _width_from_center; } + +double ellipse::height_from_center() const { return _height_from_center; } + +void ellipse::center(const cartesian_point &to_set) { _center = to_set; } + +void ellipse::width_from_center(double to_set) +{ _width_from_center = to_set; } + +void ellipse::height_from_center(double to_set) +{ _height_from_center = to_set; } + +} // namespace. + + + + diff --git a/graphiq/library/geometric/ellipse.h b/graphiq/library/geometric/ellipse.h new file mode 100644 index 00000000..53eb44d9 --- /dev/null +++ b/graphiq/library/geometric/ellipse.h @@ -0,0 +1,72 @@ +#ifndef ELLIPSE_CLASS +#define ELLIPSE_CLASS + +/*****************************************************************************\ +* * +* Name : ellipse * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +namespace geometric { + +// forward. +class cartesian_point; +class double_angle; + +//! Represents a geometric ellipse. +/*! An ellipse is specified by its height, width and center. */ + +class ellipse +{ +public: + ellipse(); + ellipse(const cartesian_point ¢er, double width_from_center, + double height_from_center); + ellipse(const ellipse &to_copy); + + ~ellipse(); + + ellipse &operator = (const ellipse &to_copy); + + double area() const; + //!< Returns the area occupied by the ellipse. + + double perimeter() const; + //!< Returns the perimeter for the ellipse. + /*!< This is the length of the virtual string around the ellipse. The + returned value is an approximation. */ + + bool inside(const cartesian_point &where) const; + //!< Returns true if the point is inside the ellipse. + + cartesian_point location(const double_angle &where) const; + //!< Describes the locus of the ellipse (basically, where it lives). + /*!< Returns the point on the ellipse that lies on a ray from the center + of the ellipse directed at an angle "where". */ + + cartesian_point center() const; + double width_from_center() const; + double height_from_center() const; + + void center(const cartesian_point &to_set); + void width_from_center(double to_set); + void height_from_center(double to_set); + +protected: + cartesian_point _center; + double _width_from_center; + double _height_from_center; +}; + +} // namespace. + +#endif + diff --git a/graphiq/library/geometric/line.h b/graphiq/library/geometric/line.h new file mode 100644 index 00000000..46f162ee --- /dev/null +++ b/graphiq/library/geometric/line.h @@ -0,0 +1,133 @@ +#ifndef LINE_CLASS +#define LINE_CLASS + +/*****************************************************************************\ +* * +* Name : line * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "point.h" + +namespace geometric { + +//! Represents a geometric line segment. + +template +class line +{ +public: + line(const point &endpoint1, + const point &endpoint2); + line(numeric_type end1_x = 0, numeric_type end1_y = 0, + numeric_type end2_x = 0, numeric_type end2_y = 0); + + point center() const; + //!< Returns the point at the center of the line segment. + + line operator + (const point &to_add) const; + line operator - (const point &to_subtract) const; + //!< Returns this line with "to_add" added to it. + /*!< This returns a line that is the result of adding or subtracting a + point to the endpoints of this line. */ + + line &operator += (const point &to_add); + line &operator -= (const point &to_subtract); + //!< Adds or subtracts a point from `this' line. + + point endpoint_1() const; + point endpoint_2() const; + + void endpoint_1(const point &to_set); + void endpoint_2(const point &to_set); + + basis::astring text_form() const; + //!< returns a string form of the points defining the line. + +protected: + point _endpoint_1; + point _endpoint_2; +}; + +////////////// + +// implementations below... + +template +line::line(const point &p1, + const point &p2) +: _endpoint_1(p1), _endpoint_2(p2) {} + +template +line::line(numeric_type x1, numeric_type y1, numeric_type x2, + numeric_type y2) +: _endpoint_1(point(x1, y1)), + _endpoint_2(point(x2, y2)) +{} + +template +point line::center() const +{ + return point(_endpoint_1.x() / 2.0 + _endpoint_2.x() / 2.0, + _endpoint_1.y() / 2.0 + _endpoint_2.y() / 2.0); +} + +template +basis::astring line::text_form() const +{ + return basis::astring("<") + _endpoint_1.text_form() + basis::astring(" ") + + _endpoint_2.text_form() + basis::astring(">"); +} + +template +line &line::operator += + (const point &to_add) +{ _endpoint_1 += to_add; _endpoint_2 += to_add; return *this; } + +template +line &line::operator -= + (const point &to_subtract) +{ _endpoint_1 -= to_subtract; _endpoint_2 -= to_subtract; return *this; } + +template +line line::operator + + (const point &to_add) const +{ line to_return(*this); to_return += to_add; return to_return; } + +template +line line::operator - + (const point &to_subtract) const +{ + line to_return(*this); + to_return -= to_subtract; + return to_return; +} + +template +point line::endpoint_1() const +{ return _endpoint_1; } + +template +point line::endpoint_2() const +{ return _endpoint_2; } + +template +void line::endpoint_1(const point &to_set) +{ _endpoint_1 = to_set; } + +template +void line::endpoint_2(const point &to_set) +{ _endpoint_2 = to_set; } + +} // namespace. + +#endif + diff --git a/graphiq/library/geometric/makefile b/graphiq/library/geometric/makefile new file mode 100644 index 00000000..d8c0da6d --- /dev/null +++ b/graphiq/library/geometric/makefile @@ -0,0 +1,9 @@ +include cpp/variables.def + +PROJECT = geometric +TYPE = library +SOURCE = circle.cpp ellipse.cpp math_bits.cpp polygon.cpp screen_rectangle.cpp triangle.cpp +TARGETS = geometric.lib + +include cpp/rules.def + diff --git a/graphiq/library/geometric/math_bits.cpp b/graphiq/library/geometric/math_bits.cpp new file mode 100644 index 00000000..442d690a --- /dev/null +++ b/graphiq/library/geometric/math_bits.cpp @@ -0,0 +1,52 @@ +/*****************************************************************************\ +* * +* Name : mathematical operations group * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "math_bits.h" + +#include + +using namespace basis; + +namespace geometric { + +astring crop_numeric(const astring &input) +{ + astring to_return(input); + for (int i = 0; i < to_return.length(); i++) + if ( ( (to_return[i] >= '0') && (to_return[i] <= '9') ) + || (to_return[i] == '.') + || (to_return[i] == '+') || (to_return[i] == '-') + || (to_return[i] == 'E') || (to_return[i] == 'e') ) { + to_return.zap(i, i); // remove non-useful character. + i--; // move backwards in loop. + } else break; + return to_return; +} + +astring crop_non_numeric(const astring &input) +{ + astring to_return(input); + for (int i = 0; i < to_return.length(); i++) + if ( ! ((to_return[i] >= '0') && (to_return[i] <= '9')) + && (to_return[i] != '.') + && (to_return[i] != '+') && (to_return[i] != '-') + && (to_return[i] != 'E') && (to_return[i] != 'e') ) { + to_return.zap(i, i); // remove non-useful character. + i--; // move backwards in loop. + } else break; + return to_return; +} + +} + diff --git a/graphiq/library/geometric/math_bits.h b/graphiq/library/geometric/math_bits.h new file mode 100644 index 00000000..50c5ff43 --- /dev/null +++ b/graphiq/library/geometric/math_bits.h @@ -0,0 +1,85 @@ +#ifndef MATHEMATICAL_OPERATIONS_GROUP +#define MATHEMATICAL_OPERATIONS_GROUP + +/*****************************************************************************\ +* * +* Name : mathematical operations group * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1996-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +/*! @file math_bits.h + @brief Provides some fairly low-level math support. +*/ + +#include + +namespace geometric { + +basis::astring crop_numeric(const basis::astring &input); + //!< Eats the numeric characters from the front of "input". + /*!< This and crop_non_numeric() return a string that is constructed from + "input" by chopping the numerical (or non-numerical) characters off of the + beginning. The types of numbers supported are floating point, but it will + generally work for integers also (problems arise when mixing in + the period, e, E, +, and - characters with integer numbers). */ +basis::astring crop_non_numeric(const basis::astring &input); + //!< Eats the non-numeric characters from the front of "input". + +// type identification functions: + +//! returns true if the specified numeric_type is a floating_point type. +/*! this is useful in templates where one wants to know about the templated +type. */ +template +bool is_floating_point(numeric_type t) + { t = numeric_type(5.1); t = numeric_type(t * 3.0); + return 0.001 < float(absolute_value(numeric_type(t - 15.0))); } + +//! returns true if the instantiation type is an integer. +template +bool is_integral(numeric_type t) { return !is_floating_point(t); } + +// the following functions (is_short, is_signed and is_unsigned) are only +// useful for integral types. + +//! returns true if the specified type is short on the PC. +template +bool is_short(numeric_type) { return sizeof(numeric_type) == 2; } + +//! returns true if the templated type is unsigned. +template +bool is_unsigned(numeric_type t) { t = -1; return t > 0; } +//! returns true if the templated type is signed. +template +bool is_signed(numeric_type t) { return !is_unsigned(t); } + +//! Guesses the formatting string needed for the type provided. +/*! Returns a string that is appropriate as the format specifier of a printf +(or the astring constructor) given the template type. templates can rely +on this to print numerical types correctly. */ +template +basis::astring numeric_specifier(numeric_type t) { + basis::astring to_return("%d"); + if (is_floating_point(t)) + to_return = basis::astring("%f"); + else { // integral. + if (is_unsigned(t)) + to_return = basis::astring("%u"); + if (is_short(t)) + to_return.insert(1, "h"); + } + return to_return; +} + +} + +#endif // outer guard. + diff --git a/graphiq/library/geometric/point.h b/graphiq/library/geometric/point.h new file mode 100644 index 00000000..0ebda983 --- /dev/null +++ b/graphiq/library/geometric/point.h @@ -0,0 +1,237 @@ +#ifndef POINT_CLASS +#define POINT_CLASS + +/*****************************************************************************\ +* * +* Name : point * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "angle.h" +#include "math_bits.h" +#include "point.h" + +#include +#include +#include +#include + +#include + +//! Contains all of our objects for geometry and avoids name clashes. + +namespace geometric { + +//! Represents a geometric point. + +template +class point : public basis::packable, public virtual basis::root_object +{ +public: + point(numeric_type x = 0, numeric_type y = 0); + point(numeric_type r, double_angle theta); + + DEFINE_CLASS_NAME("point"); + + void set(numeric_type x, numeric_type y); + void set(numeric_type r, double_angle theta); + + numeric_type x() const { return _x; } + numeric_type y() const { return _y; } + + numeric_type r() const; + double_angle theta() const; + + point rotate(const double_angle &theta) const; + //!< Rotates the point by the angle "theta". + /*!< This rotates the position of the point around the origin in the + trigonometric standard manner; zero degrees is at the right, increasing + degree angles are counterclockwise from the x axis to the y to the + -x to the -y .... */ + + numeric_type distance(const point &p2) const; + //!< Returns the distance between `this' and the second point `p2'. + + point operator - () const { return point(-_x, -_y); } + //!< return the additive inverse of the vector + + numeric_type magnitude() const; + //!< return the distance from the origin to this point. + + point operator + (const point &arg2) const; + point operator - (const point &arg2) const; + point &operator += (const point &arg2); + point &operator -= (const point &arg2); + bool operator == (const point &arg2) const; + + basis::astring text_form() const; + //!< Prints out the two values (x and y) held in the point. + + bool from_text(const basis::astring &text); + //!< returns true if the "text" is successfully pulled into this point. + + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); + int packed_size() const; + +private: + numeric_type _x; + numeric_type _y; +}; + +////////////// + +// implementations below... + +// notes: +// +// - there is an odd breaking up of the expressions where we're taking a +// square root because ms visual studio 7 has a bug of some sort that +// convinces it that angle is being used in there, although it's not. +// these lines use a temporary named "sumsquar" to deconfuse the compiler. + +template +point::point(numeric_type x, numeric_type y) { set(x, y); } + +template +point::point(numeric_type r, double_angle theta) +{ set(r, theta); } + +template +basis::astring point::text_form() const +{ + numeric_type temp = 0; + basis::astring specifier(numeric_specifier(temp)); + basis::astring sprintf_template(basis::astring::SPRINTF, "(%s, %s)", specifier.s(), specifier.s()); + return basis::astring(basis::astring::SPRINTF, sprintf_template.s(), x(), y()); +} + +template +void point::set(numeric_type x, numeric_type y) +{ _x = x; _y = y; } + +template +numeric_type point::r() const +{ + const double sumsquar = square(x()) + square(y()); + return numeric_type(sqrt(sumsquar)); +} + +template +void point::set(numeric_type r, double_angle theta) +{ set(numeric_type(r * theta.cosine()), numeric_type(r * theta.sine())); } + +template +numeric_type point::distance(const point &p2) const +{ + const double sumsquar = square(p2.x() - x()) + square(p2.y() - y()); + return numeric_type(sqrt(sumsquar)); +} + +template +double_angle point::theta() const +{ + basis::outcome retval; + return double_angle::arctangent(y(), x(), retval); +} + +template +int point::packed_size() const +{ + basis::byte_array temp; +//hmmm: inefficient! + pack(temp); + return temp.length(); +} + +template +void point::pack(basis::byte_array &packed_form) const +{ + structures::attach(packed_form, _x); + structures::attach(packed_form, _y); +} + +template +bool point::unpack(basis::byte_array &packed_form) +{ + if (!structures::detach(packed_form, _x)) return false; + if (!structures::detach(packed_form, _y)) return false; + return true; +} + +template +numeric_type point::magnitude() const +{ + const double sumsquar = square(x()) + square(y()); + return numeric_type(sqrt(sumsquar)); +} + +template +point point::operator + (const point &arg2) const +{ return point(x() + arg2.x(), y() + arg2.y()); } + +template +point point::operator - (const point &arg2) const +{ return point(x() - arg2.x(), y() - arg2.y()); } + +template +point &point::operator += (const point &arg2) +{ _x += arg2.x(); _y += arg2.y(); return *this; } + +template +point &point::operator -= (const point &arg2) +{ _x -= arg2.x(); _y -= arg2.y(); return *this; } + +template +bool point::operator == (const point &arg2) const +{ +// this bit should be part of the floating point stuff... + double epsilon = 1e-10; + return (absolute_value(x() - arg2.x()) <= epsilon) + && (absolute_value(y() - arg2.y()) <= epsilon); +} + +template +point point::rotate + (const double_angle &theta) const +{ + numeric_type tempX = x(); + numeric_type tempY = y(); + numeric_type temp1 = numeric_type(tempX * theta.cosine() + - tempY * theta.sine()); + numeric_type temp2 = numeric_type(tempX * theta.sine() + + tempY * theta.cosine()); + return point(temp1, temp2); +} + +template +bool point::from_text(const basis::astring &_text) +{ + numeric_type x = 0, y = 0; + basis::astring text(_text); + // chop junk off the front. + text = crop_non_numeric(text); + // scan the string for values. + x = text.convert(x); + // remove the number. + text = crop_numeric(text); + // chop off more junk. + text = crop_non_numeric(text); + // get the next number. + y = text.convert(y); + set(x, y); + return true; +} + +} // namespace. + +#endif + diff --git a/graphiq/library/geometric/polygon.cpp b/graphiq/library/geometric/polygon.cpp new file mode 100644 index 00000000..d1d40056 --- /dev/null +++ b/graphiq/library/geometric/polygon.cpp @@ -0,0 +1,50 @@ + + + +/*****************************************************************************\ +* * +* Name : polygon * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "polygon.h" + +#include + +using namespace basis; + +namespace geometric { + +bool polygon::inside(const cartesian_point &to_check) +{ + int right_intersect_count = 0; + for (int i = 0; i < length(); i++) { + cartesian_point vert_1 = get(i); + cartesian_point vert_2 = get( (i + 1) % length() ); + if ( (to_check.y() < minimum(vert_1.y(), vert_2.y())) + || (to_check.y() > maximum(vert_1.y(), vert_2.y())) ) continue; + double x_intersect; + if (vert_2.x() == vert_1.x()) { + x_intersect = vert_2.x(); + } else { + double m = (vert_2.y() - vert_1.y()) / (vert_2.x() - vert_1.x()); + x_intersect = 1.0 / m * (to_check.y() - vert_1.y() + m * vert_1.x()); + } + if (x_intersect > to_check.x()) right_intersect_count++; + } + return !!(right_intersect_count % 2); +} + +} + + + + diff --git a/graphiq/library/geometric/polygon.h b/graphiq/library/geometric/polygon.h new file mode 100644 index 00000000..8553ead1 --- /dev/null +++ b/graphiq/library/geometric/polygon.h @@ -0,0 +1,60 @@ +#ifndef POLYGON_CLASS +#define POLYGON_CLASS + +/*****************************************************************************\ +* * +* Name : polygon * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! Represents a multi-sided geometric figure made of line segments. +/*! + The polygon is a list of points that are assumed to be connected. It will + have as many sides as its point count minus one. Thus there is no valid + polygon with less than three points. A function that tests whether a point + is inside or outside the polygon is provided. +*/ + +#include "cartesian_objects.h" + +#include + +//hmmm: it might be nice to structuralize this. +//hmmm: also deriving from an array of points is not so great. + +namespace geometric { + +class polygon : public basis::array +{ +public: + polygon() {} + ~polygon() {} + + void add(const cartesian_point &to_add); + //!< adds a new point to the list that represents the polygon's sides. + + int points() const { return length(); } + int sides() const { return points() - 1; } + + cartesian_point &operator [] (int index); + //!< retrieves the index-th point in the list. + /*!< this is valid for indices between zero and points() - 1. */ + + bool inside(const cartesian_point &to_check); + //!< Returns true if the point "to_check" is inside of this polygon. + /*!< This function assumes that the polygon is closed when performing + the check. */ +}; + +} // namespace. + +#endif + diff --git a/graphiq/library/geometric/rectangle.h b/graphiq/library/geometric/rectangle.h new file mode 100644 index 00000000..9463652b --- /dev/null +++ b/graphiq/library/geometric/rectangle.h @@ -0,0 +1,379 @@ +#ifndef RECTANGLE_CLASS +#define RECTANGLE_CLASS + +/*****************************************************************************\ +* * +* Name : rectangle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "point.h" + +#include + +namespace geometric { + +//! Represents a geometric rectangle. + +template +class rectangle : public basis::packable +{ +public: + rectangle(const point &vertex_1, + const point &vertex_2); + rectangle(numeric_type x_1 = 0, numeric_type y_1 = 0, + numeric_type x_2 = 0, numeric_type y_2 = 0); + + numeric_type height() const; + numeric_type width() const; + + rectangle order() const; + //!< Re-orders the vertices of the line to be increasing. + /*!< Orients the vertices such that the x and y coordinates of the first + vertex are respectively closer to the origin than the x and y + coordinates of the second vertex (or they are equidistant). */ + + point top_left() const; + point bottom_left() const; + point top_right() const; + point bottom_right() const; + //!< returns the appropriate point as represented by our rectangle. + /*!< note that these are with respect to a normal cartesian coordinate + system. if you want points for a screen based coordinate system (with + the origin in the top left), then bottom_left and top_right return the + appropriate bounding points for that rectangle. */ + + numeric_type minimum_x() const; + //!< Return the smallest x from the points in the rectangle. + numeric_type minimum_y() const; + //!< Return the smallest y from the points in the rectangle. + numeric_type maximum_x() const; + //!< Return the largest x from the points in the rectangle. + numeric_type maximum_y() const; + //!< Return the largest y from the points in the rectangle. + + point center() const; + //!< Returns the point at the center of the rectangle. + + bool inside(const point &to_check) const; + //!< Returns true if `to_check' is inside `this' rectangle. + + bool operator == (const rectangle &to_compare) const; + //!< Returns true if `to_compare' has vertices equal to `this'. + + rectangle operator + (const point &to_add) const; + //!< Returns the rectangle resulting from adding a point to its vertices. + rectangle operator - (const point &to_subtract) const; + //!< Returns the rectangle resulting from subtracting "to_subtract". + + rectangle &operator += (const point &to_add); + //!< Adds the point "to_add" to our vertices. + rectangle &operator -= (const point &to_subtract); + //!< Subtracts the point "to_add" to our vertices. + + void encompass(const rectangle &to_adjust_to); + //!< Finds the largest dimension needed to contain all rectangles passed in. + /*!< The original dimension of `this' rectangle is compared with + all subsequent rectangles passed to adjust_dimension, and it is + modified (joined with `to_adjust_to') if the extent of `to_adjust_to' + is greater or lesser than the current extent of `this' rectangle. */ + + bool intersect(const rectangle &r2) const; + //!< Returns true if `this' & `r2' cover any common points. + + bool disjoint(const rectangle &r2) const; + //!< Returns true if `this' & `r2' have mutually exclusive extents. + + bool join_intersecting(const rectangle &r2, rectangle &result); + //!< Sets "result" to encompass this and "r2" if they intersect. + /*!< If `this' and `r2' intersect, `result' is adjusted to their dimension + and true is returned. If not, false is returned and `result' is + undefined. */ + + bool intersection(const rectangle &r2, rectangle &result); + //!< Sets "result" to the intersection of this and "r2". + /*!< If `this' and `r2' intersect, then `result' is set to their + intersecting extent and true is returned. If not, then false is returned + and `result' is undefined. */ + + basis::astring text_form() const; + //!< Prints out the contents of the rectangle. + + bool from_text(const basis::astring &text); + //!< Returns true if the "text" is parsed into this rectangle. + + point vertex_1() const; + point vertex_2() const; + + void vertex_1(const point &to_set); + void vertex_2(const point &to_set); + + virtual int packed_size() const; + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); + +protected: + point _vertex_1; + point _vertex_2; +}; + +////////////// + +//!< A commonly used rectangle of integers. + +typedef rectangle int_rectangle; + +////////////// + +// implementations below... + +template +rectangle::rectangle(const point &lb, const point &rt) +: _vertex_1(lb), _vertex_2(rt) {} + +template +rectangle::rectangle(numeric_type left, numeric_type bottom, numeric_type right, numeric_type top) +: _vertex_1(point(left, bottom)), + _vertex_2(point(right, top)) {} + +template +point rectangle::vertex_1() const +{ return _vertex_1; } + +template +point rectangle::vertex_2() const +{ return _vertex_2; } + +template +void rectangle::vertex_1(const point &to_set) +{ _vertex_1 = to_set; } + +template +void rectangle::vertex_2(const point &to_set) +{ _vertex_2 = to_set; } + +template +numeric_type rectangle::height() const +{ return absolute_value(_vertex_2.y() - _vertex_1.y()); } + +template +numeric_type rectangle::width() const +{ return absolute_value(_vertex_2.x() - _vertex_1.x()); } + +template +numeric_type rectangle::minimum_x() const +{ return minimum(_vertex_1.x(), _vertex_2.x()); } + +template +numeric_type rectangle::minimum_y() const +{ return minimum(_vertex_1.y(), _vertex_2.y()); } + +template +numeric_type rectangle::maximum_x() const +{ return maximum(_vertex_1.x(), _vertex_2.x()); } + +template +numeric_type rectangle::maximum_y() const +{ return maximum(_vertex_1.y(), _vertex_2.y()); } + +template +rectangle rectangle::order() const +{ + numeric_type x1 = _vertex_1.x(); + numeric_type x2 = _vertex_2.x(); + numeric_type y1 = _vertex_1.y(); + numeric_type y2 = _vertex_2.y(); + flip_increasing(x1, x2); + flip_increasing(y1, y2); + return rectangle(x1, y1, x2, y2); +} + +template +point rectangle::top_left() const +{ + rectangle temp(order()); + return point(temp.vertex_1().x(), temp.vertex_2().y()); +} + +template +point rectangle::bottom_left() const +{ + rectangle temp(order()); + return point(temp.vertex_1().x(), temp.vertex_1().y()); +} + +template +point rectangle::top_right() const +{ + rectangle temp(order()); + return point(temp.vertex_2().x(), temp.vertex_2().y()); +} + +template +point rectangle::bottom_right() const +{ + rectangle temp(order()); + return point(temp.vertex_2().x(), temp.vertex_1().y()); +} + +template +point rectangle::center() const +{ + return point(numeric_type((_vertex_1.x() + + _vertex_2.x()) / 2.0), numeric_type((_vertex_1.y() + + _vertex_2.y()) / 2.0)); +} + +template +bool rectangle::inside(const point &to_check) const +{ + rectangle ordered_me = this->order(); + return bool( (to_check.x() >= ordered_me._vertex_1.x()) + && (to_check.x() <= ordered_me._vertex_2.x()) + && (to_check.y() >= ordered_me._vertex_1.y()) + && (to_check.y() <= ordered_me._vertex_2.y()) ); +} + +template +bool rectangle::operator == (const rectangle &to_compare) const +{ + point min1(minimum_x(), minimum_y()); + point max1(maximum_x(), maximum_y()); + point min2(to_compare.minimum_x(), to_compare.minimum_y()); + point max2(to_compare.maximum_x(), to_compare.maximum_y()); + if ( (min1 == min2) && (max1 == max2) ) return true; + else return false; +} + +template +rectangle &rectangle::operator += (const point &p) +{ _vertex_1 += p; _vertex_2 += p; return *this; } + +template +rectangle &rectangle::operator -= (const point &p) +{ _vertex_1 -= p; _vertex_2 -= p; return *this; } + +template +rectangle rectangle::operator + (const point &p) const +{ + rectangle to_return(*this); + to_return += p; + return to_return; +} + +template +int rectangle::packed_size() const +{ + basis::byte_array temp; +//hmmm: inefficient! + pack(temp); + return temp.length(); +} + +template +void rectangle::pack(basis::byte_array &packed_form) const +{ + _vertex_1.pack(packed_form); + _vertex_2.pack(packed_form); +} + +template +bool rectangle::unpack(basis::byte_array &packed_form) +{ + if (!_vertex_1.unpack(packed_form)) return false; + if (!_vertex_2.unpack(packed_form)) return false; + return true; +} + +template +rectangle rectangle::operator - (const point &p) const +{ + rectangle to_return(*this); + to_return -= p; + return to_return; +} + +template +void rectangle::encompass(const rectangle &to_adjust_to) +{ + if (to_adjust_to._vertex_1.x() < _vertex_1.x()) + _vertex_1.set(to_adjust_to._vertex_1.x(), _vertex_1.y()); + if (to_adjust_to._vertex_1.y() < _vertex_1.y()) + _vertex_1.set(_vertex_1.x(), to_adjust_to._vertex_1.y()); + if (to_adjust_to._vertex_2.x() > _vertex_2.x()) + _vertex_2.set(to_adjust_to._vertex_2.x(), _vertex_2.y()); + if (to_adjust_to._vertex_2.y() > _vertex_2.y()) + _vertex_2.set(_vertex_2.x(), to_adjust_to._vertex_2.y()); +} + +template +bool rectangle::disjoint(const rectangle &r2) const +{ + if ( (maximum_x() < r2.minimum_x()) + || (minimum_x() > r2.maximum_x()) + || (maximum_y() < r2.minimum_y()) + || (minimum_y() > r2.maximum_y()) ) return true; + else return false; +} + +template +bool rectangle::intersect(const rectangle &r2) const +{ return bool(!disjoint(r2)); } + +template +bool rectangle::join_intersecting(const rectangle &r2, rectangle &result) +{ + if (disjoint(r2)) return false; + result = *this; + result.encompass(r2); + return true; +} + +template +bool rectangle::intersection(const rectangle &r2, rectangle &result) +{ + if (disjoint(r2)) return false; + result = rectangle(maximum(minimum_x(), r2.minimum_x()), + maximum(minimum_y(), r2.minimum_y()), + minimum(maximum_x(), r2.maximum_x()), + minimum(maximum_y(), r2.maximum_y())); + return true; +} + +template +basis::astring rectangle::text_form() const +{ + return basis::astring("[") + _vertex_1.text_form() + basis::astring(" ") + + _vertex_2.text_form() + basis::astring("]"); +} + +template +bool rectangle::from_text(const basis::astring &_text) +{ + numeric_type nums[4] = { 0, 0, 0, 0 }; + // setup the scanning specifier. + basis::astring spec(numeric_specifier(nums[0])); + // scan the string for values. + basis::astring text(_text); + for (int i = 0; i < 4; i++) { + text = crop_non_numeric(text); + nums[i] = text.convert(nums[i]); + text = crop_numeric(text); + } + vertex_1(point(nums[0], nums[1])); + vertex_2(point(nums[2], nums[3])); + return true; +} + +} // namespace. + +#endif + diff --git a/graphiq/library/geometric/screen_rectangle.cpp b/graphiq/library/geometric/screen_rectangle.cpp new file mode 100644 index 00000000..f8732d33 --- /dev/null +++ b/graphiq/library/geometric/screen_rectangle.cpp @@ -0,0 +1,83 @@ + + + +/*****************************************************************************\ +* * +* Name : screen_rectangle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "rectangle.h" +#include "screen_rectangle.h" + +#include + +#include + +using namespace basis; + +namespace geometric { + +SAFE_STATIC_CONST(screen_point, screen_origin, (0, 0)) + +////////////// + +#ifdef __WIN32__ +screen_point::screen_point(const tagPOINT &original) : point(original.x, original.y) {} + +screen_point::operator tagPOINT() +{ POINT to_return; to_return.x = x(); to_return.y = y(); return to_return; } +#endif + +////////////// + +screen_rectangle::screen_rectangle(const rectangle &init) +: rectangle(init) {} + +screen_rectangle::screen_rectangle(const screen_point &vertex_1, + const screen_point &vertex_2) +: rectangle(vertex_1, vertex_2) {} + +screen_rectangle::screen_rectangle(int x_1, int y_1, int x_2, int y_2) +: rectangle(x_1, y_1, x_2, y_2) {} + +screen_rectangle screen_rectangle::order() const +{ return screen_rectangle(top_left(), bottom_right()); } + +screen_point screen_rectangle::top_left() const +{ return rectangle::bottom_left(); } + +screen_point screen_rectangle::bottom_left() const +{ return rectangle::top_left(); } + +screen_point screen_rectangle::top_right() const +{ return rectangle::bottom_right(); } + +screen_point screen_rectangle::bottom_right() const +{ return rectangle::top_right(); } + +#ifdef __WIN32__ +screen_rectangle::screen_rectangle(const tagRECT &original) +: rectangle(original.left, original.top, original.right, original.bottom) +{} + +screen_rectangle::operator tagRECT() const +{ + RECT to_return; to_return.left = left(); + to_return.top = top(); to_return.right = right(); + to_return.bottom = bottom(); return to_return; +} +#endif + +} // namespace. + + + diff --git a/graphiq/library/geometric/screen_rectangle.h b/graphiq/library/geometric/screen_rectangle.h new file mode 100644 index 00000000..df6ab72d --- /dev/null +++ b/graphiq/library/geometric/screen_rectangle.h @@ -0,0 +1,95 @@ +#ifndef SCREEN_RECTANGLE_CLASS +#define SCREEN_RECTANGLE_CLASS + +/*****************************************************************************\ +* * +* Name : screen_rectangle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +#include "rectangle.h" + +#ifdef __WIN32__ + // forward. + struct tagPOINT; struct tagRECT; +#endif + +namespace geometric { + +// forward. +class double_angle; + +//! a simple class used to describe points on a graphics screen. + +class screen_point : public point +{ +public: + screen_point(int x = 0, int y = 0) : point(x, y) {} + screen_point(int r, double_angle theta) : point(r, theta) {} + screen_point(const point &original) : point(original) {} + DEFINE_CLASS_NAME("screen_point"); + +#ifdef __WIN32__ + screen_point(const tagPOINT &original); + //!< helpful conversions from basic ms-windows type. + operator tagPOINT(); + //!< helpful conversions to basic ms-windows type. +#endif +}; + +const screen_point &screen_origin(); + //!< the origin of the screen coordinate system (which is top-left here). + +////////////// + +//! Represents a rectangle as interpreted on display screens. +/*! + The origin is the top-left corner of the rectangle and the y coordinate + gets larger as one goes downwards. This class is primarily useful in + conjunction with a windowing environment. +*/ + +class screen_rectangle : public rectangle +{ +public: + screen_rectangle(const screen_point &vertex_1, const screen_point &vertex_2); + screen_rectangle(int x_1 = 0, int y_1 = 0, int x_2 = 0, int y_2 = 0); + screen_rectangle(const rectangle &init); + + screen_rectangle order() const; + //!< Re-orders the vertices to match expectations. + /*!< This is just like rectangle::order() except that the first vertex + will be closest to the top-left of the screen. */ + + screen_point top_left() const; + screen_point bottom_left() const; + screen_point top_right() const; + screen_point bottom_right() const; + + int left() const { return top_left().x(); } + int top() const { return top_left().y(); } + int right() const { return bottom_right().x(); } + int bottom() const { return bottom_right().y(); } + +#ifdef __WIN32__ + screen_rectangle(const tagRECT &original); + //!< helpful conversion from basic ms-windows type. + operator tagRECT() const; + //!< helpful conversion to basic ms-windows type. +#endif +}; + +} // namespace. + +#endif + diff --git a/graphiq/library/geometric/triangle.cpp b/graphiq/library/geometric/triangle.cpp new file mode 100644 index 00000000..4597d6a6 --- /dev/null +++ b/graphiq/library/geometric/triangle.cpp @@ -0,0 +1,112 @@ + + + +/*****************************************************************************\ +* * +* Name : triangle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "cartesian_objects.h" +#include "line.h" +#include "rectangle.h" +#include "triangle.h" + +namespace geometric { + +triangle::triangle() +: _vertex_1(cartesian_point::origin()), + _vertex_2(cartesian_point::origin()), + _vertex_3(cartesian_point::origin()) +{} + +triangle::triangle(const cartesian_point &vertex_1, + const cartesian_point &vertex_2, const cartesian_point &vertex_3) +: _vertex_1(vertex_1), + _vertex_2(vertex_2), + _vertex_3(vertex_3) +{} + +triangle::triangle(const triangle &to_copy) +: _vertex_1(to_copy._vertex_1), + _vertex_2(to_copy._vertex_2), + _vertex_3(to_copy._vertex_3) +{} + +triangle::~triangle() {} + +triangle &triangle::operator =(const triangle &to_copy) +{ + if (this == &to_copy) return *this; + _vertex_1 = to_copy._vertex_1; + _vertex_2 = to_copy._vertex_2; + _vertex_3 = to_copy._vertex_3; + return *this; +} + +line triangle::side_1_2() const +{ return line(_vertex_1, _vertex_2); } + +line triangle::side_2_3() const +{ return line(_vertex_2, _vertex_3); } + +line triangle::side_3_1() const +{ return line(_vertex_3, _vertex_1); } + +cartesian_point triangle::vertex_1() const { return _vertex_1; } + +cartesian_point triangle::vertex_2() const { return _vertex_2; } + +cartesian_point triangle::vertex_3() const { return _vertex_3; } + +void triangle::vertex_1(const cartesian_point &to_set) { _vertex_1 = to_set; } + +void triangle::vertex_2(const cartesian_point &to_set) { _vertex_2 = to_set; } + +void triangle::vertex_3(const cartesian_point &to_set) { _vertex_3 = to_set; } + +bool triangle::inside(const cartesian_point &where) const +{ +//cerr << "triangle::inside: not implemented" << endl << flush; +if (where.x()) where.y(); // bogus. + return false; +} + +double triangle::area() const +{ +//cerr << "triangle::area: not implemented" << endl << flush; + return 5; +} + +} // namespace. + +/* +//temp +#include "warper.h" +using namespace geometric; +typedef rectangle_warper chuzzo; +chuzzo beanburp = chuzzo(rectangle(0, 23, 39, 1012), + rectangle(8, 19, 92982, -2), chuzzo::BOTTOM_RIGHT, + chuzzo::TOP_LEFT); +typedef rectangle_warper::horizontal_component horzo; +typedef rectangle_warper::vertical_component verzo; +int frunk() { + horzo peen; + beanburp.separate_horizontal(chuzzo::BOTTOM_RIGHT, peen); + verzo neep; + beanburp.separate_vertical(chuzzo::TOP_RIGHT, neep); +} +*/ + + + + + diff --git a/graphiq/library/geometric/triangle.h b/graphiq/library/geometric/triangle.h new file mode 100644 index 00000000..bf1e5cbb --- /dev/null +++ b/graphiq/library/geometric/triangle.h @@ -0,0 +1,64 @@ +#ifndef TRIANGLE_CLASS +#define TRIANGLE_CLASS + +/*****************************************************************************\ +* * +* Name : triangle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + + + +// forward. +class cartesian_line; +class cartesian_point; + +namespace geometric { + +//! Represents a geometric triangle. + +class triangle +{ +public: + triangle(); + triangle(const cartesian_point &vertex1, const cartesian_point &vertex2, + const cartesian_point &vertex3); + triangle(const triangle &to_copy); + ~triangle(); + + triangle &operator =(const triangle &to_copy); + + bool inside(const cartesian_point &where) const; + + double area() const; + + line side_1_2() const; + line side_2_3() const; + line side_3_1() const; + + cartesian_point vertex_1() const; + cartesian_point vertex_2() const; + cartesian_point vertex_3() const; + + void vertex_1(const cartesian_point &to_set); + void vertex_2(const cartesian_point &to_set); + void vertex_3(const cartesian_point &to_set); + +protected: + cartesian_point _vertex_1; + cartesian_point _vertex_2; + cartesian_point _vertex_3; +}; + +} // namespace. + +#endif + diff --git a/graphiq/library/geometric/warper.h b/graphiq/library/geometric/warper.h new file mode 100644 index 00000000..dd173bbb --- /dev/null +++ b/graphiq/library/geometric/warper.h @@ -0,0 +1,291 @@ +#ifndef RECTANGLE_WARPER_CLASS +#define RECTANGLE_WARPER_CLASS + +/*****************************************************************************\ +* * +* Name : rectangle_warper * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1992-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! Warps points in one frame of reference to a different one. +/*! + This class encapsulates the notion of a rectangular region that is + referred to from two different points of view. This relates two + two-dimensional frames of reference to each other. Each frame of reference + is specified by two rectangles. A point that is measured in one frame of + reference can be transformed into a point that is measured in the other, + and vice-versa. +*/ + +#include "rectangle.h" + +#include + +namespace geometric { + +template +class rectangle_warper +{ +public: + //! describes where a rectangle's origin is located on the rectangle. + /*! our model is to consider the first vertex point of the rectangle as its + origin and the second vertex point (diagonally opposite the first point) as + its extent. since it may make sense for that first vertex point to be + located at any one of the vertices of the rectangle (as in windowing + coordinate system conversions), the enumeration below allows any one of the + rectangle's vertices to be chosen as its origin. */ + enum origin_vertex { BOTTOM_LEFT, TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT }; + + rectangle_warper(const rectangle &system_1, + const rectangle &system_2, + origin_vertex system_1_origin = BOTTOM_LEFT, + origin_vertex system_2_origin = BOTTOM_LEFT); + //!< constructs a warper given the two reference systems. + /*!< constructs a warper where the first rectangular system is in + "system_1", the second system is in "system_2" and the respective origins + for these systems are in "system_1_origin" and "system_2_origin". */ + + ~rectangle_warper(); + + point to_system_1(const point &in_system_2) const; + //!< Converts from the second system into the first. + /*!< This returns a point that is measured in the first frame of reference + when given a point "in_system_2" that is measured in the second frame of + reference. */ + + point to_system_2(const point &in_system_1) const; + //!< Converts from the first system into the second. + /*!< This returns a point that is measured in the second frame of reference + when given a point "in_system_1" that is measured in the first frame of + reference. */ + + rectangle to_system_1 + (const rectangle &in_system_2) const; + //!< flips a rectangle from the second system into the first. + rectangle to_system_2 + (const rectangle &in_system_1) const; + //!< flips a rectangle from the first system into the second. + + rectangle system_1() const { return _system_1; } + rectangle system_2() const { return _system_2; } + origin_vertex origin_1() const { return _vert_1; } + origin_vertex origin_2() const { return _vert_2; } + + void system_1(const rectangle &to_set, + origin_vertex origin_corner = BOTTOM_LEFT); + void system_2(const rectangle &to_set, + origin_vertex origin_corner = BOTTOM_LEFT); + + basis::astring text_form() const; + //!< Prints out the two systems held in the rectangle_warper. + + basis::astring vertex_name(origin_vertex v) const; + //!< Prints out the name of the vertex location. + + enum vertical_component { RW_BOTTOM, RW_TOP }; + enum horizontal_component { RW_LEFT, RW_RIGHT }; + + void separate_vertical(origin_vertex v, vertical_component &to_set) const; + void separate_horizontal(origin_vertex v, horizontal_component &to_set) const; + //!< separates out a component of the placement of the vertex. + +private: + rectangle _system_1; + rectangle _system_2; + origin_vertex _vert_1; + origin_vertex _vert_2; + + point scale_point(const rectangle &source, + const rectangle &target, + origin_vertex v1, origin_vertex v2, + const point &old) const; + rectangle scale_rectangle(const rectangle &source, + const rectangle &target, + origin_vertex v1, origin_vertex v2, + const rectangle &old) const; + rectangle flip_accordingly + (const rectangle &to_flip, origin_vertex to_flip_origin, + origin_vertex target_origin) const; + //!< Flips the points in "to_flip" to match the "target_origin". + /*!< swaps the points contained in a rectangle that uses a particular point + as the vertex ("to_flip_origin") so that the points are arranged + according to a second choice of vertex ("target_origin"). */ +}; + +////////////// + +// implementations for longer methods below... + +template +rectangle_warper::rectangle_warper + (const rectangle &system_1, + const rectangle &system_2, + origin_vertex v1, origin_vertex v2) +: _system_1(system_1), _system_2(system_2), _vert_1(v1), _vert_2(v2) +{} + +template +rectangle_warper::~rectangle_warper() {} + +template +void rectangle_warper::system_1 + (const rectangle &to_set, origin_vertex v) +{ _system_1 = to_set; _vert_1 = v; } + +template +void rectangle_warper::system_2 + (const rectangle &to_set, origin_vertex v) +{ _system_2 = to_set; _vert_2 = v; } + +template +point rectangle_warper::to_system_1 + (const point &in_system_2) const +{ return scale_point(_system_2, _system_1, _vert_2, _vert_1, in_system_2); } + +template +point rectangle_warper::to_system_2 + (const point &in_system_1) const +{ return scale_point(_system_1, _system_2, _vert_1, _vert_2, in_system_1); } + +template +rectangle rectangle_warper::to_system_1 + (const rectangle &in_system_2) const +{ + return scale_rectangle(_system_2, _system_1, _vert_2, _vert_1, + in_system_2); +} + +template +rectangle rectangle_warper::to_system_2 + (const rectangle &in_system_1) const +{ + return scale_rectangle(_system_1, _system_2, _vert_1, _vert_2, + in_system_1); +} + +template +void rectangle_warper::separate_vertical + (origin_vertex v, vertical_component &to_set) const +{ + if ( (v == BOTTOM_LEFT) || (v == BOTTOM_RIGHT) ) to_set = RW_BOTTOM; + to_set = RW_TOP; +} + +template +void rectangle_warper::separate_horizontal + (origin_vertex v, horizontal_component &to_set) const +{ + if ( (v == BOTTOM_LEFT) || (v == TOP_LEFT) ) to_set = RW_LEFT; + to_set = RW_RIGHT; +} + +template +rectangle rectangle_warper::flip_accordingly + (const rectangle &to_flip, origin_vertex flipo, + origin_vertex targo) const +{ +//LOG(basis::astring("flipping ") + to_flip.text_form() + " from " + flipo.text_form() + " to " + targo.text_form()); + if (flipo == targo) return to_flip; + numeric_type x1(to_flip.vertex_1().x()); + numeric_type y1(to_flip.vertex_1().y()); + numeric_type x2(to_flip.vertex_2().x()); + numeric_type y2(to_flip.vertex_2().y()); + horizontal_component horiz1; + separate_horizontal(flipo, horiz1); + horizontal_component horiz2; + separate_horizontal(targo, horiz2); + bool flip_x = bool(horiz1 != horiz2); + vertical_component vert1; + separate_vertical(flipo, vert1); + vertical_component vert2; + separate_vertical(targo, vert2); + bool flip_y = bool(vert1 != vert2); + if (flip_x) swap_values(x1, x2); + if (flip_y) swap_values(y1, y2); +//LOG(basis::astring("it becomes ") + rectangle(x1, y1, x2, y2).text_form()); + return rectangle(x1, y1, x2, y2); +} + +template +rectangle rectangle_warper::scale_rectangle + (const rectangle &source, + const rectangle &target, origin_vertex source_origin, + origin_vertex target_origin, const rectangle &old) const +{ + rectangle s = rectangle + (flip_accordingly(source, source_origin, BOTTOM_LEFT)); + numeric_type width_source = s.vertex_2().x() - s.vertex_1().x(); + numeric_type height_source = s.vertex_2().y() - s.vertex_1().y(); + if ( !width_source || !height_source ) { +// cerr << "degenerate rectangle in rectangle_warper::scaler: " << s +// << endl << flush; + return old; + } + rectangle t(flip_accordingly(target, target_origin, BOTTOM_LEFT)); + numeric_type width_target = t.vertex_2().x() - t.vertex_1().x(); + numeric_type height_target = t.vertex_2().y() - t.vertex_1().y(); + numeric_type x_scale = width_target / width_source; + numeric_type y_scale = height_target / height_source; + +//LOG(basis::astring("scaler: source ") + source.text_form() + " with vert " + source_origin.text_form() + " becomes " + s + " target " + target + " with vert " + target_origin + " becomes " + t + "."); + + rectangle o(flip_accordingly(old, source_origin, BOTTOM_LEFT)); + + rectangle to_return = flip_accordingly(rectangle + ((o.vertex_1().x() - s.vertex_1().x()) * x_scale + t.vertex_1().x(), + (o.vertex_1().y() - s.vertex_1().y()) * y_scale + t.vertex_1().y(), + (o.vertex_2().x() - s.vertex_1().x()) * x_scale + t.vertex_1().x(), + (o.vertex_2().y() - s.vertex_1().y()) * y_scale + t.vertex_1().y()), + BOTTOM_LEFT, target_origin); + +// LOG(basis::astring("old ") + old.text_form() + " with source vert becomes " + o.text_form() + " and then is moved into " + to_return.text_form()); + + return to_return; +} + +template +point rectangle_warper::scale_point + (const rectangle &source, const rectangle &target, + origin_vertex source_origin, origin_vertex target_origin, + const point &old) const +{ + // gross but simple. + return scale_rectangle(source, target, source_origin, target_origin, + rectangle(old, old)).vertex_1(); +} + +template +basis::astring rectangle_warper::vertex_name(origin_vertex v) const +{ + basis::astring name("unknown"); + switch (v) { + case BOTTOM_LEFT: name = "bottom-left"; break; + case BOTTOM_RIGHT: name = "bottom-right"; break; + case TOP_LEFT: name = "top-left"; break; + case TOP_RIGHT: name = "top-right"; break; + } + return name; +} + +template +basis::astring rectangle_warper::text_form() const +{ + return basis::astring(""); +} + +} // namespace. + +#endif + diff --git a/graphiq/library/makefile b/graphiq/library/makefile new file mode 100644 index 00000000..39f2fd14 --- /dev/null +++ b/graphiq/library/makefile @@ -0,0 +1,9 @@ +include variables.def + +PROJECT = graphical_libraries +BUILD_BEFORE = geometric \ + user_interface \ + tests_geometric + +include rules.def + diff --git a/graphiq/library/tests_geometric/makefile b/graphiq/library/tests_geometric/makefile new file mode 100644 index 00000000..c8136624 --- /dev/null +++ b/graphiq/library/tests_geometric/makefile @@ -0,0 +1,11 @@ +include cpp/variables.def + +PROJECT = test_geometric +TYPE = test +TARGETS = test_angle.exe test_ellipse.exe test_geometry.exe test_point.exe test_warper.exe +LOCAL_LIBS_USED = unit_test application filesystem timely loggers configuration textual \ + structures geometric basis +RUN_TARGETS = $(ACTUAL_TARGETS) + +include cpp/rules.def + diff --git a/graphiq/library/tests_geometric/test_angle.cpp b/graphiq/library/tests_geometric/test_angle.cpp new file mode 100644 index 00000000..b4bbf160 --- /dev/null +++ b/graphiq/library/tests_geometric/test_angle.cpp @@ -0,0 +1,77 @@ +/* +* Name : test_angle * +* Author : Chris Koeritz * +* Purpose: * +* Tests the angle class. * +** +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace geometric; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace unit_test; + +typedef double_plus floot; + +class test_angle : public virtual unit_base, public virtual application_shell +{ +public: + test_angle() : application_shell() {} + DEFINE_CLASS_NAME("test_angle"); + virtual int execute(); +}; + +int test_angle::execute() +{ + FUNCDEF("execute"); + { + // first test group: double angle inverse trigonometrics. + angle a(30.3, DEGREES); + ASSERT_EQUAL(floot(a.get(RADIANS)), floot(.528835), "radian conversion should be right"); + + outcome retval; + angle at = angle::arctangent(28.3, 29.5, retval); + ASSERT_EQUAL(floot(at.get(DEGREES)), floot(43.8106), "atan should be what we expect"); + angle as = angle::arcsine(17.6, 82.3, retval); + ASSERT_EQUAL(floot(as.get(DEGREES)), floot(12.3482), "asin should be what we expect"); + angle ac = angle::arccosine(17.2, 42.0, retval); + ASSERT_EQUAL(floot(ac.get(DEGREES)), floot(65.8251), "acos should be what we expect"); + } + { + // second test: packing an angle. + angle q(128, DEGREES); + byte_array pacd; + int siz = q.packed_size(); + q.pack(pacd); + ASSERT_EQUAL(siz, pacd.length(), "packed size should report proper length"); + angle x; + x.unpack(pacd); + ASSERT_EQUAL(floot(q.get(RADIANS)), floot(x.get(RADIANS)), + "unpacking should return original value"); + ASSERT_FALSE(pacd.length(), "unpacking should consume entire array"); + } + + return final_report(); +} + +////////////// + +HOOPLE_MAIN(test_angle, ) + diff --git a/graphiq/library/tests_geometric/test_ellipse.cpp b/graphiq/library/tests_geometric/test_ellipse.cpp new file mode 100644 index 00000000..c5062912 --- /dev/null +++ b/graphiq/library/tests_geometric/test_ellipse.cpp @@ -0,0 +1,83 @@ +/* +* Name : test_ellipse * +* Author : Chris Koeritz * +* Purpose: * +* Tests the ellipse class. * +** +* Copyright (c) 1993-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace geometric; +using namespace loggers; +using namespace mathematics; +using namespace structures; +using namespace unit_test; + +typedef cartesian_point e_point; + +class test_ellipse : virtual public unit_base, virtual public application_shell +{ +public: + test_ellipse() : application_shell() {} + DEFINE_CLASS_NAME("test_ellipse"); + int execute(); + point supposed_good_value(const angle &rotation); +}; + +point test_ellipse::supposed_good_value(const angle &rotation) +{ + double_plus rot(rotation.get(DEGREES)); +//log(a_sprintf("rotation coming in is %f", rot.value())); + if (rot == double_plus(0.0)) return point(25.000000, 20.0000); + if (rot == double_plus(35.3)) return point(24.7134, 23.3372); + if (rot == double_plus(70.6)) return point(22.8791, 28.1757); + if (rot == double_plus(105.9)) return point(17.5249, 11.3112); + if (rot == double_plus(141.2)) return point(15.3608, 16.2700); + if (rot == double_plus(176.5)) return point(15.0023, 19.6943); + if (rot == double_plus(211.8)) return point(15.2242, 22.9611); + if (rot == double_plus(247.1)) return point(16.7732, 27.6388); + if (rot == double_plus(282.4)) return point(22.0127, 10.8459); + if (rot == double_plus(317.7)) return point(24.5511, 15.8588); + if (rot == double_plus(353.0)) return point(24.9906, 19.3872); + return point(0, 0); // unknown angle. +} + +int test_ellipse::execute() +{ + FUNCDEF("execute"); + ellipse fred(e_point(20, 20), 5, 10); + for (double i = 0; i < 360.0; i += 35.3) { + e_point where(fred.location(double_angle(i, DEGREES))); + a_sprintf test_name("%.2f", double_angle(i, DEGREES).get(DEGREES)); +// log(astring(astring::SPRINTF, "at angle %f ellipse is at ", i) + where.text_form()); + point compare = supposed_good_value(double_angle(i, DEGREES)); + // right now point is not orderable, so we compare x and y but use the same test name. + ASSERT_EQUAL(double_plus(where.x()), double_plus(compare.x()), + test_name + " rotation should have proper position"); + ASSERT_EQUAL(double_plus(where.y()), double_plus(compare.y()), + test_name + " rotation should have proper position"); + } + return final_report(); +} + +////////////// + +HOOPLE_MAIN(test_ellipse, ) + diff --git a/graphiq/library/tests_geometric/test_geometry.cpp b/graphiq/library/tests_geometric/test_geometry.cpp new file mode 100644 index 00000000..ef31cda1 --- /dev/null +++ b/graphiq/library/tests_geometric/test_geometry.cpp @@ -0,0 +1,95 @@ +/* +* Name : test_geometry * +* Author : Chris Koeritz * +* Purpose: * +* Exercises some of the classes in the geometry library. * +** +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace geometric; +using namespace loggers; +using namespace structures; +using namespace unit_test; +using namespace geometric; + +class test_geometric : public virtual unit_base, public virtual application_shell +{ +public: + test_geometric() {} + DEFINE_CLASS_NAME("test_geometric"); + virtual int execute(); +}; + +int test_geometric::execute() +{ + FUNCDEF("execute"); + // test constructors + circle fred; + ellipse tobias; + line slugmart; + rectangle burger; + rectangle_warper space_warp(burger, burger); + triangle euclid; + + burger = cartesian_rectangle(23, 19, 82, 745); + ASSERT_TRUE(burger.from_text(astring("84.0 290.0 10.0 912.0")), + "cartesian from_text test should not return failure"); + ASSERT_FALSE(burger != cartesian_rectangle(84.0, 290.0, 10.0, 912.0), + "cartesian from_text test should compare with expected value"); + + screen_rectangle xingu(23, 19, 82, 745); + ASSERT_TRUE(xingu == screen_rectangle(screen_point(23, 19), screen_point(82, 745)), + "xingu screen test construction should agree with expectations"); + ASSERT_TRUE(xingu.from_text(astring("84 290 10 912")), + "xingu screen from_text test should not return failure"); + ASSERT_TRUE(xingu == screen_rectangle(84, 290, 10, 912), + "xingu screen from_text test should compare with expected value"); + + screen_rectangle guinness(-223, 19, 82, -745); + ASSERT_TRUE(guinness == screen_rectangle(screen_point(-223, 19), screen_point(82, -745)), + "guinness screen test construction should agree with expectations"); + ASSERT_TRUE(guinness.from_text(astring("-84 290 -10 912")), + "guinness screen from_text test should not return failure"); + ASSERT_TRUE(guinness == screen_rectangle(-84, 290, -10, 912), + "screen from_text test should compare with expected value"); + +//log(astring(astring::SPRINTF, "the string form is %s.", guinness.text_form().s())); + + // test non-mainstream constructors + // test non-mainstream constructors + + // test operators + + // test conversions + + // test class specific functions + + // test other things? + + return final_report(); +} + +HOOPLE_MAIN(test_geometric, ); + diff --git a/graphiq/library/tests_geometric/test_point.cpp b/graphiq/library/tests_geometric/test_point.cpp new file mode 100644 index 00000000..c96dcf9c --- /dev/null +++ b/graphiq/library/tests_geometric/test_point.cpp @@ -0,0 +1,74 @@ +/*****************************************************************************\ +* * +* Name : test_point * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Tests out the point class. * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace geometric; +using namespace loggers; +using namespace structures; +using namespace unit_test; + +class test_point : virtual public unit_base, virtual public application_shell +{ +public: + test_point() : application_shell() {} + DEFINE_CLASS_NAME("test_point"); + virtual int execute(); +}; + +int test_point::execute() +{ + FUNCDEF("execute"); + { + // first test just instantiates some things. + point fred(23, angle(4)); + point bob(399, angle(2.3)); + double dist = bob.distance(fred); +//LOG(astring("fred is ") + fred + " and bob is " + bob); +//LOG(a_sprintf("distance between is ", dist)); + point borg(fred - bob); +//LOG(astring("borg is fred-bob, which is ") + borg); + ASSERT_FALSE(borg.magnitude() - dist > 0.001, + "difference must be small between distance and magnitude"); + } + + { + astring pt1 = "12,38"; + point to_scan; + to_scan.from_text(pt1); + ASSERT_FALSE( (to_scan.x() != 12) || (to_scan.y() != 38), + "second test: first from_text should work"); + astring pt2 = "{14.3,16.2989}"; + to_scan.from_text(pt2); + ASSERT_FALSE( (to_scan.x() != 14.3) || (to_scan.y() != 16.2989), + "second test: second from_text should work too"); + } + + return final_report(); +} + +HOOPLE_MAIN(test_point, ) + diff --git a/graphiq/library/tests_geometric/test_warper.cpp b/graphiq/library/tests_geometric/test_warper.cpp new file mode 100644 index 00000000..b7fcf393 --- /dev/null +++ b/graphiq/library/tests_geometric/test_warper.cpp @@ -0,0 +1,69 @@ +/*****************************************************************************\ +* * +* Name : test_rectangle_warper * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Tests the rectangle warper class. * +* * +******************************************************************************* +* Copyright (c) 1993-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace geometric; +using namespace loggers; +using namespace structures; +using namespace unit_test; +using namespace geometric; + +class test_rectangle_warper : public virtual unit_base, virtual public application_shell +{ +public: + test_rectangle_warper() {} + DEFINE_CLASS_NAME("test_rectangle_warper"); + virtual int execute(); +}; + +int test_rectangle_warper::execute() +{ + FUNCDEF("execute"); + rectangle inner(-20, 0, -60, 30); + rectangle outer(20, 30, 0, 0); + rectangle_warper ito(inner, outer, rectangle_warper::TOP_LEFT, + rectangle_warper::BOTTOM_RIGHT); +//LOG(astring("inner to outer warper is: " + ito.text_form())); + + rectangle warped_inner(ito.to_system_2(inner)); +//LOG(astring("warped inner becomes ") + warped_inner.text_form()); + rectangle warped_outer(ito.to_system_1(outer)); +//LOG(astring(" and outer becomes ") + warped_outer.text_form()); + ASSERT_FALSE( (warped_inner != outer) || (warped_outer != inner), + "systems should warp to each other correctly"); + + point in_center(inner.center()); + point warp_in_center(ito.to_system_2(in_center)); + point out_center(outer.center()); + point warp_out_center(ito.to_system_1(out_center)); + ASSERT_FALSE( (warp_out_center != inner.center()) || (warp_in_center != outer.center()), + "centers should warp to each other"); + return final_report(); +} + +HOOPLE_MAIN(test_rectangle_warper, ); + diff --git a/graphiq/library/user_interface/menu_base.cpp b/graphiq/library/user_interface/menu_base.cpp new file mode 100644 index 00000000..38c9aab5 --- /dev/null +++ b/graphiq/library/user_interface/menu_base.cpp @@ -0,0 +1,180 @@ + + + +//note: in progress. + +/*****************************************************************************\ +* * +* Name : menu_base * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2003-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "menu_base.h" + +#include +#include + +class menu_common_amorph : public amorph {}; + +////////////// + +menu_common_base::~menu_common_base() {} + +////////////// + +menu_item::menu_item(const string_array &trigs, + const astring &text, const astring &description) +: _triggers(new string_array(trigs)), + _text(new astring(text)), + _description(new astring(description)) +{} + +menu_item::menu_item(const menu_item &to_copy) +: root_object(), + menu_common_base(), + _triggers(new string_array), + _text(new astring), + _description(new astring) +{ *this = to_copy; } + +menu_item::~menu_item() +{ + WHACK(_text); + WHACK(_description); +} + +menu_item &menu_item::operator =(const menu_item &to_copy) +{ + if (this == &to_copy) return *this; + *_triggers = *to_copy._triggers; + *_text = *to_copy._text; + *_description = *to_copy._description; + return *this; +} + +void menu_item::menu_activation(char formal(trigger)) {} + +const string_array &menu_item::triggers() const { return *_triggers; } + +const astring &menu_item::text() const { return *_text; } + +const astring &menu_item::description() const { return *_description; } + +////////////// + +//call this when menu invoked. +/// virtual bool menu_item_activity() = 0; + +menu_base::menu_base(const astring &title, const menu_item ¶meters) +: _title(new astring(title)), + _parameters(new menu_item(parameters)), + _items(new menu_common_amorph), + _menus(new menu_common_amorph) +{ +} + +menu_base::~menu_base() +{ + WHACK(_title); + WHACK(_menus); + WHACK(_items); +} + +bool menu_base::validate(bool recursive) +{ +if (recursive){} +//hmmm: implement this too.... +return false; +} + +astring menu_base::text_form() const +{ +//hmmm: implement this too.... +return ""; +} + +astring menu_base::recursive_text_form() const +{ +//hmmm: implement this too.... +return ""; +} + +int menu_base::items() const { return _items->elements(); } + +void menu_base::add_item(menu_item *to_invoke) +{ + if (!to_invoke) return; + *_items += to_invoke; +} + +menu_item *menu_base::get_item(int index) +{ + bounds_return(index, 0, _items->elements(), NIL); + return dynamic_cast(_items->borrow(index)); +} + +bool menu_base::zap_item(int index) +{ + bounds_return(index, 0, _items->elements(), false); + _items->zap(index, index); + return true; +} + +bool menu_base::enable_item(int index, bool enable) +{ + bounds_return(index, 0, _items->elements(), false); + _items->borrow(index)->enable(enable); + return true; +} + +int menu_base::submenus() const { return _menus->elements(); } + +void menu_base::add_submenu(menu_base *sub) +{ + if (!sub) return; + _menus->append(sub); +} + +menu_base *menu_base::get_submenu(int index) +{ + bounds_return(index, 0, _menus->elements(), NIL); + return dynamic_cast(_menus->borrow(index)); +} + +bool menu_base::zap_submenu(int index) +{ + bounds_return(index, 0, _menus->elements(), false); + _menus->zap(index, index); + return true; +} + +bool menu_base::enable_submenu(int index, bool enable) +{ + bounds_return(index, 0, _menus->elements(), false); + _menus->borrow(index)->enable(enable); + return true; +} + +menu_common_base *menu_base::evaluate_trigger(char trigger) +{ +//hmmm: implement this too.... +if (!trigger){} +return NIL; +} + +void menu_base::activate() +{ +//hmmm: implement this too.... +} + + + + diff --git a/graphiq/library/user_interface/menu_base.h b/graphiq/library/user_interface/menu_base.h new file mode 100644 index 00000000..57ecc156 --- /dev/null +++ b/graphiq/library/user_interface/menu_base.h @@ -0,0 +1,158 @@ +//note: in progress. + +#ifndef MENU_BASE_CLASS +#define MENU_BASE_CLASS + +/*****************************************************************************\ +* * +* Name : menu_base * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2003-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + + + +#include + +// forward. +class menu_common_amorph; + +////////////// + +//! a common base class for referring to menu_items or menus polymorphically. + +class menu_common_base : public virtual root_object +{ +public: + virtual ~menu_common_base(); + + bool enabled() const { return _enabled; } + void enable(bool enable = true) { _enabled = enable; } + +private: + bool _enabled; //!< is this object enabled? +}; + +////////////// + +//! A base class for the active items that can be stored inside a menu. + +class menu_item +: public menu_common_base +{ +public: + menu_item(const string_array &triggers, const astring &text, + const astring &description); + //!< constructs a menu item that shows the "text" and "description". + /*!< the "triggers" is a list of characters that are used to activate this + menu item. these correspond to hot keys that are often underlined. */ + + menu_item(const menu_item &to_copy); + + virtual ~menu_item(); + + menu_item &operator =(const menu_item &to_copy); + + DEFINE_CLASS_NAME("menu_item"); + + const string_array &triggers() const; + const astring &text() const; + const astring &description() const; + + virtual void menu_activation(char trigger); + //!< invoked when the user chooses the menu item in question. + /*!< the "trigger" holds the value that they actually chose. */ + +private: + string_array *_triggers; //!< the trigger values for this menu item. + astring *_text; //!< the text shown for this item. + astring *_description; //!< the full description of the item. +}; + +////////////// + +//! A base class for a menu-driven interface model. +/*! + The base just provides functions for manipulating menu items and submenus. +*/ + +class menu_base : public menu_common_base +{ +public: + menu_base(const astring &title, const menu_item ¶meters); + // +#include + +#include +//#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace cromp; +using namespace filesystem; +using namespace loggers; +using namespace octopi; +using namespace sockets; +using namespace structures; +using namespace textual; +using namespace timely; + +#undef BASE_LOG +#define BASE_LOG(a) EMERGENCY_LOG(program_wide_logger::get(), astring(a)) +#undef LOG +#define LOG(a) CLASS_EMERGENCY_LOG(program_wide_logger::get(), astring(a)) + +const int REPORTING_INTERVAL = 28 * SECOND_ms; // how often to squawk. + +const int REFRESH_INTERVAL = 20 * MINUTE_ms; // how often we check tree. + +const int COMPARATOR_PORT = 10809; + // simple port grabbed randomly for the default. + +const int MAX_CHUNK = 16 * KILOBYTE; + // chunk size doesn't matter here; not transferring. + +////////////// + +class find_missing : public application_shell +{ +public: + find_missing(); + ~find_missing(); + + virtual int execute(); + + DEFINE_CLASS_NAME("find_missing"); + + int retrieve_info_from_server(); + // for a client side comparison, this finds out which files are + // different and reports them. + + int print_instructions(); + // shows the instructions for this program. + +private: + bool _saw_clients; // true if we ever got a connection. + cromp_server *_server_side; + // provides connection and transmission services for servers. + cromp_client *_client_side; // client side connection. + bool _leave_when_no_clients; // true if we should just do one run. + bool _encryption; // true if we're encrypting. + astring _source; // the source path which a client will ask the server for. + astring _target; // the target path where files are stored for the client. + bool _started_okay; // got through the command line checking. +}; + +////////////// + +find_missing::find_missing() +: application_shell(), + _saw_clients(false), + _server_side(NIL), + _client_side(NIL), + _leave_when_no_clients(false), + _encryption(false), + _started_okay(false) +{ + FUNCDEF("constructor"); + SETUP_COMBO_LOGGER; + LOG(""); + LOG(""); + + command_line args(_global_argc, _global_argv); + // check for a port on the command line. + astring port_text; + int port = COMPARATOR_PORT; + if (args.get_value("port", port_text, false)) + port = port_text.convert(COMPARATOR_PORT); + int posn = 0; + if (args.find("exit", posn)) { + LOG("seeing the 'exit without clients' flag set."); + _leave_when_no_clients = true; + } + + int indy = 0; + if (args.find("encrypt", indy, false) + || (args.find('e', indy, false)) ) { +LOG("enabling encryption!"); + // they're saying that we should encrypt the communication. + _encryption = true; + } + + bool server = true; + indy = 0; + if (args.find("client", indy, false)) { +LOG("client role chosen."); + server = false; + } else { +LOG("server role chosen."); + } + + internet_address addr; + addr.port = port; + + // check for a hostname on the command line. + astring hostname("local"); + astring host_temp; + if (args.get_value("host", host_temp, false)) { + LOG(astring("using host: ") + host_temp); + hostname = host_temp; + } else LOG(astring("using host: ") + hostname); + strcpy(addr.hostname, hostname.s()); + + if (server) { + astring key; + if (!args.get_value("key", key, false)) { + print_instructions(); + LOG("No keyword specified on command line."); + return; + } + astring root; + if (!args.get_value("root", root, false)) { + print_instructions(); + LOG("No transfer root was specified on the command line."); + return; + } + + LOG("starting comparison server"); + _server_side = new cromp_server(cromp_server::any_address(port)); + file_transfer_tentacle *new_tent = new file_transfer_tentacle(MAX_CHUNK, + (file_transfer_tentacle::transfer_modes)(file_transfer_tentacle::ONLY_REPORT_DIFFS + | file_transfer_tentacle::COMPARE_SIZE_AND_TIME + | file_transfer_tentacle::COMPARE_CONTENT_SAMPLE)); + new_tent->add_correspondence(key, root, REFRESH_INTERVAL); + _server_side->add_tentacle(new_tent); + _server_side->enable_servers(_encryption); + } else { + LOG("starting comparison client"); + _client_side = new cromp_client(addr); + if (_encryption) _client_side->enable_encryption(); + + outcome ret = _client_side->connect(); + if (ret != cromp_client::OKAY) + non_continuable_error(class_name(), func, astring("failed to connect to " + "the server: ") + cromp_client::outcome_name(ret)); + + file_transfer_tentacle *new_tent = new file_transfer_tentacle(MAX_CHUNK, + (file_transfer_tentacle::transfer_modes)(file_transfer_tentacle::ONLY_REPORT_DIFFS + | file_transfer_tentacle::COMPARE_SIZE_AND_TIME + | file_transfer_tentacle::COMPARE_CONTENT_SAMPLE)); + if (!args.get_value("source", _source, false)) { + print_instructions(); + LOG("No source path was specified on the command line."); + return; + } + if (!args.get_value("target", _target, false)) { + print_instructions(); + LOG("No target path was specified on the command line."); + return; + } + + string_array includes; + outcome regis = new_tent->register_file_transfer + (_client_side->entity(), _source, _target, includes); + if (regis != cromp_client::OKAY) + non_continuable_error(class_name(), func, "failed to register transfer"); + + _client_side->add_tentacle(new_tent); + } + + _started_okay = true; + +} + +find_missing::~find_missing() +{ + WHACK(_client_side); + WHACK(_server_side); +} + +int find_missing::print_instructions() +{ + astring name = filename(_global_argv[0]).basename().raw(); + BASE_LOG(a_sprintf("%s usage:", name.s())); + BASE_LOG(""); + BASE_LOG(a_sprintf("\ +This program can compare directory trees and report the files that are\n\ +missing on the client's side compared to what the server is offering.\n\ +The program can function as either the server side or the client side.\n\ +The available flags are:\n\ +\n\ +%s --client --host srvname --port P --source key_path --target cli_dest\n\ +\n\ +The client side needs to know the server host (srvname) and the port where\n\ +the server is listening for connections (P). The client will compare its\n\ +local path (cli_dest) with the server's keyed path (key_path). The key\n\ +path will begin with whatever keyword the server is offering, plus optional\n\ +additional path components to retrieve less than the whole tree being\n\ +served.\n\ +\n\ +\n\ +%s --server --host srvname --port P --key keyname --root srv_path\n\ +\n\ +The server side needs to know what address and port to listen on (srvname\n\ +and P). It will open a server there that provides a directory hierarchy\n\ +starting at the root specified (srv_path). The directory tree will be known\n\ +to clients as the key word (keyname), thus freeing the clients from needing\n\ +to know absolute paths on the server.\n\ +\n\ +", name.s(), name.s())); + + return 23; +} + +int find_missing::retrieve_info_from_server() +{ + FUNCDEF("retrieve_info_from_server"); + // prepare a client request + file_transfer_infoton initiate; + initiate._request = true; + initiate._command = file_transfer_infoton::TREE_COMPARISON; + initiate._src_root = _source; + initiate._dest_root = _target; + directory_tree target_area(_target); + target_area.calculate(false); + string_set includes; + initiate.package_tree_info(target_area, includes); + octopus_request_id cmd_id; + outcome start_ret = _client_side->submit(initiate, cmd_id); + if (start_ret != tentacle::OKAY) + non_continuable_error(class_name(), func, astring("failed to initiate " + " the transfer: ") + cromp_client::outcome_name(start_ret)); + + infoton *start_reply_tmp = NIL; +//hmmm: set timeout appropriate to the speed of the connection! + outcome first_receipt = _client_side->acquire(start_reply_tmp, cmd_id); + if (first_receipt != cromp_client::OKAY) + non_continuable_error(class_name(), func, astring("failed to receive response: ") + + cromp_client::outcome_name(start_ret)); + file_transfer_infoton *start_reply = dynamic_cast + (start_reply_tmp); + if (!start_reply) + non_continuable_error(class_name(), func, "failed to cast starting infoton to " + "proper type"); + + filename_list diffs; + byte_array pack_copy = start_reply->_packed_data; + if (!diffs.unpack(pack_copy)) + non_continuable_error(class_name(), func, "could not unpack filename list!"); + BASE_LOG("Differences found between local target and server's tree:"); +/// BASE_LOG(diffs.text_form()); + for (int i = 0; i < diffs.elements(); i++) { + BASE_LOG(a_sprintf("%d: %s", i + 1, diffs[i]->raw().s())); + } + + return 0; +} + +int find_missing::execute() +{ + FUNCDEF("execute"); + + if (!_started_okay) return 32; + + time_stamp next_report(REPORTING_INTERVAL); + + while (true) { + // make sure we didn't see our exit condition. + + if (_server_side && !_server_side->clients() && _leave_when_no_clients + && _saw_clients) { + LOG("exiting now"); + break; + } + + if (_client_side) return retrieve_info_from_server(); + + if (time_stamp() > next_report) { + if (_server_side) + LOG(a_sprintf("There are %d clients.", _server_side->clients())); +//report about client side also. + next_report.reset(REPORTING_INTERVAL); + } + + time_control::sleep_ms(100); + } + return 0; +} + +////////////// + +HOOPLE_MAIN(find_missing, ) + diff --git a/octopi/applications/transporter/makefile b/octopi/applications/transporter/makefile new file mode 100644 index 00000000..f10f54a5 --- /dev/null +++ b/octopi/applications/transporter/makefile @@ -0,0 +1,14 @@ +CONSOLE_MODE = t + +include cpp/variables.def + +PROJECT = transporter +TYPE = application +SOURCE = transporter_version.rc +TARGETS = find_missing.exe synch_files.exe transporter.exe +LOCAL_LIBS_USED = application configuration cromp crypto loggers sockets tentacles octopus processes textual timely filesystem nodes structures basis +USE_SSL = t +VCPP_USE_SOCK = t + +include cpp/rules.def + diff --git a/octopi/applications/transporter/synch_files.cpp b/octopi/applications/transporter/synch_files.cpp new file mode 100644 index 00000000..506ad4c4 --- /dev/null +++ b/octopi/applications/transporter/synch_files.cpp @@ -0,0 +1,86 @@ +/* +* Name : synch_files +* Author : Chris Koeritz +* Purpose: * +* Provides a file transfer utility using the file_transfer_tentacle. * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace octopi; +using namespace structures; +using namespace textual; +using namespace timely; + +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +class synch_files_tentacle : public application_shell +{ +public: + synch_files_tentacle() : application_shell() {} + DEFINE_CLASS_NAME("test_dirtree_fcopy"); + int execute(); +}; + +int synch_files_tentacle::execute() +{ +// FUNCDEF("execute"); + + if (_global_argc < 3) { + log(astring("\ +This program needs two parameters:\n\ +a directory for the source root and one for the target root.\n\ +Optionally, a third parameter may specify a starting point within the\n\ +source root.\n\ +Further, if fourth or more parameters are found, they are taken to be\n\ +files to include; only they will be transferred.\n"), ALWAYS_PRINT); + return 23; + } + + astring source_dir = _global_argv[1]; + astring target_dir = _global_argv[2]; + + astring source_start = ""; + if (_global_argc >= 4) { + source_start = _global_argv[3]; + } + + string_array includes; + if (_global_argc >= 5) { + for (int i = 4; i < _global_argc; i++) { + includes += _global_argv[i]; + } + } + +//hmmm: make comparing the file chunks an option too! + outcome returned = recursive_file_copy::copy_hierarchy + (file_transfer_tentacle::COMPARE_SIZE_AND_TIME, source_dir, + target_dir, includes, source_start); + + if (returned != common::OKAY) { + critical_events::alert_message(astring("copy failure with outcome=") + + recursive_file_copy::outcome_name(returned)); + return 1; + } else return 0; +} + +HOOPLE_MAIN(synch_files_tentacle, ) + diff --git a/octopi/applications/transporter/transporter.cpp b/octopi/applications/transporter/transporter.cpp new file mode 100644 index 00000000..e6cc082a --- /dev/null +++ b/octopi/applications/transporter/transporter.cpp @@ -0,0 +1,379 @@ +/*****************************************************************************\ +* * +* Name : transporter * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace cromp; +using namespace filesystem; +using namespace loggers; +using namespace octopi; +using namespace sockets; +using namespace structures; +using namespace textual; +using namespace timely; + +#define LOG(a) CLASS_EMERGENCY_LOG(program_wide_logger::get(), a) + +const int REPORTING_INTERVAL = 28 * SECOND_ms; // how often to squawk. + +const int REFRESH_INTERVAL = 120 * MINUTE_ms; // how often we check tree. + +const int TRANSFER_PORT = 10808; + // simple port grabbed randomly for the default. + +const int MAX_CHUNK = 2 * MEGABYTE; + // we will transfer fairly large chunks so we can get this done reasonably + // quickly. even at that size, it shouldn't cause most modern machines to + // hiccup even slightly. + +////////////// + +class transporter : public application_shell +{ +public: + transporter(); + ~transporter(); + + virtual int execute(); + + DEFINE_CLASS_NAME("transporter"); + + int push_client_download(); + // for a client side download, this prods the transfer process. + + int print_instructions(); + // shows the instructions for this application. + +private: + bool _saw_clients; // true if we ever got a connection. + cromp_server *_server_side; + // provides connection and transmission services for servers. + cromp_client *_client_side; // client side connection. + bool _leave_when_no_clients; // true if we should just do one run. + bool _encryption; // true if we're encrypting. + astring _source; // the source path which a client will ask the server for. + astring _target; // the target path where files are stored for the client. + bool _started_okay; // true if we got past the command line checks. +}; + +////////////// + +transporter::transporter() +: application_shell(), + _saw_clients(false), + _server_side(NIL), + _client_side(NIL), + _leave_when_no_clients(false), + _encryption(false), + _started_okay(false) +{ + FUNCDEF("constructor"); + SETUP_COMBO_LOGGER; + LOG(""); + LOG(""); + + command_line args(_global_argc, _global_argv); + // check for a port on the command line. + astring port_text; + int port = TRANSFER_PORT; + if (args.get_value("port", port_text, false)) + port = port_text.convert(TRANSFER_PORT); + int posn = 0; + if (args.find("exit", posn)) { + LOG("seeing the 'exit without clients' flag set."); + _leave_when_no_clients = true; + } + + int indy = 0; + if (args.find("encrypt", indy, false) + || (args.find('e', indy, false)) ) { +LOG("enabling encryption!"); + // they're saying that we should encrypt the communication. + _encryption = true; + } + + bool server = true; + indy = 0; + if (args.find("client", indy, false)) { +LOG("client side chosen"); + server = false; + } + + internet_address addr; + addr.port = port; + + // check for a hostname on the command line. + astring hostname("local"); + astring host_temp; + if (args.get_value("host", host_temp, false)) { + LOG(astring("using host: ") + host_temp); + hostname = host_temp; + } else LOG(astring("using host: ") + hostname); + strcpy(addr.hostname, hostname.s()); + + if (server) { + astring key; + if (!args.get_value("key", key, false)) { + print_instructions(); + LOG("No keyword specified on command line."); + return; + } + astring root; + if (!args.get_value("root", root, false)) { + print_instructions(); + LOG("No transfer root was specified on the command line."); + return; + } + + LOG("starting transfer server"); + _server_side = new cromp_server(cromp_server::any_address(port)); + file_transfer_tentacle *new_tent = new file_transfer_tentacle(MAX_CHUNK, + (file_transfer_tentacle::transfer_modes)(file_transfer_tentacle::ONLY_REPORT_DIFFS + | file_transfer_tentacle::COMPARE_SIZE_AND_TIME + | file_transfer_tentacle::COMPARE_CONTENT_SAMPLE)); + +LOG(key + " => " + root); + new_tent->add_correspondence(key, root, REFRESH_INTERVAL); + _server_side->add_tentacle(new_tent); + _server_side->enable_servers(_encryption); + } else { + LOG("starting transfer client"); + _client_side = new cromp_client(addr); + if (_encryption) _client_side->enable_encryption(); + + outcome ret = _client_side->connect(); + if (ret != cromp_client::OKAY) + non_continuable_error(class_name(), func, astring("failed to connect to " + "the server: ") + cromp_client::outcome_name(ret)); + + file_transfer_tentacle *new_tent = new file_transfer_tentacle(MAX_CHUNK, + (file_transfer_tentacle::transfer_modes)(file_transfer_tentacle::ONLY_REPORT_DIFFS + | file_transfer_tentacle::COMPARE_SIZE_AND_TIME + | file_transfer_tentacle::COMPARE_CONTENT_SAMPLE)); + + if (!args.get_value("source", _source, false)) { + print_instructions(); + LOG("No source path was specified on the command line."); + return; + } + if (!args.get_value("target", _target, false)) { + print_instructions(); + LOG("No target path was specified on the command line."); + return; + } + + string_array includes; + outcome regis = new_tent->register_file_transfer + (_client_side->entity(), _source, _target, includes); + if (regis != cromp_client::OKAY) + non_continuable_error(class_name(), func, "failed to register transfer"); + + _client_side->add_tentacle(new_tent); + } + + _started_okay = true; + +} + +transporter::~transporter() +{ + WHACK(_client_side); + WHACK(_server_side); +} + +int transporter::print_instructions() +{ + astring name = filename(_global_argv[0]).basename().raw(); + log(a_sprintf("%s usage:", name.s())); + log(astring::empty_string()); + log(a_sprintf("\ +This program can transfer directory trees across the network. It will only\n\ +copy the files missing on the client's side given what the server offers.\n\ +The program can function as either the server side or the client side.\n\ +The available flags are:\n\ +\n\ +%s --client --host srvname --port P --source key_path --target cli_dest\n\ +\n\ +The client side needs to know the server host (srvname) and the port where\n\ +the server is listening for connections (P). The client will compare its\n\ +local path (cli_dest) with the server's keyed path (key_path) and copy the\n\ +files that are missing on the client's side. The key path will begin with\n\ +whatever keyword the server is offering, plus optional additional path\n\ +components to retrieve less than the whole tree being served.\n\ +\n\ +\n\ +%s --server --host srvname --port P --key keyname --root srv_path\n\ +\n\ +The server side needs to know what address and port to listen on (srvname\n\ +and P). It will open a server there that provides a directory hierarchy\n\ +starting at the root specified (srv_path). The directory tree will be known\n\ +to clients as the key word (keyname), thus freeing the clients from needing\n\ +to know absolute paths on the server.\n\ +\n\ +", name.s(), name.s())); + + return 23; +} + +int transporter::push_client_download() +{ + FUNCDEF("push_client_download"); + // prepare a client request + file_transfer_infoton initiate; + initiate._request = true; + initiate._command = file_transfer_infoton::TREE_COMPARISON; + initiate._src_root = _source; + initiate._dest_root = _target; + directory_tree target_area(_target); + target_area.calculate(false); + string_set includes; + initiate.package_tree_info(target_area, includes); + octopus_request_id cmd_id; + outcome start_ret = _client_side->submit(initiate, cmd_id); + if (start_ret != tentacle::OKAY) + non_continuable_error(class_name(), func, astring("failed to initiate " + " the transfer: ") + cromp_client::outcome_name(start_ret)); + + infoton *start_reply_tmp = NIL; +//hmmm: set timeout appropriate to the speed of the connection! + outcome first_receipt = _client_side->acquire(start_reply_tmp, cmd_id); + if (first_receipt != cromp_client::OKAY) + non_continuable_error(class_name(), func, astring("failed to receive response: ") + + cromp_client::outcome_name(start_ret)); + file_transfer_infoton *start_reply = dynamic_cast + (start_reply_tmp); + if (!start_reply) + non_continuable_error(class_name(), func, "failed to cast starting infoton to " + "proper type"); + +//debugging start + filename_list diffs; + byte_array pack_copy = start_reply->_packed_data; + if (!diffs.unpack(pack_copy)) + non_continuable_error(class_name(), func, "could not unpack filename list!"); + LOG(astring("got list of diffs:\n") + diffs.text_form()); +//debugging end + + outcome eval_ret = _client_side->octo()->evaluate(start_reply, cmd_id, true); + if (eval_ret != cromp_client::OKAY) + non_continuable_error(class_name(), func, astring("failed to process the " + "start response: ") + cromp_client::outcome_name(eval_ret)); + + int iter = 0; + + while (true) { +LOG(a_sprintf("ongoing chunk %d", ++iter)); + // keep going until we find a broken reply. + file_transfer_infoton ongoing; + ongoing._request = true; + ongoing._command = file_transfer_infoton::PLACE_FILE_CHUNKS; + ongoing._src_root = _source; + ongoing._dest_root = _target; + + octopus_request_id cmd_id; + outcome place_ret = _client_side->submit(ongoing, cmd_id); + if (place_ret != cromp_client::OKAY) + non_continuable_error(class_name(), func, astring("failed to send ongoing " + "chunk: ") + cromp_client::outcome_name(place_ret)); + + infoton *place_reply_tmp = NIL; +//hmmm: set timeout appropriate to the speed of the connection! + outcome place_receipt = _client_side->acquire(place_reply_tmp, cmd_id); + if (place_receipt != cromp_client::OKAY) + non_continuable_error(class_name(), func, astring("failed to receive " + "place response: ") + cromp_client::outcome_name(place_receipt)); + + file_transfer_infoton *place_reply = dynamic_cast + (place_reply_tmp); + if (!place_reply) { + if (dynamic_cast(place_reply_tmp)) { + log(astring("The server does not support file transfers."), ALWAYS_PRINT); + WHACK(place_reply_tmp); + break; + } + non_continuable_error(class_name(), func, "failed to cast storage reply infoton " + "to proper type"); + } + + int reply_size = place_reply->_packed_data.length(); + + outcome eval_ret2 = _client_side->octo()->evaluate(place_reply, cmd_id, true); + if (eval_ret2 != tentacle::OKAY) + non_continuable_error(class_name(), func, astring("failed to process the " + "place response: ") + cromp_client::outcome_name(eval_ret2)); + + if (!reply_size) { + LOG("hit termination condition: no data packed in for file chunks."); + break; + } + } + return 0; +} + +int transporter::execute() +{ + FUNCDEF("execute"); + + if (!_started_okay) return 32; + + time_stamp next_report(REPORTING_INTERVAL); + + while (true) { + // make sure we didn't see our exit condition. + + if (_server_side && !_server_side->clients() && _leave_when_no_clients + && _saw_clients) { + LOG("exiting now"); + break; + } + + if (_client_side) return push_client_download(); + + if (time_stamp() > next_report) { + if (_server_side) + LOG(a_sprintf("There are %d clients.", _server_side->clients())); +//report about client side also. + next_report.reset(REPORTING_INTERVAL); + } + + time_control::sleep_ms(100); + } + return 0; +} + +////////////// + +HOOPLE_MAIN(transporter, ) + diff --git a/octopi/applications/transporter/transporter_version.rc b/octopi/applications/transporter/transporter_version.rc new file mode 100644 index 00000000..52337057 --- /dev/null +++ b/octopi/applications/transporter/transporter_version.rc @@ -0,0 +1,46 @@ +#ifndef NO_VERSION +#include +#include <__build_version.h> +#include <__build_configuration.h> +#define BI_PLAT_WIN32 + // force 32 bit compile. +1 VERSIONINFO LOADONCALL MOVEABLE +FILEVERSION __build_FILE_VERSION_COMMAS +PRODUCTVERSION __build_PRODUCT_VERSION_COMMAS +FILEFLAGSMASK 0 +FILEFLAGS VS_FFI_FILEFLAGSMASK +#if defined(BI_PLAT_WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + // Language type = U.S. English(0x0409) and Character Set = Windows, Multilingual(0x04b0) + BLOCK "040904b0" // Matches VarFileInfo Translation hex value. + BEGIN + VALUE "CompanyName", __build_company "\000" +#ifndef _DEBUG + VALUE "FileDescription", "Transporter File Delivery Service\000" +#else + VALUE "FileDescription", "Transporter File Delivery Service (DEBUG)\000" +#endif + VALUE "FileVersion", __build_FILE_VERSION "\000" + VALUE "ProductVersion", __build_PRODUCT_VERSION "\000" + VALUE "InternalName", "Transporter with Cromp\000" + VALUE "LegalCopyright", __build_copyright "\000" + VALUE "LegalTrademarks", __build_legal_info "\000" + VALUE "OriginalFilename", "transporter.exe\000" + VALUE "ProductName", __build_product_name "\000" + + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04b0 // US English (0x0409) and win32 multilingual (0x04b0) + END +END +#endif diff --git a/octopi/applications/transporter/version.ini b/octopi/applications/transporter/version.ini new file mode 100644 index 00000000..c71206c3 --- /dev/null +++ b/octopi/applications/transporter/version.ini @@ -0,0 +1,6 @@ +[version] +description = Transporter File Delivery Service +root = transporter +name = Transporter with Cromp +extension = exe + diff --git a/octopi/library/cromp/cromp_client.cpp b/octopi/library/cromp/cromp_client.cpp new file mode 100644 index 00000000..8baae692 --- /dev/null +++ b/octopi/library/cromp/cromp_client.cpp @@ -0,0 +1,664 @@ +/*****************************************************************************\ +* * +* Name : cromp_client * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "cromp_client.h" +#include "cromp_common.h" +#include "cromp_transaction.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace configuration; +using namespace crypto; +using namespace loggers; +using namespace mathematics; +using namespace octopi; +using namespace processes; +using namespace sockets; +using namespace structures; +using namespace timely; + +namespace cromp { + +//#define DEBUG_CROMP_CLIENT + // uncomment for noisier version. + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +const int MAX_CONN_ATTEMPTS = 3; + // the number of times we'll retry connecting for certain errors. + +const int INTERCONNECTION_SNOOZE = 200; + // we will pause this long if the initial connection attempt failed, and + // in between each attempt thereafter except the last. + +// grab control of the class, preventing multiple threads from trampling data. +#define AUTO_LOCK \ + auto_synchronizer l(*_lock) + +// make sure the client is in an operational state. +#define CHECK_LOCKOUT \ + if (_disallowed) { \ + /* we can't do anything now due to the state of the connection. */ \ + return NO_CONNECTION; \ + } + +// tries to get a particular type of object back from an infoton response. +#define CAST_REPLY(type, varname, newvar, retval) \ + type *newvar = dynamic_cast(varname); \ + if (!newvar) { \ + LOG("failed to cast " #varname " to appropriate type, " #type "."); \ + WHACK(varname); \ + return retval; \ + } + +////////////// + +class asynch_connection_thread : public ethread +{ +public: + asynch_connection_thread(cromp_client &parent) + : ethread(), _parent(parent) {} + ~asynch_connection_thread() { stop(); } + void perform_activity(void *formal(ptr)) { + FUNCDEF("perform_activity"); + while (!should_stop()) { + if (_parent.connected()) { + LOG(_parent.instance_name() + " got connected."); + break; // done? + } + // invoke the real connection maker. we should be synchronized wrt + // multiple threads since the "_disallowed" flag is set before this + // thread is ever started. no one that locks the cromp_client will + // get a chance to interfere. + LOG(_parent.instance_name() + " still unconnected; trying connect now."); + _parent.locked_connect(); + LOG(_parent.instance_name() + + " done calling connect."); + } + // single shot thread is exiting now. + _parent._disallowed = false; + } + +private: + cromp_client &_parent; +}; + +////////////// + +cromp_client::cromp_client(const internet_address &addr, int connection_wait, + int max_per_ent) +: cromp_common(cromp_common::chew_hostname(addr), max_per_ent), + _encrypting(false), + _connection_wait(connection_wait), + _lock(new mutex), + _ent(new octopus_entity(randomize_entity())), + _req_id(new int_roller(1, MAXINT32 - 20)), + _identified(false), + _authorized(false), + _disallowed(false), + _asynch_connector(NIL), + _channel_secured(false), + _crypto(new blowfish_crypto(encryption_infoton::BLOWFISH_KEY_SIZE)), + _encrypt_arm(NIL), + _guardian(new blank_entity_registry), + c_verification(new byte_array) +{ +#ifdef DEBUG_CROMP_CLIENT + FUNCDEF("constructor"); + LOG(astring("initial entity=") + _ent->mangled_form()); +#endif + open_common(addr); + + // add simple security handling. + add_tentacle(new login_tentacle(*_guardian)); + // add a non-filtering tentacle for checking security. we mainly need + // this to be able to unpack answers from the server. +} + +cromp_client::~cromp_client() +{ +// FUNCDEF("destructor"); + disconnect(); + close_common(); + _identified = false; + _authorized = false; + WHACK(_ent); + WHACK(_req_id); + _channel_secured = false; + WHACK(_crypto); + WHACK(_guardian); + WHACK(c_verification); + WHACK(_lock); +} + +bool cromp_client::connected() const { return spock()->connected(); } + +const byte_array &cromp_client::verification() const +{ return *c_verification; } + +void cromp_client::enable_encryption() +{ +// FUNCDEF("enable_encryption"); + AUTO_LOCK; + +#ifdef DEBUG_CROMP_CLIENT + LOG(astring("enabling encryption for ") + class_name() + " on " + + other_side().text_form()); +#endif + _encrypting = true; + + // plug in the encryption support. + if (other_side().is_localhost()) { + // if the address is localhost, then we will use the standard key. + byte_array temp_priv_key; + localhost_only_key().private_key(temp_priv_key); + _encrypt_arm = new encryption_tentacle(temp_priv_key); +//hmmm: there is a risk that if they reset to a new address we'd still be +// using the slightly less secure local key. could be ameliorated by +// zapping the encryption tentacle for a reset and readding it if it +// existed? + } else + _encrypt_arm = new encryption_tentacle(encryption_infoton::RSA_KEY_SIZE); + add_tentacle(_encrypt_arm, true); + add_tentacle(new unwrapping_tentacle, false); +} + +void cromp_client::stop_asynch_thread() +{ +#ifdef DEBUG_CROMP_CLIENT + FUNCDEF("stop_asynch_thread"); +#endif + if (_asynch_connector) { +#ifdef DEBUG_CROMP_CLIENT + LOG(instance_name() + " stopping thread."); +#endif + _asynch_connector->cancel(); // send it a nudge before we grab control. + AUTO_LOCK; // lock the class to prevent interference. + _asynch_connector->stop(); + WHACK(_asynch_connector); + } + _disallowed = false; // no longer running the background thread. +} + +void cromp_client::reset(const internet_address &addr, int connection_wait, + int max_per_ent) +{ +#ifdef DEBUG_CROMP_CLIENT + FUNCDEF("reset"); +#endif + stop_asynch_thread(); + AUTO_LOCK; + close_common(); // shut down the low-level stuff. + max_bytes_per_entity(max_per_ent); + *_ent = randomize_entity(); + _req_id->set_current(1); + _identified = false; + _authorized = false; + _channel_secured = false; + _connection_wait = connection_wait; + _disallowed = false; +#ifdef DEBUG_CROMP_CLIENT + LOG(astring("resetting entity=") + _ent->mangled_form()); +#endif + open_common(addr); +} + +const octopus_entity &cromp_client::entity() const +{ + AUTO_LOCK; + return *_ent; +} + +SAFE_STATIC(tcpip_stack, _hidden_stack, ) + +octopus_entity cromp_client::randomize_entity() const +{ + astring host = cromp_common::chew_hostname(internet_address + (byte_array::empty_array(), _hidden_stack().hostname(), 0), NIL); + chaos randomizer; + return octopus_entity(host, application_configuration::process_id(), + randomizer.inclusive(0, MAXINT32 / 3), + randomizer.inclusive(0, MAXINT32 / 3)); +} + +octopus_request_id cromp_client::next_id() +{ + AUTO_LOCK; + return octopus_request_id(*_ent, _req_id->next_id()); +} + +outcome cromp_client::synchronous_request(const infoton &to_send, + infoton * & received, octopus_request_id &item_id, + int timeout) +{ + FUNCDEF("synchronous_request"); + received = NIL; + outcome ret = submit(to_send, item_id); + if (ret != OKAY) { + LOG(astring("failed to submit request: ") + outcome_name(ret) + " on " + to_send.text_form()); + return ret; + } + ret = acquire(received, item_id, timeout); + if (ret != OKAY) { + LOG(astring("failed to acquire response: ") + outcome_name(ret) + " for " + to_send.text_form()); + return ret; + } + return OKAY; +} + +SAFE_STATIC(byte_array, _empty_blank_verif, ); +const byte_array &cromp_client::blank_verification() +{ return _empty_blank_verif(); } + +outcome cromp_client::login() +{ + FUNCDEF("login"); + CHECK_LOCKOUT; + if (!_identified) { + _channel_secured = false; + // we need to secure an identity with the server. + identity_infoton identity; + octopus_request_id item_id = octopus_request_id::randomized_id(); + infoton *response; + outcome ret = synchronous_request(identity, response, item_id); + if (ret != OKAY) return ret; + + CAST_REPLY(identity_infoton, response, ide_reply, NO_SERVER); + if (!ide_reply->_new_name.blank()) { +#ifdef DEBUG_CROMP_CLIENT + LOG(astring("setting new entity to: ") + + ide_reply->_new_name.mangled_form()); +#endif + AUTO_LOCK; + *_ent = ide_reply->_new_name; + _identified = true; + } else { +#ifdef DEBUG_CROMP_CLIENT + LOG("identity request failed: got blank name."); +#endif + } + WHACK(ide_reply); + } + + if (_encrypting && !_channel_secured) { + // now the encryption needs to be cranked up. + + if (!_encrypt_arm) + LOG("there's no encryption arm!!!!"); + + encryption_infoton encro; + { + AUTO_LOCK; + encro.prepare_public_key(_encrypt_arm->private_key()); + } + + infoton *response; + octopus_request_id item_id; + outcome ret = synchronous_request(encro, response, item_id); + if (ret != OKAY) return ret; + + CAST_REPLY(encryption_infoton, response, enc_reply, ENCRYPTION_MISMATCH); + // this is a reasonable answer (mismatch), because a non-encrypting + // server should tell us a general failure response, since it shouldn't + // understand the request. + + // handle the encryption infoton by feeding our tentacle the new key. + byte_array transformed; + ret = _encrypt_arm->consume(*enc_reply, item_id, transformed); + if (ret != OKAY) { + LOG(astring("failed to process encryption infoton for ") + + item_id.text_form()); + WHACK(enc_reply); // nothing to give out. + return ret; + } + WHACK(enc_reply); + + octenc_key_record *reco = _encrypt_arm->keys().lock(item_id._entity); + if (!reco) { + LOG(astring("failed to locate key for ") + item_id._entity.text_form()); + return NOT_FOUND; + } + _crypto->set_key(reco->_key.get_key(), + encryption_infoton::BLOWFISH_KEY_SIZE); + _encrypt_arm->keys().unlock(reco); + _channel_secured = true; + } + + if (!_authorized) { + // we need to go through whatever authentication is used by the server. + security_infoton::login_modes login_type = security_infoton::LI_LOGIN; + security_infoton securinfo(login_type, OKAY, *c_verification); + octopus_request_id item_id; + infoton *response; + outcome ret = synchronous_request(securinfo, response, item_id); + unhandled_request *temp_unh = dynamic_cast(response); + if (temp_unh) { +#ifdef DEBUG_CROMP_CLIENT + LOG(astring("got an unhandled request with reason: ") + + common::outcome_name(temp_unh->_reason)); +#endif + return temp_unh->_reason; // return the original reason. + } + CAST_REPLY(security_infoton, response, sec_reply, NO_SERVER); + outcome success = sec_reply->_success; + WHACK(sec_reply); + if (success == tentacle::OKAY) { + AUTO_LOCK; + _authorized = true; +#ifdef DEBUG_CROMP_CLIENT + LOG(astring("login request succeeded, now logged in.")); +#endif + } else { +#ifdef DEBUG_CROMP_CLIENT + LOG(astring("login request failed.")); +#endif + return success; + } + } + + return OKAY; +} + +outcome cromp_client::connect(const byte_array &verification) +{ +// FUNCDEF("connect"); + stop_asynch_thread(); + AUTO_LOCK; // protect from multiple connect attempts. + *c_verification = verification; + return locked_connect(); +} + +outcome cromp_client::asynch_connect() +{ + FUNCDEF("asynch_connect"); + if (connected()) return OKAY; // why bother? + if (_asynch_connector) return NO_CONNECTION; // in progress. +//#ifdef DEBUG_CROMP_CLIENT + LOG(instance_name() + " entry."); +//#endif + { + AUTO_LOCK; + // protect this block only; we want to unlock before thread gets started. + if (connected()) return OKAY; // done already somehow. + if (_asynch_connector) { + LOG("logic error: asynchronous connector already exists."); + return NO_CONNECTION; + } + _disallowed = true; + _asynch_connector = new asynch_connection_thread(*this); + } + _asynch_connector->start(NIL); +//#ifdef DEBUG_CROMP_CLIENT + LOG(instance_name() + " exit."); +//#endif + return NO_CONNECTION; +} + +outcome cromp_client::locked_connect() +{ + FUNCDEF("locked_connect"); + if (!spock()) return BAD_INPUT; + if (connected()) return OKAY; // already connected. + + locked_disconnect(); // clean out any previous connection. + *_ent = randomize_entity(); // reset the login id. + + int attempts = 0; + while (attempts++ < MAX_CONN_ATTEMPTS) { +#ifdef DEBUG_CROMP_CLIENT + LOG(instance_name() + " calling spocket connect."); +#endif + outcome ret = spock()->connect(_connection_wait); +#ifdef DEBUG_CROMP_CLIENT + LOG(instance_name() + " done calling spocket connect."); +#endif + if (ret == spocket::OKAY) { +#ifdef DEBUG_CROMP_CLIENT + LOG("finished connection... now affirming identity."); +#endif + return login(); + } + if (ret == spocket::TIMED_OUT) return TIMED_OUT; + if ( (ret == spocket::NO_ANSWER) || (ret == spocket::ACCESS_DENIED) ) { + // clean up. this is a real case of something hosed. + locked_disconnect(); + return NO_SERVER; + } +#ifdef DEBUG_CROMP_CLIENT + LOG(a_sprintf("error gotten=%s", spocket::outcome_name(ret))); +#endif + + if (attempts < MAX_CONN_ATTEMPTS - 1) + time_control::sleep_ms(INTERCONNECTION_SNOOZE); + } + LOG(instance_name() + " failed to connect."); + locked_disconnect(); // clean up. + return NO_CONNECTION; +} + +outcome cromp_client::disconnect() +{ + stop_asynch_thread(); + AUTO_LOCK; + return locked_disconnect(); +} + +void cromp_client::keep_alive_pause(int duration, int interval) +{ + if (duration < 0) duration = 0; + if (interval < 0) interval = 40; + if (interval > duration) interval = duration; + + // keep looping on the cromp stimulation methods until the time has elapsed. + time_stamp leave_at(duration); + while (time_stamp() < leave_at) { + push_outgoing(1); + grab_anything(false); + // we'll only sleep if they didn't give us a zero duration. + if (duration) + time_control::sleep_ms(interval); // snooze a hopefully short time. + } +} + +outcome cromp_client::locked_disconnect() +{ + if (!spock()) return BAD_INPUT; + outcome ret = spock()->disconnect(); + _identified = false; + _authorized = false; + _channel_secured = false; + *_ent = octopus_entity(); // reset the login id. + if (ret != spocket::OKAY) { +//hmmm: any other outcomes to return? + return OKAY; + } + return OKAY; +} + +bool cromp_client::wrap_infoton(const infoton &request, + encryption_wrapper &wrapped) +{ +#ifdef DEBUG_CROMP_CLIENT + FUNCDEF("wrap_infoton"); +#endif + if (!_channel_secured) return false; + // identity is not wrapped with encryption; we need to establish and identity + // to talk on a distinct channel with the server. even if that identity were + // compromised, the interloper should still not be able to listen in on the + // establishment of an encryption channel. + bool is_ident = !!dynamic_cast(&request); + bool is_encrypt = !!dynamic_cast(&request); + bool is_wrapper = !!dynamic_cast(&request); + if (!is_ident && !is_encrypt && !is_wrapper) { + // check that we have already got a channel to speak over. otherwise, we + // can't do any encrypting of messages yet. +#ifdef DEBUG_CROMP_CLIENT + LOG(astring("encrypting ") + request.text_form()); +#endif + byte_array packed_request; + infoton::fast_pack(packed_request, request); + _crypto->encrypt(packed_request, wrapped._wrapped); + return true; + } else return false; // we didn't need or want to wrap it. +} + +outcome cromp_client::submit(const infoton &request, + octopus_request_id &item_id, int max_tries) +{ +#ifdef DEBUG_CROMP_CLIENT + FUNCDEF("submit"); +#endif + CHECK_LOCKOUT; + item_id = next_id(); + bool is_ident = !!dynamic_cast(&request); + if (!_identified && !is_ident) return BAD_INPUT; + + if (_encrypting && _channel_secured) { + // if we're encrypting things, then we need to encrypt this too. this + // assumes that authentication is wrapped by encryption, which is the sane + // thing to do. identity is not wrapped that way though; we need to + // establish and identity to talk on a distinct channel with the server. + // even if that identity were compromised, the interloper would still not + // be able to listen in on the establishment of an encryption channel. + + encryption_wrapper real_request; + bool wrapped_okay = wrap_infoton(request, real_request); + if (wrapped_okay) { + outcome to_return = cromp_common::pack_and_ship(real_request, item_id, + max_tries); + return to_return; + } + // if it didn't wrap okay, we fall through to a normal send, because it's + // probably an encryption or identity infoton, which needs to go through + // without being wrapped. + } else if (_encrypting) { +#ifdef DEBUG_CROMP_CLIENT + LOG("the channel has not been secured yet."); +#endif + } + + outcome to_return = cromp_common::pack_and_ship(request, item_id, max_tries); + return to_return; +} + +outcome cromp_client::acquire(infoton * &response, + const octopus_request_id &cmd_id, int timeout) +{ + FUNCDEF("acquire"); + CHECK_LOCKOUT; + outcome to_return = cromp_common::retrieve_and_restore(response, cmd_id, + timeout); + + unhandled_request *intermed = dynamic_cast(response); + if (intermed) { + // override the return value with the real outcome of a failed operation. + to_return = intermed->_reason; +LOG(astring("using unhandled request's intermediate result: ") + outcome_name(to_return)); + } + + decrypt_package_as_needed(to_return, response, cmd_id); + + return to_return; +} + +outcome cromp_client::acquire_any(infoton * &response, + octopus_request_id &cmd_id, int timeout) +{ +#ifdef DEBUG_CROMP_CLIENT + FUNCDEF("acquire_any"); +#endif + CHECK_LOCKOUT; + outcome to_return = cromp_common::retrieve_and_restore_any(response, cmd_id, + timeout); + + unhandled_request *intermed = dynamic_cast(response); + if (intermed) { + // override the return value with the real outcome of a failed operation. + to_return = intermed->_reason; + } + + decrypt_package_as_needed(to_return, response, cmd_id); + + return to_return; +} + +void cromp_client::decrypt_package_as_needed(outcome &to_return, + infoton * &response, const octopus_request_id &cmd_id) +{ + FUNCDEF("decrypt_package_as_needed"); + if (dynamic_cast(response)) { + if (!_encrypt_arm) { + LOG(astring("received an encryption_wrapper but we are not " + "encrypting, on ") + cmd_id.text_form()); + to_return = ENCRYPTION_MISMATCH; + return; + } + byte_array transformed; + outcome ret = _encrypt_arm->consume(*response, cmd_id, transformed); + if ( (ret != OKAY) && (ret != PARTIAL) ) { + LOG(astring("failed to decrypt wrapper for ") + cmd_id.text_form()); + to_return = ret; + return; + } + + string_array classif; + byte_array decro; // decrypted packed infoton. + bool worked = infoton::fast_unpack(transformed, classif, decro); + if (!worked) { + LOG("failed to fast_unpack the transformed data."); + to_return = ENCRYPTION_MISMATCH; // what else would we call that? + } else { + infoton *new_req = NIL; + outcome rest_ret = octo()->restore(classif, decro, new_req); + if (rest_ret == tentacle::OKAY) { + // we got a good transformed version. + WHACK(response); + response = new_req; // substitution complete. + } else { + LOG("failed to restore transformed infoton."); + to_return = ENCRYPTION_MISMATCH; // what else would we call that? + } + } + } +} + +} //namespace. + + diff --git a/octopi/library/cromp/cromp_client.h b/octopi/library/cromp/cromp_client.h new file mode 100644 index 00000000..20434cfb --- /dev/null +++ b/octopi/library/cromp/cromp_client.h @@ -0,0 +1,202 @@ +#ifndef CROMP_CLIENT_CLASS +#define CROMP_CLIENT_CLASS + +/*****************************************************************************\ +* * +* Name : cromp_client * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Supplies primitive operations for requesting services of a CROMP-based * +* server application. Tentacles in cromp clients are only used for * +* restoring original objects, but they are required for that. Keep in mind * +* that for communication to be possible, a cromp server and its cromp client * +* must possess tentacles that grok the same infotons. They do not need to * +* be the same objects and probably shouldn't be. It makes sense to * +* implement an unpacking / cloning tentacle and then derive the full-service * +* request processing tentacle from it. * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "cromp_common.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace cromp { + +// forward: +class asynch_connection_thread; + +class cromp_client : public cromp_common +{ +public: + enum constraints { + DEFAULT_MAX_CONNECT_WAIT = 28 * basis::SECOND_ms, + // the server had better connect within this time limit or it will + // be considered missing in action. + DEFAULT_MAX_RESPONSE_WAIT = 4 * basis::MINUTE_ms + // a server should be able to answer in this interval or something is + // really wrong with the system. + }; + + cromp_client(const sockets::internet_address &destination, + int connection_wait = DEFAULT_MAX_CONNECT_WAIT, + int max_per_ent = DEFAULT_MAX_ENTITY_QUEUE); + // will connect to a cromp_server on the host specified by "destination". + + virtual ~cromp_client(); + + DEFINE_CLASS_NAME("cromp_client"); + + basis::astring instance_name() const { + return basis::astring(class_name()) + ": " + entity().text_form(); + } + + const octopi::octopus_entity &entity() const; + // returns our identity within the octopus server. + + void enable_encryption(); + // this turns on the encryption. this should be done before the first + // connect or login invocations. once it's enabled, it stays enabled. + + void reset(const sockets::internet_address &destination, + int connection_wait = DEFAULT_MAX_CONNECT_WAIT, + int max_per_ent = DEFAULT_MAX_ENTITY_QUEUE); + // disconnects from any previous server and reconstructs this client. + // the client will be left in an unconnected and unauthenticated state. + // any pre-existing tentacles will still be hooked up to the object. + // use connect() to re-establish the connection to the server. + + basis::outcome connect(const basis::byte_array &verification = blank_verification()); + // attempts to connect to the server. OKAY is returned if this succeeds. + + static const basis::byte_array &blank_verification(); // empty verification. + + basis::outcome asynch_connect(); + // this is a non-blocking connect. it behaves like connect(), but should + // never take more than a few milliseconds to return. if the client is + // already connected, then nothing is done. otherwise no operations will + // be permitted until the reconnection has succeeded. a call to regular + // connect() cancels the asynchronous connect. + + bool connected() const; + // returns true if we think we are connected to the server. + + basis::outcome disconnect(); + // disconnects from the cromp server and releases all connection resources. + + virtual basis::outcome login(); + // attempts to log in to the server. we must already have connected + // when this is called. the "verification" is a protocol specific + // package of info that can be used to validate the login. + + ////////////// + + basis::outcome submit(const octopi::infoton &request, octopi::octopus_request_id &item_id, + int max_tries = 80); + // requests a transaction from the cromp_server described by "request". + // the return value is OKAY if the request was successfully sent or it + // will be another outcome that indicates a failure of transmission. + // the "max_tries" is the number of times to try getting the send out; + // if asynchronous sends are allowed to accumulate, then 1 works here. + + basis::outcome acquire(octopi::infoton * &response, const octopi::octopus_request_id &cmd_id, + int timeout = DEFAULT_MAX_RESPONSE_WAIT); + // attempts to receive a "response" to a previously submitted request + // with the "cmd_id". if "timeout" is non-zero, then the response will + // be awaited for "timeout" milliseconds. otherwise the function returns + // immediately. note that "response" will only be generated properly given + // a tentacle that knows the infoton type received. this requires the + // cromp_client to have tentacles registered for all of the types to be + // exchanged. this can be used for asynchronous retrieval if the timeout + // is zero and one knows a previously requested "cmd_id". + + ////////////// + + // grittier functions... + + basis::outcome synchronous_request(const octopi::infoton &to_send, octopi::infoton * &received, + octopi::octopus_request_id &item_id, + int timeout = DEFAULT_MAX_RESPONSE_WAIT); + // submits the infoton "to_send" and waits for the reply. if there is + // a reply in the allotted time, then "received" is set to that. the + // "item_id" is set to the request id assigned to the request. + + basis::outcome acquire_any(octopi::infoton * &response, octopi::octopus_request_id &cmd_id, + int timeout = DEFAULT_MAX_RESPONSE_WAIT); + // a request to retrieve any waiting data for the client. similar to + // acquire above, but does not require one to know the command id. + + octopi::octopus_request_id next_id(); + // generates the next identifier. this can be used as a unique identifier + // for derived objects since the id is issued from our server. it is + // not unique across different types of cromp servers though, nor across + // cromp servers running on different hosts. + + void decrypt_package_as_needed(basis::outcome &to_return, octopi::infoton * &response, + const octopi::octopus_request_id &cmd_id); + // this ensures that if the "response" is encrypted, it will be decrypted + // and replaced with the real object intended. this method is invoked + // automatically by acquire(), but if the cromp_common retrieve methods are + // used directly, this should be done to the response before using it. + + void keep_alive_pause(int duration = 0, int interval = 40); + // pauses this thread but keeps calling into the cromp support to ensure + // items get delivered or received when possible. the call will snooze + // for at least "duration" milliseconds with individual sleeps being + // allowed the "interval" milliseconds. + + const basis::byte_array &verification() const; + // returns the verification token held currently, if it's been set. + +private: + bool _encrypting; // true if this connection should be encrypted. + int _connection_wait; // the length of time we'll allow a connection. + basis::mutex *_lock; // protects our data members below. + octopi::octopus_entity *_ent; // our identity within the octopus server. + structures::int_roller *_req_id; // the numbering of our nth request is depicted here. + bool _identified; // true if our initial identifier verification succeeded. + bool _authorized; // true if our login was successful. + bool _disallowed; // nothing is allowed right now except asynch connecting. + friend class asynch_connection_thread; // solely so it can use r_p_c method. + asynch_connection_thread *_asynch_connector; // b-ground connection thread. + bool _channel_secured; // true if an encrypted connection has been made. + crypto::blowfish_crypto *_crypto; // tracks our key, once we have one. + octopi::encryption_tentacle *_encrypt_arm; // processes encryption for us. + octopi::blank_entity_registry *_guardian; // simple security support. + basis::byte_array *c_verification; // verification token we were given. + + void stop_asynch_thread(); // stops the background connector. + + basis::outcome locked_connect(); + // called by the other types of connection processes to get the work done. + + octopi::octopus_entity randomize_entity() const; + // provides a junk entity for our temporary identifier that we'll use + // until the server gives us a new one. + + basis::outcome locked_disconnect(); + // assumes that the client is locked. this is the real disconnect. + + bool wrap_infoton(const octopi::infoton &request, octopi::encryption_wrapper &wrapped); + // wraps an infoton for sending over an encrypted connection. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/cromp/cromp_common.cpp b/octopi/library/cromp/cromp_common.cpp new file mode 100644 index 00000000..107d92ae --- /dev/null +++ b/octopi/library/cromp/cromp_common.cpp @@ -0,0 +1,732 @@ +/*****************************************************************************\ +* * +* Name : cromp_common * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +// NOTES: +// +// for a cromp_common that is "normal", the base octopus will be used for +// restoring infotons. +// for a dependent cromp_common with a singleton and preexisting socket, +// the socket will be used for communications and the singleton octopus will +// be used for restore(). +// +// there are a few tiers of methods here. the lowest-level tier can be +// called by any other functions except those in the lowest-level (so being on +// tier A implies that a method may not call other methods in tier A, but being +// on a tier X allows calling of all existent tiers X-1, X-2, ...). + +// last verified that conditions stated in header about variables protected +// by accumulator lock are true: 12/30/2002. + +#include "cromp_common.h" +#include "cromp_transaction.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace crypto; +using namespace loggers; +using namespace octopi; +using namespace sockets; +using namespace structures; +using namespace textual; +using namespace timely; + +namespace cromp { + +//#define DEBUG_CROMP_COMMON + // uncomment for debugging info. + +#undef LOG +#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), astring(to_print).s()) + +const int STALENESS_PERIOD = 2 * MINUTE_ms; + // if data sits in the buffer this long without us seeing more, we assume + // it's gone stale. + +const int SEND_DELAY_TIME = 200; + // if the send failed initially, we'll delay this long before trying again. + +const int DATA_AWAIT_SNOOZE = 80; + // we sleep for this long while we await data. + +const int QUICK_CROMP_SNOOZE = 28; + // we take a quick nap if we're looking for some data and it's not there + // for us yet. + +const int CROMP_BUFFER_CHUNK_SIZE = 256 * KILOBYTE; + // the initial allocation size for buffers. + +const int MAXIMUM_RECEIVES = 70; + // the maximum number of receptions before we skip to next phase. + +const int MAXIMUM_SEND = 128 * KILOBYTE; + // the largest chunk we try to send at a time. we want to limit this + // rather than continually asking the OS to consume a big transmission. + +const int CLEANUP_INTERVAL = 28 * SECOND_ms; + // this is how frequently we'll flush out items from our data bin that + // are too old. + +const int cromp_common::HOSTCHOP = 6; + // we take this many characters as the readable textual portion of the + // hostname. + +double cromp_common::_bytes_sent_total = 0.0; +double cromp_common::_bytes_received_total = 0.0; + + SAFE_STATIC_CONST(rsa_crypto, _hidden_localhost_only_key, + (encryption_infoton::RSA_KEY_SIZE)) + const rsa_crypto &cromp_common::localhost_only_key() { +#ifdef DEBUG_CROMP_COMMON + FUNCDEF("localhost_only_key"); +#endif + static bool initted = false; +#ifdef DEBUG_CROMP_COMMON + bool was_initted = initted; +#endif + initted = true; +#ifdef DEBUG_CROMP_COMMON + if (!was_initted) + LOG("started creating localhost RSA key."); +#endif + const rsa_crypto &to_return = _hidden_localhost_only_key(); +#ifdef DEBUG_CROMP_COMMON + if (!was_initted) + LOG("done creating localhost RSA key."); +#endif + return to_return; + } + +cromp_common::cromp_common(const astring &host, int max_per_ent) +: _commlink(NIL), + _octopus(new octopus(host, max_per_ent)), + _singleton(NIL), + _requests(new entity_data_bin(max_per_ent)), + _accum_lock(new mutex), + _last_data_seen(new time_stamp), + _accumulator(new byte_array(CROMP_BUFFER_CHUNK_SIZE, NIL)), + _sendings(new byte_array(CROMP_BUFFER_CHUNK_SIZE, NIL)), + _receive_buffer(new byte_array(CROMP_BUFFER_CHUNK_SIZE, NIL)), + _still_flat(new byte_array(CROMP_BUFFER_CHUNK_SIZE, NIL)), + _last_cleanup(new time_stamp) +{ +// FUNCDEF("constructor [host/max_per_ent]"); + // clear pre-existing space. + _accumulator->reset(); + _sendings->reset(); + _receive_buffer->reset(); + _still_flat->reset(); +} + +cromp_common::cromp_common(spocket *preexisting, octopus *singleton) +: _commlink(preexisting), + _octopus(singleton), + _singleton(singleton), + _requests(new entity_data_bin(singleton? + singleton->responses().max_bytes_per_entity() + : DEFAULT_MAX_ENTITY_QUEUE)), + _accum_lock(new mutex), + _last_data_seen(new time_stamp), + _accumulator(new byte_array(CROMP_BUFFER_CHUNK_SIZE, NIL)), + _sendings(new byte_array(CROMP_BUFFER_CHUNK_SIZE, NIL)), + _receive_buffer(new byte_array(CROMP_BUFFER_CHUNK_SIZE, NIL)), + _still_flat(new byte_array(CROMP_BUFFER_CHUNK_SIZE, NIL)), + _last_cleanup(new time_stamp) +{ + FUNCDEF("constructor [preexisting/singleton]"); + if (!_octopus) { + // they passed us a bad singleton. carry on as best we can. + LOG("singleton passed as NIL; constructing new octopus instead."); + internet_address local(internet_address::localhost(), "localhost", 0); + _octopus = new octopus(chew_hostname(local), DEFAULT_MAX_ENTITY_QUEUE); + } + // clear pre-existing space. + _accumulator->reset(); + _sendings->reset(); + _receive_buffer->reset(); + _still_flat->reset(); +} + +cromp_common::~cromp_common() +{ +// FUNCDEF("destructor"); + close_common(); // shuts down our socket and other stuff. + if (_singleton) { + _singleton = NIL; // reset the pointer we had. + _octopus = NIL; // ditto. + } else { + // this one was ours so we need to clean it up. + WHACK(_octopus); + } + WHACK(_accumulator); + WHACK(_sendings); + WHACK(_commlink); + WHACK(_requests); + WHACK(_last_cleanup); + WHACK(_last_data_seen); + WHACK(_receive_buffer); + WHACK(_still_flat); + WHACK(_accum_lock); +} + +spocket *cromp_common::spock() const { return _commlink; } + +int cromp_common::default_port() { return 10008; } + +outcome cromp_common::add_tentacle(tentacle *to_add, bool filter) +{ return _octopus->add_tentacle(to_add, filter); } + +int cromp_common::pending_sends() const +{ + auto_synchronizer l(*_accum_lock); + return _sendings->length(); +} + +int cromp_common::accumulated_bytes() const +{ + auto_synchronizer l(*_accum_lock); + return _accumulator->length(); +} + +astring cromp_common::chew_hostname(const internet_address &addr, + internet_address *resolved_form) +{ +#ifdef DEBUG_CROMP_COMMON + FUNCDEF("chew_hostname"); + LOG(astring("addr coming in ") + addr.text_form()); +#endif + tcpip_stack stack; + bool worked; + internet_address res1 = stack.fill_and_resolve(addr.hostname, addr.port, + worked); + if (worked) { + if (resolved_form) *resolved_form = res1; +#ifdef DEBUG_CROMP_COMMON + LOG(astring("resolved addr ") + res1.text_form()); +#endif + } else { +#ifdef DEBUG_CROMP_COMMON + LOG(astring("failed to resolve host=") + addr.hostname); +#endif + } + + // get a readable form of the host. + astring just_host = res1.normalize_host(); + while (just_host.length() < HOSTCHOP) just_host += "-"; // filler. + machine_uid converted = res1.convert(); + astring to_return = just_host.substring(0, HOSTCHOP - 1); + to_return += converted.compact_form(); + +#ifdef DEBUG_CROMP_COMMON + LOG(astring("returning machid ") + converted.text_form() + ", packed as " + + parser_bits::platform_eol_to_chars() + + byte_formatter::text_dump((abyte *)to_return.s(), + to_return.length() + 1)); +#endif + + return to_return; +} + +astring cromp_common::responses_text_form() const +{ return _requests->text_form(); } + +internet_address cromp_common::other_side() const +{ + if (!_commlink) return internet_address(); + return _commlink->where(); +} + +int cromp_common::max_bytes_per_entity() const +{ return _requests->max_bytes_per_entity(); } + +void cromp_common::max_bytes_per_entity(int max_bytes_per_entity) +{ + _requests->max_bytes_per_entity(max_bytes_per_entity); + _octopus->responses().max_bytes_per_entity(max_bytes_per_entity); +} + +void cromp_common::conditional_cleaning() +{ +// FUNCDEF("conditional_cleaning"); + if (time_stamp(-CLEANUP_INTERVAL) > *_last_cleanup) { + _requests->clean_out_deadwood(); + // flush any items that are too old. + _last_cleanup->reset(); + // record that we just cleaned up. + } +} + +outcome cromp_common::open_common(const internet_address &where) +{ +#ifdef DEBUG_CROMP_COMMON + FUNCDEF("open_common"); +#endif + if (_singleton && _commlink) + return OKAY; // done if this uses pre-existing objects. + + if (_commlink) WHACK(_commlink); // clean up any pre-existing socket. + + internet_address other_side = where; + +#ifdef DEBUG_CROMP_COMMON + LOG(astring("opening at ") + other_side.text_form()); +#endif + _commlink = new spocket(other_side); +//hmmm: check socket health. + + return OKAY; +} + +outcome cromp_common::close_common() +{ + if (_commlink) _commlink->disconnect(); // make the thread stop bothering. + return OKAY; +} + +const char *cromp_common::outcome_name(const outcome &to_name) +{ + switch (to_name.value()) { + case TOO_FULL: return "TOO_FULL"; + case PARTIAL: return "PARTIAL"; + default: return communication_commons::outcome_name(to_name); + } +} + +outcome cromp_common::pack_and_ship(const infoton_list &requests, + int max_tries) +{ + FUNCDEF("pack_and_ship [multiple]"); + if (!_commlink) return BAD_INPUT; // they haven't opened this yet. + conditional_cleaning(); + { + auto_synchronizer l(*_accum_lock); // lock while packing. + for (int i = 0; i < requests.elements(); i++) { + if (!requests[i] || !requests[i]->_data) { + // this is a screw-up by someone. + LOG("error in infoton_list; missing data element."); + continue; + } + cromp_transaction::flatten(*_sendings, *requests[i]->_data, + requests[i]->_id); + } + } + + return push_outgoing(max_tries); +} + +bool cromp_common::buffer_clog(int max_buff) const +{ + auto_synchronizer l(*_accum_lock); + return _sendings->length() >= max_buff; +} + +outcome cromp_common::pack_and_ship(const infoton &request, + const octopus_request_id &item_id, int max_tries) +{ +#ifdef DEBUG_CROMP_COMMON + FUNCDEF("pack_and_ship [single]"); +#endif + if (!_commlink) return BAD_INPUT; // they haven't opened this yet. + conditional_cleaning(); + +#ifdef DEBUG_CROMP_COMMON + LOG(astring("sending req ") + item_id.mangled_form()); +#endif + + { + auto_synchronizer l(*_accum_lock); // lock while packing. + cromp_transaction::flatten(*_sendings, request, item_id); + } + + return push_outgoing(max_tries); +} + +outcome cromp_common::push_outgoing(int max_tries) +{ + FUNCDEF("push_outgoing"); + + if (!max_tries) return cromp_common::OKAY; + // no tries means we're done already. + + grab_anything(false); // suck any data in that happens to be waiting. + + outcome to_return = cromp_common::TOO_FULL; + int attempts = 0; + while ( (attempts++ < max_tries) && (to_return == cromp_common::TOO_FULL) ) { + to_return = send_buffer(); + grab_anything(false); // suck any data in that happens to be waiting. + if (to_return == cromp_common::OKAY) + break; // happy returns. + if (to_return == cromp_common::PARTIAL) { + // we sent all we tried to but there's more left. + attempts = 0; // skip back since we had a successful attempt. + to_return = cromp_common::TOO_FULL; + // reset so that we treat this by staying in the send loop. + continue; // jump back without waiting. + } + if (to_return == cromp_common::TOO_FULL) { + // we can't send any more yet so delay for a bit to see if we can get + // some more out. + time_stamp stop_pausing(SEND_DELAY_TIME); + while (time_stamp() < stop_pausing) { +LOG("into too full looping..."); + if (!_commlink->connected()) break; + grab_anything(true); // suck any data in that happens to be waiting. + // snooze a bit until we think we can write again. + outcome ret = _commlink->await_writable(QUICK_CROMP_SNOOZE); + if (ret != spocket::NONE_READY) + break; + } + } else { + LOG(astring("failed send: ") + cromp_common::outcome_name(to_return)); + break; + } + } + return to_return; +} + +// rules for send_buffer: this function is in the lowest-level tier for using +// the spocket. it is allowed to be called by anyone. it must not call any +// other functions on the cromp_common class. +outcome cromp_common::send_buffer() +{ +#ifdef DEBUG_CROMP_COMMON + FUNCDEF("send_buffer"); +#endif + auto_synchronizer l(*_accum_lock); + + // all done if nothing to send. + if (!_sendings->length()) + return OKAY; + + int size_to_send = minimum(_sendings->length(), MAXIMUM_SEND); +#ifdef DEBUG_CROMP_COMMON +// LOG(a_sprintf("sending %d bytes on socket %d.", size_to_send, +// _commlink->OS_socket())); +#endif + int len_sent = 0; + outcome to_return; + outcome send_ret = _commlink->send(_sendings->observe(), size_to_send, + len_sent); + switch (send_ret.value()) { + case spocket::OKAY: { + // success. +#ifdef DEBUG_CROMP_COMMON +// LOG(a_sprintf("really sent %d bytes on socket %d.", len_sent, +// _commlink->OS_socket())); +#endif + _bytes_sent_total += len_sent; + to_return = OKAY; + break; + } + case spocket::PARTIAL: { + // got something done hopefully. +#ifdef DEBUG_CROMP_COMMON + LOG(a_sprintf("partial send of %d bytes (of %d desired) on socket %d.", + len_sent, size_to_send, _commlink->OS_socket())); +#endif + _bytes_sent_total += len_sent; + to_return = PARTIAL; + break; + } + case spocket::NONE_READY: { + // did nothing useful. +#ifdef DEBUG_CROMP_COMMON + LOG(a_sprintf("too full to send any on socket %d.", + _commlink->OS_socket())); +#endif + len_sent = 0; // reset just in case. + to_return = TOO_FULL; + break; + } + default: { + // other things went wrong. +#ifdef DEBUG_CROMP_COMMON + LOG(astring("failing send with ") + spocket::outcome_name(send_ret)); +#endif + len_sent = 0; // reset just in case. + +//hmmm: these are unnecessary now since it's the same set of outcomes. + if (send_ret == spocket::NO_CONNECTION) to_return = NO_CONNECTION; + else if (send_ret == spocket::TIMED_OUT) to_return = TIMED_OUT; +//any other ideas? + else to_return = DISALLOWED; + +#ifdef DEBUG_CROMP_COMMON + LOG(astring("failed to send--got error ") + outcome_name(to_return)); +#endif + break; + } + } + + if ( (to_return == PARTIAL) || (to_return == OKAY) ) { + // accomodate our latest activity on the socket. + _sendings->zap(0, len_sent - 1); // sent just some of it. + } + + return to_return; +} + +outcome cromp_common::retrieve_and_restore_root(bool get_anything, + infoton * &item, octopus_request_id &req_id, int timeout) +{ +// FUNCDEF("retrieve_and_restore_root"); + item = NIL; + if (!_commlink) return BAD_INPUT; // they haven't opened this yet. + octopus_request_id tmp_id; + time_stamp leaving_time(timeout); + + conditional_cleaning(); + + do { + // check if it's already in the bin from someone else grabbing it. + if (get_anything) + item = _requests->acquire_for_any(req_id); + else + item = _requests->acquire_for_identifier(req_id); + if (item) + return OKAY; + + // check to see if there's any data. + grab_anything(timeout? true : false); + + push_outgoing(1); +//hmmm: parameterize the push? + + // check again just to make sure. this is before we check the timeout, + // since we could squeak in with something before that. + if (get_anything) + item = _requests->acquire_for_any(req_id); + else + item = _requests->acquire_for_identifier(req_id); + if (item) + return OKAY; + + if (!timeout) return TIMED_OUT; + // timeout is not set so we leave right away. + + if (!_commlink->connected()) return NO_CONNECTION; + + // keep going if we haven't seen it yet and still have time. + } while (time_stamp() < leaving_time); + return TIMED_OUT; +} + +outcome cromp_common::retrieve_and_restore(infoton * &item, + const octopus_request_id &req_id_in, int timeout) +{ + octopus_request_id req_id = req_id_in; + return retrieve_and_restore_root(false, item, req_id, timeout); +} + +outcome cromp_common::retrieve_and_restore_any(infoton * &item, + octopus_request_id &req_id, int timeout) +{ return retrieve_and_restore_root(true, item, req_id, timeout); } + +// rules: snarf_from_socket is in the second lowest-level tier. it must not +// call any other functions on cromp_common besides the send_buffer and +// process_accumulator methods. +void cromp_common::snarf_from_socket(bool wait) +{ +#ifdef DEBUG_CROMP_COMMON + FUNCDEF("snarf_from_socket"); +#endif + if (wait) { +#ifdef DEBUG_CROMP_COMMON +// LOG(a_sprintf("awaiting rcptblty on socket %d.", _commlink->OS_socket())); +#endif + // snooze until data seems ready for chewing or until we time out. + time_stamp stop_pausing(DATA_AWAIT_SNOOZE); + while (time_stamp() < stop_pausing) { + if (!_commlink->connected()) return; + outcome wait_ret = _commlink->await_readable(QUICK_CROMP_SNOOZE); + if (wait_ret != spocket::NONE_READY) + break; + send_buffer(); // push out some data in between. + } + } + + outcome rcv_ret = spocket::OKAY; + // this loop scrounges as much data as possible, within limits. + int receptions = 0; + while ( (rcv_ret == spocket::OKAY) && (receptions++ < MAXIMUM_RECEIVES) ) { + int rcv_size = CROMP_BUFFER_CHUNK_SIZE; + { + auto_synchronizer l(*_accum_lock); + _receive_buffer->reset(); // clear pre-existing junk. + rcv_ret = _commlink->receive(*_receive_buffer, rcv_size); +#ifdef DEBUG_CROMP_COMMON + if ( (rcv_ret == spocket::OKAY) && rcv_size) { + LOG(a_sprintf("received %d bytes on socket %d", rcv_size, + _commlink->OS_socket())); + } else if (rcv_ret != spocket::NONE_READY) { + LOG(a_sprintf("no data on sock %d--outcome=", _commlink->OS_socket()) + + spocket::outcome_name(rcv_ret)); + } +#endif + if ( (rcv_ret == spocket::OKAY) && rcv_size) { + // we got some data from the receive, so store it. + _bytes_received_total += _receive_buffer->length(); + *_accumulator += *_receive_buffer; // add to overall accumulator. + _last_data_seen->reset(); + } + } + + send_buffer(); + // force data to go out also. + } +} + +void cromp_common::grab_anything(bool wait) +{ + snarf_from_socket(wait); // get any data that's waiting. + process_accumulator(); // retrieve any commands we see. +} + +#define CHECK_STALENESS \ + if (*_last_data_seen < time_stamp(-STALENESS_PERIOD)) { \ + LOG("would resynch data due to staleness."); \ + _accumulator->zap(0, 0); /* roast first byte */ \ + cromp_transaction::resynchronize(*_accumulator); \ + _last_data_seen->reset(); \ + continue; \ + } + +// process_accumulator should do nothing besides chewing on the buffer. +// this puts it in the lowest-level tier. + +void cromp_common::process_accumulator() +{ + FUNCDEF("process_accumulator"); + infoton *item = NIL; + octopus_request_id req_id; + + string_array clas; + + if (!_accumulator->length()) return; + + // a little gymnastics to get a large buffer on the first try. + byte_array temp_chow_buffer(CROMP_BUFFER_CHUNK_SIZE, NIL); + temp_chow_buffer.reset(); + + int cmds_found = 0; + + while (_accumulator->length()) { +LOG(a_sprintf("eating command %d", cmds_found++)); + { + // first block tries to extract data from the accumulator. + auto_synchronizer l(*_accum_lock); + // there are some contents; let's look at them. + int packed_length = 0; + outcome peek_ret = cromp_transaction::peek_header(*_accumulator, + packed_length); + if ( (peek_ret == cromp_transaction::WAY_TOO_SMALL) + || (peek_ret == cromp_transaction::PARTIAL) ) { + // not ready yet. + CHECK_STALENESS; + return; + } else if (peek_ret != cromp_transaction::OKAY) { + LOG(astring("error unpacking--peek error=") + + cromp_transaction::outcome_name(peek_ret)); + // try to get to a real command. + _accumulator->zap(0, 0); // roast first byte. + if (cromp_transaction::resynchronize(*_accumulator)) continue; + return; + } + +#ifdef DEBUG_CROMP_COMMON + LOG("seeing command ready"); +#endif + // temp buffer for undoing cromp transaction. + if (!cromp_transaction::unflatten(*_accumulator, *_still_flat, req_id)) { + LOG("failed to unpack even though peek was happy!"); + // try to get to a real command. + _accumulator->zap(0, 0); // roast first byte. + if (cromp_transaction::resynchronize(*_accumulator)) continue; + return; + } +#ifdef DEBUG_CROMP_COMMON + LOG(astring("got req id of ") + req_id.mangled_form()); +#endif + + // now unwrap the onion a bit more to find the real object being sent. + if (!infoton::fast_unpack(*_still_flat, clas, temp_chow_buffer)) { + // try to resynch on transaction boundary. + LOG("failed to get back a packed infoton!"); + _accumulator->zap(0, 0); // roast first byte. + if (cromp_transaction::resynchronize(*_accumulator)) continue; + return; + } +#ifdef DEBUG_CROMP_COMMON + LOG(astring("got classifier of ") + clas.text_form()); +#endif + } // end of protected area. + + // restore the infoton from the packed form. + outcome rest_ret = octo()->restore(clas, temp_chow_buffer, item); + if (rest_ret != tentacle::OKAY) { +#ifdef DEBUG_CROMP_COMMON + LOG(astring("our octopus couldn't restore the packed data! ") + + outcome_name(rest_ret)); +#endif + // publish an unhandled request back to the requestor. + _requests->add_item(new unhandled_request(req_id, clas, rest_ret), + req_id); + } else { + // we finally have reached a point where we have a valid infoton. + if (_requests->add_item(item, req_id)) + cmds_found++; +#ifdef DEBUG_CROMP_COMMON + else + LOG("failed to add item to bin due to space constraints."); +#endif + } +LOG(a_sprintf("ate command %d", cmds_found)); + } +/// LOG(a_sprintf("added %d commands", cmds_found)); +} + +bool cromp_common::decode_host(const astring &coded_host, astring &hostname, + machine_uid &machine) +{ + if (coded_host.length() < HOSTCHOP) return false; // not big enough. + hostname = coded_host.substring(0, cromp_common::HOSTCHOP - 1); + const astring compact_uid = coded_host.substring(cromp_common::HOSTCHOP, + coded_host.length() - 1); + machine = machine_uid::expand(compact_uid); + if (!machine.valid()) return false; + return true; +} + +} //namespace. + diff --git a/octopi/library/cromp/cromp_common.h b/octopi/library/cromp/cromp_common.h new file mode 100644 index 00000000..2839271c --- /dev/null +++ b/octopi/library/cromp/cromp_common.h @@ -0,0 +1,237 @@ +#ifndef CROMP_COMMON_CLASS +#define CROMP_COMMON_CLASS + +/* +* Name : cromp_common +* Author : Chris Koeritz +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cromp { + +const int DEFAULT_MAX_ENTITY_QUEUE = 14 * basis::MEGABYTE; + //!< the default size we allow per each entity. + /*!< note that if there are many entities and they're all getting full, the total allowed + will be this number multiplied by the number of entities. */ + +//! A few common features used by both CROMP clients and servers. + +class cromp_common : public virtual basis::root_object +{ +public: + cromp_common(const basis::astring &host, int max_per_ent); + // constructs a normal common object. open_common() must be invoked before + // this object becomes useful. the "host" should be the actual TCPIP host + // that this program is running on. the "max_per_ent" is the maximum + // allowed size (in bytes) of pending items per entity. + + cromp_common(sockets::spocket *preexisting, octopi::octopus *singleton); + // uses a "preexisting" spocket object for the communications needed here. + // the "singleton" octopus is used instead of our base class for restoring + // any data. note that "singleton" is not dealt with in the destructor; + // it is considered owned externally. however, the "preexisting" spocket + // is considered to be owned by this class now. also note that if a NIL + // "preexisting" socket is passed, then socket creation occurs by the + // normal process. + + virtual ~cromp_common(); + + static int default_port(); + // returns the default port used by cromp and octopus. this is not + // appropriate for most derived apps; they should use their own ports. + + DEFINE_CLASS_NAME("cromp_common"); + + basis::outcome open_common(const sockets::internet_address &where); + // opens the object to begin communication. this is the first point at + // which the socket is opened. + + basis::outcome close_common(); + // shuts down our presence on the network. + + sockets::spocket *spock() const; + // allows external access to our socket object. do not abuse this. + // also keep in mind that in some stages of construction, this can return + // NIL. do not assume it is non-NIL. + + sockets::internet_address other_side() const; + // returns the location that we're connected to, if any. + + virtual basis::outcome add_tentacle(octopi::tentacle *to_add, bool filter = false); + // allows customization of the processing that the cromp object performs. + + enum outcomes { + OKAY = basis::common::OKAY, + DISALLOWED = basis::common::DISALLOWED, + BAD_INPUT = basis::common::BAD_INPUT, + NOT_FOUND = basis::common::NOT_FOUND, + TIMED_OUT = basis::common::TIMED_OUT, + GARBAGE = basis::common::GARBAGE, + NO_HANDLER = basis::common::NO_HANDLER, + PARTIAL = basis::common::PARTIAL, + ENCRYPTION_MISMATCH = basis::common::ENCRYPTION_MISMATCH, + + NO_SERVER = sockets::communication_commons::NO_SERVER, + NO_CONNECTION = sockets::communication_commons::NO_CONNECTION, + + DEFINE_OUTCOME(TOO_FULL, -40, "The request cannot be processed yet") + }; + + static const char *outcome_name(const basis::outcome &to_name); + + static basis::astring chew_hostname(const sockets::internet_address &addr, + sockets::internet_address *resolved = NIL); + // resolves the hostname in "addr" and returns the resolved hostname as + // a machine_uid compact_form(). the "resolved" form of the address is + // also stored if the pointer is non-NIL. + + basis::astring responses_text_form() const; + // returns a textual form of the responses awaiting pickup. + + bool buffer_clog(int clog_point = 1 * basis::MEGABYTE) const; + // returns true if the buffer is bigger than a certain "clog_point". + + basis::outcome pack_and_ship(const octopi::infoton &request, + const octopi::octopus_request_id &item_id, int max_tries); + // requests a transaction from the other side, specified by the "request". + // the return value is OKAY if the request was successfully sent or it + // will be another outcome that indicates a failure of transmission. + // the "item_id" must be set ahead of time to identify the request and + // sender. the "max_tries" limits how many times the sending is + // reattempted on failure. + + basis::outcome pack_and_ship(const octopi::infoton_list &requests, int max_tries); + // sends a batch of "requests" in one blast. + + basis::outcome retrieve_and_restore(octopi::infoton * &item, + const octopi::octopus_request_id &req_id, int timeout); + // attempts to pull down data from our spocket and process it back into + // an infoton in "item". only the specific object for the "req_id" + // will be provided. if the "timeout" is non-zero, then data will be + // awaited that long. if "timeout" is zero, the method will return + // immediately if the data is not already available. + + basis::outcome retrieve_and_restore_any(octopi::infoton * &item, octopi::octopus_request_id &req_id, + int timeout); + // returns the first infoton that becomes available. the "req_id" is set + // to the identifier from the transaction. this is useful more for the + // server side than for the client side. + + basis::outcome push_outgoing(int max_tries); + // composes calls to grab_anything and send_buffer into an attempt to + // get all of the data out that is waiting while not ignoring the incoming + // data from the other side. + + void grab_anything(bool wait); + // attempts to locate any data waiting for our socket. any infotons + // found get stuffed into the requests bin. this is never needed for + // cromp_clients; the retrieve methods will automatically invoke this + // as appropriate. if "wait" is true, then a data pause will occur + // on the socket to await data. + + basis::outcome send_buffer(); + // pushes out any pending data waiting for the other side. the returns + // can range the gamut, but OKAY and PARTIAL are both successful outcomes. + // OKAY means that everything was sent successfully (or there was nothing + // to send). PARTIAL means that not all the data was sent, but some got + // out successfully. any other errors probably indicate a major problem. + + // these adjust the storage sizes allowed internally. + int max_bytes_per_entity() const; + void max_bytes_per_entity(int max_bytes_per_entity); + + // provides information on the overall number of bytes encountered by all + // cromp clients in this program. these are only intended for testing + // purposes and might in fact roll over for a busy client app. + static double total_bytes_sent() { return _bytes_sent_total; } + static double total_bytes_received() { return _bytes_received_total; } + + static const int HOSTCHOP; + // this is the number of characters from the host's name that go into the + // octopus identity. the portion after that many characters is a compacted + // form of the machine_uid. + + static bool decode_host(const basis::astring &coded_host, basis::astring &hostname, + sockets::machine_uid &machine); + // takes the "coded_host" from an entity and returns the "hostname" and + // "machine" information that was encoded in it. those will be gibberish + // unless true is returned. + + octopi::octopus *octo() const { return _octopus; } + // returns our octopus support object. this should always exist when this + // object is constructed properly. + + static const crypto::rsa_crypto &localhost_only_key(); + // this key should *only* be used for speeding up encryption on the local + // host. it is generated when the first caller needs it but then is + // a constant key during the program's runtime. this object can be used + // for initializing services when it is _known_ that they are connecting + // only on the localhost; that's the only place it should be used because + // re-using the key on the network provides less security than using + // the randomized encryption startup in cromp. + + int pending_sends() const; + //!< returns the number of bytes still unsent. + + int accumulated_bytes() const; + //!< returns the number of bytes pending processing from the other side. + +protected: + octopi::octopus *singleton() const { return _singleton; } + // returns the singleton octopus passed to the constructor earlier. + // this will return NIL if it was not constructed that way. + +private: + sockets::spocket *_commlink; // transceiver for data. + octopi::octopus *_octopus; // main octopus; might be same as singleton. + octopi::octopus *_singleton; // used for dependent cromp server objects. + octopi::entity_data_bin *_requests; // where the incoming requests are stored. + basis::mutex *_accum_lock; // protects the accumulator and other data below. + timely::time_stamp *_last_data_seen; // last time we got anything on socket. + basis::byte_array *_accumulator; // accumulates data for this object. + basis::byte_array *_sendings; // accumulates outgoing sends when socket is full. + basis::byte_array *_receive_buffer; // temporary buffer. + basis::byte_array *_still_flat; // another temporary buffer. + timely::time_stamp *_last_cleanup; // when we last cleaned out our bin. + + // helps for testing; not used for operation. + static double _bytes_sent_total; + static double _bytes_received_total; + + void snarf_from_socket(bool wait); + // retrieves data waiting on the socket and adds to the accumulator. + // if "wait" is true, then the presence of data is awaited first. + + void process_accumulator(); + // chews on the accumulated data to seek any commands that are present. + + void conditional_cleaning(); + // flushes out any old items if they haven't been cleaned in a bit. + + basis::outcome retrieve_and_restore_root(bool get_anything, octopi::infoton * &item, + octopi::octopus_request_id &req_id, int timeout); + // used for both types of retrieval; if "get_anything" is true, then + // any old item will be returned. if "get_anything" is false, then only + // the item with "req_id" is returned. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/cromp/cromp_security.cpp b/octopi/library/cromp/cromp_security.cpp new file mode 100644 index 00000000..1a59ef29 --- /dev/null +++ b/octopi/library/cromp/cromp_security.cpp @@ -0,0 +1,58 @@ +/*****************************************************************************\ +* * +* Name : cromp_security * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "cromp_security.h" +#include "cromp_server.h" + +#include +#include +#include +#include +#include + +using namespace basis; +using namespace octopi; +using namespace sockets; +//using namespace basis; + +namespace cromp { + +//#define DEBUG_CROMP_SECURITY + // uncomment if you want the noisier version. + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +cromp_security::cromp_security() +: _stack(new tcpip_stack) +{ +} + +cromp_security::~cromp_security() +{ + WHACK(_stack); +} + +bool cromp_security::add_entity(const octopus_entity &client, + const byte_array &verification) +{ +#ifdef DEBUG_CROMP_SECURITY + FUNCDEF("add_entity"); + LOG(astring("adding ") + client.mangled_form()); +#endif + return simple_entity_registry::add_entity(client, verification); +} + +} //namespace. + diff --git a/octopi/library/cromp/cromp_security.h b/octopi/library/cromp/cromp_security.h new file mode 100644 index 00000000..10801694 --- /dev/null +++ b/octopi/library/cromp/cromp_security.h @@ -0,0 +1,49 @@ +#ifndef CROMP_SECURITY_CLASS +#define CROMP_SECURITY_CLASS + +/*** +* * +* Name : cromp_security +* Author : Chris Koeritz +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace cromp { + +//! Implements the client registry in a cromp-appropriate manner. +/*! + The identity issue request is vetted against the known connection endpoint for a client. +*/ + +class cromp_security : public octopi::simple_entity_registry +{ +public: + cromp_security(); + virtual ~cromp_security(); + + DEFINE_CLASS_NAME("cromp_security"); + + virtual bool add_entity(const octopi::octopus_entity &client, + const basis::byte_array &verification); + + // stronger security models can be implemented by overriding add_entity(). + // this object merely verifies that we have seen the entity get issued + // by the current server. + +private: + sockets::tcpip_stack *_stack; // enables access to tcpip functionality. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/cromp/cromp_server.cpp b/octopi/library/cromp/cromp_server.cpp new file mode 100644 index 00000000..2fc7089f --- /dev/null +++ b/octopi/library/cromp/cromp_server.cpp @@ -0,0 +1,816 @@ +/*****************************************************************************\ +* * +* Name : cromp_server * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "cromp_common.h" +#include "cromp_security.h" +#include "cromp_server.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace loggers; +using namespace octopi; +using namespace processes; +using namespace sockets; +using namespace structures; +using namespace timely; + +namespace cromp { + +//#define DEBUG_CROMP_SERVER + // uncomment for noisy version. + +const int DEAD_CLIENT_CLEANING_INTERVAL = 1 * SECOND_ms; + // we will drop any clients that have disconnected this long ago. + +const int MAXIMUM_ACTIONS_PER_CLIENT = 4000; + // this is the maximum number of things we'll do in one run for a + // client, including both sends and receives. + +const int SEND_TRIES_ALLOWED = 1; + // the number of attempts we will make to get outgoing data to send. + +const int SEND_THRESHOLD = 512 * KILOBYTE; + // if we pile up some data to this point in our client gathering, we'll + // go ahead and start pushing it to the client. + +const int EXTREME_SEND_TRIES_ALLOWED = 28; + // if we're clogged, we'll push this many times to get data out. + +const int MAXIMUM_BYTES_PER_SEND = 2 * MEGABYTE; + // the maximum size we want our buffer to grow. + +const int MAXIMUM_SIZE_BATCH = 384 * KILOBYTE; + // the largest chunk of updates we'll try to grab at one time. + +const int DROPPING_INTERVAL = 500; + // the rate at which we'll check for dead clients and clean up. + +const int DATA_AWAIT_TIMEOUT = 14; + // how long the server zones out waiting for data. + +const int ACCEPTANCE_SNOOZE = 60; + // if the server sees no clients, it will take a little nap. + +#undef LOG +#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), astring(to_print).s()) + +////////////// + +// forward. +class cromp_client_record; + +class cromp_data_grabber : public ethread +{ +public: + cromp_data_grabber(cromp_client_record &parent, octopus *octo) + : ethread(), _parent(parent), _octo(octo) {} + + DEFINE_CLASS_NAME("cromp_data_grabber"); + + virtual void perform_activity(void *); + +private: + cromp_client_record &_parent; + octopus *_octo; +}; + +////////////// + +class cromp_client_record : public cromp_common +{ +public: + cromp_client_record(cromp_server &parent, spocket *client, octopus *octo, + login_tentacle &security) + : cromp_common(client, octo), + _parent(parent), + _octo(octo), + _ent(), + _healthy(true), + _fixated(false), + _grabber(*this, octo), + _waiting(), + _still_connected(true), + _security_arm(security) + { + internet_address local_addr = internet_address + (internet_address::localhost(), client->stack().hostname(), 0); + open_common(local_addr); // open the common support for biz. + _grabber.start(NIL); // crank up our background data pump on the socket. + } + + ~cromp_client_record() { + croak(); + } + + DEFINE_CLASS_NAME("cromp_client_record"); + + bool handle_client_needs(ethread &prompter) { +#ifdef DEBUG_CROMP_SERVER + FUNCDEF("handle_client_needs"); + time_stamp start; +#endif + if (!_healthy) return false; // done. + if (!spock()->connected()) { + _still_connected = false; + return false; // need to stop now. + } + bool keep_going = true; + int actions = 0; + while (keep_going && (actions < MAXIMUM_ACTIONS_PER_CLIENT) ) { + // make sure we don't overstay our welcome when the thread's supposed + // to quit. + if (prompter.should_stop()) return false; + keep_going = false; // only continue if there's a reason. + bool ret = get_incoming_data(actions); // look for requests. + if (ret) keep_going = true; + ret = push_client_replies(actions); // send replies back to the client. + if (ret) keep_going = true; + } + +#ifdef DEBUG_CROMP_SERVER + if (actions > 10) { + LOG(a_sprintf("actions=%d", actions)); + LOG(a_sprintf("%d pending send bytes, %d bytes accumulated, bin has " + "%d items.", pending_sends(), accumulated_bytes(), + octo()->responses().items_held())); + } + + int duration = int(time_stamp().value() - start.value()); + if (duration > 200) { + LOG(a_sprintf("duration=%d ms.", duration)); + } +#endif + + return true; + } + + const octopus_entity &ent() const { return _ent; } + + // stops the background activity of this object and drops the connection + // to the client. + void croak() { +// FUNCDEF("croak"); + _grabber.stop(); + int actions = 0; + while (get_incoming_data(actions)) { + // keep receiving whatever's there already. we are trying to drain + // the socket before destroying it. + } + _healthy = false; + // clean out any records for this goner. + _security_arm.expunge(_ent); + close_common(); + } + + bool healthy() const { return _healthy; } + // this is true unless the object has been told to shut down. + + bool still_connected() const { return _still_connected; } + // this is true unless the client side dropped the connection. + + cromp_server &parent() const { return _parent; } + + bool push_client_replies(int &actions) { + FUNCDEF("push_client_replies"); + if (!healthy()) return false; + if (ent().blank()) { + // not pushing replies if we haven't even gotten a command yet. +#ifdef DEBUG_CROMP_SERVER + LOG("not pushing replies for blank."); +#endif + return false; + } + + if (buffer_clog(MAXIMUM_BYTES_PER_SEND)) { +LOG("buffer clog being cleared now."); + // the buffers are pretty full; we'll try later. + push_outgoing(EXTREME_SEND_TRIES_ALLOWED); + // if we're still clogged, then leave. + if (buffer_clog(MAXIMUM_BYTES_PER_SEND)) { +LOG("could not completely clear buffer clog."); + return true; + } +LOG("cleared out buffer clog."); + } + + int any_left = true; + while (actions++ < MAXIMUM_ACTIONS_PER_CLIENT) { + // make sure we're not wasting our time. + if (!_octo->responses().items_held()) { + any_left = false; + break; + } + // make sure we don't ignore receptions. + grab_anything(false); + // try to grab a result for this entity. + int num_located = _octo->responses().acquire_for_entity(ent(), + _waiting, MAXIMUM_SIZE_BATCH); + if (!num_located) { + any_left = false; + break; + } + + // if we're encrypting, we need to wrap these as well. + if (_parent.encrypting()) { + for (int i = 0; i < _waiting.elements(); i++) { + infoton *curr = _waiting[i]->_data; + infoton *processed = _parent.wrap_infoton(curr, + _waiting[i]->_id._entity); + if (processed) _waiting[i]->_data = processed; // replace infoton. + } + } + + outcome ret = pack_and_ship(_waiting, 0); + // no attempt to send yet; we're just stuffing the buffer. + if ( (ret != cromp_common::OKAY) && (ret != cromp_common::TIMED_OUT) ) { +//hmmm: what about keeping transmission as held in list; retry later on it? + +//#ifdef DEBUG_CROMP_SERVER + LOG(astring("failed to send package back to client: ") + + cromp_common::outcome_name(ret)); +//#endif + any_left = false; + break; + } + + if (pending_sends() > SEND_THRESHOLD) { +#ifdef DEBUG_CROMP_SERVER + LOG(astring("over sending threshold on ") + _ent.text_form()); +#endif + push_outgoing(SEND_TRIES_ALLOWED); + } + + } + // now that we've got a pile possibly, we'll try to send them out. + push_outgoing(SEND_TRIES_ALLOWED); + if (!spock()->connected()) { +#ifdef DEBUG_CROMP_SERVER + LOG("noticed disconnection of client."); +#endif + _still_connected = false; + } + return any_left; + } + + bool get_incoming_data(int &actions) { + FUNCDEF("get_incoming_data"); + if (!healthy()) return false; + int first_one = true; + bool saw_something = false; // true if we got a packet. + while (actions++ < MAXIMUM_ACTIONS_PER_CLIENT) { + // pull in anything waiting. + infoton *item = NIL; + octopus_request_id req_id; + outcome ret = retrieve_and_restore_any(item, req_id, + first_one? DATA_AWAIT_TIMEOUT : 0); + first_one = false; + if (ret == cromp_common::TIMED_OUT) { + actions--; // didn't actually eat one. + return false; + } else if (ret != cromp_common::OKAY) { +#ifdef DEBUG_CROMP_SERVER + LOG(astring("got error ") + cromp_common::outcome_name(ret)); +#endif + if (ret == cromp_common::NO_CONNECTION) { +#ifdef DEBUG_CROMP_SERVER + LOG("noticed disconnection of client."); +#endif + _still_connected = false; + } + actions--; // didn't actually eat one. + return false; // get outa here. + } + // got a packet. + saw_something = true; + if (!_fixated) { + if (req_id._entity.blank()) { + LOG(astring("would have assigned ours to blank id! ") + + req_id._entity.mangled_form()); + WHACK(item); + continue; + } +#ifdef DEBUG_CROMP_SERVER + LOG(astring("cmd with entity ") + req_id._entity.mangled_form()); +#endif + if (_ent.blank()) { + // assign the entity id now that we know it. + _ent = req_id._entity; +#ifdef DEBUG_CROMP_SERVER + LOG(astring("assigned own entity to ") + _ent.mangled_form()); +#endif + } else if (!_fixated && (_ent != req_id._entity) ) { +#ifdef DEBUG_CROMP_SERVER + LOG(astring("fixated on entity of ") + req_id._entity.mangled_form() + + " where we used to have " + _ent.mangled_form()); +#endif + _ent = req_id._entity; + _fixated = true; + } + } // connects to line after debug just below. +#ifdef DEBUG_CROMP_SERVER + else if (_ent != req_id._entity) { + // this checks the validity of the entity. +#ifdef DEBUG_CROMP_SERVER + LOG(astring("seeing wrong entity of ") + req_id._entity.mangled_form() + + " when we fixated on " + _ent.mangled_form()); +#endif + WHACK(item); + continue; + } +#endif + // check again so we make sure we're still healthy; could have changed + // state while getting a command. + if (!healthy()) { + WHACK(item); + continue; + } + string_array classif = item->classifier(); + // hang onto the classifier since the next time we get a chance, the + // object might be destroyed. + + // we pass responsibility for this item over to the octopus. that's why + // we're not deleting it once evaluate gets the item. + ret = _octo->evaluate(item, req_id, _parent.instantaneous()); + if (ret != tentacle::OKAY) { +#ifdef DEBUG_CROMP_SERVER + LOG(astring("failed to evaluate the infoton we got: ") + + classif.text_form()); +#endif +//hmmm: we have upgraded this response to be for all errors, since otherwise +// clients will just time out waiting for something that's never coming. + + // we do a special type of handling when the tentacle is missing. this + // is almost always because the wrong type of request is being sent to + // a server, or the server didn't register for all the objects it is + // supposed to handle. +///// if (ret == tentacle::NOT_FOUND) { +//#ifdef DEBUG_CROMP_SERVER + LOG(astring("injecting unhandled note into response stream for ") + + req_id.text_form() + ", got outcome " + outcome_name(ret)); +//#endif + _parent.send_to_client(req_id, + new unhandled_request(req_id, classif, ret)); + // this will always work, although it's not a surety that the + // client actually still exists. probably though, since we're + // just now handling this request. +///// } + } + } + return saw_something; // keep going if we actually did anything good. + } + +private: + cromp_server &_parent; // the object that owns this client. + octopus *_octo; + octopus_entity _ent; // the entity by which we know this client. + bool _healthy; // reports our current state of happiness. + bool _fixated; // true if the entity id has become firm. + cromp_data_grabber _grabber; // the data grabbing thread. + infoton_list _waiting; + // used by the push_client_replies() method; allocated once to avoid churn. + bool _still_connected; + // set to true up until we notice that the client disconnected. + login_tentacle &_security_arm; // provides login checking. +}; + +////////////// + +void cromp_data_grabber::perform_activity(void *) +{ +#ifdef DEBUG_CROMP_SERVER + FUNCDEF("perform_activity"); +#endif + while (!should_stop()) { +// time_stamp started; + bool ret = _parent.handle_client_needs(*this); +// int duration = int(time_stamp().value() - started.value()); + if (!ret) { + // they said to stop. +#ifdef DEBUG_CROMP_SERVER + LOG("done handling client needs."); +#endif + _octo->expunge(_parent.ent()); + break; + } + } +} + +////////////// + +class cromp_client_list : public amorph +{ +public: + int find(const octopus_entity &to_find) const { + for (int i = 0; i < elements(); i++) + if (to_find == get(i)->ent()) return i; + return common::NOT_FOUND; + } +}; + +////////////// + +class client_dropping_thread : public ethread +{ +public: + client_dropping_thread (cromp_server &parent) + : ethread(DROPPING_INTERVAL), + _parent(parent) {} + + void perform_activity(void *formal(ptr)) { +// FUNCDEF("perform_activity"); + _parent.drop_dead_clients(); + } + +private: + cromp_server &_parent; // we perform tricks for this object. +}; + +////////////// + +class connection_management_thread : public ethread +{ +public: + connection_management_thread(cromp_server &parent) + : ethread(), + _parent(parent) {} + + void perform_activity(void *formal(ptr)) { +// FUNCDEF("perform_activity"); + _parent.look_for_clients(*this); + } + +private: + cromp_server &_parent; // we perform tricks for this object. +}; + +////////////// + +#undef LOCK_LISTS +#define LOCK_LISTS auto_synchronizer l(*_list_lock) + // takes over access to the client list and root socket. + +cromp_server::cromp_server(const internet_address &where, + int accepting_threads, bool instantaneous, int max_per_ent) +: cromp_common(cromp_common::chew_hostname(where), max_per_ent), + _clients(new cromp_client_list), + _accepters(new thread_cabinet), + _list_lock(new mutex), + _next_droppage(new time_stamp(DEAD_CLIENT_CLEANING_INTERVAL)), + _instantaneous(instantaneous), + _where(new internet_address(where)), + _accepting_threads(accepting_threads), + _dropper(new client_dropping_thread(*this)), + _enabled(false), + _encrypt_arm(NIL), + _default_security(new cromp_security), + _security_arm(NIL) +{ +// FUNCDEF("constructor"); +} + +cromp_server::~cromp_server() +{ + disable_servers(); + WHACK(_accepters); + WHACK(_dropper); + WHACK(_clients); + WHACK(_next_droppage); + WHACK(_where); + WHACK(_default_security); + WHACK(_list_lock); + _encrypt_arm = NIL; + _security_arm = NIL; +} + +internet_address cromp_server::location() const { return *_where; } + +bool cromp_server::get_sizes(const octopus_entity &id, int &items, int &bytes) +{ return octo()->responses().get_sizes(id, items, bytes); } + +internet_address cromp_server::any_address(int port) +{ + const abyte any_list[] = { 0, 0, 0, 0 }; + return internet_address(byte_array(4, any_list), "", port); +} + +astring cromp_server::responses_text_form() const +{ return octo()->responses().text_form(); } + +int cromp_server::DEFAULT_ACCEPTERS() { + // default number of listening threads; this is the maximum number of mostly + // simultaneous connections that the server can pick up at a time. + return 7; // others are not generally so limited on resources. +} + +infoton *cromp_server::wrap_infoton(infoton * &request, + const octopus_entity &ent) +{ + FUNCDEF("wrap_infoton"); + if (!_enabled) return NIL; + // identity is not wrapped with encryption; we need to establish and identity + // to talk on a distinct channel with the server. even if that identity were + // compromised, the interloper should still not be able to listen in on the + // establishment of an encryption channel. also, the encryption startup + // itself is not encrypted and we don't want to re-encrypt the wrapper. + if (dynamic_cast(request) + || dynamic_cast(request) + || dynamic_cast(request)) return NIL; + +#ifdef DEBUG_CROMP_SERVER + LOG(astring("encrypting ") + request->text_form()); +#endif + + octenc_key_record *key = _encrypt_arm->keys().lock(ent); + // lock here is released a bit down below. + if (!key) { + LOG(astring("failed to locate key for entity ") + ent.text_form()); + return NIL; + } + byte_array packed_request; + infoton::fast_pack(packed_request, *request); + WHACK(request); + encryption_wrapper *to_return = new encryption_wrapper; + key->_key.encrypt(packed_request, to_return->_wrapped); + _encrypt_arm->keys().unlock(key); + return to_return; +} + +outcome cromp_server::enable_servers(bool encrypt, cromp_security *security) +{ + FUNCDEF("enable_servers"); + if (encrypt) { + // add the tentacles needed for encryption. +#ifdef DEBUG_CROMP_SERVER + LOG(astring("enabling encryption for ") + class_name() + + " on " + _where->text_form()); +#endif + _encrypt_arm = new encryption_tentacle; + add_tentacle(_encrypt_arm, true); + add_tentacle(new unwrapping_tentacle, false); + } + WHACK(_security_arm); // in case being reused. + if (security) { + _security_arm = new login_tentacle(*security); + add_tentacle(_security_arm, true); + } else { + _security_arm = new login_tentacle(*_default_security); + add_tentacle(_security_arm, true); + } + open_common(*_where); // open the common ground. + + _enabled = true; + // try first accept, no waiting. + outcome to_return = accept_one_client(false); + if ( (to_return != common::NOT_FOUND) && (to_return != common::OKAY) ) { + LOG(astring("failure starting up server: ") + outcome_name(to_return)); + return to_return; + } + +#ifdef DEBUG_CROMP_SERVER + LOG(a_sprintf("adding %d accepting threads.", _accepting_threads)); +#endif + for (int i = 0; i < _accepting_threads; i++) { + // crank in a new thread and tell it yes on starting it. + _accepters->add_thread(new connection_management_thread(*this), true, NIL); + } + + _dropper->start(NIL); + return OKAY; +} + +void cromp_server::disable_servers() +{ +// FUNCDEF("disable_servers"); + if (!_enabled) return; + _dropper->stop(); // signal the thread to leave when it can. + _accepters->stop_all(); // signal the accepting threads to exit. + if (_clients) { + LOCK_LISTS; + // make sure no one rearranges or uses the client list while we're + // working on it. + for (int i = 0; i < _clients->elements(); i++) { + // stop the client's activities before the big shutdown. + cromp_client_record *cli = (*_clients)[i]; + if (cli) cli->croak(); + } + } + + close_common(); // zap the socket so that our blocked waiters get woken up. + + // now finalize the shutdown. we don't grab the lock because we don't want + // a deadlock, but we also shouldn't need to grab the lock. by here, we have + // cancelled all threads, no new clients should be able to be added, and the + // destruction of this list will ensure that each client's thread really is + // stopped. + WHACK(_clients); + + _enabled = false; // record our defunctivity. +} + +int cromp_server::clients() const +{ + LOCK_LISTS; + return _clients? _clients->elements() : 0; +} + +bool cromp_server::disconnect_entity(const octopus_entity &id) +{ +// FUNCDEF("disconnect_entity"); + if (!_enabled) return false; + LOCK_LISTS; + int indy = _clients->find(id); + if (negative(indy)) return false; // didn't find it. + cromp_client_record *cli = (*_clients)[indy]; + // disconnect the client and zap its entity records. + cli->croak(); + return true; +} + +bool cromp_server::find_entity(const octopus_entity &id, + internet_address &found) +{ +// FUNCDEF("find_entity"); + if (!_enabled) return false; + found = internet_address(); + LOCK_LISTS; + int indy = _clients->find(id); + if (negative(indy)) return false; // didn't find it. + cromp_client_record *cli = (*_clients)[indy]; + // pull out the address from the record at that index. + found = cli->spock()->remote(); + return true; +} + +outcome cromp_server::accept_one_client(bool wait) +{ +#ifdef DEBUG_CROMP_SERVER + FUNCDEF("accept_one_client"); +#endif + if (!_enabled) return common::INCOMPLETE; + spocket *accepted = NIL; +//printf((timestamp(true, true) + "into accept\n").s()); + outcome ret = spock()->accept(accepted, wait); +//printf((timestamp(true, true) + "out of accept\n").s()); + // accept and wait for it to finish. + if ( (ret == spocket::OKAY) && accepted) { + // we got a new client to talk to. + cromp_client_record *adding = new cromp_client_record(*this, accepted, + octo(), *_security_arm); +#ifdef DEBUG_CROMP_SERVER + LOG(a_sprintf("found a new client on sock %d.", accepted->OS_socket())); +#endif + LOCK_LISTS; // short term lock. + _clients->append(adding); + return OKAY; + } else { + if (ret == spocket::NO_CONNECTION) + return NOT_FOUND; // normal occurrence. +#ifdef DEBUG_CROMP_SERVER + LOG(astring("error accepting client: ") + spocket::outcome_name(ret)); +#endif + return DISALLOWED; + } +} + +void cromp_server::look_for_clients(ethread &requestor) +{ + FUNCDEF("look_for_clients"); + if (!_enabled) return; + // see if any clients have been accepted. + while (!requestor.should_stop()) { + outcome ret = accept_one_client(false); + if ( (ret != OKAY) && (ret != NOT_FOUND) ) { + // we got an error condition besides our normal set. +//#ifdef DEBUG_CROMP_SERVER + LOG(astring("got real error on socket; leaving for good.") + + spocket::outcome_name(ret)); +//#endif + break; + } + // if we weren't told we got a client, then we'll sleep. if we did get + // a client, we'll try again right away. + if (ret != OKAY) + time_control::sleep_ms(ACCEPTANCE_SNOOZE); + } +} + +outcome cromp_server::send_to_client(const octopus_request_id &id, + infoton *data) +{ +#ifdef DEBUG_CROMP_SERVER + FUNCDEF("send_to_client"); +#endif + if (!_enabled) return common::INCOMPLETE; + if (!octo()->responses().add_item(data, id)) { +#ifdef DEBUG_CROMP_SERVER + LOG("failed to store result for client--no space left currently."); +#endif + return TOO_FULL; + } + return OKAY; +} + +/*outcome cromp_server::get_any_from_client(const octopus_entity &ent, + infoton * &data, int timeout) +{ + FUNCDEF("get_from_client"); +//hmmm: this implementation locks the lists; can't we get the client to do +// most of the work for this? + LOCK_LISTS; + int indy = _clients->find(id._entity); + if (negative(indy)) return NOT_FOUND; // didn't find it. + cromp_client_record *cli = (*_clients)[indy]; + octopus_request_id id; + return cli->retrieve_and_restore_any(data, ent, timeout); +} +*/ + +outcome cromp_server::get_from_client(const octopus_request_id &id, + infoton * &data, int timeout) +{ +// FUNCDEF("get_from_client"); + if (!_enabled) return common::INCOMPLETE; +//hmmm: this implementation locks the lists; can't we get the client to do +// most of the work for this? + LOCK_LISTS; + int indy = _clients->find(id._entity); + if (negative(indy)) return NOT_FOUND; // didn't find it. + cromp_client_record *cli = (*_clients)[indy]; + return cli->retrieve_and_restore(data, id, timeout); +} + +void cromp_server::drop_dead_clients() +{ +#ifdef DEBUG_CROMP_SERVER + FUNCDEF("drop_dead_clients"); +#endif + if (!_enabled) return; + // clean out any dead clients. + + { + LOCK_LISTS; + if (time_stamp() < *_next_droppage) return; // not time yet. + } + + LOCK_LISTS; // keep locked from now on. + for (int i = 0; i < _clients->elements(); i++) { + cromp_client_record *cli = (*_clients)[i]; + if (!cli) { +#ifdef DEBUG_CROMP_SERVER + LOG(astring("error in list structure.")); +#endif + _clients->zap(i, i); + i--; // skip back before deleted guy. + continue; + } + if (!cli->still_connected() || !cli->healthy()) { +#ifdef DEBUG_CROMP_SERVER + LOG(astring("dropping disconnected client ") + cli->ent().mangled_form()); +#endif + cli->croak(); // stop it from operating. + +//hmmm: check if it has data waiting and complain about it perhaps. + _clients->zap(i, i); + i--; // skip back before deleted guy. + continue; + } + } + + _next_droppage->reset(DEAD_CLIENT_CLEANING_INTERVAL); +} + +} //namespace. + diff --git a/octopi/library/cromp/cromp_server.h b/octopi/library/cromp/cromp_server.h new file mode 100644 index 00000000..bc48e321 --- /dev/null +++ b/octopi/library/cromp/cromp_server.h @@ -0,0 +1,166 @@ +#ifndef CROMP_SERVER_CLASS +#define CROMP_SERVER_CLASS + +/*****************************************************************************\ +* * +* Name : cromp_server * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Services request commands from CROMP clients. Derived objects supply * +* the specific services that a CROMP server implements by providing a set * +* of tentacles that handle the requests. These are added directly to the * +* server's octopus base class. * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "cromp_common.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace cromp { + +// forward. +class client_dropping_thread; +class connection_management_thread; +class cromp_client_list; +class cromp_client_record; +class cromp_security; +class cromp_transaction; + +class cromp_server : public cromp_common +{ +public: + cromp_server(const sockets::internet_address &where, + int accepting_threads = DEFAULT_ACCEPTERS(), + bool instantaneous = true, + int max_per_entity = DEFAULT_MAX_ENTITY_QUEUE); + // creates a server that will open servers on the location "where". the + // number of connections that can be simultaneously handled is passed in + // "accepting_threads". if the "instantaneous" parameter is true, then + // any infotons that need to be handled will be passed to the octopus for + // immediate handling rather than being handled later on a thread. + + virtual ~cromp_server(); + + DEFINE_CLASS_NAME("cromp_server"); + + int clients() const; // returns number of active clients. + + static sockets::internet_address any_address(int port); + // returns an internet_address that should work on any interface that a + // host has. + + sockets::internet_address location() const; + // returns the network location where this server is supposed to reside, + // passed in the constructor. + + bool instantaneous() const { return _instantaneous; } + // reports whether this server uses immediate handling or delayed handling. + + bool enabled() const { return _enabled; } + // reports whether this server has been cranked up or not yet. + + basis::outcome enable_servers(bool encrypt, cromp_security *security = NIL); + // this must be called after construction to start up the object before it + // will accept client requests. if "encrypt" is on, then packets will + // be encrypted and no unencrypted packets will be allowed. if the + // "security" is passed as NIL, then a default security manager is created + // and used. otherwise, the specified "security" object is used and will + // _not_ be destroyed when this object goes away. + + void disable_servers(); + // shuts down the server sockets that this object owns and disables the + // operation of this object overall. the next step is destruction. + + bool find_entity(const octopi::octopus_entity &id, sockets::internet_address &found); + // given an "id" that is currently connected, find the network address + // where it originated and put it in "found". true is returned if the + // entity was located. + + bool disconnect_entity(const octopi::octopus_entity &id); + //!< returns true if the "id" can be found and disconnected. + + basis::outcome send_to_client(const octopi::octopus_request_id &id, octopi::infoton *data); + // blasts a command back to the client identified by the _entity member in + // the "id". this allows the controlling object of the server object to + // asynchronously inject data into the client's incoming stream. the + // client had better know what's going on or this will just lead to + // ignored data that eventually gets trashed. + + basis::outcome get_from_client(const octopi::octopus_request_id &id, octopi::infoton * &data, + int timeout); + // attempts to locate a command with the request "id". if it is found + // within the timeout period, then "data" is set to the command. the + // "data" pointer must be deleted after use. + +// basis::outcome get_any_from_client(const octopi::octopus_entity &ent, octopi::infoton * &data, +// int timeout); + // attempts to grab any waiting package for the entity "ent". + + bool get_sizes(const octopi::octopus_entity &id, int &items, int &bytes); + // promoted from our octopus' entity_data_bin in order to provide some + // accounting of what is stored for the "id". + + basis::astring responses_text_form() const; + // returns a printout of the responses being held here. + + static int DEFAULT_ACCEPTERS(); + // the default number of listening threads. + + // internal use only... + + void look_for_clients(processes::ethread &requester); + // meant to be called by an arbitrary number of threads that can block + // on accepting a new client. control flow will not return from this + // method until the thread's cancel() method has been called. + + void drop_dead_clients(); + // called by a thread to manage dead or dying connections. + + bool encrypting() const { return !!_encrypt_arm; } + // true if this object is encrypting transmissions. + + octopi::infoton *wrap_infoton(octopi::infoton * &request, const octopi::octopus_entity &ent); + // when we're encrypting, this turns "request" into an encryption_wrapper. + // if NIL is returned, then nothing needed to happen to the "request". + +private: + cromp_client_list *_clients; // the active set of clients. + processes::thread_cabinet *_accepters; // the list of accepting threads. + basis::mutex *_list_lock; // protects our lists. + timely::time_stamp *_next_droppage; // times cleanup for dead clients. + bool _instantaneous; // true if the octopus gets driven hard. + sockets::internet_address *_where; // where does the server live. + int _accepting_threads; // the number of accepters we keep going. + client_dropping_thread *_dropper; // cleans our lists. + bool _enabled; // records if this is running or not. + octopi::encryption_tentacle *_encrypt_arm; // the handler for encryption. + cromp_security *_default_security; // used in lieu of other provider. + octopi::login_tentacle *_security_arm; // handles security for the logins. + + basis::outcome accept_one_client(bool wait); + // tries to get just one accepted client. if "wait" is true, then the + // routine will pause until the socket accept returns. +}; + +} //namespace. + +#endif + + diff --git a/octopi/library/cromp/cromp_transaction.cpp b/octopi/library/cromp/cromp_transaction.cpp new file mode 100644 index 00000000..c8b582ba --- /dev/null +++ b/octopi/library/cromp/cromp_transaction.cpp @@ -0,0 +1,280 @@ +/*****************************************************************************\ +* * +* Name : cromp_transaction * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "cromp_transaction.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace basis; +using namespace loggers; +using namespace octopi; +using namespace sockets; +using namespace structures; +using namespace textual; + +namespace cromp { + +//#define DEBUG_CROMP_TRANSACTION + // uncomment for noisy version. + +const int MAXIMUM_TRANSACTION = 100 * MEGABYTE; + // the largest transaction we allow in cromp. if more information needs + // to be passed, then do it in chunks. + +#undef LOG +#ifdef DEBUG_CROMP_TRANSACTION + // since the transaction stuff is so low-level, we risk a feedback loop if + // we log stuff when the program wide logger is itself a communication + // object. + #define LOG(s) CLASS_EMERGENCY_LOG(file_logger(portable::env_string("TMP") + "/cromp_transaction.log"), s) +#else + #define LOG(s) +#endif + +SAFE_STATIC(mutex, __cromp_transaction_lock, ) + +cromp_transaction::~cromp_transaction() +{} + +const char *cromp_transaction::outcome_name(const outcome &to_name) +{ + switch (to_name.value()) { + case WAY_TOO_SMALL: return "WAY_TOO_SMALL"; + case ILLEGAL_LENGTH: return "ILLEGAL_LENGTH"; + default: return communication_commons::outcome_name(to_name); + } +} + +byte_array &cromp_name_array() +{ + static byte_array _hidden_cromp_array; + static bool _initted = false; + if (!_initted) { + auto_synchronizer l(__cromp_transaction_lock()); + // check again in case someone scooped us. + if (!_initted) { + // add the special name field. + attach(_hidden_cromp_array, abyte('c')); + attach(_hidden_cromp_array, abyte('r')); + attach(_hidden_cromp_array, abyte('o')); + attach(_hidden_cromp_array, abyte('m')); + attach(_hidden_cromp_array, abyte('p')); + attach(_hidden_cromp_array, abyte('!')); + // add the space for the length. + for (int i = 0; i < 8; i++) + attach(_hidden_cromp_array, abyte('?')); + _initted = true; + } + } + return _hidden_cromp_array; +} + +int cromp_transaction::minimum_flat_size(const octopus_request_id &id) +{ + return cromp_name_array().length() // cromp identifier in header. + + id.packed_size(); // size of the request id. +} + +int cromp_transaction::minimum_flat_size(const string_array &classifier, + const octopus_request_id &id) +{ + return minimum_flat_size(id) + + infoton::fast_pack_overhead(classifier); + // size required for infoton::fast_pack. +} + +void cromp_transaction::flatten(byte_array &packed_form, + const infoton &request, const octopus_request_id &id) +{ +#ifdef DEBUG_CROMP_TRANSACTION + FUNCDEF("pack"); +#endif + int posn = packed_form.length(); + // save where we started adding. + + packed_form += cromp_name_array(); + // add the cromp prefix and space for the length. + + // add the identifier. + id.pack(packed_form); + + // add the real data. + infoton::fast_pack(packed_form, request); +#ifdef DEBUG_CROMP_TRANSACTION + // make a copy of the packed infoton to compare. + byte_array temp_holding; + infoton::fast_pack(temp_holding, request); +#endif + +//hmmm: check if too big! + + // backpatch the length now. + a_sprintf len_string("%08x", packed_form.length() - posn); +#ifdef DEBUG_CROMP_TRANSACTION + LOG(a_sprintf("len string is %s", len_string.s())); +#endif + for (int j = 6; j < 14; j++) + packed_form[posn + j] = abyte(len_string[j - 6]); + +#ifdef DEBUG_CROMP_TRANSACTION + byte_array copy = packed_form.subarray(posn, packed_form.last()); + byte_array tempo; + octopus_request_id urfid; + if (!cromp_transaction::unflatten(copy, tempo, urfid)) + continuable_error(static_class_name(), func, + "failed to unpack what we just packed."); + else if (urfid != id) + continuable_error(static_class_name(), func, "wrong id after unpack."); + else if (tempo != temp_holding) + continuable_error(static_class_name(), func, "wrong data after unpack."); +#endif + +} + +bool cromp_transaction::unflatten(byte_array &packed_form, + byte_array &still_flat, octopus_request_id &req_id) +{ +#ifdef DEBUG_CROMP_TRANSACTION + FUNCDEF("unflatten"); +#endif + still_flat.reset(); + int len = 0; + // not ready yet. + if (peek_header(packed_form, len) != OKAY) { +#ifdef DEBUG_CROMP_TRANSACTION + LOG("failed to peek the header!"); +#endif + return false; + } + packed_form.zap(0, 14 - 1); + if (!req_id.unpack(packed_form)) return false; + int array_len = len - 14 - req_id.packed_size(); + +#ifdef DEBUG_CROMP_TRANSACTION + if (array_len > packed_form.length()) + continuable_error(static_class_name(), func, + "data needed is insufficient! peek was wrong."); +#endif + + still_flat = packed_form.subarray(0, array_len - 1); + packed_form.zap(0, array_len - 1); + return true; +} + +#define WHACK_AND_GO { packed_form.zap(0, 0); continue; } + +#define CHECK_LENGTH \ + if (packed_form.length() < necessary_length) { \ + /* to this point, we are happy with the contents. */ \ + return true; \ + } \ + necessary_length++; /* require the next higher length. */ + +bool cromp_transaction::resynchronize(byte_array &packed_form) +{ +#ifdef DEBUG_CROMP_TRANSACTION + FUNCDEF("resynchronize"); +#endif + while (true) { + if (!packed_form.length()) { +//#ifdef DEBUG_CROMP_TRANSACTION + LOG("roasted entire contents..."); +//#endif + return false; + } + if (packed_form[0] != 'c') WHACK_AND_GO; + int necessary_length = 2; + CHECK_LENGTH; + if (packed_form[1] != 'r') WHACK_AND_GO; + CHECK_LENGTH; + if (packed_form[2] != 'o') WHACK_AND_GO; + CHECK_LENGTH; + if (packed_form[3] != 'm') WHACK_AND_GO; + CHECK_LENGTH; + if (packed_form[4] != 'p') WHACK_AND_GO; + CHECK_LENGTH; + if (packed_form[5] != '!') WHACK_AND_GO; + for (int k = 6; k < 14; k++) { + CHECK_LENGTH; + if (!parser_bits::is_hexadecimal(packed_form[k])) + WHACK_AND_GO; + } +#ifdef DEBUG_CROMP_TRANSACTION + LOG("found header again..."); +#endif + return true; // looks like we resynched. + } +} + +outcome cromp_transaction::peek_header(const byte_array &packed_form, + int &length) +{ +#ifdef DEBUG_CROMP_TRANSACTION + FUNCDEF("peek_header"); +#endif + length = 0; +#ifdef DEBUG_CROMP_TRANSACTION + LOG("checking for header"); +#endif + if (packed_form.length() < 14) return WAY_TOO_SMALL; + if ( (packed_form[0] != 'c') || (packed_form[1] != 'r') + || (packed_form[2] != 'o') || (packed_form[3] != 'm') + || (packed_form[4] != 'p') || (packed_form[5] != '!') ) + return GARBAGE; +#ifdef DEBUG_CROMP_TRANSACTION + LOG("obvious header bits look fine"); +#endif + + astring len_string; + for (int k = 6; k < 14; k++) { + if (!parser_bits::is_hexadecimal(packed_form[k])) { +#ifdef DEBUG_CROMP_TRANSACTION + LOG("found corruption in hex bytes"); +#endif + return GARBAGE; + } + len_string += char(packed_form[k]); + } +#ifdef DEBUG_CROMP_TRANSACTION + LOG("length was unpacked okay"); +#endif + basis::un_int temp_len = (basis::un_int)length; + int items = sscanf(len_string.s(), "%08x", &temp_len); + length = temp_len; + if (!items) { +#ifdef DEBUG_CROMP_TRANSACTION + LOG(astring("couldn't parse the len_string of: ") + len_string); +#endif + return GARBAGE; + } + +#ifdef DEBUG_CROMP_TRANSACTION + LOG(a_sprintf("length string is %s, len calc is %d and bytes " + "given are %d", len_string.s(), length, packed_form.length())); +#endif + if (length > MAXIMUM_TRANSACTION) return ILLEGAL_LENGTH; + if (length > packed_form.length()) return PARTIAL; + return OKAY; +} + +} //namespace. + diff --git a/octopi/library/cromp/cromp_transaction.h b/octopi/library/cromp/cromp_transaction.h new file mode 100644 index 00000000..04ea2ba0 --- /dev/null +++ b/octopi/library/cromp/cromp_transaction.h @@ -0,0 +1,87 @@ +#ifndef CROMP_TRANSACTION_CLASS +#define CROMP_TRANSACTION_CLASS + +/*****************************************************************************\ +* * +* Name : cromp_transaction * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* The basic structure passed around the network for CROMP requests and * +* responses. * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace cromp { + +////////////// + +class cromp_transaction +{ +public: + virtual ~cromp_transaction(); + DEFINE_CLASS_NAME("cromp_transaction"); + + enum outcomes { + OKAY = basis::common::OKAY, + GARBAGE = basis::common::GARBAGE, + PARTIAL = basis::common::PARTIAL, // header is good but not all data is there yet. + + DEFINE_OUTCOME(WAY_TOO_SMALL, -41, "The package is too small for even " + "a header"), + DEFINE_OUTCOME(ILLEGAL_LENGTH, -42, "The package claims a length larger " + "than we allow") + }; + static const char *outcome_name(const basis::outcome &to_name); + + static void flatten(basis::byte_array &packed_form, const octopi::infoton &request, + const octopi::octopus_request_id &id); + // encapsulate the "request" with the "id" in a wire-friendly format in the + // "packed_form". this makes the infoton a bit more seaworthy out on the + // network using a recognizable header. + + static bool unflatten(basis::byte_array &packed_form, basis::byte_array &still_flat, + octopi::octopus_request_id &id); + // re-inflates the infoton from the "packed_form" as far as retrieving + // the original chunk of bytes in "still_flat". the "id" is also unpacked. + + static int minimum_flat_size(const octopi::octopus_request_id &id); + static int minimum_flat_size(const structures::string_array &classifier, + const octopi::octopus_request_id &id); + // returns the amount of packing overhead added by this class given the + // request "id" that will be used. the second method can include the + // "classifier" also, in order to add in overhead from infoton::fast_pack. + // neither of these methods considers the flat size of the associated + // infoton. + + static bool resynchronize(basis::byte_array &packed_form); + // chows down on the "packed_form" until we see a header or there's + // nothing left in the array. note that if there's a header already + // present, this will stop immediately. be sure to zap at least one + // byte from the front if there was already a header present. + + static basis::outcome peek_header(const basis::byte_array &packed_form, int &length); + // examines the data in "packed_form" and judges whether we think it's + // got a valid transaction there yet or not. the outcome returned is one + // of the peek_outcomes. if the outcome is OKAY or PARTIAL, then + // the operation can be considered successful and the "length" is set to + // the expected size of the "packed_form". however, OKAY is the only + // outcome denoting that the whole package is present. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/cromp/makefile b/octopi/library/cromp/makefile new file mode 100644 index 00000000..0c159861 --- /dev/null +++ b/octopi/library/cromp/makefile @@ -0,0 +1,11 @@ +CONSOLE_MODE = t + +include cpp/variables.def + +PROJECT = cromp +TYPE = library +SOURCE = cromp_client.cpp cromp_common.cpp cromp_security.cpp cromp_server.cpp \ + cromp_transaction.cpp +TARGETS = cromp.lib + +include cpp/rules.def diff --git a/octopi/library/makefile b/octopi/library/makefile new file mode 100644 index 00000000..67b1a329 --- /dev/null +++ b/octopi/library/makefile @@ -0,0 +1,12 @@ +include variables.def + +PROJECT = octopus_libraries +BUILD_BEFORE = octopus \ + sockets \ + tentacles \ + cromp \ + synchronic \ + tests_sockets + +include rules.def + diff --git a/octopi/library/octopus/entity_data_bin.cpp b/octopi/library/octopus/entity_data_bin.cpp new file mode 100644 index 00000000..080e758c --- /dev/null +++ b/octopi/library/octopus/entity_data_bin.cpp @@ -0,0 +1,489 @@ +/*****************************************************************************\ +* * +* Name : entity_data_bin * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "entity_data_bin.h" +#include "entity_defs.h" +#include "infoton.h" +#include "tentacle.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; + +namespace octopi { + +//#define DEBUG_ENTITY_DATA_BIN + // uncomment for more debugging information. + +#undef GRAB_LOCK +#define GRAB_LOCK \ + auto_synchronizer l(*_ent_lock) + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +const int OCTOPUS_TABLE_BITS = 6; + // the hash table for items will have 2^N entries. + +//hmmm: parameterize in class interface? +////const int DATA_DECAY_INTERVAL = 4 * MINUTE_ms; + // if we haven't gotten a data item out to its entity in this long, then + // we assume the entity has croaked or doesn't want its data. + +////////////// + +class infoton_holder +{ +public: + infoton *_item; // the data making up the production. + octopus_request_id _id; // the id, if any, of the original request. + time_stamp _when_added; // when the data became available. + + infoton_holder(const octopus_request_id &id = octopus_request_id(), + infoton *item = NIL) + : _item(item), _id(id), _when_added() {} + + ~infoton_holder() { WHACK(_item); } + + astring text_form() const { + return astring("id=") + _id.text_form() + ", added=" + + _when_added.text_form() + ", item=" + + _item->classifier().text_form() + ", data=" + + _item->text_form(); + } +}; + +////////////// + +class entity_basket : public amorph +{ +public: + time_stamp _last_active; + + astring text_form() const { + astring to_return; + for (int i = 0; i < elements(); i++) + to_return += get(i)->text_form() + parser_bits::platform_eol_to_chars(); + return to_return; + } +}; + +////////////// + +class entity_hasher : public hashing_algorithm +{ +public: + virtual hashing_algorithm *clone() const { return new entity_hasher; } + + virtual basis::un_int hash(const void *key_data, int formal(key_length)) const { + octopus_entity *key = (octopus_entity *)key_data; + // jiggle the pieces of the id into a number. + return basis::un_int( + key->process_id() + + (key->add_in() << 10) + + (key->sequencer() << 14) + + (key->hostname()[0] << 20) + + (key->hostname()[1] << 24) ); + } +}; + +////////////// + +class entity_item_hash +: public hash_table +{ +public: + entity_item_hash(const entity_hasher &hash) + : hash_table(hash, OCTOPUS_TABLE_BITS) + {} +}; + +////////////// + +class basketcase : public structures::set +{ +public: +}; + +////////////// + +// used for our apply methods for communicating back to the caller. +struct apply_struct +{ + basketcase *_empty_baskets; + entity_basket *_any_item; + int &_items_held; // hooks to parent's item count. + int _decay_interval; // how long are items allowed to live? + + apply_struct(int &items_held) + : _empty_baskets(NIL), _any_item(NIL), _items_held(items_held), + _decay_interval(0) {} +}; + +////////////// + +entity_data_bin::entity_data_bin(int max_size_per_entity) +: _table(new entity_item_hash(entity_hasher())), + _ent_lock(new mutex), + _action_count(0), + _max_per_ent(max_size_per_entity), + _items_held(0) +{} + +entity_data_bin::~entity_data_bin() +{ + WHACK(_table); + WHACK(_ent_lock); +} + +int entity_data_bin::entities() const +{ + GRAB_LOCK; + return _table->elements(); +} + +struct text_form_accumulator { astring _accum; }; + +bool text_form_applier(const octopus_entity &formal(key), entity_basket &bask, + void *data_link) +{ + text_form_accumulator *shuttle = (text_form_accumulator *)data_link; + shuttle->_accum += bask.text_form(); + return true; +} + +astring entity_data_bin::text_form() const +{ + GRAB_LOCK; + text_form_accumulator shuttle; + _table->apply(text_form_applier, &shuttle); + return shuttle._accum; +} + +bool scramble_applier(const octopus_entity &formal(key), entity_basket &bask, + void *data_link) +{ + #undef static_class_name + #define static_class_name() "entity_data_bin" +// FUNCDEF("scramble_applier"); + int *county = (int *)data_link; + *county += bask.elements(); + return true; + #undef static_class_name +} + +// this could be extended to do more interesting checks also; currently it's +// just like the entities() method really. +int entity_data_bin::scramble_counter() +{ + GRAB_LOCK; + int count = 0; + _table->apply(scramble_applier, &count); + return count; +} + +#ifdef DEBUG_ENTITY_DATA_BIN + #define DUMP_STATE \ + if ( !(_action_count++ % 100) ) { \ + int items = scramble_counter(); \ + LOG(a_sprintf("-> %d items counted.", items)); \ + } +#else + #define DUMP_STATE +#endif + +bool entity_data_bin::add_item(infoton *to_add, + const octopus_request_id &orig_id) +{ +// FUNCDEF("add_item"); + GRAB_LOCK; + // create a record to add to the appropriate bin. + infoton_holder *holder = new infoton_holder(orig_id, to_add); + + // see if a basket already exists for the entity. + entity_basket *bask = _table->find(orig_id._entity); + if (!bask) { + // this entity doesn't have a basket so add one. + bask = new entity_basket; + _table->add(orig_id._entity, bask); + } + + bask->_last_active = time_stamp(); // reset activity time. + + // count up the current amount of data in use. + int current_size = 0; + for (int i = 0; i < bask->elements(); i++) + current_size += bask->borrow(i)->_item->packed_size(); + + if (current_size + to_add->packed_size() > _max_per_ent) { + WHACK(holder); + return false; + } + + // append the latest production to the list. + bask->append(holder); + _items_held++; + return true; +} + +bool any_item_applier(const octopus_entity &formal(key), entity_basket &bask, + void *data_link) +{ +//#ifdef DEBUG_ENTITY_DATA_BIN +// #define static_class_name() "entity_data_bin" +// FUNCDEF("any_item_applier"); +//#endif + apply_struct *apple = (apply_struct *)data_link; + // check the basket to see if it has any items. + if (!bask.elements()) { +//#ifdef DEBUG_ENTITY_DATA_BIN +// LOG(astring("saw empty basket ") + key.mangled_form()); +//#endif + return true; // continue iterating. + } + apple->_any_item = &bask; + return false; // stop iteration. + #undef static_class_name +} + +infoton *entity_data_bin::acquire_for_any(octopus_request_id &id) +{ + FUNCDEF("acquire_for_any"); + GRAB_LOCK; + apply_struct apple(_items_held); + _table->apply(any_item_applier, &apple); + if (!apple._any_item) return NIL; + DUMP_STATE; + // retrieve the information from our basket that was provided. + infoton_holder *found = apple._any_item->acquire(0); + apple._any_item->zap(0, 0); + if (!apple._any_item->elements()) { + // toss this empty basket. +#ifdef DEBUG_ENTITY_DATA_BIN + LOG(astring("tossing empty basket ") + found->_id._entity.mangled_form()); +#endif + _table->zap(found->_id._entity); + } + apple._any_item = NIL; + infoton *to_return = found->_item; + id = found->_id; + found->_item = NIL; // clear so it won't be whacked. + WHACK(found); + _items_held--; +//#ifdef DEBUG_ENTITY_DATA_BIN + if (_items_held < 0) + LOG("logic error: number of items went below zero."); +//#endif + return to_return; +} + +int entity_data_bin::acquire_for_entity(const octopus_entity &requester, + infoton_list &items, int maximum_size) +{ +// FUNCDEF("acquire_for_entity [multiple]"); + // this method does not grab the lock because it simply composes other + // class methods without interacting with class data members. + items.reset(); + if (maximum_size <= 0) maximum_size = 20 * KILOBYTE; + // pick a reasonable default. + octopus_request_id id; + int items_found = 0; + while (maximum_size > 0) { + infoton *inf = acquire_for_entity(requester, id); + if (!inf) + break; // none left. + items.append(new infoton_id_pair(inf, id)); + maximum_size -= inf->packed_size(); + items_found++; + } + return items_found; +} + +infoton *entity_data_bin::acquire_for_entity(const octopus_entity &requester, + octopus_request_id &id) +{ + FUNCDEF("acquire_for_entity [single]"); + id = octopus_request_id(); // reset it. + GRAB_LOCK; + infoton *to_return = NIL; + entity_basket *bask = _table->find(requester); + if (!bask) { + return NIL; + } + if (!bask->elements()) { +#ifdef DEBUG_ENTITY_DATA_BIN + LOG(astring("tossing empty basket ") + requester.mangled_form()); +#endif + _table->zap(requester); + return NIL; + } + DUMP_STATE; + id = bask->get(0)->_id; + to_return = bask->borrow(0)->_item; + bask->borrow(0)->_item = NIL; + bask->zap(0, 0); + if (!bask->elements()) { +#ifdef DEBUG_ENTITY_DATA_BIN + LOG(astring("tossing empty basket ") + requester.mangled_form()); +#endif + _table->zap(requester); + } + _items_held--; +//#ifdef DEBUG_ENTITY_DATA_BIN + if (_items_held < 0) + LOG("logic error: number of items went below zero."); +//#endif + return to_return; +} + +infoton *entity_data_bin::acquire_for_identifier(const octopus_request_id &id) +{ + FUNCDEF("acquire_for_identifier"); + infoton *to_return = NIL; + GRAB_LOCK; + entity_basket *bask = _table->find(id._entity); + if (!bask) return NIL; + if (!bask->elements()) { +#ifdef DEBUG_ENTITY_DATA_BIN + LOG(astring("tossing empty basket ") + id._entity.mangled_form()); +#endif + _table->zap(id._entity); + return NIL; + } + for (int i = 0; i < bask->elements(); i++) { + if (bask->get(i)->_id == id) { + to_return = bask->borrow(i)->_item; // snag the item. + bask->borrow(i)->_item = NIL; // clear the list's version out. + bask->zap(i, i); // whack the sanitized element. + DUMP_STATE; + if (!bask->elements()) { +#ifdef DEBUG_ENTITY_DATA_BIN + LOG(astring("tossing empty basket ") + id._entity.mangled_form()); +#endif + _table->zap(id._entity); + } + _items_held--; +//#ifdef DEBUG_ENTITY_DATA_BIN + if (_items_held < 0) + LOG("logic error: number of items went below zero."); +//#endif + return to_return; + } + } + return NIL; +} + +bool cleaning_applier(const octopus_entity &key, entity_basket &bask, + void *data_link) +{ + #define static_class_name() "entity_data_bin" + FUNCDEF("cleaning_applier"); + apply_struct *apple = (apply_struct *)data_link; + time_stamp expiration_time(-apple->_decay_interval); + + int whack_count = 0; + for (int i = 0; i < bask.elements(); i++) { + infoton_holder &rec = *bask.borrow(i); + if (rec._when_added <= expiration_time) { + // if a requester hasn't picked this up in N seconds, then drop it. +#ifdef DEBUG_ENTITY_DATA_BIN + LOG(astring("whacking old item ") + rec._id.text_form()); +#endif + whack_count++; + apple->_items_held--; +//#ifdef DEBUG_ENTITY_DATA_BIN + if (apple->_items_held < 0) + LOG("logic error: number of items went below zero."); +//#endif + bask.zap(i, i); + i--; // skip back before the delete. + } else { + // NOTE: this break is based on an assumption about the storage of + // items; if it's ever the case in the future that items can be + // disordered on time of arrival in the queue, then the break should + // be removed. + break; + } + } +#ifdef DEBUG_ENTITY_DATA_BIN + if (whack_count) + LOG(a_sprintf("==> whacked %d old items.", whack_count)); +#endif + if (!bask.elements()) { + // if the basket has nothing left in it then we signal the parent that + // it can be deleted. +//LOG("adding to empty basket list."); + *apple->_empty_baskets += key; +//LOG("added to empty basket list."); + } + + // keep iterating on items unless we know it's time to go. + return true; + #undef static_class_name +} + +void entity_data_bin::clean_out_deadwood(int decay_interval) +{ +#ifdef DEBUG_ENTITY_DATA_BIN + FUNCDEF("clean_out_deadwood"); +#endif + GRAB_LOCK; + // check that no items have timed out. + apply_struct apple(_items_held); + basketcase empty_baskets; + apple._empty_baskets = &empty_baskets; + apple._decay_interval = decay_interval; + _table->apply(cleaning_applier, &apple); + + // clean up any entities whose baskets are empty. + for (int i = empty_baskets.length() - 1; i >= 0; i--) { +#ifdef DEBUG_ENTITY_DATA_BIN + LOG(astring("removing basket ") + empty_baskets.get(i).mangled_form()); +#endif + _table->zap(empty_baskets.get(i)); + empty_baskets.zap(i, i); + // we don't skip back since we're scanning the array from its end. + } +} + +bool entity_data_bin::get_sizes(const octopus_entity &id, int &items, + int &bytes) +{ +// FUNCDEF("get_sizes"); + items = 0; + bytes = 0; + GRAB_LOCK; + entity_basket *bask = _table->find(id); + if (!bask || !bask->elements()) return false; + items = bask->elements(); + for (int i = 0; i < bask->elements(); i++) + bytes += bask->borrow(i)->_item->packed_size(); + return true; +} + +} //namespace. + diff --git a/octopi/library/octopus/entity_data_bin.h b/octopi/library/octopus/entity_data_bin.h new file mode 100644 index 00000000..024644e8 --- /dev/null +++ b/octopi/library/octopus/entity_data_bin.h @@ -0,0 +1,128 @@ +#ifndef ENTITY_DATA_BIN_CLASS +#define ENTITY_DATA_BIN_CLASS + +/*****************************************************************************\ +* * +* Name : entity_data_bin * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include + +namespace octopi { + +// forward. +class basketcase; +class entity_basket; +class entity_item_hash; +class infoton; +class infoton_list; +class octopus_entity; +class octopus_request_id; + +//! Stores a set of infotons grouped by the entity that owns them. + +class entity_data_bin +{ +public: + entity_data_bin(int max_bytes_per_entity); + //!< allows each entity in the bin to have "max_bytes_per_entity" bytes stored. + /*!< any storage attempts that would go beyond that limit are rejected. */ + + virtual ~entity_data_bin(); + + DEFINE_CLASS_NAME("entity_data_bin"); + + int max_bytes_per_entity() const { return _max_per_ent; } + // reports the maximum size allowed per entity for storage. + void max_bytes_per_entity(int max_bytes_per) { _max_per_ent = max_bytes_per; } + // allows resetting of the storage size allowed per entity. note that if + // this value is made smaller and the bin is already holding more than + // the new limit, then no additional stores will be allowed until some of + // the data is removed. + + int entities() const; + // returns the number of entities that currently possess storage bins. + // this is a very costly call. + + int items_held() const { return _items_held; } + // returns the number of items held here, if any. this is a very + // inexpensive call that should be used prior to checking for data. + // it's safe to check this at any time, since it's just an int. there's + // every likelihood that the number might change by the time one acquires + // the lock on the bin, but if it's zero then that's a good reason to + // avoid looking for data yet. + + bool get_sizes(const octopus_entity &id, int &items, int &bytes); + // finds the storage for "id". if there is any there, true is returned + // and "items" is set to the number of pending items and "bytes" is set + // to the number of bytes for those items. + + bool add_item(infoton *to_add, const octopus_request_id &id); + // stores an item "to_add" for an entity listed in "id". if the item + // cannot be stored due to space constraints, false is returned and + // "to_add" is deleted. + + infoton *acquire_for_identifier(const octopus_request_id &id); + // locates an item for the specific "id". this will generally be a + // response to a previous request. if no object can be found that matches + // the "id", then NIL is returned. + + infoton *acquire_for_entity(const octopus_entity &requester, + octopus_request_id &id); + // this returns an infoton for the "requester", if any are available. call + // this function repeatedly to ensure that all available items have + // been provided. the "original_id" is a copy of the "item_id" that was + // originally passed to evaluate_request(). the returned object must + // eventually be destroyed if non-NIL. + + int acquire_for_entity(const octopus_entity &requester, + infoton_list &items, int maximum_size); + // retrieves up to "maximum_size" in bytes of pending items for the + // "requester" into "items". the number of items found is returned. + + infoton *acquire_for_any(octopus_request_id &id); + // acquires an infoton for any random entity. if no items are ready at + // all, then NIL is returned. + + basis::astring text_form() const; + // returns a textual list of what's held here. + + void clean_out_deadwood(int decay_interval = 4 * basis::MINUTE_ms); + // gets rid of any items that haven't been picked up in a timely manner. + // note that this should be called periodically by the controlling object. + // it will not be called automatically. + +private: + entity_item_hash *_table; // our main storage object. + basis::mutex *_ent_lock; // protects our structures. + int _action_count; + // used for debugging; tracks how many acquires have occurred since the + // last dump of item count. + int _max_per_ent; // the maximum size allowed per entity. + int _items_held; // the number of items in residence. + + friend class monk_the_detective; // eerie supernatural powers, for testing. + + int scramble_counter(); // counts the number of items used. + + // not available. + entity_data_bin(const entity_data_bin &); + entity_data_bin &operator =(const entity_data_bin &); +}; + +} //namespace. + +#endif + diff --git a/octopi/library/octopus/entity_defs.cpp b/octopi/library/octopus/entity_defs.cpp new file mode 100644 index 00000000..5b16956e --- /dev/null +++ b/octopi/library/octopus/entity_defs.cpp @@ -0,0 +1,216 @@ +/*****************************************************************************\ +* * +* Name : entity definitions for octopus * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "entity_defs.h" + +#include +#include +#include +#include +#include +#include +#include + +#include // for sscanf. + +using namespace basis; +using namespace configuration; +using namespace mathematics; +using namespace textual; +using namespace structures; + +namespace octopi { + +octopus_entity::octopus_entity() +: _hostname(new astring), + _pid(0), + _sequencer(0), + _add_in(0) +{} + +octopus_entity::octopus_entity(const astring &hostname, + int process_id, int sequencer, int add_in) +: _hostname(new astring(hostname)), + _pid(process_id), + _sequencer(sequencer), + _add_in(add_in) +{} + +octopus_entity::octopus_entity(const octopus_entity &to_copy) +: packable(), + _hostname(new astring) +{ operator = (to_copy); } + +octopus_entity::~octopus_entity() { WHACK(_hostname); } + +octopus_entity octopus_entity::from_text(const astring &to_convert) +{ + astring host; + int process_id; + int sequencer; + int add_in; + breakout(to_convert, host, process_id, sequencer, add_in); + + byte_array temp_form; + byte_formatter::string_to_bytes(host, temp_form); + host = astring((char *)temp_form.observe()); + + return octopus_entity(host, process_id, sequencer, add_in); +} + +octopus_entity &octopus_entity::operator =(const octopus_entity &to_copy) +{ + if (this == &to_copy) return *this; + *_hostname = *to_copy._hostname; + _pid = to_copy._pid; + _sequencer = to_copy._sequencer; + _add_in = to_copy._add_in; + return *this; +} + +const astring &octopus_entity::hostname() const { return *_hostname; } + +int octopus_entity::process_id() const { return _pid; } + +int octopus_entity::sequencer() const { return _sequencer; } + +int octopus_entity::add_in() const { return _add_in; } + +void octopus_entity::process_id(int id) { _pid = id; } + +void octopus_entity::sequencer(int seq) { _sequencer = seq; } + +void octopus_entity::add_in(int add) { _add_in = add; } + +void octopus_entity::hostname(const astring &new_host) +{ *_hostname = new_host; } + +bool octopus_entity::blank() const +{ return !_sequencer && !_add_in && !_pid && _hostname->empty(); } + +int octopus_entity::packed_size() const +{ return sizeof(int) * 3 + _hostname->length() + 1; } + +#define REPLACEMENT_CHARACTER '#' + // used to replace unprintable characters in the entity text_form(). + +astring octopus_entity::text_form() const +{ + astring chewed_host = *_hostname; + // make sure the name we're going to show is totally printable. some + // users of entities have odd notions of hostname strings. there are no + // rules requiring these to be readable normal strings. + for (int i = 0; i < chewed_host.length(); i++) { + if (!parser_bits::is_printable_ascii(chewed_host[i])) + chewed_host[i] = REPLACEMENT_CHARACTER; + } + return a_sprintf("%d.%d.%d.%s", _add_in, _sequencer, _pid, chewed_host.s()); +} + +astring octopus_entity::mangled_form() const +{ + astring hostdump; + byte_array temp_form(_hostname->length() + 1, (abyte *)_hostname->observe()); + byte_formatter::bytes_to_string(temp_form, hostdump, false); + return a_sprintf("%d.%d.%d.%s", _add_in, _sequencer, _pid, hostdump.s()); +} + +void octopus_entity::breakout(const astring &mangled_form, astring &hostname, + int &process_id, int &sequencer, int &add_in) +{ + // there is pretty much no error checking here. + astring dupe = mangled_form; // allows us to destroy the id. + sscanf(dupe.s(), "%d", &add_in); + dupe.zap(0, dupe.find('.')); + sscanf(dupe.s(), "%d", &sequencer); + dupe.zap(0, dupe.find('.')); + sscanf(dupe.s(), "%d", &process_id); + dupe.zap(0, dupe.find('.')); + hostname = dupe; +} + +bool octopus_entity::operator == (const octopus_entity &that) const +{ + return (_add_in == that._add_in) + && (_pid == that._pid) + && (_sequencer == that._sequencer) + && (*_hostname == *that._hostname); +} + +void octopus_entity::pack(byte_array &packed_form) const +{ + _hostname->pack(packed_form); + attach(packed_form, _pid); + attach(packed_form, _sequencer); + attach(packed_form, _add_in); +} + +bool octopus_entity::unpack(byte_array &packed_form) +{ + if (!_hostname->unpack(packed_form)) return false; + if (!detach(packed_form, _pid)) return false; + if (!detach(packed_form, _sequencer)) return false; + if (!detach(packed_form, _add_in)) return false; + return true; +} + +////////////// + +int octopus_request_id::packed_size() const +{ return _entity.packed_size() + sizeof(int); } + +astring octopus_request_id::mangled_form() const +{ return _entity.mangled_form() + a_sprintf("/%d", _request_num); } + +astring octopus_request_id::text_form() const +{ return _entity.text_form() + a_sprintf("/%d", _request_num); } + +bool octopus_request_id::blank() const +{ return _entity.blank() || !_request_num; } + +octopus_request_id octopus_request_id::randomized_id() +{ + chaos randona; + return octopus_request_id( + octopus_entity(string_manipulation::make_random_name(), + application_configuration::process_id(), randona.inclusive(0, MAXINT32 / 2), + randona.inclusive(0, MAXINT32 / 2)), + randona.inclusive(0, MAXINT32 / 2)); +} + +octopus_request_id octopus_request_id::from_text(const astring &to_convert) +{ + // find the slash, starting at the end. + int indy = to_convert.find('/', to_convert.end(), true); + if (negative(indy)) return octopus_request_id(); // bad format. + astring partial = to_convert.substring(0, indy - 1); + int req_id = to_convert.substring(indy + 1, to_convert.end()).convert(0); + return octopus_request_id(octopus_entity::from_text(partial), req_id); +} + +void octopus_request_id::pack(byte_array &packed_form) const +{ + _entity.pack(packed_form); + attach(packed_form, _request_num); +} + +bool octopus_request_id::unpack(byte_array &packed_form) +{ + if (!_entity.unpack(packed_form)) return false; + if (!detach(packed_form, _request_num)) return false; + return true; +} + +} // namespace. + diff --git a/octopi/library/octopus/entity_defs.h b/octopi/library/octopus/entity_defs.h new file mode 100644 index 00000000..3a270918 --- /dev/null +++ b/octopi/library/octopus/entity_defs.h @@ -0,0 +1,186 @@ +#ifndef ENTITY_DEFINITIONS_GROUP +#define ENTITY_DEFINITIONS_GROUP + +/*****************************************************************************\ +* * +* Name : various definitions for octopus * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "infoton.h" + +#include +#include +#include +#include +#include +#include + +namespace octopi { + +//! Provides a way of identifying users of an octopus object. +/*! + NOTE: this is a heavy-weight header intended for forward declaration. +*/ + +class octopus_entity : public virtual basis::packable, public virtual basis::text_formable +{ +public: + octopus_entity(); //!< blank constructor. + + octopus_entity(const basis::astring &hostname, int process_id, int sequencer, + int add_in); + //!< constructor taking all the available parameters for an entity. + /*!< produces an id in the proper format given all the components. note + that the hostname must be some fully qualified name for the host, such + that it is as unique as you want names within the system to be. */ + + octopus_entity(const octopus_entity &to_copy); + + ~octopus_entity(); + + octopus_entity &operator = (const octopus_entity &to_copy); + + DEFINE_CLASS_NAME("octopus_entity"); + + bool blank() const; + //!< true if the entity is blank, as constructed by default constructor. + + // comparison operators. + bool operator == (const octopus_entity &that) const; + + const basis::astring &hostname() const; //!< returns the hostname portion of the id. + int process_id() const; //!< returns the process number in the id. + int sequencer() const; //!< returns the sequencing number from the id. + int add_in() const; //!< returns the random add-in from the id. + + void hostname(const basis::astring &new_host); //!< set the host. + void process_id(int id); //!< set the process id. + void sequencer(int seq); //!< set the sequencer value. + void add_in(int add); //!< set the add-in value. + + basis::astring mangled_form() const; + //!< returns the combined string form of the identifier. + + basis::astring text_form() const; + //!< returns a readable form of the identifier. + + virtual void text_form(basis::base_string &to_fill) const { + to_fill.assign(text_form()); + } + + basis::astring to_text() const { return mangled_form(); } + //!< conversion to text format for display. + static octopus_entity from_text(const basis::astring &to_convert); + //!< conversion from text format, parsing parameters out. + + static void breakout(const basis::astring &mangled_form, basis::astring &hostname, + int &process_id, int &sequencer, int &add_in); + //!< takes a "mangled_form" of an entity id and retrieves the components. + + int packed_size() const; + //!< reports how large the packed entity will be. + + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); + +private: + basis::astring *_hostname; + int _pid; + int _sequencer; + int _add_in; +}; + +////////////// + +//! Identifies requests made on an octopus by users. +/*! + Uniquely identifies a request passed to an octopus server given that + the number of requests from one entity can be contained within the range + of signed integers. if the requests come fast enough for the request + numbers to wrap around, and if the requests can remain outstanding for a + really long time, it may be wise to create a new login. +*/ + +class octopus_request_id : public virtual basis::packable +{ +public: + octopus_entity _entity; //!< the entity. + int _request_num; //!< the item number from the entity. + + octopus_request_id() : _entity(), _request_num(0) {} + + octopus_request_id(const octopus_entity &entity, int request_num) + : _entity(entity), _request_num(request_num) {} + + static octopus_request_id randomized_id(); + //!< provides a pre-randomized request id. + /*!< this should only be used for the very first request made to an + octopus, in order to obtain a valid identity. */ + + bool operator == (const octopus_request_id &that) const + { return (_entity == that._entity) + && (_request_num == that._request_num); } + + bool blank() const; + //!< returns true if this is a blank id (as constructed by default ctor). + + int packed_size() const; + //!< reports how large the packed id will be. + + basis::astring mangled_form() const; //!< similar to entity id. + + basis::astring text_form() const; //!< human readable form of the request. + + basis::astring to_text() const { return mangled_form(); } + static octopus_request_id from_text(const basis::astring &to_convert); + + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); +}; + +//! a collection of unique request ids. +class octopus_request_id_set : public structures::set +{ +public: + octopus_request_id_set() {} + + octopus_request_id_set(const structures::set &orig) + : structures::set(orig) {} +}; + +////////////// + +//! implements a list of waiting infotons. +/*! the actual infoton plus its request id are stored. */ + +class infoton_id_pair : public virtual basis::root_object +{ +public: + infoton *_data; + octopus_request_id _id; + + infoton_id_pair(infoton *data, const octopus_request_id &id) + : _data(data), _id(id) {} + + ~infoton_id_pair() { + delete _data; + _data = NIL; + } +}; + +//! a list of pending requests and who made them. +class infoton_list : public structures::amorph {}; + +} //namespace. + +#endif + diff --git a/octopi/library/octopus/identity_infoton.cpp b/octopi/library/octopus/identity_infoton.cpp new file mode 100644 index 00000000..d15abe35 --- /dev/null +++ b/octopi/library/octopus/identity_infoton.cpp @@ -0,0 +1,82 @@ +/*****************************************************************************\ +* * +* Name : identity_infoton * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "identity_infoton.h" +#include "tentacle.h" + +#include +#include +#include +#include + +using namespace basis; +using namespace structures; + +namespace octopi { + +identity_infoton::identity_infoton() +: infoton(identity_classifier()), + _new_name() +{} + +identity_infoton::identity_infoton(const octopus_entity &uid) +: infoton(identity_classifier()), + _new_name(uid) +{} + +identity_infoton::identity_infoton(const identity_infoton &to_copy) +: root_object(), + infoton(to_copy), + _new_name(to_copy._new_name) +{} + +identity_infoton::~identity_infoton() {} + +void identity_infoton::text_form(base_string &fill) const +{ + astring ent_info; + _new_name.text_form(ent_info); + fill.assign(astring("entity=") + ent_info); +} + +identity_infoton &identity_infoton::operator =(const identity_infoton &to_copy) +{ + if (this == &to_copy) return *this; + set_classifier(to_copy.classifier()); + _new_name = to_copy._new_name; + return *this; +} + +const astring identity_classifier_strings[] = { "#octide" }; + +SAFE_STATIC_CONST(string_array, identity_infoton::identity_classifier, + (1, identity_classifier_strings)) + +int identity_infoton::packed_size() const { return _new_name.packed_size(); } + +clonable *identity_infoton::clone() const +{ return cloner(*this); } + +void identity_infoton::pack(byte_array &packed_form) const +{ _new_name.pack(packed_form); } + +bool identity_infoton::unpack(byte_array &packed_form) +{ + if (!_new_name.unpack(packed_form)) return false; + return true; +} + +} //namespace. + + diff --git a/octopi/library/octopus/identity_infoton.h b/octopi/library/octopus/identity_infoton.h new file mode 100644 index 00000000..9714d952 --- /dev/null +++ b/octopi/library/octopus/identity_infoton.h @@ -0,0 +1,60 @@ +#ifndef IDENTITY_INFOTON_CLASS +#define IDENTITY_INFOTON_CLASS + +/*****************************************************************************\ +* * +* Name : identity_infoton * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "entity_defs.h" +#include "infoton.h" + +namespace octopi { + +//! Encapsulates just the action of identifying an octopus user. +/*! + This must be done for an entity before it can begin to request octopus + services when there is strong security in place. By default, the octopus + does not require this. +*/ + +class identity_infoton : public infoton +{ +public: + octopus_entity _new_name; + + identity_infoton(); + identity_infoton(const octopus_entity &name); + identity_infoton(const identity_infoton &to_copy); + + virtual ~identity_infoton(); + + DEFINE_CLASS_NAME("identity_infoton"); + + identity_infoton &operator = (const identity_infoton &to_copy); + + static const structures::string_array &identity_classifier(); + //!< returns the classifier for this type of infoton. + + virtual void text_form(basis::base_string &fill) const; + + virtual int packed_size() const; + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); + + virtual clonable *clone() const; +}; + +} //namespace. + +#endif + diff --git a/octopi/library/octopus/identity_tentacle.cpp b/octopi/library/octopus/identity_tentacle.cpp new file mode 100644 index 00000000..c83b4ce2 --- /dev/null +++ b/octopi/library/octopus/identity_tentacle.cpp @@ -0,0 +1,100 @@ +/*****************************************************************************\ +* * +* Name : identity_tentacle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "identity_tentacle.h" +#include "identity_infoton.h" +#include "octopus.h" + +#include +#include + +using namespace basis; +using namespace structures; +using namespace timely; + +namespace octopi { + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +//#define DEBUG_IDENTITY_TENTACLE + // uncomment for debugging version. + +////////////// + +identity_tentacle::identity_tentacle(octopus &parent) +: tentacle_helper(identity_infoton::identity_classifier(), + false), + _parent(parent) +{} + +identity_tentacle::~identity_tentacle() {} + +outcome identity_tentacle::reconstitute(const string_array &classifier, + byte_array &packed_form, infoton * &reformed) +{ + if (classifier != identity_infoton::identity_classifier()) + return NO_HANDLER; + + return reconstituter(classifier, packed_form, reformed, + (identity_infoton *)NIL); +} + +outcome identity_tentacle::consume(infoton &to_chow, + const octopus_request_id &item_id, byte_array &transformed) +{ +#ifdef DEBUG_IDENTITY_TENTACLE + FUNCDEF("consume"); +#endif + transformed.reset(); + identity_infoton *inf = dynamic_cast(&to_chow); + if (!inf) { + // if the infoton doesn't cast, then it is not for us. we need to vet + // that the identity looks pretty much okay. + +//hmmm: check host? +// that would imply that all users of octopi have correctly identified +// themselves. this is not currently the case. we need a way to +// automate that step for a user of an octopus? +bool uhhh = true; + + if (uhhh) { + // this infoton's entity was allowed, so we call it partially processed. + return PARTIAL; + } +#ifdef DEBUG_IDENTITY_TENTACLE + LOG(astring("denying infoton ") + item_id.mangled_form()); +#endif + // the infoton's identity is invalid; it needs to be dropped. + return DISALLOWED; + } + +#ifdef DEBUG_IDENTITY_TENTACLE + LOG(astring("old name, storing under: ") + item_id.mangled_form()); +#endif + + // this is definitely for an identity request now. + inf->_new_name = _parent.issue_identity(); + +#ifdef DEBUG_IDENTITY_TENTACLE + LOG(astring("new name: ") + inf->_new_name.mangled_form()); +#endif + + if (!store_product(dynamic_cast(inf->clone()), item_id)) + return NO_SPACE; + return OKAY; +} + +} //namespace. + diff --git a/octopi/library/octopus/identity_tentacle.h b/octopi/library/octopus/identity_tentacle.h new file mode 100644 index 00000000..be400cd0 --- /dev/null +++ b/octopi/library/octopus/identity_tentacle.h @@ -0,0 +1,61 @@ +#ifndef IDENTITY_TENTACLE_CLASS +#define IDENTITY_TENTACLE_CLASS + +/*****************************************************************************\ +* * +* Name : identity_tentacle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "identity_infoton.h" +#include "tentacle_helper.h" + +#include +#include +#include + +namespace octopi { + +//! Supports an early step in using octopus services: getting an identity. + +class identity_tentacle +: public tentacle_helper +{ +public: + identity_tentacle(octopus &parent); + //!< the "parent" will provide the real identity services. + + virtual ~identity_tentacle(); + + DEFINE_CLASS_NAME("identity_tentacle"); + + virtual basis::outcome reconstitute(const structures::string_array &classifier, + basis::byte_array &packed_form, infoton * &reformed); + //!< reinflates an infoton given that we know the type in "classifier". + /*!< recreates a "reformed" infoton from the "classifier" and packed + infoton data in "packed_form". this will only succeed if the classifier's + first name is understood here. */ + + virtual basis::outcome consume(infoton &to_chow, const octopus_request_id &item_id, + basis::byte_array &transformed); + //!< chews on the "to_chow" infoton to perform the requested action. + /*!< if it's an identity_infoton, then a new identity is provided. + otherwise, the identity is given rudimentary checks for validity and + the infoton is passed along. */ + +private: + octopus &_parent; //!< provides the real identification service. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/octopus/infoton.cpp b/octopi/library/octopus/infoton.cpp new file mode 100644 index 00000000..15f817d1 --- /dev/null +++ b/octopi/library/octopus/infoton.cpp @@ -0,0 +1,250 @@ +/*****************************************************************************\ +* * +* Name : infoton * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "infoton.h" + +#include +#include +#include +#include +#include + +using namespace basis; +using namespace loggers; +using namespace structures; +using namespace textual; + +namespace octopi { + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +//#define DEBUG_INFOTON + // if uncommented, then extra checks are made. + +const abyte FAST_PACK_VERSION = 0x14; + // this single byte version number should be increased when the network + // protocol changes. it only ensures that the fast_pack method will reject + // a lower version. + +infoton::infoton(const string_array &classifier) +: _classifier(new string_array(classifier)) +{ +// FUNCDEF("constructor [string_array]"); +} + +infoton::infoton(const astring &class_1) +: _classifier(new string_array) +{ +// FUNCDEF("constructor [one string]"); + *_classifier += class_1; +} + +infoton::infoton(const astring &class_1, const astring &class_2) +: _classifier(new string_array) +{ +// FUNCDEF("constructor [two strings]"); + *_classifier += class_1; + *_classifier += class_2; +} + +infoton::infoton(const astring &class_1, const astring &class_2, + const astring &class_3) +: _classifier(new string_array) +{ +// FUNCDEF("constructor [three strings]"); + *_classifier += class_1; + *_classifier += class_2; + *_classifier += class_3; +} + +infoton::infoton(const infoton &to_copy) +: root_object(), + packable(), + clonable(), + _classifier(new string_array(*to_copy._classifier)) +{} + +infoton::~infoton() +{ WHACK(_classifier); } + +infoton &infoton::operator = (const infoton &to_copy) +{ *_classifier = *to_copy._classifier; return *this; } + +const string_array &infoton::classifier() const +{ return *_classifier; } + +bool infoton::check_classifier(const astring &classname, const astring &caller) +{ + bool to_return = true; + if (!_classifier->length()) + to_return = false; + for (int i = 0; i < _classifier->length(); i++) { + if (!(*_classifier)[i].length()) + to_return = false; + } + if (!to_return) { + program_wide_logger::get().log(classname + "::" + caller + + ": invalid classifier provided.", ALWAYS_PRINT); + } + return to_return; +} + +void infoton::set_classifier(const string_array &new_classifier) +{ +#ifdef DEBUG_INFOTON + FUNCDEF("set_classifier [string_array]"); +#endif + *_classifier = new_classifier; +#ifdef DEBUG_INFOTON + check_classifier(class_name(), func); +#endif +} + +void infoton::set_classifier(const astring &class_1) +{ +#ifdef DEBUG_INFOTON + FUNCDEF("set_classifier [1 string]"); +#endif + _classifier->reset(); + *_classifier += class_1; +#ifdef DEBUG_INFOTON + check_classifier(class_name(), func); +#endif +} + +void infoton::set_classifier(const astring &class_1, const astring &class_2) +{ +#ifdef DEBUG_INFOTON + FUNCDEF("set_classifier [2 strings]"); +#endif + _classifier->reset(); + *_classifier += class_1; + *_classifier += class_2; +#ifdef DEBUG_INFOTON + check_classifier(class_name(), func); +#endif +} + +void infoton::set_classifier(const astring &class_1, const astring &class_2, + const astring &class_3) +{ +#ifdef DEBUG_INFOTON + FUNCDEF("set_classifier [3 strings]"); +#endif + _classifier->reset(); + *_classifier += class_1; + *_classifier += class_2; + *_classifier += class_3; +#ifdef DEBUG_INFOTON + check_classifier(class_name(), func); +#endif +} + +int infoton::fast_pack_overhead(const string_array &classifier) +{ + return classifier.packed_size() // for classifier. + + sizeof(int) // for the package size. + + 1; // for the version byte. +} + +void infoton::fast_pack(byte_array &packed_form, const infoton &to_pack) +{ +// FUNCDEF("fast_pack"); + structures::attach(packed_form, FAST_PACK_VERSION); + // add the tasty version byte as the very first item. + structures::pack_array(packed_form, to_pack.classifier()); + // must first put the packed infoton into a byte array, then use the + // byte array's packing support. + int len_prior = packed_form.length(); + structures::attach(packed_form, int(0)); + // save space for length. +//hmmm: this could use obscure_pack for more reliability. + to_pack.pack(packed_form); + int added_len = packed_form.length() - sizeof(int) - len_prior; + + // shift in the length in the place where we made space. + basis::un_int temp = basis::un_int(added_len); + for (basis::un_int i = 0; i < sizeof(int); i++) { + packed_form[len_prior + i] = abyte(temp % 0x100); + temp >>= 8; + } +} + +bool infoton::test_fast_unpack(const byte_array &packed_form, + int &packed_length) +{ +// FUNCDEF("test_fast_unpack"); + packed_length = 0; + if (!packed_form.length()) return false; + + // make sure we have the right version number, first. + if (packed_form[0] != FAST_PACK_VERSION) + return false; + + un_int strings_held = 0; + byte_array len_bytes = packed_form.subarray(1, 2 * sizeof(int)); + if (!structures::obscure_detach(len_bytes, strings_held) || !strings_held) { + return false; + } + + // check through all of the strings. + const void *zero_posn = packed_form.observe() + sizeof(int) * 2 + 1; + for (int i = 0; i < (int)strings_held; i++) { + // locate the zero termination if possible. + int index = int((abyte *)zero_posn - packed_form.observe()); + zero_posn = memchr(packed_form.observe() + index, '\0', + packed_form.length() - index); + // make sure we could find the zero termination. + if (!zero_posn) { + // nope, never saw a zero. good thing we checked. + return false; + } + } + + // base our expected position for the data length on the position of the + // last string we found. + int datalen_start = int((abyte *)zero_posn - packed_form.observe()) + 1; + byte_array just_len = packed_form.subarray(datalen_start, + datalen_start + sizeof(int) - 1); + if (!structures::detach(just_len, packed_length)) return false; + packed_length += datalen_start + sizeof(int); + // include the classifier length and integer package length. + return true; +} + +bool infoton::fast_unpack(byte_array &packed_form, string_array &classifier, + byte_array &info) +{ + FUNCDEF("fast_unpack"); + classifier.reset(); + info.reset(); + abyte version_checking = 0; + if (!structures::detach(packed_form, version_checking)) return false; + if (version_checking != FAST_PACK_VERSION) return false; + if (!structures::unpack_array(packed_form, classifier)) return false; + int len = 0; + if (!structures::detach(packed_form, len)) return false; + if (len > packed_form.length()) { + // not enough data. + continuable_error(static_class_name(), func, "failed to have enough data!"); + return false; + } + info = packed_form.subarray(0, len - 1); + packed_form.zap(0, len - 1); + return true; +} + +} //namespace. + diff --git a/octopi/library/octopus/infoton.h b/octopi/library/octopus/infoton.h new file mode 100644 index 00000000..094f50c9 --- /dev/null +++ b/octopi/library/octopus/infoton.h @@ -0,0 +1,156 @@ +#ifndef INFOTON_CLASS +#define INFOTON_CLASS + +/*****************************************************************************\ +* * +* Name : infoton * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +namespace octopi { + +//! An infoton is an individual request parcel with accompanying information. +/*! + This is the unit of data exchange in the octopus scheme. +*/ + +class infoton +: public virtual basis::packable, + public virtual basis::clonable, + public virtual basis::text_formable +{ +public: + infoton(const structures::string_array &classifier); + //!< creates an infoton with the "classifier". + /*!< keep in mind that although anything can be passed in here, the + consistency of one's collection of octopi depends on a regular + classification scheme. it is recommended that the "classifier" be + effectively constant. also, classifiers that begin with the octothorpe + (aka the pound sign '#') are reserved for octopus internal usage. */ + + // takes care of the most common cases of 1, 2 & 3 level classifiers. + infoton(const basis::astring &class_1); + infoton(const basis::astring &class_1, const basis::astring &class_2); + infoton(const basis::astring &class_1, const basis::astring &class_2, const basis::astring &cl_3); + + infoton(const infoton &to_copy); + //!< copies only the base class portion of the infoton. + /*!< clone() is the proper method for copying an instantiated infoton-- + this constructor only supports copying the base's information. */ + + virtual ~infoton(); + + DEFINE_CLASS_NAME("infoton"); + + infoton &operator =(const infoton &to_copy); + //!< assigns only the base class portion. + /*!< clone() is the proper method for copying an instantiated infoton. */ + + const structures::string_array &classifier() const; + //!< this array of strings is the "name" for this infoton. + /*!< the naming scheme for an infoton hierarchically and uniquely + identifies the exact type of this object. the last string (at the end() + index) is the most specific name for this object, while the preceding + names describe the object's membership in groups. the outermost group + name is at the zeroth index in the array. a classifier can have one or + more elements. */ + + void set_classifier(const structures::string_array &new_classifier); + //!< sets the infoton's classifier to the "new_classifier". + /*!< do not do this unless you know what you're doing; changing the + classifier may keep an infoton from being recognized properly. */ + + // these are also dangerous if you're not careful; they mimic the + // string constructors. + void set_classifier(const basis::astring &class_1); + void set_classifier(const basis::astring &class_1, const basis::astring &class_2); + void set_classifier(const basis::astring &class_1, const basis::astring &class_2, + const basis::astring &cl_3); + + bool check_classifier(const basis::astring &class_name, const basis::astring &caller); + //!< checks that the classifier seems valid. + /*!< the "class_name" and "caller" should be set to the location where + the check is being done. */ + + virtual void pack(basis::byte_array &packed_form) const = 0; + //!< stuffs the data in the infoton into the "packed_form". + /*!< the derived method must know how to pack this particular type + of infoton. */ + virtual bool unpack(basis::byte_array &packed_form) = 0; + //!< restores an infoton from a packed form. + /*!< the unpack() method will be utilized by tentacles that support + this type of object. */ + + virtual void text_form(basis::base_string &state_fill) const = 0; + //!< requires derived infotons to be able to show their state as a string. + + virtual clonable *clone() const = 0; + //!< must be provided to allow creation of a copy of this object. + + virtual int packed_size() const = 0; + //!< reports how large the infoton will be when packed. + /*!< must be overridden by derived classes to provide a guess at how + large the packed size of this will be. this is important to estimate + accurately. */ + + //! local version just makes text_form() more functional. + virtual basis::astring text_form() const { basis::astring fill; text_form(fill); return fill; } + + ////////////// + + // This defines the wire format for a flattened infoton. It is in essence + // a packet header format which all infotons must adhere to to ensure that + // they can be successfully unflattened when appropriate item managers are + // available. + static void fast_pack(basis::byte_array &packed_form, const infoton &to_pack); + //!< flattens an infoton "to_pack" into the byte array "packed_form". + + static bool fast_unpack(basis::byte_array &packed_form, structures::string_array &classifier, + basis::byte_array &info); + //!< undoes a previous fast_pack to restore the previous information. + /*!< extracts the data from a packed infoton in "packed_form" into the + "classifier" and "info" that are contained therein. */ + + static bool test_fast_unpack(const basis::byte_array &packed_form, + int &packed_length); + //!< checks that the "packed_form" could hold a valid packed infoton. + /*!< tests that the smallest prefix of the "packed_form" looks like an + appropriate packed classifier and packet length. the "packed_length" + is set to the length found in the packet. note that the byte array + does not need to contain the complete packed infoton yet; just the first + portion where the header info is located must be present. this method + does not disturb the data in the packed array. */ + + static int fast_pack_overhead(const structures::string_array &classifier); + //!< reports how much space is needed to pack the "classifier". + /*!< returns the overhead in bytes that will be added to an infoton's + packed size when it is packed with fast_pack(). the "classifier" is the + name of the infoton in question and must be accurate or the overhead will + not be calculated properly. */ + +private: + structures::string_array *_classifier; //!< our classifier held. +}; + +////////////// + +//! a templated method for cloning any infoton with a valid copy constructor. + +template +basis::clonable *cloner(const contents &this_obj) { return new contents(this_obj); } + +} //namespace. + +#endif + diff --git a/octopi/library/octopus/makefile b/octopi/library/octopus/makefile new file mode 100644 index 00000000..cba3f93a --- /dev/null +++ b/octopi/library/octopus/makefile @@ -0,0 +1,12 @@ +CONSOLE_MODE = true + +include cpp/variables.def + +PROJECT = octopus +TYPE = library +SOURCE = entity_data_bin.cpp entity_defs.cpp identity_infoton.cpp identity_tentacle.cpp \ + infoton.cpp octopus.cpp tentacle.cpp unhandled_request.cpp +TARGETS = octopus.lib + +include cpp/rules.def + diff --git a/octopi/library/octopus/octopus.cpp b/octopi/library/octopus/octopus.cpp new file mode 100644 index 00000000..9ee8d7eb --- /dev/null +++ b/octopi/library/octopus/octopus.cpp @@ -0,0 +1,534 @@ +/*****************************************************************************\ +* * +* Name : octopus * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "entity_data_bin.h" +#include "entity_defs.h" +#include "identity_tentacle.h" +#include "infoton.h" +#include "octopus.h" +#include "tentacle.h" +#include "unhandled_request.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace configuration; +using namespace loggers; +using namespace mathematics; +using namespace processes; +using namespace structures; +using namespace timely; + +namespace octopi { + +//#define DEBUG_OCTOPUS + // uncomment for debugging noise. +//#define DEBUG_OCTOPUS_FILTERS + // uncomment for noisy filter processing. + +#undef GRAB_LOCK +#define GRAB_LOCK \ + auto_synchronizer l(*_molock) + +// this macro returns a result and deletes the request due to a failure. it +// stores a response for the request, in case they were expecting one, since +// otherwise they will wait a long time for a response that isn't coming. if +// those responses are never picked up, they will eventually be cleaned out. +#define WHACK_RETURN(to_ret, to_whack) { \ + unhandled_request *bad_response = new unhandled_request(id, \ + request->classifier(), to_ret); \ + _responses->add_item(bad_response, id); \ + WHACK(to_whack); \ + return to_ret; \ +} + +const int MAXIMUM_TRASH_SIZE = 128 * KILOBYTE; + // this is how much we'll toss out on closing an entity. + +#undef LOG +#define LOG(t) CLASS_EMERGENCY_LOG(program_wide_logger::get(), t) + +const int OCTOPUS_CHECKING_INTERVAL = 4 * MINUTE_ms; + // the frequency in milliseconds of cleaning on the response bin. this + // doesn't need to happen very often; it only tosses data that has been + // abandoned in the response bin. + +////////////// + +class filter_list : public array +{ +public: + bool remove(tentacle *to_remove) { + for (int i = 0; i < length(); i++) { + if (get(i) == to_remove) { + zap(i, i); + return true; + } + } + return false; + } +}; + +////////////// + +class tentacle_record +{ +public: + tentacle *_limb; + bool _filter; + + tentacle_record(tentacle *limb, bool filter) + : _limb(limb), _filter(filter) {} + + ~tentacle_record() { WHACK(_limb); } +}; + +////////////// + +class modula_oblongata : public amorph +{ +public: + modula_oblongata() : amorph() {} + + int find_index(const string_array &group) { + for (int i = 0; i < elements(); i++) { + if (borrow(i)->_limb->group().prefix_compare(group)) + return i; + } + return common::NOT_FOUND; + } + + tentacle *find(const string_array &group) { + int indy = find_index(group); + if (negative(indy)) return NIL; + return borrow(indy)->_limb; + } + + bool zap(int a, int b) { + outcome ret = amorph::zap(a, b); + return ret == common::OKAY; + } + + bool zap(const string_array &group) { + int indy = find_index(group); + if (negative(indy)) return false; + amorph::zap(indy, indy); + return true; + } +}; + +////////////// + +octopus::octopus(const astring &name, int max_per_ent) +: _name(new astring(name)), + _tentacles(new modula_oblongata), + _molock(new mutex), + _responses(new entity_data_bin(max_per_ent)), + _disallow_removals(0), + _next_cleaning(new time_stamp(OCTOPUS_CHECKING_INTERVAL)), + _clean_lock(new mutex), + _filters(new filter_list), + _sequencer(new safe_roller(1, MAXINT32 / 2)), + _rando(new chaos) +{ + add_tentacle(new identity_tentacle(*this), true); + // register a way to issue identities. this is a filter. + add_tentacle(new unhandled_request_tentacle(false), false); + // provide a way to unpack the unhandled_request object. +} + +octopus::~octopus() +{ +// FUNCDEF("destructor"); + WHACK(_filters); + WHACK(_tentacles); + WHACK(_responses); + WHACK(_next_cleaning); + WHACK(_clean_lock); + WHACK(_name); + WHACK(_molock); + WHACK(_rando); + WHACK(_sequencer); +} + +void octopus::lock_tentacles() { _molock->lock(); } + +void octopus::unlock_tentacles() { _molock->unlock(); } + +entity_data_bin &octopus::responses() { return *_responses; } + +int octopus::locked_tentacle_count() { return _tentacles->elements(); } + +const astring &octopus::name() const { return *_name; } + +tentacle *octopus::locked_get_tentacle(int indy) +{ return _tentacles->borrow(indy)->_limb; } + +infoton *octopus::acquire_specific_result(const octopus_request_id &id) +{ return _responses->acquire_for_identifier(id); } + +infoton *octopus::acquire_result(const octopus_entity &requester, + octopus_request_id &id) +{ return _responses->acquire_for_entity(requester, id); } + +void octopus::unlock_tentacle(tentacle *to_unlock) +{ + to_unlock = NIL; + _molock->unlock(); +} + +void octopus::expunge(const octopus_entity &to_remove) +{ + FUNCDEF("expunge"); + { + // temporary lock so we can keep tentacles from evaporating. + GRAB_LOCK; + _disallow_removals++; + } + + // we've now ensured that no tentacles will be removed, so at most the + // list would get longer. we'll settle on its current length. + int len = _tentacles->elements(); + for (int i = 0; i < len; i++) { + tentacle_record *curr = _tentacles->borrow(i); + if (!curr || !curr->_limb) { +//complain... logic error. + continue; + } + // activate the expunge method on the current tentacle. + curr->_limb->expunge(to_remove); + } + + { + // re-enable tentacle removals. + GRAB_LOCK; + _disallow_removals--; + } + + // throw out any data that was waiting for that guy. + int items_found = 1; + infoton_list junk; + while (items_found) { + // grab a chunk of items to be trashed. + items_found = responses().acquire_for_entity(to_remove, junk, + MAXIMUM_TRASH_SIZE); + junk.reset(); +//#ifdef DEBUG_OCTOPUS + if (items_found) + LOG(a_sprintf("cleaned %d items for expunged entity ", items_found) + + to_remove.mangled_form()); +//#endif + } + +} + +outcome octopus::zap_tentacle(const string_array &tentacle_name) +{ + tentacle *found = NIL; + outcome ret = remove_tentacle(tentacle_name, found); + WHACK(found); + return ret; +} + +outcome octopus::add_tentacle(tentacle *to_add, bool filter) +{ + FUNCDEF("add_tentacle"); + if (!to_add) return tentacle::BAD_INPUT; + if (!to_add->group().length()) return tentacle::BAD_INPUT; + outcome zapped_it = zap_tentacle(to_add->group()); + if (zapped_it == tentacle::OKAY) { +//#ifdef DEBUG_OCTOPUS + LOG(astring("removed existing tentacle: ") + to_add->group().text_form()); +//#endif + } + GRAB_LOCK; + tentacle *found = _tentacles->find(to_add->group()); + // if found is non-NIL, then that would be a serious logic error since + // we just zapped it above. + if (found) return tentacle::ALREADY_EXISTS; + to_add->attach_storage(*_responses); + tentacle_record *new_record = new tentacle_record(to_add, filter); + _tentacles->append(new_record); + if (filter) *_filters += to_add; +#ifdef DEBUG_OCTOPUS + LOG(astring("added tentacle on ") + to_add->group().text_form()); +#endif + return tentacle::OKAY; +} + +outcome octopus::remove_tentacle(const string_array &group_name, + tentacle * &free_me) +{ + FUNCDEF("remove_tentacle"); + free_me = NIL; + if (!group_name.length()) return tentacle::BAD_INPUT; + while (true) { + // repeatedly grab the lock and make sure we're allowed to remove. if + // we're told we can't remove yet, then we drop the lock again and pause. + _molock->lock(); + if (!_disallow_removals) { + // we ARE allowed to remove it right now. we leave the loop in + // possession of the lock. + break; + } + if (_disallow_removals < 0) { + continuable_error(class_name(), func, "logic error in removal " + "reference counter."); + } + _molock->unlock(); + time_control::sleep_ms(0); // yield thread's execution to another thread. + } + int indy = _tentacles->find_index(group_name); + if (negative(indy)) { + // nope, no match. + _molock->unlock(); + return tentacle::NOT_FOUND; + } + // found the match. + tentacle_record *freeing = _tentacles->acquire(indy); + _tentacles->zap(indy, indy); + free_me = freeing->_limb; + _filters->remove(free_me); + _molock->unlock(); + freeing->_limb = NIL; + WHACK(freeing); + return tentacle::OKAY; +} + +outcome octopus::restore(const string_array &classifier, + byte_array &packed_form, infoton * &reformed) +{ +#ifdef DEBUG_OCTOPUS + FUNCDEF("restore"); +#endif + periodic_cleaning(); // freshen up if it's that time. + + reformed = NIL; + if (!classifier.length()) return tentacle::BAD_INPUT; + if (!packed_form.length()) return tentacle::BAD_INPUT; + if (!classifier.length()) return tentacle::BAD_INPUT; + { + // keep anyone from being removed until we're done. + GRAB_LOCK; + _disallow_removals++; + } + tentacle *found = _tentacles->find(classifier); + outcome to_return; + if (!found) { +#ifdef DEBUG_OCTOPUS + LOG(astring("tentacle not found for: ") + classifier.text_form()); +#endif + to_return = tentacle::NOT_FOUND; + } else { + to_return = found->reconstitute(classifier, packed_form, reformed); + } + // re-enable tentacle removals. + GRAB_LOCK; + _disallow_removals--; + return to_return; +} + +outcome octopus::evaluate(infoton *request, const octopus_request_id &id, + bool now) +{ + FUNCDEF("evaluate"); + periodic_cleaning(); // freshen up if it's that time. + + // check that the classifier is well formed. + if (!request->classifier().length()) { +#ifdef DEBUG_OCTOPUS + LOG("failed due to empty classifier."); +#endif + WHACK_RETURN(tentacle::BAD_INPUT, request); + } + + _molock->lock(); + + // block tentacle removals while we're working. + _disallow_removals++; + + // ensure that we pass this infoton through all the filters for vetting. + for (int i = 0; i < _filters->length(); i++) { + tentacle *current = (*_filters)[i]; +#ifdef DEBUG_OCTOPUS_FILTERS + LOG(a_sprintf("%d: checking ", i + 1) + current->group().text_form()); +#endif + + // check if the infoton is addressed specifically by this filter. + bool is_relevant = current->group().prefix_compare(request->classifier()); + +#ifdef DEBUG_OCTOPUS_FILTERS + if (is_relevant) + LOG(astring("found it to be relevant! for ") + id.text_form()) + else + LOG(astring("found it to not be relevant. for ") + id.text_form()); +#endif + + // this infoton is _for_ this filter. + _molock->unlock(); + // unlock octopus to allow others to operate. + + byte_array transformed; +//hmmm: maybe there should be a separate filter method? + outcome to_return = current->consume(*request, id, transformed); + // pass the infoton into the current filter. + + if (is_relevant) { + // the infoton was _for_ the current filter. that means that we are + // done processing it now. +#ifdef DEBUG_OCTOPUS_FILTERS + LOG(astring("filter ") + current->group().text_form() + " consumed " + "infoton from " + id.text_form() + " with result " + + tentacle::outcome_name(to_return)); +#endif + WHACK(request); + GRAB_LOCK; // short re-establishment of the lock. + _disallow_removals--; + return to_return; + } else { + // the infoton was vetted by the filter. make sure it was liked. +#ifdef DEBUG_OCTOPUS_FILTERS + LOG(astring("filter ") + current->group().text_form() + " vetted " + "infoton " + id.text_form() + " with result " + + tentacle::outcome_name(to_return)); +#endif + if (to_return == tentacle::PARTIAL) { + // if the infoton is partially complete, then we're allowed to keep + // going. this outcome means it was not prohibited. + + // make sure they didn't switch it out on us. + if (transformed.length()) { + // we need to substitute the transformed version for the original. + string_array classif; + byte_array decro; // decrypted packed infoton. + bool worked = infoton::fast_unpack(transformed, classif, decro); + if (!worked) { + LOG("failed to fast_unpack the transformed data."); + } else { + infoton *new_req = NIL; + outcome rest_ret = restore(classif, decro, new_req); + if (rest_ret == tentacle::OKAY) { + // we got a good transformed version. + WHACK(request); + request = new_req; // substitution complete. + } else { + LOG("failed to restore transformed infoton."); + } + } + } + + _molock->lock(); // get the lock again. + continue; + } else { + // this is a failure to process that object. +#ifdef DEBUG_OCTOPUS_FILTERS + LOG(astring("filter ") + current->group().text_form() + " denied " + "infoton from " + id.text_form()); +#endif + { + GRAB_LOCK; // short re-establishment of the lock. + _disallow_removals--; + } + WHACK_RETURN(to_return, request); + } + } + } + + // if we're here, then the infoton has been approved by all filters. + +#ifdef DEBUG_OCTOPUS_FILTERS + LOG(astring("all filters approved infoton: ") + id.text_form()); +#endif + + // locate the appropriate tentacle for this request. + tentacle *found = _tentacles->find(request->classifier()); + + _molock->unlock(); + // from here in, the octopus itself is not locked up. but we have sent + // the signal that no one must remove any tentacles for now. + + if (!found) { +#ifdef DEBUG_OCTOPUS + LOG(astring("tentacle not found for: ") + + request->classifier().text_form()); +#endif + GRAB_LOCK; // short re-establishment of the lock. + _disallow_removals--; + WHACK_RETURN(tentacle::NOT_FOUND, request); + } + // make sure they want background execution and that the tentacle can + // support this. + if (!now && found->backgrounding()) { + // pass responsibility over to the tentacle. + outcome to_return = found->enqueue(request, id); + GRAB_LOCK; // short re-establishment of the lock. + _disallow_removals--; + return to_return; + } else { + // call the tentacle directly. + byte_array ignored; + outcome to_return = found->consume(*request, id, ignored); + WHACK(request); + GRAB_LOCK; // short re-establishment of the lock. + _disallow_removals--; + return to_return; + } +} + +void octopus::periodic_cleaning() +{ +// FUNCDEF("periodic_cleaning"); + time_stamp next_time; + { + auto_synchronizer l(*_clean_lock); + next_time = *_next_cleaning; + } + if (next_time < time_stamp()) { + // the bin locks itself, so we don't need to grab the lock here. + _responses->clean_out_deadwood(); + auto_synchronizer l(*_clean_lock); + // lock before modifying the time stamp; only one writer. + _next_cleaning->reset(OCTOPUS_CHECKING_INTERVAL); + } +} + +tentacle *octopus::lock_tentacle(const string_array &tentacle_name) +{ + if (!tentacle_name.length()) return NIL; + _molock->lock(); + tentacle *found = _tentacles->find(tentacle_name); + if (!found) { + _molock->unlock(); + return NIL; + } + return found; +} + +octopus_entity octopus::issue_identity() +{ + return octopus_entity(*_name, application_configuration::process_id(), _sequencer->next_id(), + _rando->inclusive(0, MAXINT32 / 4)); +} + +} //namespace. + diff --git a/octopi/library/octopus/octopus.h b/octopi/library/octopus/octopus.h new file mode 100644 index 00000000..ad60e4f8 --- /dev/null +++ b/octopi/library/octopus/octopus.h @@ -0,0 +1,199 @@ +#ifndef OCTOPUS_CLASS +#define OCTOPUS_CLASS + +/*****************************************************************************\ +* * +* Name : octopus * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include + +namespace octopi { + +// forward. +class entity_data_bin; +class filter_list; +class infoton; +class modula_oblongata; +class octopus_entity; +class octopus_request_id; +class tentacle; + +//! Octopus is a design pattern for generalized request processing systems. +/*! + Octopus is a plug-in module manager that can serve as the base for highly + extensible program architectures. Each module is called a tentacle, + following the conceit of the naming conventions, and it services one type + of request. The requests come in the form of infoton objects, where the + tentacle that handles each class of infotons is uniquely identifiable. + Note that the outcomes returned here are from the tentacle's set of + outcomes. +*/ + +class octopus : public virtual basis::root_object +{ +public: + octopus(const basis::astring &name, int max_size_per_entity); + //!< constructs an octopus named "name". + /*!< the "name" string identifies the arena where this octopus is running. + this could be a network host or other identification string. the + "max_size_per_entity" is the largest that we allow one entity's bin of + pending data to be. this should be quite large if massive transactions + need to be processed. */ + + virtual ~octopus(); + + DEFINE_CLASS_NAME("octopus"); + + const basis::astring &name() const; + //!< returns the name that the octopus was constructed with. + + // tentacle management functions... + + basis::outcome add_tentacle(tentacle *to_add, bool filter = false); + //!< hooks a tentacle in to provide processing of one type of infoton. + /*!< lists the tentacle "to_add" as being responsible for handling requests + for the tentacle's group(). note that the octopus takes over control of + the "to_add" pointer; do not destroy that pointer independently, and do + not use the tentacle after adding it to the octopus unless the tentacle is + thread-safe. the add will replace an existing tentacle if it was already + registered under the same group name. if the "filter" flag is enabled, + then this tentacle is considered a filter; it will be consulted on all + infotons before they are processed. filters will be invoked in the order + that they are added. */ + + basis::outcome remove_tentacle(const structures::string_array &group_name, tentacle * &free_me); + //!< removes the tentacle listed for the "group_name", if any. + /*!< "free_me" provides the means for getting back what was originally + registered. NOTE: remember to destroy "free_me" if that's appropriate + (i.e. it was dynamically allocated, has no other users and no other entity + has responsibility for it). */ + + basis::outcome zap_tentacle(const structures::string_array &group_name); + //!< similar to remove_tentacle(), but destroys the tentacle. + + // entity management methods... + + octopus_entity issue_identity(); + //!< creates an entity identifier that is unique for this octopus. + /*!< this provides a unique identity using the "name" specified in the + constructor and the current process id. the sequence number and random + add_in are generated by this class also. */ + + void expunge(const octopus_entity &to_remove); + //!< invokes every tentacle's expunge() method on the id "to_remove". + /*!< this indicates that the entity is no longer extant and resources + held for it can be destroyed. */ + + entity_data_bin &responses(); + //!< allows external access to our set of results. + /*!< this should not be used unless you know what you're doing. */ + + // main functionality: restoring infotons, evaluating requests and + // locating responses. + + basis::outcome restore(const structures::string_array &classifier, basis::byte_array &packed_form, + infoton * &reformed); + //!< regenerates a packed infoton given its classifier. + /*!< locates the appropriate type of infoton with the "classifier" and + consumes the bytes in "packed_form" to return a new version of the original + data in "reformed", if possible. */ + + basis::outcome evaluate(infoton *request, const octopus_request_id &item_id, + bool now = false); + //!< tries to process the "request" using the current set of tentacles. + /*!< if there is no group handler for the "request", then NO_HANDLER will + be returned. the classifier for "request" specifies the group name for + processing. NOTE: the octopus assumes all responsibility for the + "request", even if the outcome denotes failure; do not touch the "request" + after this call. if the "request" will be processed further by lower + level octopi, then the classifier can be patched as appropriate. the + "item_id" must be maintained with the request in order to identify the + entity making the request and the sequence number of this particular + request. the "now" parameter specifies whether the processing must occur + immediately; if false, the processing will occur later when the tentacle + get around to it. if "now" is true, there is more load on the octopus + itself. note that "now" is ignored if the tentacle does not support + backgrounding; true is always assumed for that case. */ + + infoton *acquire_result(const octopus_entity &requester, + octopus_request_id &original_id); + //!< acquires responses to previous requests if there are any waiting. + /*!< it returns an infoton for the "requester", if any are available. + call this function repeatedly to ensure that all responses have been + provided. the "original_id" is a copy of the "item_id" that was + originally passed to evaluate_request(). the returned object must + eventually be destroyed if non-NIL. */ + + infoton *acquire_specific_result(const octopus_request_id &original_id); + //!< supports seeking the result for a specific request. + /*!< either the infoton that is a response to "original_id" will be + returned or NIL. */ + + ////////////// + + // tentacle accessors: careful with these--you must always unlock after + // you have locked. + + tentacle *lock_tentacle(const structures::string_array &tentacle_name); + //!< locates the tentacle with the "tentacle_name" and returns it. + /*!< the octopus will stay locked until the unlock_tentacle() method is + invoked. */ + + void unlock_tentacle(tentacle *to_unlock); + //!< unlocks the octopus when given a previously locked tentacle. + /*!< this must be the same object that was obtained via the + lock_tentacle() method. */ + + void lock_tentacles(); + //!< locks the tentacle list for use with locked_get_tentacle. + + // the following are only valid if the tentacle list is locked. + int locked_tentacle_count(); //!< number of tentacles. + tentacle *locked_get_tentacle(int indy); //!< access indy'th tentacle. + void unlock_tentacles(); //!< unlocks the list. + + ////////////// + + // used internally; don't mess with externally. + + void periodic_cleaning(); + //!< flushes any abandoned data from the response bin. + +private: + basis::astring *_name; //!< our name as passed to the constructor. + modula_oblongata *_tentacles; //!< the list of tentacles. + basis::mutex *_molock; //!< the synchronizer for our tentacle list. + entity_data_bin *_responses; //!< data awaiting pickup by requester. + int _disallow_removals; + //!< simplifies locking behavior for immediate requests. + /*!< we set this flag and don't need to lock the whole octopus. if it's + non-zero, then no tentacles can be removed yet. */ + timely::time_stamp *_next_cleaning; //!< when we'll next flush old items. + basis::mutex *_clean_lock; //!< used only to protect the time stamp above. + filter_list *_filters; //!< the filters that must vet infotons. + processes::safe_roller *_sequencer; //!< identity issue; this is the next entity id. + mathematics::chaos *_rando; //!< randomizer for providing extra uniquification. + + // not accessible. + octopus(const octopus &); + octopus &operator =(const octopus &); +}; + +} //namespace. + +#endif + diff --git a/octopi/library/octopus/tentacle.cpp b/octopi/library/octopus/tentacle.cpp new file mode 100644 index 00000000..7c618033 --- /dev/null +++ b/octopi/library/octopus/tentacle.cpp @@ -0,0 +1,190 @@ +/*****************************************************************************\ +* * +* Name : tentacle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "entity_data_bin.h" +#include "entity_defs.h" +#include "infoton.h" +#include "tentacle.h" + +#include +#include +#include +#include + +using namespace basis; +using namespace processes; +using namespace structures; + +namespace octopi { + +//#define DEBUG_TENTACLE + // uncomment for noisier version. + +#undef GRAB_CONSUMER_LOCK +#define GRAB_CONSUMER_LOCK auto_synchronizer l(*_input_guard) + +#undef LOG +#define LOG(t) CLASS_EMERGENCY_LOG(program_wide_logger::get(), t) + +////////////// + +struct infoton_record { + infoton *_product; + octopus_request_id _id; + + infoton_record(infoton *product, octopus_request_id id) + : _product(product), _id(id) {} + + ~infoton_record() { WHACK(_product); } +}; + +class queueton : public amorph {}; + +////////////// + +class pod_motivator : public ethread +{ +public: + pod_motivator(tentacle &parent, int motivational_rate) + : ethread(motivational_rate, ethread::SLACK_INTERVAL), + _parent(parent) {} + + void perform_activity(void *formal(ptr)) { _parent.propel_arm(); } + +private: + tentacle &_parent; +}; + +////////////// + +tentacle::tentacle(const string_array &group_name, bool backgrounded, + int motivational_rate) +: _group(new string_array(group_name)), + _pending(new queueton), + _input_guard(new mutex), + _action(NIL), + _products(NIL), + _backgrounded(backgrounded) +{ + // we only start the thread if they've said they'll support backgrounding. + if (backgrounded) + _action = new pod_motivator(*this, motivational_rate); +} + +tentacle::~tentacle() +{ + if (_action) _action->stop(); + WHACK(_action); + WHACK(_group); + WHACK(_pending); + WHACK(_input_guard); +} + +const string_array &tentacle::group() const { return *_group; } + +const char *tentacle::outcome_name(const outcome &to_name) +{ return common::outcome_name(to_name); } + +int tentacle::motivational_rate() const +{ if (_action) return _action->sleep_time(); else return 0; } + +void tentacle::attach_storage(entity_data_bin &storage) +{ + _products = &storage; + if (_action) _action->start(NIL); +} + +void tentacle::detach_storage() +{ + if (_action) _action->stop(); + _products = NIL; +} + +bool tentacle::store_product(infoton *product, + const octopus_request_id &original_id) +{ +#ifdef DEBUG_TENTACLE + FUNCDEF("store_product"); +#endif + if (!_products) { +#ifdef DEBUG_TENTACLE + LOG("storage bunker has not been established!"); +#endif + return false; + } + return _products->add_item(product, original_id); +} + +outcome tentacle::enqueue(infoton *to_chow, const octopus_request_id &item_id) +{ + GRAB_CONSUMER_LOCK; + int max_size = 0; + // this may be a bad assumption, but here goes: we assume that the limit + // on per entity storage in the bin is pretty much the same as a reasonable + // limit here on the queue of pending items. we need to limit it and would + // rather not add another numerical parameter to the constructor. + if (_products) + max_size = _products->max_bytes_per_entity(); + int curr_size = 0; + if (max_size) { + // check that the pending queue is also constrained. + for (int i = 0; i < _pending->elements(); i++) { + curr_size += _pending->borrow(i)->_product->packed_size(); + } + if (curr_size + to_chow->packed_size() > max_size) { + WHACK(to_chow); + return NO_SPACE; + } + } + *_pending += new infoton_record(to_chow, item_id); +//is there ever a failure outcome? +//yes, when space is tight! + return OKAY; +} + +infoton *tentacle::next_request(octopus_request_id &item_id) +{ + GRAB_CONSUMER_LOCK; + if (!_pending->elements()) return NIL; // nothing to return. + infoton *to_return = (*_pending)[0]->_product; + (*_pending)[0]->_product = NIL; + // clean out so destructor doesn't delete the object. + item_id = (*_pending)[0]->_id; + _pending->zap(0, 0); + return to_return; +} + +void tentacle::propel_arm() +{ +#ifdef DEBUG_TENTACLE + FUNCDEF("propel_arm"); +#endif + infoton *next_item = NIL; + do { + octopus_request_id id; + next_item = next_request(id); + if (!next_item) break; + byte_array ignored; + outcome ret = consume(*next_item, id, ignored); + if (ret != OKAY) { +#ifdef DEBUG_TENTACLE + LOG(astring("failed to act on ") + next_item->classifier().text_form()); +#endif + } + WHACK(next_item); // fulfill responsibility for cleanup. + } while (next_item); +} + +} //namespace. + diff --git a/octopi/library/octopus/tentacle.h b/octopi/library/octopus/tentacle.h new file mode 100644 index 00000000..abbc759a --- /dev/null +++ b/octopi/library/octopus/tentacle.h @@ -0,0 +1,187 @@ +#ifndef TENTACLE_CLASS +#define TENTACLE_CLASS + +/*****************************************************************************\ +* * +* Name : tentacle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace octopi { + +// forward. +class entity_data_bin; +class infoton; +class octopus; +class octopus_entity; +class octopus_request_id; +class pod_motivator; +class queueton; + +//! Manages a service within an octopus by processing certain infotons. + +class tentacle : public virtual basis::root_object +{ +public: + tentacle(const structures::string_array &group_name, bool backgrounded, + int motivational_rate = tentacle::DEFAULT_RATE); + //!< constructs a tentacle that handles infotons with the "group_name". + /*!< if "backgrounded" is true, then the tentacle will periodically look + for queued requests at the specified "motivational_rate". if + "backgrounded" is false, then the tentacle will not perform any background + processing, meaning that it can only provide immediate evaluation for an + octopus. */ + + virtual ~tentacle(); + + enum constants { DEFAULT_RATE = 40 }; + + DEFINE_CLASS_NAME("tentacle"); + + const structures::string_array &group() const; + //!< returns the name of the group that this tentacle services. + /*!< this can be a single string or it can be a list of names. a tentacle + can only service one name group currently. */ + + bool backgrounding() const { return _backgrounded; } + //!< reports on whether this tentacle supports background operation or not. + + int motivational_rate() const; + //!< returns the background processing rate this was constructed with. + + enum outcomes { + OKAY = basis::common::OKAY, + NOT_FOUND = basis::common::NOT_FOUND, + ALREADY_EXISTS = basis::common::EXISTING, + BAD_INPUT = basis::common::BAD_INPUT, + NO_SPACE = basis::common::NO_SPACE, + GARBAGE = basis::common::GARBAGE, + DISALLOWED = basis::common::DISALLOWED, + + NO_HANDLER = basis::common::NO_HANDLER, //!< no handler for that type of infoton. + PARTIAL = basis::common::PARTIAL, //!< processing of request is partially done. + ENCRYPTION_MISMATCH = basis::common::ENCRYPTION_MISMATCH + //!< there is a disconnect regarding encryption. + }; + + static const char *outcome_name(const basis::outcome &to_name); + //!< returns the textual form of the outcome "to_name". + + ////////////// + + // functions that must be provided by derived tentacles. + + virtual basis::outcome reconstitute(const structures::string_array &classifier, + basis::byte_array &packed_form, infoton * &reformed) = 0; + //!< regenerates an infoton from its packed form. + /*!< given the "classifier" under which the object is categorized, this + reconstructs a "reformed" infoton equivalent to the flattened infoton + in "packed_form". the "packed_form" is destructively consumed. + NOTE: it is crucial that the derived method calls set_classifier() on + the "reformed" infoton from the passed "classifier". */ + + virtual basis::outcome consume(infoton &to_chow, const octopus_request_id &item_id, + basis::byte_array &transformed) = 0; + //!< this is the main function that processes infotons for this tentacle. + /*!< the octopus will feed this function with appropriate data "to_chow" + for infotons that are to be processed by this tentacle's group(). the + "item_id" provides for the requesting entity an origination marker that + can be used in produce() below. the outcome indicates whether the + processing was successful. processing could fail due to a missing + handler for the item, due to erronous data in the infoton, because of + resource limits placed on the tentacle, from explicit rejection by the + tentacle, or due to other causes. the "transformed" is a packed infoton + that may be generated during the consumption of "to_chow" (it must + actually be a packed classifier and then the packed infoton). if it can + be unpacked successfully, then it will be treated as the actual infoton + that was to be consumed. this is only expected from a filter. + note: the infoton "to_chow" can be destructively manipulated by the + tentacle, including patching the classifier for internal octopi or + rearranging any data contained in "to_chow". none of these changes + will be seen by the entity that requested processing. + regarding filters: if this tentacle is serving as a filter, then it + may be presented with infotons that are not covered by its group. + given such an infoton, the tentacle should perform whatever filtering + is to be done, including modifying the infoton appropriately, and + return PARTIAL when it liked the infoton or DISALLOWED when it + rejects the infoton. it is understood that the infoton will be + passed along to the rest of the tentacles when the successful + result of PARTIAL is returned. */ + + virtual void expunge(const octopus_entity &to_remove) = 0; + //!< called to remove traces of the entity "to_remove". + /*!< this is an order from the octopus that all traces of the entity + "to_remove" should now be cleaned out. that entity has been utterly + destroyed and any data structures held for it should be thrown out + also. the required actions are specific to the tentacle's design. */ + + ////////////// + + basis::outcome enqueue(infoton *to_chow, const octopus_request_id &item_id); + //!< holds onto infotons coming from the octopus for backgrounding. + /*!< this will add an infoton "to_chow" into the list of objects to be + consumed. at some point after a successful outcome from this, the + tentacle will be handed the infoton for processing. NOTE: all + responsibility for the infoton "to_chow" is passed to this method; the + infoton should not be touched in any way after invocation. */ + + infoton *next_request(octopus_request_id &item_id); + //!< pops out the next queued request for processing. + /*!< this function locates the next request for the tentacle when it is + in its consume() method. the returned infoton was previously passed + to the enqueue() method and needs to be processed. if there are no + requests ready, NIL is returned. */ + + bool store_product(infoton *product, const octopus_request_id &original_id); + //!< used by tentacles to store the objects they produce from infotons. + /*!< this will cache the "product" object and its "original_id" for later + retrieval from the entity_data_bin we were given when hooked to an octopus. + note that the "product" must be allocated dynamically and that it becomes + owned by the response bin after this call (do not delete the original + pointer). if the item would not fit in the entity's bin, then false is + returned and the "product" is deleted. */ + + ////////////// + + // support that is for internal use only. + + void attach_storage(entity_data_bin &storage); + //!< used when a tentacle is being integrated with an octopus. + /*!< not for casual external users. note that the tentacle's background + processing will not be started until attach is called and that it stops + when detach is called. */ + void detach_storage(); + //!< unhooks the storage bin from this tentacle. + + void propel_arm(); + //!< invoked by our thread to cause requests to be processed. + +private: + structures::string_array *_group; //!< the group name that this tentacle handles. + queueton *_pending; //!< the requests that are waiting fulfillment. + basis::mutex *_input_guard; //!< protects the incoming requests. + pod_motivator *_action; //!< the thread that keeps things moving along. + entity_data_bin *_products; //!< if non-nil, where we store responses. + bool _backgrounded; //!< records whether we're threading or not. + + // not permitted. + tentacle(const tentacle &); + tentacle &operator =(const tentacle &); +}; + +} //namespace. + +#endif + diff --git a/octopi/library/octopus/tentacle_helper.h b/octopi/library/octopus/tentacle_helper.h new file mode 100644 index 00000000..0291f18b --- /dev/null +++ b/octopi/library/octopus/tentacle_helper.h @@ -0,0 +1,101 @@ +#ifndef TENTACLE_HELPER_CLASS +#define TENTACLE_HELPER_CLASS + +/*****************************************************************************\ +* * +* Name : tentacle_helper * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "infoton.h" +#include "tentacle.h" + +#include +#include +#include + +/*! @file tentacle_helper.h + @brief Automates some common tasks for tentacle implementations. + This template provides some default implementations for the methods that + derived tentacles must implement. This works best when the infotons being + exchanged are derived from a common base; the base class would be used as + the instantiation type here. For tentacles used in network communication, + the client side could use tentacle_helper without adding any + functionality. The server side must override the consume() and expunge() + methods in order to implement processing for the infotons sent by + its clients. +*/ + +namespace octopi { + +//!< reconstituter should work for most infotons to restore flattened infotons. +/*!< the infotons that can be used here just need valid default constructor +and unpack methods. the "junk" parameter is needed to allow the template to +be disambiguated on some compilers--it is unused and should just be NIL. */ +template +basis::outcome reconstituter(const structures::string_array &classifier, + basis::byte_array &packed_form, + infoton * &reformed, contents *formal(junk)) +{ + contents *inf = new contents; + if (!inf->unpack(packed_form)) { + WHACK(inf); + return tentacle::GARBAGE; + } + reformed = inf; + reformed->set_classifier(classifier); + return tentacle::OKAY; +} + +////////////// + +//! provides prefab implementations for parts of the tentacle object. +/*! tentacle_helper provides the base functionality of reconstitution that +should support most of the simple needs of a user of an octopus. if the +octopus will actually implement the request processing (instead of just +unpacking returned responses), the consume method needs to be filled in +appropriately so that it implements the derived tentacle's purpose. */ +template +class tentacle_helper : public tentacle +{ +public: + tentacle_helper(const structures::string_array &classifier, bool backgrounded, + int motivational_rate = tentacle::DEFAULT_RATE) + : tentacle(classifier, backgrounded, motivational_rate) {} + + virtual ~tentacle_helper() {} + //!< force a virtual destructor. + + //! this is a simple enough action that it is totally automated. + virtual basis::outcome reconstitute(const structures::string_array &classifier, + basis::byte_array &packed_form, infoton * &reformed) { + return reconstituter(classifier, packed_form, reformed, + (contents *)NIL); + } + + //! consume is not really provided here. remember to implement for servers! + /*! consume will generally need to be implemented by a "real" tentacle that + is based on the tentacle_helper. in the context of network communications, + the server side will generally have a real tentacle that implements consume() + while the client side will just have the tentacle_helper version of one, + which does nothing. */ + virtual basis::outcome consume(infoton &formal(to_chow), + const octopus_request_id &formal(item_id), basis::byte_array &transformed) + { transformed.reset(); return NO_HANDLER; } + + virtual void expunge(const octopus_entity &formal(to_remove)) {} + //!< no general actions for expunge; they are all class-specific. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/octopus/unhandled_request.cpp b/octopi/library/octopus/unhandled_request.cpp new file mode 100644 index 00000000..2c042069 --- /dev/null +++ b/octopi/library/octopus/unhandled_request.cpp @@ -0,0 +1,69 @@ +/*****************************************************************************\ +* * +* Name : unhandled_request * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "unhandled_request.h" + +using namespace basis; +using namespace structures; + +namespace octopi { + +unhandled_request::unhandled_request(const octopus_request_id &original_id, + const string_array &original_classifier, const outcome &reason) +: infoton(the_classifier()), + _original_id(original_id), + _original_classifier(original_classifier), + _reason(reason) +{} + +clonable *unhandled_request::clone() const +{ return new unhandled_request(_original_id, _original_classifier, _reason); } + +int unhandled_request::packed_size() const +{ + return _original_id.packed_size() + _original_classifier.packed_size() + + _reason.packed_size(); +} + +void unhandled_request::text_form(basis::base_string &fill) const +{ + fill.assign(astring("classifier=") + the_classifier().text_form() + + " original_id=" + _original_id.text_form() + + a_sprintf(" reason=%d", _reason.value())); +} + +const char *initter[] = { "__Unhandled__", NIL }; + +string_array unhandled_request::the_classifier() +{ return string_array(1, initter); } + +void unhandled_request::pack(byte_array &packed_form) const +{ + _original_id.pack(packed_form); + _original_classifier.pack(packed_form); + attach(packed_form, _reason.value()); +} + +bool unhandled_request::unpack(byte_array &packed_form) +{ + if (!_original_id.unpack(packed_form)) return false; + if (!_original_classifier.unpack(packed_form)) return false; + int val; + if (!detach(packed_form, val)) return false; + _reason = outcome(val); + return true; +} + +} //namespace. + diff --git a/octopi/library/octopus/unhandled_request.h b/octopi/library/octopus/unhandled_request.h new file mode 100644 index 00000000..a1fa3a1e --- /dev/null +++ b/octopi/library/octopus/unhandled_request.h @@ -0,0 +1,80 @@ +#ifndef UNHANDLED_REQUEST_CLASS +#define UNHANDLED_REQUEST_CLASS + +/*****************************************************************************\ +* * +* Name : unhandled_request * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "entity_defs.h" +#include "infoton.h" +#include "tentacle_helper.h" + +#include + +namespace octopi { + +// forward. +class octopus_request_id; + +//! Informs the caller that a request type was unknown to the server octopus. +/*! + The accompanying octopus_request_id specifies the particular request that + failed. The classifier of the original request is also included for + reference. This allows the client to get an immediate response from the + server when we have no idea what they are asking for, rather than the client + needing to timeout on the failed request. Note: this is a heavy-weight + header that should not be included in other headers. +*/ + +class unhandled_request : public infoton +{ +public: + // these members are informational so they're exposed out in public. + octopus_request_id _original_id; //!< the failed request's identifier. + structures::string_array _original_classifier; //!< the original name of the request. + basis::outcome _reason; //!< the reason why this request was provided. + + unhandled_request(const octopus_request_id &id = octopus_request_id(), + const structures::string_array &original_classifier = structures::string_array(), + const basis::outcome &reason = basis::common::NO_HANDLER); + + DEFINE_CLASS_NAME("unhandled_request"); + + static structures::string_array the_classifier(); + //!< the classifier for unknown infotons makes unhandled requests unique. + /*!< __Unhandled__ is now a reserved word for classifiers. That's the + classifier for all unhandled_request objects as returned by + the_classifier(). */ + + virtual void text_form(basis::base_string &fill) const; + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); + virtual clonable *clone() const; + virtual int packed_size() const; +}; + +////////////// + +class unhandled_request_tentacle +: public tentacle_helper +{ +public: + unhandled_request_tentacle(bool backgrounded = false) + : tentacle_helper(unhandled_request::the_classifier(), + backgrounded) {} +}; + +} //namespace. + +#endif + diff --git a/octopi/library/sockets/base_address.h b/octopi/library/sockets/base_address.h new file mode 100644 index 00000000..e5f82e86 --- /dev/null +++ b/octopi/library/sockets/base_address.h @@ -0,0 +1,104 @@ +#ifndef BASE_ADDRESS_CLASS +#define BASE_ADDRESS_CLASS + +/*****************************************************************************\ +* * +* Name : base_address * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Provides a way to describe an endpoint for communication. * +* * +******************************************************************************* +* Copyright (c) 1995-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace sockets { + +// forward. +class machine_uid; + +class base_address +: public virtual basis::packable, + public virtual basis::nameable +{ +public: + // the packable and nameable responsibilities are forwarded to the derived classes. + + virtual bool same_host(const base_address &to_compare) const = 0; + virtual bool same_port(const base_address &to_compare) const = 0; + // returns true if the host or port are identical. + + virtual bool shareable(const base_address &to_compare) const = 0; + // returns true if the two transports can be shared. + + virtual basis::astring text_form() const = 0; + // returns a readable string representing the address. + + // these flip the address into a string and back. this is different from + // text_form() because the reversal must be possible. + virtual basis::astring tokenize() const = 0; + virtual bool detokenize(const basis::astring &info) = 0; + + virtual machine_uid convert() const = 0; + // returns the address in the uniquifying format. + + virtual base_address *create_copy() const = 0; + // creates a new address that's a copy of this one. note that this + // allocates memory that must be deleted by the caller. +}; + +////////////// + +// these macros assist in tokenizing and detokenizing addresses. + +//const char *TOKEN_SEPARATOR(); +//const char *TOKEN_ASSIGN(); +//const char *TOKEN_SEPARATOR() { return ","; } +//const char *TOKEN_ASSIGN() { return "="; } + +// begins operation of a variable_tokenizer for loading. LOADER_EXIT must be called +// after finishing with the variable_tokenizer. +#define LOADER_ENTRY \ + variable_tokenizer addr_parser; \ + addr_parser.parse(info) + +#define LOADER_EXIT + // currently no implementation. + +// locates a variable in the variable_tokenizer. +#define FIND(name, value) astring value = addr_parser.find(name) + +// locates a variable like FIND, but returns if it couldn't find it. +#define GRAB(name, value) FIND(name, value); if (!value) return false + +// begins operation of a variable_tokenizer for storing. remember to call STORER_EXIT +// when finished. +#define STORER_ENTRY \ + variable_tokenizer addr_parser + +#define STORER_EXIT + // currently no implementation. + +// adds a new entry into the variable_tokenizer. +#define ADD(name, value) addr_parser.table().add(name, value) + +// returns the accumulated tokens in the storing variable_tokenizer. +#define DUMP_EXIT astring to_return = addr_parser.text_form(); \ + STORER_EXIT; \ + return to_return + +} //namespace. + +#endif + diff --git a/octopi/library/sockets/internet_address.cpp b/octopi/library/sockets/internet_address.cpp new file mode 100644 index 00000000..73713402 --- /dev/null +++ b/octopi/library/sockets/internet_address.cpp @@ -0,0 +1,470 @@ +////////////// +// Name : internet_address +// Author : Chris Koeritz +////////////// +// Copyright (c) 1995-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +#include "internet_address.h" +#include "machine_uid.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace basis; +using namespace configuration; +using namespace loggers; +using namespace structures; +using namespace textual; + +namespace sockets { + +//#define DEBUG_ADDRESS + // uncomment if you want a debugging version of address. + +////////////// + +#undef LOG +#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print) + +////////////// + +/* +//hmmm: consider moving the functions for storage out to a helper file. + +internet_address internet_address::load(configurator &config, + const astring §ion, const astring &name, const internet_address &def) +{ + astring token_list; + if (!config.get(section, name, token_list)) { + // no entry stored means no address. set the address to the default. + config.put(section, name, def.tokenize()); + return def; + } + internet_address to_return; + // get the rest of the work done by detokenize. + if (to_return.detokenize(token_list)) return to_return; + // didn't work, dang it. Note that we don't reject it here and store the + // default. that's because this indicates an error in formatting of the + // address, and we want the user to have a chance to correct that and try + // again. + return internet_address(); +} + +bool internet_address::store(configurator &config, const astring §ion, + const astring &name, const internet_address &to_store) +{ + astring text = to_store.tokenize(); + return config.put(section, name, text); +} +*/ + +////////////// + +// provides an easy way to cast to the proper type and provide a non-pointer +// to work with. +#define CAST_UP(type) \ + const type *temp = dynamic_cast(&compare_in); \ + if (!temp) return false; /* shouldn't happen but it means bad things. */ \ + const type &to_compare = *temp; + +////////////// + +internet_address::internet_address() { fill(byte_array(), "", 0); } + +internet_address::internet_address(const byte_array &ip, + const astring &host, int port_in) +{ fill(ip, host, port_in); } + +//hmmm: for ipv6, we will need a new object, called ipv6_address perhaps. +// it will copy everything here but will have a longer address array. +// we will need a new object for ipv6_machine_uid also. + +machine_uid internet_address::convert() const +{ return internet_machine_uid(hostname, byte_array(ADDRESS_SIZE, ip_address)); } + +bool internet_address::ip_appropriate_number(const astring &to_check, int indy, + astring &accum) +{ +// FUNCDEF("ip_appropriate_number"); + accum.reset(); + for (int i = indy; (i < indy + 3) && (i < to_check.length()); i++) { + // make sure it looks like a good number. + if (!parser_bits::is_numeric(to_check[i]) || (to_check[i] == '-') ) { + // this one doesn't look good right here, but if we already got a digit, + // we're okay. + if (i == indy) return false; // hadn't gotten any digits at all yet. + else break; // got one, so let's check our progress below. + } + accum += to_check[i]; // snag the current digit. + } + if (!accum.length()) return false; // how would that happen? + int convert = accum.convert(-1); + return (convert >= 0) && (convert <= 255); +} + +// needs to see 1-3 numbers, period, 1-3 numbers, period etc... +// for a total of three periods and 4 number sets. +bool internet_address::has_ip_address(const astring &to_check, + astring &ip_found) +{ +// FUNCDEF("has_ip_address"); + int nums_seen = 0; + ip_found.reset(); + for (int i = 0; i < to_check.length(); i++) { + bool hosed = false; + astring num_found; +// if (!!ip_found) LOG(astring("current ip found=") + ip_found); + if (!ip_appropriate_number(to_check, i, num_found)) { +// LOG(a_sprintf("no ip approp %d", i)); + hosed = true; + } else { + // we're seeing the number we want here. +// LOG(astring("num found = ") + num_found); + nums_seen++; + if (nums_seen >= 4) { + ip_found += num_found; // get our last part. + return true; // hey, that's a match. + } + // look for a period now. + int period_indy = to_check.find('.', i); + if (negative(period_indy) || (period_indy > i + 3) ) hosed = true; + else { + for (int x = i; x < period_indy; x++) { + if (!parser_bits::is_numeric(to_check[x]) || (to_check[x] == '-')) { +// LOG(a_sprintf("hosed by bad char at %d -> %c", x, to_check[x])); + hosed = true; // wrong character seen in between. + } + } + if (!hosed) { + ip_found += to_check.substring(i, period_indy); + i = period_indy; // skip to where our next number should be. + } + } + } + if (hosed) { + nums_seen = 0; + ip_found.reset(); + } + } + return false; +} + +const abyte localhosts_bytes[] = { 127, 0, 0, 1 }; +SAFE_STATIC_CONST(byte_array, internet_address::localhost, + (ADDRESS_SIZE, localhosts_bytes)) + +bool internet_address::is_localhost() const +{ + // check whether the host is "local" or "localhost". + astring host = hostname; + host.to_lower(); + if ( (host.equal_to("local")) || (host.equal_to("localhost")) ) + return true; + + // check whether the address is local even if the hostname wasn't. + for (int i = 0; i < ADDRESS_SIZE; i++) { + if (ip_address[i] != localhost().get(i)) + return false; + } + + return true; // the address matched. +} + +astring internet_address::normalize_host() const +{ + // try the hostname first. + astring remote = hostname; + if (remote.t()) return remote; + // there was no host we can use; try the IP address instead. + byte_array ip_form(ADDRESS_SIZE, ip_address); + remote = ip_address_text_form(ip_form); + return remote; // they get whatever we had as the address. +} + +int internet_address::packed_size() const +{ + return sizeof(port) + + + sizeof(int) + ADDRESS_SIZE + + sizeof(int) + MAXIMUM_HOSTNAME_LENGTH; +} + +void internet_address::pack(byte_array &packed_form) const +{ + attach(packed_form, port); + packed_form += byte_array(ADDRESS_SIZE, ip_address); + packed_form += byte_array(MAXIMUM_HOSTNAME_LENGTH, (abyte *)hostname); +} + +bool internet_address::unpack(byte_array &packed_form) +{ + // check for minimum expected length. + if (packed_form.length() < int(sizeof(port)) + ADDRESS_SIZE + + MAXIMUM_HOSTNAME_LENGTH) + return false; + if (!detach(packed_form, port)) return false; + packed_form.stuff(ADDRESS_SIZE, ip_address); + packed_form.zap(0, ADDRESS_SIZE - 1); + packed_form.stuff(MAXIMUM_HOSTNAME_LENGTH, (abyte *)hostname); + packed_form.zap(0, MAXIMUM_HOSTNAME_LENGTH - 1); + return true; +} + +void internet_address::fill(const byte_array &ip, const astring &host, + int port_in) +{ + port = port_in; + int mini = minimum(int(ADDRESS_SIZE), ip.length()); + for (int i = 0; i < mini; i++) ip_address[i] = ip[i]; + for (int j = mini; j < ADDRESS_SIZE; j++) ip_address[j] = 0; + hostname[0] = '\0'; + host.stuff(hostname, MAXIMUM_HOSTNAME_LENGTH - 1); +} + +base_address *internet_address::create_copy() const +{ + return new internet_address(byte_array(ADDRESS_SIZE, ip_address), + hostname, port); +} + +astring internet_address::text_form() const +{ + astring to_print("["); + if (astring(hostname).t()) { + to_print += "host="; + to_print += hostname; + to_print += ", "; + } +///// bool print_ip = false; +///// for (int i = 0; i < ADDRESS_SIZE; i++) if (ip_address[i]) print_ip = true; +///// if (print_ip) { + to_print += "ip_addr="; + for (int i = 0; i < ADDRESS_SIZE; i++) { + to_print += a_sprintf("%d", int(ip_address[i])); + if (i != ADDRESS_SIZE - 1) to_print += "."; + } + to_print += ", "; +///// } + to_print += a_sprintf("port=%u]", port); + return to_print; +} + +bool internet_address::is_nil_address(const address_array &ip_address) +{ + for (int i = 0; i < ADDRESS_SIZE; i++) if (ip_address[i]) return false; + return true; +} + +const abyte nil_address_bytes[] = { 0, 0, 0, 0 }; +SAFE_STATIC_CONST(byte_array, internet_address::nil_address, + (ADDRESS_SIZE, nil_address_bytes)) + +bool internet_address::is_nil_address() const +{ return is_nil_address(ip_address); } + +bool internet_address::same_host(const base_address &compare_in) const +{ + CAST_UP(internet_address); + + // they can't be the same if one is a valid address and the other's not, but + // they are the same if both addresses are empty. + // even so, they're not the same if either's name was non-empty but they're + // both nil. + if ( (is_nil_address(ip_address) && is_nil_address(to_compare.ip_address)) + && !astring(hostname) && !astring(to_compare.hostname) ) + return true; + if ( (is_nil_address(ip_address) && is_nil_address(to_compare.ip_address)) + && (astring(hostname).iequals(to_compare.hostname)) ) + return true; + if (is_nil_address(ip_address)) return false; + if (is_nil_address(to_compare.ip_address)) return false; + + // check that the bytes don't differ for the IP address. + for (int i = 0; i < ADDRESS_SIZE; i++) + if (ip_address[i] != to_compare.ip_address[i]) + return false; // we have a loser. + + // they could still be different addresses if the hostnames differ... + + if (astring(hostname).t() && astring(to_compare.hostname).t()) { + // name comparison. + if (astring(hostname).lower() != astring(to_compare.hostname).lower()) + return false; + } + + return true; + // all bytes and host were identical, so it's the same address. +} + +bool internet_address::same_port(const base_address &compare_in) const +{ + CAST_UP(internet_address); + return port == to_compare.port; +} + +bool internet_address::shareable(const base_address &) const +{ return true; } + +bool internet_address::detokenize(const astring &info) +{ +#ifdef DEBUG_ADDRESS + FUNCDEF("detokenize"); +#endif + LOADER_ENTRY; + // either IP address or host must be specified. + + FIND("address", addr); +#ifdef DEBUG_ADDRESS + LOG(astring("info is ") + info + astring('.')); + LOG(astring("addr is ") + addr); +#endif + byte_array ip_found; + if (addr.t()) { + // this bit rips off successive bytes from the string until the internet + // address is filled out. ignores any erroneous strings. + for (int i = 0; i < ADDRESS_SIZE; i++) { +#ifdef DEBUG_ADDRESS + LOG(astring("ip curr: ") + addr); +#endif + int current_byte = addr.convert(int(0)); + ip_found += abyte(current_byte); +#ifdef DEBUG_ADDRESS + LOG(a_sprintf("%d: %02x ", i, current_byte)); +#endif + int indy = addr.find('.'); + addr.zap(0, indy); // no error checking, but whatever. + } + } + + FIND("host", host); + GRAB("port", port_t); // this one's definitely needed. + int port = port_t.convert(0); + fill(ip_found, host, port); +#ifdef DEBUG_ADDRESS + LOG(astring("tcp/ip address found::: ") + text_form()); +#endif + LOADER_EXIT; + return true; +} + +astring internet_address::tokenize() const +{ +#ifdef DEBUG_ADDRESS + FUNCDEF("tokenize"); +#endif + STORER_ENTRY; +#ifdef DEBUG_ADDRESS + LOG(a_sprintf("host eval is %d for %s", astring(hostname).t(), hostname)); +#endif + if (astring(hostname).t()) ADD("host", hostname); + bool print_ip = false; + for (int i = 0; i < ADDRESS_SIZE; i++) { + if (ip_address[i]) print_ip = true; + } + if (print_ip) { + astring ip_addr; + for (int i = 0; i < ADDRESS_SIZE; i++) + ip_addr += a_sprintf("%d", int(ip_address[i])) + astring("."); + ip_addr.zap(ip_addr.end(), ip_addr.end()); // remove last period. + ADD("address", ip_addr); + } + ADD("port", a_sprintf("%d", int(port))); +#ifdef DEBUG_ADDRESS + LOG(astring("your toke's currently ") + fred.text_form()); +#endif + DUMP_EXIT; +} + +bool internet_address::appropriate_for_ip(const astring &to_check) +{ + for (int i = 0; i < to_check.length(); i++) { + char curr = to_check[i]; + if (curr == '.') continue; + if ( (curr >= '0') && (curr <= '9') ) continue; + // an unsavory character was seen, so fail out. + return false; + } + return true; +} + +// by Gary Hardley. +// fixes by CAK 6/26/2002. +// and more by CAK in november 2010. +bool internet_address::is_valid_internet_address(const astring &to_check, + byte_array &ip_form, bool &all_zeros) +{ + astring tmpstr = to_check; // temporary copy of the given address string. + all_zeros = true; // default to true until proven otherwise. + ip_form.reset(); // empty the address. + + // if it's got anything besides dots and numbers, bail out. + if (!appropriate_for_ip(to_check)) return false; + // catch a case the below logic does not--where the string starts or + // ends with a dot. + if ( (to_check[0] == '.') || (to_check[to_check.end()] == '.') ) + return false; + // catch another case that was missed before, where there are multiple dots + // in a row but enough numbers to give 4 components. + if (to_check.contains("..")) return false; + + // get the first part of the address. + char *p = strtok(tmpstr.s(), "."); + + int index = 0; + while (p && (index < ADDRESS_SIZE)) { + int nTemp = astring(p).convert(-1); + + // is this part of the string a valid number between 0 & 255? + if ( (nTemp < 0) || (nTemp > 255) ) return false; // no. + + // yes, assign the number to the next address byte. + ip_form += (abyte)nTemp; + + // get the next part of the string + p = strtok(NIL, "."); + } + + // if p is non-null, there was extra stuff at the end, so return false. + if (p) return false; + + for (int i = 0; i < ip_form.length(); i++) + if (ip_form[i]) { all_zeros = false; break; } + + return ip_form.length() == ADDRESS_SIZE; +} + +bool internet_address::valid_address(const astring &to_check) +{ + byte_array addr; + bool all_zeros; + return is_valid_internet_address(to_check, addr, all_zeros); +} + +astring internet_address::ip_address_text_form(const byte_array &ip_address) +{ + return a_sprintf("%d.%d.%d.%d", int(ip_address[0]), + int(ip_address[1]), int(ip_address[2]), int(ip_address[3])); +} + +} //namespace. + + diff --git a/octopi/library/sockets/internet_address.h b/octopi/library/sockets/internet_address.h new file mode 100644 index 00000000..a8fba89e --- /dev/null +++ b/octopi/library/sockets/internet_address.h @@ -0,0 +1,147 @@ +#ifndef INTERNET_ADDRESS_CLASS +#define INTERNET_ADDRESS_CLASS + +////////////// +// Name : internet_address +// Author : Chris Koeritz +////////////// +// Copyright (c) 1995-$now By Author. 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: +// http://www.gnu.org/licenses/gpl.html +// or under the terms of the GNU Library license: +// http://www.gnu.org/licenses/lgpl.html +// at your preference. Those licenses describe your legal rights to this +// software, and no other rights or warranties apply. +// Please send updates for this code to: fred@gruntose.com -- Thanks, fred. +////////////// + +#include "base_address.h" + +#include + +namespace sockets { + +// forward. +class machine_uid; + +//! this type of address describes a destination out on the internet. + +class internet_address : public base_address +{ +public: + enum internet_address_constraints { + ADDRESS_SIZE = 4, + MAXIMUM_HOSTNAME_LENGTH = 128 + }; + + typedef basis::abyte address_array[ADDRESS_SIZE]; + address_array ip_address; + int port; + + char hostname[MAXIMUM_HOSTNAME_LENGTH]; + // can be resolved to an ip_address if a domain name server + // is available. + + internet_address(); + internet_address(const basis::byte_array &ip_address, const basis::astring &host, + int port); + + DEFINE_CLASS_NAME("internet_address"); + + machine_uid convert() const; + // returns the address in the uniquifying format. + + void fill(const basis::byte_array &ip_address, const basis::astring &host, int port); + + bool same_host(const base_address &to_compare) const; + bool same_port(const base_address &to_compare) const; + bool shareable(const base_address &to_compare) const; + + bool operator == (const internet_address &to_compare) const { + return same_host(to_compare) && same_port(to_compare); + } + + basis::astring text_form() const; + + basis::astring tokenize() const; + bool detokenize(const basis::astring &info); + + basis::astring normalize_host() const; + // returns a normal form for the hostname or address. this will come from + // the hostname member first, if it's set. next it will come from the + // string form of the IP address. + + static const basis::byte_array &nil_address(); + // returns the address that is all zeros, otherwise known as INADDR_ANY. + + bool is_nil_address() const; + // returns true if this object's address_array is all zeros. + + static bool is_nil_address(const address_array &ip_address); + // returns true if the array "ip_address" is all zero. + + static bool appropriate_for_ip(const basis::astring &to_check); + // tests whether the string is possibly an ip address; it must have no + // characters in it besides dots and numbers. + + static bool valid_address(const basis::astring &to_check); + // returns true if the address "to_check" seems well-formed for IP. note + // that this will accept 0.0.0.0 as valid. + + static bool is_valid_internet_address(const basis::astring &to_check, + basis::byte_array &ip_form, bool &all_zeros); + // this function checks the string "to_check" to see if it is a valid + // internet address (e.g., 143.203.39.222). if it is a valid address, + // then the address "ip_form" is filled in with four bytes that are each + // in the range (0..255). if the "ip_form" is 0.0.0.0, then all_zeros + // is set to true. + + static bool ip_appropriate_number(const basis::astring &to_check, int indy, + basis::astring &accum); + //!< returns true if "to_check" has a number at "indy" that works in ipv4. + /*!< this reports if the string at the position specified could be part of + a valid internet address. the characters starting at the "indy" must be + numeric. up to three numbers will be checked, and when we get three or + less of the numbers together, we will check that they make an integer less + than 255. the "accum" string will be filled with the number we found. + note that this doesn't care what the characters are after our 1-3 numbers; + it merely checks whether the portion of the string at "indy" *could* work + in an IP address. */ + + static bool has_ip_address(const basis::astring &to_check, basis::astring &ip_found); + //!< returns true if "to_check" has an IP address in it somewhere. + /*!< this looks across the whole string and returns the first IP address + it finds in "ip_found", if possible. */ + + static basis::astring ip_address_text_form(const basis::byte_array &ip_address); + // returns a string containing the textual form of the "ip_address". the + // "ip_address" is expected to have the proper length (four bytes). if it + // does not, then an empty string is returned. + + static const basis::byte_array &localhost(); + // provides the array that indicates localhost, rather than a NIC. + // this is commonly known to be 127.0.0.1. + + bool is_localhost() const; + // returns true if the address in question is the same as the localhost, + // either through the name matching "local" or "localhost", or through + // the address matching 127.0.0.1. note that the word "local" here is + // an "enhancement" and would not normally be considered the same as + // localhost. beware a networked computer that's actually named "local". + // also note that an address where the name disagrees with the address is + // basically broken, but we are not checking that here; either condition + // of the hostname or the address matching causes this to return true. + + base_address *create_copy() const; + + void pack(basis::byte_array &packed_form) const; + bool unpack(basis::byte_array &packed_form); + + virtual int packed_size() const; +}; + +} //namespace. + +#endif + diff --git a/octopi/library/sockets/machine_uid.cpp b/octopi/library/sockets/machine_uid.cpp new file mode 100644 index 00000000..5b007212 --- /dev/null +++ b/octopi/library/sockets/machine_uid.cpp @@ -0,0 +1,260 @@ +/*****************************************************************************\ +* * +* Name : machine_uid * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//#include "internet_address.h" +#include "machine_uid.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace structures; +using namespace textual; + +namespace sockets { + +machine_uid::machine_uid() +: _contents(new byte_array) +{ *_contents += abyte(INVALID_LOCATION); } + +machine_uid::machine_uid(known_location_types type, + const byte_array &address) +: _contents(new byte_array) +{ + *_contents += abyte(type); + *_contents += address; +} + +machine_uid::machine_uid(const machine_uid &to_copy) +: packable(), + _contents(new byte_array) +{ *this = to_copy; } + +machine_uid::~machine_uid() { WHACK(_contents); } + +void machine_uid::reset(known_location_types type, const byte_array &address) +{ + _contents->reset(); + *_contents += abyte(type); + *_contents += address; +} + +const byte_array &machine_uid::raw() const { return *_contents; } + +const astring &machine_uid::type_name(known_location_types type) +{ + static astring TCPIP_NAME = "TCPIP"; + static astring IPX_NAME = "IPX"; + static astring NETBIOS_NAME = "NETBIOS"; + static astring UNKNOWN_NAME = "INVALID"; + + switch (type) { + case TCPIP_LOCATION: return TCPIP_NAME; + case IPX_LOCATION: return IPX_NAME; + case NETBIOS_LOCATION: return NETBIOS_NAME; + default: return UNKNOWN_NAME; + } +} + +machine_uid &machine_uid::operator = (const machine_uid &to_copy) +{ + if (this == &to_copy) return *this; + *_contents = *to_copy._contents; + return *this; +} + +astring machine_uid::text_form() const +{ + astring to_return; + to_return += type_name(type()) + "["; + for (int i = 1; i < _contents->length(); i++) { + if (type() == TCPIP_LOCATION) + to_return += a_sprintf("%d", int(_contents->get(i))); + else + to_return += a_sprintf("%02x", int(_contents->get(i))); + if (i < _contents->length() - 1) + to_return += "."; + } + to_return += "]"; + return to_return; +} + +astring machine_uid::compact_form() const +{ + astring to_return; + byte_formatter::bytes_to_shifted_string(*_contents, to_return); + return to_return; +} + +machine_uid machine_uid::expand(const astring &compacted) +{ + machine_uid to_return; + to_return._contents->reset(); + byte_formatter::shifted_string_to_bytes(compacted, *to_return._contents); + return to_return; +} + +bool machine_uid::operator == (const machine_uid &to_compare) const +{ + // an empty id is only equal to another empty id. + if (!_contents->length() || !to_compare._contents->length()) + return !_contents->length() && !to_compare._contents->length(); + if (_contents->length() != to_compare._contents->length()) return false; + for (int i = 0; i < _contents->length(); i++) + if (_contents->get(i) != to_compare._contents->get(i)) return false; + return true; +} + +machine_uid::known_location_types machine_uid::type() const +{ + if (!_contents->length()) return INVALID_LOCATION; + return known_location_types(_contents->get(0)); +} + +int machine_uid::packed_size() const +{ + return PACKED_SIZE_INT32 + _contents->length(); +} + +void machine_uid::pack(byte_array &packed_form) const +{ + structures::attach(packed_form, _contents->length()); + packed_form += *_contents; +} + +bool machine_uid::unpack(byte_array &packed_form) +{ + int len = 0; + if (!structures::detach(packed_form, len)) return false; + // there's no length even? + if (packed_form.length() < len) return false; + // not enough size left for the length specified. + *_contents = packed_form.subarray(0, len - 1); + packed_form.zap(0, len - 1); + return true; +} + +byte_array machine_uid::native() const +{ + if (_contents->length() <= 1) return byte_array(); // invalid. + return _contents->subarray(1, _contents->last()); +} + +////////////// + +class internal_machine_uid_array : public array {}; + +machine_uid_array::machine_uid_array() +: _uids(new internal_machine_uid_array) +{} + +machine_uid_array::machine_uid_array(const machine_uid_array &to_copy) +: root_object(), + _uids(new internal_machine_uid_array(*to_copy._uids)) +{ +} + +machine_uid_array::~machine_uid_array() +{ + WHACK(_uids); +} + +SAFE_STATIC_CONST(machine_uid_array, machine_uid_array::blank_array, ) + +machine_uid_array &machine_uid_array::operator = + (const machine_uid_array &to_copy) +{ + if (this != &to_copy) { + *_uids = *to_copy._uids; + } + return *this; +} + +bool machine_uid_array::operator += (const machine_uid &to_add) +{ + if (member(to_add)) return false; + _uids->concatenate(to_add); + return true; +} + +int machine_uid_array::elements() const { return _uids->length(); } + +astring machine_uid_array::text_form() const +{ + astring to_return; + for (int i = 0; i < _uids->length(); i++) { + to_return += _uids->get(i).text_form() + " "; + } + return to_return; +} + +machine_uid &machine_uid_array::operator [] (int index) +{ return _uids->use(index); } + +const machine_uid &machine_uid_array::operator [] (int index) const +{ return _uids->get(index); } + +void machine_uid_array::reset() { _uids->reset(); } + +bool machine_uid_array::member(const machine_uid &to_test) const +{ + const int test_len = to_test.raw().length(); + for (int i = 0; i < _uids->length(); i++) { + const machine_uid &curr = _uids->get(i); + // test for the length first since that's cheaper. + if ( (test_len == curr.raw().length()) && (curr == to_test) ) { + return true; + } else if (!to_test.valid() && !curr.valid()) return true; + // both are invalid, so the item to find is present. + +/* + if (!to_test.valid() || !curr.valid()) continue; + // one is invalid but the other is not; not a match. +all bogus! +//hmmm: weird partial matching being allowed here. + bool to_return = true; // assume it's good until told otherwise. + // if the first parts of the addresses agree, then assume we're + for (int j = 0; j < minimum(test_len, curr.raw().length()); j++) { + if (curr.raw().get(j) != to_test.raw().get(j)) { + to_return = false; + } + } + if (to_return) + return true; + } +*/ + } + return false; +} + +////////////// + +internet_machine_uid::internet_machine_uid(const astring &hostname, + const byte_array &ip_address) +{ + byte_array addr(ip_address); + basis::un_short fletch = checksums::fletcher_checksum((const abyte *)hostname.observe(), + hostname.length()); + structures::attach(addr, fletch); + reset(machine_uid::TCPIP_LOCATION, addr); +} + +} //namespace. + diff --git a/octopi/library/sockets/machine_uid.h b/octopi/library/sockets/machine_uid.h new file mode 100644 index 00000000..47a1ba73 --- /dev/null +++ b/octopi/library/sockets/machine_uid.h @@ -0,0 +1,161 @@ +#ifndef MACHINE_UID_CLASS +#define MACHINE_UID_CLASS + +/*****************************************************************************\ +* * +* Name : machine_uid * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* This object identifies a machine uniquely within a particular network * +* configuration. It is not world-unique, since some location names are not * +* either. For example, a TCP/IP address like 10.2.13.9 might be used many * +* times around the world, since it is reserved for private networks. But * +* within one valid network configuration, there should not be more than one * +* of these addresses. If there are, then be aware that multiple machines * +* might answer to a particular address and invalidate some assumptions of * +* uniqueness. * +* The machine_uid is most useful when a program wishes to ensure that it * +* treats a machine only once when it is performing some form of processing * +* for that address. For example, the id could be contained within a 'set' * +* to ensure that a message is only sent once to each machine of interest. * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace sockets { + +// forward. +class internal_machine_uid_array; + +class machine_uid : public virtual basis::packable +{ +public: + enum known_location_types { + INVALID_LOCATION, // this id has not been initialized. + TCPIP_LOCATION, // a location on the internet. + IPX_LOCATION, // a host on an IPX/SPX network. + NETBIOS_LOCATION // a machine reachable by SMB protocol. + }; + static const basis::astring &type_name(known_location_types type); + // returns the text form for the "type" specified. + + machine_uid(); // constructs an invalid id. + + machine_uid(known_location_types type, const basis::byte_array &address); + // sets up an id for the "type" specified given the serialized "address". + // the format used here is that bytes follow in the natural listing order + // for the address "type". + + machine_uid(const machine_uid &to_copy); // copy constructor. + + virtual ~machine_uid(); + + known_location_types type() const; + // returns the type currently held. + + bool valid() const { return type() != INVALID_LOCATION; } + // returns true if this id seems possibly valid. an id for which this + // returns true could still have protocol specific issues that make it + // invalid, but as far as this class can tell, it looks okay. + + machine_uid &operator =(const machine_uid &to_copy); + + void reset(known_location_types type, const basis::byte_array &address); + // reconstructs the machine_uid with new parameters. + + basis::astring text_form() const; + // returns a string that describes the address held here. + + basis::astring compact_form() const; + // returns a non-readable string form of the id that is more efficient. + static machine_uid expand(const basis::astring &compacted); + // gets the original machine_uid out of the "compacted" form. + + basis::byte_array native() const; + // returns the identifier for the machine in the native format. for + // internet1, this is 4 bytes of IP address. + + // equality means: same protocol type, same key length, and same contents. + bool operator == (const machine_uid &to_compare) const; + + // meets the requirements of packable. + virtual int packed_size() const; + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); + + const basis::byte_array &raw() const; + // returns the raw internal representation for the machine_uid. + +private: + basis::byte_array *_contents; +}; + +////////////// + +class internet_machine_uid : public machine_uid +{ +public: + internet_machine_uid(const basis::astring &hostname, const basis::byte_array &ip_address); + // constructs a machine uid that almost always uniquely identifies a + // machine on the internet. if all ip addresses in a system are unique, + // then IMU is always unique. however, if all ip addresses in a system + // are not unique, then the IMU can discriminate the hostname as long as + // no two hostnames resolve to the same checksum. +}; + +////////////// + +// this object contains a list of unique machine identifiers. this is +// intentionally just an array (and not a set) because some usages of the +// list of machine_uids need a notion of ordering (such as for a list of +// machines that have touched a packet). + +class machine_uid_array : public virtual basis::root_object +{ +public: + machine_uid_array(); + machine_uid_array(const machine_uid_array &to_copy); + ~machine_uid_array(); + DEFINE_CLASS_NAME("machine_uid_array"); + + static const machine_uid_array &blank_array(); + + machine_uid_array &operator =(const machine_uid_array &to_copy); + + bool operator += (const machine_uid &to_add); + + basis::astring text_form() const; + + int elements() const; + + void reset(); + + machine_uid &operator [] (int index); + const machine_uid &operator [] (int index) const; + + bool member(const machine_uid &to_test) const; + // returns true if the id "to_test" is a member of the list of machine + // ids held here. if the machine_uid's are of different sizes, then a + // prefix compare is used. this allows an id without the host + // discriminator portion to match the full version. + +private: + internal_machine_uid_array *_uids; // the underlying machine_uid list. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/sockets/makefile b/octopi/library/sockets/makefile new file mode 100644 index 00000000..83271dd1 --- /dev/null +++ b/octopi/library/sockets/makefile @@ -0,0 +1,14 @@ +CONSOLE_MODE = t + +include cpp/variables.def + +PROJECT = sockets +TYPE = library +TARGETS = sockets.lib +SOURCE = internet_address.cpp machine_uid.cpp range_limiter.cpp raw_socket.cpp \ + sequence_tracker.cpp socket_minder.cpp span_manager.cpp spocket.cpp subnet_calculator.cpp \ + tcpip_stack.cpp throughput_counter.cpp +VCPP_USE_SOCK = true + +include cpp/rules.def + diff --git a/octopi/library/sockets/range_limiter.cpp b/octopi/library/sockets/range_limiter.cpp new file mode 100644 index 00000000..9a45bc6a --- /dev/null +++ b/octopi/library/sockets/range_limiter.cpp @@ -0,0 +1,132 @@ +/*****************************************************************************\ +* * +* Name : range_limiter * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "range_limiter.h" +#include "machine_uid.h" + +#include +#include + +using namespace basis; +using namespace structures; + +namespace sockets { + +class range_record +{ +public: + machine_uid _first; + machine_uid _second; + astring _host; + bool _is_host; // true if the host has any useful information. + bool _single; // true if only the first address matters. +}; + +////////////// + +class limiter_range_list : public amorph +{ +public: +}; + +////////////// + +range_limiter::range_limiter() +: _ranges(new limiter_range_list) +{ +} + +range_limiter::range_limiter(const astring &source_file, + const astring §ion) +: _ranges(new limiter_range_list) +{ + load(source_file, section); +} + +range_limiter::~range_limiter() { WHACK(_ranges); } + +bool range_limiter::is_allowed(const machine_uid &host) +{ +if (host.valid()) {} +return false; +} + +bool range_limiter::is_allowed(const astring &hostname) +{ +if (!hostname) {} +return false; +} + +range_limiter::capabilities range_limiter::get_default() +{ +return DENY; +} + +void range_limiter::set_default(capabilities rights) +{ +if (!rights) {} +} + +bool range_limiter::add(const machine_uid &address, capabilities rights) +{ +if (address.valid() || rights) {} +return false; +} + +bool range_limiter::add(const astring &hostname, capabilities rights) +{ +if (!hostname || rights) {} +return false; +} + +bool range_limiter::add(const machine_uid &first, const machine_uid &second, + capabilities rights) +{ +if (first.valid() || second.valid() || rights) {} +return false; +} + +bool range_limiter::remove(const machine_uid &address) +{ +if (address.valid()) {} +return false; +} + +bool range_limiter::remove(const astring &hostname) +{ +if (!hostname) {} +return false; +} + +bool range_limiter::remove(const machine_uid &first, const machine_uid &second) +{ +if (first.valid() || second.valid()) {} +return false; +} + +bool range_limiter::load(const astring &file_name, const astring §ion) +{ +if (!file_name || !section) {} +return false; +} + +bool range_limiter::save(const astring &file_name, const astring §ion) +{ +if (!file_name || !section) {} +return false; +} + +} //namespace. + + diff --git a/octopi/library/sockets/range_limiter.h b/octopi/library/sockets/range_limiter.h new file mode 100644 index 00000000..418f97a1 --- /dev/null +++ b/octopi/library/sockets/range_limiter.h @@ -0,0 +1,87 @@ + +//not implemented yet. + +#ifndef ADDRESS_LIMITER_CLASS +#define ADDRESS_LIMITER_CLASS + +/*****************************************************************************\ +* * +* Name : range_limiter * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Provides a way to check whether an IP address is within a range of * +* allowed addresses. Also manages a configuration file that stores the * +* sets of ranges. * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace sockets { + +// forward. +class limiter_range_list; +class machine_uid; + +//! provides a mechanism for restricting access to a resource by the client's IP address. +class range_limiter +{ +public: + range_limiter(); + // constructs a blank range_limiter. + + range_limiter(const basis::astring &source_file, const basis::astring §ion); + // constructs an range_limiter by loading from the "source_file" in + // the ini "section". + + ~range_limiter(); + + enum capabilities { + ALLOW, + DENY + }; + + bool is_allowed(const machine_uid &host); + // checks whether a "host" is in one of the allowed ranges. + bool is_allowed(const basis::astring &hostname); + // checks whether a "hostname" is in one of the allowed ranges. this can + // either be a text string such as "jumbo.gruntose.blurgh" or it can be + // a dotted number representation (like "128.28.48.119"). + + // observes or modifies the default access permission. the default will + // be used when no other permissions apply. + capabilities get_default(); + void set_default(capabilities rights); + + // these add addresses to the list with the "rights" specified. + bool add(const machine_uid &address, capabilities rights); + bool add(const basis::astring &hostname, capabilities rights); + bool add(const machine_uid &first, const machine_uid &second, + capabilities rights); + + // takes addresses out of the list of filters. + bool remove(const machine_uid &address); + bool remove(const basis::astring &hostname); + bool remove(const machine_uid &first, const machine_uid &second); + + // retrieves or stores the range and capability information. + bool load(const basis::astring &file_name, const basis::astring §ion); + bool save(const basis::astring &file_name, const basis::astring §ion); + +private: + limiter_range_list *_ranges; +}; + +} //namespace. + +#endif + diff --git a/octopi/library/sockets/raw_socket.cpp b/octopi/library/sockets/raw_socket.cpp new file mode 100644 index 00000000..c149e18b --- /dev/null +++ b/octopi/library/sockets/raw_socket.cpp @@ -0,0 +1,475 @@ +/*****************************************************************************\ +* * +* Name : raw_socket * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "raw_socket.h" +#include "tcpip_stack.h" + +#include +#include +#include +#include + +#include +#ifdef __APPLE__ + #include +#endif +#ifdef __UNIX__ + #include + #include + #include + #include + #include + #include + #define OPTYPE (void *) +#endif +#ifdef __WIN32__ + #define OPTYPE (char *) +#endif + +using namespace basis; +using namespace loggers; +using namespace timely; + +namespace sockets { + +//#define DEBUG_RAW_SOCKET + // uncomment for noisy diagnostics. + +#undef LOG +#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print) + +const int MULTIPLE_DISCONNECT_CHECKS = 28; + // we will make certain that select really says there's data and ioctl + // really says there's no data waiting before we believe there's been + // a disconnect. we'll check that state the number of times specified. + +class fd_set_wrapper : public fd_set {}; + +////////////// + +const basis::un_int NON_BLOCKING = FIONBIO; +const basis::un_int IOCTL_READ = FIONREAD; + +#ifdef __WIN32__ +/* +// defined by winsock header but not present in the winsock dll. +int PASCAL FAR __WSAFDIsSet(SOCKET fd, fd_set FAR *the_set) +{ + int i = the_set->fd_count; + while (i--) + if (the_set->fd_array[i] == fd) + return true; + return false; +} +*/ +#endif + +////////////// + +raw_socket::raw_socket() +: _stack(new tcpip_stack()) +{} + +raw_socket::~raw_socket() +{ + WHACK(_stack); +} + +int raw_socket::close(basis::un_int &socket) +{ + int to_return = 0; +#ifdef __WIN32__ + to_return = closesocket(socket); +#endif +#ifdef __UNIX__ + to_return = ::close(socket); +#endif + socket = 0; + return to_return; +} + +//move this into parser bits as a OR combiner or something. +void combine(astring &existing, const astring &addition) +{ + if (addition.t()) { + if (existing.t()) existing += " | "; + existing += addition; + } +} + +astring raw_socket::interest_name(int interest) +{ + astring to_return; + if (interest & SI_CONNECTED) combine(to_return, "CONNECTED"); + if (interest & SI_DISCONNECTED) combine(to_return, "DISCONNECTED"); + if (interest & SI_WRITABLE) combine(to_return, "WRITABLE"); + if (interest & SI_READABLE) combine(to_return, "READABLE"); + if (interest & SI_ERRONEOUS) combine(to_return, "ERRONEOUS"); + if (!interest) combine(to_return, "NORMAL"); + return to_return; +} + +int raw_socket::ioctl(basis::un_int socket, int request, void *argp) const +{ +#ifdef __UNIX__ + return ::ioctl(socket, request, argp); +#endif +#ifdef __WIN32__ + return ioctlsocket(socket, request, (un_long *)argp); +#endif +} + +bool raw_socket::set_non_blocking(basis::un_int socket, bool non_blocking) +{ + FUNCDEF("set_non_blocking"); +#ifdef __APPLE__ + int curr_flags = fcntl(socket, F_GETFL, 0); + if (fcntl(socket, F_SETFL, curr_flags | O_NONBLOCK) < 0) return false; +#else + int arg = int(non_blocking); + if (negative(ioctl(socket, NON_BLOCKING, &arg))) { + LOG(a_sprintf("Could not set non-blocking (FIONBIO) option on raw_socket %u.", socket)); + return false; + } +#endif + return true; +} + +bool raw_socket::set_nagle_algorithm(basis::un_int socket, bool use_nagle) +{ + FUNCDEF("set_nagle_algorithm"); + int arg = int(!use_nagle); // opposite of the flag, since we set no-delay. + if (negative(setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, OPTYPE &arg, + sizeof(arg)))) { + LOG(a_sprintf("Could not change nagle coalescing mode on %u.", socket)); + return false; + } + return true; +} + +bool raw_socket::set_broadcast(basis::un_int socket, bool broadcasting) +{ + FUNCDEF("set_broadcast"); + int arg = int(broadcasting); + if (negative(setsockopt(socket, SOL_SOCKET, SO_BROADCAST, OPTYPE &arg, + sizeof(arg)))) { + LOG(a_sprintf("Could not change broadcast mode on %u.", socket)); + return false; + } + return true; +} + +bool raw_socket::set_reuse_address(basis::un_int socket, bool reuse) +{ + FUNCDEF("set_reuse_address"); + int arg = int(reuse); + if (negative(setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, OPTYPE &arg, + sizeof(arg)))) { + LOG(a_sprintf("Could not set reuse address mode on %u.", socket)); + return false; + } + return true; +} + +bool raw_socket::set_keep_alive(basis::un_int socket, bool keep_alive) +{ + FUNCDEF("set_keep_alive"); + int arg = int(keep_alive); + if (negative(setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, OPTYPE &arg, + sizeof(arg)))) { + LOG(a_sprintf("Could not set keep alive mode on %u.", socket)); + return false; + } + return true; +} + +int raw_socket::select(basis::un_int socket, int mode, int timeout) const +{ +// FUNCDEF("select [single]"); + if (!socket) return SI_ERRONEOUS; + fd_set_wrapper read_list, write_list, exceps; + int ret = inner_select(socket, mode, timeout, read_list, write_list, exceps); + if (!ret) return 0; // nothing is happening. + if (ret == SI_ERRONEOUS) return SI_ERRONEOUS; // something bad happened. + // otherwise we should be at base-line status. + return analyze_select_result(socket, mode, read_list, write_list, exceps); +} + +int raw_socket::inner_select(basis::un_int socket, int mode, int timeout, + fd_set_wrapper &read_list, fd_set_wrapper &write_list, + fd_set_wrapper &exceptions) const +{ +#ifdef DEBUG_RAW_SOCKET + FUNCDEF("inner_select"); +#endif + // setup the file descriptor sets for the select. we check readability, + // writability and exception status. + FD_ZERO(&read_list); FD_SET(socket, &read_list); + FD_ZERO(&write_list); FD_SET(socket, &write_list); + FD_ZERO(&exceptions); FD_SET(socket, &exceptions); + + timeval time_out = time_stamp::fill_timeval_ms(timeout); + // timeval has tv_sec=seconds, tv_usec=microseconds. + + // select will tell us about the socket. + int ret = ::select(socket + 1, + (mode & SELECTING_JUST_WRITE)? NIL : &read_list, + (mode & SELECTING_JUST_READ)? NIL : &write_list, + &exceptions, &time_out); + int error = critical_events::system_error(); + + if (!ret) return 0; // nothing to report. + + if (ret == SOCKET_ERROR) { + switch (error) { + // all real errors fall out to the error handling stuff. + case SOCK_EFAULT: // intentional fall-through. + case SOCK_ENETDOWN: // intentional fall-through. + case SOCK_EINVAL: // intentional fall-through. + case SOCK_EINTR: // intentional fall-through. +#ifdef __WIN32__ + case SOCK_NOTINITIALISED: // intentional fall-through. +#endif + case SOCK_ENOTSOCK: + break; + + // hopefully all these others are bogus errors... + case SOCK_EINPROGRESS: // intentional fall-through. + case 0: // intentional fall-through. + default: +#ifdef DEBUG_RAW_SOCKET + LOG("got to weird case, in progress or zero."); +#endif + return 0; // not really an error. + } +#ifdef DEBUG_RAW_SOCKET + LOG(a_sprintf("socket %u had error %d in select: %s.", + socket, error, _stack->tcpip_error_name(error).s())); +#endif + return SI_ERRONEOUS; + } + + // if we got to here, then there are some things to report... + return SI_BASELINE; +} + +int raw_socket::test_readability(basis::un_int socket) const +{ + FUNCDEF("test_readability"); + basis::un_int len; + if (negative(ioctl(socket, IOCTL_READ, &len))) { + LOG(astring(astring::SPRINTF, "socket %u had ioctl error: %s.", + socket, _stack->tcpip_error_name(critical_events::system_error()).s())); + return SI_ERRONEOUS; + } else { + if (positive(len)) return SI_READABLE; + else return SI_DISCONNECTED; + } +} + +int raw_socket::analyze_select_result(basis::un_int socket, int mode, + fd_set_wrapper &read_list, fd_set_wrapper &write_list, + fd_set_wrapper &exceptions) const +{ +#ifdef DEBUG_RAW_SOCKET + FUNCDEF("analyze_select_result"); +#endif + int to_return = 0; + + // in case of an exception, we return an error. + if (FD_ISSET(socket, &exceptions)) { +#ifdef DEBUG_RAW_SOCKET + LOG(astring(astring::SPRINTF, "exception seen for socket %u!", socket)); +#endif + } + + // check to see if there are bytes to read. + if ( ! (mode & SELECTING_JUST_WRITE) && FD_ISSET(socket, &read_list)) { + // make sure we have data. if no data is available, it means a + // disconnect occurred. + + int readable = test_readability(socket); + if (readable == SI_ERRONEOUS) + to_return |= SI_ERRONEOUS; + else if (readable == SI_READABLE) + to_return |= SI_READABLE; + else if (readable == SI_DISCONNECTED) { + // we need to check multiple times to be sure the OS really means this. + // either windoze seems to report an erroneous disconnect every few + // days or there's a bad synchronization issue as yet uncovered. + bool really_disconnected = true; + for (int i = 0; i < MULTIPLE_DISCONNECT_CHECKS; i++) { + fd_set_wrapper read_list, write_list, exceps; + int temp_ret = inner_select(socket, SELECTING_JUST_READ, 0, read_list, + write_list, exceps); + // check the return value first... + if (!temp_ret) { + // nothing happening (a zero return) means the socket's no longer + // claiming to have a readable state; our disconnect condition is + // thus violated and we can leave. + really_disconnected = false; + break; + } + if (temp_ret == SI_ERRONEOUS) { + // this, on the other hand, sounds really bad. the socket doesn't + // seem to exist any more or something else horrid happened. + really_disconnected = true; + break; + } + // if the select worked, we can check the fd_set now for readability. + if (!FD_ISSET(socket, &read_list)) { + // we are not in a disconnected state without being told we're + // readable. supposedly. + really_disconnected = false; + break; + } + // now we really test the socket for readability by making sure there + // really is data pending on the socket. if it's readable but there's + // no data, then either a disconnection has occurred or is in progress. + readable = test_readability(socket); + if (readable != SI_DISCONNECTED) { + // we are not disconnected if there's really data waiting. + really_disconnected = false; + break; + } + } + if (really_disconnected) { +#ifdef DEBUG_RAW_SOCKET + LOG(a_sprintf("connection closed on socket %u.", socket)); +#endif + to_return |= SI_DISCONNECTED; + } + } + } + + // check writability state. + if (! (mode & SELECTING_JUST_READ) && FD_ISSET(socket, &write_list)) { + to_return |= SI_WRITABLE; + } + + return to_return; +} + +int raw_socket::select(int_array &read_sox, int_array &write_sox, + int timeout) const +{ +#ifdef DEBUG_RAW_SOCKET + FUNCDEF("select [multiple]"); +#endif + if (!read_sox.length() && !write_sox.length()) + return 0; // nothing happened to nothing. + + int to_return = 0; // will get bits slammed into it to report results. + + // setup the file descriptor sets for the select. we check readability, + // writability and exception status. + fd_set_wrapper read_list; FD_ZERO(&read_list); + fd_set_wrapper write_list; FD_ZERO(&write_list); + fd_set_wrapper exceptions; FD_ZERO(&exceptions); + // set up the lists with the sets we were handed. + basis::un_int highest = 0; + int i = 0; + for (i = 0; i < read_sox.length(); i++) { + basis::un_int sock = (basis::un_int)read_sox[i]; + if (sock > highest) highest = sock; + FD_SET(sock, &read_list); + } + for (i = 0; i < write_sox.length(); i++) { + basis::un_int sock = (basis::un_int)write_sox[i]; + if (sock > highest) highest = sock; + FD_SET(sock, &write_list); + } + + timeval time_out = time_stamp::fill_timeval_ms(timeout); + // timeval has tv_sec=seconds, tv_usec=microseconds. + + // select will tell us about the socket. + int ret = ::select(highest + 1, + (read_sox.length())? &read_list : NIL, + (write_sox.length())? &write_list : NIL, + &exceptions, &time_out); + int error = critical_events::system_error(); + + if (ret == SOCKET_ERROR) { + switch (error) { + // all real errors fall out to the error handling stuff. + case SOCK_EFAULT: // intentional fall-through. + case SOCK_ENETDOWN: // intentional fall-through. + case SOCK_EINVAL: // intentional fall-through. + case SOCK_EINTR: // intentional fall-through. +#ifdef __WIN32__ + case SOCK_NOTINITIALISED: // intentional fall-through. +#endif + case SOCK_ENOTSOCK: + break; + + // hopefully all these others are bogus errors... + case SOCK_EINPROGRESS: // intentional fall-through. + case 0: // intentional fall-through. + default: +#ifdef DEBUG_RAW_SOCKET + LOG("got to weird case, in progress or zero."); +#endif + +//hmmm: fix retd sox? what's this outcome mean for the list? + + return 0; // not really an error. + } +#ifdef DEBUG_RAW_SOCKET + LOG(a_sprintf("sockets had error %d in select: %s.", + error, _stack->tcpip_error_name(error).s())); +#endif + +//hmmm: fix retd sox? what's this outcome mean for the list? + + return SI_ERRONEOUS; + } else if (!ret) { + // we know of nothing exciting for any of these. + read_sox.reset(); + write_sox.reset(); + return 0; // nothing to report. + } + + // if we got to here, then there are some things to report... + // iterate through the lists and check results. + for (int k = 0; k < read_sox.length(); k++) { + basis::un_int socket = read_sox[k]; + int interim = analyze_select_result(socket, SELECTING_JUST_READ, read_list, + write_list, exceptions); + if (!interim) { + // nothing happened on that guy. + read_sox.zap(k, k); + k--; // skip back to before the whack. + continue; + } + to_return |= interim; // join the results with overall result. + } + for (int p = 0; p < write_sox.length(); p++) { + basis::un_int socket = write_sox[p]; + int interim = analyze_select_result(socket, SELECTING_JUST_WRITE, read_list, + write_list, exceptions); + if (!interim) { + // nothing happened on that guy. + write_sox.zap(p, p); + p--; // skip back to before the whack. + continue; + } + to_return |= interim; // join the results with overall result. + } + + return to_return; +} + +} //namespace. + diff --git a/octopi/library/sockets/raw_socket.h b/octopi/library/sockets/raw_socket.h new file mode 100644 index 00000000..de98b310 --- /dev/null +++ b/octopi/library/sockets/raw_socket.h @@ -0,0 +1,274 @@ +#ifndef RAW_SOCKET_CLASS +#define RAW_SOCKET_CLASS + +/*****************************************************************************\ +* * +* Name : raw_socket * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//! Provides access to the operating system's socket methods. + +/*! + NOTE: This class does not provide any sort of synchronization of the + sockets involved. If you have multiple threads arbitrarily calling select + and read, then some selects will claim the socket was disconnected (since + the other thread might have grabbed the data that seemed to be ready but + then wasn't--a sign of a disconnect). Ensure that your accesses of the + socket are serialized. +*/ + +#include +#include +#include + +namespace sockets { + +// forward declarations. +class fd_set_wrapper; +class tcpip_stack; + +////////////// + +// sockets can be intrigued by the occurrence of the following conditions: +enum socket_interests { + SI_READABLE = 0x1, // the socket is readable; there's data there. + SI_WRITABLE = 0x2, // the socket will accept data if it's sent. + SI_CONNECTED = 0x4, // the socket is connected. + SI_DISCONNECTED = 0x8, // the socket is disconnected. + SI_ERRONEOUS = 0x10, // the socket is in an erroneous state. + SI_BASELINE = 0x20, // the socket seems okay but not interesting. + + SI_ALL_SOCK_INT = 0xFF // all socket interests are active. +}; + +////////////// + +class raw_socket : public virtual basis::root_object +{ +public: + raw_socket(); + + ~raw_socket(); + + DEFINE_CLASS_NAME("raw_socket"); + +// int open( +//hmmm... + + int close(basis::un_int &socket); + // disconnects, destroys and resets the "socket" to zero. + + int ioctl(basis::un_int socket, int request, void *argp) const; + // manipulates the device parameters for the "socket". + + bool set_non_blocking(basis::un_int socket, bool non_blocking = true); + // makes the "socket" into a non-blocking socket if "non_blocking" is true. + // if "non_blocking" is false, then the socket is reset to blocking mode. + + bool set_nagle_algorithm(basis::un_int socket, bool use_nagle = true); + // sets the nagle algorithm on "socket" if "use_nagle" is true. note that + // true is the default when a socket is created; to change that for the + // socket, "use_nagle" should be false. + + bool set_broadcast(basis::un_int socket, bool broadcasting = true); + // sets broadcast mode on the socket so that it can receive packets that + // are broadcast to the network. + + bool set_reuse_address(basis::un_int socket, bool reuse = true); + // sets the socket to allow an address to be re-used when it's already + // in use, rather than getting a bind error. + + bool set_keep_alive(basis::un_int socket, bool keep_alive = true); + // marks a connected-mode socket so that keep alive packets will be sent + // occasionally to ensure that a disconnection is noticed. + + static basis::astring interest_name(int to_name); + // returns the textual form for the interests set in "to_name". + + // outlines any special constraints on the select invocation. + enum select_types { + SELECTING_JUST_WRITE = 0x1, + SELECTING_JUST_READ = 0x2 + }; + + int select(basis::un_int socket, int selection_mode, int timeout = 0) const; + // this is similar to the low-level select on a bsd socket. usually this + // will return events of any type--read, write and exception. exceptions + // will always be cause for a return, but if you just want to look at a + // read condition, include the JUST_READ flag in the "selection_mode". to + // just check for write, add the JUST_WRITE flag. this function returns a + // bitwise ORed value from the different types of 'socket_interests' (see + // tcpip definitions, which is where the enum is currently stored). if + // there are no special conditions noted on the socket, then zero is + // returned. if the "timeout" is zero, the function will return right + // away. if "timeout" (measured in milliseconds) is non-zero, then the + // function will wait until the condition becomes true or the "timeout" + // elapses. note: no infinite timeouts are provided here. + + int select(basis::int_array &read_sox, basis::int_array &write_sox, int timeout = 0) const; + // similar to select above, but operates on a list of sockets in the + // "read_sox" and "write_sox". the "read_sox" are checked to see whether + // those sockets have data pending that could be read. the "write_sox" are + // checked to see if the sockets are currently writable without blocking. + // if any sockets have events applicable to the "selection_mode", then they + // will still be present in the lists when the call returns. zero is + // returned if absolutely nothing happened during the "timeout" period; + // otherwise, a non-zero number is returned but the individual sockets + // must still be inspected to determine what happened. + + int analyze_select_result(basis::un_int socket, int mode, fd_set_wrapper &read_list, + fd_set_wrapper &write_list, fd_set_wrapper &exceptions) const; + // examines the "socket" in the fd_sets passed in and returns a result + // based on those sets and the "mode". + +private: + tcpip_stack *_stack; + + int test_readability(basis::un_int socket) const; + // checks on the readability state for the "socket", assuming that select() + // reported the socket as readable, and returns either SI_ERRONEOUS, + // SI_READABLE or SI_DISCONNECTED based on what ioctl() says about it. + + int inner_select(basis::un_int socket, int selection_mode, int timeout, + fd_set_wrapper &read_list, fd_set_wrapper &write_list, + fd_set_wrapper &exceptions) const; + // intermediate function that doesn't attempt to fully analyze the select + // result. the returned value will be non-zero if something interesting + // is happening on the socket. if that value is SI_ERRONEOUS, then + // something bad happened to the socket. the other non-zero value is + // SI_BASELINE, which means the socket has something to report in the + // fd_set parameters. +}; + +////////////// + +#ifdef __UNIX__ + // provide some unifying definitions. + #define INVALID_SOCKET -1 + #define SOCKET_ERROR -1 + typedef void sock_hop; + + // provide synonyms for errors so we don't conflict with the windows + // brain-deadness. they define error values like EACCESS but they're not + // the real values you need to use with tcp/ip. french fried gates time. + #define SOCK_EACCES EACCES + #define SOCK_EADDRINUSE EADDRINUSE + #define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL + #define SOCK_EAFNOSUPPORT EAFNOSUPPORT + #define SOCK_EALREADY EALREADY + #define SOCK_EBADF EBADF + #define SOCK_ECONNABORTED ECONNABORTED + #define SOCK_ECONNREFUSED ECONNREFUSED + #define SOCK_ECONNRESET ECONNRESET + #define SOCK_EDESTADDRREQ EDESTADDRREQ + #define SOCK_EDQUOT EDQUOT + #define SOCK_EFAULT EFAULT + #define SOCK_EHOSTDOWN EHOSTDOWN + #define SOCK_EHOSTUNREACH EHOSTUNREACH + #define SOCK_EINPROGRESS EINPROGRESS + #define SOCK_EINTR EINTR + #define SOCK_EINVAL EINVAL + #define SOCK_EISCONN EISCONN + #define SOCK_ELOOP ELOOP + #define SOCK_EMFILE EMFILE + #define SOCK_EMSGSIZE EMSGSIZE + #define SOCK_ENAMETOOLONG ENAMETOOLONG + #define SOCK_ENETDOWN ENETDOWN + #define SOCK_ENETUNREACH ENETUNREACH + #define SOCK_ENETRESET ENETRESET + #define SOCK_ENOBUFS ENOBUFS + #define SOCK_ENOPROTOOPT ENOPROTOOPT + #define SOCK_ENOTCONN ENOTCONN + #define SOCK_ENOTEMPTY ENOTEMPTY + #define SOCK_ENOTSOCK ENOTSOCK + #define SOCK_EOPNOTSUPP EOPNOTSUPP + #define SOCK_EPFNOSUPPORT EPFNOSUPPORT + #define SOCK_EPROCLIM EPROCLIM + #define SOCK_EPROTOTYPE EPROTOTYPE + #define SOCK_EPROTONOSUPPORT EPROTONOSUPPORT + #define SOCK_EREMOTE EREMOTE + #define SOCK_ESHUTDOWN ESHUTDOWN + #define SOCK_ESOCKTNOSUPPORT ESOCKTNOSUPPORT + #define SOCK_ESTALE ESTALE + #define SOCK_ETIMEDOUT ETIMEDOUT + #define SOCK_ETOOMANYREFS ETOOMANYREFS + #define SOCK_EWOULDBLOCK EWOULDBLOCK + #define SOCK_EUSERS EUSERS +#endif //unix. + +////////////// + +#ifdef __WIN32__ + typedef char sock_hop; + typedef int socklen_t; + + // provide close to the real BSD error names using windows values. + #define SOCK_EACCES WSAEACCES + #define SOCK_EADDRINUSE WSAEADDRINUSE + #define SOCK_EADDRNOTAVAIL WSAEADDRNOTAVAIL + #define SOCK_EAFNOSUPPORT WSAEAFNOSUPPORT + #define SOCK_EALREADY WSAEALREADY + #define SOCK_EBADF WSAEBADF + #define SOCK_ECONNABORTED WSAECONNABORTED + #define SOCK_ECONNREFUSED WSAECONNREFUSED + #define SOCK_ECONNRESET WSAECONNRESET + #define SOCK_EDESTADDRREQ WSAEDESTADDRREQ + #define SOCK_EDQUOT WSAEDQUOT + #define SOCK_EFAULT WSAEFAULT + #define SOCK_EHOSTDOWN WSAEHOSTDOWN + #define SOCK_EHOSTUNREACH WSAEHOSTUNREACH + #define SOCK_EINPROGRESS WSAEINPROGRESS + #define SOCK_EINTR WSAEINTR + #define SOCK_EINVAL WSAEINVAL + #define SOCK_EISCONN WSAEISCONN + #define SOCK_ELOOP WSAELOOP + #define SOCK_EMFILE WSAEMFILE + #define SOCK_EMSGSIZE WSAEMSGSIZE + #define SOCK_ENAMETOOLONG WSAENAMETOOLONG + #define SOCK_ENETDOWN WSAENETDOWN + #define SOCK_ENETUNREACH WSAENETUNREACH + #define SOCK_ENETRESET WSAENETRESET + #define SOCK_ENOBUFS WSAENOBUFS + #define SOCK_ENOPROTOOPT WSAENOPROTOOPT + #define SOCK_ENOTCONN WSAENOTCONN + #define SOCK_ENOTEMPTY WSAENOTEMPTY + #define SOCK_ENOTSOCK WSAENOTSOCK + #define SOCK_EOPNOTSUPP WSAEOPNOTSUPP + #define SOCK_EPFNOSUPPORT WSAEPFNOSUPPORT + #define SOCK_EPROCLIM WSAEPROCLIM + #define SOCK_EPROTOTYPE WSAEPROTOTYPE + #define SOCK_EPROTONOSUPPORT WSAEPROTONOSUPPORT + #define SOCK_EREMOTE WSAEREMOTE + #define SOCK_ESHUTDOWN WSAESHUTDOWN + #define SOCK_ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT + #define SOCK_ESTALE WSAESTALE + #define SOCK_ETIMEDOUT WSAETIMEDOUT + #define SOCK_ETOOMANYREFS WSAETOOMANYREFS + #define SOCK_EUSERS WSAEUSERS + + // windows specific names. + #define SOCK_EWOULDBLOCK WSAEWOULDBLOCK + #define SOCK_HOST_NOT_FOUND WSAHOST_NOT_FOUND + #define SOCK_NO_DATA WSANO_DATA + #define SOCK_NO_RECOVERY WSANO_RECOVERY + #define SOCK_NOTINITIALISED WSANOTINITIALISED + #define SOCK_SYSNOTREADY WSASYSNOTREADY + #define SOCK_TRY_AGAIN WSATRY_AGAIN + #define SOCK_VERNOTSUPPORTED WSAVERNOTSUPPORTED +#endif //win32. + +////////////// + +} //namespace. + +#endif // outer guard. + diff --git a/octopi/library/sockets/sequence_tracker.cpp b/octopi/library/sockets/sequence_tracker.cpp new file mode 100644 index 00000000..15172195 --- /dev/null +++ b/octopi/library/sockets/sequence_tracker.cpp @@ -0,0 +1,302 @@ +/*****************************************************************************\ +* * +* Name : sequence_tracker * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "machine_uid.h" +#include "sequence_tracker.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace loggers; +using namespace structures; +using namespace textual; +using namespace timely; + +namespace sockets { + +const int MAX_BITS_FOR_SEQ_HASH = 10; + // the number of bits in the hash table of sequences, allowing 2^max buckets. + +const int CLEANING_SPAN = 20000; + // if the sequence number is this far off from the one received, we will + // clean up the span list. + +const int MAX_ITEMS = 200; + // maximum number of items in tracker. this is quite low since we don't + // want to be lugging around thousands of indices. for connection oriented, + // it will never be much of an issue, although for a broadcast style bus it + // could be kind of an issue if we do retransmissions with a lot of lag. + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +//hmmm: we need to address when a host has: +// a) rolled over in sequences (not for 4 years at least?) +// b) changed its address where another host now has old address (which +// is also a weirdo sequence jump, maybe backwards, maybe not). +// would our timing help to guarantee that any oddness introduced is +// swamped out in a few minutes? the worst thing would be a lost packet +// we dumped because we thought we'd seen it but hadn't. +// probably we will see a sequence that's really old seeming; should that +// be enough to trigger flushing the whole host? + +class sequence_record +{ +public: + int _sequence; // the sequence number in question. + time_stamp _when; // when we received this sequence. + + sequence_record(int seq = 0) : _sequence(seq) {} + + astring text_form() const { + return a_sprintf("seq=%d, time=", _sequence) + _when.text_form(); + } +}; + +////////////// + +class host_record +{ +public: + int _received_to; // highest sequence we've got full reception prior to. + machine_uid _host; // the host we're concerned with. + int_hash _sequences; // record of active sequences. + time_stamp _last_active; // the last time we heard from this host. + // we could piece this together from the sequences but we prefer not to. + + host_record(const machine_uid &host) + : _received_to(0), _host(host), _sequences(MAX_BITS_FOR_SEQ_HASH), + _last_active() + {} + + void clean_up(int coalesce_time) { + // check sequences for being right next to the highest received sequence. + // if they're one up, they can be collapsed without waiting for the aging + // process. + int_set ids; + _sequences.ids(ids); + + // we restrict the size of the array with this block. + if (ids.elements() > MAX_ITEMS) { + int zap_point = ids.elements() - MAX_ITEMS; + // we want to remove anything before this index. + for (int s0 = 0; s0 < zap_point; s0++) { + int seq = ids[s0]; + _sequences.zap(seq); + // set our received_to value from the current element. + if (_received_to < seq) + _received_to = seq; + } + // now clean the list of our ids since they're gone. + ids.zap(0, zap_point - 1); + } + + for (int s1 = 0; s1 < ids.elements(); s1++) { + sequence_record *seq; + int id = ids[s1]; + if (!_sequences.find(id, seq)) continue; // bad mistake going on. + if (_received_to + 1 == seq->_sequence) { + // we've hit one that can be collapsed. + _received_to++; + _sequences.zap(id); + ids.zap(s1, s1); + s1--; // skip back before deleted item. + } + } + + // check sequence ages. coalesce any older ones. + for (int s2 = 0; s2 < ids.elements(); s2++) { + sequence_record *seq; + int id = ids[s2]; + if (!_sequences.find(id, seq)) continue; // bad mistake going on. + if (seq->_when < time_stamp(-coalesce_time)) { + // this sequence number has floated too long; crush it now. + if (_received_to < seq->_sequence) + _received_to = seq->_sequence; // update highest received seq. + _sequences.zap(id); + } + } + } + + astring text_form(bool verbose) { + astring to_return; + to_return += astring("host=") + _host.text_form() + + a_sprintf(", rec_to=%d", _received_to) + + ", active=" + _last_active.text_form(); + if (verbose) { + int_set ids; + _sequences.ids(ids); + for (int i = 0; i < ids.elements(); i++) { + sequence_record *found; + if (!_sequences.find(ids[i], found)) + continue; // that's a bad thing. + to_return += astring(parser_bits::platform_eol_to_chars()) + "\t" + + found->text_form(); + } + } else { + to_return += a_sprintf(", sequences held=%d", _sequences.elements()); + } + return to_return; + } + +}; + +////////////// + +//hmmm: should this be a hash table? + +class host_history : public amorph +{ +public: + virtual ~host_history() {} + + DEFINE_CLASS_NAME("host_history"); + + int find_host(const machine_uid &to_find) { + for (int i = 0; i < elements(); i++) { + if (borrow(i)->_host == to_find) return i; + } + return common::NOT_FOUND; + } + + bool whack_host(const machine_uid &to_find) { + int indy = find_host(to_find); + if (negative(indy)) return false; + zap(indy, indy); + return true; + } + + void clean_up(int silence_time, int coalesce_time) { + for (int h = 0; h < elements(); h++) { + host_record &rec = *borrow(h); + // check host liveliness. + if (rec._last_active < time_stamp(-silence_time)) { + // this host got too stale; whack it now. + zap(h, h); + h--; // skip back to prior element. + continue; + } + rec.clean_up(coalesce_time); + } + } + + bool add_sequence(const machine_uid &to_find, int sequence, + int silence_time, int coalesce_time) { + FUNCDEF("add_sequence"); + int indy = find_host(to_find); + if (negative(indy)) { + host_record *rec = new host_record(to_find); + append(rec); + indy = find_host(to_find); + if (negative(indy)) { + LOG(astring("*** failure to add a host to the tracker: ") + + to_find.text_form()); + return false; // serious error. + } + } + host_record &rec = *borrow(indy); + if (borrow(indy)->_received_to + 1 == sequence) { + // this is just one up from our last received guy, so optimize it out. + rec._received_to = sequence; + } else if (sequence - borrow(indy)->_received_to > CLEANING_SPAN) { + // if the number is wildly different, assume we haven't dealt with this + // for too long. + rec._received_to = sequence; +#ifdef DEBUG_SEQUENCE_TRACKER + LOG("sequence is wildly different, cleaning."); +#endif + clean_up(silence_time, coalesce_time); + } else { + // standard treatment, add it to the list. + rec._sequences.add(sequence, new sequence_record(sequence)); + if (rec._sequences.elements() > MAX_ITEMS) { + // too many sequences floating around now. clean them up. + clean_up(silence_time, coalesce_time); + } + } + rec._last_active = time_stamp(); + return true; + } + + astring text_form(bool verbose) { + astring to_return; + for (int i = 0; i < elements(); i++) { + to_return += borrow(i)->text_form(verbose); + if (i < elements() - 1) + to_return += parser_bits::platform_eol_to_chars(); + } + return to_return; + } + +}; + +////////////// + +sequence_tracker::sequence_tracker(int coalesce_time, int silence_time) +: _coalesce_time(coalesce_time), + _silence_time(silence_time), + _hosts(new host_history), + _lock(new mutex) +{ +} + +sequence_tracker::~sequence_tracker() +{ + WHACK(_lock); + WHACK(_hosts); +} + +astring sequence_tracker::text_form(bool verbose) const +{ + auto_synchronizer l(*_lock); + return _hosts->text_form(verbose); +} + +void sequence_tracker::add_pair(const machine_uid &host, int sequence) +{ + auto_synchronizer l(*_lock); + if (!_hosts->add_sequence(host, sequence, _silence_time, _coalesce_time)) { +//complain? + return; + } +} + +bool sequence_tracker::have_seen(const machine_uid &host, int sequence) +{ + auto_synchronizer l(*_lock); + int indy = _hosts->find_host(host); + if (negative(indy)) return false; + host_record &rec = *_hosts->borrow(indy); + if (sequence <= rec._received_to) return true; + sequence_record *found; + return !!rec._sequences.find(sequence, found); +} + +void sequence_tracker::clean_up() +{ + auto_synchronizer l(*_lock); + _hosts->clean_up(_silence_time, _coalesce_time); +} + +} //namespace. + + diff --git a/octopi/library/sockets/sequence_tracker.h b/octopi/library/sockets/sequence_tracker.h new file mode 100644 index 00000000..8e92da38 --- /dev/null +++ b/octopi/library/sockets/sequence_tracker.h @@ -0,0 +1,77 @@ +#ifndef SEQUENCE_TRACKER_CLASS +#define SEQUENCE_TRACKER_CLASS + +/*****************************************************************************\ +* * +* Name : sequence_tracker * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Tracks sequence numbers coming from a collection of hosts. These are * +* presumably attached to network packets. The intention is to record if * +* we've already seen a packet or not. When hosts have not communicated for * +* a while, they are removed from tracking. Also, when enough time has * +* elapsed for a sequence number, we consider that we've heard everything * +* we're going to before that sequence number; hence, any prior sequence * +* numbers are considered already received. * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace sockets { + +class host_history; +class machine_uid; + +//! this will keep track of sequencing for a communication process on a per host basis. + +class sequence_tracker : public virtual basis::root_object +{ +public: + sequence_tracker(int coalesce_time, int silence_time); + // tracks the sequence numbers from a set of hosts. the "coalesce_time" is + // the interval that we wait before considering all prior sequence numbers + // to have been received. the "silence_time" is the time interval a host + // is allowed to be silent before being eliminated. all measurements are + // in milliseconds. + + ~sequence_tracker(); + + DEFINE_CLASS_NAME("sequence_tracker"); + + void add_pair(const machine_uid &host, int sequence); + // adds a hostname/sequence# pair as being received "now". + + bool have_seen(const machine_uid &host, int sequence); + // returns true if the "host" and "sequence" have already been seen in + // a previous transmission. + + void clean_up(); + // this must be invoked periodically to allow the clearing of outdated + // information. once a second seems frequent enough. + + basis::astring text_form(bool verbose = false) const; + // provides a dump of the information held in the tracker. + +private: + int _coalesce_time; // sequences older than this get coalesced. + int _silence_time; // hosts silent for longer than this get canned. + host_history *_hosts; // the overall record of sequence activity per host. + basis::mutex *_lock; // protects from multi-threaded access. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/sockets/socket_data.h b/octopi/library/sockets/socket_data.h new file mode 100644 index 00000000..7becbd13 --- /dev/null +++ b/octopi/library/sockets/socket_data.h @@ -0,0 +1,82 @@ +#ifndef SOCKET_DATA_CLASS +#define SOCKET_DATA_CLASS + +/*****************************************************************************\ +* * +* Name : socket_data * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Tracks the partially transmitted data for transports based on a socket * +* metaphor, where a socket is merely a numerical designation of the channel. * +* Note: this is a heavy-weight header. don't include it in other headers. * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include + +namespace sockets { + +class socket_data +{ +public: + int _socket; // the number of the socket we are managing here. + basis::byte_array _partially_sent; // accumulates bytes from partial sends. + basis::byte_array _partially_received; // accumulates bytes from partial receives. + basis::byte_array _receive_buffer; // temporary that's used for reading. + bool _is_server; // true if this socket is for a server. + int _registered_interests; + // the events being watched for on this socket. the bitwise or'ed items + // in this are from the socket_interests enum. + bool _connection_pending; + // true if a connect or accept is pending. the default is true since we + // do not want to try probing the socket until it has been connected, for + // sockets in connected mode. + int _server_socket; // non-zero if socket was accepted on root server socket. + bool _connected_mode; // true if this is a connected type of socket. + timely::time_stamp _last_conn_alert; + // when the connection was last given a check for a connected state. + + socket_data(int socket = 0, bool server = true, int server_socket = 0, + bool connected_mode = true) + : _socket(socket), _is_server(server), _registered_interests(0), + _connection_pending(true), _server_socket(server_socket), + _connected_mode(connected_mode) {} + ~socket_data() {} + + bool server() const { return _is_server; } + bool client() const { return !_is_server; } + + basis::astring text_form() const; + // returns a descriptive list of the data contained here. +}; + +////////////// + +// implementations. + +basis::astring socket_data::text_form() const +{ + return basis::a_sprintf("socket=%d, type=%s, send_pend=%d, recv_pend=%d, " + "interests=%s, conn_pending=%s", + _socket, _is_server? "server":"client", _partially_sent.length(), + _partially_received.length(), + raw_socket::interest_name(_registered_interests).s(), + _connection_pending? "true":"false"); +} + +} //namespace. + +#endif + diff --git a/octopi/library/sockets/socket_minder.cpp b/octopi/library/sockets/socket_minder.cpp new file mode 100644 index 00000000..84368cdb --- /dev/null +++ b/octopi/library/sockets/socket_minder.cpp @@ -0,0 +1,597 @@ +/*****************************************************************************\ +* * +* Name : socket_minder * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1999-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "raw_socket.h" +#include "socket_data.h" +#include "socket_minder.h" +#include "tcpip_stack.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef __WIN32__ + #include +#endif +#ifdef __UNIX__ + #include + #include +#endif + +using namespace basis; +using namespace loggers; +using namespace processes; +using namespace structures; +using namespace textual; +using namespace timely; + +namespace sockets { + +//#define DEBUG_SOCKET_MINDER + // uncomment for noisiness. + +#undef LOG +#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print) + +const int SOCKET_CHECK_INTERVAL = 50; + // we will scoot around in our sockets this frequently. + +const int SOCKMIND_MAXIMUM_RECEIVES = 10; + // we'll receive this many items from the socket in one go. + +const int MAXIMUM_TRANSFER_CHUNK = 512 * KILOBYTE; + // largest block of data we'll try to deal with at one time. + +const int CONN_ALERT_INTERVAL = 100; + // this is the most frequently that we will generate a connection checking + // event. + +const int MULTI_SELECT_TIMEOUT = 250; + // the snooze on select will occur for this long. during this interval, + // it is likely no new sockets will be considered. + +////////////// + +class socket_data_amorph : public amorph {}; + +////////////// + +class socket_minder_prompter : public ethread +{ +public: + socket_minder_prompter(socket_minder &parent) + : ethread(SOCKET_CHECK_INTERVAL, ethread::SLACK_INTERVAL), + _parent(parent) + { + start(NIL); + } + + ~socket_minder_prompter() { + stop(); // shut down our thread. + } + + virtual void perform_activity(void *formal(ptr)) { _parent.snoozy_select(); } + +private: + socket_minder &_parent; // the object we're hooked to. +}; + +////////////// + +socket_minder::socket_minder(post_office &post, int parent_route, + int event_type, int message) +: _post(post), + _parent_route(parent_route), + _event_type(event_type), + _lock(new mutex), + _socket_list(new socket_data_amorph), + _socks(new raw_socket), + _stack(new tcpip_stack), + _message(message), + _pending_sox(new int_set), + _prompter(new socket_minder_prompter(*this)) +{ + _prompter->start(NIL); +} + +socket_minder::~socket_minder() +{ + _prompter->stop(); + WHACK(_prompter); + WHACK(_socket_list); + WHACK(_lock); + WHACK(_pending_sox); + WHACK(_socks); + WHACK(_stack); +} + +void socket_minder::disengage() +{ + _prompter->stop(); +} + +astring socket_minder::text_form() const +{ + auto_synchronizer l(*_lock); + astring to_return; + + for (int i = 0; i < _socket_list->elements(); i++) { + const socket_data *curr = _socket_list->get(i); + to_return += curr->text_form(); + if (i != _socket_list->elements() - 1) + to_return += parser_bits::platform_eol_to_chars(); + } + + return to_return; +} + +void socket_minder::snoozy_select() +{ +// FUNCDEF("snoozy_select"); + int_array read_sox; + int_array write_sox; + int_array pending; + + get_sockets(read_sox, write_sox, pending); + + // process any with pending connections right now, rather than later. + for (int p = 0; p < pending.length(); p++) { + socket_data *sd = lock_socket_data(pending[p]); + if (!sd) continue; // something hosed there. + handle_pending_connecters(*sd); + unlock_socket_data(sd); + } + + // now select on all of our sockets simultaneously. + int ret = _socks->select(read_sox, write_sox, MULTI_SELECT_TIMEOUT); + if (!ret || (!read_sox.length() && !write_sox.length()) ) { + return; // nothing happened. + } + + // rotate through the lists and push socket_minders around as needed. + // any sockets we have events for but no socket_data are orphans and will + // be ignored. + + // check read sockets. + for (int r = 0; r < read_sox.length(); r++) { + const int sock = read_sox[r]; + if (owns_socket(sock)) { + socket_data *sd = lock_socket_data(sock); + if (!sd) continue; // something hosed there. + push_receives(*sd, SI_READABLE); + unlock_socket_data(sd); + read_sox.zap(r, r); + r--; // skip back before deleted guy. + } + } + + // check write sockets. + for (int w = 0; w < write_sox.length(); w++) { + const int sock = write_sox[w]; + if (owns_socket(sock)) { + socket_data *sd = lock_socket_data(sock); + if (!sd) continue; // something hosed there. + push_sends(*sd, SI_WRITABLE); + unlock_socket_data(sd); + write_sox.zap(w, w); + w--; // skip back before deleted guy. + } + } +} + +void socket_minder::get_sockets(int_array &read_sox, int_array &write_sox, + int_array &pendings) const +{ + auto_synchronizer l(*_lock); + for (int i = 0; i < _socket_list->elements(); i++) { + socket_data *sd = _socket_list->borrow(i); + if (sd->_connection_pending) { + // this is not ready for sends and receives yet. + pendings += sd->_socket; + } else { + // always add sockets to test if they have data waiting. + read_sox += sd->_socket; + // only check on writability if there is data pending for sending. + if (sd->_partially_sent.length()) + write_sox += sd->_socket; + } + } +} + +bool socket_minder::owns_socket(int socket) const +{ + auto_synchronizer l(*_lock); + for (int i = 0; i < _socket_list->elements(); i++) { + if (_socket_list->borrow(i)->_socket == socket) return true; + } + return false; +} + +socket_data *socket_minder::lock_socket_data(int socket) +{ + _lock->lock(); + for (int i = 0; i < _socket_list->elements(); i++) + if (_socket_list->borrow(i)->_socket == socket) + return _socket_list->borrow(i); + // this is a failure to get here; there was no suitable socket. + _lock->unlock(); + return NIL; +} + +void socket_minder::unlock_socket_data(socket_data *to_unlock) +{ + if (!to_unlock) return; +//can't affect it now. to_unlock = NIL; + _lock->unlock(); +} + +bool socket_minder::add_socket_data(int socket, bool server, int server_socket, + bool connected_mode, bool connection_pending) +{ + auto_synchronizer l(*_lock); + socket_data *harpo = lock_socket_data(socket); + if (harpo) { + unlock_socket_data(harpo); + return false; + } + socket_data *new_data = new socket_data(socket, server, server_socket, + connected_mode); + _socks->set_non_blocking(socket); + // ensure the new guy is treated as non-blocking. unix does not seem + // to inherit this from the parent. + new_data->_connection_pending = connection_pending; + _socket_list->append(new_data); + return true; +} + +bool socket_minder::remove_socket_data(int socket) +{ +// FUNCDEF("remove_socket_data"); + auto_synchronizer l(*_lock); + for (int i = 0; i < _socket_list->elements(); i++) { + if (_socket_list->borrow(i)->_socket == socket) { + // give the socket a last chance to get its data out. + evaluate_interest(*_socket_list->borrow(i)); + _socket_list->zap(i, i); + return true; + } + } +// LOG(a_sprintf("couldn't find socket %d.", socket)); + return false; +} + +bool socket_minder::register_interest(int socket, int interests) +{ +#ifdef DEBUG_SOCKET_MINDER + FUNCDEF("register_interest"); +#endif + socket_data *harpo = lock_socket_data(socket); +#ifdef DEBUG_SOCKET_MINDER + LOG(astring(astring::SPRINTF, "registering interest of %d for socket " + "%d.", interests, socket)); +#endif + if (!harpo) return false; + harpo->_registered_interests = interests; + unlock_socket_data(harpo); + return true; +} + +bool socket_minder::is_connection_pending(int socket) +{ + socket_data *harpo = lock_socket_data(socket); + if (!harpo) return false; + bool to_return = harpo->_connection_pending; + unlock_socket_data(harpo); + return to_return; +} + +bool socket_minder::set_connection_pending(int socket, bool pending) +{ + socket_data *harpo = lock_socket_data(socket); + if (!harpo) return false; + harpo->_connection_pending = pending; + unlock_socket_data(harpo); + return true; +} + +void socket_minder::fire_event(int to_fire, int at_whom, + basis::un_int parm1, basis::un_int parm2) +{ + _post.drop_off(at_whom, new OS_event(_event_type, to_fire, parm1, parm2)); +} + +void socket_minder::put_pending_server(int to_put, bool at_head) +{ + if (!to_put) return; // bogus. + auto_synchronizer l(*_lock); + if (at_head) { + _pending_sox->insert(0, 1); + (*_pending_sox)[0] = to_put; + } else { + *_pending_sox += to_put; + } +} + +bool socket_minder::zap_pending_server(int socket) +{ + auto_synchronizer l(*_lock); + if (!_pending_sox->member(socket)) return false; + _pending_sox->remove(socket); + return true; +} + +int socket_minder::get_pending_server() +{ + auto_synchronizer l(*_lock); + if (!_pending_sox->length()) return 0; + int to_return = _pending_sox->get(0); + _pending_sox->zap(0, 0); + return to_return; +} + +bool socket_minder::handle_pending_connecters(socket_data &to_peek) +{ + FUNCDEF("handle_pending_connecters"); + if (!to_peek._connection_pending) return false; // not needed here. + + if (to_peek._last_conn_alert > time_stamp(-CONN_ALERT_INTERVAL)) { + // not time yet. + return false; + } + to_peek._last_conn_alert.reset(); + + // force the issue; there is no simple way to portably know whether + // the socket got a connection or not, so just say it did. + if (!to_peek._is_server + && (to_peek._registered_interests & SI_CONNECTED) ) { + // deal with a client first. this just means, zing an event. +#ifdef DEBUG_SOCKET_MINDER + LOG(a_sprintf("sending client SI_CONNECTED event on parent %d", + _parent_route)); +#endif + fire_event(_message, _parent_route, to_peek._socket, SI_CONNECTED); + } else if (to_peek._is_server + && (to_peek._registered_interests & SI_CONNECTED) ) { + // special handling for servers. we accept the waiting guy if he's + // there. otherwise we don't send the event. + sockaddr sock_addr; + socklen_t sock_len = sizeof(sock_addr); + int new_sock = int(::accept(to_peek._socket, &sock_addr, &sock_len)); + // check for a new socket. + if (new_sock != INVALID_SOCKET) { + LOG(a_sprintf("accept got a client socket %d.", new_sock)); + if (_pending_sox->member(new_sock)) { + LOG(a_sprintf("already have seen socket %d in pending!", new_sock)); + } else { + *_pending_sox += new_sock; +#ifdef DEBUG_SOCKET_MINDER + LOG(a_sprintf("sending server SI_CONNECTED event on parent %d", + _parent_route)); +#endif + fire_event(_message, _parent_route, to_peek._socket, SI_CONNECTED); + } + } else if (_pending_sox->length()) { + // there are still pending connectees. + fire_event(_message, _parent_route, to_peek._socket, SI_CONNECTED); + } + } + // also, if the connection is still pending, we don't want to select on + // it yet. + return true; +} + +bool socket_minder::evaluate_interest(socket_data &to_peek) +{ + FUNCDEF("evaluate_interest"); + if (to_peek._connection_pending) { + return handle_pending_connecters(to_peek); + } + + int sel_mode = 0; + + int states = _socks->select(to_peek._socket, sel_mode); + + if (!states) { + return true; // nothing to report. + } + + if (! (states & SI_ERRONEOUS) && ! (states & SI_DISCONNECTED) ) { + push_sends(to_peek, states); + push_receives(to_peek, states); + } + + if ( (to_peek._registered_interests & SI_ERRONEOUS) + && (states & SI_ERRONEOUS) ) { + // there's some kind of bad problem on the socket. + LOG(a_sprintf("socket %d has status of erroneous.", to_peek._socket)); +//hmmm: what to do? generate an event? + } + + if ( (to_peek._registered_interests & SI_DISCONNECTED) + && (states & SI_DISCONNECTED) ) { + // we lost our state of connectedness. + fire_event(_message, _parent_route, to_peek._socket, + SI_DISCONNECTED); + return true; // get out now. + } + + return true; +} + +void socket_minder::push_sends(socket_data &to_poke, int states) +{ + FUNCDEF("push_sends"); + if (to_poke._connection_pending) { + LOG("not supposed to try this when not connected yet..."); + } + +#ifdef DEBUG_SOCKET_MINDER + if (to_poke._partially_sent.length()) { + LOG(a_sprintf("socket %d: %d bytes to send.", to_poke._socket, + to_poke._partially_sent.length())); + } +#endif + + int error_code = 0; + + if ( (states & SI_WRITABLE) && to_poke._partially_sent.length()) { + // write to the socket since we see an opportunity. + byte_array &to_send = to_poke._partially_sent; + int len_sent = send(to_poke._socket, (char *)to_send.observe(), + to_send.length(), 0); + if (!len_sent) { + // nothing got sent. + LOG(a_sprintf("didn't send any data on socket %d.", to_poke._socket)); + } else if (len_sent == SOCKET_ERROR) { + // something bad happened. + error_code = critical_events::system_error(); + } else { +#ifdef DEBUG_SOCKET_MINDER + LOG(a_sprintf("updating that %d bytes got sent out of %d to send.", + len_sent, to_send.length())); + if (to_send.length() != len_sent) + LOG(a_sprintf("size to send (%d) not same as size sent (%d).", + to_send.length(), len_sent)); +#endif + // update the partially sent chunk for the bit we sent out. + to_send.zap(0, len_sent - 1); + } + } + + // handle errors we have seen. + if (error_code) { + if (error_code != SOCK_EWOULDBLOCK) + LOG(astring(astring::SPRINTF, "error on socket %d is %s.", + to_poke._socket, _stack->tcpip_error_name(error_code).s())); + + switch (error_code) { + case SOCK_ENOTSOCK: // fall-through. + case SOCK_ECONNABORTED: // fall-through. + case SOCK_ECONNRESET: { + // the connection got hammerlocked somehow. + LOG(a_sprintf("due to %s condition, closing socket %d.", + _stack->tcpip_error_name(error_code).s(), to_poke._socket)); + fire_event(_message, _parent_route, to_poke._socket, + SI_DISCONNECTED); + to_poke._partially_sent.reset(); // clear with no connection. + break; + } + } + } +} + +void socket_minder::push_receives(socket_data &to_poke, int states) +{ + FUNCDEF("push_receives"); + if (to_poke._connection_pending) { + LOG("not supposed to try this when not connected yet..."); + } + +#ifdef DEBUG_SOCKET_MINDER + if (to_poke._partially_received.length()) + LOG(a_sprintf("socket %d: %d bytes already received.", to_poke._socket, + to_poke._partially_received.length())); +#endif + + int error_code = 0; + + if ( (states & SI_READABLE) && to_poke._connected_mode) { + // grab any data that's waiting on the connection-oriented socket. + + bool got_something = true; // hopeful for now. + to_poke._receive_buffer.reset(MAXIMUM_TRANSFER_CHUNK); + // allocate space where we'll get new data. + int count = 0; + while (got_something && (count++ < SOCKMIND_MAXIMUM_RECEIVES)) { + got_something = false; // now get all pessimistic. + int len = recv(to_poke._socket, (char *)to_poke._receive_buffer.observe(), + to_poke._receive_buffer.length(), 0); + if (len > 0) { +#ifdef DEBUG_SOCKET_MINDER + LOG(a_sprintf("received %d bytes on socket %d.", len, to_poke._socket)); +#endif + // we received some actual data; we're happy again. + got_something = true; + // zap any extra off. + if (len < MAXIMUM_TRANSFER_CHUNK) + to_poke._receive_buffer.zap(len, to_poke._receive_buffer.last()); + // add in what we were given. + to_poke._partially_received += to_poke._receive_buffer; + to_poke._receive_buffer.reset(MAXIMUM_TRANSFER_CHUNK); + // reset to the right size for trying some more. + } else { + error_code = critical_events::system_error(); + + // reset the states flag based on current state. + states = _socks->select(to_poke._socket, + raw_socket::SELECTING_JUST_READ); + if (states & SI_DISCONNECTED) { + error_code = SOCK_ECONNRESET; // make like regular disconnect. + LOG(a_sprintf("noticed disconnection on socket %d.", + to_poke._socket)); + // close the socket; we use a temporary because the close method + // wants to actually store zero in the socket. + basis::un_int deader = to_poke._socket; + _socks->close(deader); + } + + } + } + + if ( !(states & SI_DISCONNECTED) + && to_poke._partially_received.length()) { +#ifdef DEBUG_SOCKET_MINDER + LOG(a_sprintf("firing readable now: sock=%d", to_poke._socket)); +#endif + fire_event(_message, _parent_route, to_poke._socket, SI_READABLE); + } + } else if ( (states & SI_READABLE) && !to_poke._connected_mode) { + // datagram style; we need to still alert the parent. + fire_event(_message, _parent_route, to_poke._socket, + SI_READABLE); + } + + // handle errors we have seen. + if (error_code) { + if (error_code != SOCK_EWOULDBLOCK) + LOG(astring(astring::SPRINTF, "error on socket %d is %s.", + to_poke._socket, _stack->tcpip_error_name(error_code).s())); + + switch (error_code) { + case SOCK_ENOTSOCK: // fall-through. + case SOCK_ECONNABORTED: // fall-through. + case SOCK_ECONNRESET: { + // the connection got hammerlocked somehow. + LOG(a_sprintf("due to %s condition, closing socket %d.", + _stack->tcpip_error_name(error_code).s(), to_poke._socket)); + fire_event(_message, _parent_route, to_poke._socket, + SI_DISCONNECTED); + basis::un_int deader = to_poke._socket; + _socks->close(deader); + to_poke._partially_sent.reset(); // clear with no connection. + break; + } + } + } +} + +} //namespace. + diff --git a/octopi/library/sockets/socket_minder.h b/octopi/library/sockets/socket_minder.h new file mode 100644 index 00000000..da6cd18e --- /dev/null +++ b/octopi/library/sockets/socket_minder.h @@ -0,0 +1,166 @@ +#ifndef SOCKET_MINDER_CLASS +#define SOCKET_MINDER_CLASS + +/*****************************************************************************\ +* * +* Name : socket_minder * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Provides a base for activities that tend a communication element called * +* a socket. Sockets are numerically identified, but the number is only * +* unique on the specific medium of interest. The socket minder also tracks * +* the send and receive status and buffers for the socket. * +* * +******************************************************************************* +* Copyright (c) 1999-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace sockets { + +// forward declarations. +class raw_socket; +class socket_data; +class socket_data_amorph; +class socket_minder_prompter; +class tcpip_stack; + +////////////// + +class socket_minder : public virtual basis::root_object +{ +public: + socket_minder(processes::post_office &post, int parent_route, int event_type, + int message); + // the "parent_route" is where we will send asynchronous tcpip events, + // which will be stored in the "post". the "event_type" is the identifier + // for the OS_events that will be generated and the "message" is the id + // stored inside the events. + + virtual ~socket_minder(); + + void disengage(); + // this method should be invoked before the socket_minder is destroyed. + // it ensures that the object is released from background event delivery. + + DEFINE_CLASS_NAME("socket_minder"); + + basis::astring text_form() const; + // returns a dump of the minder's current state. + + socket_data *lock_socket_data(int socket); + // locates the data for the "socket" specified. the list is left locked + // unless NIL is returned. + + void unlock_socket_data(socket_data *to_unlock); + // unlocks the list of socket data again and zeroes "to_unlock". + + bool add_socket_data(int socket, bool server, int server_socket, + bool connected_mode, bool connection_pending); + // adds a new record for the "socket" which is possibly a "server". only + // fails with false when the socket is already listed. the + // "connected_mode" should be true if this socket is connection-oriented. + + bool remove_socket_data(int socket); + // rips out the data held for "socket". only fails with false when the + // record couldn't be found to delete. + + bool set_connection_pending(int socket, bool pending); + // changes the state of pending connection for the "socket". if "pending" + // is true, then the socket is either trying to accept connections or it + // is trying to connect. if false, then it is no longer attempting this. + + bool is_connection_pending(int socket); + // returns the current state of pending-ness for the "socket". false could + // also mean the socket cannot be found. + + bool register_interest(int socket, int interests); + // sets the events to watch for on the "socket" from the "interests". + // these are values merged bitwise from the socket_interests enum. they + // must be interpreted appropriately by different kinds of transports. the + // evaluate_interest() method must be over-ridden for the interests to + // actually be implemented. registering with "interests" as zero will + // reset all interests to be... disinterested. + + virtual bool evaluate_interest(socket_data &to_examine); + // this can be over-ridden by a derived socket minder to handle needs + // besides simple bsd socket management. but the implementation provided + // here will generate events upon occurrence of a registered interest on + // the socket. an event letter is sent to the appropriate parent id + // containing the event that was noticed. true is returned if "to_examine" + // was successfully evaluated. any changes made to the socket's data are + // recorded. since the socket minder is locked during this upcall, it is + // important not to cause any deadlocks by careless additional locking. + + int get_pending_server(); + // returns a non-zero socket number if a server socket was accepted on + // and is waiting to be processed. + + bool zap_pending_server(int socket); + // removes the "socket" from the pending servers list, if present. + + void put_pending_server(int to_put, bool at_head); + // stores a pending server socket into the list, either since it just got + // noticed as an accepted socket or because it cannot be processed yet. + + void get_sockets(basis::int_array &read_sox, basis::int_array &write_sox, + basis::int_array &pending) const; + // reports the set of sockets that this minder is handling in "read_sox", + // since we assume any socket could be checked for pending received data. + // if there is pending data to send, then they are put into the "write_sox" + // to be checked for writability. the existing contents of the lists are + // not cleared, so this function can be used to join the lists of several + // socket_minders together. + + bool owns_socket(int socket) const; + // returns true if this minder owns the "socket" in question. + + void push_sends(socket_data &to_poke, int states); + void push_receives(socket_data &to_poke, int states); + // if the state is right, we'll try our hand at grabbing some data. if the + // "states" include SI_READABLE, then we'll try and receive on the socket + // in push_receives. if they include SI_WRITABLE, then we'll send out + // pending data in push_sends. + + bool handle_pending_connecters(socket_data &to_peek); + // returns true if the socket "to_peek" is awaiting a connection and we + // have prompted that activity appropriately. false means that this + // socket is not awaiting a connection. + + void snoozy_select(); + // goes into a select on all applicable sockets and waits until one + // of them has activity before waking up. + +private: + processes::post_office &_post; // manages recycling of letters for us. + int _parent_route; // the identifier of our parent in the postal system. + int _event_type; // what we generate OS_events as. + basis::mutex *_lock; // maintains list integrity. + socket_data_amorph *_socket_list; // the table of information per socket. + raw_socket *_socks; // lets us access the OS's socket subsystem. + tcpip_stack *_stack; // provides tcpip protocol stack. + int _message; // the message type stored inside the generated OS_events. + structures::int_set *_pending_sox; // accepted servers that are waiting to be processed. + socket_minder_prompter *_prompter; // snoozes on sockets awaiting activity. + + void fire_event(int to_fire, int at_whom, basis::un_int parm1, basis::un_int parm2); + + // not implemented: + socket_minder(const socket_minder &sm); + socket_minder &operator =(const socket_minder &sm); +}; + +} //namespace. + +#endif + diff --git a/octopi/library/sockets/span_manager.cpp b/octopi/library/sockets/span_manager.cpp new file mode 100644 index 00000000..92d91614 --- /dev/null +++ b/octopi/library/sockets/span_manager.cpp @@ -0,0 +1,216 @@ +/*****************************************************************************\ +* * +* Name : span_manager * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1990-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "span_manager.h" + +#include +#include +#include +#include +#include + +using namespace basis; +using namespace structures; +//using namespace basis; + +namespace sockets { + +span_manager::span_manager(int packs) +: _implementation(new bit_vector(packs)) +{} + +span_manager::span_manager(const span_manager &to_copy) +: _implementation(new bit_vector(*to_copy._implementation)) +{} + +span_manager::~span_manager() { WHACK(_implementation); } + +span_manager &span_manager::operator =(const span_manager &to_copy) +{ + if (this == &to_copy) return *this; + *_implementation = *to_copy._implementation; + return *this; +} + +int span_manager::missing_sequence() const +{ return _implementation->find_first(0); } + +void span_manager::reset(int packs) { _implementation->resize(packs); } + +const bit_vector &span_manager::vector() const { return *_implementation; } + +bit_vector &span_manager::vector() { return *_implementation; } + +int span_manager::received_sequence() const +{ + int recd_to = _implementation->find_first(0); + if (negative(recd_to)) return _implementation->bits() - 1; + return recd_to - 1; +} + +void span_manager::make_received_list(int_array &to_make, int max_spans) const +{ + to_make.reset(0); + int zeros_start_at = _implementation->find_first(0); + if (negative(zeros_start_at)) { + // all bits are set in the vector, so the whole message is received. + to_make.concatenate(0); + to_make.concatenate(short(_implementation->bits() - 1)); + return; + } + zeros_start_at--; + // the sequence of ones ends right before the first zero + if (zeros_start_at >= 0) { + to_make.concatenate(0); + to_make.concatenate(short(zeros_start_at)); + } + + int ones_to_here; // keeps track of the position of the ones. + + for (int outer_loop = zeros_start_at + 2; + outer_loop < _implementation->bits(); ) { + // the first zero is zeros_start_at + 1, so we start at the first + // unknown bit. + if (_implementation->on(outer_loop)) { + // the bit is a one, so we are in a one-gathering mode. + ones_to_here = outer_loop; + int inner_loop = outer_loop + 1; + while (inner_loop < _implementation->bits()) { + if (_implementation->on(inner_loop)) ones_to_here=inner_loop; + else break; // a zero is found at this position, so leave loop. + inner_loop++; + } + // the stretch of ones is entered in the array. + to_make.concatenate(short(outer_loop)); + to_make.concatenate(short(ones_to_here)); + if ( (max_spans >= 0) && (to_make.length() >= 2 * max_spans) ) + return; + outer_loop = ones_to_here + 1; + } else { + // the bit is a zero, so we are gathering zeros. + int inner_loop = outer_loop + 1; + ones_to_here = _implementation->bits(); + while (inner_loop < _implementation->bits()) { + if (!_implementation->on(inner_loop)) inner_loop++; + else { + // ones_to_here is set to the first position of a one, actually. + ones_to_here = inner_loop; + break; + } + } + // the loop variable is set to the first unknown position again. + outer_loop = ones_to_here; + } + } +} + +bool span_manager::update(const int_array &new_spans) +{ + for (int i = 0; i < new_spans.length(); i += 2) { + if ( (new_spans.get(i) >= _implementation->bits()) + || (new_spans.get(i+1) >= _implementation->bits()) ) + return false; + for (int j = new_spans.get(i); j <= new_spans.get(i+1); j++) + _implementation->light(j); + } + return true; +} + +void span_manager::make_missing_list(int_array &to_make, int max_spans) const +{ + to_make.reset(0); + int ones_start_at = _implementation->find_first(1); + if (negative(ones_start_at)) { + // all bits are zero in the vector; no packets have been received. + to_make.concatenate(0); + to_make.concatenate(short(_implementation->bits() - 1)); + return; + } + ones_start_at--; + // the sequence of zeros ends right before the first one + if (ones_start_at >= 0) { + to_make.concatenate(0); + to_make.concatenate(short(ones_start_at)); + } + + int zeros_to_here; + for (int outer_loop = ones_start_at + 2; + outer_loop < _implementation->bits(); ) { + int inner_loop; + // the first one is ones_start_at+1, so we start at the first unknown bit + if (!_implementation->on(outer_loop)) { + // the bit is a zero, so we are in a zero-gathering mode. + zeros_to_here = outer_loop; + int inner_loop = outer_loop + 1; + while (inner_loop < _implementation->bits()) { + if (!_implementation->on(inner_loop)) zeros_to_here=inner_loop; + else break; + // a one is found at this position, so leave loop. + inner_loop++; + } + // the stretch of zeros is entered in the array. + to_make.concatenate(short(outer_loop)); + to_make.concatenate(short(zeros_to_here)); + if ( (max_spans >= 0) && (to_make.length() >= 2 * max_spans) ) return; + outer_loop = zeros_to_here + 1; + } else { + // the bit is a one, so we are gathering ones. + inner_loop = outer_loop + 1; + zeros_to_here = _implementation->bits(); + while (inner_loop < _implementation->bits()) { + if (_implementation->on(inner_loop)) + inner_loop++; + else { + // zeros_to_here is set to the first position of a zero, actually. + zeros_to_here = inner_loop; + break; + } + } + // the loop variable is set to the first unknown position again. + outer_loop = zeros_to_here; + } + } +} + +astring span_manager::funky_print(const int_array &to_spew, int rec_seq) const +{ + astring to_return(astring::SPRINTF, "through %d, [", rec_seq); + for (int i = 0; i < to_spew.length(); i += 2) { + to_return += astring(astring::SPRINTF, " %d-%d", to_spew.get(i), + to_spew.get(i+1)); + } + to_return += astring(" ] "); + return to_return; +} + +astring span_manager::print_received_list() const +{ + int_array hold_info; + make_received_list(hold_info); + astring to_return("received "); + to_return += funky_print(hold_info, received_sequence()); + return to_return; +} + +astring span_manager::print_missing_list() const +{ + int_array hold_info; + make_missing_list(hold_info); + astring to_return("missing "); + to_return += funky_print(hold_info, received_sequence()); + return to_return; +} + +} //namespace. + diff --git a/octopi/library/sockets/span_manager.h b/octopi/library/sockets/span_manager.h new file mode 100644 index 00000000..1aa2d7a2 --- /dev/null +++ b/octopi/library/sockets/span_manager.h @@ -0,0 +1,92 @@ +#ifndef SPAN_MANAGER_CLASS +#define SPAN_MANAGER_CLASS + +/*****************************************************************************\ +* * +* Name : span_manager * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1990-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace sockets { + +//! Manages lists of numbers representing the completion of some activity. +/*! + A number is either present or absent from the list. The numbers are + organized according to spans, where a span is simply a range of numbers, + such as 33-238. the span manager allows numbers to be added or removed + from the list. it also can create the full list of either the present or + absent numbers. +*/ + +class span_manager +{ +public: + span_manager(int number_of_items); + // keeps track of "number_of_items" worth of whatever the unit is. + + span_manager(const span_manager &to_copy); + + ~span_manager(); + + span_manager &operator =(const span_manager &to_copy); + + void reset(int number_of_items); + //!< sets up the span manager with a new configuration. + + bool update(const basis::int_array &new_spans); + //!< updates the span information. + /*!< the spans listed in the hold are added to the span manager. if the + spans are successfully added, then true is returned. */ + + int received_sequence() const; + //!< returns the highest chunk number at which all chunks are ready. + /*!< or a negative number is returned if chunk 0 has still not been + received yet. */ + + int missing_sequence() const; + //!< returns the number of the chunk where the first item is missing. + /*!< if none of them are missing, then a negative number is returned. */ + + void make_received_list(basis::int_array &spans, int max_spans = -1) const; + //!< Creates a list for the received spans that are ready. + /*!< The numbers in the list refer to ranges of spans fully received. For + example, if 0, 3, 4, 5, 8, 9, 12, 13, 14, and 28 have been received, the + span list is [0-0], [3-5], [8-9], [12-14], and [28-28]. The "max_spans" + is the longest the list is allowed to be, unless it is negative, which + means include all of them. */ + + void make_missing_list(basis::int_array &spans, int max_spans = -1) const; + //!< creates a list representing the spans that are not ready yet. + + const structures::bit_vector &vector() const; + //!< observes the held bit_vector that represents the spans. + structures::bit_vector &vector(); + //!< provides access to the held bit_vector that represents the spans. + + basis::astring print_received_list() const; + //!< prints out the span list for received blocks into a string. + + basis::astring print_missing_list() const; + //!< prints out the span list for missing blocks into a string. + +private: + structures::bit_vector *_implementation; //!< our underlying structure holding spans. + + basis::astring funky_print(const basis::int_array &to_spew, int rec_seq) const; + //!< prints the span holder to a string and returns it. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/sockets/spocket.cpp b/octopi/library/sockets/spocket.cpp new file mode 100644 index 00000000..8491a3ec --- /dev/null +++ b/octopi/library/sockets/spocket.cpp @@ -0,0 +1,756 @@ +/*****************************************************************************\ +* * +* Name : spocket * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "internet_address.h" +#include "raw_socket.h" +#include "spocket.h" +#include "tcpip_stack.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __UNIX__ + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +using namespace basis; +using namespace loggers; +using namespace structures; +using namespace timely; + +namespace sockets { + +//#define DEBUG_SPOCKET + // uncomment for noisy version. + +#undef LOG +#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print) + +const int PENDING_CONNECTIONS_ALLOWED = 14; + // we allow this many connections to queue up before they get rejected. + // if the OS is windoze, this number is ignored if it's greater than the + // hardcoded maximum of like 5. + +const int RESOLVE_INTERVAL = 300; + // we'll re-resolve the ip address at this rate. this mainly comes into + // play for the connect call, since the address passed in could have changed + // or been invalid to start with. we're not losing much by trying to + // resolve the address again during connection time. + +#define RECOGNIZE_DISCO \ + _client_bind = false; \ + _was_connected = false + +// ensure that the socket is in a good state. +#define ENSURE_HEALTH(retval) \ + if (!_was_connected) return retval; /* never has been. */ \ + if (!_socket) { RECOGNIZE_DISCO; return retval; /* not set. */ } + +#define CHECK_BOGUS(retval) \ + if (is_bogus()) { return retval; /* this spocket is junk. */ } + +#undef GRAB_LOCK +#ifdef __WIN32__ + // win32 seems to trip over selects unless we protect them. + #define GRAB_LOCK auto_synchronizer l(*_select_lock) + // and in truth, the locking turns out to be needed on win32 if we're + // going to allow sharing a spocket across threads. this is one of the + // design goals so we're honor bound to support that. +#else + #define GRAB_LOCK +#endif + +#ifdef __UNIX__ + SAFE_STATIC(mutex, __broken_pipe_synch, ) +#endif + +spocket::spocket(const internet_address &where, sock_types type) +: _type(type), + _socket(0), + _server_socket(0), + _was_connected(false), + _client(false), + _where(new internet_address(where)), + _remote(new internet_address), + _socks(new raw_socket), + _stack(new tcpip_stack), + _select_lock(new mutex), + _last_resolve(new time_stamp), // don't force an immediate resolve. + _client_bind(false), + _cli_bind(new internet_address) +{ + FUNCDEF("constructor"); + if ( (_type == BROADCAST) || (_type == UNICAST) ) { + // casting types are never servers. + _client = true; + } else if ( (type == CONNECTED) || (type == BOGUS_SOCK) ) { + // nothing special here currently. + } else { + // this is an unknown type. + LOG(a_sprintf("unknown socket type %d; failing out.", _type)); +//hmmm: without a validity flag of some sort, this doesn't mean much. + return; + } +} + +spocket::~spocket() +{ +#ifdef DEBUG_SPOCKET + FUNCDEF("destructor"); + LOG(a_sprintf("closing spocket: ") + text_form()); +#endif + disconnect(); + WHACK(_where); + WHACK(_socks); + WHACK(_stack); + WHACK(_remote); + WHACK(_select_lock); + WHACK(_last_resolve); + WHACK(_cli_bind); + _client_bind = false; +} + +// where and remote don't need to be protected unless we revise the design of +// the class and allow a reset or re-open kind of method. +const internet_address &spocket::where() const { return *_where; } +const internet_address &spocket::remote() const { return *_remote; } + +tcpip_stack &spocket::stack() const { return *_stack; } + +// doesn't need to be protected since the sockets are being treated as simple +// ints and since _where currently does not get destroyed. +astring spocket::text_form() +{ + a_sprintf sock_string("socket=%d", _socket); + if (is_root_server()) + sock_string += a_sprintf("root-socket=%d", _server_socket); + + return a_sprintf("%s spocket: %s, %s, %s", + (is_client()? "client" : + (is_root_server()? "root-server" : "server") ), + (connected()? "connected" : + (was_connected()? "unconnected (was once)" : "never-connected") ), + sock_string.s(), + _where->text_form().s()); +} + +void spocket::bind_client(const internet_address &addr) +{ + _client_bind = true; + *_cli_bind = addr; +} + +const char *spocket::outcome_name(const outcome &to_name) +{ + switch (to_name.value()) { + case NOT_SERVER: return "NOT_SERVER"; + default: return communication_commons::outcome_name(to_name); + } +} + +outcome spocket::disconnect() +{ +#ifdef DEBUG_SPOCKET + FUNCDEF("disconnect"); +#endif + RECOGNIZE_DISCO; + if (_socket) { +#ifdef DEBUG_SPOCKET + LOG(a_sprintf("closing socket %d", _socket)); +#endif + _socks->close(_socket); + _socket = 0; + } + if (_server_socket) { +#ifdef DEBUG_SPOCKET + LOG(a_sprintf("closing server socket %d", _server_socket)); +#endif + _socks->close(_server_socket); + _server_socket = 0; + } + return OKAY; +} + +bool spocket::connected() +{ +#ifdef DEBUG_SPOCKET + FUNCDEF("connected"); +#endif + ENSURE_HEALTH(false); + + if (_type != CONNECTED) return _was_connected; + + // do examination on spocket. + int sel_mode = 0; + GRAB_LOCK; + int ret = _socks->select(_socket, sel_mode); + + if (ret == 0) { + return true; // we are happy. + } + if ( (ret & SI_DISCONNECTED) || (ret & SI_ERRONEOUS) ) { + RECOGNIZE_DISCO; + return false; + } + return true; +} + +outcome spocket::await_readable(int timeout) +{ +#ifdef DEBUG_SPOCKET + FUNCDEF("await_readable"); +#endif + CHECK_BOGUS(NO_CONNECTION); + ENSURE_HEALTH(NO_CONNECTION); + GRAB_LOCK; + int mode = raw_socket::SELECTING_JUST_READ; + int ret = _socks->select(_socket, mode, timeout); + if (ret & SI_READABLE) return OKAY; + // we found something to report. + if (ret & SI_DISCONNECTED) { + RECOGNIZE_DISCO; + return NO_CONNECTION; + } + return _socket? NONE_READY : NO_CONNECTION; + // nothing is ready currently. +} + +outcome spocket::await_writable(int timeout) +{ +#ifdef DEBUG_SPOCKET + FUNCDEF("await_writable"); +#endif + CHECK_BOGUS(NO_CONNECTION); + ENSURE_HEALTH(NO_CONNECTION); + GRAB_LOCK; + int mode = raw_socket::SELECTING_JUST_WRITE; + int ret = _socks->select(_socket, mode, timeout); + if (ret & SI_WRITABLE) return OKAY; + // we found something to report. + if (ret & SI_DISCONNECTED) { + RECOGNIZE_DISCO; + return NO_CONNECTION; + } + return _socket? NONE_READY : NO_CONNECTION; + // nothing is ready currently. +} + +outcome spocket::connect(int communication_wait) +{ + FUNCDEF("connect"); + CHECK_BOGUS(NO_CONNECTION); + { + GRAB_LOCK; // short lock. + if ( (_was_connected && !_client) || _server_socket) { +#ifdef DEBUG_SPOCKET + LOG("this object was already opened as a server!"); +#endif + return BAD_INPUT; + } + _client = true; // set our state now that we're sure this is okay. + _was_connected = false; // reset this, since we're connecting now. + } + + if (!_socket) { + // the socket was never created (or was cleaned up previously). this is + // where we create the socket so we can communicate. +#ifdef DEBUG_SPOCKET + LOG(astring("creating socket now for ") + _where->text_form()); +#endif + GRAB_LOCK; + int sock_type = SOCK_STREAM; + int proto = IPPROTO_TCP; + + if ( (_type == BROADCAST) || (_type == UNICAST) ) { + sock_type = SOCK_DGRAM; + proto = IPPROTO_IP; + } + _socket = int(::socket(AF_INET, sock_type, proto)); + if ( (_socket == basis::un_int(INVALID_SOCKET)) || !_socket) { + _socket = NIL; + LOG("Failed to open the client's connecting spocket."); + return ACCESS_DENIED; + } + + // mark the spocket for _blocking_ I/O. we want connect to sit there + // until it's connected or returns with an error. + _socks->set_non_blocking(_socket, false); + + if (_type == BROADCAST) { + if (!_socks->set_broadcast(_socket)) return ACCESS_DENIED; + // mark the socket for broadcast capability. + } + + if (!_socks->set_reuse_address(_socket)) return ACCESS_DENIED; + // mark the socket so we don't get bind errors on in-use conditions. + } + + if (_type == CONNECTED) { + GRAB_LOCK; + // turn on the keepalive timer so that loss of the connection will + // eventually be detected by the OS. the duration that is allowed to + // elapse before a dead connection is noticed varies with the operating + // system and is not configured at this level. + if (!_socks->set_keep_alive(_socket)) { +#ifdef DEBUG_SPOCKET + LOG("couldn't set watchdog timer on socket."); +#endif + } + +//hmmm: doesn't this need to be done for bcast too? + + // create the spocket address that we will connect to. + if (strlen(_where->hostname) +// && (_where->is_nil_address() +// || (*_last_resolve < time_stamp(-RESOLVE_INTERVAL) ) ) ) { +// +//moving to always re-resolving before a connect. otherwise we have somewhat +//hard to predict behavior about when the re-resolve will happen. + ) { + // we know we need to resolve if the address is NIL or if the re-resolve + // interval has elapsed. + astring full_host; + byte_array ip_addr = _stack->full_resolve(_where->hostname, full_host); + if (ip_addr.length()) { + ip_addr.stuff(internet_address::ADDRESS_SIZE, _where->ip_address); + LOG(astring("successfully re-resolved address--") + _where->text_form()); + } + *_last_resolve = time_stamp(); // reset since we just resolved. + } + + // special code for forcing a client to bind. + if (_client_bind) { + sockaddr sock = _stack->convert(*_cli_bind); + +#ifdef DEBUG_SPOCKET + LOG(a_sprintf("binding client socket %d to ", _socket) + + inet_ntoa(((sockaddr_in *)&sock)->sin_addr)); +#endif + + // now, the socket address is bound to our socket. + if (negative(bind(_socket, &sock, sizeof(sock)))) { + LOG(a_sprintf("error binding socket %d to ", _socket) + + inet_ntoa(((sockaddr_in *)&sock)->sin_addr)); + } + } + + } else if ( (_type == BROADCAST) || (_type == UNICAST) ) { + // this is the last piece of preparation for a broadcast or unicast socket. + // there's no real connection, so we just need to get it bound and ready + // to fling packets. + GRAB_LOCK; + sockaddr sock = _stack->convert(*_where); + +#ifdef DEBUG_SPOCKET + LOG(a_sprintf("binding socket %d to ", _socket) + + inet_ntoa(((sockaddr_in *)&sock)->sin_addr)); +#endif + + // now, the socket address is bound to our socket. + if (negative(bind(_socket, &sock, sizeof(sock)))) { + LOG(a_sprintf("error binding socket %d to ", _socket) + + inet_ntoa(((sockaddr_in *)&sock)->sin_addr)); + } + + // that's it for broadcast preparation. we should be ready. + _was_connected = true; + return OKAY; + } + + // the following is for connected mode only. + + sockaddr sock = _stack->convert(*_where); + + // attempt the connection now. + +//hmmm: error returns are done differently on bsd, right? +//hmmm: perhaps hide the base connect in a func that sets our internal +// error variable and then allows comparison to enums we provide. + + time_stamp abort_time(communication_wait); + + bool connected = false; // did we connect. + + int sock_len = sizeof(sock); + + while (time_stamp() < abort_time) { + // make the low-level socket connection. + int ret = ::connect(_socket, &sock, sock_len); + if (ret != SOCKET_ERROR) { + connected = true; + _socks->set_non_blocking(_socket, true); + break; + } + + basis::un_int last_error = critical_events::system_error(); + + // if we're already done, then make this look like a normal connect. + if (last_error == SOCK_EISCONN) { + connected = true; + break; + } + + if ( (last_error != SOCK_EWOULDBLOCK) + && (last_error != SOCK_EINPROGRESS) ) { + // this seems like a real error here. +#ifdef DEBUG_SPOCKET + LOG(a_sprintf("Connect failed (error %s or %d) on address:", + critical_events::system_error_text(last_error).s(), last_error) + + _where->text_form()); +#endif + if (last_error == SOCK_ECONNREFUSED) return NO_ANSWER; +//hmmm: fix more of the possibilities to be sensible outcomes? + return ACCESS_DENIED; + } + + if (time_stamp() >= abort_time) break; // skip before sleeping if T.O. + + // snooze for a bit before trying again. + time_control::sleep_ms(10); + } + + if (connected) { +#ifdef DEBUG_SPOCKET + LOG(a_sprintf("socket %d connected to server.", _socket)); +#endif + GRAB_LOCK; // short lock. + _was_connected = true; + return OKAY; + } + + return TIMED_OUT; +} + +outcome spocket::accept(spocket * &sock, bool wait) +{ + FUNCDEF("accept"); + CHECK_BOGUS(NO_CONNECTION); + if (_type != CONNECTED) return BAD_INPUT; + + // we don't lock in here; we should not be locking on the server socket. + + sock = NIL; // reset. + + if (_socket) { +#ifdef DEBUG_SPOCKET + LOG("tried to accept on a client spocket."); +#endif + return NOT_SERVER; + } + _client = false; + + if (!_server_socket) { + _server_socket = int(::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); +#ifdef DEBUG_SPOCKET + LOG(a_sprintf("srv sock is %d", _server_socket)); +#endif + +#ifdef DEBUG_SPOCKET + LOG(astring("creating server socket now for ") + _where->text_form()); +#endif + + if (_server_socket == basis::un_int(INVALID_SOCKET)) { + LOG("Failed to open the serving spocket."); + return BAD_INPUT; + } + + // mark the socket so we don't get bind errors on in-use conditions. + if (!_socks->set_reuse_address(_server_socket)) + LOG("Failed to mark the socket for re-use."); + + // create the spocket address for where we exist. + sockaddr sock = _stack->convert(*_where); + + // now, the spocket address is bound to our spocket. + int sock_len = sizeof(sock); + if (bind(_server_socket, (sockaddr *)&sock, sock_len) < 0) { + LOG(astring("Error on bind of ") + critical_events::system_error_text(critical_events::system_error())); + _socks->close(_server_socket); + return ACCESS_DENIED; + } + + // now listen for a connection on our spocket. + if (listen(_server_socket, PENDING_CONNECTIONS_ALLOWED) < 0) { + LOG(astring("Listen failed with error of ") + + critical_events::system_error_text(critical_events::system_error())); + _socks->close(_server_socket); + return ACCESS_DENIED; + } + } + + // do the kind of accept they want; either block on it or don't. + // since our server socket is never used for sends or receives, we pretty + // much control it completely and this is safe. + if (!wait) { + _socks->set_non_blocking(_server_socket, true); + // mark our socket as non-blocking so we don't get stuck in accepts. + } else { + _socks->set_non_blocking(_server_socket, false); + // mark our socket as blocking; we will be paused until accept occurs. + } + + // now try accepting a connection on the spocket. + sockaddr new_sock; + socklen_t sock_len = sizeof(new_sock); + int accepted = int(::accept(_server_socket, &new_sock, &sock_len)); + int error = critical_events::system_error(); + if (accepted == INVALID_SOCKET) { + if (error == SOCK_EWOULDBLOCK) return NO_CONNECTION; +#ifdef DEBUG_SPOCKET + LOG(astring("Accept got no client, with an error of ") + + critical_events::system_error_text(error)); +#endif + return ACCESS_DENIED; + } + + // mark the new spocket for non-blocking I/O. + _socks->set_non_blocking(accepted, true); + +//move to socks object! + int sock_hop = 1; + if (setsockopt(accepted, SOL_SOCKET, SO_KEEPALIVE, (char *)&sock_hop, + sizeof(sock_hop)) < 0) { +#ifdef DEBUG_SPOCKET + LOG("couldn't set watchdog timer on socket."); +#endif + } + + // create the spocket address that we will connect to. +#ifdef DEBUG_SPOCKET + LOG(astring("accepted a client socket for ") + _where->text_form()); +#endif + +// NOTE: normally, our network code sets the spocket to be kept alive (using +// keep alives), but we are trying to have a minimal spocket usage and +// a minimal network load for this test scenario. + + sock = new spocket(*_where); + *sock->_remote = _stack->convert(new_sock); + sock->_socket = accepted; + sock->_server_socket = 0; // reset to avoid whacking. + sock->_was_connected = true; + return OKAY; +} + +outcome spocket::send(const byte_array &to_send, int &len_sent) +{ + return send(to_send.observe(), to_send.length(), len_sent); +} + +outcome spocket::send(const abyte *buffer, int size, int &len_sent) +{ +#ifdef DEBUG_SPOCKET + FUNCDEF("send"); +#endif + CHECK_BOGUS(OKAY); + if (_type != CONNECTED) return BAD_INPUT; + GRAB_LOCK; + ENSURE_HEALTH(NO_CONNECTION); + + len_sent = ::send(_socket, (char *)buffer, size, 0); + int error_code = critical_events::system_error(); + if (!len_sent) { +#ifdef DEBUG_SPOCKET + LOG("No data went out on the spocket."); +#endif + return PARTIAL; + } + if (len_sent == SOCKET_ERROR) { + if (error_code == SOCK_EWOULDBLOCK) { +#ifdef DEBUG_SPOCKET + LOG("would block, will try later..."); + if (len_sent > 0) + LOG("HEY HEY! some was sent but we were not counting it!!!"); +#endif + return NONE_READY; + } +#ifdef DEBUG_SPOCKET + LOG(astring("Error ") + critical_events::system_error_text(error_code) + + " occurred during the send!"); +#endif + if (!connected()) return NO_CONNECTION; +#ifdef DEBUG_SPOCKET + LOG(a_sprintf("forcing disconnect on socket %d.", _socket)); +#endif + // we're trying this new approach here... we found that the socket doesn't + // really know that it got disconnected in some circumstances. + disconnect(); + return ACCESS_DENIED; + } + if (len_sent != size) { + // only sent part of the buffer. +#ifdef DEBUG_SPOCKET + LOG(a_sprintf("sent %d bytes out of %d.", len_sent, size)); +#endif + return PARTIAL; + } + + return OKAY; +} + +outcome spocket::send_to(const internet_address &where_to, + const byte_array &to_send, int &len_sent) +{ + return send_to(where_to, to_send.observe(), to_send.length(), len_sent); +} + +outcome spocket::send_to(const internet_address &where_to, const abyte *to_send, + int size, int &len_sent) +{ + FUNCDEF("send_to"); + CHECK_BOGUS(OKAY); + if (_type == CONNECTED) return BAD_INPUT; + sockaddr dest = _stack->convert(where_to); + int ret = sendto(_socket, (char *)to_send, size, 0, &dest, sizeof(dest)); + int error = critical_events::system_error(); + if (ret < 0) { + if (error == SOCK_EWOULDBLOCK) return NONE_READY; // no buffer space? + LOG(astring("failed to send packet; error ") + + _stack->tcpip_error_name(error)); + return ACCESS_DENIED; + } + if (ret != size) { + LOG(astring("didn't send whole datagram!")); + } + len_sent = ret; + return OKAY; +} + +outcome spocket::receive(byte_array &buffer, int &size) +{ +#ifdef DEBUG_SPOCKET + FUNCDEF("receive"); +#endif + CHECK_BOGUS(NONE_READY); + if (_type != CONNECTED) return BAD_INPUT; + if (size <= 0) return BAD_INPUT; + buffer.reset(size); + outcome to_return = receive(buffer.access(), size); + // trim the buffer to the actual received size. + if (to_return == OKAY) + buffer.zap(size, buffer.last()); + return to_return; +} + +outcome spocket::receive(abyte *buffer, int &size) +{ +#ifdef DEBUG_SPOCKET + FUNCDEF("receive"); +#endif + CHECK_BOGUS(NONE_READY); + if (_type != CONNECTED) return BAD_INPUT; + ENSURE_HEALTH(NO_CONNECTION); + int expected = size; + size = 0; + if (expected <= 0) return BAD_INPUT; + GRAB_LOCK; + int len = recv(_socket, (char *)buffer, expected, 0); + if (!len) { + // check to make sure we're not disconnected. + int ret = _socks->select(_socket, raw_socket::SELECTING_JUST_READ); + if (ret & SI_DISCONNECTED) { + RECOGNIZE_DISCO; + return NO_CONNECTION; + } + // seems like more normal absence of data. + return NONE_READY; + } else if (len < 0) { + if (critical_events::system_error() == SOCK_EWOULDBLOCK) return NONE_READY; +#ifdef DEBUG_SPOCKET + LOG(astring("The receive failed with an error ") + + critical_events::system_error_text(critical_events::system_error())); +#endif + if (!connected()) return NO_CONNECTION; + return ACCESS_DENIED; + } + size = len; + return OKAY; +} + +outcome spocket::receive_from(byte_array &buffer, int &size, + internet_address &where_from) +{ +#ifdef DEBUG_SPOCKET + FUNCDEF("receive_from"); +#endif + where_from = internet_address(); + CHECK_BOGUS(NONE_READY); + if (_type == CONNECTED) return BAD_INPUT; + if (size <= 0) return BAD_INPUT; + buffer.reset(size); + outcome to_return = receive_from(buffer.access(), size, where_from); + // trim the buffer to the actual received size. + if (to_return == OKAY) + buffer.zap(size, buffer.last()); + return to_return; +} + +outcome spocket::receive_from(abyte *buffer, int &size, + internet_address &where_from) +{ +#ifdef DEBUG_SPOCKET + FUNCDEF("receive_from"); +#endif + where_from = internet_address(); + CHECK_BOGUS(NONE_READY); + if (_type == CONNECTED) return BAD_INPUT; + ENSURE_HEALTH(NO_CONNECTION); + int expected = size; + size = 0; + if (expected <= 0) return BAD_INPUT; + GRAB_LOCK; + sockaddr from; + socklen_t fromlen = sizeof(from); + int len = recvfrom(_socket, (char *)buffer, expected, 0, &from, &fromlen); + int err = critical_events::system_error(); + if (!len) return NONE_READY; + else if (len < 0) { +#ifdef DEBUG_SPOCKET + LOG(a_sprintf("actual sys err value=%d", err)); +#endif + if (err == SOCK_EWOULDBLOCK) return NONE_READY; + if (err == SOCK_ECONNRESET) return NONE_READY; + // this seems to be a necessary windoze kludge; we're not connected + // and never were but it says this idiotic garbage about the connection + // being reset. +#ifdef DEBUG_SPOCKET + LOG(astring("The recvfrom failed with an error ") + + critical_events::system_error_text(err)); +#endif + if (!connected()) return NO_CONNECTION; + return ACCESS_DENIED; + } + where_from = _stack->convert(from); + size = len; + return OKAY; +} + +} //namespace. + diff --git a/octopi/library/sockets/spocket.h b/octopi/library/sockets/spocket.h new file mode 100644 index 00000000..effe26cf --- /dev/null +++ b/octopi/library/sockets/spocket.h @@ -0,0 +1,245 @@ +#ifndef SPOCKET_CLASS +#define SPOCKET_CLASS + +/*****************************************************************************\ +* * +* Name : spocket * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* * +******************************************************************************* +* Copyright (c) 1989-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "tcpip_stack.h" + +#include +#include +#include + +namespace sockets { + +// forward declarations. +class internet_address; +class raw_socket; + +//! Abstraction for a higher-level BSD socket that is platform independent. +/*! + The class works on Unix and Win32 style operating systems. This class is named in honor of + the venerable Vulcan Spock, which also avoid naming conflicts with the OS's socket() function. +*/ + +class spocket : public virtual basis::root_object +{ +public: + enum sock_types { + CONNECTED, // connected socket over TCP. + BROADCAST, // broadcasting socket over UDP. + UNICAST, // single-address targeted socket over UDP. + BOGUS_SOCK // special type that goes nowhere and provides no data. + }; + + spocket(const internet_address &where, sock_types type = CONNECTED); + // constructs the spocket object. "where" provides the network location + // for either this side (for a server) or the other side (for a client). + // the decision about this socket's orientation (client or server) will not + // be made until either connect() or accept() are invoked. note however + // that BROADCAST sockets are only appropriate for use as a client; they + // can receive and send broadcasts that way without needing a server role. + + ~spocket(); + // drops any connection that was made and destroys the spocket object. + + DEFINE_CLASS_NAME("spocket"); + + bool healthy(); + // returns true if the spocket seems to be okay. + + const internet_address &where() const; + // returns the location where this socket exists. + + const internet_address &remote() const; + // returns the location that we have accepted from. + + enum outcomes { + OKAY = basis::common::OKAY, + TIMED_OUT = basis::common::TIMED_OUT, + ACCESS_DENIED = basis::common::ACCESS_DENIED, + BAD_INPUT = basis::common::BAD_INPUT, + NONE_READY = basis::common::NONE_READY, + PARTIAL = basis::common::PARTIAL, + + NO_CONNECTION = sockets::communication_commons::NO_CONNECTION, + NO_ANSWER = sockets::communication_commons::NO_ANSWER, + + DEFINE_OUTCOME(NOT_SERVER, -39, "Accept was tried on a spocket that is " + "not a root server") + }; + + static const char *outcome_name(const basis::outcome &to_name); + // returns the text for "to_name" if it's a member of spocket outcomes. + + // informative functions... + + basis::astring text_form(); + // returns a readable version of the contents of the spocket. + + bool was_connected() const { return _was_connected; } + // a simple check of whether a connection has been made on this object. + // if this is not true, then the object is not usable yet. this state + // will also get set to false when the spocket gets disconnected. + + bool connected(); + // returns true if the spocket is "currently" connected. this causes an + // interrogation of the operating system and may take a short while. + + // these report what type of spocket this is. + bool is_client() const { return _client; } + bool is_server() const { return !_client; } + bool is_root_server() const { return is_server() && !!_server_socket; } + + basis::un_int OS_socket() { return _socket; } + // returns the low-level operating system form of our normal action socket. + // this is zero for a root server. note: this will still record what the + // socket was after it is closed out, just for record keeping; do not + // control the returned socket outside of this class. + basis::un_int OS_root_socket() { return _server_socket; } + // returns the OS form of our server socket, but only if this is a root + // server type of socket. + + void bind_client(const internet_address &source); + //!< when a client calls connect, this forces it to bind to "source". + /*!< this has no effect on servers. it is also disabled again when the + client is disconnected, so it should always be done before every time + connect() is called. */ + + // major operations for connected mode sockets... + + basis::outcome connect(int communication_wait = 20 * basis::SECOND_ms); + // acts as a client and connects to a destination. the timeout is + // specified in milliseconds by "communication_wait". this can be as low + // as zero if you don't want to wait. TIMED_OUT is returned if the + // connection hasn't finished within the connection_wait. OKAY is returned + // if the connection succeeded. other outcomes denote failures. + + basis::outcome accept(spocket * &sock, bool wait); + // makes this spocket act as a root server and accepts a connection from a + // client if possible. a root server is a spocket object that manages a + // server spocket but which does not allow any normal sending or receiving. + // only root servers can have accept called on them. the "sock" will be + // a normal server spocket which can be used to send and receive if it + // got connected. for "sock" to be valid, it must not be returned as NIL + // and the returned outcome must be OKAY. if no new connections are + // available, then NO_CONNECTION is returned. if the "wait" flag is true, + // then the accept on the root server will block until a connection is + // accepted and the returned spocket will be made non-blocking. if "wait" + // is false, then no blocking will occur at all. note that multiple + // threads can invoke this without tripping over the protective locking; + // once the root socket is initialized, accept will not lock the spocket. + + basis::outcome disconnect(); + // closes the connection. the state is returned to the post-construction + // state, i.e. it will appear that this object had never connected yet. + + // these report which side of the connection this is functioning as. + bool client() const { return _client; } + bool server() const { return !_client; } + + // send and receive functions... + // + // if the outcome from one of these is NO_CONNECTION, then somehow the + // connection has dropped or never succeeded. + + basis::outcome send(const basis::abyte *buffer, int size, int &len_sent); + // sends "size" bytes from the "buffer". the number actually sent is + // stored in "len_sent". this can only be used for CONNECTED type sockets. + + basis::outcome send(const basis::byte_array &to_send, int &len_sent); + // this version takes a byte_array. + + basis::outcome send_to(const internet_address &where_to, const basis::abyte *buffer, + int size, int &len_sent); + // this version is used for sending when the socket type is BROADCAST + // or UNICAST. + + basis::outcome send_to(const internet_address &where_to, const basis::byte_array &to_send, + int &len_sent); + // clone of above, using byte_array. + + basis::outcome receive(basis::abyte *buffer, int &size); + // attempts to retrieve data from the spocket and place it in the "buffer". + // the "size" specifies how much space is available. if successful, the + // buffer will be filled with data and "size" will report how much there + // actually is. + + basis::outcome receive(basis::byte_array &buffer, int &size); + // this version uses a byte_array for the "buffer". the "size" expected + // must still be set and "size" will still report the bytes read. +//hmmm: could remove the size parameter for byte array type. + + // these methods are used for BROADCAST and other non-connected types of + // spockets. they report the data as the above receive methods do, but they + // also report the sender. + basis::outcome receive_from(basis::abyte *buffer, int &size, + internet_address &where_from); + basis::outcome receive_from(basis::byte_array &buffer, int &size, + internet_address &where_from); + + basis::outcome await_readable(int timeout); + // pauses this caller until data arrives. this is a blocking call that + // could potentially not return until the "timeout" elapses (measured in + // milliseconds). if "timeout" is zero, then this really doesn't do + // anything. if data is available, then OKAY is returned. + + basis::outcome await_writable(int timeout); + // pauses this caller until the socket can be written to again. could + // potentially not return until the "timeout" elapses (measured in + // milliseconds). if "timeout" is zero, then this really doesn't do + // anything. if the socket is able to send again, then OKAY is returned. + // otherwise NONE_READY is returned. + + tcpip_stack &stack() const; + // provides access to the spocket's tcpip stack to derivative objects. + + bool is_bogus() const { return _type == BOGUS_SOCK; } + //!< returns true when this object is bogus. + /*!< a spocket constructed as BOGUS_SOCK does not open a network + connection to anywhere, and it also never sends or receives any data. + it allows code based on spockets to be disabled when appropriate, so + that the spocket is still constructed and all methods can be invoked, + but it does nothing. */ + +private: + sock_types _type; // records what kind of socket we'll create. + basis::un_int _socket; // our socket that we communicate on. + basis::un_int _server_socket; // our serving socket, if we're a root server. + bool _was_connected; // did we ever successfully connect? + bool _client; // true if we are acting as a client. + internet_address *_where; // our addressing info. + internet_address *_remote; // our addressing info. + raw_socket *_socks; // provides low-level socket functionality. + tcpip_stack *_stack; // provides access to socket facilities. + basis::mutex *_select_lock; // protects concurrent access to socket. + timely::time_stamp *_last_resolve; + // tracks when we last tried a resolve on our address. if they try to + // reconnect and we haven't done this in a while, we'll re-resolve the + // socket. + bool _client_bind; //!< force the client to bind on an address. + internet_address *_cli_bind; //!< where the client binds. + + // not allowed. + spocket(const spocket &); + spocket &operator =(const spocket &); +}; + +} //namespace. + +#endif + diff --git a/octopi/library/sockets/subnet_calculator.cpp b/octopi/library/sockets/subnet_calculator.cpp new file mode 100644 index 00000000..e81b97b7 --- /dev/null +++ b/octopi/library/sockets/subnet_calculator.cpp @@ -0,0 +1,152 @@ + + + +/*****************************************************************************\ +* * +* Name : subnet_calculator * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1997-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +//hmmm: this class only handles 32 bit (4 byte) internet addresses. it should +// use an arbitrary length integer object to overcome differences in the +// sizes of internet addresses. +// really we should just start using the machine uid underneath... + +#include "subnet_calculator.h" + +#include +#include + +using namespace basis; + +namespace sockets { + +subnet_calculator::subnet_calculator(const astring &mask, const astring &samp) +: _valid(false), + _subnet_mask(new astring(mask)), + _ip_address(new astring(samp)), + _low_end(new astring("")), + _high_end(new astring("")) +{ calculate(); } + +subnet_calculator::~subnet_calculator() +{ + WHACK(_subnet_mask); + WHACK(_ip_address); + WHACK(_low_end); + WHACK(_high_end); + _valid = false; +} + +const astring &subnet_calculator::subnet_mask() const { return *_subnet_mask; } + +const astring &subnet_calculator::ip_address() const { return *_ip_address; } + +void subnet_calculator::subnet_mask(const astring &new_mask) +{ + _valid = false; + *_subnet_mask = new_mask; +} + +void subnet_calculator::ip_address(const astring &new_address) +{ + _valid = false; + *_ip_address = new_address; +} + +const astring &subnet_calculator::low_end() +{ + calculate(); + return *_low_end; +} + +const astring &subnet_calculator::high_end() +{ + calculate(); + return *_high_end; +} + +astring subnet_calculator::convert(basis::un_int num_format) +{ + astring to_return; + basis::un_int temp_num = num_format; + for (int i = 0; i < 4; i++) { + if (to_return.t()) + to_return = astring(".") + to_return; + // shave a byte off for inclusion in the string. + basis::un_int new_byte = temp_num % 256; + temp_num >>= 8; + to_return = astring(astring::SPRINTF, "%d", new_byte) + to_return; + } + return to_return; +} + +un_int subnet_calculator::convert(const astring &ip_format) +{ + basis::un_int to_return = 0; + astring ip_temp = ip_format; + for (int i = 0; i < 3; i++) { + int indy = ip_temp.find("."); + if (indy < 0) return to_return; + astring this_piece = ip_temp.substring(0, indy - 1); + to_return <<= 8; + to_return += this_piece.convert(0); + ip_temp.zap(0, indy); + } + + if (ip_temp.length()) { + to_return <<= 8; + to_return += ip_temp.convert(0); + } + + return to_return; +} + +void subnet_calculator::calculate() +{ + if (valid()) return; // already valid. + + basis::un_int ip = convert(*_ip_address); + basis::un_int sub = convert(*_subnet_mask); + + basis::un_int low_end = sub & ip; + // strips off the host part of the ip address and leaves just the network + // component that comes from the ip address. + + basis::un_int temp_sub = sub; + int bits_to_add = 0; + // we shift the subnet mask until we find it's first lowest order "1" bit. + // this tells us how many bits are in the host portion (unless the mask is + // zero, in which case the host is all of the bits). + while (temp_sub && !(temp_sub % 2)) { + temp_sub >>= 1; // shift off a bit. + bits_to_add++; // record that the place we were at had a zero bit. + } + if (!sub) bits_to_add = 32; + // account for a blank subnet mask, meaning there is no network and all + // bits are used for host addresses. + basis::un_int add_in_for_bcast = 0; + // the part added in to make a broadcast address. this is all ones. + for (int i = 0; i < bits_to_add; i++) { + add_in_for_bcast <<= 1; // shift the existing add_in to the left. + add_in_for_bcast++; // make a new one bit in the ones place. + } + + basis::un_int high_end = low_end + add_in_for_bcast; + + *_low_end = convert(low_end); + *_high_end = convert(high_end); + _valid = true; +} + +} //namespace. + + diff --git a/octopi/library/sockets/subnet_calculator.h b/octopi/library/sockets/subnet_calculator.h new file mode 100644 index 00000000..bfa7cdb3 --- /dev/null +++ b/octopi/library/sockets/subnet_calculator.h @@ -0,0 +1,68 @@ +#ifndef SUBNET_CALCULATOR_CLASS +#define SUBNET_CALCULATOR_CLASS + +/*****************************************************************************\ +* * +* Name : subnet_calculator * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1997-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace sockets { + +//! Provides an easy way to determine the range of a subnet given the subnet mask and a sample IP address. + +class subnet_calculator : public basis::root_object +{ +public: + subnet_calculator(const basis::astring &subnet_mask, const basis::astring &ip_address); + ~subnet_calculator(); + + basis::astring convert(basis::un_int num_format); + basis::un_int convert(const basis::astring &ip_format); + // converts between the numerical and string forms of the ip address. + + // these two functions return the computed low / high range of the subnet. + // they ensure that the subnet calculator is valid by calling the calculate + // method if it hasn't already been called. + const basis::astring &low_end(); + const basis::astring &high_end(); + + // these allow observation and modification of the parameters that are needed + // to calculate the subnet range. + const basis::astring &subnet_mask() const; + void subnet_mask(const basis::astring &new_mask); + const basis::astring &ip_address() const; + void ip_address(const basis::astring &new_address); + + bool valid() const { return _valid; } + // returns whether the object has recalculated its mask information yet + // or not. the object should always be valid until the mask or address + // are changed. once the two range methods are called, the object should + // be valid afterwards. + +private: + bool _valid; // is this object valid yet (has calculate been called)? + basis::astring *_subnet_mask; // the mask used for the subnets in question. + basis::astring *_ip_address; // internet address of an example host on the subnet. + basis::astring *_low_end; // lower bound and + basis::astring *_high_end; // upper bound of the subnet. + + void calculate(); + // performs the main action of determining the lower and upper bounds + // of the subnet's range. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/sockets/tcpip_definitions.h b/octopi/library/sockets/tcpip_definitions.h new file mode 100644 index 00000000..6a149210 --- /dev/null +++ b/octopi/library/sockets/tcpip_definitions.h @@ -0,0 +1,144 @@ +#ifndef TCPIP_DEFINITIONS_GROUP +#define TCPIP_DEFINITIONS_GROUP + +/*****************************************************************************\ +* * +* Name : tcpip_definitions * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Provides some variables that make porting between Unix and W32 easier. * +* * +******************************************************************************* +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +///#include "sockets_dll.h" + +#ifdef __UNIX__ + // provide some unifying definitions. + #define INVALID_SOCKET -1 + #define SOCKET_ERROR -1 + typedef void sock_hop; + + // provide synonyms for errors so we don't conflict with the windows + // brain-deadness. they define error values like EACCESS but they're not + // the real values you need to use with tcp/ip. french fried gates time. + #define SOCK_EACCES EACCES + #define SOCK_EADDRINUSE EADDRINUSE + #define SOCK_EADDRNOTAVAIL EADDRNOTAVAIL + #define SOCK_EAFNOSUPPORT EAFNOSUPPORT + #define SOCK_EALREADY EALREADY + #define SOCK_EBADF EBADF + #define SOCK_ECONNABORTED ECONNABORTED + #define SOCK_ECONNREFUSED ECONNREFUSED + #define SOCK_ECONNRESET ECONNRESET + #define SOCK_EDESTADDRREQ EDESTADDRREQ + #define SOCK_EDQUOT EDQUOT + #define SOCK_EFAULT EFAULT + #define SOCK_EHOSTDOWN EHOSTDOWN + #define SOCK_EHOSTUNREACH EHOSTUNREACH + #define SOCK_EINPROGRESS EINPROGRESS + #define SOCK_EINTR EINTR + #define SOCK_EINVAL EINVAL + #define SOCK_EISCONN EISCONN + #define SOCK_ELOOP ELOOP + #define SOCK_EMFILE EMFILE + #define SOCK_EMSGSIZE EMSGSIZE + #define SOCK_ENAMETOOLONG ENAMETOOLONG + #define SOCK_ENETDOWN ENETDOWN + #define SOCK_ENETUNREACH ENETUNREACH + #define SOCK_ENETRESET ENETRESET + #define SOCK_ENOBUFS ENOBUFS + #define SOCK_ENOPROTOOPT ENOPROTOOPT + #define SOCK_ENOTCONN ENOTCONN + #define SOCK_ENOTEMPTY ENOTEMPTY + #define SOCK_ENOTSOCK ENOTSOCK + #define SOCK_EOPNOTSUPP EOPNOTSUPP + #define SOCK_EPFNOSUPPORT EPFNOSUPPORT + #define SOCK_EPROCLIM EPROCLIM + #define SOCK_EPROTOTYPE EPROTOTYPE + #define SOCK_EPROTONOSUPPORT EPROTONOSUPPORT + #define SOCK_EREMOTE EREMOTE + #define SOCK_ESHUTDOWN ESHUTDOWN + #define SOCK_ESOCKTNOSUPPORT ESOCKTNOSUPPORT + #define SOCK_ESTALE ESTALE + #define SOCK_ETIMEDOUT ETIMEDOUT + #define SOCK_ETOOMANYREFS ETOOMANYREFS + #define SOCK_EWOULDBLOCK EWOULDBLOCK + #define SOCK_EUSERS EUSERS +#endif + +#ifdef __WIN32__ + #include + + // provide some aliases for w32. +#if COMPILER_VERSION==6 + #define hostent HOSTENT +#endif + typedef char sock_hop; + typedef int socklen_t; + + // provide close to the real BSD error names using windows values. + #define SOCK_EACCES WSAEACCES + #define SOCK_EADDRINUSE WSAEADDRINUSE + #define SOCK_EADDRNOTAVAIL WSAEADDRNOTAVAIL + #define SOCK_EAFNOSUPPORT WSAEAFNOSUPPORT + #define SOCK_EALREADY WSAEALREADY + #define SOCK_EBADF WSAEBADF + #define SOCK_ECONNABORTED WSAECONNABORTED + #define SOCK_ECONNREFUSED WSAECONNREFUSED + #define SOCK_ECONNRESET WSAECONNRESET + #define SOCK_EDESTADDRREQ WSAEDESTADDRREQ + #define SOCK_EDQUOT WSAEDQUOT + #define SOCK_EFAULT WSAEFAULT + #define SOCK_EHOSTDOWN WSAEHOSTDOWN + #define SOCK_EHOSTUNREACH WSAEHOSTUNREACH + #define SOCK_EINPROGRESS WSAEINPROGRESS + #define SOCK_EINTR WSAEINTR + #define SOCK_EINVAL WSAEINVAL + #define SOCK_EISCONN WSAEISCONN + #define SOCK_ELOOP WSAELOOP + #define SOCK_EMFILE WSAEMFILE + #define SOCK_EMSGSIZE WSAEMSGSIZE + #define SOCK_ENAMETOOLONG WSAENAMETOOLONG + #define SOCK_ENETDOWN WSAENETDOWN + #define SOCK_ENETUNREACH WSAENETUNREACH + #define SOCK_ENETRESET WSAENETRESET + #define SOCK_ENOBUFS WSAENOBUFS + #define SOCK_ENOPROTOOPT WSAENOPROTOOPT + #define SOCK_ENOTCONN WSAENOTCONN + #define SOCK_ENOTEMPTY WSAENOTEMPTY + #define SOCK_ENOTSOCK WSAENOTSOCK + #define SOCK_EOPNOTSUPP WSAEOPNOTSUPP + #define SOCK_EPFNOSUPPORT WSAEPFNOSUPPORT + #define SOCK_EPROCLIM WSAEPROCLIM + #define SOCK_EPROTOTYPE WSAEPROTOTYPE + #define SOCK_EPROTONOSUPPORT WSAEPROTONOSUPPORT + #define SOCK_EREMOTE WSAEREMOTE + #define SOCK_ESHUTDOWN WSAESHUTDOWN + #define SOCK_ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT + #define SOCK_ESTALE WSAESTALE + #define SOCK_ETIMEDOUT WSAETIMEDOUT + #define SOCK_ETOOMANYREFS WSAETOOMANYREFS + #define SOCK_EUSERS WSAEUSERS + + // windows specific names. + #define SOCK_EWOULDBLOCK WSAEWOULDBLOCK + #define SOCK_HOST_NOT_FOUND WSAHOST_NOT_FOUND + #define SOCK_NO_DATA WSANO_DATA + #define SOCK_NO_RECOVERY WSANO_RECOVERY + #define SOCK_NOTINITIALISED WSANOTINITIALISED + #define SOCK_SYSNOTREADY WSASYSNOTREADY + #define SOCK_TRY_AGAIN WSATRY_AGAIN + #define SOCK_VERNOTSUPPORTED WSAVERNOTSUPPORTED +#endif + +#endif + diff --git a/octopi/library/sockets/tcpip_stack.cpp b/octopi/library/sockets/tcpip_stack.cpp new file mode 100644 index 00000000..f45ea6aa --- /dev/null +++ b/octopi/library/sockets/tcpip_stack.cpp @@ -0,0 +1,450 @@ +/** +* Name : tcpip_stack +* Author : Chris Koeritz +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "internet_address.h" +#include "machine_uid.h" +#include "raw_socket.h" +#include "tcpip_stack.h" + +#include +#include +#include +#include + +#ifdef __UNIX__ + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +using namespace basis; +using namespace loggers; +using namespace structures; + +namespace sockets { + +//#define DEBUG_TCPIP_STACK + // turn on for clamor. + +#undef LOG +#define LOG(to_print) CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print) + +////////////// + +#ifdef __WIN32__ + const WORD WINSOCK_VERSION_REQUIRED = 0x0101; + // 1.1 version is used by this version of tcp/ip transport. +#endif + +////////////// + +const char *communication_commons::outcome_name(const outcome &to_name) +{ + switch (to_name.value()) { + case NO_CONNECTION: return "NO_CONNECTION"; + case NO_SERVER: return "NO_SERVER"; + case NO_ANSWER: return "NO_ANSWER"; + case SHUTDOWN: return "SHUTDOWN"; + case ALREADY_SETUP: return "ALREADY_SETUP"; + case MEDIUM_ERROR: return "MEDIUM_ERROR"; + case BAD_MODE: return "BAD_MODE"; + case ALREADY_CONNECTED: return "ALREADY_CONNECTED"; + case WRONG_ENTITY: return "WRONG_ENTITY"; + case IPC_ERROR: return "IPC_ERROR"; + case TOO_NOISY: return "TOO_NOISY"; + case COMM_ERROR: return "COMM_ERROR"; + default: return common::outcome_name(to_name); + } +} + +////////////// + +tcpip_stack::tcpip_stack() +: _healthy(initialize_tcpip()) +{} + +tcpip_stack::~tcpip_stack() +{ + deinitialize_tcpip(); + _healthy = false; +} + +bool tcpip_stack::initialize_tcpip() +{ +#ifdef __WIN32__ + FUNCDEF("initialize_tcpip"); + // make sure we have the right version of WinSock available. + WORD desired_winsock = WINSOCK_VERSION_REQUIRED; + WSADATA startup_data; + int error = WSAStartup(desired_winsock, &startup_data); + if (error) { + LOG(astring("startup error: ") + tcpip_error_name(critical_events::system_error())); + return false; + } +#endif + return true; +} + +void tcpip_stack::deinitialize_tcpip() +{ +#ifdef __WIN32__ + WSACleanup(); +#endif +} + +astring tcpip_stack::hostname() const +{ + FUNCDEF("hostname"); + char hostname[256]; + hostname[0] = '\0'; + // gethostname() finds the name for our tcp/ip host. + if (negative(gethostname(hostname, 255))) { + LOG(astring(astring::SPRINTF, "gethostname error %s.", + tcpip_error_name(critical_events::system_error()).s())); + return hostname; + } + return hostname; +} + +astring tcpip_stack::tcpip_error_name(int error_value) +{ + switch (error_value) { + // winsock errors: + case SOCK_EINTR: return "EINTR"; + case SOCK_EBADF: return "EBADF"; + case SOCK_EACCES: return "EACCES"; + case SOCK_EFAULT: return "EFAULT"; + case SOCK_EINVAL: return "EINVAL"; + case SOCK_EMFILE: return "EMFILE"; + case SOCK_EWOULDBLOCK: return "EWOULDBLOCK"; + case SOCK_EINPROGRESS: return "EINPROGRESS"; + case SOCK_EALREADY: return "EALREADY"; + case SOCK_ENOTSOCK: return "ENOTSOCK"; + case SOCK_EDESTADDRREQ: return "EDESTADDRREQ"; + case SOCK_EMSGSIZE: return "EMSGSIZE"; + case SOCK_EPROTOTYPE: return "EPROTOTYPE"; + case SOCK_ENOPROTOOPT: return "ENOPROTOOPT"; + case SOCK_EPROTONOSUPPORT: return "EPROTONOSUPPORT"; + case SOCK_ESOCKTNOSUPPORT: return "ESOCKTNOSUPPORT"; + case SOCK_EOPNOTSUPP: return "EOPNOTSUPP"; + case SOCK_EPFNOSUPPORT: return "EPFNOSUPPORT"; + case SOCK_EAFNOSUPPORT: return "EAFNOSUPPORT"; + case SOCK_EADDRINUSE: return "EADDRINUSE"; + case SOCK_EADDRNOTAVAIL: return "EADDRNOTAVAIL"; + case SOCK_ENETDOWN: return "ENETDOWN"; + case SOCK_ENETUNREACH: return "ENETUNREACH"; + case SOCK_ENETRESET: return "ENETRESET"; + case SOCK_ECONNABORTED: return "ECONNABORTED"; + case SOCK_ECONNRESET: return "ECONNRESET"; + case SOCK_ENOBUFS: return "ENOBUFS"; + case SOCK_EISCONN: return "EISCONN"; + case SOCK_ENOTCONN: return "ENOTCONN"; + case SOCK_ESHUTDOWN: return "ESHUTDOWN"; + case SOCK_ETOOMANYREFS: return "ETOOMANYREFS"; + case SOCK_ETIMEDOUT: return "ETIMEDOUT"; + case SOCK_ECONNREFUSED: return "ECONNREFUSED"; + case SOCK_ELOOP: return "ELOOP"; + case SOCK_ENAMETOOLONG: return "ENAMETOOLONG"; + case SOCK_EHOSTDOWN: return "EHOSTDOWN"; + case SOCK_EHOSTUNREACH: return "EHOSTUNREACH"; + case SOCK_ENOTEMPTY: return "ENOTEMPTY"; + case SOCK_EUSERS: return "EUSERS"; + case SOCK_EDQUOT: return "EDQUOT"; + case SOCK_ESTALE: return "ESTALE"; + case SOCK_EREMOTE: return "EREMOTE"; +#ifdef __WIN32__ + case SOCK_EPROCLIM: return "EPROCLIM"; + case SOCK_SYSNOTREADY: return "SYSNOTREADY"; + case SOCK_VERNOTSUPPORTED: return "VERNOTSUPPORTED"; + case SOCK_HOST_NOT_FOUND: return "HOST_NOT_FOUND"; + case SOCK_TRY_AGAIN: return "TRY_AGAIN"; + case SOCK_NO_RECOVERY: return "NO_RECOVERY"; + case SOCK_NO_DATA: return "NO_DATA"; // or NO_ADDRESS. + case SOCK_NOTINITIALISED: return "NOTINITIALISED"; +#endif + } + + // return a standard OS error... + return critical_events::system_error_text(error_value); +} + +////////////// + +bool tcpip_stack::enumerate_adapters(string_array &ip_addresses, + bool add_local) const +{ +#ifdef DEBUG_TCPIP_STACK + FUNCDEF("enumerate_adapters"); +#endif + ip_addresses.reset(); + // see if they want to put the local adapter in there. + if (add_local) + ip_addresses += "127.0.0.1"; + astring this_host = hostname(); +#ifdef DEBUG_TCPIP_STACK + LOG(astring("hostname is \"") + this_host + astring("\".")); +#endif + if (!this_host) { +#ifdef DEBUG_TCPIP_STACK + LOG("failed to get the hostname for this machine!"); +#endif + return false; + } + + hostent *host_entry = gethostbyname(this_host.s()); + if (!host_entry) { +#ifdef DEBUG_TCPIP_STACK + LOG(astring("failed to get host entry for \"") + this_host + "\"."); +#endif + return false; + } + for (int adapter_num = 0; /* check is inside loop */; adapter_num++) { + in_addr *current_entry = (in_addr *)host_entry->h_addr_list[adapter_num]; + if (!current_entry) break; + char *ip_address = inet_ntoa(*current_entry); +#ifdef DEBUG_TCPIP_STACK + LOG(astring("current is: ") + astring(ip_address)); +#endif + ip_addresses += ip_address; + } + +#ifdef DEBUG_TCPIP_STACK + LOG(astring("read addresses:") + parser_bits::platform_eol_to_chars() + + ip_addresses.text_form()); +#endif + + return !!ip_addresses.length(); +} + +bool tcpip_stack::enumerate_adapters(machine_uid_array &ip_addresses, + bool add_local) const +{ + ip_addresses.reset(); + string_array text_list; + if (!enumerate_adapters(text_list, add_local)) + return false; + for (int i = 0; i < text_list.length(); i++) { + bool worked; + internet_address addr_form = fill_and_resolve(text_list[i], 0, worked); + if (worked) { + hostname().stuff(addr_form.hostname, addr_form.MAXIMUM_HOSTNAME_LENGTH); + ip_addresses += addr_form.convert(); + } + } + return !!ip_addresses.elements(); +} + +sockaddr tcpip_stack::convert(const internet_address &make_from) +{ +// FUNCDEF("convert [to sockaddr]"); + sockaddr_in new_socket; // our socket. + memset(&new_socket, 0, sizeof(new_socket)); // clear it out. + new_socket.sin_family = AF_INET; + byte_array ip(internet_address::ADDRESS_SIZE, make_from.ip_address); + ip.stuff(internet_address::ADDRESS_SIZE, (abyte *)&new_socket.sin_addr); + new_socket.sin_port = htons(basis::un_short(make_from.port)); + // possibly unportable conversion to short above. + // now we need to return the more generic form of socket address. + sockaddr to_return; + memset(&to_return, 0, sizeof(to_return)); + memcpy(&to_return, (sockaddr *)&new_socket, sizeof(new_socket)); + // sockaddr_in guaranteed to be smaller or equal to sockaddr. + return to_return; +} + +internet_address tcpip_stack::convert(const sockaddr &make_from_o) +{ + const sockaddr_in *make_from = (const sockaddr_in *)&make_from_o; + byte_array ip(internet_address::ADDRESS_SIZE, (abyte *)&make_from->sin_addr); + internet_address to_return; + to_return.fill(ip, "", ntohs(make_from->sin_port)); + return to_return; +} + +byte_array tcpip_stack::full_resolve(const astring &hostname, + astring &full_hostname) const +{ + FUNCDEF("full_resolve"); + if (!hostname) return byte_array(); // blank hostnames go nowhere. + full_hostname.reset(); + // check first for local host equivalents. + if ( hostname.iequals("local") || hostname.iequals("localhost") ) { + byte_array to_return = internet_address::localhost(); + full_hostname = "localhost"; + return to_return; + } else if (hostname.iequals("inaddr_any") + || hostname.iequals("any-address")) { + byte_array to_return = internet_address::nil_address(); + full_hostname = "inaddr_any"; + return to_return; + } + // gethostbyname() fills in details about the host, such as its IP address + // and full hostname. + hostent *machine = gethostbyname(hostname.observe()); + if (!machine) { + LOG(astring(astring::SPRINTF, "gethostbyname error %s.", + tcpip_error_name(critical_events::system_error()).s())); + return byte_array(); + } + full_hostname = astring(machine->h_name); + return byte_array(machine->h_length, (abyte *)machine->h_addr); +} + +bool tcpip_stack::resolve_any(const astring &hostname, + internet_address &to_return) const +{ +// FUNCDEF("resolve_any"); + to_return = internet_address(); + if (!hostname) return false; // blank hostnames go nowhere. + astring full_host; + byte_array ip = full_resolve(hostname, full_host); + if (!ip.length()) return false; + // success then. fill out the address object. + full_host.stuff(to_return.hostname, + internet_address::MAXIMUM_HOSTNAME_LENGTH); + // copy the ip address into our structure. + ip.stuff(internet_address::ADDRESS_SIZE, (abyte *)&to_return.ip_address); + return true; +} + +astring tcpip_stack::dns_resolve(const astring &hostname) const +{ +// FUNCDEF("dns_resolve"); + if (!hostname) return ""; // blank hostnames go nowhere. + if (hostname.iequals("local") || hostname.iequals("localhost")) { + return "127.0.0.1"; + } else if (hostname.iequals("inaddr_any") + || hostname.iequals("any-address")) { + return "0.0.0.0"; + } + // gethostbyname() fills out details about the host, such as its IP address + // and full hostname. + hostent *machine = gethostbyname(hostname.observe()); + if (!machine) { + return ""; + } + byte_array ip(machine->h_length, (abyte *)machine->h_addr); + // copy the ip address into an array for easier manipulation. + astring to_return; + for (int i = 0; i < ip.length(); i++) { + to_return += astring(astring::SPRINTF, "%d", int(ip[i])); + if (i != ip.length() - 1) to_return += "."; + } + return to_return; +} + +/* + +//decide if this should be kept or not. + +internet_address tcpip_stack::deprecated_lookup + (const byte_array &ip_address) +{ + FUNCDEF("deprecated_lookup"); + // lookup the client's hostname through DNS. + hostent *entry = gethostbyaddr((char *)ip_address.observe(), + ip_address.length(), PF_INET); + astring hostname = "unknown_host"; + if (entry) hostname = entry->h_name; + + internet_address to_return; + to_return.fill(ip_address, hostname, 0); + // the zero is above because we don't know the port here. + return to_return; +} +*/ + +machine_uid tcpip_stack::this_host(int location_type) const +{ + switch (location_type) { + case machine_uid::TCPIP_LOCATION: { + astring host = hostname(); + astring full_host; + byte_array ip = full_resolve(host, full_host); + if (!ip.length()) return machine_uid(); // failure. + return internet_machine_uid(full_host, ip); + } + case machine_uid::IPX_LOCATION: { +///uhhh... +return machine_uid(); // return junk. + break; + } + case machine_uid::NETBIOS_LOCATION: { +///uhhh... +return machine_uid(); // return junk. + break; + } + default: +//complain. + return machine_uid(); // return junk. + } +} + +internet_address tcpip_stack::fill_and_resolve(const astring &machine_in, + int port, bool &worked) const +{ + internet_address to_return; + astring machine = machine_in; + machine.to_lower(); + if (!machine) machine = "local"; // assume they mean this machine. + bool resolved = false; // true if we know the name. + byte_array ip_addr; // the ip address we're guessing/decoding/looking up. + bool is_ip = false; // set to true if we have the IP address in ip_addr. + if (machine.iequals("local") || machine.iequals("localhost")) { + // handle our special flag and the normal localhost machine type. + machine = "localhost"; + ip_addr = internet_address::localhost(); + is_ip = true; // we have the address already. + resolved = true; // we know the name also. + } else if (machine.iequals("inaddr_any") + || machine.iequals("any-address")) { + // handle the any address case. + machine = "inaddr_any"; + ip_addr = internet_address::nil_address(); + is_ip = true; // we have the address already. + resolved = true; // we know the name also. + } else { + // well, we now are just going to guess about it being an IP address. + bool all_zeros; + is_ip = internet_address::is_valid_internet_address(machine, ip_addr, + all_zeros); + } + if (is_ip) { + // we try to fill in the hostname if given ip only. +//hmmm: use reverse dns to get the machine name for real! +// for cases where we really had an ip address, using the machine there +// would be a mistake. + if (resolved) to_return.fill(ip_addr, machine, port); + else to_return.fill(ip_addr, astring::empty_string(), port); + resolved = true; // claim we are happy with it now. + } else { + // we try to fill in the ip address for the host. + astring full_host; + ip_addr = full_resolve(machine, full_host); + if (ip_addr.length()) { + to_return.fill(ip_addr, machine, port); + resolved = true; + } + } + worked = resolved; + return to_return; +} + +} //namespace. + + diff --git a/octopi/library/sockets/tcpip_stack.h b/octopi/library/sockets/tcpip_stack.h new file mode 100644 index 00000000..45b1ce89 --- /dev/null +++ b/octopi/library/sockets/tcpip_stack.h @@ -0,0 +1,155 @@ +#ifndef TCPIP_STACK_GROUP +#define TCPIP_STACK_GROUP + +/* +Name : tcpip_stack +Author : Chris Koeritz +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "tcpip_definitions.h" + +#include +#include +#include + +// forward declarations. +struct sockaddr; + +namespace sockets { + +// forward declarations. +class internet_address; +class machine_uid; +class machine_uid_array; + +//! Helpful functions for interacting with TCP/IP stacks. +/*! + This class hides details of the platform specifics of the stack. +*/ + +class tcpip_stack : public virtual basis::root_object +{ +public: + tcpip_stack(); + virtual ~tcpip_stack(); + + bool healthy() const { return _healthy; } + // returns true if the stack seems to be functioning properly. + + DEFINE_CLASS_NAME("tcpip_stack"); + + static basis::astring tcpip_error_name(int error_value); + // returns the name for the "error_value" specified, according to the + // WinSock 1.1 specification. + + basis::astring hostname() const; + // gets the string form of the host's name for tcp/ip. + + machine_uid this_host(int location_type) const; + // returns the unique identifier of "this" host given the "location_type" + // of interest. the type should be a member of the machine_uid:: + // known_location_types enum. + + static sockaddr convert(const internet_address &to_convert); + // returns a low-level address created from our style of address. + static internet_address convert(const sockaddr &to_convert); + // returns our style address from the low-level address. + + basis::byte_array full_resolve(const basis::astring &hostname, basis::astring &full_host) const; + // finds the ip address for a "hostname". the array will have zero + // length on failure. on success, the "full_host" will have the + // possibly more authoratitative name for the host. + + bool resolve_any(const basis::astring &name, internet_address &resolved) const; + // translates "name" into a resolved form, where "name" can be either a + // hostname or an ip address. true is returned on success. + + basis::astring dns_resolve(const basis::astring &hostname) const; + // returns a string form of the IP address for "hostname" or an empty + // string if hostname cannot be found. + + bool enumerate_adapters(structures::string_array &ip_addresses, bool add_local = false) const; + // returns a list of the ip addresses that TCP/IP reports for this machine. + // if there's more than one address, then this machine is multi-homed, + // which could be due to an active dialup networking session or due to + // there being more than one network interface. if the function returns + // false, then tcp/ip failed to report any addresses at all. if the + // "add_local" parameter is true, then the localhost IP address is added + // to the list also. + + internet_address fill_and_resolve(const basis::astring &machine, int port, + bool &worked) const; + // creates an address for TCP/IP given the "machine" and the "port". + // the "machine" can either be in dotted number notation or can be a + // hostname. a special value of "local" or the empty string in "machine" + // causes _this_ host to be used in the address. otherwise, if the + // "machine" is a textual hostname, then it is plugged into the returned + // address and resolved if possible. if the resolution of the "machine" + // is successful, then "worked" is set to true. + + bool enumerate_adapters(machine_uid_array &ip_addresses, + bool add_local = false) const; + // similar to other function of same name but provides a list of + // machine_uid objects. + +private: + bool _healthy; // records if stack started properly. + + static bool initialize_tcpip(); + // starts up the socket mechanisms. true is returned on success. if + // true is returned, each call to initialize must be paired with a call + // to deinitialize. + + static void deinitialize_tcpip(); + // shuts down the socket mechanisms. +}; + +////////////// + +//! Defines our communication related outcome values. + +class communication_commons +{ +public: + enum outcomes { + DEFINE_API_OUTCOME(NO_CONNECTION, -27, "The connection was dropped or " + "could not be made"), + DEFINE_API_OUTCOME(NO_SERVER, -28, "The server is not responding to " + "requests"), + DEFINE_API_OUTCOME(NO_ANSWER, -29, "The server does not seem to be " + "listening for connections"), + DEFINE_API_OUTCOME(SHUTDOWN, -30, "The object has been shut down or was " + "never started up"), + DEFINE_API_OUTCOME(ALREADY_SETUP, -31, "The object has already been setup"), + DEFINE_API_OUTCOME(MEDIUM_ERROR, -32, "The communications medium is in an " + "unusable state currently"), + DEFINE_API_OUTCOME(BAD_MODE, -33, "The transport cannot operate in the " + "mode specified"), + DEFINE_API_OUTCOME(ALREADY_CONNECTED, -34, "This object is already " + "connected"), + DEFINE_API_OUTCOME(WRONG_ENTITY, -35, "This is the wrong entity type for " + "the request"), + DEFINE_API_OUTCOME(IPC_ERROR, -36, "An error has occurred in interprocess " + "communication"), + DEFINE_API_OUTCOME(TOO_NOISY, -37, "The communications medium is currently " + "too noisy to be used"), + DEFINE_API_OUTCOME(COMM_ERROR, -38, "There was an unspecified " + "communication error") + }; + + static const char *outcome_name(const basis::outcome &to_name); + // returns a string representation of the outcome "to_name" if it's a + // member of the communication_commons::outcomes enum. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/sockets/throughput_counter.cpp b/octopi/library/sockets/throughput_counter.cpp new file mode 100644 index 00000000..3e952be7 --- /dev/null +++ b/octopi/library/sockets/throughput_counter.cpp @@ -0,0 +1,135 @@ + + + +/*****************************************************************************\ +* * +* Name : throughput_counter * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "throughput_counter.h" + +#include + +using namespace basis; +using namespace timely; + +namespace sockets { + +throughput_counter::throughput_counter() +: _running(false), + _start(new time_stamp), + _end(new time_stamp), + _time_overall(0), + _byte_count(0), + _send_count(0) +{} + +throughput_counter::throughput_counter(const throughput_counter &to_copy) +: _start(new time_stamp), + _end(new time_stamp) +{ + *this = to_copy; +} + +throughput_counter::~throughput_counter() +{ + _running = false; + WHACK(_start); + WHACK(_end); +} + +throughput_counter &throughput_counter::operator = + (const throughput_counter &to_copy) +{ + if (this == &to_copy) return *this; // bail on copying to self. + _running = to_copy._running; + *_start = *to_copy._start; + *_end = *to_copy._end; + _time_overall = to_copy._time_overall; + _byte_count = to_copy._byte_count; + _send_count = to_copy._send_count; + return *this; +} + +void throughput_counter::combine(const throughput_counter &to_blend) +{ + if (this == &to_blend) return; // no, we don't like that. + _time_overall += to_blend._time_overall; + _byte_count += to_blend._byte_count; + _send_count += to_blend._send_count; +} + +void throughput_counter::start() +{ + if (running()) return; // can't start if already started. + *_start = time_stamp(); + *_end = time_stamp(); // just to clear. + _running = true; +} + +void throughput_counter::stop() +{ + if (!running()) return; // better have been started before stopping. + *_end = time_stamp(); + _time_overall += _end->value() - _start->value(); + _running = false; +} + +void throughput_counter::reset() +{ + _running = false; + _start->reset(); + _end->reset(); + _time_overall = 0; + _byte_count = 0; + _send_count = 0; +} + +void throughput_counter::send(double size_of_send) +{ + if (!running()) return; // can't add if we're not in a run. + _send_count++; + _byte_count += size_of_send; +} + +void throughput_counter::add_run(double size_of_send, double time_of_send, + double number_of_runs) +{ + _send_count += number_of_runs; + _byte_count += size_of_send; + _time_overall += time_of_send; +} + +time_stamp throughput_counter::start_time() const { return *_start; } + +time_stamp throughput_counter::stop_time() const { return *_end; } + +double throughput_counter::total_time() const +{ + double extra_time = running()? time_stamp().value() - _start->value() : 0; + return _time_overall + extra_time; +} + +double throughput_counter::bytes_per_second() const +{ + double total = total_time() / SECOND_ms; + return double(bytes_sent()) / total; +} + +double throughput_counter::kilobytes_per_second() const +{ return bytes_per_second() / double(KILOBYTE); } + +double throughput_counter::megabytes_per_second() const +{ return kilobytes_per_second() / double(KILOBYTE); } + +} //namespace. + diff --git a/octopi/library/sockets/throughput_counter.h b/octopi/library/sockets/throughput_counter.h new file mode 100644 index 00000000..c5deee80 --- /dev/null +++ b/octopi/library/sockets/throughput_counter.h @@ -0,0 +1,113 @@ +#ifndef THROUGHPUT_COUNTER_CLASS +#define THROUGHPUT_COUNTER_CLASS + +/*****************************************************************************\ +* * +* Name : throughput_counter * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace sockets { + +//! Reports on average bandwidth of the transfers being measured. +/*! + Tracks the amount of data sent over a period of time and provides + statistics about the transfer rate. +*/ + +class throughput_counter +{ +public: + throughput_counter(); + throughput_counter(const throughput_counter &to_copy); + ~throughput_counter(); + + throughput_counter &operator =(const throughput_counter &to_copy); + + void start(); + //!< begins timing a run. + /*!< the current time is recorded and any data sent will be tracked until + stop() is invoked. results from previous runs will be merged with the + current run. */ + + void stop(); + //!< ends the current run. + /*!< the report functions provide information about the speed achieved + over this and previous runs. */ + + void reset(); + //!< clears all statistics and starts over. + + void combine(const throughput_counter &to_blend); + //!< incorporates the statistics from "to_blend" into this counter. + /*!< the stats in "to_blend" then no longer need to be considered, + since this object records its own plus the blended statistics. note + that makes the most sense if both this and "to_blend" are not currently + running a simulation, although combining running counters is not + prohibited. if either counter is running, those current runs are + ignored and only accumulated stats are combined. */ + + void send(double size_of_send); + //!< records a sending of "size_of_send" bytes. + /*!< this should only be called while a test run is being made; the send + will be ignored if a run is not occurring. */ + + void add_run(double size_of_send, double time_of_send, + double number_of_runs = 1.0); + //!< records a run without changing the state of the current run. + /*!< this supports adding a timed run to the counter without requiring that + start and stop be used. this will work whether a run is currently + being timed or not. */ + + bool running() const { return _running; } + //!< returns whether a test run is being worked on or not. + + timely::time_stamp start_time() const; + //!< reports the time when this run started. + /*!< this and stop_time() report the timing information for the current + run, and so are only really relevant when a run is occurring. */ + timely::time_stamp stop_time() const; + //!< reports the time when this run was stopped. + + double bytes_sent() const { return _byte_count; } + //!< returns the number of bytes sent so far. + /*!< bytes_sent() and number_of_sends() work at any point during a test + run to provide an interim measurement. however after a test run, they + report the statistics for the entire history of testing. */ + double number_of_sends() const { return _send_count; } + //!< returns the number of sends that have occurred. + + double bytes_per_second() const; + //!< returns the number of bytes that transfers are getting per second. + double kilobytes_per_second() const; + //!< returns the number of kilobytes that transfers are getting per second. + double megabytes_per_second() const; + //!< returns the number of megabytes that transfers are getting per second. + + double total_time() const; + //!< the run time so far, in milliseconds. + /*!< this also counts the time in the current run, if one is occurring. */ + +private: + bool _running; //!< true if we're currently testing. + timely::time_stamp *_start; //!< when the current run was started. + timely::time_stamp *_end; //!< when the run was stopped. + double _time_overall; //!< how much time has been accumulated. + double _byte_count; //!< the amount of data sent so far. + double _send_count; //!< the number of times data has been sent. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/synchronic/bundle_list.h b/octopi/library/synchronic/bundle_list.h new file mode 100644 index 00000000..229fda91 --- /dev/null +++ b/octopi/library/synchronic/bundle_list.h @@ -0,0 +1,31 @@ +#ifndef BUNDLE_LIST_CLASS +#define BUNDLE_LIST_CLASS + +/*****************************************************************************\ +* * +* Name : bundle_list * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "synchronizable.h" + +#include + +namespace synchronic { + +//! Provides a structure for managing a collection of synchronizables. + +class bundle_list : public structures::amorph {}; + +} //namespace. + +#endif + diff --git a/octopi/library/synchronic/list_manager.cpp b/octopi/library/synchronic/list_manager.cpp new file mode 100644 index 00000000..2040295d --- /dev/null +++ b/octopi/library/synchronic/list_manager.cpp @@ -0,0 +1,208 @@ +/*****************************************************************************\ +* * +* Name : list_manager * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "bundle_list.h" +#include "list_manager.h" + +#include +#include +#include +#include + +using namespace basis; +using namespace octopi; +using namespace structures; +using namespace timely; + +namespace synchronic { + +//#define DEBUG_LIST_MANAGER + // uncomment for noisier version. + +#undef GRAB_LOCK +#define GRAB_LOCK \ + auto_synchronizer l(*_locking) + +#undef LOG +#define LOG(to_print) \ + CLASS_EMERGENCY_LOG(program_wide_logger::get(), to_print) + +list_manager::list_manager(const string_array &list_name, bool backgrounded) +: tentacle(list_name, backgrounded), + _entries(new bundle_list), + _locking(new mutex) +{ +} + +list_manager::~list_manager() +{ + WHACK(_entries); + WHACK(_locking); +} + +const string_array &list_manager::list_name() const { return group(); } + +int list_manager::entries() const +{ + GRAB_LOCK; + return _entries->elements(); +} + +void list_manager::reset() +{ + GRAB_LOCK; + _entries->zap(0, _entries->elements() - 1); +} + +bool list_manager::is_listed(const string_array &classifier) +{ + GRAB_LOCK; + int indy = locked_find(classifier); + return !negative(indy); +} + +bool list_manager::update(const string_array &classifier, int offset) +{ + GRAB_LOCK; + int indy = locked_find(classifier); + if (negative(indy)) return false; // not found. + _entries->borrow(indy)->_updated = time_stamp(offset); + return true; +} + +void list_manager::clean(int older_than) +{ + GRAB_LOCK; + for (int i = 0; i < _entries->elements(); i++) { + synchronizable *curr = _entries->borrow(i); + if (curr->_updated < time_stamp(-older_than)) { + // this one is too old to keep around. + _entries->zap(i, i); + i--; // skip back before deleted item. + } + } +} + +bool list_manager::zap(const string_array &classifier) +{ + GRAB_LOCK; + int indy = locked_find(classifier); + if (negative(indy)) return false; // not found. + _entries->zap(indy, indy); + return true; // did find and whack it. +} + +int list_manager::locked_find(const string_array &classifier) +{ + for (int i = 0; i < _entries->elements(); i++) { + // check that the classifier lengths are equal; otherwise no match. + if (_entries->get(i)->classifier().length() != classifier.length()) + continue; + // starting from the end of most significance, we compare the strings. + // we don't want to bother comparing the end that's most likely to be + // the same for items in the list (the front, that is). + bool problems = false; + for (int j = classifier.length() - 1; j >= 0; j--) { + if (_entries->get(i)->classifier()[j] != classifier[j]) { + problems = true; + break; // get out now since we're hosed. + } + } + if (problems) continue; // nope, there was a mismatch. + // success; this guy matches. + return i; + } + return common::NOT_FOUND; // not found. +} + +synchronizable *list_manager::clone_object(const string_array &classifier) +{ + GRAB_LOCK; + int indy = locked_find(classifier); + if (negative(indy)) return NIL; + return dynamic_cast(_entries->get(indy)->clone()); +} + +void list_manager::retrieve(bundle_list &to_fill) const +{ + to_fill.reset(); + GRAB_LOCK; + for (int i = 0; i < _entries->elements(); i++) + to_fill += dynamic_cast(_entries->get(i)->clone()); +} + +outcome list_manager::consume(infoton &to_chow, + const octopus_request_id &formal(item_id), byte_array &transformed) +{ +#ifdef DEBUG_LIST_MANAGER + FUNCDEF("consume"); +#endif + transformed.reset(); + synchronizable *bun = dynamic_cast(&to_chow); + if (!bun) return BAD_INPUT; + + GRAB_LOCK; + + // now perform an appropriate action depending on the type of update. + switch (bun->_mod) { + case synchronizable::ADDED: + case synchronizable::CHANGED: { + // see if the item already exists; if it does, overwrite it. + int indy = locked_find(bun->classifier()); + if (negative(indy)) { + // the item is new, so just drop it in the list. + *_entries += dynamic_cast(bun->clone()); + } else { + // not a new item, so merge with the existing contents. + _entries->borrow(indy)->merge(*bun); + _entries->borrow(indy)->_updated = time_stamp(); + } + return OKAY; + } + case synchronizable::DELETED: { + int indy = locked_find(bun->classifier()); + if (non_negative(indy)) { + // found it, so whack the entry as needed by calling merge. + outcome ret = _entries->borrow(indy)->merge(*bun); + _entries->borrow(indy)->_updated = time_stamp(); + if (ret == synchronizable::EMPTY) { + // they have told us that this must go now. +#ifdef DEBUG_LIST_MANAGER + LOG(astring("removing entry now due to merge outcome: ") + + _entries->borrow(indy)->text_form()); +#endif + _entries->zap(indy, indy); + } + return OKAY; + } else { + // that item was not listed. +#ifdef DEBUG_LIST_MANAGER + LOG(astring("could not find entry for ") + bun->text_form()); +#endif + return NOT_FOUND; + } + break; + } + default: return NO_HANDLER; + } + return OKAY; +} + +void list_manager::expunge(const octopus_entity &formal(to_remove)) +{ +// FUNCDEF("expunge"); +} + +} //namespace. + diff --git a/octopi/library/synchronic/list_manager.h b/octopi/library/synchronic/list_manager.h new file mode 100644 index 00000000..eee70942 --- /dev/null +++ b/octopi/library/synchronic/list_manager.h @@ -0,0 +1,104 @@ +#ifndef LIST_MANAGER_CLASS +#define LIST_MANAGER_CLASS + +/*****************************************************************************\ +* * +* Name : list_manager * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace synchronic { + +// forward. +class synchronizable; +class bundle_list; + +//! Supports distributed management of a list of object states. +/*! + An object may have a collection of attributes which are important to keep + up to date. The list_manager provides a means to keep that information + relevant given periodic updates to the state information by the entity in + charge of the actual object. +*/ + +class list_manager : public octopi::tentacle +{ +public: + list_manager(const structures::string_array &list_name, bool backgrounded); + // given the root of the "list_name", this will administrate the list. + // all entries in the list must have "list_name" as their prefix. + // if "backgrounded" is true, then this list will consume its requests + // on a thread. otherwise it deals with them immediately. + + virtual ~list_manager(); + + int entries() const; // returns the number of items held here. + + const structures::string_array &list_name() const; + // returns the list name this was constructed with (which is the same + // as the group for this object). + + bool is_listed(const structures::string_array &classifier); + // returns true if the object with "classifier" exists in the list. + + bool update(const structures::string_array &classifier, int offset = 0); + // returns true if the object with "classifier" could be found and its + // last update timestamp set to the current time if "offset" is zero. + // if "offset" is negative, then the time will be updated to some time + // before now. if positive, then it's updated to a time after now. + + void clean(int older_than); + // flushes out any items that haven't been updated in "older_than" + // microseconds. + + synchronizable *clone_object(const structures::string_array &classifier); + // returns a clone of the object listed for "classifier" or NIL if there + // are none matching. the returned object must be destroyed if non-NIL. + + bool zap(const structures::string_array &classifier); + // returns true if we were able to find and remove the item held under + // the "classifier". + + void retrieve(bundle_list &to_fill) const; + // loads "to_fill" with a copy of the current set of attribute bundles. + + void reset(); + // wipes out all objects that used to be listed. + + virtual basis::outcome consume(octopi::infoton &to_chow, + const octopi::octopus_request_id &item_id, + basis::byte_array &transformed); + // processes a request encapsulated in "to_chow". the "item_id" is + // currently unused. + + virtual basis::outcome reconstitute(const structures::string_array &classifier, + basis::byte_array &packed_form, octopi::infoton * &reformed) = 0; + // recovers the original form "reformed" from a "packed_form" of the + // object. this must be provided by derived list_manager objects. + + virtual void expunge(const octopi::octopus_entity &to_remove); + // cleans out any items held for the entity "to_remove". + +private: + bundle_list *_entries; // the set of elements that make up our list. + basis::mutex *_locking; // protects our contents. + + int locked_find(const structures::string_array &classifier); + // locates the item with the "classifier" in this list. if it's present, + // a non-negative index number is returned. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/synchronic/list_synchronizer.cpp b/octopi/library/synchronic/list_synchronizer.cpp new file mode 100644 index 00000000..1d185342 --- /dev/null +++ b/octopi/library/synchronic/list_synchronizer.cpp @@ -0,0 +1,73 @@ +/*****************************************************************************\ +* * +* Name : list_synchronizer * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "list_manager.h" +#include "list_synchronizer.h" + +#include +#include + +using namespace basis; +using namespace structures; +using namespace textual; + +namespace synchronic { + +const int MAX_PER_ENT = 10 * MEGABYTE; + // our arbitrary limit for how much we allow the entity data bin to store. + +list_synchronizer::list_synchronizer() +: octopus(string_manipulation::make_random_name(), MAX_PER_ENT) +{ +} + +list_synchronizer::~list_synchronizer() +{ +} + +outcome list_synchronizer::add_list(list_manager *to_add) +{ return add_tentacle(to_add); } + +outcome list_synchronizer::zap_list(const string_array &list_name) +{ return zap_tentacle(list_name); } + +bool list_synchronizer::update(const string_array &object_id) +{ + lock_tentacles(); + bool to_return = false; + for (int i = 0; i < locked_tentacle_count(); i++) { + list_manager *t = dynamic_cast(locked_get_tentacle(i)); + if (!t) continue; + if (t->list_name().prefix_compare(object_id)) { + // this is the right one to ask about the object. + to_return = t->update(object_id); + break; + } + } + unlock_tentacles(); + return to_return; +} + +void list_synchronizer::clean(int older_than) +{ + lock_tentacles(); + for (int i = 0; i < locked_tentacle_count(); i++) { + list_manager *t = dynamic_cast(locked_get_tentacle(i)); + if (t) t->clean(older_than); + } + unlock_tentacles(); +} + +} //namespace. + diff --git a/octopi/library/synchronic/list_synchronizer.h b/octopi/library/synchronic/list_synchronizer.h new file mode 100644 index 00000000..7f48e79d --- /dev/null +++ b/octopi/library/synchronic/list_synchronizer.h @@ -0,0 +1,53 @@ +#ifndef LIST_SYNCHRONIZER_CLASS +#define LIST_SYNCHRONIZER_CLASS + +/*****************************************************************************\ +* * +* Name : list_synchronizer * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Manages a collection of lists of synchronizable state information. * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace synchronic { + +// forward. +class list_manager; + +class list_synchronizer : public octopi::octopus +{ +public: + list_synchronizer(); + ~list_synchronizer(); + + basis::outcome add_list(list_manager *to_add); + // adds a new list synchronization manager "to_add" to the crew of lists. + + basis::outcome zap_list(const structures::string_array &list_name); + // takes a list registered under "list_name" back out of the synchronizer. + // the list_manager for the "list_name" is destroyed on success. + + void clean(int older_than); + // cleans out any items that are older than the "older_than" number of + // milliseconds. + + bool update(const structures::string_array &object_id); + // marks the item specified by the "object_id" as updated. +}; + +} + +#endif + diff --git a/octopi/library/synchronic/makefile b/octopi/library/synchronic/makefile new file mode 100644 index 00000000..316e18d0 --- /dev/null +++ b/octopi/library/synchronic/makefile @@ -0,0 +1,11 @@ +CONSOLE_MODE = true + +include cpp/variables.def + +PROJECT = list_synchronizer +TYPE = library +SOURCE = list_manager.cpp list_synchronizer.cpp +TARGETS = list_synchronizer.lib + +include cpp/rules.def + diff --git a/octopi/library/synchronic/synchronizable.h b/octopi/library/synchronic/synchronizable.h new file mode 100644 index 00000000..82db767a --- /dev/null +++ b/octopi/library/synchronic/synchronizable.h @@ -0,0 +1,101 @@ +#ifndef SYNCHRONIZABLE_CLASS +#define SYNCHRONIZABLE_CLASS + +/* +* Name : synchronizable +* Author : Chris Koeritz +*** +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include + +namespace synchronic { + +//! Encapsulates all of the attributes known for an object. +/*! + This relies on the naming scheme for infotons, so objects are unique + only so far as their classifiers are different. For example, if the objects + are to be differentiated by the computer that they run on, then some + unique form of that computer's name should be used as one of the components + of the classifier. Each object can hold a variety of information which + is not defined here. Instead, we require the merge() method that + performs object specific reconciliation when an update arrives. +*/ + +class synchronizable : public octopi::infoton +{ +public: + enum modifications { + ADDED, // the object is new. + CHANGED, // the object has been modified. + DELETED // the object got removed. + }; + + modifications _mod; + // the type of change that has happened for this object. the derived + // class must pack this information in the derived pack() method and + // retrieve the modification info in unpack(). there are helper functions + // pack_mod() and unpack_mod() to automate that responsibility. + + timely::time_stamp _updated; + // when this information was last updated. this should not be packed, + // since it is only locally relevant. + + synchronizable(const structures::string_array &object_id) : infoton(object_id) {} + // constructs the base portion of an attribute bundle for an object with + // the "object_id". the "object_id" must follow the rules for infoton + // classifiers. the last string in the object id is the list-unique + // identifier for this set of attributes. + + enum outcomes { + OKAY = basis::common::OKAY, // the operation completed successfully. + BAD_TYPE = basis::common::BAD_TYPE, // provided object had an incompatible type. + EMPTY = basis::common::IS_EMPTY // the merge resulted in clearing this entry. + }; + + // helper functions for packing the modification information. + void pack_mod(basis::byte_array &packed_form) const; + bool unpack_mod(basis::byte_array &packed_form); + int packed_mod_size() const { return sizeof(int); } + //!< returns the size of the packed modifier. + + virtual basis::outcome merge(const synchronizable &to_merge) = 0; + // overwrites any attributes in "this" bundle with the contents found + // in "to_merge". this can fail if the object types are different. + + virtual basis::astring text_form() const = 0; + // provides a visual form of the data held in this bundle. + + // promote requirements of the infoton to derived objects. + virtual void pack(basis::byte_array &packed_form) const = 0; + virtual bool unpack(basis::byte_array &packed_form) = 0; + virtual clonable *clone() const = 0; + virtual int packed_size() const = 0; +}; + +////////////// + +// implementations. + +void synchronizable::pack_mod(basis::byte_array &packed_form) const +{ structures::attach(packed_form, int(_mod)); } + +bool synchronizable::unpack_mod(basis::byte_array &packed_form) +{ + int temp; + if (!structures::detach(packed_form, temp)) return false; + _mod = (modifications)temp; + return true; +} + +} //namespace. + +#endif + diff --git a/octopi/library/tentacles/encryption_infoton.cpp b/octopi/library/tentacles/encryption_infoton.cpp new file mode 100644 index 00000000..7cec48c7 --- /dev/null +++ b/octopi/library/tentacles/encryption_infoton.cpp @@ -0,0 +1,156 @@ +/*****************************************************************************\ +* * +* Name : encryption_infoton * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "encryption_infoton.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace crypto; +using namespace octopi; +using namespace structures; +using namespace textual; + +namespace octopi { + +const int encryption_infoton::BLOWFISH_KEY_SIZE = 314; + // our key size is almost double the recommended key size (168 bits). + // this would take a very long time to crack using brute force. + +const int encryption_infoton::RSA_KEY_SIZE = 1480; + // a little bit larger than the 1024 bit threshold. + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s); + +encryption_infoton::encryption_infoton(const byte_array &pub_key, + const byte_array &secret_blowfish) +: infoton(encryption_classifier()), + _public_key(pub_key), + _encrypted_blowfish_key(secret_blowfish), + _success(tentacle::NOT_FOUND) +{} + +encryption_infoton::encryption_infoton(const encryption_infoton &to_copy) +: root_object(), + infoton(to_copy), + _public_key(to_copy._public_key), + _encrypted_blowfish_key(to_copy._encrypted_blowfish_key), + _success(to_copy._success) +{ +} + +encryption_infoton::~encryption_infoton() {} + +clonable *encryption_infoton::clone() const +{ return cloner(*this); } + +encryption_infoton &encryption_infoton::operator = + (const encryption_infoton &to_copy) +{ + if (this == &to_copy) return *this; + _public_key = to_copy._public_key; + _encrypted_blowfish_key = to_copy._encrypted_blowfish_key; + _success = to_copy._success; + return *this; +} + +const char *encryption_class_constant = "#octcod"; + +SAFE_STATIC_CONST(string_array, encryption_infoton::encryption_classifier, + (1, &encryption_class_constant)) + +int encryption_infoton::packed_size() const +{ + return sizeof(int) // packed outcome. + + _public_key.length() + sizeof(int) // public key array. + + _encrypted_blowfish_key.length() + sizeof(int); // secret key array. +} + +void encryption_infoton::pack(byte_array &packed_form) const +{ + structures::attach(packed_form, _success.value()); + structures::attach(packed_form, _public_key); + structures::attach(packed_form, _encrypted_blowfish_key); +} + +bool encryption_infoton::unpack(byte_array &packed_form) +{ + int value; + if (!structures::detach(packed_form, value)) return false; + _success = outcome(value); + if (!structures::detach(packed_form, _public_key)) return false; + if (!structures::detach(packed_form, _encrypted_blowfish_key)) return false; + return true; +} + +outcome encryption_infoton::prepare_blowfish_key(blowfish_crypto &new_key) +{ +// FUNCDEF("prepare_blowfish_key"); + _encrypted_blowfish_key.reset(); // clean out stuff to create. + if (!_public_key.length()) { + // wrong type of request being seen or something. + _success = tentacle::BAD_INPUT; + return _success; + } + + rsa_crypto pub(_public_key); // suck in the provided key. + blowfish_crypto agreed_key(BLOWFISH_KEY_SIZE); // random blowfish key. + new_key = agreed_key; + + // now encrypt the new key for transit. + bool worked = pub.public_encrypt(agreed_key.get_key(), + _encrypted_blowfish_key); + if (!worked) _success = tentacle::GARBAGE; // lacking a better description. + else _success = tentacle::OKAY; + return _success; +} + +outcome encryption_infoton::prepare_both_keys(rsa_crypto &private_key) +{ + rsa_crypto priv(RSA_KEY_SIZE); // generate random key. + outcome to_return = prepare_public_key(priv); + if (to_return == tentacle::OKAY) private_key = priv; + return to_return; +} + +outcome encryption_infoton::prepare_public_key(const rsa_crypto &private_key) +{ + bool worked = private_key.public_key(_public_key); + if (!worked) return tentacle::DISALLOWED; // why would that ever fail? + return tentacle::OKAY; +} + +outcome encryption_infoton::extract_response(const rsa_crypto &private_key, + blowfish_crypto &new_key) const +{ +// FUNCDEF("extract_response"); + if (_success != tentacle::OKAY) return _success; + byte_array decrypted; + bool worked = private_key.private_decrypt(_encrypted_blowfish_key, decrypted); + if (!worked) return tentacle::BAD_INPUT; // that one we hope is accurate. + new_key.set_key(decrypted, BLOWFISH_KEY_SIZE); + return tentacle::OKAY; +} + +} //namespace. + diff --git a/octopi/library/tentacles/encryption_infoton.h b/octopi/library/tentacles/encryption_infoton.h new file mode 100644 index 00000000..c9a67c2c --- /dev/null +++ b/octopi/library/tentacles/encryption_infoton.h @@ -0,0 +1,99 @@ +#ifndef ENCRYPTION_INFOTON_CLASS +#define ENCRYPTION_INFOTON_CLASS + +/*****************************************************************************\ +* * +* Name : encryption_infoton * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include + +namespace octopi { + +//! Encapsulates the chit-chat necessary to establish an encrypted connection. +/*! + This is framed in terms of a client and a server, where the client creates + a private key and gives the server the public key. The server side creates + a blowfish key and encrypts it using the public key. +*/ + +class encryption_infoton : public infoton +{ +public: + basis::byte_array _public_key; + //!< valid during the request stage of encryption. + /*!< this is used when the client is telling the server how to talk to + it to provide the key. */ + basis::byte_array _encrypted_blowfish_key; + //!< valid during the response stage of encryption. + /*!< this is used when the server reports a blowfish key that it will + use on this connection with the client. */ + + basis::outcome _success; //!< did the request succeed? + + encryption_infoton(const basis::byte_array &public_key = basis::byte_array::empty_array(), + const basis::byte_array &encrypted_blowfish_key = basis::byte_array::empty_array()); + encryption_infoton(const encryption_infoton &to_copy); + + virtual ~encryption_infoton(); + + DEFINE_CLASS_NAME("encryption_infoton"); + + static const int RSA_KEY_SIZE; + //!< this key size should be used for all RSA private keys. + static const int BLOWFISH_KEY_SIZE; + //!< this will be used for blowfish keys that this object generates. + + void text_form(basis::base_string &fill) const { + fill.assign(basis::astring(class_name())); // low exposure for vital held info. + } + + encryption_infoton &operator =(const encryption_infoton &to_copy); + + basis::outcome prepare_blowfish_key(crypto::blowfish_crypto &new_key); + //!< performs the server side's job on the current key. + /*!< the public key had better be set already or this will fail. the + "new_key" will always be used to communicate with the client after this. + */ + + basis::outcome prepare_public_key(const crypto::rsa_crypto &private_key); + //!< prepares the request side for a client. + /*!< the rsa public key will be generated from the "private_key". */ + + basis::outcome prepare_both_keys(crypto::rsa_crypto &private_key); + //!< sets up both keys by randomly generating the "private_key". + + basis::outcome extract_response(const crypto::rsa_crypto &private_key, + crypto::blowfish_crypto &new_key) const; + //!< used by the client to extract the shared blowfish key from the server. + /*!< using the private key, the server's response is decrypted and stored + in "new_key". note that this will only succeed if the _success member + is OKAY. otherwise it means the server has beefed on the request. */ + + static const structures::string_array &encryption_classifier(); + //!< returns the classifier for this type of infoton. + + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); + + virtual clonable *clone() const; + + virtual int packed_size() const; +}; + +} //namespace. + +#endif // outer guard. + diff --git a/octopi/library/tentacles/encryption_tentacle.cpp b/octopi/library/tentacles/encryption_tentacle.cpp new file mode 100644 index 00000000..227ff71d --- /dev/null +++ b/octopi/library/tentacles/encryption_tentacle.cpp @@ -0,0 +1,186 @@ +/*****************************************************************************\ +* * +* Name : encryption_tentacle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "encryption_tentacle.h" +#include "encryption_wrapper.h" +#include "key_repository.h" + +#include +#include +#include +#include +#include + +using namespace basis; +using namespace crypto; +using namespace loggers; +using namespace structures; +using namespace textual; + +namespace octopi { + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +#define DEBUG_ENCRYPTION_TENTACLE + // uncomment for noisier code. + +////////////// + +encryption_tentacle::encryption_tentacle() +: tentacle_helper + (encryption_infoton::encryption_classifier(), false), + _server_side(true), + _keys(new key_repository), + _rsa_private(NIL) +{ +} + +encryption_tentacle::encryption_tentacle(const byte_array &private_key) +: tentacle_helper + (encryption_infoton::encryption_classifier(), false), + _server_side(false), + _keys(new key_repository), + _rsa_private(new rsa_crypto(private_key)) +{ +} + +encryption_tentacle::encryption_tentacle(int key_size) +: tentacle_helper + (encryption_infoton::encryption_classifier(), false), + _server_side(false), + _keys(new key_repository), + _rsa_private(new rsa_crypto(key_size)) +{ +} + +encryption_tentacle::~encryption_tentacle() +{ + WHACK(_rsa_private); + WHACK(_keys); +} + +key_repository &encryption_tentacle::keys() const { return *_keys; } + +const rsa_crypto &encryption_tentacle::private_key() const +{ return *_rsa_private; } + +outcome encryption_tentacle::reconstitute(const string_array &classifier, + byte_array &packed_form, infoton * &reformed) +{ + if (classifier != encryption_infoton::encryption_classifier()) + return NO_HANDLER; + + return reconstituter(classifier, packed_form, reformed, + (encryption_infoton *)NIL); +} + +void encryption_tentacle::expunge(const octopus_entity &formal(to_remove)) +{ +//// _keys->whack(to_remove); +//we need a better approach. it seems there are places where an entity +//can get reused and it still expects its key to be present. +} + +outcome encryption_tentacle::consume(infoton &to_chow, + const octopus_request_id &item_id, byte_array &transformed) +{ + FUNCDEF("consume"); + transformed.reset(); + encryption_infoton *inf = dynamic_cast(&to_chow); + if (!inf) { + // this package is not explicitly an encryption infoton. we need to + // decrypt it using what we already know. + + encryption_wrapper *wrap = dynamic_cast(&to_chow); + if (!wrap) { +#ifdef DEBUG_ENCRYPTION_TENTACLE +// LOG(astring("got a stray infoton that was not encrypted: ") +// + to_chow.text_form()); +#endif + // this signals that we were expecting an encrypted package. + return ENCRYPTION_MISMATCH; + } + + octenc_key_record record; + octenc_key_record *rec = _keys->lock(item_id._entity); + if (!rec) { +#ifdef DEBUG_ENCRYPTION_TENTACLE + LOG(astring("no key stored for entity ") + + item_id._entity.mangled_form() + + "; rejecting packet."); +#endif + return DISALLOWED; + } + record = *rec; + _keys->unlock(rec); + + byte_array decro; + bool decrypts_properly = record._key.decrypt(wrap->_wrapped, decro); + if (decrypts_properly) { + // this package seems to be intact. we need to reconstitute the + // original infoton. + transformed = decro; // set the decrypted blob. + return PARTIAL; + } + +#ifdef DEBUG_ENCRYPTION_TENTACLE + LOG(astring("denying client ") + item_id._entity.mangled_form() + + " due to erroneous decryption"); +#endif + + // the infoton's client is not authorized; it needs to be dropped. + return DISALLOWED; + } + + // reaching here means this is explicitly an encryption startup request. + + if (!_server_side) { + // client's side must track the key we were given for decryption. we'll + // use that from now on. + blowfish_crypto new_key(blowfish_crypto::minimum_key_size()); // bogus. + outcome ret = inf->extract_response(*_rsa_private, new_key); + if (ret != OKAY) { +#ifdef DEBUG_ENCRYPTION_TENTACLE + LOG(astring("client failed to process encrypted blowfish key for ") + + item_id._entity.mangled_form()); +#endif + } else { + _keys->add(item_id._entity, new_key); // add our key for this guy. + } + // we do not store a copy of the infoton; it's just done now. + return ret; + } else { + // server's side need to process a key request and send it back using + // the public key the requester provided. + blowfish_crypto agreed_key(blowfish_crypto::minimum_key_size()); + // initialized with junk. + outcome worked = inf->prepare_blowfish_key(agreed_key); + if (worked != OKAY) { +#ifdef DEBUG_ENCRYPTION_TENTACLE + LOG(astring("server failed to encrypt blowfish key for ") + + item_id._entity.mangled_form()); +#endif + } else { + _keys->add(item_id._entity, agreed_key); // add our key for this guy. + } + } + + if (!store_product(dynamic_cast(inf->clone()), item_id)) + return NO_SPACE; + return OKAY; +} + +} //namespace. + diff --git a/octopi/library/tentacles/encryption_tentacle.h b/octopi/library/tentacles/encryption_tentacle.h new file mode 100644 index 00000000..362da6e8 --- /dev/null +++ b/octopi/library/tentacles/encryption_tentacle.h @@ -0,0 +1,100 @@ +#ifndef ENCRYPTION_TENTACLE_CLASS +#define ENCRYPTION_TENTACLE_CLASS + +/*****************************************************************************\ +* * +* Name : encryption_tentacle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "encryption_infoton.h" + +#include + +namespace octopi { + +// forward. +class key_repository; + +//! Processes the encryption_infoton object for setting up an encrypted channel. + +/*! + NOTE: + to use encryption, both the client and the server need to have an + encryption_tentacle added as a filter. it should be the first filter + added by users and it must be before any security tentacles (otherwise, + the security info would not be encrypted). + further, an unwrapping_tentacle (see encryption_wrapper.h) must also + be added. it must *not* be added as a filter. this is what allows the + octopus to reconstitute the encoded infotons when encryption is active. +*/ + +class encryption_tentacle +: public tentacle_helper +{ +public: + encryption_tentacle(); + //!< this tentacle will implement the server side. + /*!< it will expect only to see public keys from clients and to respond + with encrypted blowfish keys. */ + + encryption_tentacle(const basis::byte_array &rsa_key); + //!< this is the client side tentacle. + /*!< it will only deal with unwrapping a server's response with the + encrypted blowfish key. the "rsa_key" is the private key that will be + used for decrypting the key response. */ + + encryption_tentacle(int key_size); + //!< automatically creates a private key of the "key_size". + /*!< this is for use by the client side's encryption needs. */ + + virtual ~encryption_tentacle(); + + DEFINE_CLASS_NAME("encryption_tentacle"); + + virtual basis::outcome reconstitute(const structures::string_array &classifier, + basis::byte_array &packed_form, infoton * &reformed); + //!< recreates a "reformed" infoton from a packed form. + /*!< the "classifier" is provided as well as the packed infoton data + in "packed_form". this will only succeed if the classifier's first name + is understood here. */ + + virtual basis::outcome consume(infoton &to_chow, const octopus_request_id &item_id, + basis::byte_array &transformed); + //!< the base class handles the processing of the request in "to_chow". + /*!< it will generally perform all the services needed to start + the encrypted connection up. the "transformed" array will be filled + with the actual infoton if decryption is successful. if the outcome + is ENCRYPTION_MISMATCH, then the infoton is not encrypted but was + expected to be. */ + + virtual void expunge(const octopus_entity &to_remove); + //!< throws out any keys we were maintaining for this entity. + + key_repository &keys() const; + //!< provides access to our list of keys. + /*!< this is very private info, but it's needed for encrypting items + going back to the client. */ + + const crypto::rsa_crypto &private_key() const; + //!< provides access to the key held here. + /*!< this is an important object; do not expose it externally. */ + +private: + bool _server_side; //!< true if we're acting as a server. + key_repository *_keys; //!< our table of keys that we've agreed on. + crypto::rsa_crypto *_rsa_private; //!< the private key for a client side. +}; + +} //namespace. + +#endif // outer guard. + diff --git a/octopi/library/tentacles/encryption_wrapper.cpp b/octopi/library/tentacles/encryption_wrapper.cpp new file mode 100644 index 00000000..8ee8ee09 --- /dev/null +++ b/octopi/library/tentacles/encryption_wrapper.cpp @@ -0,0 +1,106 @@ +/*****************************************************************************\ +* * +* Name : encryption_wrapper * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "encryption_wrapper.h" + +#include +#include +#include +#include + +using namespace basis; +using namespace loggers; +using namespace structures; + +namespace octopi { + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +encryption_wrapper::encryption_wrapper(const byte_array &wrapped) +: infoton(encryption_classifier()), + _wrapped(wrapped) +{} + +encryption_wrapper::encryption_wrapper(const encryption_wrapper &to_copy) +: root_object(), + infoton(to_copy), + _wrapped(to_copy._wrapped) +{} + +encryption_wrapper::~encryption_wrapper() {} + +clonable *encryption_wrapper::clone() const +{ return cloner(*this); } + +encryption_wrapper &encryption_wrapper::operator = + (const encryption_wrapper &to_copy) +{ + if (this == &to_copy) return *this; + _wrapped = to_copy._wrapped; + return *this; +} + +const char *wrap_encryption_classifier[] = { "#octrap" }; + +SAFE_STATIC_CONST(string_array, encryption_wrapper::encryption_classifier, + (1, wrap_encryption_classifier)) + +int encryption_wrapper::packed_size() const +{ + return _wrapped.length() + sizeof(int); // wrapped array size. +} + +void encryption_wrapper::pack(byte_array &packed_form) const +{ + structures::attach(packed_form, _wrapped); +} + +bool encryption_wrapper::unpack(byte_array &packed_form) +{ + if (!structures::detach(packed_form, _wrapped)) return false; + return true; +} + +////////////// + +unwrapping_tentacle::unwrapping_tentacle() +: tentacle_helper + (encryption_wrapper::encryption_classifier(), false) +{} + +unwrapping_tentacle::~unwrapping_tentacle() +{} + +outcome unwrapping_tentacle::reconstitute(const string_array &classifier, + byte_array &packed_form, infoton * &reformed) +{ + if (classifier != encryption_wrapper::encryption_classifier()) + return NO_HANDLER; + + return reconstituter(classifier, packed_form, reformed, + (encryption_wrapper *)NIL); +} + +outcome unwrapping_tentacle::consume(infoton &formal(to_chow), + const octopus_request_id &formal(item_id), byte_array &transformed) +{ + FUNCDEF("consume"); + transformed.reset(); + LOG("should never enter this method."); + return common::NOT_IMPLEMENTED; +} + +} //namespace. + diff --git a/octopi/library/tentacles/encryption_wrapper.h b/octopi/library/tentacles/encryption_wrapper.h new file mode 100644 index 00000000..dd81f83e --- /dev/null +++ b/octopi/library/tentacles/encryption_wrapper.h @@ -0,0 +1,89 @@ +#ifndef ENCRYPTION_WRAPPER_CLASS +#define ENCRYPTION_WRAPPER_CLASS + +/*****************************************************************************\ +* * +* Name : encryption_wrapper * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace octopi { + +//! Wraps an encrypted infoton when the octopus is in an encrypted mode. +/*! + The enclosed package will be unwrapped by the encryption tentacle. +*/ + +class encryption_wrapper : public infoton +{ +public: + basis::byte_array _wrapped; + //!< the encrypted data that's held here. + /*!< this must be a packed classifier string array followed by + the packed infoton. */ + + encryption_wrapper(const basis::byte_array &wrapped = basis::byte_array::empty_array()); + + encryption_wrapper(const encryption_wrapper &to_copy); + + virtual ~encryption_wrapper(); + + DEFINE_CLASS_NAME("encryption_wrapper"); + + encryption_wrapper &operator =(const encryption_wrapper &to_copy); + + void text_form(basis::base_string &fill) const { + fill.assign(basis::astring(class_name())); // low exposure for vital held info. + } + + static const structures::string_array &encryption_classifier(); + //!< returns the classifier for this type of infoton. + + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); + + virtual clonable *clone() const; + + virtual int packed_size() const; +}; + +////////////// + +//! this simple tentacle just unpacks the encryption_wrapper infoton. +/*! + this object should never be doing more than that. +*/ + +class unwrapping_tentacle +: public tentacle_helper +{ +public: + unwrapping_tentacle(); + virtual ~unwrapping_tentacle(); + + DEFINE_CLASS_NAME("unwrapping_tentacle"); + + virtual basis::outcome reconstitute(const structures::string_array &classifier, + basis::byte_array &packed_form, infoton * &reformed); + + virtual basis::outcome consume(infoton &to_chow, const octopus_request_id &item_id, + basis::byte_array &transformed); + //!< this should never be called. +}; + +} //namespace. + +#endif // outer guard. + diff --git a/octopi/library/tentacles/entity_registry.cpp b/octopi/library/tentacles/entity_registry.cpp new file mode 100644 index 00000000..04ec7519 --- /dev/null +++ b/octopi/library/tentacles/entity_registry.cpp @@ -0,0 +1,54 @@ +/*****************************************************************************\ +* * +* Name : entity_registry * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "entity_registry.h" + +#include +#include +#include + +using namespace basis; +using namespace mathematics; +using namespace processes; +using namespace timely; + +namespace octopi { + +entity_registry::entity_registry() +: _sequencer(new safe_roller(1, MAXINT32 / 2)), + _rando(new chaos) +{ +} + +entity_registry::~entity_registry() +{ + WHACK(_sequencer); + WHACK(_rando); +} + +////////////// + +astring blank_entity_registry::text_form() +{ return "blank_entity_registry--all are allowed, none are remembered."; } + +bool blank_entity_registry::locate_entity(const octopus_entity &formal(entity), + time_stamp &last_active, byte_array &verification) +{ + last_active = time_stamp(); + verification = byte_array(); + return true; +} + +} //namespace. + diff --git a/octopi/library/tentacles/entity_registry.h b/octopi/library/tentacles/entity_registry.h new file mode 100644 index 00000000..0c61354c --- /dev/null +++ b/octopi/library/tentacles/entity_registry.h @@ -0,0 +1,105 @@ +#ifndef CLIENT_REGISTRY_CLASS +#define CLIENT_REGISTRY_CLASS + +/*****************************************************************************\ +* * +* Name : entity_registry * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +namespace octopi { + +class octopus_entity; + +//! Provides a security model for the octopus. +/*! + Derived versions of this class can be hooked to an octopus to enforce + a particular security model. +*/ + +class entity_registry +{ +public: + entity_registry(); + virtual ~entity_registry(); + + virtual bool authorized(const octopus_entity &entity) = 0; + //!< returns true if the "entity" is a registered entity. + /*!< this indicates that the entity is authorized to use this octopus' + services. */ + + virtual bool locate_entity(const octopus_entity &entity, + timely::time_stamp &last_active, basis::byte_array &verification) = 0; + //!< retrieves the security records for the "entity", if any exist. + /*!< true is returned on success and "last_active" is set to the entity's + last time of activity and "verification" is set to the cilent's + verification token from its login. */ + + virtual bool add_entity(const octopus_entity &entity, + const basis::byte_array &verification) = 0; + //!< adds the "entity" to the list of authorized users if allowed. + /*!< note that this will still succeed if the entity is already present. + the "verification" is used by the entity to prove its case for admittance, + but if it has zero length, it's ignored. true is returned if the entity + was allowed to login or refresh its record. false is returned if the + entity was denied, possibly because of a bad or missing verification + token. */ + + virtual bool refresh_entity(const octopus_entity &entity) = 0; + //!< this should be used to refresh the entity's health record. + /*!< it indicates that this entity is still functioning and should not + be removed due to inactivity. */ + + virtual bool zap_entity(const octopus_entity &entity) = 0; + //!< removes a "entity" if the entity can be found. + /*!< true is returned if that "entity" existed. */ + + virtual basis::astring text_form() = 0; + //!< prints out the contents of the entity registry. + +private: + processes::safe_roller *_sequencer; //!< issues unique ids for entities. + mathematics::chaos *_rando; //!< issues random portions of ids. + sockets::tcpip_stack *_stack; //!< provides hostname and network info. +}; + +////////////// + +//! the blank_entity_registry can be used when security is not an issue. +/*! + it always assumes every entity is valid. when the locate() method is + invoked, a feel-good record is produced. +*/ + +class blank_entity_registry : public entity_registry +{ +public: + bool authorized(const octopus_entity &formal(entity)) { return true; } + bool locate_entity(const octopus_entity &entity, + timely::time_stamp &last_active, basis::byte_array &verification); + bool add_entity(const octopus_entity &formal(entity), + const basis::byte_array &formal(verification)) { return true; } + bool refresh_entity(const octopus_entity &formal(entity)) { return true; } + bool zap_entity(const octopus_entity &formal(entity)) { return true; } + basis::astring text_form(); +}; + +} //namespace. + +#endif + diff --git a/octopi/library/tentacles/file_transfer_infoton.cpp b/octopi/library/tentacles/file_transfer_infoton.cpp new file mode 100644 index 00000000..acdb750e --- /dev/null +++ b/octopi/library/tentacles/file_transfer_infoton.cpp @@ -0,0 +1,108 @@ +/*****************************************************************************\ +* * +* Name : file_transfer_infoton * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "file_transfer_infoton.h" + +#include +#include +#include + +using namespace basis; +using namespace filesystem; +using namespace structures; + +namespace octopi { + +file_transfer_infoton::file_transfer_infoton() +: infoton(file_transfer_classifier()), + _success(common::OKAY), + _request(false), + _command(TREE_COMPARISON), + _src_root(), + _dest_root(), + _packed_data() +{} + +file_transfer_infoton::file_transfer_infoton(const outcome &success, + bool request, commands command, + const astring &source, const astring &destination, + const byte_array &packed_data) +: infoton(file_transfer_classifier()), + _success(success), + _request(request), + _command(abyte(command)), + _src_root(source), + _dest_root(destination), + _packed_data(packed_data) +{} + +file_transfer_infoton::~file_transfer_infoton() +{ +} + +void file_transfer_infoton::text_form(basis::base_string &fill) const +{ + fill.assign(astring(class_name()) + ": unimplemented text_form."); +} + +const char *file_transfer_constant = "#ftran"; + +SAFE_STATIC_CONST(string_array, + file_transfer_infoton::file_transfer_classifier, + (1, &file_transfer_constant)) + +int file_transfer_infoton::packed_size() const +{ + return sizeof(int) + + sizeof(abyte) * 2 + + _src_root.length() + 1 + + _dest_root.length() + 1 + + _packed_data.length() + sizeof(int); +} + +void file_transfer_infoton::package_tree_info(const directory_tree &tree, + const string_array &includes) +{ + _packed_data.reset(); + tree.pack(_packed_data); + includes.pack(_packed_data); +} + +void file_transfer_infoton::pack(byte_array &packed_form) const +{ + attach(packed_form, _success.value()); + attach(packed_form, abyte(_request)); + attach(packed_form, _command); + _src_root.pack(packed_form); + _dest_root.pack(packed_form); + attach(packed_form, _packed_data); +} + +bool file_transfer_infoton::unpack(byte_array &packed_form) +{ + int temp_o; + if (!detach(packed_form, temp_o)) return false; + _success = temp_o; + abyte temp; + if (!detach(packed_form, temp)) return false; + _request = temp; + if (!detach(packed_form, _command)) return false; + if (!_src_root.unpack(packed_form)) return false; + if (!_dest_root.unpack(packed_form)) return false; + if (!detach(packed_form, _packed_data)) return false; + return true; +} + +} //namespace. + diff --git a/octopi/library/tentacles/file_transfer_infoton.h b/octopi/library/tentacles/file_transfer_infoton.h new file mode 100644 index 00000000..5077d1ed --- /dev/null +++ b/octopi/library/tentacles/file_transfer_infoton.h @@ -0,0 +1,87 @@ +#ifndef FILE_TRANSFER_INFOTON_CLASS +#define FILE_TRANSFER_INFOTON_CLASS + +/*****************************************************************************\ +* * +* Name : file_transfer_infoton * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace octopi { + +//! Base objects used by the file transfer tentacle to schedule transfers. +/*! + Note: this is a fairly heavy-weight header. +*/ + +class file_transfer_infoton : public infoton +{ +public: + //! the commands specify what this package is intended to do. + enum commands { + TREE_COMPARISON = 1, + //!< the destination root will be compared with the source root. + /*!< the packed data in the request holds a packed symbol tree + describing the destination hierarchy. the packed data in the response + is the packed filename list that represents the differences between the + two hierarchies. this is considered to start a file transfer based on + those differences. */ + PLACE_FILE_CHUNKS + //!< the destination side requests a new set of chunks. + /*!< this is based on the source's memory of where the transfer is at. + this will only perform properly when the file transfer was requested to + be started by the client using a TREE_COMPARISON request. the request + has an empty data chunk, but the response consists of an arbitrary + number of pairs of @code + [ file_transfer_header + file chunk described in header ] + @endcode */ + }; + + basis::outcome _success; //!< reports what kind of result occurred. + bool _request; //!< if it's not a request, then it's a response. + basis::abyte _command; //!< one of the commands above. + basis::astring _src_root; //!< the top-level directory of the source. + basis::astring _dest_root; //!< the top-level directory of the destination. + basis::byte_array _packed_data; //!< the packed headers and file chunks. + + file_transfer_infoton(); + + file_transfer_infoton(const basis::outcome &success, bool request, commands command, + const basis::astring &source, const basis::astring &destination, + const basis::byte_array &packed_data); + + virtual ~file_transfer_infoton(); + + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); + + void package_tree_info(const filesystem::directory_tree &tree, + const structures::string_array &includes); + //!< prepares the packed data from the "tree" and "includes" list. + + virtual basis::clonable *clone() const { return cloner(*this); } + + virtual void text_form(basis::base_string &fill) const; + + virtual int packed_size() const; + + static const structures::string_array &file_transfer_classifier(); + //!< returns the classifier for this type of infoton. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/tentacles/file_transfer_tentacle.cpp b/octopi/library/tentacles/file_transfer_tentacle.cpp new file mode 100644 index 00000000..fa082b99 --- /dev/null +++ b/octopi/library/tentacles/file_transfer_tentacle.cpp @@ -0,0 +1,770 @@ +/*****************************************************************************\ +* * +* Name : file_transfer_tentacle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "file_transfer_tentacle.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace octopi; +using namespace processes; +using namespace structures; +using namespace textual; +using namespace timely; + +namespace octopi { + +#undef AUTO_LOCK +#define AUTO_LOCK auto_synchronizer loc(*_lock); + // protects our lists. + +const int FTT_CLEANING_INTERVAL = 30 * SECOND_ms; + // this is how frequently we clean up the list to remove outdated transfers. + +const int TRANSFER_TIMEOUT = 10 * MINUTE_ms; + // if it hasn't been touched in this long, it's out of there. + +//#define DEBUG_FILE_TRANSFER_TENTACLE + // uncomment for noisier version. + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +////////////// + +class file_transfer_record +{ +public: + // valid for both transfers and correspondences. + astring _src_root; // where the info is on the data provider. + time_stamp _last_active; // when this was last used. + + // valid for file transfers only. + octopus_entity _ent; // the entity requesting this service. + astring _dest_root; // where the info is on the data sink. + filename_list *_diffs; // the differences to be transferred. + file_transfer_header _last_sent; // the last chunk that was sent. + bool _done; // true if the transfer is finished. + string_array _includes; // the set to include. + + // valid for correspondence records only. + directory_tree *_local_dir; // our local information about the transfer. + astring _source_mapping; // valid for a correspondence record. + int _refresh_interval; // the rate of refreshing the source tree. + + file_transfer_record() : _diffs(NIL), _last_sent(file_time()), + _done(false), _local_dir(NIL) + {} + + ~file_transfer_record() { + WHACK(_local_dir); + WHACK(_diffs); + } + + astring text_form() const { + astring to_return; + to_return += astring("src=") + _src_root + astring(" last act=") + + _last_active.text_form(); + if (_ent.blank()) to_return += astring(" ent=") + _ent.text_form(); + if (_dest_root.t()) { + to_return += astring(" dest=") + _dest_root; + to_return += astring(" last_sent=") + _last_sent.text_form(); + } + return to_return; + } +}; + +////////////// + +// this implementation assumes that the same entity will never simultaneously +// transfer the same source to the same destination. that assumption holds +// up fine for different clients, since they should have different entities. +// when there is a collision on the entity/src/dest, then the default action +// is to assume that the transfer is just being started over. + +class file_transfer_status : public amorph +{ +public: + // find a transfer record by the key fields. + file_transfer_record *find(const octopus_entity &ent, const astring &src, + const astring &dest) { + for (int i = 0; i < elements(); i++) { + const file_transfer_record *rec = get(i); + if (rec && (rec->_ent == ent) && (rec->_src_root == src) + && (rec->_dest_root == dest) ) { + return borrow(i); + } + } + return NIL; + } + + virtual ~file_transfer_status() {} + + DEFINE_CLASS_NAME("file_transfer_status"); + + // find a file correspondence record by the mapping name. + file_transfer_record *find_mapping(const astring &source_mapping) { + for (int i = 0; i < elements(); i++) { + const file_transfer_record *rec = get(i); + if (rec && (rec->_source_mapping == source_mapping) ) + return borrow(i); + } + return NIL; + } + + // turns a source mapping into the location that it corresponds to. + astring translate(const astring &source_path) const { +// FUNCDEF("translate"); + string_array pieces; + filename(source_path).separate(pieces); + astring source_mapping = pieces[0]; + pieces.zap(0, 0); // remove source part. + + for (int i = 0; i < elements(); i++) { + const file_transfer_record *rec = get(i); + if (rec && (rec->_source_mapping == source_mapping) ) { + return rec->_src_root; + } + } + return astring::empty_string(); + } + + // removes a file transfer record by the key fields. + bool whack(const octopus_entity &ent, const astring &src, + const astring &dest) { + for (int i = 0; i < elements(); i++) { + const file_transfer_record *rec = get(i); + if (rec && (rec->_ent == ent) && (rec->_src_root == src) + && (rec->_dest_root == dest) ) { + zap(i, i); + return true; + } + } + return false; + } + + // clean all records for the entity "ent". + void whack_all(const octopus_entity &ent) { + for (int i = elements() - 1; i >= 0; i--) { + const file_transfer_record *rec = get(i); + if (rec && (rec->_ent == ent) ) + zap(i, i); + } + } + + // removes a file transfer correspondence. + bool whack_mapping(const astring &source_mapping) { + for (int i = elements() - 1; i >= 0; i--) { + const file_transfer_record *rec = get(i); + if (rec && (rec->_source_mapping == source_mapping) ) { + zap(i, i); + return true; + } + } + return false; + } + + // returns a string dump of the fields in this list. + astring text_form() const { + astring to_return; + for (int i = 0; i < elements(); i++) { + const file_transfer_record *rec = get(i); + if (rec) + to_return += rec->text_form() + parser_bits::platform_eol_to_chars(); + } + return to_return; + } +}; + +////////////// + +class file_transfer_cleaner : public ethread +{ +public: + file_transfer_cleaner(file_transfer_tentacle &parent) + : ethread(FTT_CLEANING_INTERVAL, SLACK_INTERVAL), _parent(parent) {} + + virtual void perform_activity(void *formal(ptr)) { _parent.periodic_actions(); } + +private: + file_transfer_tentacle &_parent; +}; + +////////////// + +file_transfer_tentacle::file_transfer_tentacle(int maximum_transfer, + file_transfer_tentacle::transfer_modes mode_of_transfer) +: tentacle_helper + (file_transfer_infoton::file_transfer_classifier(), false), + _maximum_transfer(maximum_transfer), + _transfers(new file_transfer_status), + _correspondences(new file_transfer_status), + _lock(new mutex), + _cleaner(new file_transfer_cleaner(*this)), + _mode(mode_of_transfer) +{ + _cleaner->start(NIL); +} + +file_transfer_tentacle::~file_transfer_tentacle() +{ + _cleaner->stop(); + WHACK(_transfers); + WHACK(_correspondences); + WHACK(_cleaner); + WHACK(_lock); +} + +astring file_transfer_tentacle::text_form() const +{ + AUTO_LOCK; + return _transfers->text_form(); +} + +void file_transfer_tentacle::expunge(const octopus_entity &to_remove) +{ + AUTO_LOCK; + _transfers->whack_all(to_remove); +} + +outcome file_transfer_tentacle::add_correspondence + (const astring &source_mapping, const astring &source_root, + int refresh_interval) +{ +#ifdef DEBUG_FILE_TRANSFER_TENTACLE + FUNCDEF("add_correspondence"); +#endif + AUTO_LOCK; + + remove_correspondence(source_mapping); // clean the old one out first. + + // create new file transfer record to hold this correspondence. + file_transfer_record *new_record = new file_transfer_record; + new_record->_source_mapping = source_mapping; + new_record->_src_root = source_root; + new_record->_refresh_interval = refresh_interval; + new_record->_local_dir = new directory_tree(source_root); +//hmmm: doesn't say anything about a pattern. do we need to worry about that? + + // check that the directory looked healthy. + if (!new_record->_local_dir->good()) { + WHACK(new_record); + return common::ACCESS_DENIED; + } +#ifdef DEBUG_FILE_TRANSFER_TENTACLE + LOG(astring("adding tree for: ent=") + new_record->_ent.text_form() + + " src=" + new_record->_src_root + " dest=" + new_record->_dest_root); +#endif + // calculate size and checksum info for the directory. + new_record->_local_dir->calculate( !(_mode & COMPARE_CONTENT_SAMPLE) ); + +#ifdef DEBUG_FILE_TRANSFER_TENTACLE + LOG(astring("done adding tree for: ent=") + new_record->_ent.text_form() + + " src=" + new_record->_src_root + " dest=" + new_record->_dest_root); +#endif + + _correspondences->append(new_record); + + return OKAY; +} + +outcome file_transfer_tentacle::remove_correspondence + (const astring &source_mapping) +{ + AUTO_LOCK; + if (!_correspondences->whack_mapping(source_mapping)) + return NOT_FOUND; + return OKAY; +} + +bool file_transfer_tentacle::get_differences(const octopus_entity &ent, + const astring &src, const astring &dest, filename_list &diffs) +{ +// FUNCDEF("get_differences"); + diffs.reset(); + AUTO_LOCK; + file_transfer_record *the_rec = _transfers->find(ent, src, dest); + if (!the_rec) return false; + if (!the_rec->_diffs) return false; // no diffs listed. + diffs = *the_rec->_diffs; + return true; +} + +bool file_transfer_tentacle::status(const octopus_entity &ent, + const astring &src, const astring &dest, double &total_size, + int &total_files, double ¤t_size, int ¤t_files, bool &done, + time_stamp &last_active) +{ +// FUNCDEF("status"); + total_size = 0; + total_files = 0; + current_files = 0; + current_size = 0; + AUTO_LOCK; + file_transfer_record *the_rec = _transfers->find(ent, src, dest); + if (!the_rec) return false; + done = the_rec->_done; + last_active = the_rec->_last_active; + + if (the_rec->_diffs) { + the_rec->_diffs->calculate_progress(the_rec->_last_sent._filename, + the_rec->_last_sent._byte_start + the_rec->_last_sent._length, + current_files, current_size); + total_files = the_rec->_diffs->total_files(); + total_size = the_rec->_diffs->total_size(); + } + + return true; +} + +outcome file_transfer_tentacle::register_file_transfer + (const octopus_entity &ent, const astring &src_root, + const astring &dest_root, const string_array &includes) +{ +// FUNCDEF("register_file_transfer"); + AUTO_LOCK; + // make sure that this isn't an existing transfer. if so, we just update + // the status. + file_transfer_record *the_rec = _transfers->find(ent, src_root, dest_root); + if (!the_rec) { + the_rec = new file_transfer_record; + the_rec->_src_root = src_root; + the_rec->_dest_root = dest_root; + the_rec->_ent = ent; + the_rec->_includes = includes; + _transfers->append(the_rec); // add the new record. + } else { + the_rec->_done = false; + the_rec->_includes = includes; + the_rec->_last_active.reset(); // freshen up the last activity time. + } + return OKAY; +} + +outcome file_transfer_tentacle::cancel_file_transfer(const octopus_entity &ent, + const astring &src_root, const astring &dest_root) +{ + AUTO_LOCK; + return _transfers->whack(ent, src_root, dest_root)? OKAY : NOT_FOUND; +} + +directory_tree *file_transfer_tentacle::lock_directory(const astring &key) +{ + _lock->lock(); + file_transfer_record *the_rec = _correspondences->find_mapping(key); + if (!the_rec || !the_rec->_local_dir) { + _lock->unlock(); + return NIL; // unknown transfer. + } + return the_rec->_local_dir; +} + +void file_transfer_tentacle::unlock_directory() +{ + _lock->unlock(); +} + +bool file_transfer_tentacle::add_path(const astring &key, + const astring &new_path) +{ + AUTO_LOCK; + file_transfer_record *the_rec = _correspondences->find_mapping(key); + if (!the_rec) return false; // unknown transfer. + if (!the_rec->_local_dir) return false; // not right type. + return the_rec->_local_dir->add_path(new_path) == common::OKAY; +} + +bool file_transfer_tentacle::remove_path(const astring &key, + const astring &old_path) +{ + AUTO_LOCK; + file_transfer_record *the_rec = _correspondences->find_mapping(key); + if (!the_rec) return false; // unknown transfer. + if (!the_rec->_local_dir) return false; // not right type. + return the_rec->_local_dir->remove_path(old_path) == common::OKAY; +} + +void file_transfer_tentacle::periodic_actions() +{ +#ifdef DEBUG_FILE_TRANSFER_TENTACLE + FUNCDEF("periodic_actions"); +#endif + AUTO_LOCK; + + // first, we'll clean out old transfers. + time_stamp oldest_allowed(-TRANSFER_TIMEOUT); + // nothing older than this should be kept. + for (int i = _transfers->elements() - 1; i >= 0; i--) { + const file_transfer_record *curr = _transfers->get(i); + if (curr->_last_active < oldest_allowed) { +#ifdef DEBUG_FILE_TRANSFER_TENTACLE + LOG(astring("cleaning record for: ent=") + curr->_ent.text_form() + + " src=" + curr->_src_root + " dest=" + curr->_dest_root); +#endif + _transfers->zap(i, i); + } + } + + // then we'll rescan any trees that are ready for it. + for (int i = 0; i < _correspondences->elements(); i++) { + file_transfer_record *curr = _correspondences->borrow(i); + if (curr->_last_active < time_stamp(-curr->_refresh_interval)) { + if (curr->_local_dir) { +#ifdef DEBUG_FILE_TRANSFER_TENTACLE + LOG(astring("refreshing tree for: ent=") + curr->_ent.text_form() + + " src=" + curr->_src_root + " dest=" + curr->_dest_root); +#endif + WHACK(curr->_local_dir); + curr->_local_dir = new directory_tree(curr->_src_root); + curr->_local_dir->calculate( !(_mode & COMPARE_CONTENT_SAMPLE) ); +#ifdef DEBUG_FILE_TRANSFER_TENTACLE + LOG(astring("done refreshing tree for: ent=") + curr->_ent.text_form() + + " src=" + curr->_src_root + " dest=" + curr->_dest_root); +#endif + } + curr->_last_active.reset(); // reset our action time. + } + } +} + +outcome file_transfer_tentacle::reconstitute(const string_array &classifier, + byte_array &packed_form, infoton * &reformed) +{ + // this method doesn't use the lists, so it doesn't need locking. + if (classifier != file_transfer_infoton::file_transfer_classifier()) + return NO_HANDLER; + return reconstituter(classifier, packed_form, reformed, + (file_transfer_infoton *)NIL); +} + +// the "handle_" methods are thread-safe because the mutex is locked before +// their invocations. + +outcome file_transfer_tentacle::handle_tree_compare_request + (file_transfer_infoton &req, const octopus_request_id &item_id) +{ + FUNCDEF("handle_tree_compare_request"); + + // get the mapping from the specified location on this side. + filename splitting(req._src_root); + string_array pieces; + splitting.separate(pieces); + astring source_mapping = pieces[0]; + + // patch the name up to find the sub_path for the source. + filename source_start; + pieces.zap(0, 0); + source_start.join(pieces); + + // locate the allowed transfer depot for the mapping they provided. + file_transfer_record *mapping_record + = _correspondences->find_mapping(source_mapping); + if (!mapping_record) { + LOG(astring("could not find source mapping of ") + source_mapping); + return NOT_FOUND; + } + + // unpack the tree that they sent us which describes their local area. + directory_tree *dest_tree = new directory_tree; + if (!dest_tree->unpack(req._packed_data)) { + LOG(astring("could not unpack requester's directory tree")); + WHACK(dest_tree); + return GARBAGE; + } + + string_array requested_names; + if (!requested_names.unpack(req._packed_data)) { + LOG(astring("could not unpack requester's filename includes")); + WHACK(dest_tree); + return GARBAGE; + } + + // look up to see if this is about something that has already been seen. + // we don't want to add a new transfer record if they're already working on + // this. that also lets them do a new tree compare to restart the transfer. + file_transfer_record *the_rec = _transfers->find(item_id._entity, + req._src_root, req._dest_root); + if (!the_rec) { + // there was no existing record; we'll create a new one. + the_rec = new file_transfer_record; + the_rec->_ent = item_id._entity; + the_rec->_src_root = req._src_root; + the_rec->_dest_root = req._dest_root; + _transfers->append(the_rec); + } else { + // record some activity on this record. + the_rec->_done = false; + the_rec->_last_active.reset(); + } + + the_rec->_diffs = new filename_list; + + int how_comp = file_info::EQUAL_NAME; // the prize for doing nothing. + if (_mode & COMPARE_SIZE_AND_TIME) + how_comp |= file_info::EQUAL_FILESIZE | file_info::EQUAL_TIMESTAMP; + if (_mode & COMPARE_CONTENT_SAMPLE) + how_comp |= file_info::EQUAL_CHECKSUM; + + // compare the two trees of files. + directory_tree::compare_trees(*mapping_record->_local_dir, + source_start.raw(), *dest_tree, astring::empty_string(), + *the_rec->_diffs, (file_info::file_similarity)how_comp); + +//LOG(astring("filenames decided as different:\n") + the_rec->_diffs->text_form()); + + // now prune the diffs to accord with what they claim they want. + if (requested_names.length()) { + for (int i = the_rec->_diffs->elements() - 1; i >= 0; i--) { + filename diff_curr = *the_rec->_diffs->get(i); + bool found = false; + for (int j = 0; j < requested_names.length(); j++) { + filename req_curr(requested_names[j]); + if (req_curr.compare_suffix(diff_curr)) { + found = true; +//LOG(astring("will use: ") + req_curr); + break; + } + } + if (!found) the_rec->_diffs->zap(i, i); + } + } + + req._packed_data.reset(); // clear out existing stuff before cloning. + file_transfer_infoton *reply = dynamic_cast(req.clone()); + the_rec->_diffs->pack(reply->_packed_data); + +//hmmm: does the other side really need the list of filenames? i guess we +// could check validity of what's transferred or check space available +// before the client starts the transfer. + + reply->_request = false; // it's a response now. + store_product(reply, item_id); + // send back the comparison list. + + return OKAY; +} + +outcome file_transfer_tentacle::handle_tree_compare_response + (file_transfer_infoton &resp, const octopus_request_id &item_id) +{ + FUNCDEF("handle_tree_compare_response"); + file_transfer_record *the_rec = _transfers->find(item_id._entity, + resp._src_root, resp._dest_root); + if (!the_rec) { + LOG(astring("could not find the record for this transfer: item=") + + item_id.text_form() + " src=" + resp._src_root + " dest=" + + resp._dest_root); + return NOT_FOUND; // not registered, so reject it. + } + + the_rec->_last_active.reset(); // record some activity on this record. + + filename_list *flist = new filename_list; + if (!flist->unpack(resp._packed_data)) { + WHACK(flist); + return GARBAGE; + } + +//hmmm: verify space on device? + + the_rec->_diffs = flist; // set the list of differences. + return OKAY; +} + +outcome file_transfer_tentacle::handle_storage_request + (file_transfer_infoton &req, const octopus_request_id &item_id) +{ + FUNCDEF("handle_storage_request"); + if (_mode & ONLY_REPORT_DIFFS) { + // store an unhandled infoton. + unhandled_request *deny = new unhandled_request(item_id, req.classifier(), + NO_HANDLER); + store_product(deny, item_id); + return NO_HANDLER; + } + + // look up the transfer record. + file_transfer_record *the_rec = _transfers->find(item_id._entity, + req._src_root, req._dest_root); + if (!the_rec) { + LOG(astring("could not find the record for this transfer: item=") + + item_id.text_form() + " src=" + req._src_root + " dest=" + + req._dest_root); + return NOT_FOUND; // not registered, so reject it. + } + + the_rec->_last_active.reset(); // mark it as still active. + + file_transfer_infoton *resp = dynamic_cast(req.clone()); + + if (!the_rec->_diffs) return BAD_INPUT; // wrong type of object. + + outcome bufret = heavy_file_operations::buffer_files + (_correspondences->translate(the_rec->_src_root), *the_rec->_diffs, + the_rec->_last_sent, resp->_packed_data, _maximum_transfer); + if (bufret != OKAY) { + // complain, but still send. + LOG(astring("buffer files returned an error on item=") + + item_id.text_form() + " src=" + req._src_root + " dest=" + + req._dest_root); + } + + if ( (bufret == OKAY) && !resp->_packed_data.length() ) { + // seems like the transfer is done. + + the_rec->_done = true; +//hmmm: mark the record and time out faster? + } + + resp->_request = false; // it's a response now. + store_product(resp, item_id); + return bufret; +} + +outcome file_transfer_tentacle::handle_storage_response + (file_transfer_infoton &resp, const octopus_request_id &item_id) +{ + FUNCDEF("handle_storage_response"); + if (_mode & ONLY_REPORT_DIFFS) { + // not spoken here. + return NO_HANDLER; + } + + // look up the transfer record. + file_transfer_record *the_rec = _transfers->find(item_id._entity, + resp._src_root, resp._dest_root); + if (!the_rec) return NOT_FOUND; // not registered, so reject it. + + the_rec->_last_active.reset(); // mark it as still active. + + if (!resp._packed_data.length()) { + // mark that we're done now. + the_rec->_done = true; + } + + // chew on all the things they sent us. + while (resp._packed_data.length()) { + file_time empty; + file_transfer_header found(empty); + if (!found.unpack(resp._packed_data)) { + // bomb out now. + LOG(astring("corruption seen on item=") + item_id.text_form() + + " src=" + resp._src_root + " dest=" + resp._dest_root); + return GARBAGE; + } + the_rec->_last_sent = found; + + if (found._length > resp._packed_data.length()) { + // another case for leaving--not enough data left in the buffer. + LOG(astring("data underflow seen on item=") + item_id.text_form() + + " src=" + resp._src_root + " dest=" + resp._dest_root); + return GARBAGE; + } + byte_array to_write = resp._packed_data.subarray(0, found._length - 1); + resp._packed_data.zap(0, found._length - 1); + + if (!the_rec->_diffs) return BAD_INPUT; + + const file_info *recorded_info = the_rec->_diffs->find(found._filename); + if (!recorded_info) { + LOG(astring("unrequested file seen: ") + found._filename); + continue; // maybe there are others that aren't confused. + } + + astring full_file = resp._dest_root + filename::default_separator() + + recorded_info->secondary(); + + outcome ret = heavy_file_operations::write_file_chunk(full_file, + found._byte_start, to_write); + if (ret != OKAY) { + LOG(astring("failed to write file chunk: error=") + + heavy_file_operations::outcome_name(ret) + " file=" + full_file + + a_sprintf(" start=%d len=%d", found._byte_start, found._length)); + } + found._time.set_time(full_file); + } + + // there is no response product to store. + return OKAY; +} + +// this is the only method that is allowed to invoke the "handle_X" methods +// and it must lock the object beforehand. + +outcome file_transfer_tentacle::consume(infoton &to_chow, + const octopus_request_id &item_id, byte_array &transformed) +{ +// FUNCDEF("consume"); + transformed.reset(); + file_transfer_infoton *inf = dynamic_cast(&to_chow); + if (!inf) return DISALLOWED; // not for us. + + AUTO_LOCK; // protect our lists while we're working on them. + + switch (inf->_command) { + case file_transfer_infoton::TREE_COMPARISON: { + if (inf->_request) return handle_tree_compare_request(*inf, item_id); + else return handle_tree_compare_response(*inf, item_id); + } + case file_transfer_infoton::PLACE_FILE_CHUNKS: { + if (inf->_request) return handle_storage_request(*inf, item_id); + else return handle_storage_response(*inf, item_id); + } + } + return BAD_INPUT; // not a recognized command. +} + +outcome file_transfer_tentacle::refresh_now(const astring &source_mapping) +{ +#ifdef DEBUG_FILE_TRANSFER_TENTACLE + FUNCDEF("refresh_now"); +#endif + AUTO_LOCK; + for (int i = 0; i < _correspondences->elements(); i++) { + file_transfer_record *curr = _correspondences->borrow(i); + if (!curr) continue; + if (curr->_source_mapping != source_mapping) continue; + if (curr->_local_dir) { +#ifdef DEBUG_FILE_TRANSFER_TENTACLE + LOG(astring("refreshing tree for: ent=") + curr->_ent.text_form() + + " src=" + curr->_src_root + " dest=" + curr->_dest_root); +#endif + WHACK(curr->_local_dir); + curr->_local_dir = new directory_tree(curr->_src_root); + curr->_local_dir->calculate( !(_mode & COMPARE_CONTENT_SAMPLE) ); +#ifdef DEBUG_FILE_TRANSFER_TENTACLE + LOG(astring("done refreshing tree for: ent=") + curr->_ent.text_form() + + " src=" + curr->_src_root + " dest=" + curr->_dest_root); +#endif + } + curr->_last_active.reset(); // reset our action time. + return OKAY; + } + return NOT_FOUND; +} + +} //namespace. + + diff --git a/octopi/library/tentacles/file_transfer_tentacle.h b/octopi/library/tentacles/file_transfer_tentacle.h new file mode 100644 index 00000000..f74b4999 --- /dev/null +++ b/octopi/library/tentacles/file_transfer_tentacle.h @@ -0,0 +1,183 @@ +#ifndef FILE_TRANSFER_TENTACLE_CLASS +#define FILE_TRANSFER_TENTACLE_CLASS + +/*****************************************************************************\ +* * +* Name : file_transfer_tentacle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "file_transfer_infoton.h" + +#include +#include +#include +#include +#include + +namespace octopi { + +class file_transfer_cleaner; +class file_transfer_status; + +//! Manages the transferrence of directory trees from one place to another. +/*! + Note: this is a fairly heavy-weight header due to the inclusion of the + file transfer infoton header. It is better to forward declare the + objects in both file transfer headers when using the types in other + headers. +*/ + +class file_transfer_tentacle +: public tentacle_helper +{ +public: + enum transfer_modes { + ONLY_REPORT_DIFFS = 0x1, //!< no actual file transfer, just reports. + COMPARE_SIZE_AND_TIME = 0x2, //!< uses size and time to see differences. + COMPARE_CONTENT_SAMPLE = 0x4, //!< samples parts of file for comparison. + COMPARE_ALL = 0x6 //!< compares all of the file size, file time, and contents. + }; + + + file_transfer_tentacle(int maximum_transfer, transfer_modes mode_of_transfer); + //!< constructs a tentacle for either transfers or comparisons. + /*!< the "maximum_transfer" is the largest chunk of data we will try to + sling across the network at a time. the "mode_of_transfer" selects how + to perform the operation. if "ONLY_REPORT_DIFFS" is set, then there + will only be a report of differences and no files will be copied. if + COMPARE_SIZE_AND_TIME is set, then the comparison will use the file's + size and its access time for determining if it has changed. if the + COMPARE_CONTENT_SAMPLE flag is set, then the file will be sampled at some + key locations and that will decide differences. the comparison modes + can be mixed together. if there are no comparison modes, then the files + will always be copied. */ + + virtual ~file_transfer_tentacle(); + + DEFINE_CLASS_NAME("file_transfer_tentacle"); + + basis::astring text_form() const; + //!< returns a string representing the current state of transfers. + + filesystem::directory_tree *lock_directory(const basis::astring &source_mapping); + //!< provides a view of the tentacle's current state. + void unlock_directory(); + //!< unlock MUST be called when one is done looking at the tree. + + // these methods are for the "server" side--the side that has files to offer. + + basis::outcome add_correspondence(const basis::astring &source_mapping, + const basis::astring &source_root, int refresh_interval); + //!< adds a file transfer correspondence. + /*!< this is a "source_mapping" which is a short string that is made + available to the other side for transfer requests. when they specify the + "source_mapping", it will be translated on this side to the "source_root", + which must be a valid filesystem path. the "refresh_interval" dictates + how frequently, in milliseconds, the source will be scanned to update the + internal directory tree. this is done the first time the "source_mapping" + is set up also. if a previous identical "source_mapping" existed, then it + is removed and replaced with the information from the new invocation. */ + + basis::outcome remove_correspondence(const basis::astring &source_mapping); + //!< takes out the "source_mapping" which was previously added. + /*!< this keeps any transfers from occurring on that name, and will cause + aborted transfers if any were still ongoing. */ + + basis::outcome refresh_now(const basis::astring &source_mapping); + //!< refreshes the "source_mapping" right now, regardless of the interval. + /*!< the mapping must already have been created with add_correspondence(). + */ + + bool add_path(const basis::astring &source_mapping, const basis::astring &new_path); + //!< inserts the "new_path" into a registered correspondence. + /*!< the "source_mapping" must already be registered. */ + + bool remove_path(const basis::astring &source_mapping, const basis::astring &old_path); + //!< deletes the "old_path" out of an existing correspondence. + + // these methods are for the client side--the side that wants to get files. + + basis::outcome register_file_transfer(const octopus_entity &ent, + const basis::astring &src_root, const basis::astring &dest_root, + const structures::string_array &include); + //!< records a transfer that is going to commence. + /*!< the side that wishes to download files must invoke this before + starting the transfer. if the "include" list is not empty, then only + those files will be transferred. they have to match the suffix of the + files that would have been transferred and wildcards are not currently + supported. */ + + basis::outcome cancel_file_transfer(const octopus_entity &ent, + const basis::astring &src_root, const basis::astring &dest_root); + //!< tosses a previously registered file transfer. + /*!< this will be done automatically after a time-out period, but it is + better to clean it up as soon as one is finished with the transfer. */ + + bool status(const octopus_entity &ent, const basis::astring &src, + const basis::astring &dest, double &total_size, int &total_files, + double ¤t_size, int ¤t_files, bool &done, + timely::time_stamp &last_active); + //!< locates the transfer specified and returns information about it. + /*!< the transfer is designated by the "ent", "src" and "dest" parameters + and returns the current progress. files refers to how many files are + being transferred and size refers to their combined weight in bytes. the + "done" flag is set if the transfer seems finished. note that this will + not set any values until the first reply comes back from the server. */ + + bool get_differences(const octopus_entity &ent, const basis::astring &src, + const basis::astring &dest, filesystem::filename_list &diffs); + //!< accesses the list of difference for an ongoing transfer. + /*!< the progress is stored in "diffs". */ + + // required tentacle methods... + + virtual basis::outcome reconstitute(const structures::string_array &classifier, + basis::byte_array &packed_form, infoton * &reformed); + //!< recreates a "reformed" infoton from its packed form. + /*!< this requires the "classifier" and packed infoton data in + "packed_form". this will only succeed if the classifier's first name + is understood here. */ + + virtual basis::outcome consume(infoton &to_chow, const octopus_request_id &item_id, + basis::byte_array &transformed); + //!< processes the "to_chow" infoton as a file transfer request. + + virtual void expunge(const octopus_entity &to_remove); + //!< throws out any transfers occurring for the entity "to_remove". + + // internal use only. + + void periodic_actions(); //!< drops timed out transfers. + +private: + int _maximum_transfer; //!< largest chunk to send at a time. + file_transfer_status *_transfers; //!< our record of ongoing transfers. + file_transfer_status *_correspondences; //!< the synonyms for mapping. + basis::mutex *_lock; //!< protects our lists. + file_transfer_cleaner *_cleaner; //!< cleans up dead transfers. + int _mode; //!< how will the comparison be done? + + // these process the request and response infotons that are passed to us. + basis::outcome handle_tree_compare_request(file_transfer_infoton &req, + const octopus_request_id &item_id); + basis::outcome handle_tree_compare_response(file_transfer_infoton &req, + const octopus_request_id &item_id); + basis::outcome handle_storage_request(file_transfer_infoton &req, + const octopus_request_id &item_id); + basis::outcome handle_storage_response(file_transfer_infoton &req, + const octopus_request_id &item_id); +}; + +} //namespace. + +#endif + diff --git a/octopi/library/tentacles/key_repository.cpp b/octopi/library/tentacles/key_repository.cpp new file mode 100644 index 00000000..bd6f6b4a --- /dev/null +++ b/octopi/library/tentacles/key_repository.cpp @@ -0,0 +1,86 @@ +/*****************************************************************************\ +* * +* Name : key_repository * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "key_repository.h" + +#include +#include + +using namespace basis; +using namespace crypto; +using namespace structures; + +namespace octopi { + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +//#define DEBUG_KEY_REPOSITORY + // uncomment for noisier execution. beware however, if the uls is in + // use, this can cause infinite recursion. + +key_repository::~key_repository() {} + +octenc_key_record *key_repository::lock(const octopus_entity &ent) +{ +#ifdef DEBUG_KEY_REPOSITORY + FUNCDEF("lock"); + LOG(astring("entity sought=") + ent.text_form()); +#endif + octenc_key_record *to_return = NIL; + _locker.lock(); + to_return = _keys.find(ent.mangled_form()); + if (!to_return) { +#ifdef DEBUG_KEY_REPOSITORY + LOG(astring("did not find entity=") + ent.text_form()); +#endif + _locker.unlock(); + } else { +#ifdef DEBUG_KEY_REPOSITORY + LOG(astring("found entity=") + ent.text_form()); +#endif + } + return to_return; +} + +void key_repository::unlock(octenc_key_record *to_unlock) +{ + if (!to_unlock) return; // dolts! they cannot unlock a non-record. + _locker.unlock(); +} + +outcome key_repository::add(const octopus_entity &ent, + const blowfish_crypto &key) +{ +#ifdef DEBUG_KEY_REPOSITORY + FUNCDEF("add"); + LOG(astring("adding key for entity=") + ent.text_form()); +#endif + auto_synchronizer loc(_locker); + octenc_key_record rec(ent, key); + return _keys.add(ent.mangled_form(), rec); +} + +outcome key_repository::whack(const octopus_entity &ent) +{ +#ifdef DEBUG_KEY_REPOSITORY + FUNCDEF("whack"); + LOG(astring("removing key for entity=") + ent.text_form()); +#endif + auto_synchronizer loc(_locker); + return _keys.whack(ent.mangled_form()); +} + +} //namespace. + diff --git a/octopi/library/tentacles/key_repository.h b/octopi/library/tentacles/key_repository.h new file mode 100644 index 00000000..dfa33304 --- /dev/null +++ b/octopi/library/tentacles/key_repository.h @@ -0,0 +1,76 @@ +#ifndef KEY_REPOSITORY_CLASS +#define KEY_REPOSITORY_CLASS + +/*****************************************************************************\ +* * +* Name : key_repository * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2004-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include + +namespace octopi { + +//! Tracks the keys that have been assigned for a secure channel. +/*! + This class is thread-safe, as long as one uses the lock() method below in + the proper manner. + + NOTE: this is a heavy-weight header; do not include in other headers. +*/ + +class octenc_key_record +{ +public: + octopus_entity _entity; //!< who the key belongs to. + crypto::blowfish_crypto _key; //!< used for communicating with an entity. + + octenc_key_record() : _key(200) {} //!< bogus blank constructor. + + octenc_key_record(const octopus_entity &entity, const crypto::blowfish_crypto &key) + : _entity(entity), _key(key) {} +}; + +////////////// + +class key_repository +{ +public: + key_repository() : _locker(), _keys() {} + virtual ~key_repository(); + + DEFINE_CLASS_NAME("key_repository"); + + octenc_key_record *lock(const octopus_entity &ent); + //!< locates the key for "ent", if it's stored. + /*!< the returned object, unless it's NIL, must be unlocked. */ + + void unlock(octenc_key_record *to_unlock); + //!< drops the lock on the key record in "to_unlock". + + basis::outcome add(const octopus_entity &ent, const crypto::blowfish_crypto &key); + //!< adds a "key" for the "ent". this will fail if one is already listed. + + basis::outcome whack(const octopus_entity &ent); + //!< removes the key for "ent". + +private: + basis::mutex _locker; //!< protects our list of keys. + structures::symbol_table _keys; //!< the list of keys. +}; + +} //namespace. + +#endif // outer guard. + diff --git a/octopi/library/tentacles/login_tentacle.cpp b/octopi/library/tentacles/login_tentacle.cpp new file mode 100644 index 00000000..20b34e2a --- /dev/null +++ b/octopi/library/tentacles/login_tentacle.cpp @@ -0,0 +1,101 @@ +/*****************************************************************************\ +* * +* Name : login_tentacle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "entity_registry.h" +#include "login_tentacle.h" +#include "security_infoton.h" + +#include +#include +#include + +using namespace basis; +using namespace octopi; +using namespace structures; +using namespace timely; + +namespace octopi { + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) + +////////////// + +login_tentacle::login_tentacle(entity_registry &security, int dormancy_period) +: tentacle_helper(security_infoton::security_classifier(), + false), + _security(security), + _dormancy_period(dormancy_period) +{} + +login_tentacle::~login_tentacle() {} + +outcome login_tentacle::reconstitute(const string_array &classifier, + byte_array &packed_form, infoton * &reformed) +{ + if (classifier != security_infoton::security_classifier()) + return NO_HANDLER; + + return reconstituter(classifier, packed_form, reformed, + (security_infoton *)NIL); +} + +void login_tentacle::expunge(const octopus_entity &to_remove) +{ + _security.zap_entity(to_remove); // trash it and we're done. +} + +outcome login_tentacle::consume(infoton &to_chow, + const octopus_request_id &item_id, byte_array &transformed) +{ +// FUNCDEF("consume"); + transformed.reset(); + security_infoton *inf = dynamic_cast(&to_chow); + if (!inf) { + // if the infoton doesn't cast, then it is not for us. we need to vet + // that the entity it came from is known and approved. + if (_security.authorized(item_id._entity)) { + // this infoton's entity was allowed, so we call it partially processed. + return PARTIAL; + } + // the infoton's entity is not authorized; it needs to be dropped. + return DISALLOWED; + } + + switch (inf->_mode) { + case security_infoton::LI_REFRESH: // intentional fall through. + case security_infoton::LI_LOGIN: { + bool success = _security.add_entity(item_id._entity, + inf->verification()); + inf->_success = success? OKAY : DISALLOWED; + break; + } + case security_infoton::LI_LOGOUT: { + bool success = _security.zap_entity(item_id._entity); + inf->_success = success? OKAY : DISALLOWED; + break; + } + default: { + inf->_success = BAD_INPUT; + break; + } + } + inf->verification().reset(); // we don't need to send that back. + if (!store_product(dynamic_cast(inf->clone()), item_id)) + return NO_SPACE; + return OKAY; +} + +} //namespace. + diff --git a/octopi/library/tentacles/login_tentacle.h b/octopi/library/tentacles/login_tentacle.h new file mode 100644 index 00000000..0e2ee213 --- /dev/null +++ b/octopi/library/tentacles/login_tentacle.h @@ -0,0 +1,75 @@ +#ifndef LOGIN_TENTACLE_CLASS +#define LOGIN_TENTACLE_CLASS + +/*****************************************************************************\ +* * +* Name : login_tentacle * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "security_infoton.h" + +#include + +namespace octopi { + +// forward. +class entity_registry; +class octopus_request_id; + +//! Provides rudimentary login services. +/*! + This is a way for entities to become logged into an octopus system, + should that be required by the application. +*/ + +class login_tentacle +: public tentacle_helper +{ +public: + login_tentacle(entity_registry &security, + int dormancy_period = 7 * basis::MINUTE_ms); + //!< constructs a login manager based on "security". + /*!< this will allow an entity to persist for "dormancy_period" + milliseconds without a refresh. after that time, the entities we haven't + heard from are whacked. the "security" object will provide our login + checking. */ + + virtual ~login_tentacle(); + + DEFINE_CLASS_NAME("login_tentacle"); + + virtual basis::outcome reconstitute(const structures::string_array &classifier, + basis::byte_array &packed_form, infoton * &reformed); + //!< recreates a "reformed" infoton from the packed data. + /*!< this uses the "classifier" and packed infoton data in "packed_form". + this will only succeed if the classifier's first name is understood here. + */ + + virtual basis::outcome consume(infoton &to_chow, const octopus_request_id &item_id, + basis::byte_array &transformed); + //!< the base login_tentacle allows anyone to log in. + /*!< this permits any entity that tries to log in to become a verified + entity. derived login_tentacles can force the entity to prove that it's + worthy in an application specific manner. */ + + virtual void expunge(const octopus_entity &to_remove); + //!< trashes the records we were keeping for the entity. + +private: + entity_registry &_security; //!< allows or disallows entity access. + int _dormancy_period; //!< time allowed before an entity is dropped. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/tentacles/makefile b/octopi/library/tentacles/makefile new file mode 100644 index 00000000..b81b1dd7 --- /dev/null +++ b/octopi/library/tentacles/makefile @@ -0,0 +1,13 @@ +CONSOLE_MODE = true + +include cpp/variables.def + +PROJECT = tentacles +TYPE = library +TARGETS = tentacles.lib +SOURCE = encryption_infoton.cpp encryption_tentacle.cpp encryption_wrapper.cpp \ + entity_registry.cpp file_transfer_infoton.cpp file_transfer_tentacle.cpp key_repository.cpp \ + login_tentacle.cpp recursive_file_copy.cpp security_infoton.cpp simple_entity_registry.cpp + +include cpp/rules.def + diff --git a/octopi/library/tentacles/recursive_file_copy.cpp b/octopi/library/tentacles/recursive_file_copy.cpp new file mode 100644 index 00000000..40e4f0af --- /dev/null +++ b/octopi/library/tentacles/recursive_file_copy.cpp @@ -0,0 +1,194 @@ +/*****************************************************************************\ +* * +* Name : recursive_file_copy * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "file_transfer_infoton.h" +#include "file_transfer_tentacle.h" +#include "recursive_file_copy.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace structures; +using namespace textual; + +namespace octopi { + +#define FAKE_HOSTNAME "internal_fake_host" + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s) +#undef BASE_LOG +#define BASE_LOG(s) EMERGENCY_LOG(program_wide_logger::get(), s) + +const int MAX_CHUNK_RFC_COPY_HIER = 1 * MEGABYTE; + // maximum size for each transfer chunk. + +recursive_file_copy::~recursive_file_copy() {} + +const char *recursive_file_copy::outcome_name(const outcome &to_name) +{ return common::outcome_name(to_name); } + +#define RETURN_ERROR_RFC(msg, err) { \ + LOG(msg); \ + return err; \ +} + +outcome recursive_file_copy::copy_hierarchy(int transfer_mode, + const astring &source_dir, const astring &target_dir, + const string_array &includes, const astring &source_start) +{ + FUNCDEF("copy_hierarchy"); + +/* + string_array includes; + if (_global_argc >= 5) { + for (int i = 4; i < _global_argc; i++) { + includes += _global_argv[i]; + } + } +*/ + + astring source_root = "snootums"; + if (source_start.t()) { + source_root += filename::default_separator() + source_start; + } + + octopus ring_leader(FAKE_HOSTNAME, 10 * MEGABYTE); + file_transfer_tentacle *tran = new file_transfer_tentacle + (MAX_CHUNK_RFC_COPY_HIER, (file_transfer_tentacle::transfer_modes)transfer_mode); + ring_leader.add_tentacle(tran); + + outcome add_ret = tran->add_correspondence("snootums", source_dir, + 10 * MINUTE_ms); + if (add_ret != tentacle::OKAY) + RETURN_ERROR_RFC("failed to add the correspondence", NOT_FOUND); + + file_transfer_infoton *initiate = new file_transfer_infoton; + initiate->_request = true; + initiate->_command = file_transfer_infoton::TREE_COMPARISON; + initiate->_src_root = source_root; + initiate->_dest_root = target_dir; + directory_tree target_area(target_dir); +//hmmm: simple asset counting debugging in calculate would be nice too. + target_area.calculate( !(transfer_mode & file_transfer_tentacle::COMPARE_CONTENT_SAMPLE) ); + initiate->package_tree_info(target_area, includes); + + octopus_entity ent = ring_leader.issue_identity(); + octopus_request_id req_id(ent, 1); + outcome start_ret = ring_leader.evaluate(initiate, req_id); + if (start_ret != tentacle::OKAY) + RETURN_ERROR_RFC("failed to start the comparison", NONE_READY); + + file_transfer_infoton *reply_from_init + = (file_transfer_infoton *)ring_leader.acquire_specific_result(req_id); + if (!reply_from_init) + RETURN_ERROR_RFC("no response to tree compare start", NONE_READY); + + filename_list diffs; + byte_array pack_copy = reply_from_init->_packed_data; + if (!diffs.unpack(pack_copy)) { + RETURN_ERROR_RFC("could not unpack filename list!", GARBAGE); + } +// LOG(astring("got list of diffs:\n") + diffs.text_form()); + + octopus client_spider(FAKE_HOSTNAME, 10 * MEGABYTE); + file_transfer_tentacle *tran2 = new file_transfer_tentacle + (MAX_CHUNK_RFC_COPY_HIER, (file_transfer_tentacle::transfer_modes)transfer_mode); + tran2->register_file_transfer(ent, source_root, target_dir, includes); + client_spider.add_tentacle(tran2); + + octopus_request_id resp_id(ent, 2); + outcome ini_resp_ret = client_spider.evaluate(reply_from_init, resp_id); + if (ini_resp_ret != tentacle::OKAY) + RETURN_ERROR_RFC("failed to process the start response!", FAILURE); + + infoton *junk = client_spider.acquire_specific_result(resp_id); + if (junk) + RETURN_ERROR_RFC("got a response we shouldn't have!", FAILURE); + + astring current_file; // what file is in progress right now? + + int iter = 0; + while (true) { +//LOG(a_sprintf("ongoing chunk %d", ++iter)); + + // keep going until we find a broken reply. + file_transfer_infoton *ongoing = new file_transfer_infoton; + ongoing->_request = true; + ongoing->_command = file_transfer_infoton::PLACE_FILE_CHUNKS; + ongoing->_src_root = source_root; + ongoing->_dest_root = target_dir; + + octopus_request_id chunk_id(ent, iter + 10); + outcome place_ret = ring_leader.evaluate(ongoing, chunk_id); + if (place_ret != tentacle::OKAY) + RETURN_ERROR_RFC("failed to run ongoing transfer", FAILURE); + + file_transfer_infoton *reply = (file_transfer_infoton *)ring_leader + .acquire_specific_result(chunk_id); + if (!reply) + RETURN_ERROR_RFC("failed to get ongoing transfer reply", NONE_READY); + + if (!reply->_packed_data.length()) { + BASE_LOG(astring("finished transfer from \"") + source_dir + + "\" to \"" + target_dir + "\""); + break; + } + + byte_array copy = reply->_packed_data; + while (copy.length()) { + file_time empty; + file_transfer_header head(empty); + if (!head.unpack(copy)) + RETURN_ERROR_RFC("failed to unpack header", GARBAGE); +//LOG(a_sprintf("size in array: %d", copy.length())); + if (copy.length() < head._length) + RETURN_ERROR_RFC("not enough length in array", GARBAGE); +//hmmm: are we doing nothing here besides validating that we GOT something in the header? + copy.zap(0, head._length - 1); +//LOG(a_sprintf("size in array now: %d", copy.length())); + +//hmmm: if logging, then... + BASE_LOG(head.readable_text_form()); + } + if (copy.length()) + RETURN_ERROR_RFC("still had data in array", GARBAGE); + + octopus_request_id resp_id(ent, iter + 11); + outcome resp_ret = client_spider.evaluate(reply, resp_id); + if (resp_ret != tentacle::OKAY) + RETURN_ERROR_RFC("failed to process the transfer reply!", FAILURE); + } + + return OKAY; +} + +} //namespace. + + diff --git a/octopi/library/tentacles/recursive_file_copy.h b/octopi/library/tentacles/recursive_file_copy.h new file mode 100644 index 00000000..6d414da1 --- /dev/null +++ b/octopi/library/tentacles/recursive_file_copy.h @@ -0,0 +1,59 @@ +#ifndef RECURSIVE_FILE_COPY_CLASS +#define RECURSIVE_FILE_COPY_CLASS + +/*****************************************************************************\ +* * +* Name : recursive file copy * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2005-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +namespace octopi { + +//! Copies entire hierarchies in the file system from one place to another. + +class recursive_file_copy +{ +public: + virtual ~recursive_file_copy(); + + DEFINE_CLASS_NAME("recursive_file_copy"); + + enum outcomes { + OKAY = basis::common::OKAY, + BAD_INPUT = basis::common::BAD_INPUT, + GARBAGE = basis::common::GARBAGE, + NOT_FOUND = basis::common::NOT_FOUND, + NONE_READY = basis::common::NONE_READY, + FAILURE = basis::common::FAILURE + }; + static const char *outcome_name(const basis::outcome &to_name); + +//hmmm: need an option to specify the logging that we are currently doing in here. + + //! copies a directory hierarchy starting at "source_dir" into "target_dir". + /*! the "transfer_mode" is a combination of mode values from the file_transfer_tentacle class, + and dictates how the files to copy will be decided. the "includes" is a list of files that will + be included. if that is an empty array, then all files will be included. the "source_start" + is a subdirectory within the source to start copying at; only items there and below will be + included. */ + static basis::outcome copy_hierarchy(int transfer_mode, const basis::astring &source_dir, + const basis::astring &target_dir, const structures::string_array &includes, + const basis::astring &source_start = basis::astring::empty_string()); +}; + +} //namespace. + +#endif + diff --git a/octopi/library/tentacles/security_infoton.cpp b/octopi/library/tentacles/security_infoton.cpp new file mode 100644 index 00000000..449e0710 --- /dev/null +++ b/octopi/library/tentacles/security_infoton.cpp @@ -0,0 +1,107 @@ +/*****************************************************************************\ +* * +* Name : security_infoton * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "security_infoton.h" + +#include +#include +#include +#include +#include +#include + +using namespace basis; +using namespace structures; +//using namespace textual; + +namespace octopi { + +security_infoton::security_infoton() +: infoton(security_classifier()), + _mode(LI_LOGIN), + _success(tentacle::NOT_FOUND), + _verification(new byte_array) +{} + +security_infoton::security_infoton(login_modes mode, const outcome &success, + const byte_array &verification) +: infoton(security_classifier()), + _mode(mode), + _success(success), + _verification(new byte_array(verification)) +{} + +security_infoton::security_infoton(const security_infoton &to_copy) +: root_object(), + infoton(to_copy), + _mode(to_copy._mode), + _success(to_copy._success), + _verification(new byte_array(*to_copy._verification)) +{ +} + +security_infoton::~security_infoton() +{ WHACK(_verification); } + +clonable *security_infoton::clone() const +{ return cloner(*this); } + +security_infoton &security_infoton::operator =(const security_infoton &to_copy) +{ + if (this == &to_copy) return *this; + set_classifier(to_copy.classifier()); + _mode = to_copy._mode; + _success = to_copy._success; + *_verification = *to_copy._verification; + return *this; +} + +const byte_array &security_infoton::verification() const +{ return *_verification; } + +byte_array &security_infoton::verification() { return *_verification; } + +const astring login_classifier[] = { "#octsec" }; + +SAFE_STATIC_CONST(string_array, security_infoton::security_classifier, + (1, login_classifier)) + +int security_infoton::packed_size() const +{ + return sizeof(_mode) + + sizeof(int) // packed outcome. + + _verification->length() + sizeof(int); +} + +void security_infoton::pack(byte_array &packed_form) const +{ + structures::attach(packed_form, int(_mode)); + attach(packed_form, _success.value()); + structures::attach(packed_form, *_verification); +} + +bool security_infoton::unpack(byte_array &packed_form) +{ + int int_hold; + if (!structures::detach(packed_form, int_hold)) return false; + _mode = login_modes(int_hold); + int value; + if (!detach(packed_form, value)) return false; + _success = outcome(value); + if (!structures::detach(packed_form, *_verification)) return false; + return true; +} + +} //namespace. + diff --git a/octopi/library/tentacles/security_infoton.h b/octopi/library/tentacles/security_infoton.h new file mode 100644 index 00000000..a70c6e51 --- /dev/null +++ b/octopi/library/tentacles/security_infoton.h @@ -0,0 +1,73 @@ +#ifndef SECURITY_INFOTON_CLASS +#define SECURITY_INFOTON_CLASS + +/*****************************************************************************\ +* * +* Name : security_infoton * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include + +namespace octopi { + +//! Encapsulates security activities (login, logout, refresh). + +class security_infoton : public infoton +{ +public: + enum login_modes { + LI_LOGIN, //!< the requester wants to log in as a new entity. + LI_LOGOUT, //!< the requester surrenders its login. + LI_REFRESH //!< the requester is still alive and wants to keep its login. + }; + + login_modes _mode; //!< what kind of request is being made here? + basis::outcome _success; //!< did the request succeed? + + security_infoton(); + security_infoton(login_modes mode, const basis::outcome &success, + const basis::byte_array &verification); + security_infoton(const security_infoton &to_copy); + + virtual ~security_infoton(); + + DEFINE_CLASS_NAME("security_infoton"); + + security_infoton &operator =(const security_infoton &to_copy); + + // observes or modifies the verification token. + const basis::byte_array &verification() const; + basis::byte_array &verification(); + + static const structures::string_array &security_classifier(); + //!< returns the classifier for this type of infoton. + + virtual void pack(basis::byte_array &packed_form) const; + virtual bool unpack(basis::byte_array &packed_form); + + virtual clonable *clone() const; + + virtual int packed_size() const; + + virtual void text_form(basis::base_string &fill) const { + fill.assign(basis::astring(class_name()) + + basis::a_sprintf(": mode %d, outcome=%d", _mode, _success.value())); + } + +private: + basis::byte_array *_verification; //!< anything needed for upper-level verification. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/tentacles/simple_entity_registry.cpp b/octopi/library/tentacles/simple_entity_registry.cpp new file mode 100644 index 00000000..19cf42f5 --- /dev/null +++ b/octopi/library/tentacles/simple_entity_registry.cpp @@ -0,0 +1,149 @@ +/*****************************************************************************\ +* * +* Name : simple_entity_registry * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "simple_entity_registry.h" + +#include +#include +#include + +using namespace basis; +using namespace octopi; +using namespace structures; +using namespace timely; + +namespace octopi { + +#undef GRAB_LOCK +#define GRAB_LOCK \ + auto_synchronizer l(*_secure_lock) + +#undef LOG +#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s); + +const int ENTITY_HASH_BITS = 8; + // the hash table for entities will be 2^N wide. + +// this record is stored for each verified entity. if such a record exists, +// then the entity has passed through whatever security system is installed +// in the octopus. + +////////////// + +class recognized_entity +{ +public: + octopus_entity _entity; // the identifier for this entity. + time_stamp _last_active; // when was this entity last active? + byte_array _verification; // verification information sent by entity. +}; + +////////////// + +class recognized_entity_list : public string_hash +{ +public: + recognized_entity_list() : string_hash(ENTITY_HASH_BITS) {} +}; + +////////////// + +simple_entity_registry::simple_entity_registry() +: _secure_lock(new mutex), + _entities(new recognized_entity_list) +{ +} + +simple_entity_registry::~simple_entity_registry() +{ + WHACK(_entities); + WHACK(_secure_lock); +} + +bool simple_entity_registry::authorized(const octopus_entity &entity) +{ + GRAB_LOCK; + recognized_entity *found = _entities->find(entity.mangled_form()); + return !!found; +} + +bool simple_entity_registry::refresh_entity(const octopus_entity &entity) +{ + GRAB_LOCK; + recognized_entity *found = _entities->find(entity.mangled_form()); + if (!found) return false; + // we found their record so update that time stamp. + found->_last_active = time_stamp(); + return true; +} + +bool simple_entity_registry::add_entity(const octopus_entity &entity, + const byte_array &verification) +{ + GRAB_LOCK; + recognized_entity *found = _entities->find(entity.mangled_form()); + if (found) { + // already had a record for this guy so update the time stamp. + found->_last_active = time_stamp(); + // if there's a verification token, make sure we keep their most recent + // version of it. + if (verification.length()) + found->_verification = verification; + } else { + // this is a new entity, so add a new entity record for it. + recognized_entity *new_one = new recognized_entity; + new_one->_entity = entity; + new_one->_verification = verification; + _entities->add(entity.mangled_form(), new_one); + } + return true; +} + +bool simple_entity_registry::zap_entity(const octopus_entity &entity) +{ + GRAB_LOCK; + return _entities->zap(entity.mangled_form()); +} + +bool simple_entity_registry::locate_entity(const octopus_entity &entity, + time_stamp &last_active, byte_array &verification) +{ + GRAB_LOCK; + recognized_entity *found = _entities->find(entity.mangled_form()); + if (!found) return false; + last_active = found->_last_active; + verification = found->_verification; + return true; +} + +bool text_form_applier(const astring &formal(key), recognized_entity &info, + void *datalink) +{ + astring *accum = (astring *)datalink; + *accum += astring("ent=") + info._entity.mangled_form() + ", active=" + + info._last_active.text_form() + + a_sprintf(", %d veribytes", info._verification.length()); + return true; +} + +astring simple_entity_registry::text_form() +{ + astring to_return; + GRAB_LOCK; + _entities->apply(text_form_applier, &to_return); + return to_return; +} + +} //namespace. + diff --git a/octopi/library/tentacles/simple_entity_registry.h b/octopi/library/tentacles/simple_entity_registry.h new file mode 100644 index 00000000..c3edbec2 --- /dev/null +++ b/octopi/library/tentacles/simple_entity_registry.h @@ -0,0 +1,80 @@ +#ifndef SIMPLE_CLIENT_REGISTRY_CLASS +#define SIMPLE_CLIENT_REGISTRY_CLASS + +/*****************************************************************************\ +* * +* Name : simple_entity_registry * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2002-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "entity_registry.h" + +namespace octopi { + +// forward. +class octopus_entity; +class recognized_entity; +class recognized_entity_list; + +//! Provides a basic implementation of an entity_registry for octopus. +/*! + Requests to add an entity are always permitted; the registration is a + formality that allows us to establish a record for the entity. + This base class just implements authorized() by invoking locate_entity() + to check if the entity exists. Thus, using it as a security model really + just requires calling add_entity() whenever a new entity is seen. + A different security model can be implemented in a derived class by over- + riding the authorized() method and making it perform an application- + specific security check. +*/ + +class simple_entity_registry : public entity_registry +{ +public: + simple_entity_registry(); + virtual ~simple_entity_registry(); + + virtual bool authorized(const octopus_entity &entity); + //!< returns true if the "entity" is a registered and authorized entity. + + virtual bool locate_entity(const octopus_entity &entity, + timely::time_stamp &last_active, basis::byte_array &verification); + //!< retrieves the "security_record" for the "entity" if it exists. + /*!< true is returned on success. */ + + virtual bool add_entity(const octopus_entity &entity, + const basis::byte_array &verification); + //!< adds the "entity" to the list of authorized users. + /*!< note that this will still succeed if the entity is already present, + allowing it to serve as an entity refresh method. if the "verification" + has any length, it will be stored in the record for the "entity". */ + + virtual bool refresh_entity(const octopus_entity &entity); + //!< this should be used to refresh the entity's health record. + /*!< it indicates that this entity is still functioning and should not be + removed due to inactivity. */ + + virtual bool zap_entity(const octopus_entity &entity); + //!< removes an "entity" if the entity can be found. + /*!< true is returned if that "entity" existed. */ + + virtual basis::astring text_form(); + //!< shows the contents of the registry. + +private: + basis::mutex *_secure_lock; //!< the synchronizer for our tentacle list. + recognized_entity_list *_entities; //!< list of recognized entities. +}; + +} //namespace. + +#endif + diff --git a/octopi/library/tests_sockets/bcast_spocketer.cpp b/octopi/library/tests_sockets/bcast_spocketer.cpp new file mode 100644 index 00000000..c3a91c5b --- /dev/null +++ b/octopi/library/tests_sockets/bcast_spocketer.cpp @@ -0,0 +1,266 @@ +/*****************************************************************************\ +* * +* Name : broadcast_spocket_tester * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "bcast_spocketer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//using namespace application; +using namespace basis; +//using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace sockets; +using namespace structures; +//using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +const int MAXIMUM_WINSOCK_MTU = 1500; + // the largest chunk of bytes we can receive at once. + +const int MAXIMUM_TRANSFER_WAIT = 40 * SECOND_ms; + // the longest amount of time we wait in trying to receive data. + +static abyte receive_buffer[MAXIMUM_WINSOCK_MTU + 1]; + // used for dumping received data into. + +//#define DEBUG_SPOCKET_TESTER + // uncomment for noisy version. + +broadcast_spocket_tester::broadcast_spocket_tester + (const internet_address &where, bool unicast) +: _where(new internet_address(where)), + _stack(new tcpip_stack), + _socket(NIL), + _raw(new raw_socket), + _ucast(unicast) +{ +} + +broadcast_spocket_tester::~broadcast_spocket_tester() +{ + WHACK(_socket); + WHACK(_stack); + WHACK(_where); + WHACK(_raw); +} + +bool broadcast_spocket_tester::connect() +{ + spocket::sock_types type = spocket::BROADCAST; + if (_ucast) type = spocket::UNICAST; + if (!_socket) { + _socket = new spocket(*_where, type); + } + outcome ret = _socket->connect(); + return ret == spocket::OKAY; +} + +bool broadcast_spocket_tester::do_a_send(const internet_address &where_to, + abyte *buffer, int size, testing_statistics &stats) +{ + time_stamp start_time; + int len_sent; + time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT); + outcome worked; + +#ifdef DEBUG_SPOCKET_TESTER + LOG(a_sprintf("into do a send with %d bytes", size)); +#endif + + while (time_stamp() < when_to_leave) { + worked = _socket->send_to(where_to, buffer, size, len_sent); + if (worked == spocket::NONE_READY) { + time_control::sleep_ms(20); + continue; + } else if (worked == spocket::PARTIAL) { +//danger danger if we get wrong info. + buffer += len_sent; + size -= len_sent; + time_control::sleep_ms(20); + continue; + } else break; + } +#ifdef DEBUG_SPOCKET_TESTER + LOG("got out of loop"); +#endif + + stats.send_time += int(time_stamp().value() - start_time.value()); + + if ( (worked != spocket::OKAY) || !len_sent) { + LOG("No data went out on the socket."); + return false; + } + if (len_sent != size) { + LOG(a_sprintf("The full chunk didn't get sent out: %d bytes instead of %d", + len_sent, size)); +//more bad news. what do we do about getting the rest out? + return false; + } + stats.bytes_sent += len_sent; + + time_stamp end_time; +// int time_taken = int(end_time.value() - start_time.value()); + + return true; +} + +bool broadcast_spocket_tester::do_a_receive(int size_expected, + testing_statistics &stats) +{ + time_stamp start_time; + +#ifdef DEBUG_SPOCKET_TESTER + LOG("into do a rcv"); +#endif + + time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT); + int full_length = 0; + while ( (full_length < size_expected) && (time_stamp() < when_to_leave) ) { + time_stamp start_of_receive; + int len = MAXIMUM_WINSOCK_MTU; + internet_address where_from; + outcome ret = _socket->receive_from(receive_buffer, len, where_from); +///LOG(astring("recvfrom outcome is ") + spocket::outcome_name(ret)); + if (ret != spocket::OKAY) { + if (ret == spocket::NONE_READY) { + time_control::sleep_ms(20); + continue; + } else break; + } + // reset our time if we've gotten good data. + if (ret == spocket::OKAY) + when_to_leave.reset(MAXIMUM_TRANSFER_WAIT); + + int receive_duration = int(time_stamp().value() + - start_of_receive.value()); + stats.receive_time += receive_duration; + +#ifdef DEBUG_SPOCKET_TESTER + LOG(a_sprintf("did recv, len=%d", len)); +#endif + + if (!len) { + LOG("Our socket has been disconnected."); + return false; + } else if (len < 0) { + if (errno == SOCK_EWOULDBLOCK) continue; // no data. + LOG(astring("The receive failed with an error ") + + critical_events::system_error_text(errno)); + return false; + } + full_length += len; + stats.bytes_received += len; + } + + if (full_length != size_expected) + LOG(a_sprintf("Did not get the full size expected (wanted %d and " + "got %d bytes).", size_expected, full_length)); + + return true; +} + +bool broadcast_spocket_tester::perform_test(const internet_address &dest, + int size, int count, testing_statistics &stats) +{ +#ifdef DEBUG_SPOCKET_TESTER + LOG("into perf test"); +#endif + + // the statics are used to generate our random buffer for sending. + static abyte garbage_buffer[MAXIMUM_WINSOCK_MTU + 1]; + static bool garbage_initialized = false; + chaos randomizer; + + // if our static buffer full of random stuff was never initialized, we do + // so now. this supports efficiently re-using the tester if desired. + if (!garbage_initialized) { + // note the less than or equal; we know we have one more byte to fill. + for (int i = 0; i <= MAXIMUM_WINSOCK_MTU; i++) + garbage_buffer[i] = randomizer.inclusive(0, 255); + garbage_initialized = true; + } + + // reset the statistical package. + stats.total_runs = 0; + stats.send_time = 0; + stats.receive_time = 0; + stats.bytes_sent = 0; + stats.bytes_received = 0; + + // check that they aren't trying to do too big of a send. + if (size > MAXIMUM_WINSOCK_MTU) { + LOG("The size is over our limit. To fix this, edit the " + "send_data function."); + return false; + } + + // check that our socket is usable. + if (!_socket) { + LOG("One cannot send data on an uninitialized tester!"); + return false; + } + + int runs_completed = 0; + // counts up how many times we've done our test cycle. + + while (runs_completed < count) { +#ifdef DEBUG_SPOCKET_TESTER + LOG(a_sprintf("iter %d", runs_completed)); +#endif + // we're doing the client side routine here. + time_stamp trip_start; +#ifdef DEBUG_SPOCKET_TESTER + LOG("client about to send"); +#endif + if (!do_a_send(dest, garbage_buffer, size, stats)) { + LOG("We failed on a send. Now quitting."); + return false; + } +#ifdef DEBUG_SPOCKET_TESTER + LOG("client about to rcv"); +#endif + if (!do_a_receive(size, stats)) { + LOG("We failed on a receive. Now quitting."); + return false; + } + stats.round_trip_time += int(time_stamp().value() - trip_start.value()); + + runs_completed++; // finished a run. + stats.total_runs++; // count it in the overall stats too. + if ( !(runs_completed % 10) ) + LOG(a_sprintf("Completed test #%d.", runs_completed)); + } + + return true; +} + diff --git a/octopi/library/tests_sockets/bcast_spocketer.h b/octopi/library/tests_sockets/bcast_spocketer.h new file mode 100644 index 00000000..bf171c15 --- /dev/null +++ b/octopi/library/tests_sockets/bcast_spocketer.h @@ -0,0 +1,81 @@ +#ifndef TEST_CROMP_CLASS +#define TEST_CROMP_CLASS + +/*****************************************************************************\ +* * +* Name : broadcast_spocket_tester * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Puts the spocket class in broadcast mode through some test paces. * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +////#include +#include +#include +#include + +// this structure is filled by the tester during a send data call. +class testing_statistics +{ +public: + int total_runs; // how many cycles did we successfully do? + int bytes_sent; // overall count of bytes sent. + int bytes_received; // overall count of bytes received. + int send_time; // time taken just to invoke "send" function. + int receive_time; // time taken just to invoke "recv" function. + int round_trip_time; // time taken between sending and receiving back. + + testing_statistics() + : total_runs(0), bytes_sent(0), bytes_received(0), send_time(0), + receive_time(0), round_trip_time(0) {} +}; + +// our main tester class. +class broadcast_spocket_tester +{ +public: + broadcast_spocket_tester(const sockets::internet_address &where, + bool unicast = false); + // constructs the tester object. "where" provides information about either + // this side (for a server) or the other side (for a client). + // if "unicast" is true, then we test unicasts instead of broadcasts. + + ~broadcast_spocket_tester(); + // destroys the tester object. + + DEFINE_CLASS_NAME("broadcast_spocket_tester"); + + bool connect(); + // gets ready for sending and reception. + + bool do_a_send(const sockets::internet_address &where_to, basis::abyte *buffer, int size, + testing_statistics &stats); + + bool do_a_receive(int size_expected, testing_statistics &stats); + + bool perform_test(const sockets::internet_address &dest, int size, int count, + testing_statistics &stats_to_fill); + // sends "count" random data chunks of the "size" specified. the measured + // performance during this sending is reported in "stats_to_fill". + +private: + sockets::internet_address *_where; // our communications endpoint. + sockets::tcpip_stack *_stack; // provides access to the operating system layers. + sockets::spocket *_socket; // does the communication for us. + sockets::spocket *_root_server; // used for server side testing. + sockets::raw_socket *_raw; // provides functions on sockets. + bool _ucast; // true if we're unicasting. +}; + +#endif + diff --git a/octopi/library/tests_sockets/makefile b/octopi/library/tests_sockets/makefile new file mode 100644 index 00000000..1dad659d --- /dev/null +++ b/octopi/library/tests_sockets/makefile @@ -0,0 +1,16 @@ +include cpp/variables.def + +PROJECT = tests_sockets +TYPE = test +SOURCE = bcast_spocketer.cpp spocket_tester.cpp +TARGETS = test_address.exe test_bcast_spocket.exe test_enum_adapters.exe \ + test_sequence_tracker.exe test_span_manager.exe test_spocket.exe test_ucast_spocket.exe +LOCAL_LIBS_USED = sockets unit_test application configuration loggers textual timely \ + processes filesystem structures basis +VCPP_USE_SOCK = t +ifeq "$(OP_SYSTEM)" "WIN32" + LIBS_USED += ws2_32.lib +endif +RUN_TARGETS = $(ACTUAL_TARGETS) + +include cpp/rules.def diff --git a/octopi/library/tests_sockets/spocket_tester.cpp b/octopi/library/tests_sockets/spocket_tester.cpp new file mode 100644 index 00000000..d5f731c0 --- /dev/null +++ b/octopi/library/tests_sockets/spocket_tester.cpp @@ -0,0 +1,316 @@ +/*****************************************************************************\ +* * +* Name : spocket_tester * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include "spocket_tester.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//using namespace application; +using namespace basis; +//using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace sockets; +using namespace structures; +using namespace textual; +using namespace timely; +//using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +const int MAXIMUM_WINSOCK_MTU = 100000; + // the largest chunk of bytes we will receive at one time. + +const int MAXIMUM_TRANSFER_WAIT = 40 * SECOND_ms; + // the longest amount of time we wait in trying to receive data. + +static abyte receive_buffer[MAXIMUM_WINSOCK_MTU + 1]; + // used for dumping received data into. + +const int PAUSE_TIME = 200; + // the snooze interval when we encounter socket underflow or overflow. + +//#define DEBUG_SPOCKET_TESTER + // uncomment for noisy version. + +spocket_tester::spocket_tester(const internet_address &where) +: _where(new internet_address(where)), + _stack(new tcpip_stack), + _socket(NIL), + _root_server(NIL), + _raw(new raw_socket) +{ +} + +spocket_tester::~spocket_tester() +{ + WHACK(_socket); + WHACK(_root_server); + WHACK(_stack); + WHACK(_where); + WHACK(_raw); +} + +bool spocket_tester::connect() +{ + if (!_socket) { + _socket = new spocket(*_where); + } + outcome ret = spocket::NO_CONNECTION; + while (true) { + ret = _socket->connect(); + if (ret == spocket::OKAY) break; + if (ret != spocket::NO_CONNECTION) break; + time_control::sleep_ms(100); + } + return ret == spocket::OKAY; +} + +bool spocket_tester::accept(bool wait) +{ + if (!_root_server) { + _root_server = new spocket(*_where); + } + if (_socket) { + LOG("already have a socket for accept!"); + return true; + } + outcome ret = spocket::NO_CONNECTION; + while (true) { + ret = _root_server->accept(_socket, false); + if (ret == spocket::OKAY) break; + if (ret != spocket::NO_CONNECTION) break; + if (!wait) return true; // we accepted at least once. + time_control::sleep_ms(100); + } + return ret == spocket::OKAY; +} + +bool spocket_tester::do_a_send(abyte *buffer, int size, + testing_statistics &stats) +{ + time_stamp start_time; + int len_sent; + time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT); + outcome worked; + +#ifdef DEBUG_SPOCKET_TESTER + LOG("into do a send"); +#endif + + while (time_stamp() < when_to_leave) { + worked = _socket->send(buffer, size, len_sent); + if (worked == spocket::NONE_READY) { +//// time_control::sleep_ms(PAUSE_TIME); + _socket->await_writable(PAUSE_TIME); + continue; + } else if (worked == spocket::PARTIAL) { +//danger danger if we get wrong info. + buffer += len_sent; + size -= len_sent; + stats.bytes_sent += len_sent; +/// time_control::sleep_ms(PAUSE_TIME); + _socket->await_writable(PAUSE_TIME); + continue; + } else break; + } +#ifdef DEBUG_SPOCKET_TESTER + LOG("got out of loop"); +#endif + + stats.send_time += int(time_stamp().value() - start_time.value()); + stats.bytes_sent += len_sent; + + if ( (worked != spocket::OKAY) && (worked != spocket::PARTIAL) ) { + LOG(astring("No data went out on the socket: ") + + spocket::outcome_name(worked)); + return false; + } + if (len_sent != size) { + LOG(a_sprintf("partial send on socket, %d bytes instead of %d, recurse.", + len_sent, size)); + return do_a_send(buffer + len_sent, size - len_sent, stats); + } + + time_stamp end_time; +// int time_taken = int(end_time.value() - start_time.value()); + + return true; +} + +bool spocket_tester::do_a_receive(int size_expected, testing_statistics &stats) +{ + time_stamp start_time; + +#ifdef DEBUG_SPOCKET_TESTER + LOG("into do a rcv"); +#endif + + time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT); + int full_length = 0; + while ( (full_length < size_expected) && (time_stamp() < when_to_leave) ) { + time_stamp start_of_receive; + int len = MAXIMUM_WINSOCK_MTU; + outcome ret = _socket->receive(receive_buffer, len); + if (ret != spocket::OKAY) { + if (ret == spocket::NONE_READY) { +if (len != 0) LOG(a_sprintf("supposedly nothing was received (%d bytes)", len)); +/// time_control::sleep_ms(PAUSE_TIME); + _socket->await_readable(PAUSE_TIME); + continue; + } else break; + } + // reset our time if we've gotten good data. + if (ret == spocket::OKAY) + when_to_leave.reset(MAXIMUM_TRANSFER_WAIT); + + int receive_duration = int(time_stamp().value() + - start_of_receive.value()); + stats.receive_time += receive_duration; + +#ifdef DEBUG_SPOCKET_TESTER + LOG(a_sprintf("did recv, len=%d", len)); +#endif + + if (!len) { + LOG("Our socket has been disconnected."); + return false; + } else if (len < 0) { + if (errno == SOCK_EWOULDBLOCK) continue; // no data. + LOG(astring("The receive failed with an error ") + + critical_events::system_error_text(errno)); + return false; + } + full_length += len; + stats.bytes_received += len; + } + + if (full_length != size_expected) + LOG(a_sprintf("Did not get the full size expected (wanted %d and " + "got %d bytes).", size_expected, full_length)); + + return true; +} + +bool spocket_tester::perform_test(int size, int count, + testing_statistics &stats) +{ +#ifdef DEBUG_SPOCKET_TESTER + LOG("into perf test"); +#endif + + // the statics are used to generate our random buffer for sending. + static abyte garbage_buffer[MAXIMUM_WINSOCK_MTU + 1]; + static bool garbage_initialized = false; + chaos randomizer; + + // if our static buffer full of random stuff was never initialized, we do + // so now. this supports efficiently re-using the tester if desired. + if (!garbage_initialized) { + LOG("initializing random send buffer."); + // note the less than or equal; we know we have one more byte to fill. + for (int i = 0; i <= MAXIMUM_WINSOCK_MTU; i++) + garbage_buffer[i] = randomizer.inclusive(0, 255); + garbage_initialized = true; + LOG("random send buffer initialized."); + } + + // reset the statistical package. + stats.total_runs = 0; + stats.send_time = 0; + stats.receive_time = 0; + stats.bytes_sent = 0; + stats.bytes_received = 0; + + // check that they aren't trying to do too big of a send. + if (size > MAXIMUM_WINSOCK_MTU) { + LOG("The size is over our limit. To fix this, edit the " + "send_data function."); + return false; + } + + // check that our socket is usable. + if (!_socket) { + LOG("One cannot send data on an uninitialized tester!"); + return false; + } + + int runs_completed = 0; + // counts up how many times we've done our test cycle. + + while (runs_completed < count) { +#ifdef DEBUG_SPOCKET_TESTER + LOG(a_sprintf("iter %d", runs_completed)); +#endif + if (_socket->client()) { + // we're doing the client side routine here. + time_stamp trip_start; +#ifdef DEBUG_SPOCKET_TESTER + LOG("client about to send"); +#endif + if (!do_a_send(garbage_buffer, size, stats)) { + LOG("We failed on a send. Now quitting."); + return false; + } +#ifdef DEBUG_SPOCKET_TESTER + LOG("client about to rcv"); +#endif + if (!do_a_receive(size, stats)) { + LOG("We failed on a receive. Now quitting."); + return false; + } + stats.round_trip_time += int(time_stamp().value() - trip_start.value()); + } else { + // we're doing the server side routine here. + time_stamp trip_start; +#ifdef DEBUG_SPOCKET_TESTER + LOG("server about to rcv"); +#endif + if (!do_a_receive(size, stats)) { + LOG("We failed on a receive. Now quitting."); + return false; + } +#ifdef DEBUG_SPOCKET_TESTER + LOG("server about to send"); +#endif + if (!do_a_send(garbage_buffer, size, stats)) { + LOG("We failed on a send. Now quitting."); + return false; + } + stats.round_trip_time += int(time_stamp().value() - trip_start.value()); + } + + runs_completed++; // finished a run. + stats.total_runs++; // count it in the overall stats too. + if ( !(runs_completed % 10) ) + LOG(a_sprintf("Completed test #%d.", runs_completed)); + } + + return true; +} + diff --git a/octopi/library/tests_sockets/spocket_tester.h b/octopi/library/tests_sockets/spocket_tester.h new file mode 100644 index 00000000..f4f719c2 --- /dev/null +++ b/octopi/library/tests_sockets/spocket_tester.h @@ -0,0 +1,76 @@ +#ifndef TEST_CROMP_CLASS +#define TEST_CROMP_CLASS + +/*****************************************************************************\ +* * +* Name : spocket_tester * +* Author : Chris Koeritz * +* * +* Purpose: * +* * +* Puts the spocket class through some test paces. * +* * +******************************************************************************* +* Copyright (c) 2000-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include + +// this structure is filled by the tester during a send data call. +class testing_statistics +{ +public: + int total_runs; // how many cycles did we successfully do? + int bytes_sent; // overall count of bytes sent. + int bytes_received; // overall count of bytes received. + int send_time; // time taken just to invoke "send" function. + int receive_time; // time taken just to invoke "recv" function. + int round_trip_time; // time taken between sending and receiving back. + + testing_statistics() + : total_runs(0), bytes_sent(0), bytes_received(0), send_time(0), + receive_time(0), round_trip_time(0) {} +}; + +// our main tester class. +class spocket_tester +{ +public: + spocket_tester(const sockets::internet_address &where); + // constructs the tester object. "where" provides information about either + // this side (for a server) or the other side (for a client). + + ~spocket_tester(); + // destroys the tester object. + + bool connect(); + // acts as a client and connects to a destination. + + bool accept(bool wait = true); + // acts as a server and accepts a connection from a client. + + bool do_a_send(basis::abyte *buffer, int size, testing_statistics &stats); + + bool do_a_receive(int size_expected, testing_statistics &stats); + + bool perform_test(int size, int count, testing_statistics &stats_to_fill); + // sends "count" random data chunks of the "size" specified. the measured + // performance during this sending is reported in "stats_to_fill". + +private: + sockets::internet_address *_where; // our communications endpoint. + sockets::tcpip_stack *_stack; // provides access to the operating system layers. + sockets::spocket *_socket; // does the communication for us. + sockets::spocket *_root_server; // used for server side testing. + sockets::raw_socket *_raw; // provides functions on sockets. +}; + +#endif + diff --git a/octopi/library/tests_sockets/t_sockets_version.rc b/octopi/library/tests_sockets/t_sockets_version.rc new file mode 100644 index 00000000..019863e0 --- /dev/null +++ b/octopi/library/tests_sockets/t_sockets_version.rc @@ -0,0 +1,46 @@ +#ifndef NO_VERSION +#include +#include <__build_version.h> +#include <__build_configuration.h> +#define BI_PLAT_WIN32 + // force 32 bit compile. +1 VERSIONINFO LOADONCALL MOVEABLE +FILEVERSION __build_FILE_VERSION_COMMAS +PRODUCTVERSION __build_PRODUCT_VERSION_COMMAS +FILEFLAGSMASK 0 +FILEFLAGS VS_FFI_FILEFLAGSMASK +#if defined(BI_PLAT_WIN32) + FILEOS VOS__WINDOWS32 +#else + FILEOS VOS__WINDOWS16 +#endif +FILETYPE VFT_APP +BEGIN + BLOCK "StringFileInfo" + BEGIN + // Language type = U.S. English(0x0409) and Character Set = Windows, Multilingual(0x04b0) + BLOCK "040904b0" // Matches VarFileInfo Translation hex value. + BEGIN + VALUE "CompanyName", __build_company "\000" +#ifndef _DEBUG + VALUE "FileDescription", "Test for Sockets Library\000" +#else + VALUE "FileDescription", "Test for Sockets Library (DEBUG)\000" +#endif + VALUE "FileVersion", __build_FILE_VERSION "\000" + VALUE "ProductVersion", __build_PRODUCT_VERSION "\000" + VALUE "InternalName", "t_sockets\000" + VALUE "LegalCopyright", __build_copyright "\000" + VALUE "LegalTrademarks", __build_legal_info "\000" + VALUE "OriginalFilename", "t_sockets.exe\000" + VALUE "ProductName", __build_product_name "\000" + + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04b0 // US English (0x0409) and win32 multilingual (0x04b0) + END +END +#endif diff --git a/octopi/library/tests_sockets/test_address.cpp b/octopi/library/tests_sockets/test_address.cpp new file mode 100644 index 00000000..ddf7e8ad --- /dev/null +++ b/octopi/library/tests_sockets/test_address.cpp @@ -0,0 +1,129 @@ +/* +* Name : test_address +* Author : Chris Koeritz +* Purpose: Tests some attributes of the network_address class for correctness. +** +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +//using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace sockets; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +//#include +//#include + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +class test_address : public virtual unit_base, virtual public application_shell +{ +public: + test_address() {} + DEFINE_CLASS_NAME("test_address"); + virtual int execute(); +}; + +int test_address::execute() +{ + FUNCDEF("execute"); + // testing is_valid_internet_address here... + + bool all_zero = false; + byte_array ip_form; + astring to_test = "12.5.55.37"; + if (!internet_address::is_valid_internet_address(to_test, ip_form, all_zero)) + deadly_error(class_name(), "1st address", + "failed to say address was valid"); + if (all_zero) + deadly_error(class_name(), "1st address", "said address was all zeros"); + if ( (ip_form[0] != 12) || (ip_form[1] != 5) || (ip_form[2] != 55) + || (ip_form[3] != 37) ) + deadly_error(class_name(), "1st address", "address had incorrect contents"); + + to_test = "12.5.55.372"; + if (internet_address::is_valid_internet_address(to_test, ip_form, all_zero)) + deadly_error(class_name(), "2nd address", "failed to say address was invalid"); + + to_test = "12.5.55.37.3"; + if (internet_address::is_valid_internet_address(to_test, ip_form, all_zero)) + deadly_error(class_name(), "3rd address", "failed to say address was invalid"); + + to_test = "12.5.55"; + if (internet_address::is_valid_internet_address(to_test, ip_form, all_zero)) + deadly_error(class_name(), "4th address", "failed to say address was invalid"); + + to_test = "0.0.0.0"; + if (!internet_address::is_valid_internet_address(to_test, ip_form, all_zero)) + deadly_error(class_name(), "5th address", "failed to say address was valid"); + if (!all_zero) + deadly_error(class_name(), "5th address", "said address was not all zeros"); + if ( (ip_form[0] != 0) || (ip_form[1] != 0) || (ip_form[2] != 0) + || (ip_form[3] != 0) ) + deadly_error(class_name(), "5th address", "address had incorrect contents"); + + to_test = "0.0.0.1"; + if (!internet_address::is_valid_internet_address(to_test, ip_form, all_zero)) + deadly_error(class_name(), "6th address", "failed to say address was valid"); + if (all_zero) + deadly_error(class_name(), "6th address", "said address was all zeros"); + if ( (ip_form[0] != 0) || (ip_form[1] != 0) || (ip_form[2] != 0) + || (ip_form[3] != 1) ) + deadly_error(class_name(), "6th address", "address had incorrect contents"); + + to_test = "0.0.0."; + if (internet_address::is_valid_internet_address(to_test, ip_form, all_zero)) + deadly_error(class_name(), "7th address", "failed to say address was invalid"); + + to_test = "0.0.0"; + if (internet_address::is_valid_internet_address(to_test, ip_form, all_zero)) + deadly_error(class_name(), "7th address", "failed to say address was invalid"); + + to_test = "this may have. an ip address in it somewhere... 92.21. 23.123. 1235.6.3 9 oops hey where is it. 23.51.2 2.4 1.2.343 09023.2.3. marbles 23.15.123.5 manus kobble 23.1.5.2 sturp nort ation"; + astring found; + if (!internet_address::has_ip_address(to_test, found)) + deadly_error(class_name(), "8th address", "failed to find ip address"); + ASSERT_EQUAL(found, astring("23.15.123.5"), "8th address: ip address found should be correct"); + + to_test = "furples 92.23.1 9123.5.3 12398. 23 11 1 1 0202 2.4.1.5"; + if (!internet_address::has_ip_address(to_test, found)) + deadly_error(class_name(), "9th address", "failed to find ip address"); + if (found != astring("2.4.1.5")) + deadly_error(class_name(), "9th address", astring("ip address found was wrong: ") + found + ", should have been 2.4.1.5"); + + to_test = "12.5.55.2\""; + if (!internet_address::has_ip_address(to_test, found)) + deadly_error(class_name(), "10th address", "failed to find ip address"); + if (found != astring("12.5.55.2")) + deadly_error(class_name(), "10th address", astring("ip address found was wrong: ") + found + ", should have been 12.5.55.2"); + + to_test = "12.5.55.2"; + if (!internet_address::has_ip_address(to_test, found)) + deadly_error(class_name(), "11th address", "failed to find ip address"); + if (found != astring("12.5.55.2")) + deadly_error(class_name(), "11th address", astring("ip address found was wrong: ") + found + ", should have been 12.5.55.2"); + + return final_report(); +} + +HOOPLE_MAIN(test_address, ) + diff --git a/octopi/library/tests_sockets/test_bcast_spocket.cpp b/octopi/library/tests_sockets/test_bcast_spocket.cpp new file mode 100644 index 00000000..232d0fe9 --- /dev/null +++ b/octopi/library/tests_sockets/test_bcast_spocket.cpp @@ -0,0 +1,262 @@ +/* +* Name : test_bcast_spocket +* Author : Chris Koeritz +* Purpose: This is the "main" program for our sockets tester. It parses command line +* parameters and starts up the tester class. +** +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include "bcast_spocketer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace application; +using namespace basis; +using namespace filesystem; +using namespace loggers; +using namespace mathematics; +using namespace processes; +using namespace sockets; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +const int INITIAL_DELAY = 1; // number of seconds before starting sends. + +typedef abyte ip_address_holder[4]; + +class test_bcast_spocket : public virtual unit_base, public virtual application_shell +{ +public: + test_bcast_spocket() {} + DEFINE_CLASS_NAME("test_bcast_spocket"); + virtual int execute(); + bool parse_address(const astring &input, ip_address_holder &ip_address); +}; + +//hmmm: extract this to function in sockets. +bool test_bcast_spocket::parse_address(const astring &input, ip_address_holder &ip_address) +{ + int index = 0; // current storage position in our array. + int current_byte = 0; + const char *last_period = 0; // helps check for non-empty numbers. + bool got_digit = false; + for (const char *address = input.s(); *address; address++) { + if ( (*address <= '9') && (*address >= '0') ) { + current_byte *= 10; // shift over. + current_byte += *address - '0'; // add in current character. + got_digit = true; + } else if (*address == '.') { + got_digit = false; + if (last_period + 1 == address) { + LOG("The IP address entry has an empty digit. Exiting."); + return false; + } + last_period = address; // set our last period location. + if (current_byte > 255) { + LOG("The IP address entry has an illegal abyte. Exiting."); + return false; + } + ip_address[index] = abyte(current_byte); // store current byte. + current_byte = 0; // reset. + index++; // next place in array. + if (index > 3) break; + // stop if there are too many periods, but keep accumulated address. + } else { + LOG("The IP address entry has illegal characters. Exiting."); + return false; + } + } + // catch the case where we ran out of chars before adding the last byte. + if ( (index == 3) && got_digit) { + if (current_byte > 255) { + LOG("The IP address entry has an illegal abyte. Exiting."); + return false; + } + ip_address[index] = current_byte; + } else if (index < 4) { + LOG("The IP address entry is too short. Exiting."); + return false; + } + return true; +} + +int test_bcast_spocket::execute() +{ + FUNCDEF("execute"); + ip_address_holder ip_address; // accumulates the source address. + ip_address_holder dest_addr; // where to send stuff to. + int rcv_port = 0; + int send_port = 0; + int send_size = 0; + int send_count = 0; + + const char *DEFAULT_HOST = "127.0.0.1"; + const int DEFAULT_PORT = 12342; + const int DEFAULT_SEND_SIZE = 1008; + const int DEFAULT_SEND_COUNT = 10; + + if (_global_argc < 7) { + if (_global_argc > 1) { + LOG("\ +This program takes six command line arguments to begin operation.\n\ +These arguments (in order) are:\n\ +\tIP address for src\tIn the form w.x.y.z\n\ +\tIP address for dest\tIn the form w.x.y.z\n\ +\tReceive Port number\tAs a short integer\n\ +\tSending Port number\tAs a short integer\n\ +\tSend size\t\tThe size of the data to exchange.\n\ +\tSend count\t\tThe number of \"packets\" to exchange.\n\ +Note: it is expected that the testers have equal send sizes; this\n\ +allows the receiver to know when it's gotten all the data that's\n\ +expected during a cycle."); + return 1; // bail if they provided anything; otherwise we test. + } else { + parse_address(DEFAULT_HOST, ip_address); + parse_address(DEFAULT_HOST, dest_addr); + rcv_port = DEFAULT_PORT; + send_port = DEFAULT_PORT + 1; + send_size = DEFAULT_SEND_SIZE; + send_count = DEFAULT_SEND_COUNT; + } + } + + // only parse the parameters if we got enough from the user; otherwise we accept our + // defaults to do a simple test run. + if (_global_argc >= 7) { + + if (!parse_address(_global_argv[1], ip_address)) { + LOG("failed to parse source address."); + return 9283; + } + + LOG(a_sprintf("\tParsed a source of: \"%d.%d.%d.%d\".", + (int)ip_address[0], (int)ip_address[1], (int)ip_address[2], + (int)ip_address[3])); + + if (!parse_address(_global_argv[2], dest_addr)) { + LOG("failed to parse dest address."); + return 9283; + } + + LOG(a_sprintf("\tParsed a destination of: \"%d.%d.%d.%d\".", + (int)dest_addr[0], (int)dest_addr[1], (int)dest_addr[2], + (int)dest_addr[3])); + + // parse the third parameter: the port. + if (sscanf(_global_argv[3], "%d", &rcv_port) < 1) { + LOG("The port entry is malformed. Exiting."); + return 3; + } + LOG(a_sprintf("\tGot a receive port of %d.", rcv_port)); + + // parse the fourth parameter: the port. + if (sscanf(_global_argv[4], "%d", &send_port) < 1) { + LOG("The port entry is malformed. Exiting."); + return 3; + } + LOG(a_sprintf("\tGot a send port of %d.", send_port)); + + // parse the fifth parameter: the size of the sends. + if (sscanf(_global_argv[5], "%d", &send_size) < 1) { + LOG("The send size entry is malformed. Exiting."); + return 5; + } + LOG(a_sprintf("\tGot a send size of %d.", send_size)); + + // parse the sixth parameter: the number of sends. + if (sscanf(_global_argv[6], "%d", &send_count) < 1) { + LOG("The send count entry is malformed. Exiting."); + return 5; + } + LOG(a_sprintf("\tGot a send count of %d.", send_count)); + } + + if (_global_argc == 1) { + // launch a paired duplicate of our test so we can chat. + launch_process zingit; + un_int kidnum; + un_int result = zingit.run(_global_argv[0], + astring(DEFAULT_HOST) + " " + DEFAULT_HOST + " " + /* we have reversed the send and receive ports. */ + + a_sprintf("%d", DEFAULT_PORT + 1) + " " + a_sprintf("%d", DEFAULT_PORT) + + " " + a_sprintf("%d", DEFAULT_SEND_SIZE) + " " + a_sprintf("%d", DEFAULT_SEND_COUNT), + launch_process::RETURN_IMMEDIATELY, kidnum); + ASSERT_EQUAL(result, 0, "launching paired process should start successfully"); + } + + // package our parameters in a form the tester likes. + internet_address to_pass(byte_array(4, ip_address), "", rcv_port); + internet_address dest(byte_array(4, dest_addr), "", send_port); + + // now, construct our tester object. + broadcast_spocket_tester tester(to_pass); + + // choose the appropriate action based on our role. + bool outcome = tester.connect(); + if (!outcome) { + LOG(astring("Failed to connect on the tester.")); + return 10; + } + + LOG(a_sprintf("you now have %d seconds; get other side ready.", + INITIAL_DELAY)); + time_control::sleep_ms(INITIAL_DELAY * SECOND_ms); + LOG("starting test"); + + // so, we're connected. try sending the test packages out. + testing_statistics stats; // to be filled by the tester. + outcome = tester.perform_test(dest, send_size, send_count * 2, stats); + // multiply send_count since we count each side as one. + if (!outcome) { + LOG("Failed out of send_data; maybe other side terminated."); + } + + stats.total_runs /= 2; // cut down to the real number of tests. + + if (!stats.total_runs) + stats.total_runs = 1; + + // now report on the stats that we get from the data sending. + LOG(a_sprintf("Report for %d completed test cycles.", stats.total_runs)); + LOG(""); + LOG("\t\tsend stats\t\treceive stats"); + LOG("\t\t----------\t\t-------------"); + LOG(a_sprintf("bytes\t\t%d\t\t\t%d", stats.bytes_sent, + stats.bytes_received)); + LOG(a_sprintf("time\t\t%d\t\t\t%d", stats.send_time, stats.receive_time)); + LOG(a_sprintf("avg. bytes\t%d\t\t\t%d", stats.bytes_sent + / stats.total_runs / 2, stats.bytes_received / stats.total_runs / 2)); + LOG(""); + LOG(a_sprintf("round trip time: %d ms", stats.round_trip_time)); +//hmmm: use the bandwidth measurer object!!! + double bandwidth = double(stats.bytes_sent + stats.bytes_received) + / stats.round_trip_time / 1024.0 * 1000.0; + LOG(a_sprintf("bandwidth overall: %f K/s", bandwidth)); + + if (_global_argc == 1) return final_report(); + else return 0; // no unit test report for non-top-level process +} + +HOOPLE_MAIN(test_bcast_spocket, ); + diff --git a/octopi/library/tests_sockets/test_enum_adapters.cpp b/octopi/library/tests_sockets/test_enum_adapters.cpp new file mode 100644 index 00000000..539d33d6 --- /dev/null +++ b/octopi/library/tests_sockets/test_enum_adapters.cpp @@ -0,0 +1,65 @@ +/* +* Name : test_enumerate_adapters * +* Author : Chris Koeritz * +* Purpose: Makes sure that the adapter enumerator function is working properly. +** +* Copyright (c) 2003-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +//#include + +using namespace application; +using namespace basis; +using namespace loggers; +using namespace mathematics; +using namespace sockets; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +class test_enum_adapaters : public virtual unit_base, virtual public application_shell +{ +public: + test_enum_adapaters() {} + DEFINE_CLASS_NAME("test_enum_adapaters"); + virtual int execute(); +}; + +int test_enum_adapaters::execute() +{ + FUNCDEF("execute"); + tcpip_stack stack; + + string_array ips; + bool did_it = stack.enumerate_adapters(ips); + if (!did_it) + deadly_error(class_name(), func, "could not enumerate adapters"); + + for (int i = 0; i < ips.length(); i++) { + log(a_sprintf("%d: ", i+1) + ips[i]); + } + + return final_report(); +} + +HOOPLE_MAIN(test_enum_adapaters, ) + diff --git a/octopi/library/tests_sockets/test_sequence_tracker.cpp b/octopi/library/tests_sockets/test_sequence_tracker.cpp new file mode 100644 index 00000000..9db9be4d --- /dev/null +++ b/octopi/library/tests_sockets/test_sequence_tracker.cpp @@ -0,0 +1,119 @@ +/* +* Name : test_sequence_tracker +* Author : Chris Koeritz +* Purpose: Runs a couple of tests on the sequence tracker object. +** +* Copyright (c) 2003-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include +//#include + +using namespace application; +using namespace basis; +using namespace loggers; +using namespace mathematics; +using namespace sockets; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +class test_sequence_tracker : public virtual unit_base, virtual public application_shell +{ +public: + test_sequence_tracker() {} + DEFINE_CLASS_NAME("test_sequence_tracker"); + virtual int execute(); +}; + +int test_sequence_tracker::execute() +{ + FUNCDEF("execute"); + // some arbitrary ip addresses. + abyte arb1[] = { 127, 0, 0, 1 }; + abyte arb2[] = { 192, 168, 0, 1 }; + abyte arb3[] = { 28, 42, 56, 253 }; + + machine_uid eep(machine_uid::TCPIP_LOCATION, byte_array(4, arb1)); + machine_uid op(machine_uid::TCPIP_LOCATION, byte_array(4, arb2)); + machine_uid ork(machine_uid::TCPIP_LOCATION, byte_array(4, arb3)); + + sequence_tracker chevy(1 * MINUTE_ms, 10 * MINUTE_ms); + + int_set eep_set; + int adds = randomizer().inclusive(400, 900); + int starter = 12092; + while (adds--) { + int seq = starter + randomizer().inclusive(1, 129); + eep_set += seq; + chevy.add_pair(eep, seq); + } + + int_set op_set; + adds = randomizer().inclusive(200, 600); + starter = 1222; + while (adds--) { + int seq = starter + randomizer().inclusive(1, 129); + op_set += seq; + chevy.add_pair(op, seq); + } + + int_set ork_set; + adds = randomizer().inclusive(200, 600); + starter = 992981; + while (adds--) { + int seq = starter + randomizer().inclusive(1, 129); + ork_set += seq; + chevy.add_pair(ork, seq); + } + + int i; + for (i = 0; i < eep_set.elements(); i++) { + int seq = eep_set[i]; + if (!chevy.have_seen(eep, seq)) { + log(a_sprintf("missing sequence is %d", seq)); + log(chevy.text_form(true)); + deadly_error("test_sequence_tracker", "eep check", "missing sequence"); + } + } + for (i = 0; i < op_set.elements(); i++) { + int seq = op_set[i]; + if (!chevy.have_seen(op, seq)) { + log(a_sprintf("missing sequence is %d", seq)); + log(chevy.text_form(true)); + deadly_error("test_sequence_tracker", "op check", "missing sequence"); + } + } + for (i = 0; i < ork_set.elements(); i++) { + int seq = ork_set[i]; + if (!chevy.have_seen(ork, seq)) { + log(a_sprintf("missing sequence is %d", seq)); + log(chevy.text_form(true)); + deadly_error("test_sequence_tracker", "ork check", "missing sequence"); + } + } + + return final_report(); +} + +HOOPLE_MAIN(test_sequence_tracker, ) + diff --git a/octopi/library/tests_sockets/test_span_manager.cpp b/octopi/library/tests_sockets/test_span_manager.cpp new file mode 100644 index 00000000..5e168aed --- /dev/null +++ b/octopi/library/tests_sockets/test_span_manager.cpp @@ -0,0 +1,158 @@ +/*****************************************************************************\ +* * +* Name : test_span_manager * +* Author : Chris Koeritz * +* * +******************************************************************************* +* Copyright (c) 1991-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace application; +using namespace basis; +//using namespace filesystem; +using namespace loggers; +//using namespace mathematics; +using namespace sockets; +using namespace structures; +//using namespace textual; +//using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +#define DEBUG_SPAN_MANAGER + // uncomment for noisier output. + +#define MAX_SPANS 8 + +// INIT_STUFF gets the macros ready for use. +#define INIT_STUFF int_array stuffer; + +// STUFF puts two numbers (a span) into the next place in the storage array. +#define STUFF(a, b) { \ + ASSERT_FALSE(stuffer.length() / 2 + 1 > MAX_SPANS, "STUFF: too many for spans"); \ + stuffer.concatenate(a); stuffer.concatenate(b); \ +} + +// SEND updates the span manager with the current state of the storage array and resets the +// counter for the next set of spans. +#define SEND(send_to) \ + send_to.update(stuffer); stuffer.reset(0); +/// stuffer.number_of_spans = stuff_index / 2; \ old + +// COMP compares the list of spans at position "i" with a value "to_compare". +#define COMP(to_compare, i) { \ + ASSERT_EQUAL(to_compare, rec_list[i], "comparison found incorrect"); \ + if (to_compare != rec_list[i]) LOG(a_sprintf("failed at index %d", i)); \ +} + +class test_span_manager : public virtual unit_base, public virtual application_shell +{ +public: + test_span_manager() {} + DEFINE_CLASS_NAME("test_span_manager"); + virtual int execute(); +}; + +int test_span_manager::execute() +{ + FUNCDEF("execute"); + span_manager fred(452); + + INIT_STUFF; + + // stuffs a bunch of spans into the storage array. + STUFF(8, 8); + STUFF(27, 29); + STUFF(28, 31); + STUFF(3, 5); + STUFF(80, 83); + STUFF(96, 123); + STUFF(3, 6); + STUFF(212, 430); + SEND(fred); + + // stuffs the rest few in. + STUFF(13, 17); + STUFF(151, 250); + SEND(fred); + +#ifdef DEBUG_SPAN_MANAGER + LOG(astring(astring::SPRINTF, "received sequence so far is %d", + fred.received_sequence())); + LOG("making received list:"); +#endif + + int_array rec_list; + fred.make_received_list(rec_list); + +#ifdef DEBUG_SPAN_MANAGER + LOG(fred.print_received_list()); +#endif + + // the loop goes through the list of received spans and sees if they're + // in the right places. + for (int i = 0; i < rec_list.length(); i += 2) { + switch (i) { + case 0: COMP(3, i); COMP(6, i+1); break; + case 2: COMP(8, i); COMP(8, i+1); break; + case 4: COMP(13, i); COMP(17, i+1); break; + case 6: COMP(27, i); COMP(31, i+1); break; + case 8: COMP(80, i); COMP(83, i+1); break; + case 10: COMP(96, i); COMP(123, i+1); break; + case 12: COMP(151, i); COMP(430, i+1); break; + } + } + + ASSERT_EQUAL(fred.missing_sequence(), 0, "missing: should not have wrong sequence"); + + // some more items are stuffed in, including the beginning few. + STUFF(0, 5); + STUFF(7, 7); + STUFF(9, 12); + SEND(fred); + ASSERT_EQUAL(fred.missing_sequence(), 18, "missing 2: should not have wrong sequence"); +#ifdef DEBUG_SPAN_MANAGER + LOG("here are the missing ones now:"); + LOG(fred.print_missing_list()); +#endif + fred.make_missing_list(rec_list); + // this loop looks into the list of missing places and makes sure they're + // the right holes. + for (int j = 0; j < rec_list.length(); j+=2) { + switch (j) { + case 0: COMP(18, j); COMP(26, j+1); break; + case 2: COMP(32, j); COMP(79, j+1); break; + case 4: COMP(84, j); COMP(95, j+1); break; + case 6: COMP(124, j); COMP(150, j+1); break; + case 8: COMP(431, j); COMP(451, j+1); break; + } + } + + STUFF(0, 451); + SEND(fred); +#ifdef DEBUG_SPAN_MANAGER + LOG("should be filled out now:"); + LOG(fred.print_received_list()); +#endif + ASSERT_EQUAL(fred.received_sequence(), 451, "received sequence should be filled out"); + + return final_report(); +} + +HOOPLE_MAIN(test_span_manager, ); + diff --git a/octopi/library/tests_sockets/test_spocket.cpp b/octopi/library/tests_sockets/test_spocket.cpp new file mode 100644 index 00000000..25881724 --- /dev/null +++ b/octopi/library/tests_sockets/test_spocket.cpp @@ -0,0 +1,257 @@ +/* +* Name : test_spocket +* Author : Chris Koeritz +* Purpose: This is the "main" program for our sockets tester. It parses command +* line parameters and starts up the tester class. +** +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include "spocket_tester.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace application; +using namespace basis; +using namespace loggers; +using namespace mathematics; +using namespace processes; +using namespace sockets; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +typedef abyte ip_address_holder[4]; + +class test_spocket : public virtual application_shell, public virtual unit_base +{ +public: + test_spocket() {} + DEFINE_CLASS_NAME("test_spocket"); + virtual int execute(); + + bool parse_address(const astring &input, ip_address_holder &ip_address); +}; + +//hmmm: get into a library; same func as in t_bcast_spocket +bool test_spocket::parse_address(const astring &input, ip_address_holder &ip_address) +{ + int index = 0; // current storage position in our array. + int current_byte = 0; + const char *last_period = 0; // helps check for non-empty numbers. + bool got_digit = false; + for (const char *address = input.s(); *address; address++) { + if ( (*address <= '9') && (*address >= '0') ) { + current_byte *= 10; // shift over. + current_byte += *address - '0'; // add in current character. + got_digit = true; + } else if (*address == '.') { + got_digit = false; + if (last_period + 1 == address) { + LOG("The IP address entry has an empty digit. Exiting."); + return false; + } + last_period = address; // set our last period location. + if (current_byte > 255) { + LOG("The IP address entry has an illegal abyte. Exiting."); + return false; + } + ip_address[index] = abyte(current_byte); // store current byte. + current_byte = 0; // reset. + index++; // next place in array. + if (index > 3) break; + // stop if there are too many periods, but keep accumulated address. + } else { + LOG("The IP address entry has illegal characters. Exiting."); + return false; + } + } + // catch the case where we ran out of chars before adding the last byte. + if ( (index == 3) && got_digit) { + if (current_byte > 255) { + LOG("The IP address entry has an illegal abyte. Exiting."); + return false; + } + ip_address[index] = current_byte; + } else if (index < 4) { + LOG("The IP address entry is too short. Exiting."); + return false; + } + return true; +} + +int test_spocket::execute() +{ + FUNCDEF("execute"); + ip_address_holder ip_address; // accumulates the source address. + int rcv_port = 0; + bool is_client = false; + int send_size = 0; + int send_count = 0; + + const char *DEFAULT_HOST = "127.0.0.1"; + const int DEFAULT_PORT = 12342; + const int DEFAULT_SEND_SIZE = 1008; + const int DEFAULT_SEND_COUNT = 10; + + if (_global_argc < 6) { + if (_global_argc > 1) { + LOG("\ +This program takes five command line arguments to begin operation.\n\ +These arguments (in order) are:\n\ +\tIP address\t\tIn the form w.x.y.z\n\ +\tPort number\t\tAs a short integer\n\ +\tTester role\t\tEither \"client\" or \"server\"\n\ +\tSend size\t\tThe size of the data to exchange.\n\ +\tSend count\t\tThe number of \"packets\" to exchange.\n\ +Note: it is expected that the client and server have equal send sizes;\n\ +this allows the receiver to know when it's gotten all the data that's\n\ +expected during a cycle."); + return 1; // bail if they provided anything; otherwise we test. + } else { + parse_address(DEFAULT_HOST, ip_address); + rcv_port = DEFAULT_PORT; + is_client = false; // we're a server to start with for unit test. + send_size = DEFAULT_SEND_SIZE; + send_count = DEFAULT_SEND_COUNT; + } + } + + // only parse the parameters if we got enough from the user; otherwise we accept our + // defaults to do a simple test run. + if (_global_argc >= 6) { + + if (!parse_address(_global_argv[1], ip_address)) { + LOG("failed to parse source address."); + return 9283; + } + + LOG(a_sprintf("\tParsed a source of: \"%d.%d.%d.%d\".", + (int)ip_address[0], (int)ip_address[1], (int)ip_address[2], + (int)ip_address[3])); + + // parse the next parameter: the port. + if (sscanf(_global_argv[2], "%d", &rcv_port) < 1) { + LOG("The port entry is malformed. Exiting."); + return 3; + } + LOG(a_sprintf("\tGot a port of %d.", rcv_port)); + + // parse the next parameter: the role for this tester. + astring arg3 = _global_argv[3]; + arg3.to_lower(); + if (arg3 == astring("client")) is_client = true; + else if (arg3 == astring("server")) is_client = false; + else { + LOG("The tester role (client/server) is malformed. Exiting."); + return 4; + } + if (is_client) LOG("\tTester role is \"client\".") + else LOG("\tTester role is \"server\"."); + + // parse the next parameter: the size of the sends. + if (sscanf(_global_argv[4], "%d", &send_size) < 1) { + LOG("The send size entry is malformed. Exiting."); + return 5; + } + LOG(a_sprintf("\tGot a send size of %d.", send_size)); + + // parse the next parameter: the number of sends. + if (sscanf(_global_argv[5], "%d", &send_count) < 1) { + LOG("The send count entry is malformed. Exiting."); + return 5; + } + LOG(a_sprintf("\tGot a send count of %d.", send_count)); + } + + // package our parameters in a form the tester likes. + internet_address to_pass(byte_array(4, ip_address), "", rcv_port); + + // now, construct our tester object. + spocket_tester tester(to_pass); + + // choose the appropriate action based on our role. + bool outcome; + if (is_client) + outcome = tester.connect(); + else + outcome = tester.accept(_global_argc != 1); + if (!outcome) { + const char *action = is_client? "connect" : "accept"; + LOG(astring("Failed to ") + action + " on the tester."); + return 10; + } + + if (_global_argc == 1) { + // launch a paired duplicate of our test so we can chat. + launch_process zingit; + un_int kidnum; + un_int result = zingit.run(_global_argv[0], + astring(DEFAULT_HOST) + " " + a_sprintf("%d", DEFAULT_PORT) + " client " + + a_sprintf("%d", DEFAULT_SEND_SIZE) + " " + a_sprintf("%d", DEFAULT_SEND_COUNT), + launch_process::RETURN_IMMEDIATELY, kidnum); + ASSERT_EQUAL(result, 0, "launching paired process should start successfully"); + + // now we try again accepting from our client. + outcome = tester.accept(); +//hmmm: redundant below. + if (!outcome) { + const char *action = is_client? "connect" : "accept"; + LOG(astring("Failed to ") + action + " on the tester."); + return 10; + } + } + + // so, we're connected. try sending the test packages out. + testing_statistics stats; // to be filled by the tester. + outcome = tester.perform_test(send_size, send_count * 2, stats); + // multiply send_count since we count each side as one. + if (!outcome) { + LOG("Failed out of send_data; maybe other side terminated."); + } + + stats.total_runs /= 2; // cut down to the real number of tests. + + if (!stats.total_runs) + stats.total_runs = 1; + + // now report on the stats that we get from the data sending. + LOG(a_sprintf("Report for %d completed test cycles.", stats.total_runs)); + LOG(""); + LOG("\t\tsend stats\t\treceive stats"); + LOG("\t\t----------\t\t-------------"); + LOG(a_sprintf("bytes\t\t%d\t\t\t%d", stats.bytes_sent, + stats.bytes_received)); + LOG(a_sprintf("time\t\t%d\t\t\t%d", stats.send_time, stats.receive_time)); + LOG(a_sprintf("avg. bytes\t%d\t\t\t%d", stats.bytes_sent + / stats.total_runs / 2, stats.bytes_received / stats.total_runs / 2)); + LOG(""); + LOG(a_sprintf("round trip time: %d ms", stats.round_trip_time)); +//hmmm: use the bandwidth measurer object!!! + double bandwidth = double(stats.bytes_sent + stats.bytes_received) + / stats.round_trip_time / 1024.0 * 1000.0; + LOG(a_sprintf("bandwidth overall: %f K/s", bandwidth)); + + if (_global_argc == 1) return final_report(); + else return 0; // no unit test report for non-top-level process +} + +HOOPLE_MAIN(test_spocket, ); + diff --git a/octopi/library/tests_sockets/test_ucast_spocket.cpp b/octopi/library/tests_sockets/test_ucast_spocket.cpp new file mode 100644 index 00000000..eb5cb909 --- /dev/null +++ b/octopi/library/tests_sockets/test_ucast_spocket.cpp @@ -0,0 +1,265 @@ +/* +* Name : test_ucast_spocket +* Author : Chris Koeritz +* Purpose: This is the "main" program for our sockets tester. It parses command +* line parameters and starts up the tester class. +** +* Copyright (c) 2001-$now By Author. 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 is online at: * +* http://www.fsf.org/copyleft/gpl.html * +* Please send any updates to: fred@gruntose.com * +*/ + +#include "bcast_spocketer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace application; +using namespace basis; +using namespace loggers; +using namespace mathematics; +using namespace processes; +using namespace sockets; +using namespace structures; +using namespace textual; +using namespace timely; +using namespace unit_test; + +#define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print)) + +const int INITIAL_DELAY = 1; // number of seconds before starting sends. + +typedef abyte ip_address_holder[4]; + +class test_ucast_spocket : public virtual application_shell, public virtual unit_base +{ +public: + test_ucast_spocket() {} + DEFINE_CLASS_NAME("test_ucast_spocket"); + virtual int execute(); + + bool parse_address(const astring &input, ip_address_holder &ip_address); +}; + +//hmmm: extract this to function in sockets. +bool test_ucast_spocket::parse_address(const astring &input, ip_address_holder &ip_address) +{ + int index = 0; // current storage position in our array. + int current_byte = 0; + const char *last_period = 0; // helps check for non-empty numbers. + bool got_digit = false; + for (const char *address = input.s(); *address; address++) { + if ( (*address <= '9') && (*address >= '0') ) { + current_byte *= 10; // shift over. + current_byte += *address - '0'; // add in current character. + got_digit = true; + } else if (*address == '.') { + got_digit = false; + if (last_period + 1 == address) { + LOG("The IP address entry has an empty digit. Exiting."); + return false; + } + last_period = address; // set our last period location. + if (current_byte > 255) { + LOG("The IP address entry has an illegal abyte. Exiting."); + return false; + } + ip_address[index] = abyte(current_byte); // store current byte. + current_byte = 0; // reset. + index++; // next place in array. + if (index > 3) break; + // stop if there are too many periods, but keep accumulated address. + } else { + LOG("The IP address entry has illegal characters. Exiting."); + return false; + } + } + // catch the case where we ran out of chars before adding the last byte. + if ( (index == 3) && got_digit) { + if (current_byte > 255) { + LOG("The IP address entry has an illegal abyte. Exiting."); + return false; + } + ip_address[index] = current_byte; + } else if (index < 4) { + LOG("The IP address entry is too short. Exiting."); + return false; + } + return true; +} + +int test_ucast_spocket::execute() +{ +//this preamble with command line parsing is identical to bcast version, +//and will be very close to regular spocket version. +//get it extracted to a helper class for use by all three. + FUNCDEF("execute"); + ip_address_holder ip_address; // accumulates the source address. + ip_address_holder dest_addr; // where to send stuff to. + int rcv_port = 0; + int send_port = 0; + int send_size = 0; + int send_count = 0; + + const char *DEFAULT_HOST = "127.0.0.1"; + const int DEFAULT_PORT = 12342; + const int DEFAULT_SEND_SIZE = 1008; + const int DEFAULT_SEND_COUNT = 10; + + if (_global_argc < 7) { + if (_global_argc > 1) { + LOG("\ +This program takes six command line arguments to begin operation.\n\ +These arguments (in order) are:\n\ +\tIP address for src\tIn the form w.x.y.z\n\ +\tIP address for dest\tIn the form w.x.y.z\n\ +\tReceive Port number\tAs a short integer\n\ +\tSending Port number\tAs a short integer\n\ +\tSend size\t\tThe size of the data to exchange.\n\ +\tSend count\t\tThe number of \"packets\" to exchange.\n\ +Note: it is expected that the testers have equal send sizes; this\n\ +allows the receiver to know when it's gotten all the data that's\n\ +expected during a cycle."); + return 1; // bail if they provided anything; otherwise we test. + } else { + parse_address(DEFAULT_HOST, ip_address); + parse_address(DEFAULT_HOST, dest_addr); + rcv_port = DEFAULT_PORT; + send_port = DEFAULT_PORT + 1; + send_size = DEFAULT_SEND_SIZE; + send_count = DEFAULT_SEND_COUNT; + } + } + + // only parse the parameters if we got enough from the user; otherwise we accept our + // defaults to do a simple test run. + if (_global_argc >= 7) { + + if (!parse_address(_global_argv[1], ip_address)) { + LOG("failed to parse source address."); + return 9283; + } + + LOG(a_sprintf("\tParsed a source of: \"%d.%d.%d.%d\".", + (int)ip_address[0], (int)ip_address[1], (int)ip_address[2], + (int)ip_address[3])); + + if (!parse_address(_global_argv[2], dest_addr)) { + LOG("failed to parse dest address."); + return 9283; + } + + LOG(a_sprintf("\tParsed a destination of: \"%d.%d.%d.%d\".", + (int)dest_addr[0], (int)dest_addr[1], (int)dest_addr[2], + (int)dest_addr[3])); + + // parse the third parameter: the port. + if (sscanf(_global_argv[3], "%d", &rcv_port) < 1) { + LOG("The port entry is malformed. Exiting."); + return 3; + } + LOG(a_sprintf("\tGot a receive port of %d.", rcv_port)); + + // parse the fourth parameter: the port. + if (sscanf(_global_argv[4], "%d", &send_port) < 1) { + LOG("The port entry is malformed. Exiting."); + return 3; + } + LOG(a_sprintf("\tGot a send port of %d.", send_port)); + + // parse the fifth parameter: the size of the sends. + if (sscanf(_global_argv[5], "%d", &send_size) < 1) { + LOG("The send size entry is malformed. Exiting."); + return 5; + } + LOG(a_sprintf("\tGot a send size of %d.", send_size)); + + // parse the sixth parameter: the number of sends. + if (sscanf(_global_argv[6], "%d", &send_count) < 1) { + LOG("The send count entry is malformed. Exiting."); + return 5; + } + LOG(a_sprintf("\tGot a send count of %d.", send_count)); + } + + if (_global_argc == 1) { + // launch a paired duplicate of our test so we can chat. + launch_process zingit; + un_int kidnum; + un_int result = zingit.run(_global_argv[0], + astring(DEFAULT_HOST) + " " + DEFAULT_HOST + " " + /* we have reversed the send and receive ports. */ + + a_sprintf("%d", DEFAULT_PORT + 1) + " " + a_sprintf("%d", DEFAULT_PORT) + + " " + a_sprintf("%d", DEFAULT_SEND_SIZE) + " " + a_sprintf("%d", DEFAULT_SEND_COUNT), + launch_process::RETURN_IMMEDIATELY, kidnum); + ASSERT_EQUAL(result, 0, "launching paired process should start successfully"); + } + + // package our parameters in a form the tester likes. + internet_address to_pass(byte_array(4, ip_address), "", rcv_port); + internet_address dest(byte_array(4, dest_addr), "", send_port); + + // now, construct our tester object. + broadcast_spocket_tester tester(to_pass, true); + + // choose the appropriate action based on our role. + bool outcome = tester.connect(); + if (!outcome) { + LOG(astring("Failed to connect on the tester.")); + return 10; + } + + LOG(a_sprintf("you now have %d seconds; get other side ready.", + INITIAL_DELAY)); + time_control::sleep_ms(INITIAL_DELAY * SECOND_ms); + LOG("starting test"); + + // so, we're connected. try sending the test packages out. + testing_statistics stats; // to be filled by the tester. + outcome = tester.perform_test(dest, send_size, send_count * 2, stats); + // multiply send_count since we count each side as one. + if (!outcome) { + LOG("Failed out of send_data; maybe other side terminated."); + } + + stats.total_runs /= 2; // cut down to the real number of tests. + + if (!stats.total_runs) + stats.total_runs = 1; + + // now report on the stats that we get from the data sending. + LOG(a_sprintf("Report for %d completed test cycles.", stats.total_runs)); + LOG(""); + LOG("\t\tsend stats\t\treceive stats"); + LOG("\t\t----------\t\t-------------"); + LOG(a_sprintf("bytes\t\t%d\t\t\t%d", stats.bytes_sent, + stats.bytes_received)); + LOG(a_sprintf("time\t\t%d\t\t\t%d", stats.send_time, stats.receive_time)); + LOG(a_sprintf("avg. bytes\t%d\t\t\t%d", stats.bytes_sent + / stats.total_runs / 2, stats.bytes_received / stats.total_runs / 2)); + LOG(""); + LOG(a_sprintf("round trip time: %d ms", stats.round_trip_time)); +//hmmm: use the bandwidth measurer object!!! + double bandwidth = double(stats.bytes_sent + stats.bytes_received) + / stats.round_trip_time / 1024.0 * 1000.0; + LOG(a_sprintf("bandwidth overall: %f K/s", bandwidth)); + + if (_global_argc == 1) return final_report(); + else return 0; // no unit test report for non-top-level process +} + +HOOPLE_MAIN(test_ucast_spocket, ); + diff --git a/octopi/library/tests_sockets/version.ini b/octopi/library/tests_sockets/version.ini new file mode 100644 index 00000000..467e2327 --- /dev/null +++ b/octopi/library/tests_sockets/version.ini @@ -0,0 +1,5 @@ +[version] +description=Test for Sockets Library +root=t_sockets +name=t_sockets +extension=exe diff --git a/octopi/makefile b/octopi/makefile new file mode 100644 index 00000000..a9980948 --- /dev/null +++ b/octopi/makefile @@ -0,0 +1,7 @@ +include variables.def + +PROJECT = octopus_modules +BUILD_BEFORE = library applications + +include rules.def + diff --git a/production/3rdparty/curl/COPYING b/production/3rdparty/curl/COPYING new file mode 100644 index 00000000..9769e843 --- /dev/null +++ b/production/3rdparty/curl/COPYING @@ -0,0 +1,21 @@ +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1996 - 2005, Daniel Stenberg, . + +All rights reserved. + +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", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. 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. + +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization of the copyright holder. diff --git a/production/3rdparty/curl/README b/production/3rdparty/curl/README new file mode 100644 index 00000000..9043dcea --- /dev/null +++ b/production/3rdparty/curl/README @@ -0,0 +1,79 @@ + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + +README + + Curl is a command line tool for transferring data specified with URL + syntax. Find out how to use Curl by reading the curl.1 man page or the + MANUAL document. Find out how to install Curl by reading the INSTALL + document. + + libcurl is the library curl is using to do its job. It is readily + available to be used by your software. Read the libcurl.3 man page to + learn how! + + You find answers to the most frequent questions we get in the FAQ document. + + Study the COPYING file for distribution terms and similar. If you distribute + curl binaries or other binaries that involve libcurl, you might enjoy the + LICENSE-MIXING document. + +CONTACT + + If you have problems, questions, ideas or suggestions, please contact us + by posting to a suitable mailing list. See http://curl.haxx.se/mail/ + + All contributors to the project are listed in the THANKS document. + +WEB SITE + + Visit the curl web site or mirrors for the latest news and downloads: + + Sweden http://curl.haxx.se/ + Australia http://curl.planetmirror.com/ + Austria http://curl.gds.tuwien.ac.at/ + Denmark http://curl.cofman.dk/ + France http://curl.fastmirror.net/ + Germany http://curl.miscellaneousmirror.org/ + Germany http://curl.mirror.at.stealer.net/ + Germany http://curl.mirroring.de/ + Germany http://curl.mons-new-media.de/ + Germany http://curl.triplemind.com/ + Germany http://curl.freemirror.de/ + Netherlands http://curl.nedmirror.nl/ + Russia http://curl.tsuren.net/ + Taiwan http://curl.cs.pu.edu.tw/ + Thailand http://curl.siamu.ac.th/ + US (AZ) http://curl.islandofpoker.com/ + US (CA) http://curl.mirror.redwire.net/ + US (CA) http://curl.mirrormonster.com/ + US (CA) http://curl.signal42.com/ + US (CA) http://curl.tolix.org/ + US (CA) http://curl.webhosting76.com/ + US (CA) http://curl.meulie.net/ + US (FL) http://curl.hoxt.com/ + US (TX) http://curl.109k.com/ + US (TX) http://curl.mirrors.cyberservers.net/ + US (TX) http://curl.seekmeup.com/ + US (TX) http://curl.hostingzero.com/ + +CVS + + To download the very latest source off the CVS server do this: + + cvs -d :pserver:anonymous@cool.haxx.se:/cvsroot/curl login + + (just press enter when asked for password) + + cvs -d :pserver:anonymous@cool.haxx.se:/cvsroot/curl co curl + + (you'll get a directory named curl created, filled with the source code) + +NOTICE + + Curl contains pieces of source code that is Copyright (c) 1998, 1999 + Kungliga Tekniska Högskolan. This notice is included here to comply with the + distribution terms. diff --git a/production/3rdparty/curl/bin/curl-config b/production/3rdparty/curl/bin/curl-config new file mode 100644 index 00000000..c6879296 --- /dev/null +++ b/production/3rdparty/curl/bin/curl-config @@ -0,0 +1,157 @@ +#! /bin/sh +# +# The idea to this kind of setup info script was stolen from numerous +# other packages, such as neon, libxml and gnome. +# +# $Id: curl-config,v 1.1 2006/07/09 22:41:13 fred_t_hamster Exp $ +# +prefix=/usr/local +exec_prefix=${prefix} +includedir=${prefix}/include + +usage() +{ + cat <, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: curl.h,v 1.1 2006/07/09 22:41:14 fred_t_hamster Exp $ + ***************************************************************************/ + +/* If you have problems, all libcurl docs and details are found here: + http://curl.haxx.se/libcurl/ +*/ + +#include "curlver.h" /* the libcurl version defines */ + +#include +#include + +/* The include stuff here below is mainly for time_t! */ +#ifdef vms +# include +# include +#else +# include +# include +#endif /* defined (vms) */ + +typedef void CURL; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Decorate exportable functions for Win32 DLL linking. + * This avoids using a .def file for building libcurl.dll. + */ +#if (defined(WIN32) || defined(_WIN32)) && !defined(CURL_STATICLIB) +#if defined(BUILDING_LIBCURL) +#define CURL_EXTERN __declspec(dllexport) +#else +#define CURL_EXTERN __declspec(dllimport) +#endif +#else +#define CURL_EXTERN +#endif + +/* + * We want the typedef curl_off_t setup for large file support on all + * platforms. We also provide a CURL_FORMAT_OFF_T define to use in *printf + * format strings when outputting a variable of type curl_off_t. + */ + +#if defined(_MSC_VER) || defined(__LCC__) +/* MSVC */ +#ifdef _WIN32_WCE + typedef long curl_off_t; +#define CURL_FORMAT_OFF_T "%ld" +#else + typedef signed __int64 curl_off_t; +#define CURL_FORMAT_OFF_T "%I64d" +#endif +#else /* _MSC_VER || __LCC__ */ +#if (defined(__GNUC__) && defined(WIN32)) || defined(__WATCOMC__) +/* gcc on windows or Watcom */ + typedef long long curl_off_t; +#define CURL_FORMAT_OFF_T "%I64d" +#else /* GCC or Watcom on Windows */ + +/* "normal" POSIX approach, do note that this does not necessarily mean that + the type is >32 bits, see the SIZEOF_CURL_OFF_T define for that! */ + typedef off_t curl_off_t; + +/* Check a range of defines to detect large file support. On Linux it seems + none of these are set by default, so if you don't explicitly switches on + large file support, this define will be made for "small file" support. */ +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 0 /* to prevent warnings in the check below */ +#define UNDEF_FILE_OFFSET_BITS +#endif +#ifndef FILESIZEBITS +#define FILESIZEBITS 0 /* to prevent warnings in the check below */ +#define UNDEF_FILESIZEBITS +#endif + +#if defined(_LARGE_FILES) || (_FILE_OFFSET_BITS > 32) || (FILESIZEBITS > 32) \ + || defined(_LARGEFILE_SOURCE) || defined(_LARGEFILE64_SOURCE) + /* For now, we assume at least one of these to be set for large files to + work! */ +#define CURL_FORMAT_OFF_T "%lld" +#else /* LARGE_FILE support */ +#define CURL_FORMAT_OFF_T "%ld" +#endif +#endif /* GCC or Watcom on Windows */ +#endif /* _MSC_VER || __LCC__ */ + +#ifdef UNDEF_FILE_OFFSET_BITS +/* this was defined above for our checks, undefine it again */ +#undef _FILE_OFFSET_BITS +#endif + +#ifdef UNDEF_FILESIZEBITS +/* this was defined above for our checks, undefine it again */ +#undef FILESIZEBITS +#endif + +struct curl_httppost { + struct curl_httppost *next; /* next entry in the list */ + char *name; /* pointer to allocated name */ + long namelength; /* length of name length */ + char *contents; /* pointer to allocated data contents */ + long contentslength; /* length of contents field */ + char *buffer; /* pointer to allocated buffer contents */ + long bufferlength; /* length of buffer field */ + char *contenttype; /* Content-Type */ + struct curl_slist* contentheader; /* list of extra headers for this form */ + struct curl_httppost *more; /* if one field name has more than one + file, this link should link to following + files */ + long flags; /* as defined below */ +#define HTTPPOST_FILENAME (1<<0) /* specified content is a file name */ +#define HTTPPOST_READFILE (1<<1) /* specified content is a file name */ +#define HTTPPOST_PTRNAME (1<<2) /* name is only stored pointer + do not free in formfree */ +#define HTTPPOST_PTRCONTENTS (1<<3) /* contents is only stored pointer + do not free in formfree */ +#define HTTPPOST_BUFFER (1<<4) /* upload file from buffer */ +#define HTTPPOST_PTRBUFFER (1<<5) /* upload file from pointer contents */ + + char *showfilename; /* The file name to show. If not set, the + actual file name will be used (if this + is a file part) */ +}; + +typedef int (*curl_progress_callback)(void *clientp, + double dltotal, + double dlnow, + double ultotal, + double ulnow); + + /* Tests have proven that 20K is a very bad buffer size for uploads on + Windows, while 16K for some odd reason performed a lot better. */ +#define CURL_MAX_WRITE_SIZE 16384 + +typedef size_t (*curl_write_callback)(char *buffer, + size_t size, + size_t nitems, + void *outstream); + +/* This is a return code for the read callback that, when returned, will + signal libcurl to immediately abort the current transfer. */ +#define CURL_READFUNC_ABORT 0x10000000 +typedef size_t (*curl_read_callback)(char *buffer, + size_t size, + size_t nitems, + void *instream); + + +#ifndef CURL_NO_OLDIES + /* not used since 7.10.8, will be removed in a future release */ +typedef int (*curl_passwd_callback)(void *clientp, + const char *prompt, + char *buffer, + int buflen); +#endif + +typedef enum { + CURLIOE_OK, /* I/O operation successful */ + CURLIOE_UNKNOWNCMD, /* command was unknown to callback */ + CURLIOE_FAILRESTART, /* failed to restart the read */ + CURLIOE_LAST /* never use */ +} curlioerr; + +typedef enum { + CURLIOCMD_NOP, /* no operation */ + CURLIOCMD_RESTARTREAD, /* restart the read stream from start */ + CURLIOCMD_LAST /* never use */ +} curliocmd; + +typedef curlioerr (*curl_ioctl_callback)(CURL *handle, + int cmd, + void *clientp); + +/* + * The following typedef's are signatures of malloc, free, realloc, strdup and + * calloc respectively. Function pointers of these types can be passed to the + * curl_global_init_mem() function to set user defined memory management + * callback routines. + */ +typedef void *(*curl_malloc_callback)(size_t size); +typedef void (*curl_free_callback)(void *ptr); +typedef void *(*curl_realloc_callback)(void *ptr, size_t size); +typedef char *(*curl_strdup_callback)(const char *str); +typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size); + +/* the kind of data that is passed to information_callback*/ +typedef enum { + CURLINFO_TEXT = 0, + CURLINFO_HEADER_IN, /* 1 */ + CURLINFO_HEADER_OUT, /* 2 */ + CURLINFO_DATA_IN, /* 3 */ + CURLINFO_DATA_OUT, /* 4 */ + CURLINFO_SSL_DATA_IN, /* 5 */ + CURLINFO_SSL_DATA_OUT, /* 6 */ + CURLINFO_END +} curl_infotype; + +typedef int (*curl_debug_callback) + (CURL *handle, /* the handle/transfer this concerns */ + curl_infotype type, /* what kind of data */ + char *data, /* points to the data */ + size_t size, /* size of the data pointed to */ + void *userptr); /* whatever the user please */ + +/* All possible error codes from all sorts of curl functions. Future versions + may return other values, stay prepared. + + Always add new return codes last. Never *EVER* remove any. The return + codes must remain the same! + */ + +typedef enum { + CURLE_OK = 0, + CURLE_UNSUPPORTED_PROTOCOL, /* 1 */ + CURLE_FAILED_INIT, /* 2 */ + CURLE_URL_MALFORMAT, /* 3 */ + CURLE_URL_MALFORMAT_USER, /* 4 (NOT USED) */ + CURLE_COULDNT_RESOLVE_PROXY, /* 5 */ + CURLE_COULDNT_RESOLVE_HOST, /* 6 */ + CURLE_COULDNT_CONNECT, /* 7 */ + CURLE_FTP_WEIRD_SERVER_REPLY, /* 8 */ + CURLE_FTP_ACCESS_DENIED, /* 9 a service was denied by the FTP server + due to lack of access - when login fails + this is not returned. */ + CURLE_FTP_USER_PASSWORD_INCORRECT, /* 10 */ + CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */ + CURLE_FTP_WEIRD_USER_REPLY, /* 12 */ + CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */ + CURLE_FTP_WEIRD_227_FORMAT, /* 14 */ + CURLE_FTP_CANT_GET_HOST, /* 15 */ + CURLE_FTP_CANT_RECONNECT, /* 16 */ + CURLE_FTP_COULDNT_SET_BINARY, /* 17 */ + CURLE_PARTIAL_FILE, /* 18 */ + CURLE_FTP_COULDNT_RETR_FILE, /* 19 */ + CURLE_FTP_WRITE_ERROR, /* 20 */ + CURLE_FTP_QUOTE_ERROR, /* 21 */ + CURLE_HTTP_RETURNED_ERROR, /* 22 */ + CURLE_WRITE_ERROR, /* 23 */ + CURLE_MALFORMAT_USER, /* 24 - NOT USED */ + CURLE_FTP_COULDNT_STOR_FILE, /* 25 - failed FTP upload */ + CURLE_READ_ERROR, /* 26 - could open/read from file */ + CURLE_OUT_OF_MEMORY, /* 27 */ + CURLE_OPERATION_TIMEOUTED, /* 28 - the timeout time was reached */ + CURLE_FTP_COULDNT_SET_ASCII, /* 29 - TYPE A failed */ + CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */ + CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */ + CURLE_FTP_COULDNT_GET_SIZE, /* 32 - the SIZE command failed */ + CURLE_HTTP_RANGE_ERROR, /* 33 - RANGE "command" didn't work */ + CURLE_HTTP_POST_ERROR, /* 34 */ + CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */ + CURLE_BAD_DOWNLOAD_RESUME, /* 36 - couldn't resume download */ + CURLE_FILE_COULDNT_READ_FILE, /* 37 */ + CURLE_LDAP_CANNOT_BIND, /* 38 */ + CURLE_LDAP_SEARCH_FAILED, /* 39 */ + CURLE_LIBRARY_NOT_FOUND, /* 40 */ + CURLE_FUNCTION_NOT_FOUND, /* 41 */ + CURLE_ABORTED_BY_CALLBACK, /* 42 */ + CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */ + CURLE_BAD_CALLING_ORDER, /* 44 - NOT USED */ + CURLE_INTERFACE_FAILED, /* 45 - CURLOPT_INTERFACE failed */ + CURLE_BAD_PASSWORD_ENTERED, /* 46 - NOT USED */ + CURLE_TOO_MANY_REDIRECTS , /* 47 - catch endless re-direct loops */ + CURLE_UNKNOWN_TELNET_OPTION, /* 48 - User specified an unknown option */ + CURLE_TELNET_OPTION_SYNTAX , /* 49 - Malformed telnet option */ + CURLE_OBSOLETE, /* 50 - NOT USED */ + CURLE_SSL_PEER_CERTIFICATE, /* 51 - peer's certificate wasn't ok */ + CURLE_GOT_NOTHING, /* 52 - when this is a specific error */ + CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */ + CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as + default */ + CURLE_SEND_ERROR, /* 55 - failed sending network data */ + CURLE_RECV_ERROR, /* 56 - failure in receiving network data */ + CURLE_SHARE_IN_USE, /* 57 - share is in use */ + CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */ + CURLE_SSL_CIPHER, /* 59 - couldn't use specified cipher */ + CURLE_SSL_CACERT, /* 60 - problem with the CA cert (path?) */ + CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized transfer encoding */ + CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */ + CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */ + CURLE_FTP_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ + CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind + that failed */ + CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ + CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not + accepted and we failed to login */ + CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */ + CURLE_TFTP_PERM, /* 69 - permission problem on server */ + CURLE_TFTP_DISKFULL, /* 70 - out of disk space on server */ + CURLE_TFTP_ILLEGAL, /* 71 - Illegal TFTP operation */ + CURLE_TFTP_UNKNOWNID, /* 72 - Unknown transfer ID */ + CURLE_TFTP_EXISTS, /* 73 - File already exists */ + CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */ + CURL_LAST /* never use! */ +} CURLcode; + +typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ + void *ssl_ctx, /* actually an + OpenSSL SSL_CTX */ + void *userptr); + +/* Make a spelling correction for the operation timed-out define */ +#define CURLE_OPERATION_TIMEDOUT CURLE_OPERATION_TIMEOUTED + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ +/* backwards compatibility with older names */ +#define CURLE_HTTP_NOT_FOUND CURLE_HTTP_RETURNED_ERROR +#define CURLE_HTTP_PORT_FAILED CURLE_INTERFACE_FAILED +#endif + +typedef enum { + CURLPROXY_HTTP = 0, + CURLPROXY_SOCKS4 = 4, + CURLPROXY_SOCKS5 = 5 +} curl_proxytype; + +#define CURLAUTH_NONE 0 /* nothing */ +#define CURLAUTH_BASIC (1<<0) /* Basic (default) */ +#define CURLAUTH_DIGEST (1<<1) /* Digest */ +#define CURLAUTH_GSSNEGOTIATE (1<<2) /* GSS-Negotiate */ +#define CURLAUTH_NTLM (1<<3) /* NTLM */ +#define CURLAUTH_ANY ~0 /* all types set */ +#define CURLAUTH_ANYSAFE (~CURLAUTH_BASIC) + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ +/* this was the error code 50 in 7.7.3 and a few earlier versions, this + is no longer used by libcurl but is instead #defined here only to not + make programs break */ +#define CURLE_ALREADY_COMPLETE 99999 + +/* These are just to make older programs not break: */ +#define CURLE_FTP_PARTIAL_FILE CURLE_PARTIAL_FILE +#define CURLE_FTP_BAD_DOWNLOAD_RESUME CURLE_BAD_DOWNLOAD_RESUME +#endif + +#define CURL_ERROR_SIZE 256 + +/* parameter for the CURLOPT_FTP_SSL option */ +typedef enum { + CURLFTPSSL_NONE, /* do not attempt to use SSL */ + CURLFTPSSL_TRY, /* try using SSL, proceed anyway otherwise */ + CURLFTPSSL_CONTROL, /* SSL for the control connection or fail */ + CURLFTPSSL_ALL, /* SSL for all communication or fail */ + CURLFTPSSL_LAST /* not an option, never use */ +} curl_ftpssl; + +/* parameter for the CURLOPT_FTPSSLAUTH option */ +typedef enum { + CURLFTPAUTH_DEFAULT, /* let libcurl decide */ + CURLFTPAUTH_SSL, /* use "AUTH SSL" */ + CURLFTPAUTH_TLS, /* use "AUTH TLS" */ + CURLFTPAUTH_LAST /* not an option, never use */ +} curl_ftpauth; + +/* long may be 32 or 64 bits, but we should never depend on anything else + but 32 */ +#define CURLOPTTYPE_LONG 0 +#define CURLOPTTYPE_OBJECTPOINT 10000 +#define CURLOPTTYPE_FUNCTIONPOINT 20000 +#define CURLOPTTYPE_OFF_T 30000 + +/* name is uppercase CURLOPT_, + type is one of the defined CURLOPTTYPE_ + number is unique identifier */ +#ifdef CINIT +#undef CINIT +#endif +/* + * Figure out if we can use the ## operator, which is supported by ISO/ANSI C + * and C++. Some compilers support it without setting __STDC__ or __cplusplus + * so we need to carefully check for them too. We don't use configure-checks + * for these since we want these headers to remain generic and working for all + * platforms. + */ +#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \ + defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) + /* This compiler is believed to have an ISO compatible preprocessor */ +#define CURL_ISOCPP +#else + /* This compiler is believed NOT to have an ISO compatible preprocessor */ +#undef CURL_ISOCPP +#endif + +#ifdef CURL_ISOCPP +#define CINIT(name,type,number) CURLOPT_ ## name = CURLOPTTYPE_ ## type + number +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define LONG CURLOPTTYPE_LONG +#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT +#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT +#define OFF_T CURLOPTTYPE_OFF_T +#define CINIT(name,type,number) CURLOPT_/**/name = type + number +#endif + +/* + * This macro-mania below setups the CURLOPT_[what] enum, to be used with + * curl_easy_setopt(). The first argument in the CINIT() macro is the [what] + * word. + */ + +typedef enum { + /* This is the FILE * or void * the regular output should be written to. */ + CINIT(FILE, OBJECTPOINT, 1), + + /* The full URL to get/put */ + CINIT(URL, OBJECTPOINT, 2), + + /* Port number to connect to, if other than default. */ + CINIT(PORT, LONG, 3), + + /* Name of proxy to use. */ + CINIT(PROXY, OBJECTPOINT, 4), + + /* "name:password" to use when fetching. */ + CINIT(USERPWD, OBJECTPOINT, 5), + + /* "name:password" to use with proxy. */ + CINIT(PROXYUSERPWD, OBJECTPOINT, 6), + + /* Range to get, specified as an ASCII string. */ + CINIT(RANGE, OBJECTPOINT, 7), + + /* not used */ + + /* Specified file stream to upload from (use as input): */ + CINIT(INFILE, OBJECTPOINT, 9), + + /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE + * bytes big. If this is not used, error messages go to stderr instead: */ + CINIT(ERRORBUFFER, OBJECTPOINT, 10), + + /* Function that will be called to store the output (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11), + + /* Function that will be called to read the input (instead of fread). The + * parameters will use fread() syntax, make sure to follow them. */ + CINIT(READFUNCTION, FUNCTIONPOINT, 12), + + /* Time-out the read operation after this amount of seconds */ + CINIT(TIMEOUT, LONG, 13), + + /* If the CURLOPT_INFILE is used, this can be used to inform libcurl about + * how large the file being sent really is. That allows better error + * checking and better verifies that the upload was succcessful. -1 means + * unknown size. + * + * For large file support, there is also a _LARGE version of the key + * which takes an off_t type, allowing platforms with larger off_t + * sizes to handle larger files. See below for INFILESIZE_LARGE. + */ + CINIT(INFILESIZE, LONG, 14), + + /* POST input fields. */ + CINIT(POSTFIELDS, OBJECTPOINT, 15), + + /* Set the referer page (needed by some CGIs) */ + CINIT(REFERER, OBJECTPOINT, 16), + + /* Set the FTP PORT string (interface name, named or numerical IP address) + Use i.e '-' to use default address. */ + CINIT(FTPPORT, OBJECTPOINT, 17), + + /* Set the User-Agent string (examined by some CGIs) */ + CINIT(USERAGENT, OBJECTPOINT, 18), + + /* If the download receives less than "low speed limit" bytes/second + * during "low speed time" seconds, the operations is aborted. + * You could i.e if you have a pretty high speed connection, abort if + * it is less than 2000 bytes/sec during 20 seconds. + */ + + /* Set the "low speed limit" */ + CINIT(LOW_SPEED_LIMIT, LONG , 19), + + /* Set the "low speed time" */ + CINIT(LOW_SPEED_TIME, LONG, 20), + + /* Set the continuation offset. + * + * Note there is also a _LARGE version of this key which uses + * off_t types, allowing for large file offsets on platforms which + * use larger-than-32-bit off_t's. Look below for RESUME_FROM_LARGE. + */ + CINIT(RESUME_FROM, LONG, 21), + + /* Set cookie in request: */ + CINIT(COOKIE, OBJECTPOINT, 22), + + /* This points to a linked list of headers, struct curl_slist kind */ + CINIT(HTTPHEADER, OBJECTPOINT, 23), + + /* This points to a linked list of post entries, struct HttpPost */ + CINIT(HTTPPOST, OBJECTPOINT, 24), + + /* name of the file keeping your private SSL-certificate */ + CINIT(SSLCERT, OBJECTPOINT, 25), + + /* password for the SSL-private key, keep this for compatibility */ + CINIT(SSLCERTPASSWD, OBJECTPOINT, 26), + /* password for the SSL private key */ + CINIT(SSLKEYPASSWD, OBJECTPOINT, 26), + + /* send TYPE parameter? */ + CINIT(CRLF, LONG, 27), + + /* send linked-list of QUOTE commands */ + CINIT(QUOTE, OBJECTPOINT, 28), + + /* send FILE * or void * to store headers to, if you use a callback it + is simply passed to the callback unmodified */ + CINIT(WRITEHEADER, OBJECTPOINT, 29), + + /* point to a file to read the initial cookies from, also enables + "cookie awareness" */ + CINIT(COOKIEFILE, OBJECTPOINT, 31), + + /* What version to specifly try to use. + See CURL_SSLVERSION defines below. */ + CINIT(SSLVERSION, LONG, 32), + + /* What kind of HTTP time condition to use, see defines */ + CINIT(TIMECONDITION, LONG, 33), + + /* Time to use with the above condition. Specified in number of seconds + since 1 Jan 1970 */ + CINIT(TIMEVALUE, LONG, 34), + + /* 35 = OBSOLETE */ + + /* Custom request, for customizing the get command like + HTTP: DELETE, TRACE and others + FTP: to use a different list command + */ + CINIT(CUSTOMREQUEST, OBJECTPOINT, 36), + + /* HTTP request, for odd commands like DELETE, TRACE and others */ + CINIT(STDERR, OBJECTPOINT, 37), + + /* 38 is not used */ + + /* send linked-list of post-transfer QUOTE commands */ + CINIT(POSTQUOTE, OBJECTPOINT, 39), + + /* Pass a pointer to string of the output using full variable-replacement + as described elsewhere. */ + CINIT(WRITEINFO, OBJECTPOINT, 40), + + CINIT(VERBOSE, LONG, 41), /* talk a lot */ + CINIT(HEADER, LONG, 42), /* throw the header out too */ + CINIT(NOPROGRESS, LONG, 43), /* shut off the progress meter */ + CINIT(NOBODY, LONG, 44), /* use HEAD to get http document */ + CINIT(FAILONERROR, LONG, 45), /* no output on http error codes >= 300 */ + CINIT(UPLOAD, LONG, 46), /* this is an upload */ + CINIT(POST, LONG, 47), /* HTTP POST method */ + CINIT(FTPLISTONLY, LONG, 48), /* Use NLST when listing ftp dir */ + + CINIT(FTPAPPEND, LONG, 50), /* Append instead of overwrite on upload! */ + + /* Specify whether to read the user+password from the .netrc or the URL. + * This must be one of the CURL_NETRC_* enums below. */ + CINIT(NETRC, LONG, 51), + + CINIT(FOLLOWLOCATION, LONG, 52), /* use Location: Luke! */ + + CINIT(TRANSFERTEXT, LONG, 53), /* transfer data in text/ASCII format */ + CINIT(PUT, LONG, 54), /* HTTP PUT */ + + /* 55 = OBSOLETE */ + + /* Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_progress_callback + * prototype defines. */ + CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56), + + /* Data passed to the progress callback */ + CINIT(PROGRESSDATA, OBJECTPOINT, 57), + + /* We want the referer field set automatically when following locations */ + CINIT(AUTOREFERER, LONG, 58), + + /* Port of the proxy, can be set in the proxy string as well with: + "[host]:[port]" */ + CINIT(PROXYPORT, LONG, 59), + + /* size of the POST input data, if strlen() is not good to use */ + CINIT(POSTFIELDSIZE, LONG, 60), + + /* tunnel non-http operations through a HTTP proxy */ + CINIT(HTTPPROXYTUNNEL, LONG, 61), + + /* Set the interface string to use as outgoing network interface */ + CINIT(INTERFACE, OBJECTPOINT, 62), + + /* Set the krb4 security level, this also enables krb4 awareness. This is a + * string, 'clear', 'safe', 'confidential' or 'private'. If the string is + * set but doesn't match one of these, 'private' will be used. */ + CINIT(KRB4LEVEL, OBJECTPOINT, 63), + + /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ + CINIT(SSL_VERIFYPEER, LONG, 64), + + /* The CApath or CAfile used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAINFO, OBJECTPOINT, 65), + + /* 66 = OBSOLETE */ + /* 67 = OBSOLETE */ + + /* Maximum number of http redirects to follow */ + CINIT(MAXREDIRS, LONG, 68), + + /* Pass a long set to 1 to get the date of the requested document (if + possible)! Pass a zero to shut it off. */ + CINIT(FILETIME, LONG, 69), + + /* This points to a linked list of telnet options */ + CINIT(TELNETOPTIONS, OBJECTPOINT, 70), + + /* Max amount of cached alive connections */ + CINIT(MAXCONNECTS, LONG, 71), + + /* What policy to use when closing connections when the cache is filled + up */ + CINIT(CLOSEPOLICY, LONG, 72), + + /* 73 = OBSOLETE */ + + /* Set to explicitly use a new connection for the upcoming transfer. + Do not use this unless you're absolutely sure of this, as it makes the + operation slower and is less friendly for the network. */ + CINIT(FRESH_CONNECT, LONG, 74), + + /* Set to explicitly forbid the upcoming transfer's connection to be re-used + when done. Do not use this unless you're absolutely sure of this, as it + makes the operation slower and is less friendly for the network. */ + CINIT(FORBID_REUSE, LONG, 75), + + /* Set to a file name that contains random data for libcurl to use to + seed the random engine when doing SSL connects. */ + CINIT(RANDOM_FILE, OBJECTPOINT, 76), + + /* Set to the Entropy Gathering Daemon socket pathname */ + CINIT(EGDSOCKET, OBJECTPOINT, 77), + + /* Time-out connect operations after this amount of seconds, if connects + are OK within this time, then fine... This only aborts the connect + phase. [Only works on unix-style/SIGALRM operating systems] */ + CINIT(CONNECTTIMEOUT, LONG, 78), + + /* Function that will be called to store headers (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(HEADERFUNCTION, FUNCTIONPOINT, 79), + + /* Set this to force the HTTP request to get back to GET. Only really usable + if POST, PUT or a custom request have been used first. + */ + CINIT(HTTPGET, LONG, 80), + + /* Set if we should verify the Common name from the peer certificate in ssl + * handshake, set 1 to check existence, 2 to ensure that it matches the + * provided hostname. */ + CINIT(SSL_VERIFYHOST, LONG, 81), + + /* Specify which file name to write all known cookies in after completed + operation. Set file name to "-" (dash) to make it go to stdout. */ + CINIT(COOKIEJAR, OBJECTPOINT, 82), + + /* Specify which SSL ciphers to use */ + CINIT(SSL_CIPHER_LIST, OBJECTPOINT, 83), + + /* Specify which HTTP version to use! This must be set to one of the + CURL_HTTP_VERSION* enums set below. */ + CINIT(HTTP_VERSION, LONG, 84), + + /* Specificly switch on or off the FTP engine's use of the EPSV command. By + default, that one will always be attempted before the more traditional + PASV command. */ + CINIT(FTP_USE_EPSV, LONG, 85), + + /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */ + CINIT(SSLCERTTYPE, OBJECTPOINT, 86), + + /* name of the file keeping your private SSL-key */ + CINIT(SSLKEY, OBJECTPOINT, 87), + + /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */ + CINIT(SSLKEYTYPE, OBJECTPOINT, 88), + + /* crypto engine for the SSL-sub system */ + CINIT(SSLENGINE, OBJECTPOINT, 89), + + /* set the crypto engine for the SSL-sub system as default + the param has no meaning... + */ + CINIT(SSLENGINE_DEFAULT, LONG, 90), + + /* Non-zero value means to use the global dns cache */ + CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* To becomeO BSOLETE soon */ + + /* DNS cache timeout */ + CINIT(DNS_CACHE_TIMEOUT, LONG, 92), + + /* send linked-list of pre-transfer QUOTE commands (Wesley Laxton)*/ + CINIT(PREQUOTE, OBJECTPOINT, 93), + + /* set the debug function */ + CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94), + + /* set the data for the debug function */ + CINIT(DEBUGDATA, OBJECTPOINT, 95), + + /* mark this as start of a cookie session */ + CINIT(COOKIESESSION, LONG, 96), + + /* The CApath directory used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAPATH, OBJECTPOINT, 97), + + /* Instruct libcurl to use a smaller receive buffer */ + CINIT(BUFFERSIZE, LONG, 98), + + /* Instruct libcurl to not use any signal/alarm handlers, even when using + timeouts. This option is useful for multi-threaded applications. + See libcurl-the-guide for more background information. */ + CINIT(NOSIGNAL, LONG, 99), + + /* Provide a CURLShare for mutexing non-ts data */ + CINIT(SHARE, OBJECTPOINT, 100), + + /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), + CURLPROXY_SOCKS4 and CURLPROXY_SOCKS5. */ + CINIT(PROXYTYPE, LONG, 101), + + /* Set the Accept-Encoding string. Use this to tell a server you would like + the response to be compressed. */ + CINIT(ENCODING, OBJECTPOINT, 102), + + /* Set pointer to private data */ + CINIT(PRIVATE, OBJECTPOINT, 103), + + /* Set aliases for HTTP 200 in the HTTP Response header */ + CINIT(HTTP200ALIASES, OBJECTPOINT, 104), + + /* Continue to send authentication (user+password) when following locations, + even when hostname changed. This can potentionally send off the name + and password to whatever host the server decides. */ + CINIT(UNRESTRICTED_AUTH, LONG, 105), + + /* Specificly switch on or off the FTP engine's use of the EPRT command ( it + also disables the LPRT attempt). By default, those ones will always be + attempted before the good old traditional PORT command. */ + CINIT(FTP_USE_EPRT, LONG, 106), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_USERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(HTTPAUTH, LONG, 107), + + /* Set the ssl context callback function, currently only for OpenSSL ssl_ctx + in second argument. The function must be matching the + curl_ssl_ctx_callback proto. */ + CINIT(SSL_CTX_FUNCTION, FUNCTIONPOINT, 108), + + /* Set the userdata for the ssl context callback function's third + argument */ + CINIT(SSL_CTX_DATA, OBJECTPOINT, 109), + + /* FTP Option that causes missing dirs to be created on the remote server */ + CINIT(FTP_CREATE_MISSING_DIRS, LONG, 110), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_PROXYUSERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(PROXYAUTH, LONG, 111), + + /* FTP option that changes the timeout, in seconds, associated with + getting a response. This is different from transfer timeout time and + essentially places a demand on the FTP server to acknowledge commands + in a timely manner. */ + CINIT(FTP_RESPONSE_TIMEOUT, LONG , 112), + + /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to + tell libcurl to resolve names to those IP versions only. This only has + affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */ + CINIT(IPRESOLVE, LONG, 113), + + /* Set this option to limit the size of a file that will be downloaded from + an HTTP or FTP server. + + Note there is also _LARGE version which adds large file support for + platforms which have larger off_t sizes. See MAXFILESIZE_LARGE below. */ + CINIT(MAXFILESIZE, LONG, 114), + + /* See the comment for INFILESIZE above, but in short, specifies + * the size of the file being uploaded. -1 means unknown. + */ + CINIT(INFILESIZE_LARGE, OFF_T, 115), + + /* Sets the continuation offset. There is also a LONG version of this; + * look above for RESUME_FROM. + */ + CINIT(RESUME_FROM_LARGE, OFF_T, 116), + + /* Sets the maximum size of data that will be downloaded from + * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. + */ + CINIT(MAXFILESIZE_LARGE, OFF_T, 117), + + /* Set this option to the file name of your .netrc file you want libcurl + to parse (using the CURLOPT_NETRC option). If not set, libcurl will do + a poor attempt to find the user's home directory and check for a .netrc + file in there. */ + CINIT(NETRC_FILE, OBJECTPOINT, 118), + + /* Enable SSL/TLS for FTP, pick one of: + CURLFTPSSL_TRY - try using SSL, proceed anyway otherwise + CURLFTPSSL_CONTROL - SSL for the control connection or fail + CURLFTPSSL_ALL - SSL for all communication or fail + */ + CINIT(FTP_SSL, LONG, 119), + + /* The _LARGE version of the standard POSTFIELDSIZE option */ + CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120), + + /* Enable/disable the TCP Nagle algorithm */ + CINIT(TCP_NODELAY, LONG, 121), + + /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + + /* When doing 3rd party transfer, set the source user and password with + this */ + CINIT(SOURCE_USERPWD, OBJECTPOINT, 123), + + /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + + /* When doing 3rd party transfer, set the source pre-quote linked list + of commands with this */ + CINIT(SOURCE_PREQUOTE, OBJECTPOINT, 127), + + /* When doing 3rd party transfer, set the source post-quote linked list + of commands with this */ + CINIT(SOURCE_POSTQUOTE, OBJECTPOINT, 128), + + /* When FTP over SSL/TLS is selected (with CURLOPT_FTP_SSL), this option + can be used to change libcurl's default action which is to first try + "AUTH SSL" and then "AUTH TLS" in this order, and proceed when a OK + response has been received. + + Available parameters are: + CURLFTPAUTH_DEFAULT - let libcurl decide + CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS + CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL + */ + CINIT(FTPSSLAUTH, LONG, 129), + + CINIT(IOCTLFUNCTION, FUNCTIONPOINT, 130), + CINIT(IOCTLDATA, OBJECTPOINT, 131), + + /* To make a 3rd party transfer, set the source URL with this */ + CINIT(SOURCE_URL, OBJECTPOINT, 132), + + /* When doing 3rd party transfer, set the source quote linked list of + commands with this */ + CINIT(SOURCE_QUOTE, OBJECTPOINT, 133), + + /* zero terminated string for pass on to the FTP server when asked for + "account" info */ + CINIT(FTP_ACCOUNT, OBJECTPOINT, 134), + + /* feed cookies into cookie engine */ + CINIT(COOKIELIST, OBJECTPOINT, 135), + + /* ignore Content-Length */ + CINIT(IGNORE_CONTENT_LENGTH, LONG, 136), + + /* Set to non-zero to skip the IP address received in a 227 PASV FTP server + response. Typically used for FTP-SSL purposes but is not restricted to + that. libcurl will then instead use the same IP address it used for the + control connection. */ + CINIT(FTP_SKIP_PASV_IP, LONG, 137), + + /* Select "file method" to use when doing FTP */ + CINIT(FTP_FILEMETHOD, LONG, 138), + + CURLOPT_LASTENTRY /* the last unused */ +} CURLoption; + + /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host + name resolves addresses using more than one IP protocol version, this + option might be handy to force libcurl to use a specific IP version. */ +#define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP + versions that your system allows */ +#define CURL_IPRESOLVE_V4 1 /* resolve to ipv4 addresses */ +#define CURL_IPRESOLVE_V6 2 /* resolve to ipv6 addresses */ + + /* three convenient "aliases" that follow the name scheme better */ +#define CURLOPT_WRITEDATA CURLOPT_FILE +#define CURLOPT_READDATA CURLOPT_INFILE +#define CURLOPT_HEADERDATA CURLOPT_WRITEHEADER + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ +#define CURLOPT_HTTPREQUEST -1 +#define CURLOPT_FTPASCII CURLOPT_TRANSFERTEXT +#define CURLOPT_MUTE -2 +#define CURLOPT_PASSWDFUNCTION -3 +#define CURLOPT_PASSWDDATA -4 +#define CURLOPT_CLOSEFUNCTION -5 + +#define CURLOPT_SOURCE_HOST -6 +#define CURLOPT_SOURCE_PATH -7 +#define CURLOPT_SOURCE_PORT -8 +#define CURLOPT_PASV_HOST -9 + +#else +/* This is set if CURL_NO_OLDIES is defined at compile-time */ +#undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */ +#endif + + + /* These enums are for use with the CURLOPT_HTTP_VERSION option. */ +enum { + CURL_HTTP_VERSION_NONE, /* setting this means we don't care, and that we'd + like the library to choose the best possible + for us! */ + CURL_HTTP_VERSION_1_0, /* please use HTTP 1.0 in the request */ + CURL_HTTP_VERSION_1_1, /* please use HTTP 1.1 in the request */ + + CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ +}; + + /* These enums are for use with the CURLOPT_NETRC option. */ +enum CURL_NETRC_OPTION { + CURL_NETRC_IGNORED, /* The .netrc will never be read. + * This is the default. */ + CURL_NETRC_OPTIONAL, /* A user:password in the URL will be preferred + * to one in the .netrc. */ + CURL_NETRC_REQUIRED, /* A user:password in the URL will be ignored. + * Unless one is set programmatically, the .netrc + * will be queried. */ + CURL_NETRC_LAST +}; + +enum { + CURL_SSLVERSION_DEFAULT, + CURL_SSLVERSION_TLSv1, + CURL_SSLVERSION_SSLv2, + CURL_SSLVERSION_SSLv3, + + CURL_SSLVERSION_LAST /* never use, keep last */ +}; + + +typedef enum { + CURL_TIMECOND_NONE, + + CURL_TIMECOND_IFMODSINCE, + CURL_TIMECOND_IFUNMODSINCE, + CURL_TIMECOND_LASTMOD, + + CURL_TIMECOND_LAST +} curl_TimeCond; + +#ifdef __BEOS__ +#include +#endif + + +/* curl_strequal() and curl_strnequal() are subject for removal in a future + libcurl, see lib/README.curlx for details */ +CURL_EXTERN int (curl_strequal)(const char *s1, const char *s2); +CURL_EXTERN int (curl_strnequal)(const char *s1, const char *s2, size_t n); + +/* name is uppercase CURLFORM_ */ +#ifdef CFINIT +#undef CFINIT +#endif + +#ifdef CURL_ISOCPP +#define CFINIT(name) CURLFORM_ ## name +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define CFINIT(name) CURLFORM_/**/name +#endif + +typedef enum { + CFINIT(NOTHING), /********* the first one is unused ************/ + + /* */ + CFINIT(COPYNAME), + CFINIT(PTRNAME), + CFINIT(NAMELENGTH), + CFINIT(COPYCONTENTS), + CFINIT(PTRCONTENTS), + CFINIT(CONTENTSLENGTH), + CFINIT(FILECONTENT), + CFINIT(ARRAY), + CFINIT(OBSOLETE), + CFINIT(FILE), + + CFINIT(BUFFER), + CFINIT(BUFFERPTR), + CFINIT(BUFFERLENGTH), + + CFINIT(CONTENTTYPE), + CFINIT(CONTENTHEADER), + CFINIT(FILENAME), + CFINIT(END), + CFINIT(OBSOLETE2), + + CURLFORM_LASTENTRY /* the last unusued */ +} CURLformoption; + +#undef CFINIT /* done */ + +/* structure to be used as parameter for CURLFORM_ARRAY */ +struct curl_forms { + CURLformoption option; + const char *value; +}; + +/* use this for multipart formpost building */ +/* Returns code for curl_formadd() + * + * Returns: + * CURL_FORMADD_OK on success + * CURL_FORMADD_MEMORY if the FormInfo allocation fails + * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form + * CURL_FORMADD_NULL if a null pointer was given for a char + * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed + * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used + * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error) + * CURL_FORMADD_MEMORY if a HttpPost struct cannot be allocated + * CURL_FORMADD_MEMORY if some allocation for string copying failed. + * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array + * + ***************************************************************************/ +typedef enum { + CURL_FORMADD_OK, /* first, no error */ + + CURL_FORMADD_MEMORY, + CURL_FORMADD_OPTION_TWICE, + CURL_FORMADD_NULL, + CURL_FORMADD_UNKNOWN_OPTION, + CURL_FORMADD_INCOMPLETE, + CURL_FORMADD_ILLEGAL_ARRAY, + CURL_FORMADD_DISABLED, /* libcurl was built with this disabled */ + + CURL_FORMADD_LAST /* last */ +} CURLFORMcode; + +/* + * NAME curl_formadd() + * + * DESCRIPTION + * + * Pretty advanved function for building multi-part formposts. Each invoke + * adds one part that together construct a full post. Then use + * CURLOPT_HTTPPOST to send it off to libcurl. + */ +CURL_EXTERN CURLFORMcode curl_formadd(struct curl_httppost **httppost, + struct curl_httppost **last_post, + ...); + +/* + * NAME curl_formfree() + * + * DESCRIPTION + * + * Free a multipart formpost previously built with curl_formadd(). + */ +CURL_EXTERN void curl_formfree(struct curl_httppost *form); + +/* + * NAME curl_getenv() + * + * DESCRIPTION + * + * Returns a malloc()'ed string that MUST be curl_free()ed after usage is + * complete. DEPRECATED - see lib/README.curlx + */ +CURL_EXTERN char *curl_getenv(const char *variable); + +/* + * NAME curl_version() + * + * DESCRIPTION + * + * Returns a static ascii string of the libcurl version. + */ +CURL_EXTERN char *curl_version(void); + +/* + * NAME curl_escape() + * + * DESCRIPTION + * + * Escapes URL strings (converts all letters consider illegal in URLs to their + * %XX versions). This function returns a new allocated string or NULL if an + * error occurred. + */ +CURL_EXTERN char *curl_escape(const char *string, int length); + +/* + * NAME curl_unescape() + * + * DESCRIPTION + * + * Unescapes URL encoding in strings (converts all %XX codes to their 8bit + * versions). This function returns a new allocated string or NULL if an error + * occurred. + */ +CURL_EXTERN char *curl_unescape(const char *string, int length); + +/* + * NAME curl_free() + * + * DESCRIPTION + * + * Provided for de-allocation in the same translation unit that did the + * allocation. Added in libcurl 7.10 + */ +CURL_EXTERN void curl_free(void *p); + +/* + * NAME curl_global_init() + * + * DESCRIPTION + * + * curl_global_init() should be invoked exactly once for each application that + * uses libcurl + */ +CURL_EXTERN CURLcode curl_global_init(long flags); + +/* + * NAME curl_global_init_mem() + * + * DESCRIPTION + * + * curl_global_init() or curl_global_init_mem() should be invoked exactly once + * for each application that uses libcurl. This function can be used to + * initialize libcurl and set user defined memory management callback + * functions. Users can implement memory management routines to check for + * memory leaks, check for mis-use of the curl library etc. User registered + * callback routines with be invoked by this library instead of the system + * memory management routines like malloc, free etc. + */ +CURL_EXTERN CURLcode curl_global_init_mem(long flags, + curl_malloc_callback m, + curl_free_callback f, + curl_realloc_callback r, + curl_strdup_callback s, + curl_calloc_callback c); + +/* + * NAME curl_global_cleanup() + * + * DESCRIPTION + * + * curl_global_cleanup() should be invoked exactly once for each application + * that uses libcurl + */ +CURL_EXTERN void curl_global_cleanup(void); + +/* linked-list structure for the CURLOPT_QUOTE option (and other) */ +struct curl_slist { + char *data; + struct curl_slist *next; +}; + +/* + * NAME curl_slist_append() + * + * DESCRIPTION + * + * Appends a string to a linked list. If no list exists, it will be created + * first. Returns the new list, after appending. + */ +CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *, + const char *); + +/* + * NAME curl_slist_free_all() + * + * DESCRIPTION + * + * free a previously built curl_slist. + */ +CURL_EXTERN void curl_slist_free_all(struct curl_slist *); + +/* + * NAME curl_getdate() + * + * DESCRIPTION + * + * Returns the time, in seconds since 1 Jan 1970 of the time string given in + * the first argument. The time argument in the second parameter is unused + * and should be set to NULL. + */ +CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused); + +#define CURLINFO_STRING 0x100000 +#define CURLINFO_LONG 0x200000 +#define CURLINFO_DOUBLE 0x300000 +#define CURLINFO_SLIST 0x400000 +#define CURLINFO_MASK 0x0fffff +#define CURLINFO_TYPEMASK 0xf00000 + +typedef enum { + CURLINFO_NONE, /* first, never use this */ + CURLINFO_EFFECTIVE_URL = CURLINFO_STRING + 1, + CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2, + CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE + 3, + CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE + 4, + CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE + 5, + CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6, + CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE + 7, + CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE + 8, + CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE + 9, + CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE + 10, + CURLINFO_HEADER_SIZE = CURLINFO_LONG + 11, + CURLINFO_REQUEST_SIZE = CURLINFO_LONG + 12, + CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG + 13, + CURLINFO_FILETIME = CURLINFO_LONG + 14, + CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE + 15, + CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE + 16, + CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17, + CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18, + CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE + 19, + CURLINFO_REDIRECT_COUNT = CURLINFO_LONG + 20, + CURLINFO_PRIVATE = CURLINFO_STRING + 21, + CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG + 22, + CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG + 23, + CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG + 24, + CURLINFO_OS_ERRNO = CURLINFO_LONG + 25, + CURLINFO_NUM_CONNECTS = CURLINFO_LONG + 26, + CURLINFO_SSL_ENGINES = CURLINFO_SLIST + 27, + CURLINFO_COOKIELIST = CURLINFO_SLIST + 28, + /* Fill in new entries below here! */ + + CURLINFO_LASTONE = 28 +} CURLINFO; + +/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as + CURLINFO_HTTP_CODE */ +#define CURLINFO_HTTP_CODE CURLINFO_RESPONSE_CODE + +typedef enum { + CURLCLOSEPOLICY_NONE, /* first, never use this */ + + CURLCLOSEPOLICY_OLDEST, + CURLCLOSEPOLICY_LEAST_RECENTLY_USED, + CURLCLOSEPOLICY_LEAST_TRAFFIC, + CURLCLOSEPOLICY_SLOWEST, + CURLCLOSEPOLICY_CALLBACK, + + CURLCLOSEPOLICY_LAST /* last, never use this */ +} curl_closepolicy; + +#define CURL_GLOBAL_SSL (1<<0) +#define CURL_GLOBAL_WIN32 (1<<1) +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) +#define CURL_GLOBAL_NOTHING 0 +#define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL + + +/***************************************************************************** + * Setup defines, protos etc for the sharing stuff. + */ + +/* Different data locks for a single share */ +typedef enum { + CURL_LOCK_DATA_NONE = 0, + /* CURL_LOCK_DATA_SHARE is used internaly to say that + * the locking is just made to change the internal state of the share + * itself. + */ + CURL_LOCK_DATA_SHARE, + CURL_LOCK_DATA_COOKIE, + CURL_LOCK_DATA_DNS, + CURL_LOCK_DATA_SSL_SESSION, + CURL_LOCK_DATA_CONNECT, + CURL_LOCK_DATA_LAST +} curl_lock_data; + +/* Different lock access types */ +typedef enum { + CURL_LOCK_ACCESS_NONE = 0, /* unspecified action */ + CURL_LOCK_ACCESS_SHARED = 1, /* for read perhaps */ + CURL_LOCK_ACCESS_SINGLE = 2, /* for write perhaps */ + CURL_LOCK_ACCESS_LAST /* never use */ +} curl_lock_access; + +typedef void (*curl_lock_function)(CURL *handle, + curl_lock_data data, + curl_lock_access locktype, + void *userptr); +typedef void (*curl_unlock_function)(CURL *handle, + curl_lock_data data, + void *userptr); + +typedef void CURLSH; + +typedef enum { + CURLSHE_OK, /* all is fine */ + CURLSHE_BAD_OPTION, /* 1 */ + CURLSHE_IN_USE, /* 2 */ + CURLSHE_INVALID, /* 3 */ + CURLSHE_NOMEM, /* out of memory */ + CURLSHE_LAST /* never use */ +} CURLSHcode; + +typedef enum { + CURLSHOPT_NONE, /* don't use */ + CURLSHOPT_SHARE, /* specify a data type to share */ + CURLSHOPT_UNSHARE, /* specify shich data type to stop sharing */ + CURLSHOPT_LOCKFUNC, /* pass in a 'curl_lock_function' pointer */ + CURLSHOPT_UNLOCKFUNC, /* pass in a 'curl_unlock_function' pointer */ + CURLSHOPT_USERDATA, /* pass in a user data pointer used in the lock/unlock + callback functions */ + CURLSHOPT_LAST /* never use */ +} CURLSHoption; + +CURL_EXTERN CURLSH *curl_share_init(void); +CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option, ...); +CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *); + +/**************************************************************************** + * Structures for querying information about the curl library at runtime. + */ + +typedef enum { + CURLVERSION_FIRST, + CURLVERSION_SECOND, + CURLVERSION_THIRD, + CURLVERSION_LAST /* never actually use this */ +} CURLversion; + +/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by + basicly all programs ever, that want to get version information. It is + meant to be a built-in version number for what kind of struct the caller + expects. If the struct ever changes, we redefine the NOW to another enum + from above. */ +#define CURLVERSION_NOW CURLVERSION_THIRD + +typedef struct { + CURLversion age; /* age of the returned struct */ + const char *version; /* LIBCURL_VERSION */ + unsigned int version_num; /* LIBCURL_VERSION_NUM */ + const char *host; /* OS/host/cpu/machine when configured */ + int features; /* bitmask, see defines below */ + const char *ssl_version; /* human readable string */ + long ssl_version_num; /* not used anymore, always 0 */ + const char *libz_version; /* human readable string */ + /* protocols is terminated by an entry with a NULL protoname */ + const char * const *protocols; + + /* The fields below this were added in CURLVERSION_SECOND */ + const char *ares; + int ares_num; + + /* This field was aded in CURLVERSION_THIRD */ + const char *libidn; +} curl_version_info_data; + +#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ +#define CURL_VERSION_KERBEROS4 (1<<1) /* kerberos auth is supported */ +#define CURL_VERSION_SSL (1<<2) /* SSL options are present */ +#define CURL_VERSION_LIBZ (1<<3) /* libz features are present */ +#define CURL_VERSION_NTLM (1<<4) /* NTLM auth is supported */ +#define CURL_VERSION_GSSNEGOTIATE (1<<5) /* Negotiate auth support */ +#define CURL_VERSION_DEBUG (1<<6) /* built with debug capabilities */ +#define CURL_VERSION_ASYNCHDNS (1<<7) /* asynchronous dns resolves */ +#define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth */ +#define CURL_VERSION_LARGEFILE (1<<9) /* supports files bigger than 2GB */ +#define CURL_VERSION_IDN (1<<10) /* International Domain Names support */ +#define CURL_VERSION_SSPI (1<<11) /* SSPI is supported */ + +/* + * NAME curl_version_info() + * + * DESCRIPTION + * + * This function returns a pointer to a static copy of the version info + * struct. See above. + */ +CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion); + +/* + * NAME curl_easy_strerror() + * + * DESCRIPTION + * + * The curl_easy_strerror function may be used to turn a CURLcode value + * into the equivalent human readable error string. This is useful + * for printing meaningful error messages. + */ +CURL_EXTERN const char *curl_easy_strerror(CURLcode); + +/* + * NAME curl_share_strerror() + * + * DESCRIPTION + * + * The curl_share_strerror function may be used to turn a CURLSHcode value + * into the equivalent human readable error string. This is useful + * for printing meaningful error messages. + */ +CURL_EXTERN const char *curl_share_strerror(CURLSHcode); + +#ifdef __cplusplus +} +#endif + +/* unfortunately, the easy.h and multi.h include files need options and info + stuff before they can be included! */ +#include "easy.h" /* nothing in curl is fun without the easy stuff */ +#include "multi.h" + +#endif /* __CURL_CURL_H */ diff --git a/production/3rdparty/curl/include/curl/curlver.h b/production/3rdparty/curl/include/curl/curlver.h new file mode 100644 index 00000000..eea00b1c --- /dev/null +++ b/production/3rdparty/curl/include/curl/curlver.h @@ -0,0 +1,56 @@ +#ifndef __CURL_CURLVER_H +#define __CURL_CURLVER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: curlver.h,v 1.1 2006/07/09 22:41:14 fred_t_hamster Exp $ + ***************************************************************************/ + +/* This header file contains nothing but libcurl version info, generated by + a script at release-time. This was made its own header file in 7.11.2 */ + +/* This is the version number of the libcurl package from which this header + file origins: */ +#define LIBCURL_VERSION "7.15.1" + +/* The numeric version number is also available "in parts" by using these + defines: */ +#define LIBCURL_VERSION_MAJOR 7 +#define LIBCURL_VERSION_MINOR 15 +#define LIBCURL_VERSION_PATCH 1 + +/* This is the numeric version of the libcurl version number, meant for easier + parsing and comparions by programs. The LIBCURL_VERSION_NUM define will + always follow this syntax: + + 0xXXYYZZ + + Where XX, YY and ZZ are the main version, release and patch numbers in + hexadecimal (using 8 bits each). All three numbers are always represented + using two digits. 1.2 would appear as "0x010200" while version 9.11.7 + appears as "0x090b07". + + This 6-digit (24 bits) hexadecimal number does not show pre-release number, + and it is always a greater number in a more recent release. It makes + comparisons with greater than and less than work. +*/ +#define LIBCURL_VERSION_NUM 0x070f01 + +#endif /* __CURL_CURLVER_H */ diff --git a/production/3rdparty/curl/include/curl/easy.h b/production/3rdparty/curl/include/curl/easy.h new file mode 100644 index 00000000..deb137e0 --- /dev/null +++ b/production/3rdparty/curl/include/curl/easy.h @@ -0,0 +1,81 @@ +#ifndef __CURL_EASY_H +#define __CURL_EASY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2004, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: easy.h,v 1.1 2006/07/09 22:41:14 fred_t_hamster Exp $ + ***************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +CURL_EXTERN CURL *curl_easy_init(void); +CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...); +CURL_EXTERN CURLcode curl_easy_perform(CURL *curl); +CURL_EXTERN void curl_easy_cleanup(CURL *curl); + +/* + * NAME curl_easy_getinfo() + * + * DESCRIPTION + * + * Request internal information from the curl session with this function. The + * third argument MUST be a pointer to a long, a pointer to a char * or a + * pointer to a double (as the documentation describes elsewhere). The data + * pointed to will be filled in accordingly and can be relied upon only if the + * function returns CURLE_OK. This function is intended to get used *AFTER* a + * performed transfer, all results from this function are undefined until the + * transfer is completed. + */ +CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); + + +/* + * NAME curl_easy_duphandle() + * + * DESCRIPTION + * + * Creates a new curl session handle with the same options set for the handle + * passed in. Duplicating a handle could only be a matter of cloning data and + * options, internal state info and things like persistant connections cannot + * be transfered. It is useful in multithreaded applications when you can run + * curl_easy_duphandle() for each new thread to avoid a series of identical + * curl_easy_setopt() invokes in every thread. + */ +CURL_EXTERN CURL* curl_easy_duphandle(CURL *curl); + +/* + * NAME curl_easy_reset() + * + * DESCRIPTION + * + * Re-initializes a CURL handle to the default values. This puts back the + * handle to the same state as it was in when it was just created. + * + * It does keep: live connections, the Session ID cache, the DNS cache and the + * cookies. + */ +CURL_EXTERN void curl_easy_reset(CURL *curl); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/curl/include/curl/mprintf.h b/production/3rdparty/curl/include/curl/mprintf.h new file mode 100644 index 00000000..da0a78e7 --- /dev/null +++ b/production/3rdparty/curl/include/curl/mprintf.h @@ -0,0 +1,55 @@ +#ifndef __CURL_MPRINTF_H +#define __CURL_MPRINTF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2004, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: mprintf.h,v 1.1 2006/07/09 22:41:14 fred_t_hamster Exp $ + ***************************************************************************/ + +#include +#include /* needed for FILE */ + +#include "curl.h" + +CURL_EXTERN int curl_mprintf(const char *format, ...); +CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...); +CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...); +CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...); +CURL_EXTERN int curl_mvprintf(const char *format, va_list args); +CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args); +CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args); +CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, va_list args); +CURL_EXTERN char *curl_maprintf(const char *format, ...); +CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args); + +#ifdef _MPRINTF_REPLACE +# define printf curl_mprintf +# define fprintf curl_mfprintf +# define sprintf curl_msprintf +# define snprintf curl_msnprintf +# define vprintf curl_mvprintf +# define vfprintf curl_mvfprintf +# define vsprintf curl_mvsprintf +# define vsnprintf curl_mvsnprintf +# define aprintf curl_maprintf +# define vaprintf curl_mvaprintf +#endif + +#endif /* __CURL_MPRINTF_H */ diff --git a/production/3rdparty/curl/include/curl/multi.h b/production/3rdparty/curl/include/curl/multi.h new file mode 100644 index 00000000..5f55ab0b --- /dev/null +++ b/production/3rdparty/curl/include/curl/multi.h @@ -0,0 +1,341 @@ +#ifndef __CURL_MULTI_H +#define __CURL_MULTI_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: multi.h,v 1.1 2006/07/09 22:41:14 fred_t_hamster Exp $ + ***************************************************************************/ +/* + This is an "external" header file. Don't give away any internals here! + + GOALS + + o Enable a "pull" interface. The application that uses libcurl decides where + and when to ask libcurl to get/send data. + + o Enable multiple simultaneous transfers in the same thread without making it + complicated for the application. + + o Enable the application to select() on its own file descriptors and curl's + file descriptors simultaneous easily. + +*/ +#if defined(_WIN32) && !defined(WIN32) +/* Chris Lewis mentioned that he doesn't get WIN32 defined, only _WIN32 so we + make this adjustment to catch this. */ +#define WIN32 1 +#endif + +#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__GNUC__) || \ + defined(__MINGW32__) +#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H)) +/* The check above prevents the winsock2 inclusion if winsock.h already was + included, since they can't co-exist without problems */ +#include +#endif +#else + +/* HP-UX systems version 9, 10 and 11 lack sys/select.h and so does oldish + libc5-based Linux systems. Only include it on system that are known to + require it! */ +#if defined(_AIX) || defined(NETWARE) +#include +#endif + +#ifndef _WIN32_WCE +#include +#endif +#include +#include +#endif + +/* + * This header file should not really need to include "curl.h" since curl.h + * itself includes this file and we expect user applications to do #include + * without the need for especially including multi.h. + * + * For some reason we added this include here at one point, and rather than to + * break existing (wrongly written) libcurl applications, we leave it as-is + * but with this warning attached. + */ +#include "curl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void CURLM; + +#ifdef HAVE_CURL_MULTI_SOCKET /* this is not set by anything yet */ + +#ifndef curl_socket_typedef +/* Public socket typedef */ +#ifdef WIN32 +typedef SOCKET curl_socket_t; +#define CURL_SOCKET_BAD INVALID_SOCKET +#else +typedef int curl_socket_t; +#define CURL_SOCKET_BAD -1 +#endif +#define curl_socket_typedef +#endif /* curl_socket_typedef */ + +#endif /* HAVE_CURL_MULTI_SOCKET */ + +typedef enum { + CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() soon */ + CURLM_OK, + CURLM_BAD_HANDLE, /* the passed-in handle is not a valid CURLM handle */ + CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */ + CURLM_OUT_OF_MEMORY, /* if you ever get this, you're in deep sh*t */ + CURLM_INTERNAL_ERROR, /* this is a libcurl bug */ + CURLM_LAST +} CURLMcode; + +typedef enum { + CURLMSG_NONE, /* first, not used */ + CURLMSG_DONE, /* This easy handle has completed. 'result' contains + the CURLcode of the transfer */ + CURLMSG_LAST /* last, not used */ +} CURLMSG; + +struct CURLMsg { + CURLMSG msg; /* what this message means */ + CURL *easy_handle; /* the handle it concerns */ + union { + void *whatever; /* message-specific data */ + CURLcode result; /* return code for transfer */ + } data; +}; +typedef struct CURLMsg CURLMsg; + +/* + * Name: curl_multi_init() + * + * Desc: inititalize multi-style curl usage + * + * Returns: a new CURLM handle to use in all 'curl_multi' functions. + */ +CURL_EXTERN CURLM *curl_multi_init(void); + +/* + * Name: curl_multi_add_handle() + * + * Desc: add a standard curl handle to the multi stack + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_remove_handle() + * + * Desc: removes a curl handle from the multi stack again + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_fdset() + * + * Desc: Ask curl for its fd_set sets. The app can use these to select() or + * poll() on. We want curl_multi_perform() called as soon as one of + * them are ready. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); + + /* + * Name: curl_multi_perform() + * + * Desc: When the app thinks there's data available for curl it calls this + * function to read/write whatever there is right now. This returns + * as soon as the reads and writes are done. This function does not + * require that there actually is data available for reading or that + * data can be written, it can be called just in case. It returns + * the number of handles that still transfer data in the second + * argument's integer-pointer. + * + * Returns: CURLMcode type, general multi error code. *NOTE* that this only + * returns errors etc regarding the whole multi stack. There might + * still have occurred problems on invidual transfers even when this + * returns OK. + */ +CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle, + int *running_handles); + + /* + * Name: curl_multi_cleanup() + * + * Desc: Cleans up and removes a whole multi stack. It does not free or + * touch any individual easy handles in any way. We need to define + * in what state those handles will be if this function is called + * in the middle of a transfer. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); + +/* + * Name: curl_multi_info_read() + * + * Desc: Ask the multi handle if there's any messages/informationals from + * the individual transfers. Messages include informationals such as + * error code from the transfer or just the fact that a transfer is + * completed. More details on these should be written down as well. + * + * Repeated calls to this function will return a new struct each + * time, until a special "end of msgs" struct is returned as a signal + * that there is no more to get at this point. + * + * The data the returned pointer points to will not survive calling + * curl_multi_cleanup(). + * + * The 'CURLMsg' struct is meant to be very simple and only contain + * very basic informations. If more involved information is wanted, + * we will provide the particular "transfer handle" in that struct + * and that should/could/would be used in subsequent + * curl_easy_getinfo() calls (or similar). The point being that we + * must never expose complex structs to applications, as then we'll + * undoubtably get backwards compatibility problems in the future. + * + * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out + * of structs. It also writes the number of messages left in the + * queue (after this read) in the integer the second argument points + * to. + */ +CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, + int *msgs_in_queue); + +/* + * Name: curl_multi_strerror() + * + * Desc: The curl_multi_strerror function may be used to turn a CURLMcode + * value into the equivalent human readable error string. This is + * useful for printing meaningful error messages. + * + * Returns: A pointer to a zero-terminated error message. + */ +CURL_EXTERN const char *curl_multi_strerror(CURLMcode); + +#ifdef HAVE_CURL_MULTI_SOCKET +/* + * Name: curl_multi_socket() and + * curl_multi_socket_all() + * + * Desc: An alternative version of curl_multi_perform() that allows the + * application to pass in one of the file descriptors that have been + * detected to have "action" on them and let libcurl perform. This + * allows libcurl to not have to scan through all possible file + * descriptors to check for this. The app is recommended to pass in + * the 'easy' argument (or set it to CURL_EASY_NONE) to make libcurl + * figure out the internal structure even faster and easier. If the + * easy argument is set to something else than CURL_EASY_NONE, the + * 's' (socket) argument will be ignored by libcurl. + * + * It also informs the application about updates in the socket (file + * descriptor) status by doing none, one or multiple calls to the + * curl_socket_callback. It thus updates the status with changes + * since the previous time this function was used. If 'callback' is + * NULL, no callback will be called. A status change may also be a + * new timeout only, having the same IN/OUT status as before. + * + * If a previous wait for socket action(s) timed out, you should call + * this function with the socket argument set to + * CURL_SOCKET_TIMEOUT. If you want to force libcurl to (re-)check + * all its internal sockets, and call the callback with status for + * all sockets no matter what the previous state is, you call + * curl_multi_socket_all() instead. + * + * curl_multi_perform() is thus the equivalent of calling + * curl_multi_socket_all(handle, NULL, NULL); + * + * IMPLEMENTATION: libcurl will need an internal hash table to map + * socket numbers to internal easy handles for the cases when 'easy' + * is set to CURL_EASY_NONE. + * + * Regarding the timeout argument in the callback: it is the timeout + * (in milliseconds) for waiting on action on this socket (and the + * given time period starts when the callback is called) until you + * should call curl_multi_socket() with the timeout stuff mentioned + * above. If "actions" happens on the socket before the timeout + * happens, remember that the timout timer keeps ticking until told + * otherwise. + * + * The "what" argument has one of five values: + * + * 0 CURL_POLL_NONE (0) - register, not interested in readiness + * 1 CURL_POLL_IN - register, interested in read readiness + * 2 CURL_POLL_OUT - register, interested in write readiness + * 3 CURL_POLL_INOUT - register, interested in both + * 4 CURL_POLL_REMOVE - deregister + */ +#define CURL_POLL_NONE 0 +#define CURL_POLL_IN 1 +#define CURL_POLL_OUT 2 +#define CURL_POLL_INOUT 3 +#define CURL_POLL_REMOVE 4 + +#define CURL_EASY_NONE (CURL *)0 +#define CURL_EASY_TIMEOUT (CURL *)0 +#define CURL_SOCKET_TIMEOUT CURL_SOCKET_BAD + +typedef int (*curl_socket_callback)(CURL *easy, /* easy handle */ + curl_socket_t s, /* socket */ + int what, /* see above */ + long ms, /* timeout for wait */ + void *userp); /* "private" pointer */ + +CURLMcode curl_multi_socket(CURLM *multi_handle, + curl_socket_t s, + CURL *easy, + curl_socket_callback callback, + void *userp); /* passed to callback */ + +CURLMcode curl_multi_socket_all(CURLM *multi_handle, + curl_socket_callback callback, + void *userp); /* passed to callback */ + +/* + * Name: curl_multi_timeout() + * + * Desc: Returns the maximum number of milliseconds the app is allowed to + * wait before curl_multi_socket() or curl_multi_perform() must be + * called (to allow libcurl's timed events to take place). + * + * Returns: CURLM error code. + */ +CURLMcode curl_multi_timeout(CURLM *multi_handle, long *milliseconds); + +#endif /* HAVE_CURL_MULTI_SOCKET */ + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif diff --git a/production/3rdparty/curl/include/curl/stdcheaders.h b/production/3rdparty/curl/include/curl/stdcheaders.h new file mode 100644 index 00000000..2c5bddc8 --- /dev/null +++ b/production/3rdparty/curl/include/curl/stdcheaders.h @@ -0,0 +1,34 @@ +#ifndef __STDC_HEADERS_H +#define __STDC_HEADERS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2004, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: stdcheaders.h,v 1.1 2006/07/09 22:41:14 fred_t_hamster Exp $ + ***************************************************************************/ + +#include + +size_t fread (void *, size_t, size_t, FILE *); +size_t fwrite (const void *, size_t, size_t, FILE *); + +int strcasecmp(const char *, const char *); +int strncasecmp(const char *, const char *, size_t); + +#endif diff --git a/production/3rdparty/curl/include/curl/types.h b/production/3rdparty/curl/include/curl/types.h new file mode 100644 index 00000000..d37d6ae9 --- /dev/null +++ b/production/3rdparty/curl/include/curl/types.h @@ -0,0 +1 @@ +/* not used */ diff --git a/production/3rdparty/curl/lib/libcurl-3.dll b/production/3rdparty/curl/lib/libcurl-3.dll new file mode 100644 index 0000000000000000000000000000000000000000..ce3ef581625c346e1143ebfe22e414ba2e971bd8 GIT binary patch literal 181248 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

      vy3eOVu0xXiD$x4)i?CoKyYE4= z704=t(rAU{n+NZ^i-4*(HSjdC1-3+{ZCc&6NJht{6Pji3mbip)S* z-M8eB9uN%;r5ZBW-e@SB-e5eG1<0SfOc0yZn=*)+2qT4+r+O}kebnxA^ryUT2fVOL za%um;^e;#K1!t5r`591WT#wu&Kb?_Oe+vG|ze1xy>`c9eM)~|1UP+uxjrrXj4-2S{yN>$==4s7Dms$!-45cGLPosM5;19 zAsDwp)FQh%wiSGlF#Q0xECJ5P4OlKU_DvkNC=)=LkA$OI)SF9M3@5UC99N`xT13;f zXv?9PZ^}P4c}Z676*#V_J2yjU2P8I2i!)>hZkrV6w6mVik~SFZ+isu750p%#bp)zPn%HS z4`eH6+IdJsKs~}1DtH@Zeru`&+s}WtJI21N)fbig^l@23#Pm`jDKR@ zK2o97i%)}j75qtTXIZ^?5KR+T+yuHef;;(_6MOY#j!^)vOd=xMx6vw9{<*+16*r~xE)S{;?Yo^%-dxR z2GAHQ&P6qda~f^^jHMrALd4rei25N(56KNqJ|#J=-y(Kkvu`csO( zj@KaWlJ|suOadUbp-0GvKs04nBpOqP0KTfqIE6e~_hVp-n(A{*l^sb1-d&yu91_@h z=6dO2vGaTZnZb9h2?k4O=p-Jfaa8bI#mQ(|UI%KDDtHU<62F@%7$S`0lHdYQ_B{PI zii==8&z`~GjmN_gwi@Rw{&#SgqD4F*n(J)vfa5>H@3G;PiUe%Ze}sKD0XrtRDmh0T z#lFrzIJx{VuH97MZ%8VtclMiV`~#D9)``JV<~yzi6wFDu-fLyb2C$%PS@2DtLqk7PKx&FPN!0mE=O>WI?NXy>{ld zW!EyK6tZOFN`MTuK-PAHK;0q8o6TGckkrYZRY}Z~2qUCRv9tAhp@E*Gil9ne4<03b zi8Z%Sut>?WQhE+_qwj-N6A8?GY@zws0$#dNXp1`hK&bLsa5Ip7r7t#5>pGoiC{1Y_ zzd?Wa*a@1>&Y?O#AO2skqy7$|_wzTlIHU`>IRqfcqU%U7o$t>UL0i}g5E(ugx@vt?p#NT)UqY)RDn2ZO`GPe;T9O*(#pNKig(ggHw8ovTL;)upcy!hmQ3hMI3yzj!`w6ms~4nK%} zm~L(dmT!xkdUnBAs;l6rb^a7aSg`RggxnL3;RzywPtfVoCVZ65LO>|~@XdI6u1(ax z;6*bkh+$1LQKA0!3_QF2Mzhc$-J$z%ZDGO3sZ>6P!9Xv~0UIjV0 zEkoS;JQnj*xD#mYTUNRwj|ciKWrum)$1A#M09{meJ}A2=qWx$CRgVyTKr{88DrIN| z^ho)y(aVBravm+OWXtCm*UO&AFc`N{3wRQooR!tMwFw`zBW3jG_r8c`3ruY40#>$A z7nwa+L>HroxedM*XsDFN5y0n@FUQl*cL0&4&ib*oCUl6zzlLRki7RmjJl$929iF>`g&_+KTvO{|wv;!mD^;m&TK8}i1+&Mcs|4_6f-+@>- zGMhWiMZ+7{qFk_v8ZAL+p2nB2f-|zQ#Prv%X#{fU8WYSBJyuZmn5;xO7T$%f*Vl`u zWnP78{(SWXAk)8BA10ZOg#Uu4Oh!g5ycMtX4|@vy%r7w*O6bHxH2gZ!k^L(D;2-r( zBYu7SB&>A`KE(-FbLk=@H?om%3OA1*34GuK=xFP@5I>Is80&KU&BDusQON{CL%yJ| zTBJv)?gmklSjcN<9ZM@ByU80B3)7P-#$C=B-P9f)-awqloT2usuv6Z{%@YWu6Tr}G z!*o(SL~?*1gq+Wwuk98MP;ZEY-xp}V@xse zw3-$^j0D^SBftgTH|B~S2AsO<^gO-reyO-Pw`?Ez&h;sCVUqO|iI|>-jJ16`;yzj@m4}U?+ z%GpO>NF^G4@+e>l@KvUB|O^4zZD@MOcOsUEP6Wz&l0EpfZ3CsPPM!)S}b#9m4w? zY}9GkD}6*(9>%<}2-@~R5C(1gi1_IQ0FEdRB0v>BR0_jKsA5#36o;K542v#P+14S< zXFtDPEPKgPz5?^jFce|SX7JUhTwX%Wp1`q4H?esDS75Id0ZlE|lt5;Wm|dPl0o*bn zRvp&f(vT(u=+f*m4hW5Oyj%dAde1MHAQ}Z#C$xWn|GF0@vRr_m16osHS`M{z2_`HO z?3s129e}Bn1e?fWl$2Yv<+!xe%`DX%oyJfkHNq0z-$3i|Nf&A9*AjiTGe@P5p zC~E_nY)zrDweuM?`coHUub|J;zS?=WlBw5I=NtX>`funrRj=>DFOKrVC)Nph#0RC) zpOl(`<4cI+qmwa(GoVZgVzq;&>$f{6*K*Ix6i#97QK$ysMwGFhV zK8(MM2Waqweu)>!GX9O&b|p_&_y!CNeLaEd zC6xvHlr;RQ#F9$mT65_=wpY329zq%`nPdG_(hqk=Kt;&{7FdW&YT{k6i|{iGW}?24 zNQ#Bu#FHTFTFBw4mlWd9Xduba!cIiPGw5y1s%f!sp$!%bPqE+F?n=DfLY^GYlYbCp z$=7$2xpcJ8&u9KjOwb4@H<#uzF%Mc~R>n$lslHgrXzOPz{TP!Y0><|Cl@uaEN$|kj zMttP|_%kVa;qsdT$yZ=e5N%RC4;2S@i+B7H8lpnr!4Z_vmhaDkGq+i-rDh19=Hu9h zt(qVI65?ZZTfXTiDRdOc1u;*A*8gUhnirgmvnNXkC_)H=J1jpSZZ0FT@aIhExpZ2M zql}NkE-pyCn3iN!%HKd$n6_xOLDhT0h?9jaKtqYWf|&BXyfe6wZZr#wg~VM9Xf4TA`cuNvX+9pqnj5xb!u2?> zZdIW;pE3-N#|X)cwp++GExqx_ZzqFZJ4GFUKkWhlo`nv_y^19C`floTeIvcIjWlj> zdgCSGg%lkNO^tZ32s{URgmNi9u9m(^WEOTJ0e^d8xaY~aNW$M@3^>2 z-VfDfOUq}UV+}zovv@I;6^cV^f(e6RW*xQ`nkBenGg9DT(ec#Ryovl+iI0*HcCb`j zK{3F{iItFl1v!>R&&N09)xI!vf?lL_^-s`OclJ&Y16Qq>A=R6;<#Mt*5oelvEU&M>*xvF4(k3Tpp|#78 zfhztg9!(38f?GD|PZUz%aESV&?kurgovLeGz$>GD@=X`frFNrBvC-X0=*sYrZv&X0 zA8#iwf#E5W?-k$g;~TBoSq%7i9^fpURd*KVyQC*6L(60pfIj|n3$PKv+)_LpU2XTr zmEb^}PX1Z*QgBBz03>xz4SZ)PScX5?M=U}eO=-xWE`0~~O#EFu5>5ONe@KhYg?X$l zvU11Y!!Y^+|pdTrLrwgzvCf z*+@= zfLOg}dXbV6yje1i>AMLrTa{BG{{#)S2ic*|pqYl_uO%VRnwI3>Ud-K!TyP@31xA;B zZO;g+F=g_51U8;eK7mm}RziFufXt1RWl{^dx#l|Sp(Ko)?JV}s;b~eU`>MkGjb(c-pN|-nZ5SX1H%9dE=vNCz3+Rd5!fTa=P(NcUr>S-eT z$u9pg1yKcNfJi|dSzGoHzA-@}B7vX*brg(;@URNn)9b0avwtFFmLqEz3r_^^mHu^S zuSQ>bLXU$V@dxmThy`q9Cs`v{$h{0G{JdoSJa7H1vwofuKN`u0d^4Kh<4ehzus;WX z#Zj%M8?&$ot+Ezf<~G7)ZX-nIHo{|WBQ)kV!Xo=oTy_+~KfuEENZ@Zh0cqZ!8xdb{ zn>>V51Xa~cds!pFq@M2ni}D|!S;3uR(clT~7Yw7e(el9)+A1=IsqYE>f-(gPatUm} z7Yl~~8WB%~*1UvBjQ{I+;3d=sn%^q;$;XNM(Fk**zW_Y?6A~f(F>SgQ?i*~eU_bpA z0QU3uJ|!y%$D*V7+=KZB&d~+ON%!bFtx90iKcGKHZ4Mt=b+@j$DXn>x7k~a($tl>k z`gXZp;8_Da`g&^hy!p{0tU=d70Tt|9l_zH`rFuxJs`Ll}%#Kb){vzbd6GdJL@+xtC znkW1(VvjY7=41I6H|SeT2e!;&>3fCshg`*V+!W(HM7~R4`QiIRzQ)nLSG0Nz#mf1p^sjtZvja3IWZUly46T>I@ z(SC+6YcI(b|C>ZSE3Jrcu?mZ$2K*tItca7#voH@(oi!OoAM0ZvBGCFY5k8xYThQEj zh%oJnm&0FRjTnSU#KPl26u=Of5P3xeBy1du6vX&gqe&eSd6LARJc9W9WX}k52kV=U zta`H4at-kmDG1_GCI|fTFF?AGa&sG@;qNaeF~QZ;)^|;E#s9tMT8?r!T z0|X3;5)=glg(xZkqJfZr4|GEyFNDxm@fvMKxeKT~7Vd7y<+^HxD79j-YHM5CqCAX5 zAUv7{wF+o0K3cKW_FfllsuluT_W%8!xtj$4?eF*h{G;rh=b1AzXU@!=IdjJT<~^Ym zE>>A~AUX-)T`aHWB*LSA1-)M&dKz_b8*HrF*qV*Qkj;8rXUwYk_!V`_!3FQ&ti@Nb zcj4LEPGC`KpL`c2(FAJs%yNzh7|E!%O;z*F`MOoM*t%|s8S?<3+4v0>XWb74B9%zX zN}Y{LK~v;i0hT5->ud%zV+YfF?t>VQS*%8SPaNU%wb48}-!mJB;9Ky{@@`9}6zsjX z*i9H?`6rI34Cci7$Z_Jm;d@o}2E1dI>1NepbN-S zR{FJ5g4EflA>>X?Lx7d@FZ_&<)Rwf+xsu+jnQz{!TT--Drdt!|(@bpWYLV}oXpzA=AtF`Kd2onbY!h3H90@P!q&w`##^o>hw}QBD^AWq&d_-{a#)43 z>(Y~#+xHw11StRsM8`0v8nd5bx^XVWG^;Yfq?Ztl_7suMw>SI|Os07d@R;MA#$tw> z{4Vt|WjDsPD{I5G+#5*byp11gJ)6&+qkk+%P}iF42O`WoZOxjxqLo7phDCSeB1q2d zR-ELaCr>h>^AQHOhi7;{!?$i&g`K$u{M_6N%`JU-EDEYk7Go}3f-gQR<+C-6Pb>-* z?PRb*+k`4WzYE!neFI^$E;My@BNJ@$j?L*78efD99n1cOQ{jA7WjwQEH464io4AIVjE~$z*r-3x1;(q^QpDy%6q%1+&(ag7KiFf?GghzHL?D#_xh&Li+);_1wt^#_u2Ruh*~ z&b>@4wYFBZpvs_*Yj}OEKvg`po}^TsoIfKuAA8sj!!inRN&y}td|*fhwxm_673Nv@ z&PaSG@X~trd2B0l#+L4@fOe%y`*cU{3OD3$J}hMB%(J#Khp|J*HZ-WbQj2z?JNs=Z zvk5CqYy&Yj#iDm3HTquTN8y?AEMkLr5)wf!Yz<2x5rL>MHk=u^A6kK?izaK#c37GN zFxAGZ1!TjDUTA?7u_@uo*x33l$mt#dZDo=6{mY~Qh2Yr;ftQZsVI}RS#QAbN9W;e< z72xp8>cZz>h~-&Fkmx?B$d}oBn}`m_1g5pmF!8H;8iu^z<0*!M zb|^>tOya*j%ptf-lp9Bw@2Qmb3W$)*c;Xx_DTzh`NNQhEJ_D{;5277nyoyaQ;&qkcs2FCNWuU5baFiwEBTC%mhH z*ao622{cA9YG#5a;l1ia@=onM2rsa%1Y^OfrRFAZHa%pv4L@S;y+I$ly4!?d`sM%q zy0|OQI`t6Mj>&*812#7$ephOo_Ue~$DN|uD1riuAc$uvW22es-(ra0kDOE5ViGFyO zI_K!?w%|!nF2`dpELBujhMQYZ5syAd8v>*&?LWSSman2^!)lzKK+iNGUpSCP-pWSTW(Wcm zKmbH&?O@Y#icey?@0yNhXfn`icXBx8u|C%T!^AbIj#3ie?z11jeI^1VjP% zN;&He%8+T|s@^$`?R^Ki9|jYJ6!=qyxfH}H!n)kr&Pi_>K-2>Sp89q{@RZ7FM)L5^ zGn;!g>O%B>LQw9ms0E2}yd%wBVQV1tS!iyzN}mK$ZGIQCWX72oCtyB)lG`@Q@ALSr zuwMh)012RVyrhXUmO>>vq-F3dS8CgR8vNNj;`9g9j<8Q9>`ZyRxp%tNv;hTBSF6q1 z%nCy-ZkguM@4_M4I5Z8!OgW-5OB*KB-^6~HD}}@~TpXjsK9r6NL#|sFxBXjX$BVZ} za#f^hX0D4Yh~vz{8@FtiW1oK%9vs z75-=Yu?j_xELO(v@d}ebsina*?tNp$b4@|L)$pKcJ$O*S2=iO&vzjAxEc4rWhr~{L zPv{=>6Q|yz30w`&+Ttf(DjH4y(ISs$vy2_I=d9z!-P4pso*&}$xR%QCQ+Nsrc(SvC z`%~~wJ6#81z%*bwjzEm2gILa_h0cN^QMt`E*Q_~--bl%ZuK=_g+PF21R%Xt)>419& z9ol`+s}cP^DE5W#r-s`{967w`C}**O=zjXC4yRP<@>hq+PrrS3h+9hF<{$8bVp44c5|_9V#K z09fuTrYGgd3O!t~8g?-BEB-rIb}fV6ifyh~2K^n5y24{G)ptNM6L3kT-B=D5OeHY-e-6`3Fp{1|^6ZWc% z6`1!dHt$=K7&QE!NIc+cW4G6M%VRc>1(@r|4$S$B&BaUbc4X_M(J&eT9YwVf?+%qT zidbaNFj&T%-`*%&uB`181EA8Q616ZR^`B2~dDC^NN{*htT8twA1$!%48o$5)8sBLj zhwsB#^#EZ%olFQ>_d^e zZ$L>nWW1FH6@$xnXopr=qBY-nF=Pm;gPHb-UH^Ngoq)8RkrNmRTc+()LKekm=!=z= zO_%e~pnU+PkQ{cSmLp;%?N|SX>dWp7ug_bAL*KjMg4So7Ua{ zI+X30I#LyIM1&VSeb#3|t?J2k=(5bD7yOt+iglP7U~0`?)R_3qHq zXtS$O4|HUw9^Hy#>ri9z-4OXMUb$QrG-p1jsI0XZO1mq`Hz)|hkx#mwnfN}=NfLAU z#Apu)9eHQSENKWth9FhEI0wI;Iv-|ZdAUSli?dw=Fns9SDrp$r9;~#7-UefYHC8zf zEx|5R8pF;K06>}|vv{DxV@QC+FFv_E$*A)s7vR;TASC){f_@X(oClGD55|t*LC!K* z7tZm7(q8lyERE>nt&&J!+u`Bu!~cj^t19j1Z$X{5+1&z)ix=M)@VG9>2fgXfN8K1!UyyLj=6Mpk9D7x^`z z6lb%I=7Co-&_wysT2j#i192;hyRl+Q)CK_&i7y@)%EkEWhLW|PlEOj{+E>Rhf7B-K zw96DeOz;WFPLwd6_fd*_rK`2L0M~6MfMxGPAobjW_V2#uLXIv$v2GXq9MOr3ItqBF zmAi~YdJ?ZGAF)>xG-qrljGxqFYs@Vg{bfn(hJj}67Wg5x?QiX%3+!1`VI}yw;jg;6 z9+)5sas{u3hEIpGcycSD*v1wTF%}tmUu~mX$6xH{wigw^0*;B)VM#5h6%V)uqGIV` zC)I#axluk4hfJVWJ%g;r$P3lH#ZdqybFcN3#9R}v{|YHUaZs~}%fgw!;|3Sc%Efqi z@cIjv)e=^piVbXgoRg}(7E3I!ld-NJjkHpQTx>o54f0a)7=fRuz~0v5U#Y-MjI|>Y z7>Hg^ta~Yp%^0Uz^-hJ=NVgf~9Bjt!l3$I#TW^){x%f5ipO1&jq5L7?46)?)E?x#` z2``5p%Y>8yOO91Z1(vf=@Gl{q1~)zs8kGP3_}ZMM{qd%fm9q{pkEocIPh+@^bi)Ym@ z5CBs3<$G1tMf30mrju@OE!Kv+a86O?ATspF42gB0JB{C z4w6;a-@up%1sJG1WNldTJxQhWA)RF@2ECz>`?x9A)rCN|^Pq*I% z%W=`pCjw|ef?AdyOpGFtRg~G=I+KX(=D*hL_RgARxiyoG<@UV`1S#dYp{wWTT|KwK(>i4=E`;-Dr)&Z+XPRww-R(bNTPGGoaLC#% zwF|)I^sEZ|0Z61~VWLR{$P_36!u#)gTD{)Y^{ie)$r@ZDIt<%lGqd=zqDLPX*fw(w z(*EiM#xYbrLOm0i*vKS6Rh@5P@qRE?k>I7>)murklOLrnrD;tDw@_-Lq#;Zi2$o$G zUSvj*BKYdKNoVlk6&Ufm8ZmKLCqt{j^)w$1Gg|V@;vwS14LteAix;Wn_cvYIHO>t!%aFD&|PVhb#`CpnQzBm8pbZb-1{E!2h^-{~TSd#?RE} z=lq}`eh?iuZo!Ak(pKX^6-6@R3L##c#}`ZE29?YC7cVZND_%TVKE_d?ElY_US2F;5 zv!?L@LaRgF=IuP!gGUrBCdzO5X7@*Fuw4JkxG@@eRrWFXAQA;kfGZ3F#69$Zmb77) z1$Iq&{`;;r8aj#B+`Ft2xjy4_#t~ef^H_MMtO_EUs&F&*_+TZylMCjntgVKGR2j`x zEE0;G&FkoZk+b?+(Zj9v^YQC3>ot6=dL6%JJ>O?h7HRR3%bw--k3Yh#Z%Z%+jy~9u zz~i-e(juu33TnTQaeu*X&}F&Ls%J}pzT>_}0NRt3owof5cCX3J9Qt;Y&ILExOXWM# z4#{_d{e67P?Tu(AcY(Vvmjytd{CjsYYy+UV0n-tnp0lv8X70@q#MvXo*+A!DadJ6l z_ZKI-+wLjOxpaEOsnPkbZwTR5IzNRIJEr93yXgBsB6vP%9}*`i-)Vmh1YT##dL zmZ0`@1U(OD%s2vP(4PJo*q?C_NZ_6U=6XPOZIg7}31P*L%jZC3^hV@%P@XzvRX@{IU9#OOwQzd&SW7JToDZ+IZ`07_gm)My_Z zh^$3v%#nw;YTGn zrW+!8CkG;P;UlI+kU6k6Jtd+f5Sh**{*7ZDqy3IRk^~=Zd`a|8EBO|Vf+`)z{=@B!D#372}EMZ5?J|tsn5Nr58?e2KZPb* zAP|{^T#H5&t`JWp!2{X*0+9twN75>6kf;~wf0_Q?Lzyqm;Le+(agfL#j}ezom<#XJTu@a>@Uvs z1bZdHo+JJOg~I^x)WhTCGIEge6DL3YL&g7Z_!kW>yg)oOB^OwE7P1sxECDB(`aQIK z;c)SLi1A7tp@o-7z=a5KUS@)u#eWz4i*7HxQasf}_|n2r;%R0<*@c&i=fO;5Tghx= z#P6s7Uiz;S{|;4!tHrZ`Rp3A@yh;40S>F=+XN!Lj7y^o^5YNp-AiJ+f%qu0^;Cm=$!A1 z=lx80&Mnl$Lt~YNcZf4UDC-F29`XMaeiV7PcrIhj9$=&{{zFWBC;j!}@5>Sf7cLgh zT#Sss%JryH;Zg}GWa>huepvi|I04!c&)-?~JVrhu{(sS*P5<3D0Z!z-Oe78#J}!|B z9F6@88^!YyQU+GN$@CB7r*NdC|2@-Z7mg6m&*5?QGO|ki?c^kz>3=3f;G;;kmli%K zk*4a)W#SpdoF-HLRQ!$f-$(y*l2Z}bm&jlEyacqPbDb(AfHb(Cvz9ZRo^v6u%dmg> z9XvGm)C&xq;3k<$&nVZ!g^F{5>)~?6=||7_@jJO5S$dB7wVIxjW7ZYhTd+8+8vhhy zme_bCVrtCC&i25xEvN8Kam35q(FY>GNAp{fyx{AlJ>ou!eLTg9FHD*7O)0{8j)^3o z%UY%?E+I@GuV;DubM8k{V?3_(*8h~4-r3=OhTdB`yc_79*5RcRn{!Rd+tN_a^;@LH zs^@Z2?`74|c>+Hta0z@NzS!lunQ|OwV`ReCz0$ z;d-8=XG%w|271T3aejJ6xSlL}&UZb-=;_^&iyJa0O~sYY#TG5{FVG`4)Pd+RIO3&q z_h2pO9EDpo1kOJ_&Y=!3_gT&!cu}p=pwpxx^!}<5*|DwlY+R_EB1(md_aVvCnGCNU zIrmE#^y4+N;x6Yd@nHe!`fury16%>?(*0e)UxV22&esVW)zhXRfD(FdOuXNKHMRuDQ50IZ2)rFDbLTR|L&}6F_qYSCus@D=DHDrM8po4OTnHB|+eAY5 zHE6!BXSwz^3bjX|OxaWGS+U}tgaLH~zUYd*Erfyc9CC%-vXoGB-9>Db(*zKtlgMx3 z={9@F*hL&I8%bQ8!CteLcMk-MCzO?ogi6Jg1hfzAuxYjK6%1l^gtKMUgY* zTj5-Am=%+qQTT>;?gJ2zLu`YrT4B_3S;aMp_JN2VCX zHpM`;6P7U2de_i8l}Da)c4%9bjRtwA#|grdGFlF5Z$b$vYJUZ8PqmfaT2IXh++7)M z)-&E2r~s6Xb^5rV$giCY*YhMj+_A9!6P(YLtDf5s=OfocNu=|(>p4abR{*qBV%A6C zMf>n#ne&31 z>k_HbBzRoe0&ZGlJD0nWOPpb@YcYjS=fVSvx)e1y0Todn23W==fp%PclsY(V@UpQV zWT}63J!|QC$Mw8U&jHtSke*ju&)f9;*7Y2r=clgc19}?ikr8geg>#fZWF;IpSb6WC zXp53Iyft(W0xImcT)*>OcokiJLI~yXvXgY@I&rb}hoFbi`cJc?uTb&zlrT6vsbWni zWN-$!o*+G0u4e{4N&GO`1bV)3J!9$l#P#^-IpTWe;ybmFRtf_$w&L{+;h(b=4u$L{ zxEu}->s+yreLZY$!}{5d7KBvzA=28HRWK?+8XTS!0^y?2alAr^m~Omwiwif+Tqb*9!0`%u^ly={ z-2Un_;IsLgl@QZ-ajieO*>Avk{8OTq6>i?g^mr6vGiM3KUm;RcYYc>PDbW!sY?o=9 zIzyA2DF(HF26!|#UvBRa4WMkxIEX|>^HG5J=wnHQ<@SQ*SbQfpGc388L}&jAxD;V#BRI6BY7)9gAx0TS9ntEA zb*AK_^i|g+(joOuf$WlY^ciXC>rAU2vPy~AKtQlodIj!ag_|in=ljyl0dZVmG)tMG z^wvx~gq5hSORKPr8Z@c(|1wzm@zUSKZZTjg;y+@51;up*7Sx1fj8F}T|uY`O~Y z-5A_HDTFVmU4!50cGj;*zBw_;4dvV$A$JYN>IphniAnG|;v~-@1fq9vZ6Ya5Xi%2o zEa^K>vuJB2nQb-SM~`00G_pLf9^-V4J6eEf2|(zVHI62HGfGjgeH%)tGIlTl%&*lL zEorgFsqi#J8}S=kzZSo+NQH2`4}ukj>DxT)F-e_ zawAhgl)zwB_q(WRVni%D8D7``aY4cG=X0rC-HjcLOw+S86N!^oId`XHHLybp1B5Mv z;H(zN+-9=A6Vd|FEPP?@83zfLR71U!lb^(crp69d730Gahl+tUsq&bhp4`f62OHM2 zs;X9Ad)D`qa@47YwIf)+nAEn?*qff*N@(6A-mio9l`2(Csuyb{j=^%+)?@p?jPLWQ zO5tXSG(Zo{0s78RkeX@(H&0z!N2a%*O=aq6i5^<+Qsnw422-lu`mN}VAR1VT)NVjL zbZzvXF>kgr83SX2H`GIWfca=r>U2wJnCe~_5wV02Fa(fd8&f=v6xw%|!4;!xHC?>k z5O}GyDz?4|m2o?}9Ci)Hv`X81&+K@woiSYs$FTzh*656>q3&3^mA=DOi4`Rfjv)?r zYb;c@Bdg(xt)GGLx}x$*`*!dI47phJ8iauQC#cUj^%={Lwik%uM%#DUH8}9_;f`;V zF%8jm_?>4(nYtnRBm7zdpdtE%iXhO2=#TLWyUfsUCpu`bJ;1p-nw%9n8}-uO0(b;o zCbXF4oCbnyfuQ|^>0Y49rk-a;pTtLXco5cmQ++)^nx15jZew5fz@rtX*^tC3Mev>J zKl|yox|!)OT8{KRkUmB3J+yKLoxn*+N6(i^&%Y#izzuy!N~R+(Qm1HWvW zpw<9Ds9c$lS6*TWb$Dgy*I@+$V-huqbHEsLwB1NI-M$3LX4O(?0qgSSk?;;a_XwNp zP_wJB|3Ez_zL|*qBj*Kg;wt4JYaM~J*KwRzLo?6|V7@8|^e}6&MA(4jFiL~-RvId^ zue=@;LA^`_kC8IYu#_W;H?H-nSFmZ1M`xg5q{873Org(pJ-Dmmx-f-fdNp3=LIaxz zFHCIIc{M-SpsU4Xw(=FUUQpQL?hMOJ8hheN5+>!Dc#v!Z!ax^EbF{UByxWAd=26<7M(5(RyllEFI2PSxkE4vQuT!c>P8-v0X!zNsey-iI1D&UGtIB{2%~Z@NaYSo67062$lag8 z&?Y^$0c>TK^K%@f)Mb*hT#N|&i-$OYQz5H+U8Z&!f)aC}V~6M~8U0pAbQZDH=PwW) zJC$M_Q@qfTqPt4rN!+90U(4u6JEHpl{uBh2+iP!;X)JLaUa3YwH!)pZM>>L;d|mO3 z#KkIl2BU8{Bl^b46NA_YmF8-?KA+8I+~oJx6#GSOjx(T^=^A9BFS3al_!^Nc*B{dFJ}7P zRbwDJ9MijY7a-gH#$k5FD2z@;1B3=>AqIL7hyqlgQYKGsWddy(qssu4MFgTe;=*Kw z7!<k7Mg~w0UeLT*=hNJpXB{Vu@9{Sgw6vh#1}7}5k&O7IF?$}mwiRj{1RzE z+BN#b6*u{_P;n${1{fx5#vD5k?%*w+?6^qykRMXYJcOM!i(xFVvBs0_9Kb}c?GcDo zfXEKimtpJ!TB%!TsVA`Wf3v+bunKQ903AHaS{keyRTQ*ORk*swr9oUYDZLSMBPx^j zKO^RhBRU5JodXcuvJvn$9{GQ{-Gl(vI1sr8*t>zxAaEDvSA}^Ww1uY|+qVTl-g&$J zoDRA$m(4)Y5T1}8=9$Z?faeGwx=U5U71)U9QAq9PJnAff9WpFt^X4dy5>m|r4 zG# z+CKn|%c`$txR@`!mvxeP?`<6G+hQGhnV0U@>YQ5XBp2YdW zo{keZmQl|=E?YuT{dBHCCD7;A$`<4zpJ6wH$Z^;X2P_Zq+)i4j2(4qradj`SQo)%G zI9QJLfpA6}#IUVQVvhx)oQU=G*y8lMB3zRw$B>r${pxktSWHJcux==Fmj$&8k+stP z3lgOkvrnVBlG`vsJ@)&!oQC`8NE!DHL4aqv{lXQBg49I6i?SqrPIB9WOumuH1-zTQ z%wDOIV_m=$`WVK>wyEqB!XDjR#cds=+G}p5-i2(w?Yh1N?%Aq=pxnlwn zgs+_y3QjSTei?1UMM-ihuAYSV!{muXij^rzzH!VbpF z4qX8`WNUplw27B3R5v?lj}G#VAUjSi*~4SU>F2|AAJPMVPoj@n@|j3B3%2tZ&Ve|I z;`wFa?YRaL>7jWrU+J#(p(LGJk-MFUmqd{W3`688z)ZUx7~1W{WK%e8?`XtnkT{^% z!cDILL!L0mqD2vVHA*#~;IhPBqa^003aqr&6RlYER{R#%^$3>RtAQ@AL&J~$D|k;I z!>bIV3R$Ox=LWTfY$5QNiE9*(!MyBo-t30bQ>e2-B;fK1ScVN$G}A#YT-Q&x-?>@H zrL{hn*mh8xM=!;>4ua0i#16T&N@|Rj>&I63p^HWDM!_P{OM$|`=YHP{BG1+@4DY1J z17ifQF}YGus1G9uyyFiDN)S6zao0Fiw&@$<4m-Reh8&P)NARn6H+J$137|B+^9S(8 zSp7kS1W}C)^8TRx2K=)mzLt{K7mx;B5Z?X_(b30;x3{k(h|8TX(0Y~HF}E_4*Ggr| z?Pa(uUQOA`3ZARDr&P`@$c=W*PmXUsir_%(1_qF`GnF;4iOM8Iz`6H*^GY<1^1Hu9{a+S`~=m_)GDh^IUagw^i(^Zvsk$t244^EXbeF4h5d~>9ctQ zfePaO(DdZyFyfNi_-i}MRIz%k(z#AFry)E{>CL7 z4!G#&-)e(9>IRv-6ik^Sn{Z_mFxC7=w`DL~O^=qra5eYEC7h7r5=vL9$1lLD(cRft z#UmkpvWiDTlfx<=Lw;X8)+`;n+UPCC^ViK5>XBUFW~)suyiozLif^oje9I~hw$^h4 z35^iA(%{1C=Xv~MOgBlw%%(oDb5z?z{EU@J`t|0pW$IT*-w?k_#8B{Z{KWLIg4>-Tl@%=xKMO zn z5f1Nc9Jk|{-S@2@lJE-NTH2>$pc@m`dGz$+#HZjSCXVgIbXbWspG0 zqA9Zg`T8868vo{)bpj~NcBu~{vo&#;Sw2wvey zYPG3naJMeA7gjPc0fa`G0Zvk3XB9|;Obzi-E1bC|?lX2sNul}4g=4ikx5j^bP}ri9 zVq>E5B+6nH6HxR8D61>T$rX@2bby?l-J0%AT$}=VUUK17E$V`dbOj0Sj3Yr7j9ZTQ zwHmoiklRidTa66Cy(c_aL*aGkQkb^jdud+#w=fV31DBu`y!@PHrNM!%+TPazU|Lq3 zt?a3$Vga55!JCQlP9iQ|m~GZ$N5S=Rc5Qzj5JX{4&#bUlBjX(0+35Ow2^o!9D`%)#@JdRM)7b&2kl=cFm>0!8)@Ynu8%?A{tZBF57f1$^ zk1)X8U2B}Y8dJ?rm=IUco5*~S>|y+B2c#q~NQNne=Z-eD1p@03*nn5Lb=C#i`@<4)d;>-`WmrEXlM zJwb(G?}rfTi&JnM(E6evHp&PSBm^Ut2#3M5jGdPt9BSVgkQ#EjNezPr z$LwvZ+2pw}6#F?*^J}*-jh$SWs@gDA?KTNZbcE#~-5nD4$5faeVlAGUoZ5)$qoi6f z1fYj%%~q*`RO>z_qB!e6S3!gRYi-q;RVc5}?(M`mO>l-nL87p3RJlDI@MPaUuPFa8 zrANKP6SBvJ@CsRW-Xu6ZiO+)CQ=QrOqK#znj^^vFwMayp{m)fwxx_$OxP)w1BmwgG z+qEG!Mjq^0($qs*=Pf-v#45TmU7loB_bub$x54-M7Y`qCT`BbsyxDB*@-Z;bzzNvc zwS@`w<;j5)h0w+4>JptMjrRzyZiK@f`2Zbw!~MkXTg-N`SAWN zIoqL2aYx?|v-juNf@SWn85QSWd1HuLhgM8K5p zH%l=%5`XQy;i?$Bx+%Ge4ONUIt&Wu=)TjoY>BUL6ei4-3KqooLF`E>B#)hdl$C$9m zllT;O{oF;5k^ub2w0Wttc`EH36Z@^R$mx~YjAX~-;c^UY9J?!C@&Su56N?eN@*MhkF822y6@jj_p@`@Yx3gTunu17uM$|SDIjcY>OM8=)Z zxGN=YiyOBEaThV}a>fmixJ_={CdBbZV^b;P{*41Qvt*+iw-Ir?RM1q;xQ`_6c{lEP z#2sSXEsXmeFkj( z{Y?EwuzQSw+f`;j;&A={f?<{%(er}InftI=*B)^Z{>4fxgi1cR7(kuHKoT0Zcol(T zy~d?{SUFkSRD*b9=~^_>hbW&b)=5EN1?fMI(_=NcIP1spntE>Xx-u(G$y3VhdGlS) z5bCj$rwdMlo4sVMHEr!qf{hpD;ukW(Nmb@VC=ib^ClY#5YDZx$T?<+Clx)cCke4UG ze=o#rY{tfZIYhg7(J##qBY?m@^4^GLf znFKru;=M1WFUVsuCm9nS@gz=|AF|*74ZP~EEw>l4z-SAWuXrr;KLTE={R9nKX}^y9 z_M9w5maNazo{`XP2vtGAS-)2Mm4t3U=$xAT!w4FwZIYl^DmyzF&sc4d&_zs$_Zeyj z95()vemxtGZE2R}PEr^AiLG8vOkbaHW)nz4tR?~x>N#2^Yhj0W@jxs=kIX|u*eAJu zSIv+Yh#Uh@9HVw=1{6hQ5FOJj?EBhN&I634YiH#jMm@|a0|Sv?AqxOte>RDndK5EX zf?RdjPJv&qvvX9I8mlq~6N~A4Lcg~dGf_8V!V{jv+iq5hZqLY?$E@!Tg!QUg>3*0& zVb-g=W(5T;=3?n$o@f6WX>0Hv1cbk2>wsHj)?}k?-`9I4eEEkH8PX%t`6xbPUjFd} z2JBj!2hp|h5-J5@h_8jkv1!LJP=L^R-ySN%O^Ra(M9q!;Ir;*lbnec z!Z(oel&V`Yv;jr9&mSgdw$%Bw*8vJDR-tuf$%FPnzyn612PMS59bTGq_iBeysqAuu z;T5-_0+pQk3a}Lm?-8D=RiuKq03UT0|0R(di^mol-~b){8NYz|mOwO5uo}r_EEca@ z)AI#9Sbf!{X@BS_^v?`?Ra9jT$>}^;q;WEHVI?FICm0aBF~7Y`JAw@MtK-xN3XQXt zHsG=Mpyg{$j^@vNpZ&na1kO#|)u_QyRbm*>;ZZ$r?8`~umC**vw}!daAeTy2BX~hG zqDy0xGG|GHHLZaNO|Wo92La{DnVV|QXteW@t|B>eBY?xwOsb8V*hZ~R%V`$>Afm03 zk2${aL@!9^`RZAzg!_{vPof{4E0EGydc+s%n+!dv9Yt-~U`v-voj^>?3$EuO^EH+} z3vMI?^>)?MTtFZe8|EL6hi$P8Sz@pw&Nz?b;`h6C9HRc#RkEYQqxUJjX0;%6_fL2l z8+%E8fZW+BhrJKYvx+CDnU@JACPrb!1#JpEe{Hr&hZRiAT6PZh^0571fOk{zfO3l4 zL#qp_j$JKb*nh{l@@z0?q5QowF&I99Z8;^|$dqxIsMLH?;7Fj`!7oD-H9tbj?f$7y zvcb>@ElWaCTb1?_hW694CG^uf5DMm44d(dj2=idd{M?FTYyqyW_XC|+Bc*I&B4v8e z?goPmm;~*o5Q8P2EQRstGujW4-NEcT-_5qSgQMDiD8f$*e!5i4{KWp8aRo>}+xZgx zq8e8fOpW%CztZlBBqFp3^{v!$fN_O=)dN`aU5Prt>RNq+A(Dr_LHnP8fI~j`?c;D( z z&ur&L0DuuPhCv*?2+6Dh4q+50@ zu+w{IOOL?LGOzbwY*QN+y2k1J<=tYNW`L6fc1}!FvFYA}&FRP#dxS|Y?Tt}m;=S4S zo`F?AWBEHL`c%SKo@*qI@BwX+022P7}>NQVYphri^R#$2bD0N13pa- zYJ+{ZX-^>HubC<#lXcACE@0UzvYbkXNSH<#p5-WdYB4n*d&-$e<1 z9$dyQHHAW^w$$$pJT6aK1)@Bb+I`Zm_s)OcIGG-3+n=d7k{pO3 zcV^?3%qf3h)e01l9Maf^_@)4U5{sMq@pDg89zS(Wqxtz>(>46u0#riNjZ-;`rXf#` zi9FXcd~#D2KZQ-V@pDzvz5I-7TEx$=CX1iJO+Vr1oTiQZ^lf^DpRA_+{A4s8!e@FA zk7uKn<=|@gXzwCS^PoY??b$h;=z`!5m9{5DW;D7E>?|3AmN?#6M^moFg^`RPbS7Y^ zEj8bznDG%*5fXXy8yvE85k2g<7@w1%KGuUPHo#>z(varvqduM!#GqB4j78GOH_RyR z3?##ab1@5JEsT>}c#W4a9bg_h%Xk?AEspYM@M{SSK^#ZLNM@~MGWAL#NI9cPL%Rj- zXSt-WwcltLi^jJpkk$%90t3Tr@I54`F4DW(zfwsi*((qNl?csK#6~HHuL{D%8a{R_ z%~Ym9KA_U~(dMHdsIdYE`y6oJ>RGKBtbkXcXszVYCoO=Uvpi#}-YWrpnRLCM{Q%fq zqObidKUwxtelqNOe1diuD?}^Croa%I=&Ql4jweut^Ciz~qb$gvY8f^zGpt4?)1GVh zWY?XQyw_)c1>|Jm7RuDNBU559CR+szYnWcidwljkB?-n2ruKI_ardK7ohKm8u%s8T zeoF>G4|!Y;?nD;1Ixq5w1*9P6{@#Lr+v-VEzMtjOyLbu+ipf&S*lMgAWsRCUkaNEcF!jA2>3s-)VbK%>(B?mcX=Z0AzIq;v$w zTT1l}LxM|(KqQJ^sjpiku1CdK7SwhzkY=$mHj=2G;_U_npQ02(uqRzkWFUbB20gTu zOwScjibAgduI!Cm?FB*T(0q#J%WVtUoaeAyzy(w|=OoV8UPpq&a<>^my|l0Czgrvj zAU>K~^{?38*cZYku~x>2o1x!;Fz(@nw)6{dP=pRL#|$y|+EGMdomXyWp$x9_a`>&V z&mtHXq!$!~>`=%$?U>;4KCM(D{tqJLc70#Ul+76kmL)DNWQ>yg#836p*T}T3dCsU z)QhZ@Oer1yJCv$`1g#a!*%2DlUhTx_7Gg93nVsK(VQIZ%Xt)fm3?CQeAA(F${FlJd z9LlbmlHsM5cJHcJ)`#dlf!wGzrFPzZE3A?T1P!d{H&y<)BpP(&P&>;OJ=9 zvg8jLA(b~}hQ!sdCsCZU3gcdZCNNuoaio#&`)kk(yE>85etJfVRhNtx>ISjSL?sQyg8cq^IZi*xa7xAuimRH$y_gUSxy z8)&$*8>x_Oi0k`!qK()d&Bu`CQZn)qKy{G2-##0I2N7x2m?C$uhKc!@oZ*q`CcbCa zgVE>Xh`F%-Xl9O{wev#cFRb@;&k6O|H31dfNg6axU#OOg2e3ZE`%KYSF;C<20^rf` ze%)fb+dsh;%=RdrdQ&TPZ5c8?dvouA0Gw8xh*s ze`Tl+H`OQ6;`nOMbhh{tpb-+{4h@BSLgPDo>>E(7OB(0=+9eH_7W-+hbV5kOoZ{4> z{1o;2Z7<4GrGqRdWz7Lu9_h?*=uVI&G+jLo4bo`ebjF+oWE28ec|fe0WuHKE67z@g z#l$Eq_DZey!yVKX0kBlTS#|@GseM3v#G@mb1b=mDcx(^ucyx#xVm}shds@#?nRs}i za-dRZCHyxNw`7psegab*=b<=fIM_$QF#S7%FgXu&!QsI?xC#V(AhrQXl3N*9X}^Jl zHC7|hf~>Y6J2aadcrhmGIk-EG&@>!8WpP%IueLT>--y`T%SbUS7go)(h#&8q0CP-jA?Zb0BYFnQTYB1~2dqmoc9ex~bA$1h=TlEDpwsyxMMb zxV99yK;IcL1c!o5DPO;=(jJRIbKnp(L9dI*dx;kkwfb^Bi9k$MFlb+nRMoRq>_}k^ zBQFvKY~4Iyf#a+wt7|lo*1XHk)*g{!BN(?;GMZ|fUm(4$(Kf>4TqR=31nSkcJU_W#_2gxmP z%J_yZBgXenJLC7=!SUS#rKNa--+mB%>n!ZugJk1=Z?ud!WbY5n40&~8*^YnwWu z<#m)MZE-G)ec3;7lO5?yb~6fPQ zoD1QpsSaw-F|J!sdxdCUW&is&)csYUbcWoe6RDYgZr+zdERY@Bk}Y^G^xM|~3+Z3j z7XfM&(5Lg;ThOvNQUhw;i5M)oB+~KxuRb=B0jnU1k@kf^0SnuWvw>p%75n_64VzcJ=Wg2v z6{Uuf(+OW|$!X8-PqShrr*VgV&+bqCsn2w+jb((gxNf4|tpiYkQc{h&fcE60Y1!KR z7I2>dgE@sN_wONTtof!IkNCtPpwDky^D4O_U*_K&lpmamMz|!knfq1@HJ)Q$HiyGv1~F$Ao(FM!S%w{ zXd4y=XluPsOq?3ex}kdVj|;GDX$kL;XuX$;bdPym6)#mk=y!q0qAjs1Zz$kCKC#sQ z#c#~lPP}E##&NnxTiq+-I0%MJ49k#SLciQ&JNlXT^l9G(m5)jl5@;#Yw(Sm1p7?MX?7A9Lzddf#7dm*wE8mf zkV%*EB=X}tHJVgid%5>Ia3;p8x)qF

      A8FHA_CglVeLi06*c{^9GzyKeP&t)!iCO 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 z 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 oaT{EZKxf^K|61w_~Seb2pB)zwLK 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 literal 0 HcmV?d00001 diff --git a/production/3rdparty/curl/lib/libcurl.dll.a b/production/3rdparty/curl/lib/libcurl.dll.a new file mode 100644 index 0000000000000000000000000000000000000000..b9102b4895e97fb595855c415f46c608708de10f GIT binary patch literal 29366 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 P5*wt8| literal 0 HcmV?d00001 diff --git a/production/3rdparty/openssl/include/openssl/aes.h b/production/3rdparty/openssl/include/openssl/aes.h new file mode 100644 index 00000000..9ffcc9ff --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/aes.h @@ -0,0 +1,127 @@ +/* crypto/aes/aes.h -*- mode:C; c-file-style: "eay" -*- */ +/* ==================================================================== + * Copyright (c) 1998-2002 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 HEADER_AES_H +#define HEADER_AES_H + +#include + +#ifdef OPENSSL_NO_AES +#error AES is disabled. +#endif + +#define AES_ENCRYPT 1 +#define AES_DECRYPT 0 + +/* Because array size can't be a const in C, the following two are macros. + Both sizes are in bytes. */ +#define AES_MAXNR 14 +#define AES_BLOCK_SIZE 16 + +#ifdef __cplusplus +extern "C" { +#endif + +/* This should be a hidden type, but EVP requires that the size be known */ +struct aes_key_st { +#ifdef AES_LONG + unsigned long rd_key[4 *(AES_MAXNR + 1)]; +#else + unsigned int rd_key[4 *(AES_MAXNR + 1)]; +#endif + int rounds; +}; +typedef struct aes_key_st AES_KEY; + +const char *AES_options(void); + +int AES_set_encrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key); +int AES_set_decrypt_key(const unsigned char *userKey, const int bits, + AES_KEY *key); + +void AES_encrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key); +void AES_decrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key); + +void AES_ecb_encrypt(const unsigned char *in, unsigned char *out, + const AES_KEY *key, const int enc); +void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, + const unsigned long length, const AES_KEY *key, + unsigned char *ivec, const int enc); +void AES_cfb128_encrypt(const unsigned char *in, unsigned char *out, + const unsigned long length, const AES_KEY *key, + unsigned char *ivec, int *num, const int enc); +void AES_cfb1_encrypt(const unsigned char *in, unsigned char *out, + const unsigned long length, const AES_KEY *key, + unsigned char *ivec, int *num, const int enc); +void AES_cfb8_encrypt(const unsigned char *in, unsigned char *out, + const unsigned long length, const AES_KEY *key, + unsigned char *ivec, int *num, const int enc); +void AES_cfbr_encrypt_block(const unsigned char *in,unsigned char *out, + const int nbits,const AES_KEY *key, + unsigned char *ivec,const int enc); +void AES_ofb128_encrypt(const unsigned char *in, unsigned char *out, + const unsigned long length, const AES_KEY *key, + unsigned char *ivec, int *num); +void AES_ctr128_encrypt(const unsigned char *in, unsigned char *out, + const unsigned long length, const AES_KEY *key, + unsigned char ivec[AES_BLOCK_SIZE], + unsigned char ecount_buf[AES_BLOCK_SIZE], + unsigned int *num); + + +#ifdef __cplusplus +} +#endif + +#endif /* !HEADER_AES_H */ diff --git a/production/3rdparty/openssl/include/openssl/applink.c b/production/3rdparty/openssl/include/openssl/applink.c new file mode 100644 index 00000000..54a0a642 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/applink.c @@ -0,0 +1,94 @@ +#define APPLINK_STDIN 1 +#define APPLINK_STDOUT 2 +#define APPLINK_STDERR 3 +#define APPLINK_FPRINTF 4 +#define APPLINK_FGETS 5 +#define APPLINK_FREAD 6 +#define APPLINK_FWRITE 7 +#define APPLINK_FSETMOD 8 +#define APPLINK_FEOF 9 +#define APPLINK_FCLOSE 10 /* should not be used */ + +#define APPLINK_FOPEN 11 /* solely for completeness */ +#define APPLINK_FSEEK 12 +#define APPLINK_FTELL 13 +#define APPLINK_FFLUSH 14 +#define APPLINK_FERROR 15 +#define APPLINK_CLEARERR 16 +#define APPLINK_FILENO 17 /* to be used with below */ + +#define APPLINK_OPEN 18 /* formally can't be used, as flags can vary */ +#define APPLINK_READ 19 +#define APPLINK_WRITE 20 +#define APPLINK_LSEEK 21 +#define APPLINK_CLOSE 22 +#define APPLINK_MAX 22 /* always same as last macro */ + +#ifndef APPMACROS_ONLY +#include +#include +#include + +static void *app_stdin(void) { return stdin; } +static void *app_stdout(void) { return stdout; } +static void *app_stderr(void) { return stderr; } +static int app_feof(FILE *fp) { return feof(fp); } +static int app_ferror(FILE *fp) { return ferror(fp); } +static void app_clearerr(FILE *fp) { clearerr(fp); } +static int app_fileno(FILE *fp) { return _fileno(fp); } +static int app_fsetmod(FILE *fp,char mod) +{ return _setmode (_fileno(fp),mod=='b'?_O_BINARY:_O_TEXT); } + +#ifdef __cplusplus +extern "C" { +#endif + +__declspec(dllexport) +void ** +#if defined(__BORLANDC__) +__stdcall /* __stdcall appears to be the only way to get the name + * decoration right with Borland C. Otherwise it works + * purely incidentally, as we pass no parameters. */ +#else +__cdecl +#endif +OPENSSL_Applink(void) +{ static int once=1; + static void *OPENSSL_ApplinkTable[APPLINK_MAX+1]={(void *)APPLINK_MAX}; + + if (once) + { OPENSSL_ApplinkTable[APPLINK_STDIN] = app_stdin; + OPENSSL_ApplinkTable[APPLINK_STDOUT] = app_stdout; + OPENSSL_ApplinkTable[APPLINK_STDERR] = app_stderr; + OPENSSL_ApplinkTable[APPLINK_FPRINTF] = fprintf; + OPENSSL_ApplinkTable[APPLINK_FGETS] = fgets; + OPENSSL_ApplinkTable[APPLINK_FREAD] = fread; + OPENSSL_ApplinkTable[APPLINK_FWRITE] = fwrite; + OPENSSL_ApplinkTable[APPLINK_FSETMOD] = app_fsetmod; + OPENSSL_ApplinkTable[APPLINK_FEOF] = app_feof; + OPENSSL_ApplinkTable[APPLINK_FCLOSE] = fclose; + + OPENSSL_ApplinkTable[APPLINK_FOPEN] = fopen; + OPENSSL_ApplinkTable[APPLINK_FSEEK] = fseek; + OPENSSL_ApplinkTable[APPLINK_FTELL] = ftell; + OPENSSL_ApplinkTable[APPLINK_FFLUSH] = fflush; + OPENSSL_ApplinkTable[APPLINK_FERROR] = app_ferror; + OPENSSL_ApplinkTable[APPLINK_CLEARERR] = app_clearerr; + OPENSSL_ApplinkTable[APPLINK_FILENO] = app_fileno; + + OPENSSL_ApplinkTable[APPLINK_OPEN] = _open; + OPENSSL_ApplinkTable[APPLINK_READ] = _read; + OPENSSL_ApplinkTable[APPLINK_WRITE] = _write; + OPENSSL_ApplinkTable[APPLINK_LSEEK] = _lseek; + OPENSSL_ApplinkTable[APPLINK_CLOSE] = _close; + + once = 0; + } + + return OPENSSL_ApplinkTable; +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/asn1.h b/production/3rdparty/openssl/include/openssl/asn1.h new file mode 100644 index 00000000..30f1eecd --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/asn1.h @@ -0,0 +1,1233 @@ +/* crypto/asn1/asn1.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_ASN1_H +#define HEADER_ASN1_H + +#include +#include +#ifndef OPENSSL_NO_BIO +#include +#endif +#include +#include + +#include + +#include +#ifndef OPENSSL_NO_DEPRECATED +#include +#endif + +#ifdef OPENSSL_BUILD_SHLIBCRYPTO +# undef OPENSSL_EXTERN +# define OPENSSL_EXTERN OPENSSL_EXPORT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define V_ASN1_UNIVERSAL 0x00 +#define V_ASN1_APPLICATION 0x40 +#define V_ASN1_CONTEXT_SPECIFIC 0x80 +#define V_ASN1_PRIVATE 0xc0 + +#define V_ASN1_CONSTRUCTED 0x20 +#define V_ASN1_PRIMITIVE_TAG 0x1f +#define V_ASN1_PRIMATIVE_TAG 0x1f + +#define V_ASN1_APP_CHOOSE -2 /* let the recipient choose */ +#define V_ASN1_OTHER -3 /* used in ASN1_TYPE */ +#define V_ASN1_ANY -4 /* used in ASN1 template code */ + +#define V_ASN1_NEG 0x100 /* negative flag */ + +#define V_ASN1_UNDEF -1 +#define V_ASN1_EOC 0 +#define V_ASN1_BOOLEAN 1 /**/ +#define V_ASN1_INTEGER 2 +#define V_ASN1_NEG_INTEGER (2 | V_ASN1_NEG) +#define V_ASN1_BIT_STRING 3 +#define V_ASN1_OCTET_STRING 4 +#define V_ASN1_NULL 5 +#define V_ASN1_OBJECT 6 +#define V_ASN1_OBJECT_DESCRIPTOR 7 +#define V_ASN1_EXTERNAL 8 +#define V_ASN1_REAL 9 +#define V_ASN1_ENUMERATED 10 +#define V_ASN1_NEG_ENUMERATED (10 | V_ASN1_NEG) +#define V_ASN1_UTF8STRING 12 +#define V_ASN1_SEQUENCE 16 +#define V_ASN1_SET 17 +#define V_ASN1_NUMERICSTRING 18 /**/ +#define V_ASN1_PRINTABLESTRING 19 +#define V_ASN1_T61STRING 20 +#define V_ASN1_TELETEXSTRING 20 /* alias */ +#define V_ASN1_VIDEOTEXSTRING 21 /**/ +#define V_ASN1_IA5STRING 22 +#define V_ASN1_UTCTIME 23 +#define V_ASN1_GENERALIZEDTIME 24 /**/ +#define V_ASN1_GRAPHICSTRING 25 /**/ +#define V_ASN1_ISO64STRING 26 /**/ +#define V_ASN1_VISIBLESTRING 26 /* alias */ +#define V_ASN1_GENERALSTRING 27 /**/ +#define V_ASN1_UNIVERSALSTRING 28 /**/ +#define V_ASN1_BMPSTRING 30 + +/* For use with d2i_ASN1_type_bytes() */ +#define B_ASN1_NUMERICSTRING 0x0001 +#define B_ASN1_PRINTABLESTRING 0x0002 +#define B_ASN1_T61STRING 0x0004 +#define B_ASN1_TELETEXSTRING 0x0004 +#define B_ASN1_VIDEOTEXSTRING 0x0008 +#define B_ASN1_IA5STRING 0x0010 +#define B_ASN1_GRAPHICSTRING 0x0020 +#define B_ASN1_ISO64STRING 0x0040 +#define B_ASN1_VISIBLESTRING 0x0040 +#define B_ASN1_GENERALSTRING 0x0080 +#define B_ASN1_UNIVERSALSTRING 0x0100 +#define B_ASN1_OCTET_STRING 0x0200 +#define B_ASN1_BIT_STRING 0x0400 +#define B_ASN1_BMPSTRING 0x0800 +#define B_ASN1_UNKNOWN 0x1000 +#define B_ASN1_UTF8STRING 0x2000 +#define B_ASN1_UTCTIME 0x4000 +#define B_ASN1_GENERALIZEDTIME 0x8000 +#define B_ASN1_SEQUENCE 0x10000 + +/* For use with ASN1_mbstring_copy() */ +#define MBSTRING_FLAG 0x1000 +#define MBSTRING_UTF8 (MBSTRING_FLAG) +#define MBSTRING_ASC (MBSTRING_FLAG|1) +#define MBSTRING_BMP (MBSTRING_FLAG|2) +#define MBSTRING_UNIV (MBSTRING_FLAG|4) + +struct X509_algor_st; + +#define DECLARE_ASN1_SET_OF(type) /* filled in by mkstack.pl */ +#define IMPLEMENT_ASN1_SET_OF(type) /* nothing, no longer needed */ + +/* We MUST make sure that, except for constness, asn1_ctx_st and + asn1_const_ctx are exactly the same. Fortunately, as soon as + the old ASN1 parsing macros are gone, we can throw this away + as well... */ +typedef struct asn1_ctx_st + { + unsigned char *p;/* work char pointer */ + int eos; /* end of sequence read for indefinite encoding */ + int error; /* error code to use when returning an error */ + int inf; /* constructed if 0x20, indefinite is 0x21 */ + int tag; /* tag from last 'get object' */ + int xclass; /* class from last 'get object' */ + long slen; /* length of last 'get object' */ + unsigned char *max; /* largest value of p allowed */ + unsigned char *q;/* temporary variable */ + unsigned char **pp;/* variable */ + int line; /* used in error processing */ + } ASN1_CTX; + +typedef struct asn1_const_ctx_st + { + const unsigned char *p;/* work char pointer */ + int eos; /* end of sequence read for indefinite encoding */ + int error; /* error code to use when returning an error */ + int inf; /* constructed if 0x20, indefinite is 0x21 */ + int tag; /* tag from last 'get object' */ + int xclass; /* class from last 'get object' */ + long slen; /* length of last 'get object' */ + const unsigned char *max; /* largest value of p allowed */ + const unsigned char *q;/* temporary variable */ + const unsigned char **pp;/* variable */ + int line; /* used in error processing */ + } ASN1_const_CTX; + +/* These are used internally in the ASN1_OBJECT to keep track of + * whether the names and data need to be free()ed */ +#define ASN1_OBJECT_FLAG_DYNAMIC 0x01 /* internal use */ +#define ASN1_OBJECT_FLAG_CRITICAL 0x02 /* critical x509v3 object id */ +#define ASN1_OBJECT_FLAG_DYNAMIC_STRINGS 0x04 /* internal use */ +#define ASN1_OBJECT_FLAG_DYNAMIC_DATA 0x08 /* internal use */ +typedef struct asn1_object_st + { + const char *sn,*ln; + int nid; + int length; + unsigned char *data; + int flags; /* Should we free this one */ + } ASN1_OBJECT; + +#define ASN1_STRING_FLAG_BITS_LEFT 0x08 /* Set if 0x07 has bits left value */ +/* This indicates that the ASN1_STRING is not a real value but just a place + * holder for the location where indefinite length constructed data should + * be inserted in the memory buffer + */ +#define ASN1_STRING_FLAG_NDEF 0x010 +/* This is the base type that holds just about everything :-) */ +typedef struct asn1_string_st + { + int length; + int type; + unsigned char *data; + /* The value of the following field depends on the type being + * held. It is mostly being used for BIT_STRING so if the + * input data has a non-zero 'unused bits' value, it will be + * handled correctly */ + long flags; + } ASN1_STRING; + +/* ASN1_ENCODING structure: this is used to save the received + * encoding of an ASN1 type. This is useful to get round + * problems with invalid encodings which can break signatures. + */ + +typedef struct ASN1_ENCODING_st + { + unsigned char *enc; /* DER encoding */ + long len; /* Length of encoding */ + int modified; /* set to 1 if 'enc' is invalid */ + } ASN1_ENCODING; + +/* Used with ASN1 LONG type: if a long is set to this it is omitted */ +#define ASN1_LONG_UNDEF 0x7fffffffL + +#define STABLE_FLAGS_MALLOC 0x01 +#define STABLE_NO_MASK 0x02 +#define DIRSTRING_TYPE \ + (B_ASN1_PRINTABLESTRING|B_ASN1_T61STRING|B_ASN1_BMPSTRING|B_ASN1_UTF8STRING) +#define PKCS9STRING_TYPE (DIRSTRING_TYPE|B_ASN1_IA5STRING) + +typedef struct asn1_string_table_st { + int nid; + long minsize; + long maxsize; + unsigned long mask; + unsigned long flags; +} ASN1_STRING_TABLE; + +DECLARE_STACK_OF(ASN1_STRING_TABLE) + +/* size limits: this stuff is taken straight from RFC2459 */ + +#define ub_name 32768 +#define ub_common_name 64 +#define ub_locality_name 128 +#define ub_state_name 128 +#define ub_organization_name 64 +#define ub_organization_unit_name 64 +#define ub_title 64 +#define ub_email_address 128 + +/* Declarations for template structures: for full definitions + * see asn1t.h + */ +typedef struct ASN1_TEMPLATE_st ASN1_TEMPLATE; +typedef struct ASN1_ITEM_st ASN1_ITEM; +typedef struct ASN1_TLC_st ASN1_TLC; +/* This is just an opaque pointer */ +typedef struct ASN1_VALUE_st ASN1_VALUE; + +/* Declare ASN1 functions: the implement macro in in asn1t.h */ + +#define DECLARE_ASN1_FUNCTIONS(type) DECLARE_ASN1_FUNCTIONS_name(type, type) + +#define DECLARE_ASN1_ALLOC_FUNCTIONS(type) \ + DECLARE_ASN1_ALLOC_FUNCTIONS_name(type, type) + +#define DECLARE_ASN1_FUNCTIONS_name(type, name) \ + DECLARE_ASN1_ALLOC_FUNCTIONS_name(type, name) \ + DECLARE_ASN1_ENCODE_FUNCTIONS(type, name, name) + +#define DECLARE_ASN1_FUNCTIONS_fname(type, itname, name) \ + DECLARE_ASN1_ALLOC_FUNCTIONS_name(type, name) \ + DECLARE_ASN1_ENCODE_FUNCTIONS(type, itname, name) + +#define DECLARE_ASN1_ENCODE_FUNCTIONS(type, itname, name) \ + type *d2i_##name(type **a, const unsigned char **in, long len); \ + int i2d_##name(type *a, unsigned char **out); \ + DECLARE_ASN1_ITEM(itname) + +#define DECLARE_ASN1_ENCODE_FUNCTIONS_const(type, name) \ + type *d2i_##name(type **a, const unsigned char **in, long len); \ + int i2d_##name(const type *a, unsigned char **out); \ + DECLARE_ASN1_ITEM(name) + +#define DECLARE_ASN1_NDEF_FUNCTION(name) \ + int i2d_##name##_NDEF(name *a, unsigned char **out); + +#define DECLARE_ASN1_FUNCTIONS_const(name) \ + name *name##_new(void); \ + void name##_free(name *a); + +#define DECLARE_ASN1_ALLOC_FUNCTIONS_name(type, name) \ + type *name##_new(void); \ + void name##_free(type *a); + +#define D2I_OF(type) type *(*)(type **,const unsigned char **,long) +#define I2D_OF(type) int (*)(type *,unsigned char **) +#define I2D_OF_const(type) int (*)(const type *,unsigned char **) + +#define TYPEDEF_D2I_OF(type) typedef type *d2i_of_##type(type **,const unsigned char **,long) +#define TYPEDEF_I2D_OF(type) typedef int i2d_of_##type(type *,unsigned char **) +#define TYPEDEF_D2I2D_OF(type) TYPEDEF_D2I_OF(type); TYPEDEF_I2D_OF(type) + +TYPEDEF_D2I2D_OF(void); + +/* The following macros and typedefs allow an ASN1_ITEM + * to be embedded in a structure and referenced. Since + * the ASN1_ITEM pointers need to be globally accessible + * (possibly from shared libraries) they may exist in + * different forms. On platforms that support it the + * ASN1_ITEM structure itself will be globally exported. + * Other platforms will export a function that returns + * an ASN1_ITEM pointer. + * + * To handle both cases transparently the macros below + * should be used instead of hard coding an ASN1_ITEM + * pointer in a structure. + * + * The structure will look like this: + * + * typedef struct SOMETHING_st { + * ... + * ASN1_ITEM_EXP *iptr; + * ... + * } SOMETHING; + * + * It would be initialised as e.g.: + * + * SOMETHING somevar = {...,ASN1_ITEM_ref(X509),...}; + * + * and the actual pointer extracted with: + * + * const ASN1_ITEM *it = ASN1_ITEM_ptr(somevar.iptr); + * + * Finally an ASN1_ITEM pointer can be extracted from an + * appropriate reference with: ASN1_ITEM_rptr(X509). This + * would be used when a function takes an ASN1_ITEM * argument. + * + */ + +#ifndef OPENSSL_EXPORT_VAR_AS_FUNCTION + +/* ASN1_ITEM pointer exported type */ +typedef const ASN1_ITEM ASN1_ITEM_EXP; + +/* Macro to obtain ASN1_ITEM pointer from exported type */ +#define ASN1_ITEM_ptr(iptr) (iptr) + +/* Macro to include ASN1_ITEM pointer from base type */ +#define ASN1_ITEM_ref(iptr) (&(iptr##_it)) + +#define ASN1_ITEM_rptr(ref) (&(ref##_it)) + +#define DECLARE_ASN1_ITEM(name) \ + OPENSSL_EXTERN const ASN1_ITEM name##_it; + +#else + +/* Platforms that can't easily handle shared global variables are declared + * as functions returning ASN1_ITEM pointers. + */ + +/* ASN1_ITEM pointer exported type */ +typedef const ASN1_ITEM * ASN1_ITEM_EXP(void); + +/* Macro to obtain ASN1_ITEM pointer from exported type */ +#define ASN1_ITEM_ptr(iptr) (iptr()) + +/* Macro to include ASN1_ITEM pointer from base type */ +#define ASN1_ITEM_ref(iptr) (iptr##_it) + +#define ASN1_ITEM_rptr(ref) (ref##_it()) + +#define DECLARE_ASN1_ITEM(name) \ + const ASN1_ITEM * name##_it(void); + +#endif + +/* Parameters used by ASN1_STRING_print_ex() */ + +/* These determine which characters to escape: + * RFC2253 special characters, control characters and + * MSB set characters + */ + +#define ASN1_STRFLGS_ESC_2253 1 +#define ASN1_STRFLGS_ESC_CTRL 2 +#define ASN1_STRFLGS_ESC_MSB 4 + + +/* This flag determines how we do escaping: normally + * RC2253 backslash only, set this to use backslash and + * quote. + */ + +#define ASN1_STRFLGS_ESC_QUOTE 8 + + +/* These three flags are internal use only. */ + +/* Character is a valid PrintableString character */ +#define CHARTYPE_PRINTABLESTRING 0x10 +/* Character needs escaping if it is the first character */ +#define CHARTYPE_FIRST_ESC_2253 0x20 +/* Character needs escaping if it is the last character */ +#define CHARTYPE_LAST_ESC_2253 0x40 + +/* NB the internal flags are safely reused below by flags + * handled at the top level. + */ + +/* If this is set we convert all character strings + * to UTF8 first + */ + +#define ASN1_STRFLGS_UTF8_CONVERT 0x10 + +/* If this is set we don't attempt to interpret content: + * just assume all strings are 1 byte per character. This + * will produce some pretty odd looking output! + */ + +#define ASN1_STRFLGS_IGNORE_TYPE 0x20 + +/* If this is set we include the string type in the output */ +#define ASN1_STRFLGS_SHOW_TYPE 0x40 + +/* This determines which strings to display and which to + * 'dump' (hex dump of content octets or DER encoding). We can + * only dump non character strings or everything. If we + * don't dump 'unknown' they are interpreted as character + * strings with 1 octet per character and are subject to + * the usual escaping options. + */ + +#define ASN1_STRFLGS_DUMP_ALL 0x80 +#define ASN1_STRFLGS_DUMP_UNKNOWN 0x100 + +/* These determine what 'dumping' does, we can dump the + * content octets or the DER encoding: both use the + * RFC2253 #XXXXX notation. + */ + +#define ASN1_STRFLGS_DUMP_DER 0x200 + +/* All the string flags consistent with RFC2253, + * escaping control characters isn't essential in + * RFC2253 but it is advisable anyway. + */ + +#define ASN1_STRFLGS_RFC2253 (ASN1_STRFLGS_ESC_2253 | \ + ASN1_STRFLGS_ESC_CTRL | \ + ASN1_STRFLGS_ESC_MSB | \ + ASN1_STRFLGS_UTF8_CONVERT | \ + ASN1_STRFLGS_DUMP_UNKNOWN | \ + ASN1_STRFLGS_DUMP_DER) + +DECLARE_STACK_OF(ASN1_INTEGER) +DECLARE_ASN1_SET_OF(ASN1_INTEGER) + +DECLARE_STACK_OF(ASN1_GENERALSTRING) + +typedef struct asn1_type_st + { + int type; + union { + char *ptr; + ASN1_BOOLEAN boolean; + ASN1_STRING * asn1_string; + ASN1_OBJECT * object; + ASN1_INTEGER * integer; + ASN1_ENUMERATED * enumerated; + ASN1_BIT_STRING * bit_string; + ASN1_OCTET_STRING * octet_string; + ASN1_PRINTABLESTRING * printablestring; + ASN1_T61STRING * t61string; + ASN1_IA5STRING * ia5string; + ASN1_GENERALSTRING * generalstring; + ASN1_BMPSTRING * bmpstring; + ASN1_UNIVERSALSTRING * universalstring; + ASN1_UTCTIME * utctime; + ASN1_GENERALIZEDTIME * generalizedtime; + ASN1_VISIBLESTRING * visiblestring; + ASN1_UTF8STRING * utf8string; + /* set and sequence are left complete and still + * contain the set or sequence bytes */ + ASN1_STRING * set; + ASN1_STRING * sequence; + } value; + } ASN1_TYPE; + +DECLARE_STACK_OF(ASN1_TYPE) +DECLARE_ASN1_SET_OF(ASN1_TYPE) + +typedef struct asn1_method_st + { + i2d_of_void *i2d; + d2i_of_void *d2i; + void *(*create)(void); + void (*destroy)(void *); + } ASN1_METHOD; + +/* This is used when parsing some Netscape objects */ +typedef struct asn1_header_st + { + ASN1_OCTET_STRING *header; + void *data; + ASN1_METHOD *meth; + } ASN1_HEADER; + +/* This is used to contain a list of bit names */ +typedef struct BIT_STRING_BITNAME_st { + int bitnum; + const char *lname; + const char *sname; +} BIT_STRING_BITNAME; + + +#define M_ASN1_STRING_length(x) ((x)->length) +#define M_ASN1_STRING_length_set(x, n) ((x)->length = (n)) +#define M_ASN1_STRING_type(x) ((x)->type) +#define M_ASN1_STRING_data(x) ((x)->data) + +/* Macros for string operations */ +#define M_ASN1_BIT_STRING_new() (ASN1_BIT_STRING *)\ + ASN1_STRING_type_new(V_ASN1_BIT_STRING) +#define M_ASN1_BIT_STRING_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_ASN1_BIT_STRING_dup(a) (ASN1_BIT_STRING *)\ + ASN1_STRING_dup((ASN1_STRING *)a) +#define M_ASN1_BIT_STRING_cmp(a,b) ASN1_STRING_cmp(\ + (ASN1_STRING *)a,(ASN1_STRING *)b) +#define M_ASN1_BIT_STRING_set(a,b,c) ASN1_STRING_set((ASN1_STRING *)a,b,c) + +#define M_ASN1_INTEGER_new() (ASN1_INTEGER *)\ + ASN1_STRING_type_new(V_ASN1_INTEGER) +#define M_ASN1_INTEGER_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_ASN1_INTEGER_dup(a) (ASN1_INTEGER *)ASN1_STRING_dup((ASN1_STRING *)a) +#define M_ASN1_INTEGER_cmp(a,b) ASN1_STRING_cmp(\ + (ASN1_STRING *)a,(ASN1_STRING *)b) + +#define M_ASN1_ENUMERATED_new() (ASN1_ENUMERATED *)\ + ASN1_STRING_type_new(V_ASN1_ENUMERATED) +#define M_ASN1_ENUMERATED_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_ASN1_ENUMERATED_dup(a) (ASN1_ENUMERATED *)ASN1_STRING_dup((ASN1_STRING *)a) +#define M_ASN1_ENUMERATED_cmp(a,b) ASN1_STRING_cmp(\ + (ASN1_STRING *)a,(ASN1_STRING *)b) + +#define M_ASN1_OCTET_STRING_new() (ASN1_OCTET_STRING *)\ + ASN1_STRING_type_new(V_ASN1_OCTET_STRING) +#define M_ASN1_OCTET_STRING_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_ASN1_OCTET_STRING_dup(a) (ASN1_OCTET_STRING *)\ + ASN1_STRING_dup((ASN1_STRING *)a) +#define M_ASN1_OCTET_STRING_cmp(a,b) ASN1_STRING_cmp(\ + (ASN1_STRING *)a,(ASN1_STRING *)b) +#define M_ASN1_OCTET_STRING_set(a,b,c) ASN1_STRING_set((ASN1_STRING *)a,b,c) +#define M_ASN1_OCTET_STRING_print(a,b) ASN1_STRING_print(a,(ASN1_STRING *)b) +#define M_i2d_ASN1_OCTET_STRING(a,pp) \ + i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_OCTET_STRING,\ + V_ASN1_UNIVERSAL) + +#define B_ASN1_TIME \ + B_ASN1_UTCTIME | \ + B_ASN1_GENERALIZEDTIME + +#define B_ASN1_PRINTABLE \ + B_ASN1_PRINTABLESTRING| \ + B_ASN1_T61STRING| \ + B_ASN1_IA5STRING| \ + B_ASN1_BIT_STRING| \ + B_ASN1_UNIVERSALSTRING|\ + B_ASN1_BMPSTRING|\ + B_ASN1_UTF8STRING|\ + B_ASN1_SEQUENCE|\ + B_ASN1_UNKNOWN + +#define B_ASN1_DIRECTORYSTRING \ + B_ASN1_PRINTABLESTRING| \ + B_ASN1_TELETEXSTRING|\ + B_ASN1_BMPSTRING|\ + B_ASN1_UNIVERSALSTRING|\ + B_ASN1_UTF8STRING + +#define B_ASN1_DISPLAYTEXT \ + B_ASN1_IA5STRING| \ + B_ASN1_VISIBLESTRING| \ + B_ASN1_BMPSTRING|\ + B_ASN1_UTF8STRING + +#define M_ASN1_PRINTABLE_new() ASN1_STRING_type_new(V_ASN1_T61STRING) +#define M_ASN1_PRINTABLE_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_i2d_ASN1_PRINTABLE(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,\ + pp,a->type,V_ASN1_UNIVERSAL) +#define M_d2i_ASN1_PRINTABLE(a,pp,l) \ + d2i_ASN1_type_bytes((ASN1_STRING **)a,pp,l, \ + B_ASN1_PRINTABLE) + +#define M_DIRECTORYSTRING_new() ASN1_STRING_type_new(V_ASN1_PRINTABLESTRING) +#define M_DIRECTORYSTRING_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_i2d_DIRECTORYSTRING(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,\ + pp,a->type,V_ASN1_UNIVERSAL) +#define M_d2i_DIRECTORYSTRING(a,pp,l) \ + d2i_ASN1_type_bytes((ASN1_STRING **)a,pp,l, \ + B_ASN1_DIRECTORYSTRING) + +#define M_DISPLAYTEXT_new() ASN1_STRING_type_new(V_ASN1_VISIBLESTRING) +#define M_DISPLAYTEXT_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_i2d_DISPLAYTEXT(a,pp) i2d_ASN1_bytes((ASN1_STRING *)a,\ + pp,a->type,V_ASN1_UNIVERSAL) +#define M_d2i_DISPLAYTEXT(a,pp,l) \ + d2i_ASN1_type_bytes((ASN1_STRING **)a,pp,l, \ + B_ASN1_DISPLAYTEXT) + +#define M_ASN1_PRINTABLESTRING_new() (ASN1_PRINTABLESTRING *)\ + ASN1_STRING_type_new(V_ASN1_PRINTABLESTRING) +#define M_ASN1_PRINTABLESTRING_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_i2d_ASN1_PRINTABLESTRING(a,pp) \ + i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_PRINTABLESTRING,\ + V_ASN1_UNIVERSAL) +#define M_d2i_ASN1_PRINTABLESTRING(a,pp,l) \ + (ASN1_PRINTABLESTRING *)d2i_ASN1_type_bytes\ + ((ASN1_STRING **)a,pp,l,B_ASN1_PRINTABLESTRING) + +#define M_ASN1_T61STRING_new() (ASN1_T61STRING *)\ + ASN1_STRING_type_new(V_ASN1_T61STRING) +#define M_ASN1_T61STRING_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_i2d_ASN1_T61STRING(a,pp) \ + i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_T61STRING,\ + V_ASN1_UNIVERSAL) +#define M_d2i_ASN1_T61STRING(a,pp,l) \ + (ASN1_T61STRING *)d2i_ASN1_type_bytes\ + ((ASN1_STRING **)a,pp,l,B_ASN1_T61STRING) + +#define M_ASN1_IA5STRING_new() (ASN1_IA5STRING *)\ + ASN1_STRING_type_new(V_ASN1_IA5STRING) +#define M_ASN1_IA5STRING_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_ASN1_IA5STRING_dup(a) \ + (ASN1_IA5STRING *)ASN1_STRING_dup((ASN1_STRING *)a) +#define M_i2d_ASN1_IA5STRING(a,pp) \ + i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_IA5STRING,\ + V_ASN1_UNIVERSAL) +#define M_d2i_ASN1_IA5STRING(a,pp,l) \ + (ASN1_IA5STRING *)d2i_ASN1_type_bytes((ASN1_STRING **)a,pp,l,\ + B_ASN1_IA5STRING) + +#define M_ASN1_UTCTIME_new() (ASN1_UTCTIME *)\ + ASN1_STRING_type_new(V_ASN1_UTCTIME) +#define M_ASN1_UTCTIME_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_ASN1_UTCTIME_dup(a) (ASN1_UTCTIME *)ASN1_STRING_dup((ASN1_STRING *)a) + +#define M_ASN1_GENERALIZEDTIME_new() (ASN1_GENERALIZEDTIME *)\ + ASN1_STRING_type_new(V_ASN1_GENERALIZEDTIME) +#define M_ASN1_GENERALIZEDTIME_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_ASN1_GENERALIZEDTIME_dup(a) (ASN1_GENERALIZEDTIME *)ASN1_STRING_dup(\ + (ASN1_STRING *)a) + +#define M_ASN1_TIME_new() (ASN1_TIME *)\ + ASN1_STRING_type_new(V_ASN1_UTCTIME) +#define M_ASN1_TIME_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_ASN1_TIME_dup(a) (ASN1_TIME *)ASN1_STRING_dup((ASN1_STRING *)a) + +#define M_ASN1_GENERALSTRING_new() (ASN1_GENERALSTRING *)\ + ASN1_STRING_type_new(V_ASN1_GENERALSTRING) +#define M_ASN1_GENERALSTRING_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_i2d_ASN1_GENERALSTRING(a,pp) \ + i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_GENERALSTRING,\ + V_ASN1_UNIVERSAL) +#define M_d2i_ASN1_GENERALSTRING(a,pp,l) \ + (ASN1_GENERALSTRING *)d2i_ASN1_type_bytes\ + ((ASN1_STRING **)a,pp,l,B_ASN1_GENERALSTRING) + +#define M_ASN1_UNIVERSALSTRING_new() (ASN1_UNIVERSALSTRING *)\ + ASN1_STRING_type_new(V_ASN1_UNIVERSALSTRING) +#define M_ASN1_UNIVERSALSTRING_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_i2d_ASN1_UNIVERSALSTRING(a,pp) \ + i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_UNIVERSALSTRING,\ + V_ASN1_UNIVERSAL) +#define M_d2i_ASN1_UNIVERSALSTRING(a,pp,l) \ + (ASN1_UNIVERSALSTRING *)d2i_ASN1_type_bytes\ + ((ASN1_STRING **)a,pp,l,B_ASN1_UNIVERSALSTRING) + +#define M_ASN1_BMPSTRING_new() (ASN1_BMPSTRING *)\ + ASN1_STRING_type_new(V_ASN1_BMPSTRING) +#define M_ASN1_BMPSTRING_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_i2d_ASN1_BMPSTRING(a,pp) \ + i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_BMPSTRING,\ + V_ASN1_UNIVERSAL) +#define M_d2i_ASN1_BMPSTRING(a,pp,l) \ + (ASN1_BMPSTRING *)d2i_ASN1_type_bytes\ + ((ASN1_STRING **)a,pp,l,B_ASN1_BMPSTRING) + +#define M_ASN1_VISIBLESTRING_new() (ASN1_VISIBLESTRING *)\ + ASN1_STRING_type_new(V_ASN1_VISIBLESTRING) +#define M_ASN1_VISIBLESTRING_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_i2d_ASN1_VISIBLESTRING(a,pp) \ + i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_VISIBLESTRING,\ + V_ASN1_UNIVERSAL) +#define M_d2i_ASN1_VISIBLESTRING(a,pp,l) \ + (ASN1_VISIBLESTRING *)d2i_ASN1_type_bytes\ + ((ASN1_STRING **)a,pp,l,B_ASN1_VISIBLESTRING) + +#define M_ASN1_UTF8STRING_new() (ASN1_UTF8STRING *)\ + ASN1_STRING_type_new(V_ASN1_UTF8STRING) +#define M_ASN1_UTF8STRING_free(a) ASN1_STRING_free((ASN1_STRING *)a) +#define M_i2d_ASN1_UTF8STRING(a,pp) \ + i2d_ASN1_bytes((ASN1_STRING *)a,pp,V_ASN1_UTF8STRING,\ + V_ASN1_UNIVERSAL) +#define M_d2i_ASN1_UTF8STRING(a,pp,l) \ + (ASN1_UTF8STRING *)d2i_ASN1_type_bytes\ + ((ASN1_STRING **)a,pp,l,B_ASN1_UTF8STRING) + + /* for the is_set parameter to i2d_ASN1_SET */ +#define IS_SEQUENCE 0 +#define IS_SET 1 + +DECLARE_ASN1_FUNCTIONS_fname(ASN1_TYPE, ASN1_ANY, ASN1_TYPE) + +int ASN1_TYPE_get(ASN1_TYPE *a); +void ASN1_TYPE_set(ASN1_TYPE *a, int type, void *value); + +ASN1_OBJECT * ASN1_OBJECT_new(void ); +void ASN1_OBJECT_free(ASN1_OBJECT *a); +int i2d_ASN1_OBJECT(ASN1_OBJECT *a,unsigned char **pp); +ASN1_OBJECT * c2i_ASN1_OBJECT(ASN1_OBJECT **a,const unsigned char **pp, + long length); +ASN1_OBJECT * d2i_ASN1_OBJECT(ASN1_OBJECT **a,const unsigned char **pp, + long length); + +DECLARE_ASN1_ITEM(ASN1_OBJECT) + +DECLARE_STACK_OF(ASN1_OBJECT) +DECLARE_ASN1_SET_OF(ASN1_OBJECT) + +ASN1_STRING * ASN1_STRING_new(void); +void ASN1_STRING_free(ASN1_STRING *a); +ASN1_STRING * ASN1_STRING_dup(ASN1_STRING *a); +ASN1_STRING * ASN1_STRING_type_new(int type ); +int ASN1_STRING_cmp(ASN1_STRING *a, ASN1_STRING *b); + /* Since this is used to store all sorts of things, via macros, for now, make + its data void * */ +int ASN1_STRING_set(ASN1_STRING *str, const void *data, int len); +int ASN1_STRING_length(ASN1_STRING *x); +void ASN1_STRING_length_set(ASN1_STRING *x, int n); +int ASN1_STRING_type(ASN1_STRING *x); +unsigned char * ASN1_STRING_data(ASN1_STRING *x); + +DECLARE_ASN1_FUNCTIONS(ASN1_BIT_STRING) +int i2c_ASN1_BIT_STRING(ASN1_BIT_STRING *a,unsigned char **pp); +ASN1_BIT_STRING *c2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a,const unsigned char **pp, + long length); +int ASN1_BIT_STRING_set(ASN1_BIT_STRING *a, unsigned char *d, + int length ); +int ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *a, int n, int value); +int ASN1_BIT_STRING_get_bit(ASN1_BIT_STRING *a, int n); + +#ifndef OPENSSL_NO_BIO +int ASN1_BIT_STRING_name_print(BIO *out, ASN1_BIT_STRING *bs, + BIT_STRING_BITNAME *tbl, int indent); +#endif +int ASN1_BIT_STRING_num_asc(char *name, BIT_STRING_BITNAME *tbl); +int ASN1_BIT_STRING_set_asc(ASN1_BIT_STRING *bs, char *name, int value, + BIT_STRING_BITNAME *tbl); + +int i2d_ASN1_BOOLEAN(int a,unsigned char **pp); +int d2i_ASN1_BOOLEAN(int *a,const unsigned char **pp,long length); + +DECLARE_ASN1_FUNCTIONS(ASN1_INTEGER) +int i2c_ASN1_INTEGER(ASN1_INTEGER *a,unsigned char **pp); +ASN1_INTEGER *c2i_ASN1_INTEGER(ASN1_INTEGER **a,const unsigned char **pp, + long length); +ASN1_INTEGER *d2i_ASN1_UINTEGER(ASN1_INTEGER **a,const unsigned char **pp, + long length); +ASN1_INTEGER * ASN1_INTEGER_dup(ASN1_INTEGER *x); +int ASN1_INTEGER_cmp(ASN1_INTEGER *x, ASN1_INTEGER *y); + +DECLARE_ASN1_FUNCTIONS(ASN1_ENUMERATED) + +int ASN1_UTCTIME_check(ASN1_UTCTIME *a); +ASN1_UTCTIME *ASN1_UTCTIME_set(ASN1_UTCTIME *s,time_t t); +int ASN1_UTCTIME_set_string(ASN1_UTCTIME *s, const char *str); +int ASN1_UTCTIME_cmp_time_t(const ASN1_UTCTIME *s, time_t t); +#if 0 +time_t ASN1_UTCTIME_get(const ASN1_UTCTIME *s); +#endif + +int ASN1_GENERALIZEDTIME_check(ASN1_GENERALIZEDTIME *a); +ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_set(ASN1_GENERALIZEDTIME *s,time_t t); +int ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *s, const char *str); + +DECLARE_ASN1_FUNCTIONS(ASN1_OCTET_STRING) +ASN1_OCTET_STRING * ASN1_OCTET_STRING_dup(ASN1_OCTET_STRING *a); +int ASN1_OCTET_STRING_cmp(ASN1_OCTET_STRING *a, ASN1_OCTET_STRING *b); +int ASN1_OCTET_STRING_set(ASN1_OCTET_STRING *str, const unsigned char *data, int len); + +DECLARE_ASN1_FUNCTIONS(ASN1_VISIBLESTRING) +DECLARE_ASN1_FUNCTIONS(ASN1_UNIVERSALSTRING) +DECLARE_ASN1_FUNCTIONS(ASN1_UTF8STRING) +DECLARE_ASN1_FUNCTIONS(ASN1_NULL) +DECLARE_ASN1_FUNCTIONS(ASN1_BMPSTRING) + +int UTF8_getc(const unsigned char *str, int len, unsigned long *val); +int UTF8_putc(unsigned char *str, int len, unsigned long value); + +DECLARE_ASN1_FUNCTIONS_name(ASN1_STRING, ASN1_PRINTABLE) + +DECLARE_ASN1_FUNCTIONS_name(ASN1_STRING, DIRECTORYSTRING) +DECLARE_ASN1_FUNCTIONS_name(ASN1_STRING, DISPLAYTEXT) +DECLARE_ASN1_FUNCTIONS(ASN1_PRINTABLESTRING) +DECLARE_ASN1_FUNCTIONS(ASN1_T61STRING) +DECLARE_ASN1_FUNCTIONS(ASN1_IA5STRING) +DECLARE_ASN1_FUNCTIONS(ASN1_GENERALSTRING) +DECLARE_ASN1_FUNCTIONS(ASN1_UTCTIME) +DECLARE_ASN1_FUNCTIONS(ASN1_GENERALIZEDTIME) +DECLARE_ASN1_FUNCTIONS(ASN1_TIME) + +DECLARE_ASN1_ITEM(ASN1_OCTET_STRING_NDEF) + +ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s,time_t t); +int ASN1_TIME_check(ASN1_TIME *t); +ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(ASN1_TIME *t, ASN1_GENERALIZEDTIME **out); + +int i2d_ASN1_SET(STACK *a, unsigned char **pp, + i2d_of_void *i2d, int ex_tag, int ex_class, int is_set); +STACK * d2i_ASN1_SET(STACK **a, const unsigned char **pp, long length, + d2i_of_void *d2i, void (*free_func)(void *), + int ex_tag, int ex_class); + +#ifndef OPENSSL_NO_BIO +int i2a_ASN1_INTEGER(BIO *bp, ASN1_INTEGER *a); +int a2i_ASN1_INTEGER(BIO *bp,ASN1_INTEGER *bs,char *buf,int size); +int i2a_ASN1_ENUMERATED(BIO *bp, ASN1_ENUMERATED *a); +int a2i_ASN1_ENUMERATED(BIO *bp,ASN1_ENUMERATED *bs,char *buf,int size); +int i2a_ASN1_OBJECT(BIO *bp,ASN1_OBJECT *a); +int a2i_ASN1_STRING(BIO *bp,ASN1_STRING *bs,char *buf,int size); +int i2a_ASN1_STRING(BIO *bp, ASN1_STRING *a, int type); +#endif +int i2t_ASN1_OBJECT(char *buf,int buf_len,ASN1_OBJECT *a); + +int a2d_ASN1_OBJECT(unsigned char *out,int olen, const char *buf, int num); +ASN1_OBJECT *ASN1_OBJECT_create(int nid, unsigned char *data,int len, + const char *sn, const char *ln); + +int ASN1_INTEGER_set(ASN1_INTEGER *a, long v); +long ASN1_INTEGER_get(ASN1_INTEGER *a); +ASN1_INTEGER *BN_to_ASN1_INTEGER(BIGNUM *bn, ASN1_INTEGER *ai); +BIGNUM *ASN1_INTEGER_to_BN(ASN1_INTEGER *ai,BIGNUM *bn); + +int ASN1_ENUMERATED_set(ASN1_ENUMERATED *a, long v); +long ASN1_ENUMERATED_get(ASN1_ENUMERATED *a); +ASN1_ENUMERATED *BN_to_ASN1_ENUMERATED(BIGNUM *bn, ASN1_ENUMERATED *ai); +BIGNUM *ASN1_ENUMERATED_to_BN(ASN1_ENUMERATED *ai,BIGNUM *bn); + +/* General */ +/* given a string, return the correct type, max is the maximum length */ +int ASN1_PRINTABLE_type(const unsigned char *s, int max); + +int i2d_ASN1_bytes(ASN1_STRING *a, unsigned char **pp, int tag, int xclass); +ASN1_STRING *d2i_ASN1_bytes(ASN1_STRING **a, const unsigned char **pp, + long length, int Ptag, int Pclass); +unsigned long ASN1_tag2bit(int tag); +/* type is one or more of the B_ASN1_ values. */ +ASN1_STRING *d2i_ASN1_type_bytes(ASN1_STRING **a,const unsigned char **pp, + long length,int type); + +/* PARSING */ +int asn1_Finish(ASN1_CTX *c); +int asn1_const_Finish(ASN1_const_CTX *c); + +/* SPECIALS */ +int ASN1_get_object(const unsigned char **pp, long *plength, int *ptag, + int *pclass, long omax); +int ASN1_check_infinite_end(unsigned char **p,long len); +int ASN1_const_check_infinite_end(const unsigned char **p,long len); +void ASN1_put_object(unsigned char **pp, int constructed, int length, + int tag, int xclass); +int ASN1_put_eoc(unsigned char **pp); +int ASN1_object_size(int constructed, int length, int tag); + +/* Used to implement other functions */ +void *ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, char *x); +#define ASN1_dup_of(type,i2d,d2i,x) \ + ((type *(*)(I2D_OF(type),D2I_OF(type),type *))openssl_fcast(ASN1_dup))(i2d,d2i,x) +#define ASN1_dup_of_const(type,i2d,d2i,x) \ + ((type *(*)(I2D_OF_const(type),D2I_OF(type),type *))openssl_fcast(ASN1_dup))(i2d,d2i,x) + +void *ASN1_item_dup(const ASN1_ITEM *it, void *x); + +#ifndef OPENSSL_NO_FP_API +void *ASN1_d2i_fp(void *(*xnew)(void), d2i_of_void *d2i, FILE *in, void **x); +#define ASN1_d2i_fp_of(type,xnew,d2i,in,x) \ + ((type *(*)(type *(*)(void),D2I_OF(type),FILE *,type **))openssl_fcast(ASN1_d2i_fp))(xnew,d2i,in,x) +void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x); +int ASN1_i2d_fp(i2d_of_void *i2d,FILE *out,void *x); +#define ASN1_i2d_fp_of(type,i2d,out,x) \ + ((int (*)(I2D_OF(type),FILE *,type *))openssl_fcast(ASN1_i2d_fp))(i2d,out,x) +#define ASN1_i2d_fp_of_const(type,i2d,out,x) \ + ((int (*)(I2D_OF_const(type),FILE *,type *))openssl_fcast(ASN1_i2d_fp))(i2d,out,x) +int ASN1_item_i2d_fp(const ASN1_ITEM *it, FILE *out, void *x); +int ASN1_STRING_print_ex_fp(FILE *fp, ASN1_STRING *str, unsigned long flags); +#endif + +int ASN1_STRING_to_UTF8(unsigned char **out, ASN1_STRING *in); + +#ifndef OPENSSL_NO_BIO +void *ASN1_d2i_bio(void *(*xnew)(void), d2i_of_void *d2i, BIO *in, void **x); +#define ASN1_d2i_bio_of(type,xnew,d2i,in,x) \ + ((type *(*)(type *(*)(void),D2I_OF(type),BIO *,type **))openssl_fcast(ASN1_d2i_bio))(xnew,d2i,in,x) +void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x); +int ASN1_i2d_bio(i2d_of_void *i2d,BIO *out, unsigned char *x); +#define ASN1_i2d_bio_of(type,i2d,out,x) \ + ((int (*)(I2D_OF(type),BIO *,type *))openssl_fcast(ASN1_i2d_bio))(i2d,out,x) +#define ASN1_i2d_bio_of_const(type,i2d,out,x) \ + ((int (*)(I2D_OF_const(type),BIO *,const type *))openssl_fcast(ASN1_i2d_bio))(i2d,out,x) +int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, void *x); +int ASN1_UTCTIME_print(BIO *fp,ASN1_UTCTIME *a); +int ASN1_GENERALIZEDTIME_print(BIO *fp,ASN1_GENERALIZEDTIME *a); +int ASN1_TIME_print(BIO *fp,ASN1_TIME *a); +int ASN1_STRING_print(BIO *bp,ASN1_STRING *v); +int ASN1_STRING_print_ex(BIO *out, ASN1_STRING *str, unsigned long flags); +int ASN1_parse(BIO *bp,const unsigned char *pp,long len,int indent); +int ASN1_parse_dump(BIO *bp,const unsigned char *pp,long len,int indent,int dump); +#endif +const char *ASN1_tag2str(int tag); + +/* Used to load and write netscape format cert/key */ +int i2d_ASN1_HEADER(ASN1_HEADER *a,unsigned char **pp); +ASN1_HEADER *d2i_ASN1_HEADER(ASN1_HEADER **a,const unsigned char **pp, long length); +ASN1_HEADER *ASN1_HEADER_new(void ); +void ASN1_HEADER_free(ASN1_HEADER *a); + +int ASN1_UNIVERSALSTRING_to_string(ASN1_UNIVERSALSTRING *s); + +/* Not used that much at this point, except for the first two */ +ASN1_METHOD *X509_asn1_meth(void); +ASN1_METHOD *RSAPrivateKey_asn1_meth(void); +ASN1_METHOD *ASN1_IA5STRING_asn1_meth(void); +ASN1_METHOD *ASN1_BIT_STRING_asn1_meth(void); + +int ASN1_TYPE_set_octetstring(ASN1_TYPE *a, + unsigned char *data, int len); +int ASN1_TYPE_get_octetstring(ASN1_TYPE *a, + unsigned char *data, int max_len); +int ASN1_TYPE_set_int_octetstring(ASN1_TYPE *a, long num, + unsigned char *data, int len); +int ASN1_TYPE_get_int_octetstring(ASN1_TYPE *a,long *num, + unsigned char *data, int max_len); + +STACK *ASN1_seq_unpack(const unsigned char *buf, int len, + d2i_of_void *d2i, void (*free_func)(void *)); +unsigned char *ASN1_seq_pack(STACK *safes, i2d_of_void *i2d, + unsigned char **buf, int *len ); +void *ASN1_unpack_string(ASN1_STRING *oct, d2i_of_void *d2i); +void *ASN1_item_unpack(ASN1_STRING *oct, const ASN1_ITEM *it); +ASN1_STRING *ASN1_pack_string(void *obj, i2d_of_void *i2d, + ASN1_OCTET_STRING **oct); +#define ASN1_pack_string_of(type,obj,i2d,oct) \ + ((ASN1_STRING *(*)(type *,I2D_OF(type),ASN1_OCTET_STRING **))openssl_fcast(ASN1_pack_string))(obj,i2d,oct) +ASN1_STRING *ASN1_item_pack(void *obj, const ASN1_ITEM *it, ASN1_OCTET_STRING **oct); + +void ASN1_STRING_set_default_mask(unsigned long mask); +int ASN1_STRING_set_default_mask_asc(char *p); +unsigned long ASN1_STRING_get_default_mask(void); +int ASN1_mbstring_copy(ASN1_STRING **out, const unsigned char *in, int len, + int inform, unsigned long mask); +int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len, + int inform, unsigned long mask, + long minsize, long maxsize); + +ASN1_STRING *ASN1_STRING_set_by_NID(ASN1_STRING **out, + const unsigned char *in, int inlen, int inform, int nid); +ASN1_STRING_TABLE *ASN1_STRING_TABLE_get(int nid); +int ASN1_STRING_TABLE_add(int, long, long, unsigned long, unsigned long); +void ASN1_STRING_TABLE_cleanup(void); + +/* ASN1 template functions */ + +/* Old API compatible functions */ +ASN1_VALUE *ASN1_item_new(const ASN1_ITEM *it); +void ASN1_item_free(ASN1_VALUE *val, const ASN1_ITEM *it); +ASN1_VALUE * ASN1_item_d2i(ASN1_VALUE **val, const unsigned char **in, long len, const ASN1_ITEM *it); +int ASN1_item_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it); +int ASN1_item_ndef_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it); + +void ASN1_add_oid_module(void); + +ASN1_TYPE *ASN1_generate_nconf(char *str, CONF *nconf); +ASN1_TYPE *ASN1_generate_v3(char *str, X509V3_CTX *cnf); + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_ASN1_strings(void); + +/* Error codes for the ASN1 functions. */ + +/* Function codes. */ +#define ASN1_F_A2D_ASN1_OBJECT 100 +#define ASN1_F_A2I_ASN1_ENUMERATED 101 +#define ASN1_F_A2I_ASN1_INTEGER 102 +#define ASN1_F_A2I_ASN1_STRING 103 +#define ASN1_F_APPEND_EXP 176 +#define ASN1_F_ASN1_BIT_STRING_SET_BIT 183 +#define ASN1_F_ASN1_CB 177 +#define ASN1_F_ASN1_CHECK_TLEN 104 +#define ASN1_F_ASN1_COLLATE_PRIMITIVE 105 +#define ASN1_F_ASN1_COLLECT 106 +#define ASN1_F_ASN1_D2I_EX_PRIMITIVE 108 +#define ASN1_F_ASN1_D2I_FP 109 +#define ASN1_F_ASN1_D2I_READ_BIO 107 +#define ASN1_F_ASN1_DIGEST 184 +#define ASN1_F_ASN1_DO_ADB 110 +#define ASN1_F_ASN1_DUP 111 +#define ASN1_F_ASN1_ENUMERATED_SET 112 +#define ASN1_F_ASN1_ENUMERATED_TO_BN 113 +#define ASN1_F_ASN1_EX_C2I 204 +#define ASN1_F_ASN1_FIND_END 190 +#define ASN1_F_ASN1_GENERALIZEDTIME_SET 185 +#define ASN1_F_ASN1_GENERATE_V3 178 +#define ASN1_F_ASN1_GET_OBJECT 114 +#define ASN1_F_ASN1_HEADER_NEW 115 +#define ASN1_F_ASN1_I2D_BIO 116 +#define ASN1_F_ASN1_I2D_FP 117 +#define ASN1_F_ASN1_INTEGER_SET 118 +#define ASN1_F_ASN1_INTEGER_TO_BN 119 +#define ASN1_F_ASN1_ITEM_D2I_FP 206 +#define ASN1_F_ASN1_ITEM_DUP 191 +#define ASN1_F_ASN1_ITEM_EX_COMBINE_NEW 121 +#define ASN1_F_ASN1_ITEM_EX_D2I 120 +#define ASN1_F_ASN1_ITEM_I2D_BIO 192 +#define ASN1_F_ASN1_ITEM_I2D_FP 193 +#define ASN1_F_ASN1_ITEM_PACK 198 +#define ASN1_F_ASN1_ITEM_SIGN 195 +#define ASN1_F_ASN1_ITEM_UNPACK 199 +#define ASN1_F_ASN1_ITEM_VERIFY 197 +#define ASN1_F_ASN1_MBSTRING_NCOPY 122 +#define ASN1_F_ASN1_OBJECT_NEW 123 +#define ASN1_F_ASN1_PACK_STRING 124 +#define ASN1_F_ASN1_PCTX_NEW 205 +#define ASN1_F_ASN1_PKCS5_PBE_SET 125 +#define ASN1_F_ASN1_SEQ_PACK 126 +#define ASN1_F_ASN1_SEQ_UNPACK 127 +#define ASN1_F_ASN1_SIGN 128 +#define ASN1_F_ASN1_STR2TYPE 179 +#define ASN1_F_ASN1_STRING_SET 186 +#define ASN1_F_ASN1_STRING_TABLE_ADD 129 +#define ASN1_F_ASN1_STRING_TYPE_NEW 130 +#define ASN1_F_ASN1_TEMPLATE_EX_D2I 132 +#define ASN1_F_ASN1_TEMPLATE_NEW 133 +#define ASN1_F_ASN1_TEMPLATE_NOEXP_D2I 131 +#define ASN1_F_ASN1_TIME_SET 175 +#define ASN1_F_ASN1_TYPE_GET_INT_OCTETSTRING 134 +#define ASN1_F_ASN1_TYPE_GET_OCTETSTRING 135 +#define ASN1_F_ASN1_UNPACK_STRING 136 +#define ASN1_F_ASN1_UTCTIME_SET 187 +#define ASN1_F_ASN1_VERIFY 137 +#define ASN1_F_BITSTR_CB 180 +#define ASN1_F_BN_TO_ASN1_ENUMERATED 138 +#define ASN1_F_BN_TO_ASN1_INTEGER 139 +#define ASN1_F_C2I_ASN1_BIT_STRING 189 +#define ASN1_F_C2I_ASN1_INTEGER 194 +#define ASN1_F_C2I_ASN1_OBJECT 196 +#define ASN1_F_COLLECT_DATA 140 +#define ASN1_F_D2I_ASN1_BIT_STRING 141 +#define ASN1_F_D2I_ASN1_BOOLEAN 142 +#define ASN1_F_D2I_ASN1_BYTES 143 +#define ASN1_F_D2I_ASN1_GENERALIZEDTIME 144 +#define ASN1_F_D2I_ASN1_HEADER 145 +#define ASN1_F_D2I_ASN1_INTEGER 146 +#define ASN1_F_D2I_ASN1_OBJECT 147 +#define ASN1_F_D2I_ASN1_SET 148 +#define ASN1_F_D2I_ASN1_TYPE_BYTES 149 +#define ASN1_F_D2I_ASN1_UINTEGER 150 +#define ASN1_F_D2I_ASN1_UTCTIME 151 +#define ASN1_F_D2I_NETSCAPE_RSA 152 +#define ASN1_F_D2I_NETSCAPE_RSA_2 153 +#define ASN1_F_D2I_PRIVATEKEY 154 +#define ASN1_F_D2I_PUBLICKEY 155 +#define ASN1_F_D2I_RSA_NET 200 +#define ASN1_F_D2I_RSA_NET_2 201 +#define ASN1_F_D2I_X509 156 +#define ASN1_F_D2I_X509_CINF 157 +#define ASN1_F_D2I_X509_PKEY 159 +#define ASN1_F_I2D_ASN1_SET 188 +#define ASN1_F_I2D_ASN1_TIME 160 +#define ASN1_F_I2D_DSA_PUBKEY 161 +#define ASN1_F_I2D_EC_PUBKEY 181 +#define ASN1_F_I2D_PRIVATEKEY 163 +#define ASN1_F_I2D_PUBLICKEY 164 +#define ASN1_F_I2D_RSA_NET 162 +#define ASN1_F_I2D_RSA_PUBKEY 165 +#define ASN1_F_LONG_C2I 166 +#define ASN1_F_OID_MODULE_INIT 174 +#define ASN1_F_PARSE_TAGGING 182 +#define ASN1_F_PKCS5_PBE2_SET 167 +#define ASN1_F_PKCS5_PBE_SET 202 +#define ASN1_F_X509_CINF_NEW 168 +#define ASN1_F_X509_CRL_ADD0_REVOKED 169 +#define ASN1_F_X509_INFO_NEW 170 +#define ASN1_F_X509_NAME_ENCODE 203 +#define ASN1_F_X509_NAME_EX_D2I 158 +#define ASN1_F_X509_NAME_EX_NEW 171 +#define ASN1_F_X509_NEW 172 +#define ASN1_F_X509_PKEY_NEW 173 + +/* Reason codes. */ +#define ASN1_R_ADDING_OBJECT 171 +#define ASN1_R_AUX_ERROR 100 +#define ASN1_R_BAD_CLASS 101 +#define ASN1_R_BAD_OBJECT_HEADER 102 +#define ASN1_R_BAD_PASSWORD_READ 103 +#define ASN1_R_BAD_TAG 104 +#define ASN1_R_BN_LIB 105 +#define ASN1_R_BOOLEAN_IS_WRONG_LENGTH 106 +#define ASN1_R_BUFFER_TOO_SMALL 107 +#define ASN1_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER 108 +#define ASN1_R_DATA_IS_WRONG 109 +#define ASN1_R_DECODE_ERROR 110 +#define ASN1_R_DECODING_ERROR 111 +#define ASN1_R_DEPTH_EXCEEDED 174 +#define ASN1_R_ENCODE_ERROR 112 +#define ASN1_R_ERROR_GETTING_TIME 173 +#define ASN1_R_ERROR_LOADING_SECTION 172 +#define ASN1_R_ERROR_PARSING_SET_ELEMENT 113 +#define ASN1_R_ERROR_SETTING_CIPHER_PARAMS 114 +#define ASN1_R_EXPECTING_AN_INTEGER 115 +#define ASN1_R_EXPECTING_AN_OBJECT 116 +#define ASN1_R_EXPECTING_A_BOOLEAN 117 +#define ASN1_R_EXPECTING_A_TIME 118 +#define ASN1_R_EXPLICIT_LENGTH_MISMATCH 119 +#define ASN1_R_EXPLICIT_TAG_NOT_CONSTRUCTED 120 +#define ASN1_R_FIELD_MISSING 121 +#define ASN1_R_FIRST_NUM_TOO_LARGE 122 +#define ASN1_R_HEADER_TOO_LONG 123 +#define ASN1_R_ILLEGAL_BITSTRING_FORMAT 175 +#define ASN1_R_ILLEGAL_BOOLEAN 176 +#define ASN1_R_ILLEGAL_CHARACTERS 124 +#define ASN1_R_ILLEGAL_FORMAT 177 +#define ASN1_R_ILLEGAL_HEX 178 +#define ASN1_R_ILLEGAL_IMPLICIT_TAG 179 +#define ASN1_R_ILLEGAL_INTEGER 180 +#define ASN1_R_ILLEGAL_NESTED_TAGGING 181 +#define ASN1_R_ILLEGAL_NULL 125 +#define ASN1_R_ILLEGAL_NULL_VALUE 182 +#define ASN1_R_ILLEGAL_OBJECT 183 +#define ASN1_R_ILLEGAL_OPTIONAL_ANY 126 +#define ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE 170 +#define ASN1_R_ILLEGAL_TAGGED_ANY 127 +#define ASN1_R_ILLEGAL_TIME_VALUE 184 +#define ASN1_R_INTEGER_NOT_ASCII_FORMAT 185 +#define ASN1_R_INTEGER_TOO_LARGE_FOR_LONG 128 +#define ASN1_R_INVALID_BMPSTRING_LENGTH 129 +#define ASN1_R_INVALID_DIGIT 130 +#define ASN1_R_INVALID_MODIFIER 186 +#define ASN1_R_INVALID_NUMBER 187 +#define ASN1_R_INVALID_SEPARATOR 131 +#define ASN1_R_INVALID_TIME_FORMAT 132 +#define ASN1_R_INVALID_UNIVERSALSTRING_LENGTH 133 +#define ASN1_R_INVALID_UTF8STRING 134 +#define ASN1_R_IV_TOO_LARGE 135 +#define ASN1_R_LENGTH_ERROR 136 +#define ASN1_R_LIST_ERROR 188 +#define ASN1_R_MISSING_EOC 137 +#define ASN1_R_MISSING_SECOND_NUMBER 138 +#define ASN1_R_MISSING_VALUE 189 +#define ASN1_R_MSTRING_NOT_UNIVERSAL 139 +#define ASN1_R_MSTRING_WRONG_TAG 140 +#define ASN1_R_NESTED_ASN1_STRING 197 +#define ASN1_R_NON_HEX_CHARACTERS 141 +#define ASN1_R_NOT_ASCII_FORMAT 190 +#define ASN1_R_NOT_ENOUGH_DATA 142 +#define ASN1_R_NO_MATCHING_CHOICE_TYPE 143 +#define ASN1_R_NULL_IS_WRONG_LENGTH 144 +#define ASN1_R_OBJECT_NOT_ASCII_FORMAT 191 +#define ASN1_R_ODD_NUMBER_OF_CHARS 145 +#define ASN1_R_PRIVATE_KEY_HEADER_MISSING 146 +#define ASN1_R_SECOND_NUMBER_TOO_LARGE 147 +#define ASN1_R_SEQUENCE_LENGTH_MISMATCH 148 +#define ASN1_R_SEQUENCE_NOT_CONSTRUCTED 149 +#define ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG 192 +#define ASN1_R_SHORT_LINE 150 +#define ASN1_R_STRING_TOO_LONG 151 +#define ASN1_R_STRING_TOO_SHORT 152 +#define ASN1_R_TAG_VALUE_TOO_HIGH 153 +#define ASN1_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD 154 +#define ASN1_R_TIME_NOT_ASCII_FORMAT 193 +#define ASN1_R_TOO_LONG 155 +#define ASN1_R_TYPE_NOT_CONSTRUCTED 156 +#define ASN1_R_UNABLE_TO_DECODE_RSA_KEY 157 +#define ASN1_R_UNABLE_TO_DECODE_RSA_PRIVATE_KEY 158 +#define ASN1_R_UNEXPECTED_EOC 159 +#define ASN1_R_UNKNOWN_FORMAT 160 +#define ASN1_R_UNKNOWN_MESSAGE_DIGEST_ALGORITHM 161 +#define ASN1_R_UNKNOWN_OBJECT_TYPE 162 +#define ASN1_R_UNKNOWN_PUBLIC_KEY_TYPE 163 +#define ASN1_R_UNKNOWN_TAG 194 +#define ASN1_R_UNKOWN_FORMAT 195 +#define ASN1_R_UNSUPPORTED_ANY_DEFINED_BY_TYPE 164 +#define ASN1_R_UNSUPPORTED_CIPHER 165 +#define ASN1_R_UNSUPPORTED_ENCRYPTION_ALGORITHM 166 +#define ASN1_R_UNSUPPORTED_PUBLIC_KEY_TYPE 167 +#define ASN1_R_UNSUPPORTED_TYPE 196 +#define ASN1_R_WRONG_TAG 168 +#define ASN1_R_WRONG_TYPE 169 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/asn1_mac.h b/production/3rdparty/openssl/include/openssl/asn1_mac.h new file mode 100644 index 00000000..d958ca60 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/asn1_mac.h @@ -0,0 +1,571 @@ +/* crypto/asn1/asn1_mac.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_ASN1_MAC_H +#define HEADER_ASN1_MAC_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef ASN1_MAC_ERR_LIB +#define ASN1_MAC_ERR_LIB ERR_LIB_ASN1 +#endif + +#define ASN1_MAC_H_err(f,r,line) \ + ERR_PUT_error(ASN1_MAC_ERR_LIB,(f),(r),__FILE__,(line)) + +#define M_ASN1_D2I_vars(a,type,func) \ + ASN1_const_CTX c; \ + type ret=NULL; \ + \ + c.pp=(const unsigned char **)pp; \ + c.q= *(const unsigned char **)pp; \ + c.error=ERR_R_NESTED_ASN1_ERROR; \ + if ((a == NULL) || ((*a) == NULL)) \ + { if ((ret=(type)func()) == NULL) \ + { c.line=__LINE__; goto err; } } \ + else ret=(*a); + +#define M_ASN1_D2I_Init() \ + c.p= *(const unsigned char **)pp; \ + c.max=(length == 0)?0:(c.p+length); + +#define M_ASN1_D2I_Finish_2(a) \ + if (!asn1_const_Finish(&c)) \ + { c.line=__LINE__; goto err; } \ + *(const unsigned char **)pp=c.p; \ + if (a != NULL) (*a)=ret; \ + return(ret); + +#define M_ASN1_D2I_Finish(a,func,e) \ + M_ASN1_D2I_Finish_2(a); \ +err:\ + ASN1_MAC_H_err((e),c.error,c.line); \ + asn1_add_error(*(const unsigned char **)pp,(int)(c.q- *pp)); \ + if ((ret != NULL) && ((a == NULL) || (*a != ret))) func(ret); \ + return(NULL) + +#define M_ASN1_D2I_start_sequence() \ + if (!asn1_GetSequence(&c,&length)) \ + { c.line=__LINE__; goto err; } +/* Begin reading ASN1 without a surrounding sequence */ +#define M_ASN1_D2I_begin() \ + c.slen = length; + +/* End reading ASN1 with no check on length */ +#define M_ASN1_D2I_Finish_nolen(a, func, e) \ + *pp=c.p; \ + if (a != NULL) (*a)=ret; \ + return(ret); \ +err:\ + ASN1_MAC_H_err((e),c.error,c.line); \ + asn1_add_error(*pp,(int)(c.q- *pp)); \ + if ((ret != NULL) && ((a == NULL) || (*a != ret))) func(ret); \ + return(NULL) + +#define M_ASN1_D2I_end_sequence() \ + (((c.inf&1) == 0)?(c.slen <= 0): \ + (c.eos=ASN1_const_check_infinite_end(&c.p,c.slen))) + +/* Don't use this with d2i_ASN1_BOOLEAN() */ +#define M_ASN1_D2I_get(b, func) \ + c.q=c.p; \ + if (func(&(b),&c.p,c.slen) == NULL) \ + {c.line=__LINE__; goto err; } \ + c.slen-=(c.p-c.q); + +/* Don't use this with d2i_ASN1_BOOLEAN() */ +#define M_ASN1_D2I_get_x(type,b,func) \ + c.q=c.p; \ + if (((D2I_OF(type))func)(&(b),&c.p,c.slen) == NULL) \ + {c.line=__LINE__; goto err; } \ + c.slen-=(c.p-c.q); + +/* use this instead () */ +#define M_ASN1_D2I_get_int(b,func) \ + c.q=c.p; \ + if (func(&(b),&c.p,c.slen) < 0) \ + {c.line=__LINE__; goto err; } \ + c.slen-=(c.p-c.q); + +#define M_ASN1_D2I_get_opt(b,func,type) \ + if ((c.slen != 0) && ((M_ASN1_next & (~V_ASN1_CONSTRUCTED)) \ + == (V_ASN1_UNIVERSAL|(type)))) \ + { \ + M_ASN1_D2I_get(b,func); \ + } + +#define M_ASN1_D2I_get_imp(b,func, type) \ + M_ASN1_next=(_tmp& V_ASN1_CONSTRUCTED)|type; \ + c.q=c.p; \ + if (func(&(b),&c.p,c.slen) == NULL) \ + {c.line=__LINE__; M_ASN1_next_prev = _tmp; goto err; } \ + c.slen-=(c.p-c.q);\ + M_ASN1_next_prev=_tmp; + +#define M_ASN1_D2I_get_IMP_opt(b,func,tag,type) \ + if ((c.slen != 0) && ((M_ASN1_next & (~V_ASN1_CONSTRUCTED)) == \ + (V_ASN1_CONTEXT_SPECIFIC|(tag)))) \ + { \ + unsigned char _tmp = M_ASN1_next; \ + M_ASN1_D2I_get_imp(b,func, type);\ + } + +#define M_ASN1_D2I_get_set(r,func,free_func) \ + M_ASN1_D2I_get_imp_set(r,func,free_func, \ + V_ASN1_SET,V_ASN1_UNIVERSAL); + +#define M_ASN1_D2I_get_set_type(type,r,func,free_func) \ + M_ASN1_D2I_get_imp_set_type(type,r,func,free_func, \ + V_ASN1_SET,V_ASN1_UNIVERSAL); + +#define M_ASN1_D2I_get_set_opt(r,func,free_func) \ + if ((c.slen != 0) && (M_ASN1_next == (V_ASN1_UNIVERSAL| \ + V_ASN1_CONSTRUCTED|V_ASN1_SET)))\ + { M_ASN1_D2I_get_set(r,func,free_func); } + +#define M_ASN1_D2I_get_set_opt_type(type,r,func,free_func) \ + if ((c.slen != 0) && (M_ASN1_next == (V_ASN1_UNIVERSAL| \ + V_ASN1_CONSTRUCTED|V_ASN1_SET)))\ + { M_ASN1_D2I_get_set_type(type,r,func,free_func); } + +#define M_ASN1_I2D_len_SET_opt(a,f) \ + if ((a != NULL) && (sk_num(a) != 0)) \ + M_ASN1_I2D_len_SET(a,f); + +#define M_ASN1_I2D_put_SET_opt(a,f) \ + if ((a != NULL) && (sk_num(a) != 0)) \ + M_ASN1_I2D_put_SET(a,f); + +#define M_ASN1_I2D_put_SEQUENCE_opt(a,f) \ + if ((a != NULL) && (sk_num(a) != 0)) \ + M_ASN1_I2D_put_SEQUENCE(a,f); + +#define M_ASN1_I2D_put_SEQUENCE_opt_type(type,a,f) \ + if ((a != NULL) && (sk_##type##_num(a) != 0)) \ + M_ASN1_I2D_put_SEQUENCE_type(type,a,f); + +#define M_ASN1_D2I_get_IMP_set_opt(b,func,free_func,tag) \ + if ((c.slen != 0) && \ + (M_ASN1_next == \ + (V_ASN1_CONTEXT_SPECIFIC|V_ASN1_CONSTRUCTED|(tag))))\ + { \ + M_ASN1_D2I_get_imp_set(b,func,free_func,\ + tag,V_ASN1_CONTEXT_SPECIFIC); \ + } + +#define M_ASN1_D2I_get_IMP_set_opt_type(type,b,func,free_func,tag) \ + if ((c.slen != 0) && \ + (M_ASN1_next == \ + (V_ASN1_CONTEXT_SPECIFIC|V_ASN1_CONSTRUCTED|(tag))))\ + { \ + M_ASN1_D2I_get_imp_set_type(type,b,func,free_func,\ + tag,V_ASN1_CONTEXT_SPECIFIC); \ + } + +#define M_ASN1_D2I_get_seq(r,func,free_func) \ + M_ASN1_D2I_get_imp_set(r,func,free_func,\ + V_ASN1_SEQUENCE,V_ASN1_UNIVERSAL); + +#define M_ASN1_D2I_get_seq_type(type,r,func,free_func) \ + M_ASN1_D2I_get_imp_set_type(type,r,func,free_func,\ + V_ASN1_SEQUENCE,V_ASN1_UNIVERSAL) + +#define M_ASN1_D2I_get_seq_opt(r,func,free_func) \ + if ((c.slen != 0) && (M_ASN1_next == (V_ASN1_UNIVERSAL| \ + V_ASN1_CONSTRUCTED|V_ASN1_SEQUENCE)))\ + { M_ASN1_D2I_get_seq(r,func,free_func); } + +#define M_ASN1_D2I_get_seq_opt_type(type,r,func,free_func) \ + if ((c.slen != 0) && (M_ASN1_next == (V_ASN1_UNIVERSAL| \ + V_ASN1_CONSTRUCTED|V_ASN1_SEQUENCE)))\ + { M_ASN1_D2I_get_seq_type(type,r,func,free_func); } + +#define M_ASN1_D2I_get_IMP_set(r,func,free_func,x) \ + M_ASN1_D2I_get_imp_set(r,func,free_func,\ + x,V_ASN1_CONTEXT_SPECIFIC); + +#define M_ASN1_D2I_get_IMP_set_type(type,r,func,free_func,x) \ + M_ASN1_D2I_get_imp_set_type(type,r,func,free_func,\ + x,V_ASN1_CONTEXT_SPECIFIC); + +#define M_ASN1_D2I_get_imp_set(r,func,free_func,a,b) \ + c.q=c.p; \ + if (d2i_ASN1_SET(&(r),&c.p,c.slen,(char *(*)())func,\ + (void (*)())free_func,a,b) == NULL) \ + { c.line=__LINE__; goto err; } \ + c.slen-=(c.p-c.q); + +#define M_ASN1_D2I_get_imp_set_type(type,r,func,free_func,a,b) \ + c.q=c.p; \ + if (d2i_ASN1_SET_OF_##type(&(r),&c.p,c.slen,func,\ + free_func,a,b) == NULL) \ + { c.line=__LINE__; goto err; } \ + c.slen-=(c.p-c.q); + +#define M_ASN1_D2I_get_set_strings(r,func,a,b) \ + c.q=c.p; \ + if (d2i_ASN1_STRING_SET(&(r),&c.p,c.slen,a,b) == NULL) \ + { c.line=__LINE__; goto err; } \ + c.slen-=(c.p-c.q); + +#define M_ASN1_D2I_get_EXP_opt(r,func,tag) \ + if ((c.slen != 0L) && (M_ASN1_next == \ + (V_ASN1_CONSTRUCTED|V_ASN1_CONTEXT_SPECIFIC|tag))) \ + { \ + int Tinf,Ttag,Tclass; \ + long Tlen; \ + \ + c.q=c.p; \ + Tinf=ASN1_get_object(&c.p,&Tlen,&Ttag,&Tclass,c.slen); \ + if (Tinf & 0x80) \ + { c.error=ERR_R_BAD_ASN1_OBJECT_HEADER; \ + c.line=__LINE__; goto err; } \ + if (Tinf == (V_ASN1_CONSTRUCTED+1)) \ + Tlen = c.slen - (c.p - c.q) - 2; \ + if (func(&(r),&c.p,Tlen) == NULL) \ + { c.line=__LINE__; goto err; } \ + if (Tinf == (V_ASN1_CONSTRUCTED+1)) { \ + Tlen = c.slen - (c.p - c.q); \ + if(!ASN1_const_check_infinite_end(&c.p, Tlen)) \ + { c.error=ERR_R_MISSING_ASN1_EOS; \ + c.line=__LINE__; goto err; } \ + }\ + c.slen-=(c.p-c.q); \ + } + +#define M_ASN1_D2I_get_EXP_set_opt(r,func,free_func,tag,b) \ + if ((c.slen != 0) && (M_ASN1_next == \ + (V_ASN1_CONSTRUCTED|V_ASN1_CONTEXT_SPECIFIC|tag))) \ + { \ + int Tinf,Ttag,Tclass; \ + long Tlen; \ + \ + c.q=c.p; \ + Tinf=ASN1_get_object(&c.p,&Tlen,&Ttag,&Tclass,c.slen); \ + if (Tinf & 0x80) \ + { c.error=ERR_R_BAD_ASN1_OBJECT_HEADER; \ + c.line=__LINE__; goto err; } \ + if (Tinf == (V_ASN1_CONSTRUCTED+1)) \ + Tlen = c.slen - (c.p - c.q) - 2; \ + if (d2i_ASN1_SET(&(r),&c.p,Tlen,(char *(*)())func, \ + (void (*)())free_func, \ + b,V_ASN1_UNIVERSAL) == NULL) \ + { c.line=__LINE__; goto err; } \ + if (Tinf == (V_ASN1_CONSTRUCTED+1)) { \ + Tlen = c.slen - (c.p - c.q); \ + if(!ASN1_check_infinite_end(&c.p, Tlen)) \ + { c.error=ERR_R_MISSING_ASN1_EOS; \ + c.line=__LINE__; goto err; } \ + }\ + c.slen-=(c.p-c.q); \ + } + +#define M_ASN1_D2I_get_EXP_set_opt_type(type,r,func,free_func,tag,b) \ + if ((c.slen != 0) && (M_ASN1_next == \ + (V_ASN1_CONSTRUCTED|V_ASN1_CONTEXT_SPECIFIC|tag))) \ + { \ + int Tinf,Ttag,Tclass; \ + long Tlen; \ + \ + c.q=c.p; \ + Tinf=ASN1_get_object(&c.p,&Tlen,&Ttag,&Tclass,c.slen); \ + if (Tinf & 0x80) \ + { c.error=ERR_R_BAD_ASN1_OBJECT_HEADER; \ + c.line=__LINE__; goto err; } \ + if (Tinf == (V_ASN1_CONSTRUCTED+1)) \ + Tlen = c.slen - (c.p - c.q) - 2; \ + if (d2i_ASN1_SET_OF_##type(&(r),&c.p,Tlen,func, \ + free_func,b,V_ASN1_UNIVERSAL) == NULL) \ + { c.line=__LINE__; goto err; } \ + if (Tinf == (V_ASN1_CONSTRUCTED+1)) { \ + Tlen = c.slen - (c.p - c.q); \ + if(!ASN1_check_infinite_end(&c.p, Tlen)) \ + { c.error=ERR_R_MISSING_ASN1_EOS; \ + c.line=__LINE__; goto err; } \ + }\ + c.slen-=(c.p-c.q); \ + } + +/* New macros */ +#define M_ASN1_New_Malloc(ret,type) \ + if ((ret=(type *)OPENSSL_malloc(sizeof(type))) == NULL) \ + { c.line=__LINE__; goto err2; } + +#define M_ASN1_New(arg,func) \ + if (((arg)=func()) == NULL) return(NULL) + +#define M_ASN1_New_Error(a) \ +/* err: ASN1_MAC_H_err((a),ERR_R_NESTED_ASN1_ERROR,c.line); \ + return(NULL);*/ \ + err2: ASN1_MAC_H_err((a),ERR_R_MALLOC_FAILURE,c.line); \ + return(NULL) + + +/* BIG UGLY WARNING! This is so damn ugly I wanna puke. Unfortunately, + some macros that use ASN1_const_CTX still insist on writing in the input + stream. ARGH! ARGH! ARGH! Let's get rid of this macro package. + Please? -- Richard Levitte */ +#define M_ASN1_next (*((unsigned char *)(c.p))) +#define M_ASN1_next_prev (*((unsigned char *)(c.q))) + +/*************************************************/ + +#define M_ASN1_I2D_vars(a) int r=0,ret=0; \ + unsigned char *p; \ + if (a == NULL) return(0) + +/* Length Macros */ +#define M_ASN1_I2D_len(a,f) ret+=f(a,NULL) +#define M_ASN1_I2D_len_IMP_opt(a,f) if (a != NULL) M_ASN1_I2D_len(a,f) + +#define M_ASN1_I2D_len_SET(a,f) \ + ret+=i2d_ASN1_SET(a,NULL,f,V_ASN1_SET,V_ASN1_UNIVERSAL,IS_SET); + +#define M_ASN1_I2D_len_SET_type(type,a,f) \ + ret+=i2d_ASN1_SET_OF_##type(a,NULL,f,V_ASN1_SET, \ + V_ASN1_UNIVERSAL,IS_SET); + +#define M_ASN1_I2D_len_SEQUENCE(a,f) \ + ret+=i2d_ASN1_SET(a,NULL,f,V_ASN1_SEQUENCE,V_ASN1_UNIVERSAL, \ + IS_SEQUENCE); + +#define M_ASN1_I2D_len_SEQUENCE_type(type,a,f) \ + ret+=i2d_ASN1_SET_OF_##type(a,NULL,f,V_ASN1_SEQUENCE, \ + V_ASN1_UNIVERSAL,IS_SEQUENCE) + +#define M_ASN1_I2D_len_SEQUENCE_opt(a,f) \ + if ((a != NULL) && (sk_num(a) != 0)) \ + M_ASN1_I2D_len_SEQUENCE(a,f); + +#define M_ASN1_I2D_len_SEQUENCE_opt_type(type,a,f) \ + if ((a != NULL) && (sk_##type##_num(a) != 0)) \ + M_ASN1_I2D_len_SEQUENCE_type(type,a,f); + +#define M_ASN1_I2D_len_IMP_SET(a,f,x) \ + ret+=i2d_ASN1_SET(a,NULL,f,x,V_ASN1_CONTEXT_SPECIFIC,IS_SET); + +#define M_ASN1_I2D_len_IMP_SET_type(type,a,f,x) \ + ret+=i2d_ASN1_SET_OF_##type(a,NULL,f,x, \ + V_ASN1_CONTEXT_SPECIFIC,IS_SET); + +#define M_ASN1_I2D_len_IMP_SET_opt(a,f,x) \ + if ((a != NULL) && (sk_num(a) != 0)) \ + ret+=i2d_ASN1_SET(a,NULL,f,x,V_ASN1_CONTEXT_SPECIFIC, \ + IS_SET); + +#define M_ASN1_I2D_len_IMP_SET_opt_type(type,a,f,x) \ + if ((a != NULL) && (sk_##type##_num(a) != 0)) \ + ret+=i2d_ASN1_SET_OF_##type(a,NULL,f,x, \ + V_ASN1_CONTEXT_SPECIFIC,IS_SET); + +#define M_ASN1_I2D_len_IMP_SEQUENCE(a,f,x) \ + ret+=i2d_ASN1_SET(a,NULL,f,x,V_ASN1_CONTEXT_SPECIFIC, \ + IS_SEQUENCE); + +#define M_ASN1_I2D_len_IMP_SEQUENCE_opt(a,f,x) \ + if ((a != NULL) && (sk_num(a) != 0)) \ + ret+=i2d_ASN1_SET(a,NULL,f,x,V_ASN1_CONTEXT_SPECIFIC, \ + IS_SEQUENCE); + +#define M_ASN1_I2D_len_IMP_SEQUENCE_opt_type(type,a,f,x) \ + if ((a != NULL) && (sk_##type##_num(a) != 0)) \ + ret+=i2d_ASN1_SET_OF_##type(a,NULL,f,x, \ + V_ASN1_CONTEXT_SPECIFIC, \ + IS_SEQUENCE); + +#define M_ASN1_I2D_len_EXP_opt(a,f,mtag,v) \ + if (a != NULL)\ + { \ + v=f(a,NULL); \ + ret+=ASN1_object_size(1,v,mtag); \ + } + +#define M_ASN1_I2D_len_EXP_SET_opt(a,f,mtag,tag,v) \ + if ((a != NULL) && (sk_num(a) != 0))\ + { \ + v=i2d_ASN1_SET(a,NULL,f,tag,V_ASN1_UNIVERSAL,IS_SET); \ + ret+=ASN1_object_size(1,v,mtag); \ + } + +#define M_ASN1_I2D_len_EXP_SEQUENCE_opt(a,f,mtag,tag,v) \ + if ((a != NULL) && (sk_num(a) != 0))\ + { \ + v=i2d_ASN1_SET(a,NULL,f,tag,V_ASN1_UNIVERSAL, \ + IS_SEQUENCE); \ + ret+=ASN1_object_size(1,v,mtag); \ + } + +#define M_ASN1_I2D_len_EXP_SEQUENCE_opt_type(type,a,f,mtag,tag,v) \ + if ((a != NULL) && (sk_##type##_num(a) != 0))\ + { \ + v=i2d_ASN1_SET_OF_##type(a,NULL,f,tag, \ + V_ASN1_UNIVERSAL, \ + IS_SEQUENCE); \ + ret+=ASN1_object_size(1,v,mtag); \ + } + +/* Put Macros */ +#define M_ASN1_I2D_put(a,f) f(a,&p) + +#define M_ASN1_I2D_put_IMP_opt(a,f,t) \ + if (a != NULL) \ + { \ + unsigned char *q=p; \ + f(a,&p); \ + *q=(V_ASN1_CONTEXT_SPECIFIC|t|(*q&V_ASN1_CONSTRUCTED));\ + } + +#define M_ASN1_I2D_put_SET(a,f) i2d_ASN1_SET(a,&p,f,V_ASN1_SET,\ + V_ASN1_UNIVERSAL,IS_SET) +#define M_ASN1_I2D_put_SET_type(type,a,f) \ + i2d_ASN1_SET_OF_##type(a,&p,f,V_ASN1_SET,V_ASN1_UNIVERSAL,IS_SET) +#define M_ASN1_I2D_put_IMP_SET(a,f,x) i2d_ASN1_SET(a,&p,f,x,\ + V_ASN1_CONTEXT_SPECIFIC,IS_SET) +#define M_ASN1_I2D_put_IMP_SET_type(type,a,f,x) \ + i2d_ASN1_SET_OF_##type(a,&p,f,x,V_ASN1_CONTEXT_SPECIFIC,IS_SET) +#define M_ASN1_I2D_put_IMP_SEQUENCE(a,f,x) i2d_ASN1_SET(a,&p,f,x,\ + V_ASN1_CONTEXT_SPECIFIC,IS_SEQUENCE) + +#define M_ASN1_I2D_put_SEQUENCE(a,f) i2d_ASN1_SET(a,&p,f,V_ASN1_SEQUENCE,\ + V_ASN1_UNIVERSAL,IS_SEQUENCE) + +#define M_ASN1_I2D_put_SEQUENCE_type(type,a,f) \ + i2d_ASN1_SET_OF_##type(a,&p,f,V_ASN1_SEQUENCE,V_ASN1_UNIVERSAL, \ + IS_SEQUENCE) + +#define M_ASN1_I2D_put_SEQUENCE_opt(a,f) \ + if ((a != NULL) && (sk_num(a) != 0)) \ + M_ASN1_I2D_put_SEQUENCE(a,f); + +#define M_ASN1_I2D_put_IMP_SET_opt(a,f,x) \ + if ((a != NULL) && (sk_num(a) != 0)) \ + { i2d_ASN1_SET(a,&p,f,x,V_ASN1_CONTEXT_SPECIFIC, \ + IS_SET); } + +#define M_ASN1_I2D_put_IMP_SET_opt_type(type,a,f,x) \ + if ((a != NULL) && (sk_##type##_num(a) != 0)) \ + { i2d_ASN1_SET_OF_##type(a,&p,f,x, \ + V_ASN1_CONTEXT_SPECIFIC, \ + IS_SET); } + +#define M_ASN1_I2D_put_IMP_SEQUENCE_opt(a,f,x) \ + if ((a != NULL) && (sk_num(a) != 0)) \ + { i2d_ASN1_SET(a,&p,f,x,V_ASN1_CONTEXT_SPECIFIC, \ + IS_SEQUENCE); } + +#define M_ASN1_I2D_put_IMP_SEQUENCE_opt_type(type,a,f,x) \ + if ((a != NULL) && (sk_##type##_num(a) != 0)) \ + { i2d_ASN1_SET_OF_##type(a,&p,f,x, \ + V_ASN1_CONTEXT_SPECIFIC, \ + IS_SEQUENCE); } + +#define M_ASN1_I2D_put_EXP_opt(a,f,tag,v) \ + if (a != NULL) \ + { \ + ASN1_put_object(&p,1,v,tag,V_ASN1_CONTEXT_SPECIFIC); \ + f(a,&p); \ + } + +#define M_ASN1_I2D_put_EXP_SET_opt(a,f,mtag,tag,v) \ + if ((a != NULL) && (sk_num(a) != 0)) \ + { \ + ASN1_put_object(&p,1,v,mtag,V_ASN1_CONTEXT_SPECIFIC); \ + i2d_ASN1_SET(a,&p,f,tag,V_ASN1_UNIVERSAL,IS_SET); \ + } + +#define M_ASN1_I2D_put_EXP_SEQUENCE_opt(a,f,mtag,tag,v) \ + if ((a != NULL) && (sk_num(a) != 0)) \ + { \ + ASN1_put_object(&p,1,v,mtag,V_ASN1_CONTEXT_SPECIFIC); \ + i2d_ASN1_SET(a,&p,f,tag,V_ASN1_UNIVERSAL,IS_SEQUENCE); \ + } + +#define M_ASN1_I2D_put_EXP_SEQUENCE_opt_type(type,a,f,mtag,tag,v) \ + if ((a != NULL) && (sk_##type##_num(a) != 0)) \ + { \ + ASN1_put_object(&p,1,v,mtag,V_ASN1_CONTEXT_SPECIFIC); \ + i2d_ASN1_SET_OF_##type(a,&p,f,tag,V_ASN1_UNIVERSAL, \ + IS_SEQUENCE); \ + } + +#define M_ASN1_I2D_seq_total() \ + r=ASN1_object_size(1,ret,V_ASN1_SEQUENCE); \ + if (pp == NULL) return(r); \ + p= *pp; \ + ASN1_put_object(&p,1,ret,V_ASN1_SEQUENCE,V_ASN1_UNIVERSAL) + +#define M_ASN1_I2D_INF_seq_start(tag,ctx) \ + *(p++)=(V_ASN1_CONSTRUCTED|(tag)|(ctx)); \ + *(p++)=0x80 + +#define M_ASN1_I2D_INF_seq_end() *(p++)=0x00; *(p++)=0x00 + +#define M_ASN1_I2D_finish() *pp=p; \ + return(r); + +int asn1_GetSequence(ASN1_const_CTX *c, long *length); +void asn1_add_error(const unsigned char *address,int offset); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/asn1t.h b/production/3rdparty/openssl/include/openssl/asn1t.h new file mode 100644 index 00000000..cc0cd1c8 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/asn1t.h @@ -0,0 +1,886 @@ +/* asn1t.h */ +/* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL + * project 2000. + */ +/* ==================================================================== + * Copyright (c) 2000 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +#ifndef HEADER_ASN1T_H +#define HEADER_ASN1T_H + +#include +#include +#include + +#ifdef OPENSSL_BUILD_SHLIBCRYPTO +# undef OPENSSL_EXTERN +# define OPENSSL_EXTERN OPENSSL_EXPORT +#endif + +/* ASN1 template defines, structures and functions */ + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef OPENSSL_EXPORT_VAR_AS_FUNCTION + +/* Macro to obtain ASN1_ADB pointer from a type (only used internally) */ +#define ASN1_ADB_ptr(iptr) ((const ASN1_ADB *)(iptr)) + + +/* Macros for start and end of ASN1_ITEM definition */ + +#define ASN1_ITEM_start(itname) \ + OPENSSL_GLOBAL const ASN1_ITEM itname##_it = { + +#define ASN1_ITEM_end(itname) \ + }; + +#else + +/* Macro to obtain ASN1_ADB pointer from a type (only used internally) */ +#define ASN1_ADB_ptr(iptr) ((const ASN1_ADB *)(iptr())) + + +/* Macros for start and end of ASN1_ITEM definition */ + +#define ASN1_ITEM_start(itname) \ + const ASN1_ITEM * itname##_it(void) \ + { \ + static const ASN1_ITEM local_it = { \ + +#define ASN1_ITEM_end(itname) \ + }; \ + return &local_it; \ + } + +#endif + + +/* Macros to aid ASN1 template writing */ + +#define ASN1_ITEM_TEMPLATE(tname) \ + static const ASN1_TEMPLATE tname##_item_tt + +#define ASN1_ITEM_TEMPLATE_END(tname) \ + ;\ + ASN1_ITEM_start(tname) \ + ASN1_ITYPE_PRIMITIVE,\ + -1,\ + &tname##_item_tt,\ + 0,\ + NULL,\ + 0,\ + #tname \ + ASN1_ITEM_end(tname) + + +/* This is a ASN1 type which just embeds a template */ + +/* This pair helps declare a SEQUENCE. We can do: + * + * ASN1_SEQUENCE(stname) = { + * ... SEQUENCE components ... + * } ASN1_SEQUENCE_END(stname) + * + * This will produce an ASN1_ITEM called stname_it + * for a structure called stname. + * + * If you want the same structure but a different + * name then use: + * + * ASN1_SEQUENCE(itname) = { + * ... SEQUENCE components ... + * } ASN1_SEQUENCE_END_name(stname, itname) + * + * This will create an item called itname_it using + * a structure called stname. + */ + +#define ASN1_SEQUENCE(tname) \ + static const ASN1_TEMPLATE tname##_seq_tt[] + +#define ASN1_SEQUENCE_END(stname) ASN1_SEQUENCE_END_name(stname, stname) + +#define ASN1_SEQUENCE_END_name(stname, tname) \ + ;\ + ASN1_ITEM_start(tname) \ + ASN1_ITYPE_SEQUENCE,\ + V_ASN1_SEQUENCE,\ + tname##_seq_tt,\ + sizeof(tname##_seq_tt) / sizeof(ASN1_TEMPLATE),\ + NULL,\ + sizeof(stname),\ + #stname \ + ASN1_ITEM_end(tname) + +#define ASN1_NDEF_SEQUENCE(tname) \ + ASN1_SEQUENCE(tname) + +#define ASN1_SEQUENCE_cb(tname, cb) \ + static const ASN1_AUX tname##_aux = {NULL, 0, 0, 0, cb, 0}; \ + ASN1_SEQUENCE(tname) + +#define ASN1_BROKEN_SEQUENCE(tname) \ + static const ASN1_AUX tname##_aux = {NULL, ASN1_AFLG_BROKEN, 0, 0, 0, 0}; \ + ASN1_SEQUENCE(tname) + +#define ASN1_SEQUENCE_ref(tname, cb, lck) \ + static const ASN1_AUX tname##_aux = {NULL, ASN1_AFLG_REFCOUNT, offsetof(tname, references), lck, cb, 0}; \ + ASN1_SEQUENCE(tname) + +#define ASN1_SEQUENCE_enc(tname, enc, cb) \ + static const ASN1_AUX tname##_aux = {NULL, ASN1_AFLG_ENCODING, 0, 0, cb, offsetof(tname, enc)}; \ + ASN1_SEQUENCE(tname) + +#define ASN1_NDEF_SEQUENCE_END(tname) \ + ;\ + ASN1_ITEM_start(tname) \ + ASN1_ITYPE_NDEF_SEQUENCE,\ + V_ASN1_SEQUENCE,\ + tname##_seq_tt,\ + sizeof(tname##_seq_tt) / sizeof(ASN1_TEMPLATE),\ + NULL,\ + sizeof(tname),\ + #tname \ + ASN1_ITEM_end(tname) + +#define ASN1_BROKEN_SEQUENCE_END(stname) ASN1_SEQUENCE_END_ref(stname, stname) + +#define ASN1_SEQUENCE_END_enc(stname, tname) ASN1_SEQUENCE_END_ref(stname, tname) + +#define ASN1_SEQUENCE_END_cb(stname, tname) ASN1_SEQUENCE_END_ref(stname, tname) + +#define ASN1_SEQUENCE_END_ref(stname, tname) \ + ;\ + ASN1_ITEM_start(tname) \ + ASN1_ITYPE_SEQUENCE,\ + V_ASN1_SEQUENCE,\ + tname##_seq_tt,\ + sizeof(tname##_seq_tt) / sizeof(ASN1_TEMPLATE),\ + &tname##_aux,\ + sizeof(stname),\ + #stname \ + ASN1_ITEM_end(tname) + + +/* This pair helps declare a CHOICE type. We can do: + * + * ASN1_CHOICE(chname) = { + * ... CHOICE options ... + * ASN1_CHOICE_END(chname) + * + * This will produce an ASN1_ITEM called chname_it + * for a structure called chname. The structure + * definition must look like this: + * typedef struct { + * int type; + * union { + * ASN1_SOMETHING *opt1; + * ASN1_SOMEOTHER *opt2; + * } value; + * } chname; + * + * the name of the selector must be 'type'. + * to use an alternative selector name use the + * ASN1_CHOICE_END_selector() version. + */ + +#define ASN1_CHOICE(tname) \ + static const ASN1_TEMPLATE tname##_ch_tt[] + +#define ASN1_CHOICE_cb(tname, cb) \ + static const ASN1_AUX tname##_aux = {NULL, 0, 0, 0, cb, 0}; \ + ASN1_CHOICE(tname) + +#define ASN1_CHOICE_END(stname) ASN1_CHOICE_END_name(stname, stname) + +#define ASN1_CHOICE_END_name(stname, tname) ASN1_CHOICE_END_selector(stname, tname, type) + +#define ASN1_CHOICE_END_selector(stname, tname, selname) \ + ;\ + ASN1_ITEM_start(tname) \ + ASN1_ITYPE_CHOICE,\ + offsetof(stname,selname) ,\ + tname##_ch_tt,\ + sizeof(tname##_ch_tt) / sizeof(ASN1_TEMPLATE),\ + NULL,\ + sizeof(stname),\ + #stname \ + ASN1_ITEM_end(tname) + +#define ASN1_CHOICE_END_cb(stname, tname, selname) \ + ;\ + ASN1_ITEM_start(tname) \ + ASN1_ITYPE_CHOICE,\ + offsetof(stname,selname) ,\ + tname##_ch_tt,\ + sizeof(tname##_ch_tt) / sizeof(ASN1_TEMPLATE),\ + &tname##_aux,\ + sizeof(stname),\ + #stname \ + ASN1_ITEM_end(tname) + +/* This helps with the template wrapper form of ASN1_ITEM */ + +#define ASN1_EX_TEMPLATE_TYPE(flags, tag, name, type) { \ + (flags), (tag), 0,\ + #name, ASN1_ITEM_ref(type) } + +/* These help with SEQUENCE or CHOICE components */ + +/* used to declare other types */ + +#define ASN1_EX_TYPE(flags, tag, stname, field, type) { \ + (flags), (tag), offsetof(stname, field),\ + #field, ASN1_ITEM_ref(type) } + +/* used when the structure is combined with the parent */ + +#define ASN1_EX_COMBINE(flags, tag, type) { \ + (flags)|ASN1_TFLG_COMBINE, (tag), 0, NULL, ASN1_ITEM_ref(type) } + +/* implicit and explicit helper macros */ + +#define ASN1_IMP_EX(stname, field, type, tag, ex) \ + ASN1_EX_TYPE(ASN1_TFLG_IMPLICIT | ex, tag, stname, field, type) + +#define ASN1_EXP_EX(stname, field, type, tag, ex) \ + ASN1_EX_TYPE(ASN1_TFLG_EXPLICIT | ex, tag, stname, field, type) + +/* Any defined by macros: the field used is in the table itself */ + +#ifndef OPENSSL_EXPORT_VAR_AS_FUNCTION +#define ASN1_ADB_OBJECT(tblname) { ASN1_TFLG_ADB_OID, -1, 0, #tblname, (const ASN1_ITEM *)&(tblname##_adb) } +#define ASN1_ADB_INTEGER(tblname) { ASN1_TFLG_ADB_INT, -1, 0, #tblname, (const ASN1_ITEM *)&(tblname##_adb) } +#else +#define ASN1_ADB_OBJECT(tblname) { ASN1_TFLG_ADB_OID, -1, 0, #tblname, tblname##_adb } +#define ASN1_ADB_INTEGER(tblname) { ASN1_TFLG_ADB_INT, -1, 0, #tblname, tblname##_adb } +#endif +/* Plain simple type */ +#define ASN1_SIMPLE(stname, field, type) ASN1_EX_TYPE(0,0, stname, field, type) + +/* OPTIONAL simple type */ +#define ASN1_OPT(stname, field, type) ASN1_EX_TYPE(ASN1_TFLG_OPTIONAL, 0, stname, field, type) + +/* IMPLICIT tagged simple type */ +#define ASN1_IMP(stname, field, type, tag) ASN1_IMP_EX(stname, field, type, tag, 0) + +/* IMPLICIT tagged OPTIONAL simple type */ +#define ASN1_IMP_OPT(stname, field, type, tag) ASN1_IMP_EX(stname, field, type, tag, ASN1_TFLG_OPTIONAL) + +/* Same as above but EXPLICIT */ + +#define ASN1_EXP(stname, field, type, tag) ASN1_EXP_EX(stname, field, type, tag, 0) +#define ASN1_EXP_OPT(stname, field, type, tag) ASN1_EXP_EX(stname, field, type, tag, ASN1_TFLG_OPTIONAL) + +/* SEQUENCE OF type */ +#define ASN1_SEQUENCE_OF(stname, field, type) \ + ASN1_EX_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, stname, field, type) + +/* OPTIONAL SEQUENCE OF */ +#define ASN1_SEQUENCE_OF_OPT(stname, field, type) \ + ASN1_EX_TYPE(ASN1_TFLG_SEQUENCE_OF|ASN1_TFLG_OPTIONAL, 0, stname, field, type) + +/* Same as above but for SET OF */ + +#define ASN1_SET_OF(stname, field, type) \ + ASN1_EX_TYPE(ASN1_TFLG_SET_OF, 0, stname, field, type) + +#define ASN1_SET_OF_OPT(stname, field, type) \ + ASN1_EX_TYPE(ASN1_TFLG_SET_OF|ASN1_TFLG_OPTIONAL, 0, stname, field, type) + +/* Finally compound types of SEQUENCE, SET, IMPLICIT, EXPLICIT and OPTIONAL */ + +#define ASN1_IMP_SET_OF(stname, field, type, tag) \ + ASN1_IMP_EX(stname, field, type, tag, ASN1_TFLG_SET_OF) + +#define ASN1_EXP_SET_OF(stname, field, type, tag) \ + ASN1_EXP_EX(stname, field, type, tag, ASN1_TFLG_SET_OF) + +#define ASN1_IMP_SET_OF_OPT(stname, field, type, tag) \ + ASN1_IMP_EX(stname, field, type, tag, ASN1_TFLG_SET_OF|ASN1_TFLG_OPTIONAL) + +#define ASN1_EXP_SET_OF_OPT(stname, field, type, tag) \ + ASN1_EXP_EX(stname, field, type, tag, ASN1_TFLG_SET_OF|ASN1_TFLG_OPTIONAL) + +#define ASN1_IMP_SEQUENCE_OF(stname, field, type, tag) \ + ASN1_IMP_EX(stname, field, type, tag, ASN1_TFLG_SEQUENCE_OF) + +#define ASN1_IMP_SEQUENCE_OF_OPT(stname, field, type, tag) \ + ASN1_IMP_EX(stname, field, type, tag, ASN1_TFLG_SEQUENCE_OF|ASN1_TFLG_OPTIONAL) + +#define ASN1_EXP_SEQUENCE_OF(stname, field, type, tag) \ + ASN1_EXP_EX(stname, field, type, tag, ASN1_TFLG_SEQUENCE_OF) + +#define ASN1_EXP_SEQUENCE_OF_OPT(stname, field, type, tag) \ + ASN1_EXP_EX(stname, field, type, tag, ASN1_TFLG_SEQUENCE_OF|ASN1_TFLG_OPTIONAL) + +/* EXPLICIT OPTIONAL using indefinite length constructed form */ +#define ASN1_NDEF_EXP_OPT(stname, field, type, tag) \ + ASN1_EXP_EX(stname, field, type, tag, ASN1_TFLG_OPTIONAL|ASN1_TFLG_NDEF) + +/* Macros for the ASN1_ADB structure */ + +#define ASN1_ADB(name) \ + static const ASN1_ADB_TABLE name##_adbtbl[] + +#ifndef OPENSSL_EXPORT_VAR_AS_FUNCTION + +#define ASN1_ADB_END(name, flags, field, app_table, def, none) \ + ;\ + static const ASN1_ADB name##_adb = {\ + flags,\ + offsetof(name, field),\ + app_table,\ + name##_adbtbl,\ + sizeof(name##_adbtbl) / sizeof(ASN1_ADB_TABLE),\ + def,\ + none\ + } + +#else + +#define ASN1_ADB_END(name, flags, field, app_table, def, none) \ + ;\ + static const ASN1_ITEM *name##_adb(void) \ + { \ + static const ASN1_ADB internal_adb = \ + {\ + flags,\ + offsetof(name, field),\ + app_table,\ + name##_adbtbl,\ + sizeof(name##_adbtbl) / sizeof(ASN1_ADB_TABLE),\ + def,\ + none\ + }; \ + return (const ASN1_ITEM *) &internal_adb; \ + } \ + void dummy_function(void) + +#endif + +#define ADB_ENTRY(val, template) {val, template} + +#define ASN1_ADB_TEMPLATE(name) \ + static const ASN1_TEMPLATE name##_tt + +/* This is the ASN1 template structure that defines + * a wrapper round the actual type. It determines the + * actual position of the field in the value structure, + * various flags such as OPTIONAL and the field name. + */ + +struct ASN1_TEMPLATE_st { +unsigned long flags; /* Various flags */ +long tag; /* tag, not used if no tagging */ +unsigned long offset; /* Offset of this field in structure */ +#ifndef NO_ASN1_FIELD_NAMES +const char *field_name; /* Field name */ +#endif +ASN1_ITEM_EXP *item; /* Relevant ASN1_ITEM or ASN1_ADB */ +}; + +/* Macro to extract ASN1_ITEM and ASN1_ADB pointer from ASN1_TEMPLATE */ + +#define ASN1_TEMPLATE_item(t) (t->item_ptr) +#define ASN1_TEMPLATE_adb(t) (t->item_ptr) + +typedef struct ASN1_ADB_TABLE_st ASN1_ADB_TABLE; +typedef struct ASN1_ADB_st ASN1_ADB; + +struct ASN1_ADB_st { + unsigned long flags; /* Various flags */ + unsigned long offset; /* Offset of selector field */ + STACK_OF(ASN1_ADB_TABLE) **app_items; /* Application defined items */ + const ASN1_ADB_TABLE *tbl; /* Table of possible types */ + long tblcount; /* Number of entries in tbl */ + const ASN1_TEMPLATE *default_tt; /* Type to use if no match */ + const ASN1_TEMPLATE *null_tt; /* Type to use if selector is NULL */ +}; + +struct ASN1_ADB_TABLE_st { + long value; /* NID for an object or value for an int */ + const ASN1_TEMPLATE tt; /* item for this value */ +}; + +/* template flags */ + +/* Field is optional */ +#define ASN1_TFLG_OPTIONAL (0x1) + +/* Field is a SET OF */ +#define ASN1_TFLG_SET_OF (0x1 << 1) + +/* Field is a SEQUENCE OF */ +#define ASN1_TFLG_SEQUENCE_OF (0x2 << 1) + +/* Special case: this refers to a SET OF that + * will be sorted into DER order when encoded *and* + * the corresponding STACK will be modified to match + * the new order. + */ +#define ASN1_TFLG_SET_ORDER (0x3 << 1) + +/* Mask for SET OF or SEQUENCE OF */ +#define ASN1_TFLG_SK_MASK (0x3 << 1) + +/* These flags mean the tag should be taken from the + * tag field. If EXPLICIT then the underlying type + * is used for the inner tag. + */ + +/* IMPLICIT tagging */ +#define ASN1_TFLG_IMPTAG (0x1 << 3) + + +/* EXPLICIT tagging, inner tag from underlying type */ +#define ASN1_TFLG_EXPTAG (0x2 << 3) + +#define ASN1_TFLG_TAG_MASK (0x3 << 3) + +/* context specific IMPLICIT */ +#define ASN1_TFLG_IMPLICIT ASN1_TFLG_IMPTAG|ASN1_TFLG_CONTEXT + +/* context specific EXPLICIT */ +#define ASN1_TFLG_EXPLICIT ASN1_TFLG_EXPTAG|ASN1_TFLG_CONTEXT + +/* If tagging is in force these determine the + * type of tag to use. Otherwise the tag is + * determined by the underlying type. These + * values reflect the actual octet format. + */ + +/* Universal tag */ +#define ASN1_TFLG_UNIVERSAL (0x0<<6) +/* Application tag */ +#define ASN1_TFLG_APPLICATION (0x1<<6) +/* Context specific tag */ +#define ASN1_TFLG_CONTEXT (0x2<<6) +/* Private tag */ +#define ASN1_TFLG_PRIVATE (0x3<<6) + +#define ASN1_TFLG_TAG_CLASS (0x3<<6) + +/* These are for ANY DEFINED BY type. In this case + * the 'item' field points to an ASN1_ADB structure + * which contains a table of values to decode the + * relevant type + */ + +#define ASN1_TFLG_ADB_MASK (0x3<<8) + +#define ASN1_TFLG_ADB_OID (0x1<<8) + +#define ASN1_TFLG_ADB_INT (0x1<<9) + +/* This flag means a parent structure is passed + * instead of the field: this is useful is a + * SEQUENCE is being combined with a CHOICE for + * example. Since this means the structure and + * item name will differ we need to use the + * ASN1_CHOICE_END_name() macro for example. + */ + +#define ASN1_TFLG_COMBINE (0x1<<10) + +/* This flag when present in a SEQUENCE OF, SET OF + * or EXPLICIT causes indefinite length constructed + * encoding to be used if required. + */ + +#define ASN1_TFLG_NDEF (0x1<<11) + +/* This is the actual ASN1 item itself */ + +struct ASN1_ITEM_st { +char itype; /* The item type, primitive, SEQUENCE, CHOICE or extern */ +long utype; /* underlying type */ +const ASN1_TEMPLATE *templates; /* If SEQUENCE or CHOICE this contains the contents */ +long tcount; /* Number of templates if SEQUENCE or CHOICE */ +const void *funcs; /* functions that handle this type */ +long size; /* Structure size (usually)*/ +#ifndef NO_ASN1_FIELD_NAMES +const char *sname; /* Structure name */ +#endif +}; + +/* These are values for the itype field and + * determine how the type is interpreted. + * + * For PRIMITIVE types the underlying type + * determines the behaviour if items is NULL. + * + * Otherwise templates must contain a single + * template and the type is treated in the + * same way as the type specified in the template. + * + * For SEQUENCE types the templates field points + * to the members, the size field is the + * structure size. + * + * For CHOICE types the templates field points + * to each possible member (typically a union) + * and the 'size' field is the offset of the + * selector. + * + * The 'funcs' field is used for application + * specific functions. + * + * For COMPAT types the funcs field gives a + * set of functions that handle this type, this + * supports the old d2i, i2d convention. + * + * The EXTERN type uses a new style d2i/i2d. + * The new style should be used where possible + * because it avoids things like the d2i IMPLICIT + * hack. + * + * MSTRING is a multiple string type, it is used + * for a CHOICE of character strings where the + * actual strings all occupy an ASN1_STRING + * structure. In this case the 'utype' field + * has a special meaning, it is used as a mask + * of acceptable types using the B_ASN1 constants. + * + * NDEF_SEQUENCE is the same as SEQUENCE except + * that it will use indefinite length constructed + * encoding if requested. + * + */ + +#define ASN1_ITYPE_PRIMITIVE 0x0 + +#define ASN1_ITYPE_SEQUENCE 0x1 + +#define ASN1_ITYPE_CHOICE 0x2 + +#define ASN1_ITYPE_COMPAT 0x3 + +#define ASN1_ITYPE_EXTERN 0x4 + +#define ASN1_ITYPE_MSTRING 0x5 + +#define ASN1_ITYPE_NDEF_SEQUENCE 0x6 + +/* Cache for ASN1 tag and length, so we + * don't keep re-reading it for things + * like CHOICE + */ + +struct ASN1_TLC_st{ + char valid; /* Values below are valid */ + int ret; /* return value */ + long plen; /* length */ + int ptag; /* class value */ + int pclass; /* class value */ + int hdrlen; /* header length */ +}; + +/* Typedefs for ASN1 function pointers */ + +typedef ASN1_VALUE * ASN1_new_func(void); +typedef void ASN1_free_func(ASN1_VALUE *a); +typedef ASN1_VALUE * ASN1_d2i_func(ASN1_VALUE **a, const unsigned char ** in, long length); +typedef int ASN1_i2d_func(ASN1_VALUE * a, unsigned char **in); + +typedef int ASN1_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, const ASN1_ITEM *it, + int tag, int aclass, char opt, ASN1_TLC *ctx); + +typedef int ASN1_ex_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_ITEM *it, int tag, int aclass); +typedef int ASN1_ex_new_func(ASN1_VALUE **pval, const ASN1_ITEM *it); +typedef void ASN1_ex_free_func(ASN1_VALUE **pval, const ASN1_ITEM *it); + +typedef int ASN1_primitive_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype, const ASN1_ITEM *it); +typedef int ASN1_primitive_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, int utype, char *free_cont, const ASN1_ITEM *it); + +typedef struct ASN1_COMPAT_FUNCS_st { + ASN1_new_func *asn1_new; + ASN1_free_func *asn1_free; + ASN1_d2i_func *asn1_d2i; + ASN1_i2d_func *asn1_i2d; +} ASN1_COMPAT_FUNCS; + +typedef struct ASN1_EXTERN_FUNCS_st { + void *app_data; + ASN1_ex_new_func *asn1_ex_new; + ASN1_ex_free_func *asn1_ex_free; + ASN1_ex_free_func *asn1_ex_clear; + ASN1_ex_d2i *asn1_ex_d2i; + ASN1_ex_i2d *asn1_ex_i2d; +} ASN1_EXTERN_FUNCS; + +typedef struct ASN1_PRIMITIVE_FUNCS_st { + void *app_data; + unsigned long flags; + ASN1_ex_new_func *prim_new; + ASN1_ex_free_func *prim_free; + ASN1_ex_free_func *prim_clear; + ASN1_primitive_c2i *prim_c2i; + ASN1_primitive_i2c *prim_i2c; +} ASN1_PRIMITIVE_FUNCS; + +/* This is the ASN1_AUX structure: it handles various + * miscellaneous requirements. For example the use of + * reference counts and an informational callback. + * + * The "informational callback" is called at various + * points during the ASN1 encoding and decoding. It can + * be used to provide minor customisation of the structures + * used. This is most useful where the supplied routines + * *almost* do the right thing but need some extra help + * at a few points. If the callback returns zero then + * it is assumed a fatal error has occurred and the + * main operation should be abandoned. + * + * If major changes in the default behaviour are required + * then an external type is more appropriate. + */ + +typedef int ASN1_aux_cb(int operation, ASN1_VALUE **in, const ASN1_ITEM *it); + +typedef struct ASN1_AUX_st { + void *app_data; + int flags; + int ref_offset; /* Offset of reference value */ + int ref_lock; /* Lock type to use */ + ASN1_aux_cb *asn1_cb; + int enc_offset; /* Offset of ASN1_ENCODING structure */ +} ASN1_AUX; + +/* Flags in ASN1_AUX */ + +/* Use a reference count */ +#define ASN1_AFLG_REFCOUNT 1 +/* Save the encoding of structure (useful for signatures) */ +#define ASN1_AFLG_ENCODING 2 +/* The Sequence length is invalid */ +#define ASN1_AFLG_BROKEN 4 + +/* operation values for asn1_cb */ + +#define ASN1_OP_NEW_PRE 0 +#define ASN1_OP_NEW_POST 1 +#define ASN1_OP_FREE_PRE 2 +#define ASN1_OP_FREE_POST 3 +#define ASN1_OP_D2I_PRE 4 +#define ASN1_OP_D2I_POST 5 +#define ASN1_OP_I2D_PRE 6 +#define ASN1_OP_I2D_POST 7 + +/* Macro to implement a primitive type */ +#define IMPLEMENT_ASN1_TYPE(stname) IMPLEMENT_ASN1_TYPE_ex(stname, stname, 0) +#define IMPLEMENT_ASN1_TYPE_ex(itname, vname, ex) \ + ASN1_ITEM_start(itname) \ + ASN1_ITYPE_PRIMITIVE, V_##vname, NULL, 0, NULL, ex, #itname \ + ASN1_ITEM_end(itname) + +/* Macro to implement a multi string type */ +#define IMPLEMENT_ASN1_MSTRING(itname, mask) \ + ASN1_ITEM_start(itname) \ + ASN1_ITYPE_MSTRING, mask, NULL, 0, NULL, sizeof(ASN1_STRING), #itname \ + ASN1_ITEM_end(itname) + +/* Macro to implement an ASN1_ITEM in terms of old style funcs */ + +#define IMPLEMENT_COMPAT_ASN1(sname) IMPLEMENT_COMPAT_ASN1_type(sname, V_ASN1_SEQUENCE) + +#define IMPLEMENT_COMPAT_ASN1_type(sname, tag) \ + static const ASN1_COMPAT_FUNCS sname##_ff = { \ + (ASN1_new_func *)sname##_new, \ + (ASN1_free_func *)sname##_free, \ + (ASN1_d2i_func *)d2i_##sname, \ + (ASN1_i2d_func *)i2d_##sname, \ + }; \ + ASN1_ITEM_start(sname) \ + ASN1_ITYPE_COMPAT, \ + tag, \ + NULL, \ + 0, \ + &sname##_ff, \ + 0, \ + #sname \ + ASN1_ITEM_end(sname) + +#define IMPLEMENT_EXTERN_ASN1(sname, tag, fptrs) \ + ASN1_ITEM_start(sname) \ + ASN1_ITYPE_EXTERN, \ + tag, \ + NULL, \ + 0, \ + &fptrs, \ + 0, \ + #sname \ + ASN1_ITEM_end(sname) + +/* Macro to implement standard functions in terms of ASN1_ITEM structures */ + +#define IMPLEMENT_ASN1_FUNCTIONS(stname) IMPLEMENT_ASN1_FUNCTIONS_fname(stname, stname, stname) + +#define IMPLEMENT_ASN1_FUNCTIONS_name(stname, itname) IMPLEMENT_ASN1_FUNCTIONS_fname(stname, itname, itname) + +#define IMPLEMENT_ASN1_FUNCTIONS_ENCODE_name(stname, itname) \ + IMPLEMENT_ASN1_FUNCTIONS_ENCODE_fname(stname, itname, itname) + +#define IMPLEMENT_ASN1_ALLOC_FUNCTIONS(stname) \ + IMPLEMENT_ASN1_ALLOC_FUNCTIONS_fname(stname, stname, stname) + +#define IMPLEMENT_ASN1_ALLOC_FUNCTIONS_fname(stname, itname, fname) \ + stname *fname##_new(void) \ + { \ + return (stname *)ASN1_item_new(ASN1_ITEM_rptr(itname)); \ + } \ + void fname##_free(stname *a) \ + { \ + ASN1_item_free((ASN1_VALUE *)a, ASN1_ITEM_rptr(itname)); \ + } + +#define IMPLEMENT_ASN1_FUNCTIONS_fname(stname, itname, fname) \ + IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(stname, itname, fname) \ + IMPLEMENT_ASN1_ALLOC_FUNCTIONS_fname(stname, itname, fname) + +#define IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(stname, itname, fname) \ + stname *d2i_##fname(stname **a, const unsigned char **in, long len) \ + { \ + return (stname *)ASN1_item_d2i((ASN1_VALUE **)a, in, len, ASN1_ITEM_rptr(itname));\ + } \ + int i2d_##fname(stname *a, unsigned char **out) \ + { \ + return ASN1_item_i2d((ASN1_VALUE *)a, out, ASN1_ITEM_rptr(itname));\ + } + +#define IMPLEMENT_ASN1_NDEF_FUNCTION(stname) \ + int i2d_##stname##_NDEF(stname *a, unsigned char **out) \ + { \ + return ASN1_item_ndef_i2d((ASN1_VALUE *)a, out, ASN1_ITEM_rptr(stname));\ + } + +/* This includes evil casts to remove const: they will go away when full + * ASN1 constification is done. + */ +#define IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(stname, itname, fname) \ + stname *d2i_##fname(stname **a, const unsigned char **in, long len) \ + { \ + return (stname *)ASN1_item_d2i((ASN1_VALUE **)a, in, len, ASN1_ITEM_rptr(itname));\ + } \ + int i2d_##fname(const stname *a, unsigned char **out) \ + { \ + return ASN1_item_i2d((ASN1_VALUE *)a, out, ASN1_ITEM_rptr(itname));\ + } + +#define IMPLEMENT_ASN1_DUP_FUNCTION(stname) \ + stname * stname##_dup(stname *x) \ + { \ + return ASN1_item_dup(ASN1_ITEM_rptr(stname), x); \ + } + +#define IMPLEMENT_ASN1_FUNCTIONS_const(name) \ + IMPLEMENT_ASN1_FUNCTIONS_const_fname(name, name, name) + +#define IMPLEMENT_ASN1_FUNCTIONS_const_fname(stname, itname, fname) \ + IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(stname, itname, fname) \ + IMPLEMENT_ASN1_ALLOC_FUNCTIONS_fname(stname, itname, fname) + +/* external definitions for primitive types */ + +DECLARE_ASN1_ITEM(ASN1_BOOLEAN) +DECLARE_ASN1_ITEM(ASN1_TBOOLEAN) +DECLARE_ASN1_ITEM(ASN1_FBOOLEAN) +DECLARE_ASN1_ITEM(ASN1_SEQUENCE) +DECLARE_ASN1_ITEM(CBIGNUM) +DECLARE_ASN1_ITEM(BIGNUM) +DECLARE_ASN1_ITEM(LONG) +DECLARE_ASN1_ITEM(ZLONG) + +DECLARE_STACK_OF(ASN1_VALUE) + +/* Functions used internally by the ASN1 code */ + +int ASN1_item_ex_new(ASN1_VALUE **pval, const ASN1_ITEM *it); +void ASN1_item_ex_free(ASN1_VALUE **pval, const ASN1_ITEM *it); +int ASN1_template_new(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt); +int ASN1_primitive_new(ASN1_VALUE **pval, const ASN1_ITEM *it); + +void ASN1_template_free(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt); +int ASN1_template_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, const ASN1_TEMPLATE *tt); +int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, const ASN1_ITEM *it, + int tag, int aclass, char opt, ASN1_TLC *ctx); + +int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_ITEM *it, int tag, int aclass); +int ASN1_template_i2d(ASN1_VALUE **pval, unsigned char **out, const ASN1_TEMPLATE *tt); +void ASN1_primitive_free(ASN1_VALUE **pval, const ASN1_ITEM *it); + +int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype, const ASN1_ITEM *it); +int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, int utype, char *free_cont, const ASN1_ITEM *it); + +int asn1_get_choice_selector(ASN1_VALUE **pval, const ASN1_ITEM *it); +int asn1_set_choice_selector(ASN1_VALUE **pval, int value, const ASN1_ITEM *it); + +ASN1_VALUE ** asn1_get_field_ptr(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt); + +const ASN1_TEMPLATE *asn1_do_adb(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt, int nullerr); + +int asn1_do_lock(ASN1_VALUE **pval, int op, const ASN1_ITEM *it); + +void asn1_enc_init(ASN1_VALUE **pval, const ASN1_ITEM *it); +void asn1_enc_free(ASN1_VALUE **pval, const ASN1_ITEM *it); +int asn1_enc_restore(int *len, unsigned char **out, ASN1_VALUE **pval, const ASN1_ITEM *it); +int asn1_enc_save(ASN1_VALUE **pval, const unsigned char *in, int inlen, const ASN1_ITEM *it); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/bio.h b/production/3rdparty/openssl/include/openssl/bio.h new file mode 100644 index 00000000..07333cf0 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/bio.h @@ -0,0 +1,770 @@ +/* crypto/bio/bio.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_BIO_H +#define HEADER_BIO_H + +#include + +#ifndef OPENSSL_NO_FP_API +# include +#endif +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* These are the 'types' of BIOs */ +#define BIO_TYPE_NONE 0 +#define BIO_TYPE_MEM (1|0x0400) +#define BIO_TYPE_FILE (2|0x0400) + +#define BIO_TYPE_FD (4|0x0400|0x0100) +#define BIO_TYPE_SOCKET (5|0x0400|0x0100) +#define BIO_TYPE_NULL (6|0x0400) +#define BIO_TYPE_SSL (7|0x0200) +#define BIO_TYPE_MD (8|0x0200) /* passive filter */ +#define BIO_TYPE_BUFFER (9|0x0200) /* filter */ +#define BIO_TYPE_CIPHER (10|0x0200) /* filter */ +#define BIO_TYPE_BASE64 (11|0x0200) /* filter */ +#define BIO_TYPE_CONNECT (12|0x0400|0x0100) /* socket - connect */ +#define BIO_TYPE_ACCEPT (13|0x0400|0x0100) /* socket for accept */ +#define BIO_TYPE_PROXY_CLIENT (14|0x0200) /* client proxy BIO */ +#define BIO_TYPE_PROXY_SERVER (15|0x0200) /* server proxy BIO */ +#define BIO_TYPE_NBIO_TEST (16|0x0200) /* server proxy BIO */ +#define BIO_TYPE_NULL_FILTER (17|0x0200) +#define BIO_TYPE_BER (18|0x0200) /* BER -> bin filter */ +#define BIO_TYPE_BIO (19|0x0400) /* (half a) BIO pair */ +#define BIO_TYPE_LINEBUFFER (20|0x0200) /* filter */ +#define BIO_TYPE_DGRAM (21|0x0400|0x0100) + +#define BIO_TYPE_DESCRIPTOR 0x0100 /* socket, fd, connect or accept */ +#define BIO_TYPE_FILTER 0x0200 +#define BIO_TYPE_SOURCE_SINK 0x0400 + +/* BIO_FILENAME_READ|BIO_CLOSE to open or close on free. + * BIO_set_fp(in,stdin,BIO_NOCLOSE); */ +#define BIO_NOCLOSE 0x00 +#define BIO_CLOSE 0x01 + +/* These are used in the following macros and are passed to + * BIO_ctrl() */ +#define BIO_CTRL_RESET 1 /* opt - rewind/zero etc */ +#define BIO_CTRL_EOF 2 /* opt - are we at the eof */ +#define BIO_CTRL_INFO 3 /* opt - extra tit-bits */ +#define BIO_CTRL_SET 4 /* man - set the 'IO' type */ +#define BIO_CTRL_GET 5 /* man - get the 'IO' type */ +#define BIO_CTRL_PUSH 6 /* opt - internal, used to signify change */ +#define BIO_CTRL_POP 7 /* opt - internal, used to signify change */ +#define BIO_CTRL_GET_CLOSE 8 /* man - set the 'close' on free */ +#define BIO_CTRL_SET_CLOSE 9 /* man - set the 'close' on free */ +#define BIO_CTRL_PENDING 10 /* opt - is their more data buffered */ +#define BIO_CTRL_FLUSH 11 /* opt - 'flush' buffered output */ +#define BIO_CTRL_DUP 12 /* man - extra stuff for 'duped' BIO */ +#define BIO_CTRL_WPENDING 13 /* opt - number of bytes still to write */ +/* callback is int cb(BIO *bio,state,ret); */ +#define BIO_CTRL_SET_CALLBACK 14 /* opt - set callback function */ +#define BIO_CTRL_GET_CALLBACK 15 /* opt - set callback function */ + +#define BIO_CTRL_SET_FILENAME 30 /* BIO_s_file special */ + +/* dgram BIO stuff */ +#define BIO_CTRL_DGRAM_CONNECT 31 /* BIO dgram special */ +#define BIO_CTRL_DGRAM_SET_CONNECTED 32 /* allow for an externally + * connected socket to be + * passed in */ +#define BIO_CTRL_DGRAM_SET_RECV_TIMEOUT 33 /* setsockopt, essentially */ +#define BIO_CTRL_DGRAM_GET_RECV_TIMEOUT 34 /* getsockopt, essentially */ +#define BIO_CTRL_DGRAM_SET_SEND_TIMEOUT 35 /* setsockopt, essentially */ +#define BIO_CTRL_DGRAM_GET_SEND_TIMEOUT 36 /* getsockopt, essentially */ + +#define BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP 37 /* flag whether the last */ +#define BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP 38 /* I/O operation tiemd out */ + +/* #ifdef IP_MTU_DISCOVER */ +#define BIO_CTRL_DGRAM_MTU_DISCOVER 39 /* set DF bit on egress packets */ +/* #endif */ + +#define BIO_CTRL_DGRAM_QUERY_MTU 40 /* as kernel for current MTU */ +#define BIO_CTRL_DGRAM_GET_MTU 41 /* get cached value for MTU */ +#define BIO_CTRL_DGRAM_SET_MTU 42 /* set cached value for + * MTU. want to use this + * if asking the kernel + * fails */ + +#define BIO_CTRL_DGRAM_MTU_EXCEEDED 43 /* check whether the MTU + * was exceed in the + * previous write + * operation */ + +#define BIO_CTRL_DGRAM_SET_PEER 44 /* Destination for the data */ + + +/* modifiers */ +#define BIO_FP_READ 0x02 +#define BIO_FP_WRITE 0x04 +#define BIO_FP_APPEND 0x08 +#define BIO_FP_TEXT 0x10 + +#define BIO_FLAGS_READ 0x01 +#define BIO_FLAGS_WRITE 0x02 +#define BIO_FLAGS_IO_SPECIAL 0x04 +#define BIO_FLAGS_RWS (BIO_FLAGS_READ|BIO_FLAGS_WRITE|BIO_FLAGS_IO_SPECIAL) +#define BIO_FLAGS_SHOULD_RETRY 0x08 +#ifndef BIO_FLAGS_UPLINK +/* "UPLINK" flag denotes file descriptors provided by application. + It defaults to 0, as most platforms don't require UPLINK interface. */ +#define BIO_FLAGS_UPLINK 0 +#endif + +/* Used in BIO_gethostbyname() */ +#define BIO_GHBN_CTRL_HITS 1 +#define BIO_GHBN_CTRL_MISSES 2 +#define BIO_GHBN_CTRL_CACHE_SIZE 3 +#define BIO_GHBN_CTRL_GET_ENTRY 4 +#define BIO_GHBN_CTRL_FLUSH 5 + +/* Mostly used in the SSL BIO */ +/* Not used anymore + * #define BIO_FLAGS_PROTOCOL_DELAYED_READ 0x10 + * #define BIO_FLAGS_PROTOCOL_DELAYED_WRITE 0x20 + * #define BIO_FLAGS_PROTOCOL_STARTUP 0x40 + */ + +#define BIO_FLAGS_BASE64_NO_NL 0x100 + +/* This is used with memory BIOs: it means we shouldn't free up or change the + * data in any way. + */ +#define BIO_FLAGS_MEM_RDONLY 0x200 + +#define BIO_set_flags(b,f) ((b)->flags|=(f)) +#define BIO_get_flags(b) ((b)->flags) +#define BIO_set_retry_special(b) \ + ((b)->flags|=(BIO_FLAGS_IO_SPECIAL|BIO_FLAGS_SHOULD_RETRY)) +#define BIO_set_retry_read(b) \ + ((b)->flags|=(BIO_FLAGS_READ|BIO_FLAGS_SHOULD_RETRY)) +#define BIO_set_retry_write(b) \ + ((b)->flags|=(BIO_FLAGS_WRITE|BIO_FLAGS_SHOULD_RETRY)) + +/* These are normally used internally in BIOs */ +#define BIO_clear_flags(b,f) ((b)->flags&= ~(f)) +#define BIO_clear_retry_flags(b) \ + ((b)->flags&= ~(BIO_FLAGS_RWS|BIO_FLAGS_SHOULD_RETRY)) +#define BIO_get_retry_flags(b) \ + ((b)->flags&(BIO_FLAGS_RWS|BIO_FLAGS_SHOULD_RETRY)) + +/* These should be used by the application to tell why we should retry */ +#define BIO_should_read(a) ((a)->flags & BIO_FLAGS_READ) +#define BIO_should_write(a) ((a)->flags & BIO_FLAGS_WRITE) +#define BIO_should_io_special(a) ((a)->flags & BIO_FLAGS_IO_SPECIAL) +#define BIO_retry_type(a) ((a)->flags & BIO_FLAGS_RWS) +#define BIO_should_retry(a) ((a)->flags & BIO_FLAGS_SHOULD_RETRY) + +/* The next three are used in conjunction with the + * BIO_should_io_special() condition. After this returns true, + * BIO *BIO_get_retry_BIO(BIO *bio, int *reason); will walk the BIO + * stack and return the 'reason' for the special and the offending BIO. + * Given a BIO, BIO_get_retry_reason(bio) will return the code. */ +/* Returned from the SSL bio when the certificate retrieval code had an error */ +#define BIO_RR_SSL_X509_LOOKUP 0x01 +/* Returned from the connect BIO when a connect would have blocked */ +#define BIO_RR_CONNECT 0x02 +/* Returned from the accept BIO when an accept would have blocked */ +#define BIO_RR_ACCEPT 0x03 + +/* These are passed by the BIO callback */ +#define BIO_CB_FREE 0x01 +#define BIO_CB_READ 0x02 +#define BIO_CB_WRITE 0x03 +#define BIO_CB_PUTS 0x04 +#define BIO_CB_GETS 0x05 +#define BIO_CB_CTRL 0x06 + +/* The callback is called before and after the underling operation, + * The BIO_CB_RETURN flag indicates if it is after the call */ +#define BIO_CB_RETURN 0x80 +#define BIO_CB_return(a) ((a)|BIO_CB_RETURN)) +#define BIO_cb_pre(a) (!((a)&BIO_CB_RETURN)) +#define BIO_cb_post(a) ((a)&BIO_CB_RETURN) + +#define BIO_set_callback(b,cb) ((b)->callback=(cb)) +#define BIO_set_callback_arg(b,arg) ((b)->cb_arg=(char *)(arg)) +#define BIO_get_callback_arg(b) ((b)->cb_arg) +#define BIO_get_callback(b) ((b)->callback) +#define BIO_method_name(b) ((b)->method->name) +#define BIO_method_type(b) ((b)->method->type) + +typedef struct bio_st BIO; + +typedef void bio_info_cb(struct bio_st *, int, const char *, int, long, long); + +#ifndef OPENSSL_SYS_WIN16 +typedef struct bio_method_st + { + int type; + const char *name; + int (*bwrite)(BIO *, const char *, int); + int (*bread)(BIO *, char *, int); + int (*bputs)(BIO *, const char *); + int (*bgets)(BIO *, char *, int); + long (*ctrl)(BIO *, int, long, void *); + int (*create)(BIO *); + int (*destroy)(BIO *); + long (*callback_ctrl)(BIO *, int, bio_info_cb *); + } BIO_METHOD; +#else +typedef struct bio_method_st + { + int type; + const char *name; + int (_far *bwrite)(); + int (_far *bread)(); + int (_far *bputs)(); + int (_far *bgets)(); + long (_far *ctrl)(); + int (_far *create)(); + int (_far *destroy)(); + long (_far *callback_ctrl)(); + } BIO_METHOD; +#endif + +struct bio_st + { + BIO_METHOD *method; + /* bio, mode, argp, argi, argl, ret */ + long (*callback)(struct bio_st *,int,const char *,int, long,long); + char *cb_arg; /* first argument for the callback */ + + int init; + int shutdown; + int flags; /* extra storage */ + int retry_reason; + int num; + void *ptr; + struct bio_st *next_bio; /* used by filter BIOs */ + struct bio_st *prev_bio; /* used by filter BIOs */ + int references; + unsigned long num_read; + unsigned long num_write; + + CRYPTO_EX_DATA ex_data; + }; + +DECLARE_STACK_OF(BIO) + +typedef struct bio_f_buffer_ctx_struct + { + /* BIO *bio; */ /* this is now in the BIO struct */ + int ibuf_size; /* how big is the input buffer */ + int obuf_size; /* how big is the output buffer */ + + char *ibuf; /* the char array */ + int ibuf_len; /* how many bytes are in it */ + int ibuf_off; /* write/read offset */ + + char *obuf; /* the char array */ + int obuf_len; /* how many bytes are in it */ + int obuf_off; /* write/read offset */ + } BIO_F_BUFFER_CTX; + +/* connect BIO stuff */ +#define BIO_CONN_S_BEFORE 1 +#define BIO_CONN_S_GET_IP 2 +#define BIO_CONN_S_GET_PORT 3 +#define BIO_CONN_S_CREATE_SOCKET 4 +#define BIO_CONN_S_CONNECT 5 +#define BIO_CONN_S_OK 6 +#define BIO_CONN_S_BLOCKED_CONNECT 7 +#define BIO_CONN_S_NBIO 8 +/*#define BIO_CONN_get_param_hostname BIO_ctrl */ + +#define BIO_C_SET_CONNECT 100 +#define BIO_C_DO_STATE_MACHINE 101 +#define BIO_C_SET_NBIO 102 +#define BIO_C_SET_PROXY_PARAM 103 +#define BIO_C_SET_FD 104 +#define BIO_C_GET_FD 105 +#define BIO_C_SET_FILE_PTR 106 +#define BIO_C_GET_FILE_PTR 107 +#define BIO_C_SET_FILENAME 108 +#define BIO_C_SET_SSL 109 +#define BIO_C_GET_SSL 110 +#define BIO_C_SET_MD 111 +#define BIO_C_GET_MD 112 +#define BIO_C_GET_CIPHER_STATUS 113 +#define BIO_C_SET_BUF_MEM 114 +#define BIO_C_GET_BUF_MEM_PTR 115 +#define BIO_C_GET_BUFF_NUM_LINES 116 +#define BIO_C_SET_BUFF_SIZE 117 +#define BIO_C_SET_ACCEPT 118 +#define BIO_C_SSL_MODE 119 +#define BIO_C_GET_MD_CTX 120 +#define BIO_C_GET_PROXY_PARAM 121 +#define BIO_C_SET_BUFF_READ_DATA 122 /* data to read first */ +#define BIO_C_GET_CONNECT 123 +#define BIO_C_GET_ACCEPT 124 +#define BIO_C_SET_SSL_RENEGOTIATE_BYTES 125 +#define BIO_C_GET_SSL_NUM_RENEGOTIATES 126 +#define BIO_C_SET_SSL_RENEGOTIATE_TIMEOUT 127 +#define BIO_C_FILE_SEEK 128 +#define BIO_C_GET_CIPHER_CTX 129 +#define BIO_C_SET_BUF_MEM_EOF_RETURN 130/*return end of input value*/ +#define BIO_C_SET_BIND_MODE 131 +#define BIO_C_GET_BIND_MODE 132 +#define BIO_C_FILE_TELL 133 +#define BIO_C_GET_SOCKS 134 +#define BIO_C_SET_SOCKS 135 + +#define BIO_C_SET_WRITE_BUF_SIZE 136/* for BIO_s_bio */ +#define BIO_C_GET_WRITE_BUF_SIZE 137 +#define BIO_C_MAKE_BIO_PAIR 138 +#define BIO_C_DESTROY_BIO_PAIR 139 +#define BIO_C_GET_WRITE_GUARANTEE 140 +#define BIO_C_GET_READ_REQUEST 141 +#define BIO_C_SHUTDOWN_WR 142 +#define BIO_C_NREAD0 143 +#define BIO_C_NREAD 144 +#define BIO_C_NWRITE0 145 +#define BIO_C_NWRITE 146 +#define BIO_C_RESET_READ_REQUEST 147 + + +#define BIO_set_app_data(s,arg) BIO_set_ex_data(s,0,arg) +#define BIO_get_app_data(s) BIO_get_ex_data(s,0) + +/* BIO_s_connect() and BIO_s_socks4a_connect() */ +#define BIO_set_conn_hostname(b,name) BIO_ctrl(b,BIO_C_SET_CONNECT,0,(char *)name) +#define BIO_set_conn_port(b,port) BIO_ctrl(b,BIO_C_SET_CONNECT,1,(char *)port) +#define BIO_set_conn_ip(b,ip) BIO_ctrl(b,BIO_C_SET_CONNECT,2,(char *)ip) +#define BIO_set_conn_int_port(b,port) BIO_ctrl(b,BIO_C_SET_CONNECT,3,(char *)port) +#define BIO_get_conn_hostname(b) BIO_ptr_ctrl(b,BIO_C_GET_CONNECT,0) +#define BIO_get_conn_port(b) BIO_ptr_ctrl(b,BIO_C_GET_CONNECT,1) +#define BIO_get_conn_ip(b) BIO_ptr_ctrl(b,BIO_C_GET_CONNECT,2) +#define BIO_get_conn_int_port(b) BIO_int_ctrl(b,BIO_C_GET_CONNECT,3) + + +#define BIO_set_nbio(b,n) BIO_ctrl(b,BIO_C_SET_NBIO,(n),NULL) + +/* BIO_s_accept_socket() */ +#define BIO_set_accept_port(b,name) BIO_ctrl(b,BIO_C_SET_ACCEPT,0,(char *)name) +#define BIO_get_accept_port(b) BIO_ptr_ctrl(b,BIO_C_GET_ACCEPT,0) +/* #define BIO_set_nbio(b,n) BIO_ctrl(b,BIO_C_SET_NBIO,(n),NULL) */ +#define BIO_set_nbio_accept(b,n) BIO_ctrl(b,BIO_C_SET_ACCEPT,1,(n)?"a":NULL) +#define BIO_set_accept_bios(b,bio) BIO_ctrl(b,BIO_C_SET_ACCEPT,2,(char *)bio) + +#define BIO_BIND_NORMAL 0 +#define BIO_BIND_REUSEADDR_IF_UNUSED 1 +#define BIO_BIND_REUSEADDR 2 +#define BIO_set_bind_mode(b,mode) BIO_ctrl(b,BIO_C_SET_BIND_MODE,mode,NULL) +#define BIO_get_bind_mode(b,mode) BIO_ctrl(b,BIO_C_GET_BIND_MODE,0,NULL) + +#define BIO_do_connect(b) BIO_do_handshake(b) +#define BIO_do_accept(b) BIO_do_handshake(b) +#define BIO_do_handshake(b) BIO_ctrl(b,BIO_C_DO_STATE_MACHINE,0,NULL) + +/* BIO_s_proxy_client() */ +#define BIO_set_url(b,url) BIO_ctrl(b,BIO_C_SET_PROXY_PARAM,0,(char *)(url)) +#define BIO_set_proxies(b,p) BIO_ctrl(b,BIO_C_SET_PROXY_PARAM,1,(char *)(p)) +/* BIO_set_nbio(b,n) */ +#define BIO_set_filter_bio(b,s) BIO_ctrl(b,BIO_C_SET_PROXY_PARAM,2,(char *)(s)) +/* BIO *BIO_get_filter_bio(BIO *bio); */ +#define BIO_set_proxy_cb(b,cb) BIO_callback_ctrl(b,BIO_C_SET_PROXY_PARAM,3,(void *(*cb)())) +#define BIO_set_proxy_header(b,sk) BIO_ctrl(b,BIO_C_SET_PROXY_PARAM,4,(char *)sk) +#define BIO_set_no_connect_return(b,bool) BIO_int_ctrl(b,BIO_C_SET_PROXY_PARAM,5,bool) + +#define BIO_get_proxy_header(b,skp) BIO_ctrl(b,BIO_C_GET_PROXY_PARAM,0,(char *)skp) +#define BIO_get_proxies(b,pxy_p) BIO_ctrl(b,BIO_C_GET_PROXY_PARAM,1,(char *)(pxy_p)) +#define BIO_get_url(b,url) BIO_ctrl(b,BIO_C_GET_PROXY_PARAM,2,(char *)(url)) +#define BIO_get_no_connect_return(b) BIO_ctrl(b,BIO_C_GET_PROXY_PARAM,5,NULL) + +#define BIO_set_fd(b,fd,c) BIO_int_ctrl(b,BIO_C_SET_FD,c,fd) +#define BIO_get_fd(b,c) BIO_ctrl(b,BIO_C_GET_FD,0,(char *)c) + +#define BIO_set_fp(b,fp,c) BIO_ctrl(b,BIO_C_SET_FILE_PTR,c,(char *)fp) +#define BIO_get_fp(b,fpp) BIO_ctrl(b,BIO_C_GET_FILE_PTR,0,(char *)fpp) + +#define BIO_seek(b,ofs) (int)BIO_ctrl(b,BIO_C_FILE_SEEK,ofs,NULL) +#define BIO_tell(b) (int)BIO_ctrl(b,BIO_C_FILE_TELL,0,NULL) + +/* name is cast to lose const, but might be better to route through a function + so we can do it safely */ +#ifdef CONST_STRICT +/* If you are wondering why this isn't defined, its because CONST_STRICT is + * purely a compile-time kludge to allow const to be checked. + */ +int BIO_read_filename(BIO *b,const char *name); +#else +#define BIO_read_filename(b,name) BIO_ctrl(b,BIO_C_SET_FILENAME, \ + BIO_CLOSE|BIO_FP_READ,(char *)name) +#endif +#define BIO_write_filename(b,name) BIO_ctrl(b,BIO_C_SET_FILENAME, \ + BIO_CLOSE|BIO_FP_WRITE,name) +#define BIO_append_filename(b,name) BIO_ctrl(b,BIO_C_SET_FILENAME, \ + BIO_CLOSE|BIO_FP_APPEND,name) +#define BIO_rw_filename(b,name) BIO_ctrl(b,BIO_C_SET_FILENAME, \ + BIO_CLOSE|BIO_FP_READ|BIO_FP_WRITE,name) + +/* WARNING WARNING, this ups the reference count on the read bio of the + * SSL structure. This is because the ssl read BIO is now pointed to by + * the next_bio field in the bio. So when you free the BIO, make sure + * you are doing a BIO_free_all() to catch the underlying BIO. */ +#define BIO_set_ssl(b,ssl,c) BIO_ctrl(b,BIO_C_SET_SSL,c,(char *)ssl) +#define BIO_get_ssl(b,sslp) BIO_ctrl(b,BIO_C_GET_SSL,0,(char *)sslp) +#define BIO_set_ssl_mode(b,client) BIO_ctrl(b,BIO_C_SSL_MODE,client,NULL) +#define BIO_set_ssl_renegotiate_bytes(b,num) \ + BIO_ctrl(b,BIO_C_SET_SSL_RENEGOTIATE_BYTES,num,NULL); +#define BIO_get_num_renegotiates(b) \ + BIO_ctrl(b,BIO_C_GET_SSL_NUM_RENEGOTIATES,0,NULL); +#define BIO_set_ssl_renegotiate_timeout(b,seconds) \ + BIO_ctrl(b,BIO_C_SET_SSL_RENEGOTIATE_TIMEOUT,seconds,NULL); + +/* defined in evp.h */ +/* #define BIO_set_md(b,md) BIO_ctrl(b,BIO_C_SET_MD,1,(char *)md) */ + +#define BIO_get_mem_data(b,pp) BIO_ctrl(b,BIO_CTRL_INFO,0,(char *)pp) +#define BIO_set_mem_buf(b,bm,c) BIO_ctrl(b,BIO_C_SET_BUF_MEM,c,(char *)bm) +#define BIO_get_mem_ptr(b,pp) BIO_ctrl(b,BIO_C_GET_BUF_MEM_PTR,0,(char *)pp) +#define BIO_set_mem_eof_return(b,v) \ + BIO_ctrl(b,BIO_C_SET_BUF_MEM_EOF_RETURN,v,NULL) + +/* For the BIO_f_buffer() type */ +#define BIO_get_buffer_num_lines(b) BIO_ctrl(b,BIO_C_GET_BUFF_NUM_LINES,0,NULL) +#define BIO_set_buffer_size(b,size) BIO_ctrl(b,BIO_C_SET_BUFF_SIZE,size,NULL) +#define BIO_set_read_buffer_size(b,size) BIO_int_ctrl(b,BIO_C_SET_BUFF_SIZE,size,0) +#define BIO_set_write_buffer_size(b,size) BIO_int_ctrl(b,BIO_C_SET_BUFF_SIZE,size,1) +#define BIO_set_buffer_read_data(b,buf,num) BIO_ctrl(b,BIO_C_SET_BUFF_READ_DATA,num,buf) + +/* Don't use the next one unless you know what you are doing :-) */ +#define BIO_dup_state(b,ret) BIO_ctrl(b,BIO_CTRL_DUP,0,(char *)(ret)) + +#define BIO_reset(b) (int)BIO_ctrl(b,BIO_CTRL_RESET,0,NULL) +#define BIO_eof(b) (int)BIO_ctrl(b,BIO_CTRL_EOF,0,NULL) +#define BIO_set_close(b,c) (int)BIO_ctrl(b,BIO_CTRL_SET_CLOSE,(c),NULL) +#define BIO_get_close(b) (int)BIO_ctrl(b,BIO_CTRL_GET_CLOSE,0,NULL) +#define BIO_pending(b) (int)BIO_ctrl(b,BIO_CTRL_PENDING,0,NULL) +#define BIO_wpending(b) (int)BIO_ctrl(b,BIO_CTRL_WPENDING,0,NULL) +/* ...pending macros have inappropriate return type */ +size_t BIO_ctrl_pending(BIO *b); +size_t BIO_ctrl_wpending(BIO *b); +#define BIO_flush(b) (int)BIO_ctrl(b,BIO_CTRL_FLUSH,0,NULL) +#define BIO_get_info_callback(b,cbp) (int)BIO_ctrl(b,BIO_CTRL_GET_CALLBACK,0, \ + cbp) +#define BIO_set_info_callback(b,cb) (int)BIO_callback_ctrl(b,BIO_CTRL_SET_CALLBACK,cb) + +/* For the BIO_f_buffer() type */ +#define BIO_buffer_get_num_lines(b) BIO_ctrl(b,BIO_CTRL_GET,0,NULL) + +/* For BIO_s_bio() */ +#define BIO_set_write_buf_size(b,size) (int)BIO_ctrl(b,BIO_C_SET_WRITE_BUF_SIZE,size,NULL) +#define BIO_get_write_buf_size(b,size) (size_t)BIO_ctrl(b,BIO_C_GET_WRITE_BUF_SIZE,size,NULL) +#define BIO_make_bio_pair(b1,b2) (int)BIO_ctrl(b1,BIO_C_MAKE_BIO_PAIR,0,b2) +#define BIO_destroy_bio_pair(b) (int)BIO_ctrl(b,BIO_C_DESTROY_BIO_PAIR,0,NULL) +#define BIO_shutdown_wr(b) (int)BIO_ctrl(b, BIO_C_SHUTDOWN_WR, 0, NULL) +/* macros with inappropriate type -- but ...pending macros use int too: */ +#define BIO_get_write_guarantee(b) (int)BIO_ctrl(b,BIO_C_GET_WRITE_GUARANTEE,0,NULL) +#define BIO_get_read_request(b) (int)BIO_ctrl(b,BIO_C_GET_READ_REQUEST,0,NULL) +size_t BIO_ctrl_get_write_guarantee(BIO *b); +size_t BIO_ctrl_get_read_request(BIO *b); +int BIO_ctrl_reset_read_request(BIO *b); + +/* ctrl macros for dgram */ +#define BIO_ctrl_dgram_connect(b,peer) \ + (int)BIO_ctrl(b,BIO_CTRL_DGRAM_CONNECT,0, (char *)peer) +#define BIO_ctrl_set_connected(b, state, peer) \ + (int)BIO_ctrl(b, BIO_CTRL_DGRAM_SET_CONNECTED, state, (char *)peer) +#define BIO_dgram_recv_timedout(b) \ + (int)BIO_ctrl(b, BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP, 0, NULL) +#define BIO_dgram_send_timedout(b) \ + (int)BIO_ctrl(b, BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP, 0, NULL) +#define BIO_dgram_set_peer(b,peer) \ + (int)BIO_ctrl(b, BIO_CTRL_DGRAM_SET_PEER, 0, (char *)peer) + +/* These two aren't currently implemented */ +/* int BIO_get_ex_num(BIO *bio); */ +/* void BIO_set_ex_free_func(BIO *bio,int idx,void (*cb)()); */ +int BIO_set_ex_data(BIO *bio,int idx,void *data); +void *BIO_get_ex_data(BIO *bio,int idx); +int BIO_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, + CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); +unsigned long BIO_number_read(BIO *bio); +unsigned long BIO_number_written(BIO *bio); + +# ifndef OPENSSL_NO_FP_API +# if defined(OPENSSL_SYS_WIN16) && defined(_WINDLL) +BIO_METHOD *BIO_s_file_internal(void); +BIO *BIO_new_file_internal(char *filename, char *mode); +BIO *BIO_new_fp_internal(FILE *stream, int close_flag); +# define BIO_s_file BIO_s_file_internal +# define BIO_new_file BIO_new_file_internal +# define BIO_new_fp BIO_new_fp_internal +# else /* FP_API */ +BIO_METHOD *BIO_s_file(void ); +BIO *BIO_new_file(const char *filename, const char *mode); +BIO *BIO_new_fp(FILE *stream, int close_flag); +# define BIO_s_file_internal BIO_s_file +# define BIO_new_file_internal BIO_new_file +# define BIO_new_fp_internal BIO_s_file +# endif /* FP_API */ +# endif +BIO * BIO_new(BIO_METHOD *type); +int BIO_set(BIO *a,BIO_METHOD *type); +int BIO_free(BIO *a); +void BIO_vfree(BIO *a); +int BIO_read(BIO *b, void *data, int len); +int BIO_gets(BIO *bp,char *buf, int size); +int BIO_write(BIO *b, const void *data, int len); +int BIO_puts(BIO *bp,const char *buf); +int BIO_indent(BIO *b,int indent,int max); +long BIO_ctrl(BIO *bp,int cmd,long larg,void *parg); +long BIO_callback_ctrl(BIO *b, int cmd, void (*fp)(struct bio_st *, int, const char *, int, long, long)); +char * BIO_ptr_ctrl(BIO *bp,int cmd,long larg); +long BIO_int_ctrl(BIO *bp,int cmd,long larg,int iarg); +BIO * BIO_push(BIO *b,BIO *append); +BIO * BIO_pop(BIO *b); +void BIO_free_all(BIO *a); +BIO * BIO_find_type(BIO *b,int bio_type); +BIO * BIO_next(BIO *b); +BIO * BIO_get_retry_BIO(BIO *bio, int *reason); +int BIO_get_retry_reason(BIO *bio); +BIO * BIO_dup_chain(BIO *in); + +int BIO_nread0(BIO *bio, char **buf); +int BIO_nread(BIO *bio, char **buf, int num); +int BIO_nwrite0(BIO *bio, char **buf); +int BIO_nwrite(BIO *bio, char **buf, int num); + +#ifndef OPENSSL_SYS_WIN16 +long BIO_debug_callback(BIO *bio,int cmd,const char *argp,int argi, + long argl,long ret); +#else +long _far _loadds BIO_debug_callback(BIO *bio,int cmd,const char *argp,int argi, + long argl,long ret); +#endif + +BIO_METHOD *BIO_s_mem(void); +BIO *BIO_new_mem_buf(void *buf, int len); +BIO_METHOD *BIO_s_socket(void); +BIO_METHOD *BIO_s_connect(void); +BIO_METHOD *BIO_s_accept(void); +BIO_METHOD *BIO_s_fd(void); +#ifndef OPENSSL_SYS_OS2 +BIO_METHOD *BIO_s_log(void); +#endif +BIO_METHOD *BIO_s_bio(void); +BIO_METHOD *BIO_s_null(void); +BIO_METHOD *BIO_f_null(void); +BIO_METHOD *BIO_f_buffer(void); +#ifdef OPENSSL_SYS_VMS +BIO_METHOD *BIO_f_linebuffer(void); +#endif +BIO_METHOD *BIO_f_nbio_test(void); +#ifndef OPENSSL_NO_DGRAM +BIO_METHOD *BIO_s_datagram(void); +#endif + +/* BIO_METHOD *BIO_f_ber(void); */ + +int BIO_sock_should_retry(int i); +int BIO_sock_non_fatal_error(int error); +int BIO_dgram_non_fatal_error(int error); + +int BIO_fd_should_retry(int i); +int BIO_fd_non_fatal_error(int error); +int BIO_dump_cb(int (*cb)(const void *data, size_t len, void *u), + void *u, const char *s, int len); +int BIO_dump_indent_cb(int (*cb)(const void *data, size_t len, void *u), + void *u, const char *s, int len, int indent); +int BIO_dump(BIO *b,const char *bytes,int len); +int BIO_dump_indent(BIO *b,const char *bytes,int len,int indent); +#ifndef OPENSSL_NO_FP_API +int BIO_dump_fp(FILE *fp, const char *s, int len); +int BIO_dump_indent_fp(FILE *fp, const char *s, int len, int indent); +#endif +struct hostent *BIO_gethostbyname(const char *name); +/* We might want a thread-safe interface too: + * struct hostent *BIO_gethostbyname_r(const char *name, + * struct hostent *result, void *buffer, size_t buflen); + * or something similar (caller allocates a struct hostent, + * pointed to by "result", and additional buffer space for the various + * substructures; if the buffer does not suffice, NULL is returned + * and an appropriate error code is set). + */ +int BIO_sock_error(int sock); +int BIO_socket_ioctl(int fd, long type, void *arg); +int BIO_socket_nbio(int fd,int mode); +int BIO_get_port(const char *str, unsigned short *port_ptr); +int BIO_get_host_ip(const char *str, unsigned char *ip); +int BIO_get_accept_socket(char *host_port,int mode); +int BIO_accept(int sock,char **ip_port); +int BIO_sock_init(void ); +void BIO_sock_cleanup(void); +int BIO_set_tcp_ndelay(int sock,int turn_on); + +BIO *BIO_new_socket(int sock, int close_flag); +BIO *BIO_new_dgram(int fd, int close_flag); +BIO *BIO_new_fd(int fd, int close_flag); +BIO *BIO_new_connect(char *host_port); +BIO *BIO_new_accept(char *host_port); + +int BIO_new_bio_pair(BIO **bio1, size_t writebuf1, + BIO **bio2, size_t writebuf2); +/* If successful, returns 1 and in *bio1, *bio2 two BIO pair endpoints. + * Otherwise returns 0 and sets *bio1 and *bio2 to NULL. + * Size 0 uses default value. + */ + +void BIO_copy_next_retry(BIO *b); + +/*long BIO_ghbn_ctrl(int cmd,int iarg,char *parg);*/ + +#ifdef __GNUC__ +# define __bio_h__attr__ __attribute__ +#else +# define __bio_h__attr__(x) +#endif +int BIO_printf(BIO *bio, const char *format, ...) + __bio_h__attr__((__format__(__printf__,2,3))); +int BIO_vprintf(BIO *bio, const char *format, va_list args) + __bio_h__attr__((__format__(__printf__,2,0))); +int BIO_snprintf(char *buf, size_t n, const char *format, ...) + __bio_h__attr__((__format__(__printf__,3,4))); +int BIO_vsnprintf(char *buf, size_t n, const char *format, va_list args) + __bio_h__attr__((__format__(__printf__,3,0))); +#undef __bio_h__attr__ + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_BIO_strings(void); + +/* Error codes for the BIO functions. */ + +/* Function codes. */ +#define BIO_F_ACPT_STATE 100 +#define BIO_F_BIO_ACCEPT 101 +#define BIO_F_BIO_BER_GET_HEADER 102 +#define BIO_F_BIO_CALLBACK_CTRL 131 +#define BIO_F_BIO_CTRL 103 +#define BIO_F_BIO_GETHOSTBYNAME 120 +#define BIO_F_BIO_GETS 104 +#define BIO_F_BIO_GET_ACCEPT_SOCKET 105 +#define BIO_F_BIO_GET_HOST_IP 106 +#define BIO_F_BIO_GET_PORT 107 +#define BIO_F_BIO_MAKE_PAIR 121 +#define BIO_F_BIO_NEW 108 +#define BIO_F_BIO_NEW_FILE 109 +#define BIO_F_BIO_NEW_MEM_BUF 126 +#define BIO_F_BIO_NREAD 123 +#define BIO_F_BIO_NREAD0 124 +#define BIO_F_BIO_NWRITE 125 +#define BIO_F_BIO_NWRITE0 122 +#define BIO_F_BIO_PUTS 110 +#define BIO_F_BIO_READ 111 +#define BIO_F_BIO_SOCK_INIT 112 +#define BIO_F_BIO_WRITE 113 +#define BIO_F_BUFFER_CTRL 114 +#define BIO_F_CONN_CTRL 127 +#define BIO_F_CONN_STATE 115 +#define BIO_F_FILE_CTRL 116 +#define BIO_F_FILE_READ 130 +#define BIO_F_LINEBUFFER_CTRL 129 +#define BIO_F_MEM_READ 128 +#define BIO_F_MEM_WRITE 117 +#define BIO_F_SSL_NEW 118 +#define BIO_F_WSASTARTUP 119 + +/* Reason codes. */ +#define BIO_R_ACCEPT_ERROR 100 +#define BIO_R_BAD_FOPEN_MODE 101 +#define BIO_R_BAD_HOSTNAME_LOOKUP 102 +#define BIO_R_BROKEN_PIPE 124 +#define BIO_R_CONNECT_ERROR 103 +#define BIO_R_EOF_ON_MEMORY_BIO 127 +#define BIO_R_ERROR_SETTING_NBIO 104 +#define BIO_R_ERROR_SETTING_NBIO_ON_ACCEPTED_SOCKET 105 +#define BIO_R_ERROR_SETTING_NBIO_ON_ACCEPT_SOCKET 106 +#define BIO_R_GETHOSTBYNAME_ADDR_IS_NOT_AF_INET 107 +#define BIO_R_INVALID_ARGUMENT 125 +#define BIO_R_INVALID_IP_ADDRESS 108 +#define BIO_R_IN_USE 123 +#define BIO_R_KEEPALIVE 109 +#define BIO_R_NBIO_CONNECT_ERROR 110 +#define BIO_R_NO_ACCEPT_PORT_SPECIFIED 111 +#define BIO_R_NO_HOSTNAME_SPECIFIED 112 +#define BIO_R_NO_PORT_DEFINED 113 +#define BIO_R_NO_PORT_SPECIFIED 114 +#define BIO_R_NO_SUCH_FILE 128 +#define BIO_R_NULL_PARAMETER 115 +#define BIO_R_TAG_MISMATCH 116 +#define BIO_R_UNABLE_TO_BIND_SOCKET 117 +#define BIO_R_UNABLE_TO_CREATE_SOCKET 118 +#define BIO_R_UNABLE_TO_LISTEN_SOCKET 119 +#define BIO_R_UNINITIALIZED 120 +#define BIO_R_UNSUPPORTED_METHOD 121 +#define BIO_R_WRITE_TO_READ_ONLY_BIO 126 +#define BIO_R_WSASTARTUP 122 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/blowfish.h b/production/3rdparty/openssl/include/openssl/blowfish.h new file mode 100644 index 00000000..cd49e85a --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/blowfish.h @@ -0,0 +1,127 @@ +/* crypto/bf/blowfish.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_BLOWFISH_H +#define HEADER_BLOWFISH_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef OPENSSL_NO_BF +#error BF is disabled. +#endif + +#define BF_ENCRYPT 1 +#define BF_DECRYPT 0 + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * ! BF_LONG has to be at least 32 bits wide. If it's wider, then ! + * ! BF_LONG_LOG2 has to be defined along. ! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + */ + +#if defined(OPENSSL_SYS_WIN16) || defined(__LP32__) +#define BF_LONG unsigned long +#elif defined(OPENSSL_SYS_CRAY) || defined(__ILP64__) +#define BF_LONG unsigned long +#define BF_LONG_LOG2 3 +/* + * _CRAY note. I could declare short, but I have no idea what impact + * does it have on performance on none-T3E machines. I could declare + * int, but at least on C90 sizeof(int) can be chosen at compile time. + * So I've chosen long... + * + */ +#else +#define BF_LONG unsigned int +#endif + +#define BF_ROUNDS 16 +#define BF_BLOCK 8 + +typedef struct bf_key_st + { + BF_LONG P[BF_ROUNDS+2]; + BF_LONG S[4*256]; + } BF_KEY; + + +void BF_set_key(BF_KEY *key, int len, const unsigned char *data); + +void BF_encrypt(BF_LONG *data,const BF_KEY *key); +void BF_decrypt(BF_LONG *data,const BF_KEY *key); + +void BF_ecb_encrypt(const unsigned char *in, unsigned char *out, + const BF_KEY *key, int enc); +void BF_cbc_encrypt(const unsigned char *in, unsigned char *out, long length, + const BF_KEY *schedule, unsigned char *ivec, int enc); +void BF_cfb64_encrypt(const unsigned char *in, unsigned char *out, long length, + const BF_KEY *schedule, unsigned char *ivec, int *num, int enc); +void BF_ofb64_encrypt(const unsigned char *in, unsigned char *out, long length, + const BF_KEY *schedule, unsigned char *ivec, int *num); +const char *BF_options(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/bn.h b/production/3rdparty/openssl/include/openssl/bn.h new file mode 100644 index 00000000..95c5d643 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/bn.h @@ -0,0 +1,827 @@ +/* crypto/bn/bn.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * + * Portions of the attached software ("Contribution") are developed by + * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project. + * + * The Contribution is licensed pursuant to the Eric Young open source + * license provided above. + * + * The binary polynomial arithmetic software is originally written by + * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems Laboratories. + * + */ + +#ifndef HEADER_BN_H +#define HEADER_BN_H + +#include +#ifndef OPENSSL_NO_FP_API +#include /* FILE */ +#endif +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* These preprocessor symbols control various aspects of the bignum headers and + * library code. They're not defined by any "normal" configuration, as they are + * intended for development and testing purposes. NB: defining all three can be + * useful for debugging application code as well as openssl itself. + * + * BN_DEBUG - turn on various debugging alterations to the bignum code + * BN_DEBUG_RAND - uses random poisoning of unused words to trip up + * mismanagement of bignum internals. You must also define BN_DEBUG. + */ +/* #define BN_DEBUG */ +/* #define BN_DEBUG_RAND */ + +#define BN_MUL_COMBA +#define BN_SQR_COMBA +#define BN_RECURSION + +/* This next option uses the C libraries (2 word)/(1 word) function. + * If it is not defined, I use my C version (which is slower). + * The reason for this flag is that when the particular C compiler + * library routine is used, and the library is linked with a different + * compiler, the library is missing. This mostly happens when the + * library is built with gcc and then linked using normal cc. This would + * be a common occurrence because gcc normally produces code that is + * 2 times faster than system compilers for the big number stuff. + * For machines with only one compiler (or shared libraries), this should + * be on. Again this in only really a problem on machines + * using "long long's", are 32bit, and are not using my assembler code. */ +#if defined(OPENSSL_SYS_MSDOS) || defined(OPENSSL_SYS_WINDOWS) || \ + defined(OPENSSL_SYS_WIN32) || defined(linux) +# ifndef BN_DIV2W +# define BN_DIV2W +# endif +#endif + +/* assuming long is 64bit - this is the DEC Alpha + * unsigned long long is only 64 bits :-(, don't define + * BN_LLONG for the DEC Alpha */ +#ifdef SIXTY_FOUR_BIT_LONG +#define BN_ULLONG unsigned long long +#define BN_ULONG unsigned long +#define BN_LONG long +#define BN_BITS 128 +#define BN_BYTES 8 +#define BN_BITS2 64 +#define BN_BITS4 32 +#define BN_MASK (0xffffffffffffffffffffffffffffffffLL) +#define BN_MASK2 (0xffffffffffffffffL) +#define BN_MASK2l (0xffffffffL) +#define BN_MASK2h (0xffffffff00000000L) +#define BN_MASK2h1 (0xffffffff80000000L) +#define BN_TBIT (0x8000000000000000L) +#define BN_DEC_CONV (10000000000000000000UL) +#define BN_DEC_FMT1 "%lu" +#define BN_DEC_FMT2 "%019lu" +#define BN_DEC_NUM 19 +#endif + +/* This is where the long long data type is 64 bits, but long is 32. + * For machines where there are 64bit registers, this is the mode to use. + * IRIX, on R4000 and above should use this mode, along with the relevant + * assembler code :-). Do NOT define BN_LLONG. + */ +#ifdef SIXTY_FOUR_BIT +#undef BN_LLONG +#undef BN_ULLONG +#define BN_ULONG unsigned long long +#define BN_LONG long long +#define BN_BITS 128 +#define BN_BYTES 8 +#define BN_BITS2 64 +#define BN_BITS4 32 +#define BN_MASK2 (0xffffffffffffffffLL) +#define BN_MASK2l (0xffffffffL) +#define BN_MASK2h (0xffffffff00000000LL) +#define BN_MASK2h1 (0xffffffff80000000LL) +#define BN_TBIT (0x8000000000000000LL) +#define BN_DEC_CONV (10000000000000000000ULL) +#define BN_DEC_FMT1 "%llu" +#define BN_DEC_FMT2 "%019llu" +#define BN_DEC_NUM 19 +#endif + +#ifdef THIRTY_TWO_BIT +#ifdef BN_LLONG +# if defined(OPENSSL_SYS_WIN32) && !defined(__GNUC__) +# define BN_ULLONG unsigned __int64 +# else +# define BN_ULLONG unsigned long long +# endif +#endif +#define BN_ULONG unsigned long +#define BN_LONG long +#define BN_BITS 64 +#define BN_BYTES 4 +#define BN_BITS2 32 +#define BN_BITS4 16 +#ifdef OPENSSL_SYS_WIN32 +/* VC++ doesn't like the LL suffix */ +#define BN_MASK (0xffffffffffffffffL) +#else +#define BN_MASK (0xffffffffffffffffLL) +#endif +#define BN_MASK2 (0xffffffffL) +#define BN_MASK2l (0xffff) +#define BN_MASK2h1 (0xffff8000L) +#define BN_MASK2h (0xffff0000L) +#define BN_TBIT (0x80000000L) +#define BN_DEC_CONV (1000000000L) +#define BN_DEC_FMT1 "%lu" +#define BN_DEC_FMT2 "%09lu" +#define BN_DEC_NUM 9 +#endif + +#ifdef SIXTEEN_BIT +#ifndef BN_DIV2W +#define BN_DIV2W +#endif +#define BN_ULLONG unsigned long +#define BN_ULONG unsigned short +#define BN_LONG short +#define BN_BITS 32 +#define BN_BYTES 2 +#define BN_BITS2 16 +#define BN_BITS4 8 +#define BN_MASK (0xffffffff) +#define BN_MASK2 (0xffff) +#define BN_MASK2l (0xff) +#define BN_MASK2h1 (0xff80) +#define BN_MASK2h (0xff00) +#define BN_TBIT (0x8000) +#define BN_DEC_CONV (100000) +#define BN_DEC_FMT1 "%u" +#define BN_DEC_FMT2 "%05u" +#define BN_DEC_NUM 5 +#endif + +#ifdef EIGHT_BIT +#ifndef BN_DIV2W +#define BN_DIV2W +#endif +#define BN_ULLONG unsigned short +#define BN_ULONG unsigned char +#define BN_LONG char +#define BN_BITS 16 +#define BN_BYTES 1 +#define BN_BITS2 8 +#define BN_BITS4 4 +#define BN_MASK (0xffff) +#define BN_MASK2 (0xff) +#define BN_MASK2l (0xf) +#define BN_MASK2h1 (0xf8) +#define BN_MASK2h (0xf0) +#define BN_TBIT (0x80) +#define BN_DEC_CONV (100) +#define BN_DEC_FMT1 "%u" +#define BN_DEC_FMT2 "%02u" +#define BN_DEC_NUM 2 +#endif + +#define BN_DEFAULT_BITS 1280 + +#define BN_FLG_MALLOCED 0x01 +#define BN_FLG_STATIC_DATA 0x02 +#define BN_FLG_EXP_CONSTTIME 0x04 /* avoid leaking exponent information through timings + * (BN_mod_exp_mont() will call BN_mod_exp_mont_consttime) */ +#ifndef OPENSSL_NO_DEPRECATED +#define BN_FLG_FREE 0x8000 /* used for debuging */ +#endif +#define BN_set_flags(b,n) ((b)->flags|=(n)) +#define BN_get_flags(b,n) ((b)->flags&(n)) + +/* get a clone of a BIGNUM with changed flags, for *temporary* use only + * (the two BIGNUMs cannot not be used in parallel!) */ +#define BN_with_flags(dest,b,n) ((dest)->d=(b)->d, \ + (dest)->top=(b)->top, \ + (dest)->dmax=(b)->dmax, \ + (dest)->neg=(b)->neg, \ + (dest)->flags=(((dest)->flags & BN_FLG_MALLOCED) \ + | ((b)->flags & ~BN_FLG_MALLOCED) \ + | BN_FLG_STATIC_DATA \ + | (n))) + +/* Already declared in ossl_typ.h */ +#if 0 +typedef struct bignum_st BIGNUM; +/* Used for temp variables (declaration hidden in bn_lcl.h) */ +typedef struct bignum_ctx BN_CTX; +typedef struct bn_blinding_st BN_BLINDING; +typedef struct bn_mont_ctx_st BN_MONT_CTX; +typedef struct bn_recp_ctx_st BN_RECP_CTX; +typedef struct bn_gencb_st BN_GENCB; +#endif + +struct bignum_st + { + BN_ULONG *d; /* Pointer to an array of 'BN_BITS2' bit chunks. */ + int top; /* Index of last used d +1. */ + /* The next are internal book keeping for bn_expand. */ + int dmax; /* Size of the d array. */ + int neg; /* one if the number is negative */ + int flags; + }; + +/* Used for montgomery multiplication */ +struct bn_mont_ctx_st + { + int ri; /* number of bits in R */ + BIGNUM RR; /* used to convert to montgomery form */ + BIGNUM N; /* The modulus */ + BIGNUM Ni; /* R*(1/R mod N) - N*Ni = 1 + * (Ni is only stored for bignum algorithm) */ + BN_ULONG n0; /* least significant word of Ni */ + int flags; + }; + +/* Used for reciprocal division/mod functions + * It cannot be shared between threads + */ +struct bn_recp_ctx_st + { + BIGNUM N; /* the divisor */ + BIGNUM Nr; /* the reciprocal */ + int num_bits; + int shift; + int flags; + }; + +/* Used for slow "generation" functions. */ +struct bn_gencb_st + { + unsigned int ver; /* To handle binary (in)compatibility */ + void *arg; /* callback-specific data */ + union + { + /* if(ver==1) - handles old style callbacks */ + void (*cb_1)(int, int, void *); + /* if(ver==2) - new callback style */ + int (*cb_2)(int, int, BN_GENCB *); + } cb; + }; +/* Wrapper function to make using BN_GENCB easier, */ +int BN_GENCB_call(BN_GENCB *cb, int a, int b); +/* Macro to populate a BN_GENCB structure with an "old"-style callback */ +#define BN_GENCB_set_old(gencb, callback, cb_arg) { \ + BN_GENCB *tmp_gencb = (gencb); \ + tmp_gencb->ver = 1; \ + tmp_gencb->arg = (cb_arg); \ + tmp_gencb->cb.cb_1 = (callback); } +/* Macro to populate a BN_GENCB structure with a "new"-style callback */ +#define BN_GENCB_set(gencb, callback, cb_arg) { \ + BN_GENCB *tmp_gencb = (gencb); \ + tmp_gencb->ver = 2; \ + tmp_gencb->arg = (cb_arg); \ + tmp_gencb->cb.cb_2 = (callback); } + +#define BN_prime_checks 0 /* default: select number of iterations + based on the size of the number */ + +/* number of Miller-Rabin iterations for an error rate of less than 2^-80 + * for random 'b'-bit input, b >= 100 (taken from table 4.4 in the Handbook + * of Applied Cryptography [Menezes, van Oorschot, Vanstone; CRC Press 1996]; + * original paper: Damgaard, Landrock, Pomerance: Average case error estimates + * for the strong probable prime test. -- Math. Comp. 61 (1993) 177-194) */ +#define BN_prime_checks_for_size(b) ((b) >= 1300 ? 2 : \ + (b) >= 850 ? 3 : \ + (b) >= 650 ? 4 : \ + (b) >= 550 ? 5 : \ + (b) >= 450 ? 6 : \ + (b) >= 400 ? 7 : \ + (b) >= 350 ? 8 : \ + (b) >= 300 ? 9 : \ + (b) >= 250 ? 12 : \ + (b) >= 200 ? 15 : \ + (b) >= 150 ? 18 : \ + /* b >= 100 */ 27) + +#define BN_num_bytes(a) ((BN_num_bits(a)+7)/8) + +/* Note that BN_abs_is_word didn't work reliably for w == 0 until 0.9.8 */ +#define BN_abs_is_word(a,w) ((((a)->top == 1) && ((a)->d[0] == (BN_ULONG)(w))) || \ + (((w) == 0) && ((a)->top == 0))) +#define BN_is_zero(a) ((a)->top == 0) +#define BN_is_one(a) (BN_abs_is_word((a),1) && !(a)->neg) +#define BN_is_word(a,w) (BN_abs_is_word((a),(w)) && (!(w) || !(a)->neg)) +#define BN_is_odd(a) (((a)->top > 0) && ((a)->d[0] & 1)) + +#define BN_one(a) (BN_set_word((a),1)) +#define BN_zero_ex(a) \ + do { \ + BIGNUM *_tmp_bn = (a); \ + _tmp_bn->top = 0; \ + _tmp_bn->neg = 0; \ + } while(0) +#ifdef OPENSSL_NO_DEPRECATED +#define BN_zero(a) BN_zero_ex(a) +#else +#define BN_zero(a) (BN_set_word((a),0)) +#endif + +const BIGNUM *BN_value_one(void); +char * BN_options(void); +BN_CTX *BN_CTX_new(void); +#ifndef OPENSSL_NO_DEPRECATED +void BN_CTX_init(BN_CTX *c); +#endif +void BN_CTX_free(BN_CTX *c); +void BN_CTX_start(BN_CTX *ctx); +BIGNUM *BN_CTX_get(BN_CTX *ctx); +void BN_CTX_end(BN_CTX *ctx); +int BN_rand(BIGNUM *rnd, int bits, int top,int bottom); +int BN_pseudo_rand(BIGNUM *rnd, int bits, int top,int bottom); +int BN_rand_range(BIGNUM *rnd, BIGNUM *range); +int BN_pseudo_rand_range(BIGNUM *rnd, BIGNUM *range); +int BN_num_bits(const BIGNUM *a); +int BN_num_bits_word(BN_ULONG); +BIGNUM *BN_new(void); +void BN_init(BIGNUM *); +void BN_clear_free(BIGNUM *a); +BIGNUM *BN_copy(BIGNUM *a, const BIGNUM *b); +void BN_swap(BIGNUM *a, BIGNUM *b); +BIGNUM *BN_bin2bn(const unsigned char *s,int len,BIGNUM *ret); +int BN_bn2bin(const BIGNUM *a, unsigned char *to); +BIGNUM *BN_mpi2bn(const unsigned char *s,int len,BIGNUM *ret); +int BN_bn2mpi(const BIGNUM *a, unsigned char *to); +int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); +int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); +int BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); +int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); +int BN_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx); +int BN_sqr(BIGNUM *r, const BIGNUM *a,BN_CTX *ctx); +/** BN_set_negative sets sign of a BIGNUM + * \param b pointer to the BIGNUM object + * \param n 0 if the BIGNUM b should be positive and a value != 0 otherwise + */ +void BN_set_negative(BIGNUM *b, int n); +/** BN_is_negative returns 1 if the BIGNUM is negative + * \param a pointer to the BIGNUM object + * \return 1 if a < 0 and 0 otherwise + */ +#define BN_is_negative(a) ((a)->neg != 0) + +int BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, const BIGNUM *d, + BN_CTX *ctx); +#define BN_mod(rem,m,d,ctx) BN_div(NULL,(rem),(m),(d),(ctx)) +int BN_nnmod(BIGNUM *r, const BIGNUM *m, const BIGNUM *d, BN_CTX *ctx); +int BN_mod_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_CTX *ctx); +int BN_mod_add_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m); +int BN_mod_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m, BN_CTX *ctx); +int BN_mod_sub_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, const BIGNUM *m); +int BN_mod_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, + const BIGNUM *m, BN_CTX *ctx); +int BN_mod_sqr(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx); +int BN_mod_lshift1(BIGNUM *r, const BIGNUM *a, const BIGNUM *m, BN_CTX *ctx); +int BN_mod_lshift1_quick(BIGNUM *r, const BIGNUM *a, const BIGNUM *m); +int BN_mod_lshift(BIGNUM *r, const BIGNUM *a, int n, const BIGNUM *m, BN_CTX *ctx); +int BN_mod_lshift_quick(BIGNUM *r, const BIGNUM *a, int n, const BIGNUM *m); + +BN_ULONG BN_mod_word(const BIGNUM *a, BN_ULONG w); +BN_ULONG BN_div_word(BIGNUM *a, BN_ULONG w); +int BN_mul_word(BIGNUM *a, BN_ULONG w); +int BN_add_word(BIGNUM *a, BN_ULONG w); +int BN_sub_word(BIGNUM *a, BN_ULONG w); +int BN_set_word(BIGNUM *a, BN_ULONG w); +BN_ULONG BN_get_word(const BIGNUM *a); + +int BN_cmp(const BIGNUM *a, const BIGNUM *b); +void BN_free(BIGNUM *a); +int BN_is_bit_set(const BIGNUM *a, int n); +int BN_lshift(BIGNUM *r, const BIGNUM *a, int n); +int BN_lshift1(BIGNUM *r, const BIGNUM *a); +int BN_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,BN_CTX *ctx); + +int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, + const BIGNUM *m,BN_CTX *ctx); +int BN_mod_exp_mont(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, + const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx); +int BN_mod_exp_mont_consttime(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p, + const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *in_mont); +int BN_mod_exp_mont_word(BIGNUM *r, BN_ULONG a, const BIGNUM *p, + const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx); +int BN_mod_exp2_mont(BIGNUM *r, const BIGNUM *a1, const BIGNUM *p1, + const BIGNUM *a2, const BIGNUM *p2,const BIGNUM *m, + BN_CTX *ctx,BN_MONT_CTX *m_ctx); +int BN_mod_exp_simple(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, + const BIGNUM *m,BN_CTX *ctx); + +int BN_mask_bits(BIGNUM *a,int n); +#ifndef OPENSSL_NO_FP_API +int BN_print_fp(FILE *fp, const BIGNUM *a); +#endif +#ifdef HEADER_BIO_H +int BN_print(BIO *fp, const BIGNUM *a); +#else +int BN_print(void *fp, const BIGNUM *a); +#endif +int BN_reciprocal(BIGNUM *r, const BIGNUM *m, int len, BN_CTX *ctx); +int BN_rshift(BIGNUM *r, const BIGNUM *a, int n); +int BN_rshift1(BIGNUM *r, const BIGNUM *a); +void BN_clear(BIGNUM *a); +BIGNUM *BN_dup(const BIGNUM *a); +int BN_ucmp(const BIGNUM *a, const BIGNUM *b); +int BN_set_bit(BIGNUM *a, int n); +int BN_clear_bit(BIGNUM *a, int n); +char * BN_bn2hex(const BIGNUM *a); +char * BN_bn2dec(const BIGNUM *a); +int BN_hex2bn(BIGNUM **a, const char *str); +int BN_dec2bn(BIGNUM **a, const char *str); +int BN_gcd(BIGNUM *r,const BIGNUM *a,const BIGNUM *b,BN_CTX *ctx); +int BN_kronecker(const BIGNUM *a,const BIGNUM *b,BN_CTX *ctx); /* returns -2 for error */ +BIGNUM *BN_mod_inverse(BIGNUM *ret, + const BIGNUM *a, const BIGNUM *n,BN_CTX *ctx); +BIGNUM *BN_mod_sqrt(BIGNUM *ret, + const BIGNUM *a, const BIGNUM *n,BN_CTX *ctx); + +/* Deprecated versions */ +#ifndef OPENSSL_NO_DEPRECATED +BIGNUM *BN_generate_prime(BIGNUM *ret,int bits,int safe, + const BIGNUM *add, const BIGNUM *rem, + void (*callback)(int,int,void *),void *cb_arg); +int BN_is_prime(const BIGNUM *p,int nchecks, + void (*callback)(int,int,void *), + BN_CTX *ctx,void *cb_arg); +int BN_is_prime_fasttest(const BIGNUM *p,int nchecks, + void (*callback)(int,int,void *),BN_CTX *ctx,void *cb_arg, + int do_trial_division); +#endif /* !defined(OPENSSL_NO_DEPRECATED) */ + +/* Newer versions */ +int BN_generate_prime_ex(BIGNUM *ret,int bits,int safe, const BIGNUM *add, + const BIGNUM *rem, BN_GENCB *cb); +int BN_is_prime_ex(const BIGNUM *p,int nchecks, BN_CTX *ctx, BN_GENCB *cb); +int BN_is_prime_fasttest_ex(const BIGNUM *p,int nchecks, BN_CTX *ctx, + int do_trial_division, BN_GENCB *cb); + +BN_MONT_CTX *BN_MONT_CTX_new(void ); +void BN_MONT_CTX_init(BN_MONT_CTX *ctx); +int BN_mod_mul_montgomery(BIGNUM *r,const BIGNUM *a,const BIGNUM *b, + BN_MONT_CTX *mont, BN_CTX *ctx); +#define BN_to_montgomery(r,a,mont,ctx) BN_mod_mul_montgomery(\ + (r),(a),&((mont)->RR),(mont),(ctx)) +int BN_from_montgomery(BIGNUM *r,const BIGNUM *a, + BN_MONT_CTX *mont, BN_CTX *ctx); +void BN_MONT_CTX_free(BN_MONT_CTX *mont); +int BN_MONT_CTX_set(BN_MONT_CTX *mont,const BIGNUM *mod,BN_CTX *ctx); +BN_MONT_CTX *BN_MONT_CTX_copy(BN_MONT_CTX *to,BN_MONT_CTX *from); +BN_MONT_CTX *BN_MONT_CTX_set_locked(BN_MONT_CTX **pmont, int lock, + const BIGNUM *mod, BN_CTX *ctx); + +/* BN_BLINDING flags */ +#define BN_BLINDING_NO_UPDATE 0x00000001 +#define BN_BLINDING_NO_RECREATE 0x00000002 + +BN_BLINDING *BN_BLINDING_new(const BIGNUM *A, const BIGNUM *Ai, BIGNUM *mod); +void BN_BLINDING_free(BN_BLINDING *b); +int BN_BLINDING_update(BN_BLINDING *b,BN_CTX *ctx); +int BN_BLINDING_convert(BIGNUM *n, BN_BLINDING *b, BN_CTX *ctx); +int BN_BLINDING_invert(BIGNUM *n, BN_BLINDING *b, BN_CTX *ctx); +int BN_BLINDING_convert_ex(BIGNUM *n, BIGNUM *r, BN_BLINDING *b, BN_CTX *); +int BN_BLINDING_invert_ex(BIGNUM *n, const BIGNUM *r, BN_BLINDING *b, BN_CTX *); +unsigned long BN_BLINDING_get_thread_id(const BN_BLINDING *); +void BN_BLINDING_set_thread_id(BN_BLINDING *, unsigned long); +unsigned long BN_BLINDING_get_flags(const BN_BLINDING *); +void BN_BLINDING_set_flags(BN_BLINDING *, unsigned long); +BN_BLINDING *BN_BLINDING_create_param(BN_BLINDING *b, + const BIGNUM *e, BIGNUM *m, BN_CTX *ctx, + int (*bn_mod_exp)(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, + const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *m_ctx), + BN_MONT_CTX *m_ctx); + +#ifndef OPENSSL_NO_DEPRECATED +void BN_set_params(int mul,int high,int low,int mont); +int BN_get_params(int which); /* 0, mul, 1 high, 2 low, 3 mont */ +#endif + +void BN_RECP_CTX_init(BN_RECP_CTX *recp); +BN_RECP_CTX *BN_RECP_CTX_new(void); +void BN_RECP_CTX_free(BN_RECP_CTX *recp); +int BN_RECP_CTX_set(BN_RECP_CTX *recp,const BIGNUM *rdiv,BN_CTX *ctx); +int BN_mod_mul_reciprocal(BIGNUM *r, const BIGNUM *x, const BIGNUM *y, + BN_RECP_CTX *recp,BN_CTX *ctx); +int BN_mod_exp_recp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, + const BIGNUM *m, BN_CTX *ctx); +int BN_div_recp(BIGNUM *dv, BIGNUM *rem, const BIGNUM *m, + BN_RECP_CTX *recp, BN_CTX *ctx); + +/* Functions for arithmetic over binary polynomials represented by BIGNUMs. + * + * The BIGNUM::neg property of BIGNUMs representing binary polynomials is + * ignored. + * + * Note that input arguments are not const so that their bit arrays can + * be expanded to the appropriate size if needed. + */ + +int BN_GF2m_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b); /*r = a + b*/ +#define BN_GF2m_sub(r, a, b) BN_GF2m_add(r, a, b) +int BN_GF2m_mod(BIGNUM *r, const BIGNUM *a, const BIGNUM *p); /*r=a mod p*/ +int BN_GF2m_mod_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, + const BIGNUM *p, BN_CTX *ctx); /* r = (a * b) mod p */ +int BN_GF2m_mod_sqr(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, + BN_CTX *ctx); /* r = (a * a) mod p */ +int BN_GF2m_mod_inv(BIGNUM *r, const BIGNUM *b, const BIGNUM *p, + BN_CTX *ctx); /* r = (1 / b) mod p */ +int BN_GF2m_mod_div(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, + const BIGNUM *p, BN_CTX *ctx); /* r = (a / b) mod p */ +int BN_GF2m_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, + const BIGNUM *p, BN_CTX *ctx); /* r = (a ^ b) mod p */ +int BN_GF2m_mod_sqrt(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, + BN_CTX *ctx); /* r = sqrt(a) mod p */ +int BN_GF2m_mod_solve_quad(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, + BN_CTX *ctx); /* r^2 + r = a mod p */ +#define BN_GF2m_cmp(a, b) BN_ucmp((a), (b)) +/* Some functions allow for representation of the irreducible polynomials + * as an unsigned int[], say p. The irreducible f(t) is then of the form: + * t^p[0] + t^p[1] + ... + t^p[k] + * where m = p[0] > p[1] > ... > p[k] = 0. + */ +int BN_GF2m_mod_arr(BIGNUM *r, const BIGNUM *a, const unsigned int p[]); + /* r = a mod p */ +int BN_GF2m_mod_mul_arr(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, + const unsigned int p[], BN_CTX *ctx); /* r = (a * b) mod p */ +int BN_GF2m_mod_sqr_arr(BIGNUM *r, const BIGNUM *a, const unsigned int p[], + BN_CTX *ctx); /* r = (a * a) mod p */ +int BN_GF2m_mod_inv_arr(BIGNUM *r, const BIGNUM *b, const unsigned int p[], + BN_CTX *ctx); /* r = (1 / b) mod p */ +int BN_GF2m_mod_div_arr(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, + const unsigned int p[], BN_CTX *ctx); /* r = (a / b) mod p */ +int BN_GF2m_mod_exp_arr(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, + const unsigned int p[], BN_CTX *ctx); /* r = (a ^ b) mod p */ +int BN_GF2m_mod_sqrt_arr(BIGNUM *r, const BIGNUM *a, + const unsigned int p[], BN_CTX *ctx); /* r = sqrt(a) mod p */ +int BN_GF2m_mod_solve_quad_arr(BIGNUM *r, const BIGNUM *a, + const unsigned int p[], BN_CTX *ctx); /* r^2 + r = a mod p */ +int BN_GF2m_poly2arr(const BIGNUM *a, unsigned int p[], int max); +int BN_GF2m_arr2poly(const unsigned int p[], BIGNUM *a); + +/* faster mod functions for the 'NIST primes' + * 0 <= a < p^2 */ +int BN_nist_mod_192(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx); +int BN_nist_mod_224(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx); +int BN_nist_mod_256(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx); +int BN_nist_mod_384(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx); +int BN_nist_mod_521(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx); + +const BIGNUM *BN_get0_nist_prime_192(void); +const BIGNUM *BN_get0_nist_prime_224(void); +const BIGNUM *BN_get0_nist_prime_256(void); +const BIGNUM *BN_get0_nist_prime_384(void); +const BIGNUM *BN_get0_nist_prime_521(void); + +/* library internal functions */ + +#define bn_expand(a,bits) ((((((bits+BN_BITS2-1))/BN_BITS2)) <= (a)->dmax)?\ + (a):bn_expand2((a),(bits+BN_BITS2-1)/BN_BITS2)) +#define bn_wexpand(a,words) (((words) <= (a)->dmax)?(a):bn_expand2((a),(words))) +BIGNUM *bn_expand2(BIGNUM *a, int words); +#ifndef OPENSSL_NO_DEPRECATED +BIGNUM *bn_dup_expand(const BIGNUM *a, int words); /* unused */ +#endif + +/* Bignum consistency macros + * There is one "API" macro, bn_fix_top(), for stripping leading zeroes from + * bignum data after direct manipulations on the data. There is also an + * "internal" macro, bn_check_top(), for verifying that there are no leading + * zeroes. Unfortunately, some auditing is required due to the fact that + * bn_fix_top() has become an overabused duct-tape because bignum data is + * occasionally passed around in an inconsistent state. So the following + * changes have been made to sort this out; + * - bn_fix_top()s implementation has been moved to bn_correct_top() + * - if BN_DEBUG isn't defined, bn_fix_top() maps to bn_correct_top(), and + * bn_check_top() is as before. + * - if BN_DEBUG *is* defined; + * - bn_check_top() tries to pollute unused words even if the bignum 'top' is + * consistent. (ed: only if BN_DEBUG_RAND is defined) + * - bn_fix_top() maps to bn_check_top() rather than "fixing" anything. + * The idea is to have debug builds flag up inconsistent bignums when they + * occur. If that occurs in a bn_fix_top(), we examine the code in question; if + * the use of bn_fix_top() was appropriate (ie. it follows directly after code + * that manipulates the bignum) it is converted to bn_correct_top(), and if it + * was not appropriate, we convert it permanently to bn_check_top() and track + * down the cause of the bug. Eventually, no internal code should be using the + * bn_fix_top() macro. External applications and libraries should try this with + * their own code too, both in terms of building against the openssl headers + * with BN_DEBUG defined *and* linking with a version of OpenSSL built with it + * defined. This not only improves external code, it provides more test + * coverage for openssl's own code. + */ + +#ifdef BN_DEBUG + +/* We only need assert() when debugging */ +#include + +#ifdef BN_DEBUG_RAND +/* To avoid "make update" cvs wars due to BN_DEBUG, use some tricks */ +#ifndef RAND_pseudo_bytes +int RAND_pseudo_bytes(unsigned char *buf,int num); +#define BN_DEBUG_TRIX +#endif +#define bn_pollute(a) \ + do { \ + const BIGNUM *_bnum1 = (a); \ + if(_bnum1->top < _bnum1->dmax) { \ + unsigned char _tmp_char; \ + /* We cast away const without the compiler knowing, any \ + * *genuinely* constant variables that aren't mutable \ + * wouldn't be constructed with top!=dmax. */ \ + BN_ULONG *_not_const; \ + memcpy(&_not_const, &_bnum1->d, sizeof(BN_ULONG*)); \ + RAND_pseudo_bytes(&_tmp_char, 1); \ + memset((unsigned char *)(_not_const + _bnum1->top), _tmp_char, \ + (_bnum1->dmax - _bnum1->top) * sizeof(BN_ULONG)); \ + } \ + } while(0) +#ifdef BN_DEBUG_TRIX +#undef RAND_pseudo_bytes +#endif +#else +#define bn_pollute(a) +#endif +#define bn_check_top(a) \ + do { \ + const BIGNUM *_bnum2 = (a); \ + if (_bnum2 != NULL) { \ + assert((_bnum2->top == 0) || \ + (_bnum2->d[_bnum2->top - 1] != 0)); \ + bn_pollute(_bnum2); \ + } \ + } while(0) + +#define bn_fix_top(a) bn_check_top(a) + +#else /* !BN_DEBUG */ + +#define bn_pollute(a) +#define bn_check_top(a) +#define bn_fix_top(a) bn_correct_top(a) + +#endif + +#define bn_correct_top(a) \ + { \ + BN_ULONG *ftl; \ + if ((a)->top > 0) \ + { \ + for (ftl= &((a)->d[(a)->top-1]); (a)->top > 0; (a)->top--) \ + if (*(ftl--)) break; \ + } \ + bn_pollute(a); \ + } + +BN_ULONG bn_mul_add_words(BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w); +BN_ULONG bn_mul_words(BN_ULONG *rp, const BN_ULONG *ap, int num, BN_ULONG w); +void bn_sqr_words(BN_ULONG *rp, const BN_ULONG *ap, int num); +BN_ULONG bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d); +BN_ULONG bn_add_words(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,int num); +BN_ULONG bn_sub_words(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,int num); + +/* Primes from RFC 2409 */ +BIGNUM *get_rfc2409_prime_768(BIGNUM *bn); +BIGNUM *get_rfc2409_prime_1024(BIGNUM *bn); + +/* Primes from RFC 3526 */ +BIGNUM *get_rfc3526_prime_1536(BIGNUM *bn); +BIGNUM *get_rfc3526_prime_2048(BIGNUM *bn); +BIGNUM *get_rfc3526_prime_3072(BIGNUM *bn); +BIGNUM *get_rfc3526_prime_4096(BIGNUM *bn); +BIGNUM *get_rfc3526_prime_6144(BIGNUM *bn); +BIGNUM *get_rfc3526_prime_8192(BIGNUM *bn); + +int BN_bntest_rand(BIGNUM *rnd, int bits, int top,int bottom); + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_BN_strings(void); + +/* Error codes for the BN functions. */ + +/* Function codes. */ +#define BN_F_BNRAND 127 +#define BN_F_BN_BLINDING_CONVERT_EX 100 +#define BN_F_BN_BLINDING_CREATE_PARAM 128 +#define BN_F_BN_BLINDING_INVERT_EX 101 +#define BN_F_BN_BLINDING_NEW 102 +#define BN_F_BN_BLINDING_UPDATE 103 +#define BN_F_BN_BN2DEC 104 +#define BN_F_BN_BN2HEX 105 +#define BN_F_BN_CTX_GET 116 +#define BN_F_BN_CTX_NEW 106 +#define BN_F_BN_CTX_START 129 +#define BN_F_BN_DIV 107 +#define BN_F_BN_DIV_RECP 130 +#define BN_F_BN_EXP 123 +#define BN_F_BN_EXPAND2 108 +#define BN_F_BN_EXPAND_INTERNAL 120 +#define BN_F_BN_GF2M_MOD 131 +#define BN_F_BN_GF2M_MOD_EXP 132 +#define BN_F_BN_GF2M_MOD_MUL 133 +#define BN_F_BN_GF2M_MOD_SOLVE_QUAD 134 +#define BN_F_BN_GF2M_MOD_SOLVE_QUAD_ARR 135 +#define BN_F_BN_GF2M_MOD_SQR 136 +#define BN_F_BN_GF2M_MOD_SQRT 137 +#define BN_F_BN_MOD_EXP2_MONT 118 +#define BN_F_BN_MOD_EXP_MONT 109 +#define BN_F_BN_MOD_EXP_MONT_CONSTTIME 124 +#define BN_F_BN_MOD_EXP_MONT_WORD 117 +#define BN_F_BN_MOD_EXP_RECP 125 +#define BN_F_BN_MOD_EXP_SIMPLE 126 +#define BN_F_BN_MOD_INVERSE 110 +#define BN_F_BN_MOD_LSHIFT_QUICK 119 +#define BN_F_BN_MOD_MUL_RECIPROCAL 111 +#define BN_F_BN_MOD_SQRT 121 +#define BN_F_BN_MPI2BN 112 +#define BN_F_BN_NEW 113 +#define BN_F_BN_RAND 114 +#define BN_F_BN_RAND_RANGE 122 +#define BN_F_BN_USUB 115 + +/* Reason codes. */ +#define BN_R_ARG2_LT_ARG3 100 +#define BN_R_BAD_RECIPROCAL 101 +#define BN_R_BIGNUM_TOO_LONG 114 +#define BN_R_CALLED_WITH_EVEN_MODULUS 102 +#define BN_R_DIV_BY_ZERO 103 +#define BN_R_ENCODING_ERROR 104 +#define BN_R_EXPAND_ON_STATIC_BIGNUM_DATA 105 +#define BN_R_INPUT_NOT_REDUCED 110 +#define BN_R_INVALID_LENGTH 106 +#define BN_R_INVALID_RANGE 115 +#define BN_R_NOT_A_SQUARE 111 +#define BN_R_NOT_INITIALIZED 107 +#define BN_R_NO_INVERSE 108 +#define BN_R_NO_SOLUTION 116 +#define BN_R_P_IS_NOT_PRIME 112 +#define BN_R_TOO_MANY_ITERATIONS 113 +#define BN_R_TOO_MANY_TEMPORARY_VARIABLES 109 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/buffer.h b/production/3rdparty/openssl/include/openssl/buffer.h new file mode 100644 index 00000000..1db96074 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/buffer.h @@ -0,0 +1,118 @@ +/* crypto/buffer/buffer.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_BUFFER_H +#define HEADER_BUFFER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#if !defined(NO_SYS_TYPES_H) +#include +#endif + +/* Already declared in ossl_typ.h */ +/* typedef struct buf_mem_st BUF_MEM; */ + +struct buf_mem_st + { + int length; /* current number of bytes */ + char *data; + int max; /* size of buffer */ + }; + +BUF_MEM *BUF_MEM_new(void); +void BUF_MEM_free(BUF_MEM *a); +int BUF_MEM_grow(BUF_MEM *str, int len); +int BUF_MEM_grow_clean(BUF_MEM *str, int len); +char * BUF_strdup(const char *str); +char * BUF_strndup(const char *str, size_t siz); +void * BUF_memdup(const void *data, size_t siz); + +/* safe string functions */ +size_t BUF_strlcpy(char *dst,const char *src,size_t siz); +size_t BUF_strlcat(char *dst,const char *src,size_t siz); + + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_BUF_strings(void); + +/* Error codes for the BUF functions. */ + +/* Function codes. */ +#define BUF_F_BUF_MEMDUP 103 +#define BUF_F_BUF_MEM_GROW 100 +#define BUF_F_BUF_MEM_GROW_CLEAN 105 +#define BUF_F_BUF_MEM_NEW 101 +#define BUF_F_BUF_STRDUP 102 +#define BUF_F_BUF_STRNDUP 104 + +/* Reason codes. */ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/cast.h b/production/3rdparty/openssl/include/openssl/cast.h new file mode 100644 index 00000000..90b45b95 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/cast.h @@ -0,0 +1,105 @@ +/* crypto/cast/cast.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_CAST_H +#define HEADER_CAST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef OPENSSL_NO_CAST +#error CAST is disabled. +#endif + +#define CAST_ENCRYPT 1 +#define CAST_DECRYPT 0 + +#define CAST_LONG unsigned long + +#define CAST_BLOCK 8 +#define CAST_KEY_LENGTH 16 + +typedef struct cast_key_st + { + CAST_LONG data[32]; + int short_key; /* Use reduced rounds for short key */ + } CAST_KEY; + + +void CAST_set_key(CAST_KEY *key, int len, const unsigned char *data); +void CAST_ecb_encrypt(const unsigned char *in,unsigned char *out,CAST_KEY *key, + int enc); +void CAST_encrypt(CAST_LONG *data,CAST_KEY *key); +void CAST_decrypt(CAST_LONG *data,CAST_KEY *key); +void CAST_cbc_encrypt(const unsigned char *in, unsigned char *out, long length, + CAST_KEY *ks, unsigned char *iv, int enc); +void CAST_cfb64_encrypt(const unsigned char *in, unsigned char *out, + long length, CAST_KEY *schedule, unsigned char *ivec, + int *num, int enc); +void CAST_ofb64_encrypt(const unsigned char *in, unsigned char *out, + long length, CAST_KEY *schedule, unsigned char *ivec, + int *num); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/comp.h b/production/3rdparty/openssl/include/openssl/comp.h new file mode 100644 index 00000000..5d59354a --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/comp.h @@ -0,0 +1,66 @@ + +#ifndef HEADER_COMP_H +#define HEADER_COMP_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct comp_ctx_st COMP_CTX; + +typedef struct comp_method_st + { + int type; /* NID for compression library */ + const char *name; /* A text string to identify the library */ + int (*init)(COMP_CTX *ctx); + void (*finish)(COMP_CTX *ctx); + int (*compress)(COMP_CTX *ctx, + unsigned char *out, unsigned int olen, + unsigned char *in, unsigned int ilen); + int (*expand)(COMP_CTX *ctx, + unsigned char *out, unsigned int olen, + unsigned char *in, unsigned int ilen); + /* The following two do NOTHING, but are kept for backward compatibility */ + long (*ctrl)(void); + long (*callback_ctrl)(void); + } COMP_METHOD; + +struct comp_ctx_st + { + COMP_METHOD *meth; + unsigned long compress_in; + unsigned long compress_out; + unsigned long expand_in; + unsigned long expand_out; + + CRYPTO_EX_DATA ex_data; + }; + + +COMP_CTX *COMP_CTX_new(COMP_METHOD *meth); +void COMP_CTX_free(COMP_CTX *ctx); +int COMP_compress_block(COMP_CTX *ctx, unsigned char *out, int olen, + unsigned char *in, int ilen); +int COMP_expand_block(COMP_CTX *ctx, unsigned char *out, int olen, + unsigned char *in, int ilen); +COMP_METHOD *COMP_rle(void ); +COMP_METHOD *COMP_zlib(void ); + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_COMP_strings(void); + +/* Error codes for the COMP functions. */ + +/* Function codes. */ + +/* Reason codes. */ + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/conf.h b/production/3rdparty/openssl/include/openssl/conf.h new file mode 100644 index 00000000..4c073dd8 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/conf.h @@ -0,0 +1,253 @@ +/* crypto/conf/conf.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_CONF_H +#define HEADER_CONF_H + +#include +#include +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct + { + char *section; + char *name; + char *value; + } CONF_VALUE; + +DECLARE_STACK_OF(CONF_VALUE) +DECLARE_STACK_OF(CONF_MODULE) +DECLARE_STACK_OF(CONF_IMODULE) + +struct conf_st; +struct conf_method_st; +typedef struct conf_method_st CONF_METHOD; + +struct conf_method_st + { + const char *name; + CONF *(*create)(CONF_METHOD *meth); + int (*init)(CONF *conf); + int (*destroy)(CONF *conf); + int (*destroy_data)(CONF *conf); + int (*load_bio)(CONF *conf, BIO *bp, long *eline); + int (*dump)(const CONF *conf, BIO *bp); + int (*is_number)(const CONF *conf, char c); + int (*to_int)(const CONF *conf, char c); + int (*load)(CONF *conf, const char *name, long *eline); + }; + +/* Module definitions */ + +typedef struct conf_imodule_st CONF_IMODULE; +typedef struct conf_module_st CONF_MODULE; + +/* DSO module function typedefs */ +typedef int conf_init_func(CONF_IMODULE *md, const CONF *cnf); +typedef void conf_finish_func(CONF_IMODULE *md); + +#define CONF_MFLAGS_IGNORE_ERRORS 0x1 +#define CONF_MFLAGS_IGNORE_RETURN_CODES 0x2 +#define CONF_MFLAGS_SILENT 0x4 +#define CONF_MFLAGS_NO_DSO 0x8 +#define CONF_MFLAGS_IGNORE_MISSING_FILE 0x10 + +int CONF_set_default_method(CONF_METHOD *meth); +void CONF_set_nconf(CONF *conf,LHASH *hash); +LHASH *CONF_load(LHASH *conf,const char *file,long *eline); +#ifndef OPENSSL_NO_FP_API +LHASH *CONF_load_fp(LHASH *conf, FILE *fp,long *eline); +#endif +LHASH *CONF_load_bio(LHASH *conf, BIO *bp,long *eline); +STACK_OF(CONF_VALUE) *CONF_get_section(LHASH *conf,const char *section); +char *CONF_get_string(LHASH *conf,const char *group,const char *name); +long CONF_get_number(LHASH *conf,const char *group,const char *name); +void CONF_free(LHASH *conf); +int CONF_dump_fp(LHASH *conf, FILE *out); +int CONF_dump_bio(LHASH *conf, BIO *out); + +void OPENSSL_config(const char *config_name); +void OPENSSL_no_config(void); + +/* New conf code. The semantics are different from the functions above. + If that wasn't the case, the above functions would have been replaced */ + +struct conf_st + { + CONF_METHOD *meth; + void *meth_data; + LHASH *data; + }; + +CONF *NCONF_new(CONF_METHOD *meth); +CONF_METHOD *NCONF_default(void); +CONF_METHOD *NCONF_WIN32(void); +#if 0 /* Just to give you an idea of what I have in mind */ +CONF_METHOD *NCONF_XML(void); +#endif +void NCONF_free(CONF *conf); +void NCONF_free_data(CONF *conf); + +int NCONF_load(CONF *conf,const char *file,long *eline); +#ifndef OPENSSL_NO_FP_API +int NCONF_load_fp(CONF *conf, FILE *fp,long *eline); +#endif +int NCONF_load_bio(CONF *conf, BIO *bp,long *eline); +STACK_OF(CONF_VALUE) *NCONF_get_section(const CONF *conf,const char *section); +char *NCONF_get_string(const CONF *conf,const char *group,const char *name); +int NCONF_get_number_e(const CONF *conf,const char *group,const char *name, + long *result); +int NCONF_dump_fp(const CONF *conf, FILE *out); +int NCONF_dump_bio(const CONF *conf, BIO *out); + +#if 0 /* The following function has no error checking, + and should therefore be avoided */ +long NCONF_get_number(CONF *conf,char *group,char *name); +#else +#define NCONF_get_number(c,g,n,r) NCONF_get_number_e(c,g,n,r) +#endif + +/* Module functions */ + +int CONF_modules_load(const CONF *cnf, const char *appname, + unsigned long flags); +int CONF_modules_load_file(const char *filename, const char *appname, + unsigned long flags); +void CONF_modules_unload(int all); +void CONF_modules_finish(void); +void CONF_modules_free(void); +int CONF_module_add(const char *name, conf_init_func *ifunc, + conf_finish_func *ffunc); + +const char *CONF_imodule_get_name(const CONF_IMODULE *md); +const char *CONF_imodule_get_value(const CONF_IMODULE *md); +void *CONF_imodule_get_usr_data(const CONF_IMODULE *md); +void CONF_imodule_set_usr_data(CONF_IMODULE *md, void *usr_data); +CONF_MODULE *CONF_imodule_get_module(const CONF_IMODULE *md); +unsigned long CONF_imodule_get_flags(const CONF_IMODULE *md); +void CONF_imodule_set_flags(CONF_IMODULE *md, unsigned long flags); +void *CONF_module_get_usr_data(CONF_MODULE *pmod); +void CONF_module_set_usr_data(CONF_MODULE *pmod, void *usr_data); + +char *CONF_get1_default_config_file(void); + +int CONF_parse_list(const char *list, int sep, int nospc, + int (*list_cb)(const char *elem, int len, void *usr), void *arg); + +void OPENSSL_load_builtin_modules(void); + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_CONF_strings(void); + +/* Error codes for the CONF functions. */ + +/* Function codes. */ +#define CONF_F_CONF_DUMP_FP 104 +#define CONF_F_CONF_LOAD 100 +#define CONF_F_CONF_LOAD_BIO 102 +#define CONF_F_CONF_LOAD_FP 103 +#define CONF_F_CONF_MODULES_LOAD 116 +#define CONF_F_DEF_LOAD 120 +#define CONF_F_DEF_LOAD_BIO 121 +#define CONF_F_MODULE_INIT 115 +#define CONF_F_MODULE_LOAD_DSO 117 +#define CONF_F_MODULE_RUN 118 +#define CONF_F_NCONF_DUMP_BIO 105 +#define CONF_F_NCONF_DUMP_FP 106 +#define CONF_F_NCONF_GET_NUMBER 107 +#define CONF_F_NCONF_GET_NUMBER_E 112 +#define CONF_F_NCONF_GET_SECTION 108 +#define CONF_F_NCONF_GET_STRING 109 +#define CONF_F_NCONF_LOAD 113 +#define CONF_F_NCONF_LOAD_BIO 110 +#define CONF_F_NCONF_LOAD_FP 114 +#define CONF_F_NCONF_NEW 111 +#define CONF_F_STR_COPY 101 + +/* Reason codes. */ +#define CONF_R_ERROR_LOADING_DSO 110 +#define CONF_R_MISSING_CLOSE_SQUARE_BRACKET 100 +#define CONF_R_MISSING_EQUAL_SIGN 101 +#define CONF_R_MISSING_FINISH_FUNCTION 111 +#define CONF_R_MISSING_INIT_FUNCTION 112 +#define CONF_R_MODULE_INITIALIZATION_ERROR 109 +#define CONF_R_NO_CLOSE_BRACE 102 +#define CONF_R_NO_CONF 105 +#define CONF_R_NO_CONF_OR_ENVIRONMENT_VARIABLE 106 +#define CONF_R_NO_SECTION 107 +#define CONF_R_NO_SUCH_FILE 114 +#define CONF_R_NO_VALUE 108 +#define CONF_R_UNABLE_TO_CREATE_NEW_SECTION 103 +#define CONF_R_UNKNOWN_MODULE_NAME 113 +#define CONF_R_VARIABLE_HAS_NO_VALUE 104 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/conf_api.h b/production/3rdparty/openssl/include/openssl/conf_api.h new file mode 100644 index 00000000..87a954af --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/conf_api.h @@ -0,0 +1,89 @@ +/* conf_api.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_CONF_API_H +#define HEADER_CONF_API_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Up until OpenSSL 0.9.5a, this was new_section */ +CONF_VALUE *_CONF_new_section(CONF *conf, const char *section); +/* Up until OpenSSL 0.9.5a, this was get_section */ +CONF_VALUE *_CONF_get_section(const CONF *conf, const char *section); +/* Up until OpenSSL 0.9.5a, this was CONF_get_section */ +STACK_OF(CONF_VALUE) *_CONF_get_section_values(const CONF *conf, + const char *section); + +int _CONF_add_string(CONF *conf, CONF_VALUE *section, CONF_VALUE *value); +char *_CONF_get_string(const CONF *conf, const char *section, + const char *name); +long _CONF_get_number(const CONF *conf, const char *section, const char *name); + +int _CONF_new_data(CONF *conf); +void _CONF_free_data(CONF *conf); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/production/3rdparty/openssl/include/openssl/crypto.h b/production/3rdparty/openssl/include/openssl/crypto.h new file mode 100644 index 00000000..d2b5ffe3 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/crypto.h @@ -0,0 +1,550 @@ +/* crypto/crypto.h */ +/* ==================================================================== + * Copyright (c) 1998-2003 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * ECDH support in OpenSSL originally developed by + * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. + */ + +#ifndef HEADER_CRYPTO_H +#define HEADER_CRYPTO_H + +#include + +#include + +#ifndef OPENSSL_NO_FP_API +#include +#endif + +#include +#include +#include +#include + +#ifdef CHARSET_EBCDIC +#include +#endif + +/* Resolve problems on some operating systems with symbol names that clash + one way or another */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Backward compatibility to SSLeay */ +/* This is more to be used to check the correct DLL is being used + * in the MS world. */ +#define SSLEAY_VERSION_NUMBER OPENSSL_VERSION_NUMBER +#define SSLEAY_VERSION 0 +/* #define SSLEAY_OPTIONS 1 no longer supported */ +#define SSLEAY_CFLAGS 2 +#define SSLEAY_BUILT_ON 3 +#define SSLEAY_PLATFORM 4 +#define SSLEAY_DIR 5 + +/* Already declared in ossl_typ.h */ +#if 0 +typedef struct crypto_ex_data_st CRYPTO_EX_DATA; +/* Called when a new object is created */ +typedef int CRYPTO_EX_new(void *parent, void *ptr, CRYPTO_EX_DATA *ad, + int idx, long argl, void *argp); +/* Called when an object is free()ed */ +typedef void CRYPTO_EX_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, + int idx, long argl, void *argp); +/* Called when we need to dup an object */ +typedef int CRYPTO_EX_dup(CRYPTO_EX_DATA *to, CRYPTO_EX_DATA *from, void *from_d, + int idx, long argl, void *argp); +#endif + +/* A generic structure to pass assorted data in a expandable way */ +typedef struct openssl_item_st + { + int code; + void *value; /* Not used for flag attributes */ + size_t value_size; /* Max size of value for output, length for input */ + size_t *value_length; /* Returned length of value for output */ + } OPENSSL_ITEM; + + +/* When changing the CRYPTO_LOCK_* list, be sure to maintin the text lock + * names in cryptlib.c + */ + +#define CRYPTO_LOCK_ERR 1 +#define CRYPTO_LOCK_EX_DATA 2 +#define CRYPTO_LOCK_X509 3 +#define CRYPTO_LOCK_X509_INFO 4 +#define CRYPTO_LOCK_X509_PKEY 5 +#define CRYPTO_LOCK_X509_CRL 6 +#define CRYPTO_LOCK_X509_REQ 7 +#define CRYPTO_LOCK_DSA 8 +#define CRYPTO_LOCK_RSA 9 +#define CRYPTO_LOCK_EVP_PKEY 10 +#define CRYPTO_LOCK_X509_STORE 11 +#define CRYPTO_LOCK_SSL_CTX 12 +#define CRYPTO_LOCK_SSL_CERT 13 +#define CRYPTO_LOCK_SSL_SESSION 14 +#define CRYPTO_LOCK_SSL_SESS_CERT 15 +#define CRYPTO_LOCK_SSL 16 +#define CRYPTO_LOCK_SSL_METHOD 17 +#define CRYPTO_LOCK_RAND 18 +#define CRYPTO_LOCK_RAND2 19 +#define CRYPTO_LOCK_MALLOC 20 +#define CRYPTO_LOCK_BIO 21 +#define CRYPTO_LOCK_GETHOSTBYNAME 22 +#define CRYPTO_LOCK_GETSERVBYNAME 23 +#define CRYPTO_LOCK_READDIR 24 +#define CRYPTO_LOCK_RSA_BLINDING 25 +#define CRYPTO_LOCK_DH 26 +#define CRYPTO_LOCK_MALLOC2 27 +#define CRYPTO_LOCK_DSO 28 +#define CRYPTO_LOCK_DYNLOCK 29 +#define CRYPTO_LOCK_ENGINE 30 +#define CRYPTO_LOCK_UI 31 +#define CRYPTO_LOCK_ECDSA 32 +#define CRYPTO_LOCK_EC 33 +#define CRYPTO_LOCK_ECDH 34 +#define CRYPTO_LOCK_BN 35 +#define CRYPTO_LOCK_EC_PRE_COMP 36 +#define CRYPTO_LOCK_STORE 37 +#define CRYPTO_LOCK_COMP 38 +#define CRYPTO_NUM_LOCKS 39 + +#define CRYPTO_LOCK 1 +#define CRYPTO_UNLOCK 2 +#define CRYPTO_READ 4 +#define CRYPTO_WRITE 8 + +#ifndef OPENSSL_NO_LOCKING +#ifndef CRYPTO_w_lock +#define CRYPTO_w_lock(type) \ + CRYPTO_lock(CRYPTO_LOCK|CRYPTO_WRITE,type,__FILE__,__LINE__) +#define CRYPTO_w_unlock(type) \ + CRYPTO_lock(CRYPTO_UNLOCK|CRYPTO_WRITE,type,__FILE__,__LINE__) +#define CRYPTO_r_lock(type) \ + CRYPTO_lock(CRYPTO_LOCK|CRYPTO_READ,type,__FILE__,__LINE__) +#define CRYPTO_r_unlock(type) \ + CRYPTO_lock(CRYPTO_UNLOCK|CRYPTO_READ,type,__FILE__,__LINE__) +#define CRYPTO_add(addr,amount,type) \ + CRYPTO_add_lock(addr,amount,type,__FILE__,__LINE__) +#endif +#else +#define CRYPTO_w_lock(a) +#define CRYPTO_w_unlock(a) +#define CRYPTO_r_lock(a) +#define CRYPTO_r_unlock(a) +#define CRYPTO_add(a,b,c) ((*(a))+=(b)) +#endif + +/* Some applications as well as some parts of OpenSSL need to allocate + and deallocate locks in a dynamic fashion. The following typedef + makes this possible in a type-safe manner. */ +/* struct CRYPTO_dynlock_value has to be defined by the application. */ +typedef struct + { + int references; + struct CRYPTO_dynlock_value *data; + } CRYPTO_dynlock; + + +/* The following can be used to detect memory leaks in the SSLeay library. + * It used, it turns on malloc checking */ + +#define CRYPTO_MEM_CHECK_OFF 0x0 /* an enume */ +#define CRYPTO_MEM_CHECK_ON 0x1 /* a bit */ +#define CRYPTO_MEM_CHECK_ENABLE 0x2 /* a bit */ +#define CRYPTO_MEM_CHECK_DISABLE 0x3 /* an enume */ + +/* The following are bit values to turn on or off options connected to the + * malloc checking functionality */ + +/* Adds time to the memory checking information */ +#define V_CRYPTO_MDEBUG_TIME 0x1 /* a bit */ +/* Adds thread number to the memory checking information */ +#define V_CRYPTO_MDEBUG_THREAD 0x2 /* a bit */ + +#define V_CRYPTO_MDEBUG_ALL (V_CRYPTO_MDEBUG_TIME | V_CRYPTO_MDEBUG_THREAD) + + +/* predec of the BIO type */ +typedef struct bio_st BIO_dummy; + +struct crypto_ex_data_st + { + STACK *sk; + int dummy; /* gcc is screwing up this data structure :-( */ + }; + +/* This stuff is basically class callback functions + * The current classes are SSL_CTX, SSL, SSL_SESSION, and a few more */ + +typedef struct crypto_ex_data_func_st + { + long argl; /* Arbitary long */ + void *argp; /* Arbitary void * */ + CRYPTO_EX_new *new_func; + CRYPTO_EX_free *free_func; + CRYPTO_EX_dup *dup_func; + } CRYPTO_EX_DATA_FUNCS; + +DECLARE_STACK_OF(CRYPTO_EX_DATA_FUNCS) + +/* Per class, we have a STACK of CRYPTO_EX_DATA_FUNCS for each CRYPTO_EX_DATA + * entry. + */ + +#define CRYPTO_EX_INDEX_BIO 0 +#define CRYPTO_EX_INDEX_SSL 1 +#define CRYPTO_EX_INDEX_SSL_CTX 2 +#define CRYPTO_EX_INDEX_SSL_SESSION 3 +#define CRYPTO_EX_INDEX_X509_STORE 4 +#define CRYPTO_EX_INDEX_X509_STORE_CTX 5 +#define CRYPTO_EX_INDEX_RSA 6 +#define CRYPTO_EX_INDEX_DSA 7 +#define CRYPTO_EX_INDEX_DH 8 +#define CRYPTO_EX_INDEX_ENGINE 9 +#define CRYPTO_EX_INDEX_X509 10 +#define CRYPTO_EX_INDEX_UI 11 +#define CRYPTO_EX_INDEX_ECDSA 12 +#define CRYPTO_EX_INDEX_ECDH 13 +#define CRYPTO_EX_INDEX_COMP 14 +#define CRYPTO_EX_INDEX_STORE 15 + +/* Dynamically assigned indexes start from this value (don't use directly, use + * via CRYPTO_ex_data_new_class). */ +#define CRYPTO_EX_INDEX_USER 100 + + +/* This is the default callbacks, but we can have others as well: + * this is needed in Win32 where the application malloc and the + * library malloc may not be the same. + */ +#define CRYPTO_malloc_init() CRYPTO_set_mem_functions(\ + malloc, realloc, free) + +#if defined CRYPTO_MDEBUG_ALL || defined CRYPTO_MDEBUG_TIME || defined CRYPTO_MDEBUG_THREAD +# ifndef CRYPTO_MDEBUG /* avoid duplicate #define */ +# define CRYPTO_MDEBUG +# endif +#endif + +/* Set standard debugging functions (not done by default + * unless CRYPTO_MDEBUG is defined) */ +#define CRYPTO_malloc_debug_init() do {\ + CRYPTO_set_mem_debug_functions(\ + CRYPTO_dbg_malloc,\ + CRYPTO_dbg_realloc,\ + CRYPTO_dbg_free,\ + CRYPTO_dbg_set_options,\ + CRYPTO_dbg_get_options);\ + } while(0) + +int CRYPTO_mem_ctrl(int mode); +int CRYPTO_is_mem_check_on(void); + +/* for applications */ +#define MemCheck_start() CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON) +#define MemCheck_stop() CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_OFF) + +/* for library-internal use */ +#define MemCheck_on() CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ENABLE) +#define MemCheck_off() CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_DISABLE) +#define is_MemCheck_on() CRYPTO_is_mem_check_on() + +#define OPENSSL_malloc(num) CRYPTO_malloc((int)num,__FILE__,__LINE__) +#define OPENSSL_realloc(addr,num) \ + CRYPTO_realloc((char *)addr,(int)num,__FILE__,__LINE__) +#define OPENSSL_realloc_clean(addr,old_num,num) \ + CRYPTO_realloc_clean(addr,old_num,num,__FILE__,__LINE__) +#define OPENSSL_remalloc(addr,num) \ + CRYPTO_remalloc((char **)addr,(int)num,__FILE__,__LINE__) +#define OPENSSL_freeFunc CRYPTO_free +#define OPENSSL_free(addr) CRYPTO_free(addr) + +#define OPENSSL_malloc_locked(num) \ + CRYPTO_malloc_locked((int)num,__FILE__,__LINE__) +#define OPENSSL_free_locked(addr) CRYPTO_free_locked(addr) + + +const char *SSLeay_version(int type); +unsigned long SSLeay(void); + +int OPENSSL_issetugid(void); + +/* An opaque type representing an implementation of "ex_data" support */ +typedef struct st_CRYPTO_EX_DATA_IMPL CRYPTO_EX_DATA_IMPL; +/* Return an opaque pointer to the current "ex_data" implementation */ +const CRYPTO_EX_DATA_IMPL *CRYPTO_get_ex_data_implementation(void); +/* Sets the "ex_data" implementation to be used (if it's not too late) */ +int CRYPTO_set_ex_data_implementation(const CRYPTO_EX_DATA_IMPL *i); +/* Get a new "ex_data" class, and return the corresponding "class_index" */ +int CRYPTO_ex_data_new_class(void); +/* Within a given class, get/register a new index */ +int CRYPTO_get_ex_new_index(int class_index, long argl, void *argp, + CRYPTO_EX_new *new_func, CRYPTO_EX_dup *dup_func, + CRYPTO_EX_free *free_func); +/* Initialise/duplicate/free CRYPTO_EX_DATA variables corresponding to a given + * class (invokes whatever per-class callbacks are applicable) */ +int CRYPTO_new_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad); +int CRYPTO_dup_ex_data(int class_index, CRYPTO_EX_DATA *to, + CRYPTO_EX_DATA *from); +void CRYPTO_free_ex_data(int class_index, void *obj, CRYPTO_EX_DATA *ad); +/* Get/set data in a CRYPTO_EX_DATA variable corresponding to a particular index + * (relative to the class type involved) */ +int CRYPTO_set_ex_data(CRYPTO_EX_DATA *ad, int idx, void *val); +void *CRYPTO_get_ex_data(const CRYPTO_EX_DATA *ad,int idx); +/* This function cleans up all "ex_data" state. It mustn't be called under + * potential race-conditions. */ +void CRYPTO_cleanup_all_ex_data(void); + +int CRYPTO_get_new_lockid(char *name); + +int CRYPTO_num_locks(void); /* return CRYPTO_NUM_LOCKS (shared libs!) */ +void CRYPTO_lock(int mode, int type,const char *file,int line); +void CRYPTO_set_locking_callback(void (*func)(int mode,int type, + const char *file,int line)); +void (*CRYPTO_get_locking_callback(void))(int mode,int type,const char *file, + int line); +void CRYPTO_set_add_lock_callback(int (*func)(int *num,int mount,int type, + const char *file, int line)); +int (*CRYPTO_get_add_lock_callback(void))(int *num,int mount,int type, + const char *file,int line); +void CRYPTO_set_id_callback(unsigned long (*func)(void)); +unsigned long (*CRYPTO_get_id_callback(void))(void); +unsigned long CRYPTO_thread_id(void); +const char *CRYPTO_get_lock_name(int type); +int CRYPTO_add_lock(int *pointer,int amount,int type, const char *file, + int line); + +int CRYPTO_get_new_dynlockid(void); +void CRYPTO_destroy_dynlockid(int i); +struct CRYPTO_dynlock_value *CRYPTO_get_dynlock_value(int i); +void CRYPTO_set_dynlock_create_callback(struct CRYPTO_dynlock_value *(*dyn_create_function)(const char *file, int line)); +void CRYPTO_set_dynlock_lock_callback(void (*dyn_lock_function)(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line)); +void CRYPTO_set_dynlock_destroy_callback(void (*dyn_destroy_function)(struct CRYPTO_dynlock_value *l, const char *file, int line)); +struct CRYPTO_dynlock_value *(*CRYPTO_get_dynlock_create_callback(void))(const char *file,int line); +void (*CRYPTO_get_dynlock_lock_callback(void))(int mode, struct CRYPTO_dynlock_value *l, const char *file,int line); +void (*CRYPTO_get_dynlock_destroy_callback(void))(struct CRYPTO_dynlock_value *l, const char *file,int line); + +/* CRYPTO_set_mem_functions includes CRYPTO_set_locked_mem_functions -- + * call the latter last if you need different functions */ +int CRYPTO_set_mem_functions(void *(*m)(size_t),void *(*r)(void *,size_t), void (*f)(void *)); +int CRYPTO_set_locked_mem_functions(void *(*m)(size_t), void (*free_func)(void *)); +int CRYPTO_set_mem_ex_functions(void *(*m)(size_t,const char *,int), + void *(*r)(void *,size_t,const char *,int), + void (*f)(void *)); +int CRYPTO_set_locked_mem_ex_functions(void *(*m)(size_t,const char *,int), + void (*free_func)(void *)); +int CRYPTO_set_mem_debug_functions(void (*m)(void *,int,const char *,int,int), + void (*r)(void *,void *,int,const char *,int,int), + void (*f)(void *,int), + void (*so)(long), + long (*go)(void)); +void CRYPTO_get_mem_functions(void *(**m)(size_t),void *(**r)(void *, size_t), void (**f)(void *)); +void CRYPTO_get_locked_mem_functions(void *(**m)(size_t), void (**f)(void *)); +void CRYPTO_get_mem_ex_functions(void *(**m)(size_t,const char *,int), + void *(**r)(void *, size_t,const char *,int), + void (**f)(void *)); +void CRYPTO_get_locked_mem_ex_functions(void *(**m)(size_t,const char *,int), + void (**f)(void *)); +void CRYPTO_get_mem_debug_functions(void (**m)(void *,int,const char *,int,int), + void (**r)(void *,void *,int,const char *,int,int), + void (**f)(void *,int), + void (**so)(long), + long (**go)(void)); + +void *CRYPTO_malloc_locked(int num, const char *file, int line); +void CRYPTO_free_locked(void *); +void *CRYPTO_malloc(int num, const char *file, int line); +void CRYPTO_free(void *); +void *CRYPTO_realloc(void *addr,int num, const char *file, int line); +void *CRYPTO_realloc_clean(void *addr,int old_num,int num,const char *file, + int line); +void *CRYPTO_remalloc(void *addr,int num, const char *file, int line); + +void OPENSSL_cleanse(void *ptr, size_t len); + +void CRYPTO_set_mem_debug_options(long bits); +long CRYPTO_get_mem_debug_options(void); + +#define CRYPTO_push_info(info) \ + CRYPTO_push_info_(info, __FILE__, __LINE__); +int CRYPTO_push_info_(const char *info, const char *file, int line); +int CRYPTO_pop_info(void); +int CRYPTO_remove_all_info(void); + + +/* Default debugging functions (enabled by CRYPTO_malloc_debug_init() macro; + * used as default in CRYPTO_MDEBUG compilations): */ +/* The last argument has the following significance: + * + * 0: called before the actual memory allocation has taken place + * 1: called after the actual memory allocation has taken place + */ +void CRYPTO_dbg_malloc(void *addr,int num,const char *file,int line,int before_p); +void CRYPTO_dbg_realloc(void *addr1,void *addr2,int num,const char *file,int line,int before_p); +void CRYPTO_dbg_free(void *addr,int before_p); +/* Tell the debugging code about options. By default, the following values + * apply: + * + * 0: Clear all options. + * V_CRYPTO_MDEBUG_TIME (1): Set the "Show Time" option. + * V_CRYPTO_MDEBUG_THREAD (2): Set the "Show Thread Number" option. + * V_CRYPTO_MDEBUG_ALL (3): 1 + 2 + */ +void CRYPTO_dbg_set_options(long bits); +long CRYPTO_dbg_get_options(void); + + +#ifndef OPENSSL_NO_FP_API +void CRYPTO_mem_leaks_fp(FILE *); +#endif +void CRYPTO_mem_leaks(struct bio_st *bio); +/* unsigned long order, char *file, int line, int num_bytes, char *addr */ +typedef void *CRYPTO_MEM_LEAK_CB(unsigned long, const char *, int, int, void *); +void CRYPTO_mem_leaks_cb(CRYPTO_MEM_LEAK_CB *cb); + +/* die if we have to */ +void OpenSSLDie(const char *file,int line,const char *assertion); +#define OPENSSL_assert(e) (void)((e) ? 0 : (OpenSSLDie(__FILE__, __LINE__, #e),1)) + +unsigned long *OPENSSL_ia32cap_loc(void); +#define OPENSSL_ia32cap (*(OPENSSL_ia32cap_loc())) + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_CRYPTO_strings(void); + +/* Error codes for the CRYPTO functions. */ + +/* Function codes. */ +#define CRYPTO_F_CRYPTO_GET_EX_NEW_INDEX 100 +#define CRYPTO_F_CRYPTO_GET_NEW_DYNLOCKID 103 +#define CRYPTO_F_CRYPTO_GET_NEW_LOCKID 101 +#define CRYPTO_F_CRYPTO_SET_EX_DATA 102 +#define CRYPTO_F_DEF_ADD_INDEX 104 +#define CRYPTO_F_DEF_GET_CLASS 105 +#define CRYPTO_F_INT_DUP_EX_DATA 106 +#define CRYPTO_F_INT_FREE_EX_DATA 107 +#define CRYPTO_F_INT_NEW_EX_DATA 108 + +/* Reason codes. */ +#define CRYPTO_R_NO_DYNLOCK_CREATE_CALLBACK 100 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/des.h b/production/3rdparty/openssl/include/openssl/des.h new file mode 100644 index 00000000..3cbc2b56 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/des.h @@ -0,0 +1,244 @@ +/* crypto/des/des.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_NEW_DES_H +#define HEADER_NEW_DES_H + +#include /* OPENSSL_EXTERN, OPENSSL_NO_DES, + DES_LONG (via openssl/opensslconf.h */ + +#ifdef OPENSSL_NO_DES +#error DES is disabled. +#endif + +#ifdef OPENSSL_BUILD_SHLIBCRYPTO +# undef OPENSSL_EXTERN +# define OPENSSL_EXTERN OPENSSL_EXPORT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned char DES_cblock[8]; +typedef /* const */ unsigned char const_DES_cblock[8]; +/* With "const", gcc 2.8.1 on Solaris thinks that DES_cblock * + * and const_DES_cblock * are incompatible pointer types. */ + +typedef struct DES_ks + { + union + { + DES_cblock cblock; + /* make sure things are correct size on machines with + * 8 byte longs */ + DES_LONG deslong[2]; + } ks[16]; + } DES_key_schedule; + +#ifndef OPENSSL_DISABLE_OLD_DES_SUPPORT +# ifndef OPENSSL_ENABLE_OLD_DES_SUPPORT +# define OPENSSL_ENABLE_OLD_DES_SUPPORT +# endif +#endif + +#ifdef OPENSSL_ENABLE_OLD_DES_SUPPORT +# include +#endif + +#define DES_KEY_SZ (sizeof(DES_cblock)) +#define DES_SCHEDULE_SZ (sizeof(DES_key_schedule)) + +#define DES_ENCRYPT 1 +#define DES_DECRYPT 0 + +#define DES_CBC_MODE 0 +#define DES_PCBC_MODE 1 + +#define DES_ecb2_encrypt(i,o,k1,k2,e) \ + DES_ecb3_encrypt((i),(o),(k1),(k2),(k1),(e)) + +#define DES_ede2_cbc_encrypt(i,o,l,k1,k2,iv,e) \ + DES_ede3_cbc_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(e)) + +#define DES_ede2_cfb64_encrypt(i,o,l,k1,k2,iv,n,e) \ + DES_ede3_cfb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n),(e)) + +#define DES_ede2_ofb64_encrypt(i,o,l,k1,k2,iv,n) \ + DES_ede3_ofb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n)) + +OPENSSL_DECLARE_GLOBAL(int,DES_check_key); /* defaults to false */ +#define DES_check_key OPENSSL_GLOBAL_REF(DES_check_key) +OPENSSL_DECLARE_GLOBAL(int,DES_rw_mode); /* defaults to DES_PCBC_MODE */ +#define DES_rw_mode OPENSSL_GLOBAL_REF(DES_rw_mode) + +const char *DES_options(void); +void DES_ecb3_encrypt(const_DES_cblock *input, DES_cblock *output, + DES_key_schedule *ks1,DES_key_schedule *ks2, + DES_key_schedule *ks3, int enc); +DES_LONG DES_cbc_cksum(const unsigned char *input,DES_cblock *output, + long length,DES_key_schedule *schedule, + const_DES_cblock *ivec); +/* DES_cbc_encrypt does not update the IV! Use DES_ncbc_encrypt instead. */ +void DES_cbc_encrypt(const unsigned char *input,unsigned char *output, + long length,DES_key_schedule *schedule,DES_cblock *ivec, + int enc); +void DES_ncbc_encrypt(const unsigned char *input,unsigned char *output, + long length,DES_key_schedule *schedule,DES_cblock *ivec, + int enc); +void DES_xcbc_encrypt(const unsigned char *input,unsigned char *output, + long length,DES_key_schedule *schedule,DES_cblock *ivec, + const_DES_cblock *inw,const_DES_cblock *outw,int enc); +void DES_cfb_encrypt(const unsigned char *in,unsigned char *out,int numbits, + long length,DES_key_schedule *schedule,DES_cblock *ivec, + int enc); +void DES_ecb_encrypt(const_DES_cblock *input,DES_cblock *output, + DES_key_schedule *ks,int enc); + +/* This is the DES encryption function that gets called by just about + every other DES routine in the library. You should not use this + function except to implement 'modes' of DES. I say this because the + functions that call this routine do the conversion from 'char *' to + long, and this needs to be done to make sure 'non-aligned' memory + access do not occur. The characters are loaded 'little endian'. + Data is a pointer to 2 unsigned long's and ks is the + DES_key_schedule to use. enc, is non zero specifies encryption, + zero if decryption. */ +void DES_encrypt1(DES_LONG *data,DES_key_schedule *ks, int enc); + +/* This functions is the same as DES_encrypt1() except that the DES + initial permutation (IP) and final permutation (FP) have been left + out. As for DES_encrypt1(), you should not use this function. + It is used by the routines in the library that implement triple DES. + IP() DES_encrypt2() DES_encrypt2() DES_encrypt2() FP() is the same + as DES_encrypt1() DES_encrypt1() DES_encrypt1() except faster :-). */ +void DES_encrypt2(DES_LONG *data,DES_key_schedule *ks, int enc); + +void DES_encrypt3(DES_LONG *data, DES_key_schedule *ks1, + DES_key_schedule *ks2, DES_key_schedule *ks3); +void DES_decrypt3(DES_LONG *data, DES_key_schedule *ks1, + DES_key_schedule *ks2, DES_key_schedule *ks3); +void DES_ede3_cbc_encrypt(const unsigned char *input,unsigned char *output, + long length, + DES_key_schedule *ks1,DES_key_schedule *ks2, + DES_key_schedule *ks3,DES_cblock *ivec,int enc); +void DES_ede3_cbcm_encrypt(const unsigned char *in,unsigned char *out, + long length, + DES_key_schedule *ks1,DES_key_schedule *ks2, + DES_key_schedule *ks3, + DES_cblock *ivec1,DES_cblock *ivec2, + int enc); +void DES_ede3_cfb64_encrypt(const unsigned char *in,unsigned char *out, + long length,DES_key_schedule *ks1, + DES_key_schedule *ks2,DES_key_schedule *ks3, + DES_cblock *ivec,int *num,int enc); +void DES_ede3_cfb_encrypt(const unsigned char *in,unsigned char *out, + int numbits,long length,DES_key_schedule *ks1, + DES_key_schedule *ks2,DES_key_schedule *ks3, + DES_cblock *ivec,int enc); +void DES_ede3_ofb64_encrypt(const unsigned char *in,unsigned char *out, + long length,DES_key_schedule *ks1, + DES_key_schedule *ks2,DES_key_schedule *ks3, + DES_cblock *ivec,int *num); + +void DES_xwhite_in2out(const_DES_cblock *DES_key,const_DES_cblock *in_white, + DES_cblock *out_white); + +int DES_enc_read(int fd,void *buf,int len,DES_key_schedule *sched, + DES_cblock *iv); +int DES_enc_write(int fd,const void *buf,int len,DES_key_schedule *sched, + DES_cblock *iv); +char *DES_fcrypt(const char *buf,const char *salt, char *ret); +char *DES_crypt(const char *buf,const char *salt); +void DES_ofb_encrypt(const unsigned char *in,unsigned char *out,int numbits, + long length,DES_key_schedule *schedule,DES_cblock *ivec); +void DES_pcbc_encrypt(const unsigned char *input,unsigned char *output, + long length,DES_key_schedule *schedule,DES_cblock *ivec, + int enc); +DES_LONG DES_quad_cksum(const unsigned char *input,DES_cblock output[], + long length,int out_count,DES_cblock *seed); +int DES_random_key(DES_cblock *ret); +void DES_set_odd_parity(DES_cblock *key); +int DES_check_key_parity(const_DES_cblock *key); +int DES_is_weak_key(const_DES_cblock *key); +/* DES_set_key (= set_key = DES_key_sched = key_sched) calls + * DES_set_key_checked if global variable DES_check_key is set, + * DES_set_key_unchecked otherwise. */ +int DES_set_key(const_DES_cblock *key,DES_key_schedule *schedule); +int DES_key_sched(const_DES_cblock *key,DES_key_schedule *schedule); +int DES_set_key_checked(const_DES_cblock *key,DES_key_schedule *schedule); +void DES_set_key_unchecked(const_DES_cblock *key,DES_key_schedule *schedule); +void DES_string_to_key(const char *str,DES_cblock *key); +void DES_string_to_2keys(const char *str,DES_cblock *key1,DES_cblock *key2); +void DES_cfb64_encrypt(const unsigned char *in,unsigned char *out,long length, + DES_key_schedule *schedule,DES_cblock *ivec,int *num, + int enc); +void DES_ofb64_encrypt(const unsigned char *in,unsigned char *out,long length, + DES_key_schedule *schedule,DES_cblock *ivec,int *num); + +int DES_read_password(DES_cblock *key, const char *prompt, int verify); +int DES_read_2passwords(DES_cblock *key1, DES_cblock *key2, const char *prompt, + int verify); + +#define DES_fixup_key_parity DES_set_odd_parity + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/des_old.h b/production/3rdparty/openssl/include/openssl/des_old.h new file mode 100644 index 00000000..1b0620c3 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/des_old.h @@ -0,0 +1,445 @@ +/* crypto/des/des_old.h -*- mode:C; c-file-style: "eay" -*- */ + +/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + * + * The function names in here are deprecated and are only present to + * provide an interface compatible with openssl 0.9.6 and older as + * well as libdes. OpenSSL now provides functions where "des_" has + * been replaced with "DES_" in the names, to make it possible to + * make incompatible changes that are needed for C type security and + * other stuff. + * + * This include files has two compatibility modes: + * + * - If OPENSSL_DES_LIBDES_COMPATIBILITY is defined, you get an API + * that is compatible with libdes and SSLeay. + * - If OPENSSL_DES_LIBDES_COMPATIBILITY isn't defined, you get an + * API that is compatible with OpenSSL 0.9.5x to 0.9.6x. + * + * Note that these modes break earlier snapshots of OpenSSL, where + * libdes compatibility was the only available mode or (later on) the + * prefered compatibility mode. However, after much consideration + * (and more or less violent discussions with external parties), it + * was concluded that OpenSSL should be compatible with earlier versions + * of itself before anything else. Also, in all honesty, libdes is + * an old beast that shouldn't really be used any more. + * + * Please consider starting to use the DES_ functions rather than the + * des_ ones. The des_ functions will disappear completely before + * OpenSSL 1.0! + * + * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + */ + +/* Written by Richard Levitte (richard@levitte.org) for the OpenSSL + * project 2001. + */ +/* ==================================================================== + * Copyright (c) 1998-2002 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#ifndef HEADER_DES_H +#define HEADER_DES_H + +#include /* OPENSSL_EXTERN, OPENSSL_NO_DES, DES_LONG */ + +#ifdef OPENSSL_NO_DES +#error DES is disabled. +#endif + +#ifndef HEADER_NEW_DES_H +#error You must include des.h, not des_old.h directly. +#endif + +#ifdef _KERBEROS_DES_H +#error replaces . +#endif + +#include + +#ifdef OPENSSL_BUILD_SHLIBCRYPTO +# undef OPENSSL_EXTERN +# define OPENSSL_EXTERN OPENSSL_EXPORT +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _ +#undef _ +#endif + +typedef unsigned char _ossl_old_des_cblock[8]; +typedef struct _ossl_old_des_ks_struct + { + union { + _ossl_old_des_cblock _; + /* make sure things are correct size on machines with + * 8 byte longs */ + DES_LONG pad[2]; + } ks; + } _ossl_old_des_key_schedule[16]; + +#ifndef OPENSSL_DES_LIBDES_COMPATIBILITY +#define des_cblock DES_cblock +#define const_des_cblock const_DES_cblock +#define des_key_schedule DES_key_schedule +#define des_ecb3_encrypt(i,o,k1,k2,k3,e)\ + DES_ecb3_encrypt((i),(o),&(k1),&(k2),&(k3),(e)) +#define des_ede3_cbc_encrypt(i,o,l,k1,k2,k3,iv,e)\ + DES_ede3_cbc_encrypt((i),(o),(l),&(k1),&(k2),&(k3),(iv),(e)) +#define des_ede3_cbcm_encrypt(i,o,l,k1,k2,k3,iv1,iv2,e)\ + DES_ede3_cbcm_encrypt((i),(o),(l),&(k1),&(k2),&(k3),(iv1),(iv2),(e)) +#define des_ede3_cfb64_encrypt(i,o,l,k1,k2,k3,iv,n,e)\ + DES_ede3_cfb64_encrypt((i),(o),(l),&(k1),&(k2),&(k3),(iv),(n),(e)) +#define des_ede3_ofb64_encrypt(i,o,l,k1,k2,k3,iv,n)\ + DES_ede3_ofb64_encrypt((i),(o),(l),&(k1),&(k2),&(k3),(iv),(n)) +#define des_options()\ + DES_options() +#define des_cbc_cksum(i,o,l,k,iv)\ + DES_cbc_cksum((i),(o),(l),&(k),(iv)) +#define des_cbc_encrypt(i,o,l,k,iv,e)\ + DES_cbc_encrypt((i),(o),(l),&(k),(iv),(e)) +#define des_ncbc_encrypt(i,o,l,k,iv,e)\ + DES_ncbc_encrypt((i),(o),(l),&(k),(iv),(e)) +#define des_xcbc_encrypt(i,o,l,k,iv,inw,outw,e)\ + DES_xcbc_encrypt((i),(o),(l),&(k),(iv),(inw),(outw),(e)) +#define des_cfb_encrypt(i,o,n,l,k,iv,e)\ + DES_cfb_encrypt((i),(o),(n),(l),&(k),(iv),(e)) +#define des_ecb_encrypt(i,o,k,e)\ + DES_ecb_encrypt((i),(o),&(k),(e)) +#define des_encrypt1(d,k,e)\ + DES_encrypt1((d),&(k),(e)) +#define des_encrypt2(d,k,e)\ + DES_encrypt2((d),&(k),(e)) +#define des_encrypt3(d,k1,k2,k3)\ + DES_encrypt3((d),&(k1),&(k2),&(k3)) +#define des_decrypt3(d,k1,k2,k3)\ + DES_decrypt3((d),&(k1),&(k2),&(k3)) +#define des_xwhite_in2out(k,i,o)\ + DES_xwhite_in2out((k),(i),(o)) +#define des_enc_read(f,b,l,k,iv)\ + DES_enc_read((f),(b),(l),&(k),(iv)) +#define des_enc_write(f,b,l,k,iv)\ + DES_enc_write((f),(b),(l),&(k),(iv)) +#define des_fcrypt(b,s,r)\ + DES_fcrypt((b),(s),(r)) +#if 0 +#define des_crypt(b,s)\ + DES_crypt((b),(s)) +#if !defined(PERL5) && !defined(__FreeBSD__) && !defined(NeXT) && !defined(__OpenBSD__) +#define crypt(b,s)\ + DES_crypt((b),(s)) +#endif +#endif +#define des_ofb_encrypt(i,o,n,l,k,iv)\ + DES_ofb_encrypt((i),(o),(n),(l),&(k),(iv)) +#define des_pcbc_encrypt(i,o,l,k,iv,e)\ + DES_pcbc_encrypt((i),(o),(l),&(k),(iv),(e)) +#define des_quad_cksum(i,o,l,c,s)\ + DES_quad_cksum((i),(o),(l),(c),(s)) +#define des_random_seed(k)\ + _ossl_096_des_random_seed((k)) +#define des_random_key(r)\ + DES_random_key((r)) +#define des_read_password(k,p,v) \ + DES_read_password((k),(p),(v)) +#define des_read_2passwords(k1,k2,p,v) \ + DES_read_2passwords((k1),(k2),(p),(v)) +#define des_set_odd_parity(k)\ + DES_set_odd_parity((k)) +#define des_check_key_parity(k)\ + DES_check_key_parity((k)) +#define des_is_weak_key(k)\ + DES_is_weak_key((k)) +#define des_set_key(k,ks)\ + DES_set_key((k),&(ks)) +#define des_key_sched(k,ks)\ + DES_key_sched((k),&(ks)) +#define des_set_key_checked(k,ks)\ + DES_set_key_checked((k),&(ks)) +#define des_set_key_unchecked(k,ks)\ + DES_set_key_unchecked((k),&(ks)) +#define des_string_to_key(s,k)\ + DES_string_to_key((s),(k)) +#define des_string_to_2keys(s,k1,k2)\ + DES_string_to_2keys((s),(k1),(k2)) +#define des_cfb64_encrypt(i,o,l,ks,iv,n,e)\ + DES_cfb64_encrypt((i),(o),(l),&(ks),(iv),(n),(e)) +#define des_ofb64_encrypt(i,o,l,ks,iv,n)\ + DES_ofb64_encrypt((i),(o),(l),&(ks),(iv),(n)) + + +#define des_ecb2_encrypt(i,o,k1,k2,e) \ + des_ecb3_encrypt((i),(o),(k1),(k2),(k1),(e)) + +#define des_ede2_cbc_encrypt(i,o,l,k1,k2,iv,e) \ + des_ede3_cbc_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(e)) + +#define des_ede2_cfb64_encrypt(i,o,l,k1,k2,iv,n,e) \ + des_ede3_cfb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n),(e)) + +#define des_ede2_ofb64_encrypt(i,o,l,k1,k2,iv,n) \ + des_ede3_ofb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n)) + +#define des_check_key DES_check_key +#define des_rw_mode DES_rw_mode +#else /* libdes compatibility */ +/* Map all symbol names to _ossl_old_des_* form, so we avoid all + clashes with libdes */ +#define des_cblock _ossl_old_des_cblock +#define des_key_schedule _ossl_old_des_key_schedule +#define des_ecb3_encrypt(i,o,k1,k2,k3,e)\ + _ossl_old_des_ecb3_encrypt((i),(o),(k1),(k2),(k3),(e)) +#define des_ede3_cbc_encrypt(i,o,l,k1,k2,k3,iv,e)\ + _ossl_old_des_ede3_cbc_encrypt((i),(o),(l),(k1),(k2),(k3),(iv),(e)) +#define des_ede3_cfb64_encrypt(i,o,l,k1,k2,k3,iv,n,e)\ + _ossl_old_des_ede3_cfb64_encrypt((i),(o),(l),(k1),(k2),(k3),(iv),(n),(e)) +#define des_ede3_ofb64_encrypt(i,o,l,k1,k2,k3,iv,n)\ + _ossl_old_des_ede3_ofb64_encrypt((i),(o),(l),(k1),(k2),(k3),(iv),(n)) +#define des_options()\ + _ossl_old_des_options() +#define des_cbc_cksum(i,o,l,k,iv)\ + _ossl_old_des_cbc_cksum((i),(o),(l),(k),(iv)) +#define des_cbc_encrypt(i,o,l,k,iv,e)\ + _ossl_old_des_cbc_encrypt((i),(o),(l),(k),(iv),(e)) +#define des_ncbc_encrypt(i,o,l,k,iv,e)\ + _ossl_old_des_ncbc_encrypt((i),(o),(l),(k),(iv),(e)) +#define des_xcbc_encrypt(i,o,l,k,iv,inw,outw,e)\ + _ossl_old_des_xcbc_encrypt((i),(o),(l),(k),(iv),(inw),(outw),(e)) +#define des_cfb_encrypt(i,o,n,l,k,iv,e)\ + _ossl_old_des_cfb_encrypt((i),(o),(n),(l),(k),(iv),(e)) +#define des_ecb_encrypt(i,o,k,e)\ + _ossl_old_des_ecb_encrypt((i),(o),(k),(e)) +#define des_encrypt(d,k,e)\ + _ossl_old_des_encrypt((d),(k),(e)) +#define des_encrypt2(d,k,e)\ + _ossl_old_des_encrypt2((d),(k),(e)) +#define des_encrypt3(d,k1,k2,k3)\ + _ossl_old_des_encrypt3((d),(k1),(k2),(k3)) +#define des_decrypt3(d,k1,k2,k3)\ + _ossl_old_des_decrypt3((d),(k1),(k2),(k3)) +#define des_xwhite_in2out(k,i,o)\ + _ossl_old_des_xwhite_in2out((k),(i),(o)) +#define des_enc_read(f,b,l,k,iv)\ + _ossl_old_des_enc_read((f),(b),(l),(k),(iv)) +#define des_enc_write(f,b,l,k,iv)\ + _ossl_old_des_enc_write((f),(b),(l),(k),(iv)) +#define des_fcrypt(b,s,r)\ + _ossl_old_des_fcrypt((b),(s),(r)) +#define des_crypt(b,s)\ + _ossl_old_des_crypt((b),(s)) +#if 0 +#define crypt(b,s)\ + _ossl_old_crypt((b),(s)) +#endif +#define des_ofb_encrypt(i,o,n,l,k,iv)\ + _ossl_old_des_ofb_encrypt((i),(o),(n),(l),(k),(iv)) +#define des_pcbc_encrypt(i,o,l,k,iv,e)\ + _ossl_old_des_pcbc_encrypt((i),(o),(l),(k),(iv),(e)) +#define des_quad_cksum(i,o,l,c,s)\ + _ossl_old_des_quad_cksum((i),(o),(l),(c),(s)) +#define des_random_seed(k)\ + _ossl_old_des_random_seed((k)) +#define des_random_key(r)\ + _ossl_old_des_random_key((r)) +#define des_read_password(k,p,v) \ + _ossl_old_des_read_password((k),(p),(v)) +#define des_read_2passwords(k1,k2,p,v) \ + _ossl_old_des_read_2passwords((k1),(k2),(p),(v)) +#define des_set_odd_parity(k)\ + _ossl_old_des_set_odd_parity((k)) +#define des_is_weak_key(k)\ + _ossl_old_des_is_weak_key((k)) +#define des_set_key(k,ks)\ + _ossl_old_des_set_key((k),(ks)) +#define des_key_sched(k,ks)\ + _ossl_old_des_key_sched((k),(ks)) +#define des_string_to_key(s,k)\ + _ossl_old_des_string_to_key((s),(k)) +#define des_string_to_2keys(s,k1,k2)\ + _ossl_old_des_string_to_2keys((s),(k1),(k2)) +#define des_cfb64_encrypt(i,o,l,ks,iv,n,e)\ + _ossl_old_des_cfb64_encrypt((i),(o),(l),(ks),(iv),(n),(e)) +#define des_ofb64_encrypt(i,o,l,ks,iv,n)\ + _ossl_old_des_ofb64_encrypt((i),(o),(l),(ks),(iv),(n)) + + +#define des_ecb2_encrypt(i,o,k1,k2,e) \ + des_ecb3_encrypt((i),(o),(k1),(k2),(k1),(e)) + +#define des_ede2_cbc_encrypt(i,o,l,k1,k2,iv,e) \ + des_ede3_cbc_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(e)) + +#define des_ede2_cfb64_encrypt(i,o,l,k1,k2,iv,n,e) \ + des_ede3_cfb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n),(e)) + +#define des_ede2_ofb64_encrypt(i,o,l,k1,k2,iv,n) \ + des_ede3_ofb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n)) + +#define des_check_key DES_check_key +#define des_rw_mode DES_rw_mode +#endif + +const char *_ossl_old_des_options(void); +void _ossl_old_des_ecb3_encrypt(_ossl_old_des_cblock *input,_ossl_old_des_cblock *output, + _ossl_old_des_key_schedule ks1,_ossl_old_des_key_schedule ks2, + _ossl_old_des_key_schedule ks3, int enc); +DES_LONG _ossl_old_des_cbc_cksum(_ossl_old_des_cblock *input,_ossl_old_des_cblock *output, + long length,_ossl_old_des_key_schedule schedule,_ossl_old_des_cblock *ivec); +void _ossl_old_des_cbc_encrypt(_ossl_old_des_cblock *input,_ossl_old_des_cblock *output,long length, + _ossl_old_des_key_schedule schedule,_ossl_old_des_cblock *ivec,int enc); +void _ossl_old_des_ncbc_encrypt(_ossl_old_des_cblock *input,_ossl_old_des_cblock *output,long length, + _ossl_old_des_key_schedule schedule,_ossl_old_des_cblock *ivec,int enc); +void _ossl_old_des_xcbc_encrypt(_ossl_old_des_cblock *input,_ossl_old_des_cblock *output,long length, + _ossl_old_des_key_schedule schedule,_ossl_old_des_cblock *ivec, + _ossl_old_des_cblock *inw,_ossl_old_des_cblock *outw,int enc); +void _ossl_old_des_cfb_encrypt(unsigned char *in,unsigned char *out,int numbits, + long length,_ossl_old_des_key_schedule schedule,_ossl_old_des_cblock *ivec,int enc); +void _ossl_old_des_ecb_encrypt(_ossl_old_des_cblock *input,_ossl_old_des_cblock *output, + _ossl_old_des_key_schedule ks,int enc); +void _ossl_old_des_encrypt(DES_LONG *data,_ossl_old_des_key_schedule ks, int enc); +void _ossl_old_des_encrypt2(DES_LONG *data,_ossl_old_des_key_schedule ks, int enc); +void _ossl_old_des_encrypt3(DES_LONG *data, _ossl_old_des_key_schedule ks1, + _ossl_old_des_key_schedule ks2, _ossl_old_des_key_schedule ks3); +void _ossl_old_des_decrypt3(DES_LONG *data, _ossl_old_des_key_schedule ks1, + _ossl_old_des_key_schedule ks2, _ossl_old_des_key_schedule ks3); +void _ossl_old_des_ede3_cbc_encrypt(_ossl_old_des_cblock *input, _ossl_old_des_cblock *output, + long length, _ossl_old_des_key_schedule ks1, _ossl_old_des_key_schedule ks2, + _ossl_old_des_key_schedule ks3, _ossl_old_des_cblock *ivec, int enc); +void _ossl_old_des_ede3_cfb64_encrypt(unsigned char *in, unsigned char *out, + long length, _ossl_old_des_key_schedule ks1, _ossl_old_des_key_schedule ks2, + _ossl_old_des_key_schedule ks3, _ossl_old_des_cblock *ivec, int *num, int enc); +void _ossl_old_des_ede3_ofb64_encrypt(unsigned char *in, unsigned char *out, + long length, _ossl_old_des_key_schedule ks1, _ossl_old_des_key_schedule ks2, + _ossl_old_des_key_schedule ks3, _ossl_old_des_cblock *ivec, int *num); + +void _ossl_old_des_xwhite_in2out(_ossl_old_des_cblock (*des_key), _ossl_old_des_cblock (*in_white), + _ossl_old_des_cblock (*out_white)); + +int _ossl_old_des_enc_read(int fd,char *buf,int len,_ossl_old_des_key_schedule sched, + _ossl_old_des_cblock *iv); +int _ossl_old_des_enc_write(int fd,char *buf,int len,_ossl_old_des_key_schedule sched, + _ossl_old_des_cblock *iv); +char *_ossl_old_des_fcrypt(const char *buf,const char *salt, char *ret); +char *_ossl_old_des_crypt(const char *buf,const char *salt); +#if !defined(PERL5) && !defined(NeXT) +char *_ossl_old_crypt(const char *buf,const char *salt); +#endif +void _ossl_old_des_ofb_encrypt(unsigned char *in,unsigned char *out, + int numbits,long length,_ossl_old_des_key_schedule schedule,_ossl_old_des_cblock *ivec); +void _ossl_old_des_pcbc_encrypt(_ossl_old_des_cblock *input,_ossl_old_des_cblock *output,long length, + _ossl_old_des_key_schedule schedule,_ossl_old_des_cblock *ivec,int enc); +DES_LONG _ossl_old_des_quad_cksum(_ossl_old_des_cblock *input,_ossl_old_des_cblock *output, + long length,int out_count,_ossl_old_des_cblock *seed); +void _ossl_old_des_random_seed(_ossl_old_des_cblock key); +void _ossl_old_des_random_key(_ossl_old_des_cblock ret); +int _ossl_old_des_read_password(_ossl_old_des_cblock *key,const char *prompt,int verify); +int _ossl_old_des_read_2passwords(_ossl_old_des_cblock *key1,_ossl_old_des_cblock *key2, + const char *prompt,int verify); +void _ossl_old_des_set_odd_parity(_ossl_old_des_cblock *key); +int _ossl_old_des_is_weak_key(_ossl_old_des_cblock *key); +int _ossl_old_des_set_key(_ossl_old_des_cblock *key,_ossl_old_des_key_schedule schedule); +int _ossl_old_des_key_sched(_ossl_old_des_cblock *key,_ossl_old_des_key_schedule schedule); +void _ossl_old_des_string_to_key(char *str,_ossl_old_des_cblock *key); +void _ossl_old_des_string_to_2keys(char *str,_ossl_old_des_cblock *key1,_ossl_old_des_cblock *key2); +void _ossl_old_des_cfb64_encrypt(unsigned char *in, unsigned char *out, long length, + _ossl_old_des_key_schedule schedule, _ossl_old_des_cblock *ivec, int *num, int enc); +void _ossl_old_des_ofb64_encrypt(unsigned char *in, unsigned char *out, long length, + _ossl_old_des_key_schedule schedule, _ossl_old_des_cblock *ivec, int *num); + +void _ossl_096_des_random_seed(des_cblock *key); + +/* The following definitions provide compatibility with the MIT Kerberos + * library. The _ossl_old_des_key_schedule structure is not binary compatible. */ + +#define _KERBEROS_DES_H + +#define KRBDES_ENCRYPT DES_ENCRYPT +#define KRBDES_DECRYPT DES_DECRYPT + +#ifdef KERBEROS +# define ENCRYPT DES_ENCRYPT +# define DECRYPT DES_DECRYPT +#endif + +#ifndef NCOMPAT +# define C_Block des_cblock +# define Key_schedule des_key_schedule +# define KEY_SZ DES_KEY_SZ +# define string_to_key des_string_to_key +# define read_pw_string des_read_pw_string +# define random_key des_random_key +# define pcbc_encrypt des_pcbc_encrypt +# define set_key des_set_key +# define key_sched des_key_sched +# define ecb_encrypt des_ecb_encrypt +# define cbc_encrypt des_cbc_encrypt +# define ncbc_encrypt des_ncbc_encrypt +# define xcbc_encrypt des_xcbc_encrypt +# define cbc_cksum des_cbc_cksum +# define quad_cksum des_quad_cksum +# define check_parity des_check_key_parity +#endif + +#define des_fixup_key_parity DES_fixup_key_parity + +#ifdef __cplusplus +} +#endif + +/* for DES_read_pw_string et al */ +#include + +#endif diff --git a/production/3rdparty/openssl/include/openssl/dh.h b/production/3rdparty/openssl/include/openssl/dh.h new file mode 100644 index 00000000..4d0c5653 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/dh.h @@ -0,0 +1,229 @@ +/* crypto/dh/dh.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_DH_H +#define HEADER_DH_H + +#include + +#ifdef OPENSSL_NO_DH +#error DH is disabled. +#endif + +#ifndef OPENSSL_NO_BIO +#include +#endif +#include +#ifndef OPENSSL_NO_DEPRECATED +#include +#endif + +#define DH_FLAG_CACHE_MONT_P 0x01 +#define DH_FLAG_NO_EXP_CONSTTIME 0x02 /* new with 0.9.7h; the built-in DH + * implementation now uses constant time + * modular exponentiation for secret exponents + * by default. This flag causes the + * faster variable sliding window method to + * be used for all exponents. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Already defined in ossl_typ.h */ +/* typedef struct dh_st DH; */ +/* typedef struct dh_method DH_METHOD; */ + +struct dh_method + { + const char *name; + /* Methods here */ + int (*generate_key)(DH *dh); + int (*compute_key)(unsigned char *key,const BIGNUM *pub_key,DH *dh); + int (*bn_mod_exp)(const DH *dh, BIGNUM *r, const BIGNUM *a, + const BIGNUM *p, const BIGNUM *m, BN_CTX *ctx, + BN_MONT_CTX *m_ctx); /* Can be null */ + + int (*init)(DH *dh); + int (*finish)(DH *dh); + int flags; + char *app_data; + /* If this is non-NULL, it will be used to generate parameters */ + int (*generate_params)(DH *dh, int prime_len, int generator, BN_GENCB *cb); + }; + +struct dh_st + { + /* This first argument is used to pick up errors when + * a DH is passed instead of a EVP_PKEY */ + int pad; + int version; + BIGNUM *p; + BIGNUM *g; + long length; /* optional */ + BIGNUM *pub_key; /* g^x */ + BIGNUM *priv_key; /* x */ + + int flags; + BN_MONT_CTX *method_mont_p; + /* Place holders if we want to do X9.42 DH */ + BIGNUM *q; + BIGNUM *j; + unsigned char *seed; + int seedlen; + BIGNUM *counter; + + int references; + CRYPTO_EX_DATA ex_data; + const DH_METHOD *meth; + ENGINE *engine; + }; + +#define DH_GENERATOR_2 2 +/* #define DH_GENERATOR_3 3 */ +#define DH_GENERATOR_5 5 + +/* DH_check error codes */ +#define DH_CHECK_P_NOT_PRIME 0x01 +#define DH_CHECK_P_NOT_SAFE_PRIME 0x02 +#define DH_UNABLE_TO_CHECK_GENERATOR 0x04 +#define DH_NOT_SUITABLE_GENERATOR 0x08 + +/* DH_check_pub_key error codes */ +#define DH_CHECK_PUBKEY_TOO_SMALL 0x01 +#define DH_CHECK_PUBKEY_TOO_LARGE 0x02 + +/* primes p where (p-1)/2 is prime too are called "safe"; we define + this for backward compatibility: */ +#define DH_CHECK_P_NOT_STRONG_PRIME DH_CHECK_P_NOT_SAFE_PRIME + +#define DHparams_dup(x) ASN1_dup_of_const(DH,i2d_DHparams,d2i_DHparams,x) +#define d2i_DHparams_fp(fp,x) (DH *)ASN1_d2i_fp((char *(*)())DH_new, \ + (char *(*)())d2i_DHparams,(fp),(unsigned char **)(x)) +#define i2d_DHparams_fp(fp,x) ASN1_i2d_fp(i2d_DHparams,(fp), \ + (unsigned char *)(x)) +#define d2i_DHparams_bio(bp,x) ASN1_d2i_bio_of(DH,DH_new,d2i_DHparams,bp,x) +#define i2d_DHparams_bio(bp,x) ASN1_i2d_bio_of_const(DH,i2d_DHparams,bp,x) + +const DH_METHOD *DH_OpenSSL(void); + +void DH_set_default_method(const DH_METHOD *meth); +const DH_METHOD *DH_get_default_method(void); +int DH_set_method(DH *dh, const DH_METHOD *meth); +DH *DH_new_method(ENGINE *engine); + +DH * DH_new(void); +void DH_free(DH *dh); +int DH_up_ref(DH *dh); +int DH_size(const DH *dh); +int DH_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, + CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); +int DH_set_ex_data(DH *d, int idx, void *arg); +void *DH_get_ex_data(DH *d, int idx); + +/* Deprecated version */ +#ifndef OPENSSL_NO_DEPRECATED +DH * DH_generate_parameters(int prime_len,int generator, + void (*callback)(int,int,void *),void *cb_arg); +#endif /* !defined(OPENSSL_NO_DEPRECATED) */ + +/* New version */ +int DH_generate_parameters_ex(DH *dh, int prime_len,int generator, BN_GENCB *cb); + +int DH_check(const DH *dh,int *codes); +int DH_check_pub_key(const DH *dh,const BIGNUM *pub_key, int *codes); +int DH_generate_key(DH *dh); +int DH_compute_key(unsigned char *key,const BIGNUM *pub_key,DH *dh); +DH * d2i_DHparams(DH **a,const unsigned char **pp, long length); +int i2d_DHparams(const DH *a,unsigned char **pp); +#ifndef OPENSSL_NO_FP_API +int DHparams_print_fp(FILE *fp, const DH *x); +#endif +#ifndef OPENSSL_NO_BIO +int DHparams_print(BIO *bp, const DH *x); +#else +int DHparams_print(char *bp, const DH *x); +#endif + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_DH_strings(void); + +/* Error codes for the DH functions. */ + +/* Function codes. */ +#define DH_F_COMPUTE_KEY 102 +#define DH_F_DHPARAMS_PRINT 100 +#define DH_F_DHPARAMS_PRINT_FP 101 +#define DH_F_DH_BUILTIN_GENPARAMS 106 +#define DH_F_DH_NEW_METHOD 105 +#define DH_F_GENERATE_KEY 103 +#define DH_F_GENERATE_PARAMETERS 104 + +/* Reason codes. */ +#define DH_R_BAD_GENERATOR 101 +#define DH_R_INVALID_PUBKEY 102 +#define DH_R_NO_PRIVATE_VALUE 100 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/dsa.h b/production/3rdparty/openssl/include/openssl/dsa.h new file mode 100644 index 00000000..b12db98b --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/dsa.h @@ -0,0 +1,279 @@ +/* crypto/dsa/dsa.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +/* + * The DSS routines are based on patches supplied by + * Steven Schoch . He basically did the + * work and I have just tweaked them a little to fit into my + * stylistic vision for SSLeay :-) */ + +#ifndef HEADER_DSA_H +#define HEADER_DSA_H + +#include + +#ifdef OPENSSL_NO_DSA +#error DSA is disabled. +#endif + +#ifndef OPENSSL_NO_BIO +#include +#endif +#include +#include + +#ifndef OPENSSL_NO_DEPRECATED +#include +#ifndef OPENSSL_NO_DH +# include +#endif +#endif + +#define DSA_FLAG_CACHE_MONT_P 0x01 +#define DSA_FLAG_NO_EXP_CONSTTIME 0x02 /* new with 0.9.7h; the built-in DSA + * implementation now uses constant time + * modular exponentiation for secret exponents + * by default. This flag causes the + * faster variable sliding window method to + * be used for all exponents. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Already defined in ossl_typ.h */ +/* typedef struct dsa_st DSA; */ +/* typedef struct dsa_method DSA_METHOD; */ + +typedef struct DSA_SIG_st + { + BIGNUM *r; + BIGNUM *s; + } DSA_SIG; + +struct dsa_method + { + const char *name; + DSA_SIG * (*dsa_do_sign)(const unsigned char *dgst, int dlen, DSA *dsa); + int (*dsa_sign_setup)(DSA *dsa, BN_CTX *ctx_in, BIGNUM **kinvp, + BIGNUM **rp); + int (*dsa_do_verify)(const unsigned char *dgst, int dgst_len, + DSA_SIG *sig, DSA *dsa); + int (*dsa_mod_exp)(DSA *dsa, BIGNUM *rr, BIGNUM *a1, BIGNUM *p1, + BIGNUM *a2, BIGNUM *p2, BIGNUM *m, BN_CTX *ctx, + BN_MONT_CTX *in_mont); + int (*bn_mod_exp)(DSA *dsa, BIGNUM *r, BIGNUM *a, const BIGNUM *p, + const BIGNUM *m, BN_CTX *ctx, + BN_MONT_CTX *m_ctx); /* Can be null */ + int (*init)(DSA *dsa); + int (*finish)(DSA *dsa); + int flags; + char *app_data; + /* If this is non-NULL, it is used to generate DSA parameters */ + int (*dsa_paramgen)(DSA *dsa, int bits, + unsigned char *seed, int seed_len, + int *counter_ret, unsigned long *h_ret, + BN_GENCB *cb); + /* If this is non-NULL, it is used to generate DSA keys */ + int (*dsa_keygen)(DSA *dsa); + }; + +struct dsa_st + { + /* This first variable is used to pick up errors where + * a DSA is passed instead of of a EVP_PKEY */ + int pad; + long version; + int write_params; + BIGNUM *p; + BIGNUM *q; /* == 20 */ + BIGNUM *g; + + BIGNUM *pub_key; /* y public key */ + BIGNUM *priv_key; /* x private key */ + + BIGNUM *kinv; /* Signing pre-calc */ + BIGNUM *r; /* Signing pre-calc */ + + int flags; + /* Normally used to cache montgomery values */ + BN_MONT_CTX *method_mont_p; + int references; + CRYPTO_EX_DATA ex_data; + const DSA_METHOD *meth; + /* functional reference if 'meth' is ENGINE-provided */ + ENGINE *engine; + }; + +#define DSAparams_dup(x) ASN1_dup_of_const(DSA,i2d_DSAparams,d2i_DSAparams,x) +#define d2i_DSAparams_fp(fp,x) (DSA *)ASN1_d2i_fp((char *(*)())DSA_new, \ + (char *(*)())d2i_DSAparams,(fp),(unsigned char **)(x)) +#define i2d_DSAparams_fp(fp,x) ASN1_i2d_fp(i2d_DSAparams,(fp), \ + (unsigned char *)(x)) +#define d2i_DSAparams_bio(bp,x) ASN1_d2i_bio_of(DSA,DSA_new,d2i_DSAparams,bp,x) +#define i2d_DSAparams_bio(bp,x) ASN1_i2d_bio_of_const(DSA,i2d_DSAparams,bp,x) + + +DSA_SIG * DSA_SIG_new(void); +void DSA_SIG_free(DSA_SIG *a); +int i2d_DSA_SIG(const DSA_SIG *a, unsigned char **pp); +DSA_SIG * d2i_DSA_SIG(DSA_SIG **v, const unsigned char **pp, long length); + +DSA_SIG * DSA_do_sign(const unsigned char *dgst,int dlen,DSA *dsa); +int DSA_do_verify(const unsigned char *dgst,int dgst_len, + DSA_SIG *sig,DSA *dsa); + +const DSA_METHOD *DSA_OpenSSL(void); + +void DSA_set_default_method(const DSA_METHOD *); +const DSA_METHOD *DSA_get_default_method(void); +int DSA_set_method(DSA *dsa, const DSA_METHOD *); + +DSA * DSA_new(void); +DSA * DSA_new_method(ENGINE *engine); +void DSA_free (DSA *r); +/* "up" the DSA object's reference count */ +int DSA_up_ref(DSA *r); +int DSA_size(const DSA *); + /* next 4 return -1 on error */ +int DSA_sign_setup( DSA *dsa,BN_CTX *ctx_in,BIGNUM **kinvp,BIGNUM **rp); +int DSA_sign(int type,const unsigned char *dgst,int dlen, + unsigned char *sig, unsigned int *siglen, DSA *dsa); +int DSA_verify(int type,const unsigned char *dgst,int dgst_len, + const unsigned char *sigbuf, int siglen, DSA *dsa); +int DSA_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, + CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); +int DSA_set_ex_data(DSA *d, int idx, void *arg); +void *DSA_get_ex_data(DSA *d, int idx); + +DSA * d2i_DSAPublicKey(DSA **a, const unsigned char **pp, long length); +DSA * d2i_DSAPrivateKey(DSA **a, const unsigned char **pp, long length); +DSA * d2i_DSAparams(DSA **a, const unsigned char **pp, long length); + +/* Deprecated version */ +#ifndef OPENSSL_NO_DEPRECATED +DSA * DSA_generate_parameters(int bits, + unsigned char *seed,int seed_len, + int *counter_ret, unsigned long *h_ret,void + (*callback)(int, int, void *),void *cb_arg); +#endif /* !defined(OPENSSL_NO_DEPRECATED) */ + +/* New version */ +int DSA_generate_parameters_ex(DSA *dsa, int bits, + unsigned char *seed,int seed_len, + int *counter_ret, unsigned long *h_ret, BN_GENCB *cb); + +int DSA_generate_key(DSA *a); +int i2d_DSAPublicKey(const DSA *a, unsigned char **pp); +int i2d_DSAPrivateKey(const DSA *a, unsigned char **pp); +int i2d_DSAparams(const DSA *a,unsigned char **pp); + +#ifndef OPENSSL_NO_BIO +int DSAparams_print(BIO *bp, const DSA *x); +int DSA_print(BIO *bp, const DSA *x, int off); +#endif +#ifndef OPENSSL_NO_FP_API +int DSAparams_print_fp(FILE *fp, const DSA *x); +int DSA_print_fp(FILE *bp, const DSA *x, int off); +#endif + +#define DSS_prime_checks 50 +/* Primality test according to FIPS PUB 186[-1], Appendix 2.1: + * 50 rounds of Rabin-Miller */ +#define DSA_is_prime(n, callback, cb_arg) \ + BN_is_prime(n, DSS_prime_checks, callback, NULL, cb_arg) + +#ifndef OPENSSL_NO_DH +/* Convert DSA structure (key or just parameters) into DH structure + * (be careful to avoid small subgroup attacks when using this!) */ +DH *DSA_dup_DH(const DSA *r); +#endif + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_DSA_strings(void); + +/* Error codes for the DSA functions. */ + +/* Function codes. */ +#define DSA_F_D2I_DSA_SIG 110 +#define DSA_F_DSAPARAMS_PRINT 100 +#define DSA_F_DSAPARAMS_PRINT_FP 101 +#define DSA_F_DSA_DO_SIGN 112 +#define DSA_F_DSA_DO_VERIFY 113 +#define DSA_F_DSA_NEW_METHOD 103 +#define DSA_F_DSA_PRINT 104 +#define DSA_F_DSA_PRINT_FP 105 +#define DSA_F_DSA_SIGN 106 +#define DSA_F_DSA_SIGN_SETUP 107 +#define DSA_F_DSA_SIG_NEW 109 +#define DSA_F_DSA_VERIFY 108 +#define DSA_F_I2D_DSA_SIG 111 +#define DSA_F_SIG_CB 114 + +/* Reason codes. */ +#define DSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE 100 +#define DSA_R_MISSING_PARAMETERS 101 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/dso.h b/production/3rdparty/openssl/include/openssl/dso.h new file mode 100644 index 00000000..3e51913a --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/dso.h @@ -0,0 +1,368 @@ +/* dso.h -*- mode:C; c-file-style: "eay" -*- */ +/* Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL + * project 2000. + */ +/* ==================================================================== + * Copyright (c) 2000 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#ifndef HEADER_DSO_H +#define HEADER_DSO_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* These values are used as commands to DSO_ctrl() */ +#define DSO_CTRL_GET_FLAGS 1 +#define DSO_CTRL_SET_FLAGS 2 +#define DSO_CTRL_OR_FLAGS 3 + +/* By default, DSO_load() will translate the provided filename into a form + * typical for the platform (more specifically the DSO_METHOD) using the + * dso_name_converter function of the method. Eg. win32 will transform "blah" + * into "blah.dll", and dlfcn will transform it into "libblah.so". The + * behaviour can be overriden by setting the name_converter callback in the DSO + * object (using DSO_set_name_converter()). This callback could even utilise + * the DSO_METHOD's converter too if it only wants to override behaviour for + * one or two possible DSO methods. However, the following flag can be set in a + * DSO to prevent *any* native name-translation at all - eg. if the caller has + * prompted the user for a path to a driver library so the filename should be + * interpreted as-is. */ +#define DSO_FLAG_NO_NAME_TRANSLATION 0x01 +/* An extra flag to give if only the extension should be added as + * translation. This is obviously only of importance on Unix and + * other operating systems where the translation also may prefix + * the name with something, like 'lib', and ignored everywhere else. + * This flag is also ignored if DSO_FLAG_NO_NAME_TRANSLATION is used + * at the same time. */ +#define DSO_FLAG_NAME_TRANSLATION_EXT_ONLY 0x02 + +/* The following flag controls the translation of symbol names to upper + * case. This is currently only being implemented for OpenVMS. + */ +#define DSO_FLAG_UPCASE_SYMBOL 0x10 + +/* This flag loads the library with public symbols. + * Meaning: The exported symbols of this library are public + * to all libraries loaded after this library. + * At the moment only implemented in unix. + */ +#define DSO_FLAG_GLOBAL_SYMBOLS 0x20 + + +typedef void (*DSO_FUNC_TYPE)(void); + +typedef struct dso_st DSO; + +/* The function prototype used for method functions (or caller-provided + * callbacks) that transform filenames. They are passed a DSO structure pointer + * (or NULL if they are to be used independantly of a DSO object) and a + * filename to transform. They should either return NULL (if there is an error + * condition) or a newly allocated string containing the transformed form that + * the caller will need to free with OPENSSL_free() when done. */ +typedef char* (*DSO_NAME_CONVERTER_FUNC)(DSO *, const char *); +/* The function prototype used for method functions (or caller-provided + * callbacks) that merge two file specifications. They are passed a + * DSO structure pointer (or NULL if they are to be used independantly of + * a DSO object) and two file specifications to merge. They should + * either return NULL (if there is an error condition) or a newly allocated + * string containing the result of merging that the caller will need + * to free with OPENSSL_free() when done. + * Here, merging means that bits and pieces are taken from each of the + * file specifications and added together in whatever fashion that is + * sensible for the DSO method in question. The only rule that really + * applies is that if the two specification contain pieces of the same + * type, the copy from the first string takes priority. One could see + * it as the first specification is the one given by the user and the + * second being a bunch of defaults to add on if they're missing in the + * first. */ +typedef char* (*DSO_MERGER_FUNC)(DSO *, const char *, const char *); + +typedef struct dso_meth_st + { + const char *name; + /* Loads a shared library, NB: new DSO_METHODs must ensure that a + * successful load populates the loaded_filename field, and likewise a + * successful unload OPENSSL_frees and NULLs it out. */ + int (*dso_load)(DSO *dso); + /* Unloads a shared library */ + int (*dso_unload)(DSO *dso); + /* Binds a variable */ + void *(*dso_bind_var)(DSO *dso, const char *symname); + /* Binds a function - assumes a return type of DSO_FUNC_TYPE. + * This should be cast to the real function prototype by the + * caller. Platforms that don't have compatible representations + * for different prototypes (this is possible within ANSI C) + * are highly unlikely to have shared libraries at all, let + * alone a DSO_METHOD implemented for them. */ + DSO_FUNC_TYPE (*dso_bind_func)(DSO *dso, const char *symname); + +/* I don't think this would actually be used in any circumstances. */ +#if 0 + /* Unbinds a variable */ + int (*dso_unbind_var)(DSO *dso, char *symname, void *symptr); + /* Unbinds a function */ + int (*dso_unbind_func)(DSO *dso, char *symname, DSO_FUNC_TYPE symptr); +#endif + /* The generic (yuck) "ctrl()" function. NB: Negative return + * values (rather than zero) indicate errors. */ + long (*dso_ctrl)(DSO *dso, int cmd, long larg, void *parg); + /* The default DSO_METHOD-specific function for converting filenames to + * a canonical native form. */ + DSO_NAME_CONVERTER_FUNC dso_name_converter; + /* The default DSO_METHOD-specific function for converting filenames to + * a canonical native form. */ + DSO_MERGER_FUNC dso_merger; + + /* [De]Initialisation handlers. */ + int (*init)(DSO *dso); + int (*finish)(DSO *dso); + } DSO_METHOD; + +/**********************************************************************/ +/* The low-level handle type used to refer to a loaded shared library */ + +struct dso_st + { + DSO_METHOD *meth; + /* Standard dlopen uses a (void *). Win32 uses a HANDLE. VMS + * doesn't use anything but will need to cache the filename + * for use in the dso_bind handler. All in all, let each + * method control its own destiny. "Handles" and such go in + * a STACK. */ + STACK *meth_data; + int references; + int flags; + /* For use by applications etc ... use this for your bits'n'pieces, + * don't touch meth_data! */ + CRYPTO_EX_DATA ex_data; + /* If this callback function pointer is set to non-NULL, then it will + * be used in DSO_load() in place of meth->dso_name_converter. NB: This + * should normally set using DSO_set_name_converter(). */ + DSO_NAME_CONVERTER_FUNC name_converter; + /* If this callback function pointer is set to non-NULL, then it will + * be used in DSO_load() in place of meth->dso_merger. NB: This + * should normally set using DSO_set_merger(). */ + DSO_MERGER_FUNC merger; + /* This is populated with (a copy of) the platform-independant + * filename used for this DSO. */ + char *filename; + /* This is populated with (a copy of) the translated filename by which + * the DSO was actually loaded. It is NULL iff the DSO is not currently + * loaded. NB: This is here because the filename translation process + * may involve a callback being invoked more than once not only to + * convert to a platform-specific form, but also to try different + * filenames in the process of trying to perform a load. As such, this + * variable can be used to indicate (a) whether this DSO structure + * corresponds to a loaded library or not, and (b) the filename with + * which it was actually loaded. */ + char *loaded_filename; + }; + + +DSO * DSO_new(void); +DSO * DSO_new_method(DSO_METHOD *method); +int DSO_free(DSO *dso); +int DSO_flags(DSO *dso); +int DSO_up_ref(DSO *dso); +long DSO_ctrl(DSO *dso, int cmd, long larg, void *parg); + +/* This function sets the DSO's name_converter callback. If it is non-NULL, + * then it will be used instead of the associated DSO_METHOD's function. If + * oldcb is non-NULL then it is set to the function pointer value being + * replaced. Return value is non-zero for success. */ +int DSO_set_name_converter(DSO *dso, DSO_NAME_CONVERTER_FUNC cb, + DSO_NAME_CONVERTER_FUNC *oldcb); +/* These functions can be used to get/set the platform-independant filename + * used for a DSO. NB: set will fail if the DSO is already loaded. */ +const char *DSO_get_filename(DSO *dso); +int DSO_set_filename(DSO *dso, const char *filename); +/* This function will invoke the DSO's name_converter callback to translate a + * filename, or if the callback isn't set it will instead use the DSO_METHOD's + * converter. If "filename" is NULL, the "filename" in the DSO itself will be + * used. If the DSO_FLAG_NO_NAME_TRANSLATION flag is set, then the filename is + * simply duplicated. NB: This function is usually called from within a + * DSO_METHOD during the processing of a DSO_load() call, and is exposed so that + * caller-created DSO_METHODs can do the same thing. A non-NULL return value + * will need to be OPENSSL_free()'d. */ +char *DSO_convert_filename(DSO *dso, const char *filename); +/* This function will invoke the DSO's merger callback to merge two file + * specifications, or if the callback isn't set it will instead use the + * DSO_METHOD's merger. A non-NULL return value will need to be + * OPENSSL_free()'d. */ +char *DSO_merge(DSO *dso, const char *filespec1, const char *filespec2); +/* If the DSO is currently loaded, this returns the filename that it was loaded + * under, otherwise it returns NULL. So it is also useful as a test as to + * whether the DSO is currently loaded. NB: This will not necessarily return + * the same value as DSO_convert_filename(dso, dso->filename), because the + * DSO_METHOD's load function may have tried a variety of filenames (with + * and/or without the aid of the converters) before settling on the one it + * actually loaded. */ +const char *DSO_get_loaded_filename(DSO *dso); + +void DSO_set_default_method(DSO_METHOD *meth); +DSO_METHOD *DSO_get_default_method(void); +DSO_METHOD *DSO_get_method(DSO *dso); +DSO_METHOD *DSO_set_method(DSO *dso, DSO_METHOD *meth); + +/* The all-singing all-dancing load function, you normally pass NULL + * for the first and third parameters. Use DSO_up and DSO_free for + * subsequent reference count handling. Any flags passed in will be set + * in the constructed DSO after its init() function but before the + * load operation. If 'dso' is non-NULL, 'flags' is ignored. */ +DSO *DSO_load(DSO *dso, const char *filename, DSO_METHOD *meth, int flags); + +/* This function binds to a variable inside a shared library. */ +void *DSO_bind_var(DSO *dso, const char *symname); + +/* This function binds to a function inside a shared library. */ +DSO_FUNC_TYPE DSO_bind_func(DSO *dso, const char *symname); + +/* This method is the default, but will beg, borrow, or steal whatever + * method should be the default on any particular platform (including + * DSO_METH_null() if necessary). */ +DSO_METHOD *DSO_METHOD_openssl(void); + +/* This method is defined for all platforms - if a platform has no + * DSO support then this will be the only method! */ +DSO_METHOD *DSO_METHOD_null(void); + +/* If DSO_DLFCN is defined, the standard dlfcn.h-style functions + * (dlopen, dlclose, dlsym, etc) will be used and incorporated into + * this method. If not, this method will return NULL. */ +DSO_METHOD *DSO_METHOD_dlfcn(void); + +/* If DSO_DL is defined, the standard dl.h-style functions (shl_load, + * shl_unload, shl_findsym, etc) will be used and incorporated into + * this method. If not, this method will return NULL. */ +DSO_METHOD *DSO_METHOD_dl(void); + +/* If WIN32 is defined, use DLLs. If not, return NULL. */ +DSO_METHOD *DSO_METHOD_win32(void); + +/* If VMS is defined, use shared images. If not, return NULL. */ +DSO_METHOD *DSO_METHOD_vms(void); + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_DSO_strings(void); + +/* Error codes for the DSO functions. */ + +/* Function codes. */ +#define DSO_F_DLFCN_BIND_FUNC 100 +#define DSO_F_DLFCN_BIND_VAR 101 +#define DSO_F_DLFCN_LOAD 102 +#define DSO_F_DLFCN_MERGER 130 +#define DSO_F_DLFCN_NAME_CONVERTER 123 +#define DSO_F_DLFCN_UNLOAD 103 +#define DSO_F_DL_BIND_FUNC 104 +#define DSO_F_DL_BIND_VAR 105 +#define DSO_F_DL_LOAD 106 +#define DSO_F_DL_MERGER 131 +#define DSO_F_DL_NAME_CONVERTER 124 +#define DSO_F_DL_UNLOAD 107 +#define DSO_F_DSO_BIND_FUNC 108 +#define DSO_F_DSO_BIND_VAR 109 +#define DSO_F_DSO_CONVERT_FILENAME 126 +#define DSO_F_DSO_CTRL 110 +#define DSO_F_DSO_FREE 111 +#define DSO_F_DSO_GET_FILENAME 127 +#define DSO_F_DSO_GET_LOADED_FILENAME 128 +#define DSO_F_DSO_LOAD 112 +#define DSO_F_DSO_MERGE 132 +#define DSO_F_DSO_NEW_METHOD 113 +#define DSO_F_DSO_SET_FILENAME 129 +#define DSO_F_DSO_SET_NAME_CONVERTER 122 +#define DSO_F_DSO_UP_REF 114 +#define DSO_F_VMS_BIND_SYM 115 +#define DSO_F_VMS_LOAD 116 +#define DSO_F_VMS_MERGER 133 +#define DSO_F_VMS_UNLOAD 117 +#define DSO_F_WIN32_BIND_FUNC 118 +#define DSO_F_WIN32_BIND_VAR 119 +#define DSO_F_WIN32_JOINER 135 +#define DSO_F_WIN32_LOAD 120 +#define DSO_F_WIN32_MERGER 134 +#define DSO_F_WIN32_NAME_CONVERTER 125 +#define DSO_F_WIN32_SPLITTER 136 +#define DSO_F_WIN32_UNLOAD 121 + +/* Reason codes. */ +#define DSO_R_CTRL_FAILED 100 +#define DSO_R_DSO_ALREADY_LOADED 110 +#define DSO_R_EMPTY_FILE_STRUCTURE 113 +#define DSO_R_FAILURE 114 +#define DSO_R_FILENAME_TOO_BIG 101 +#define DSO_R_FINISH_FAILED 102 +#define DSO_R_INCORRECT_FILE_SYNTAX 115 +#define DSO_R_LOAD_FAILED 103 +#define DSO_R_NAME_TRANSLATION_FAILED 109 +#define DSO_R_NO_FILENAME 111 +#define DSO_R_NO_FILE_SPECIFICATION 116 +#define DSO_R_NULL_HANDLE 104 +#define DSO_R_SET_FILENAME_FAILED 112 +#define DSO_R_STACK_ERROR 105 +#define DSO_R_SYM_FAILURE 106 +#define DSO_R_UNLOAD_FAILED 107 +#define DSO_R_UNSUPPORTED 108 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/dtls1.h b/production/3rdparty/openssl/include/openssl/dtls1.h new file mode 100644 index 00000000..b377cc5f --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/dtls1.h @@ -0,0 +1,212 @@ +/* ssl/dtls1.h */ +/* + * DTLS implementation written by Nagendra Modadugu + * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. + */ +/* ==================================================================== + * Copyright (c) 1999-2005 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#ifndef HEADER_DTLS1_H +#define HEADER_DTLS1_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DTLS1_VERSION 0x0100 +#define DTLS1_VERSION_MAJOR 0x01 +#define DTLS1_VERSION_MINOR 0x00 + +#define DTLS1_AD_MISSING_HANDSHAKE_MESSAGE 110 + +/* lengths of messages */ +#define DTLS1_COOKIE_LENGTH 32 + +#define DTLS1_RT_HEADER_LENGTH 13 + +#define DTLS1_HM_HEADER_LENGTH 12 + +#define DTLS1_HM_BAD_FRAGMENT -2 +#define DTLS1_HM_FRAGMENT_RETRY -3 + +#define DTLS1_CCS_HEADER_LENGTH 3 + +#define DTLS1_AL_HEADER_LENGTH 7 + + +typedef struct dtls1_bitmap_st + { + PQ_64BIT map; + unsigned long length; /* sizeof the bitmap in bits */ + PQ_64BIT max_seq_num; /* max record number seen so far */ + } DTLS1_BITMAP; + +struct hm_header_st + { + unsigned char type; + unsigned long msg_len; + unsigned short seq; + unsigned long frag_off; + unsigned long frag_len; + unsigned int is_ccs; + }; + +struct ccs_header_st + { + unsigned char type; + unsigned short seq; + }; + +struct dtls1_timeout_st + { + /* Number of read timeouts so far */ + unsigned int read_timeouts; + + /* Number of write timeouts so far */ + unsigned int write_timeouts; + + /* Number of alerts received so far */ + unsigned int num_alerts; + }; + +typedef struct record_pqueue_st + { + unsigned short epoch; + pqueue q; + } record_pqueue; + +typedef struct hm_fragment_st + { + struct hm_header_st msg_header; + unsigned char *fragment; + } hm_fragment; + +typedef struct dtls1_state_st + { + unsigned int send_cookie; + unsigned char cookie[DTLS1_COOKIE_LENGTH]; + unsigned char rcvd_cookie[DTLS1_COOKIE_LENGTH]; + unsigned int cookie_len; + + /* + * The current data and handshake epoch. This is initially + * undefined, and starts at zero once the initial handshake is + * completed + */ + unsigned short r_epoch; + unsigned short w_epoch; + + /* records being received in the current epoch */ + DTLS1_BITMAP bitmap; + + /* renegotiation starts a new set of sequence numbers */ + DTLS1_BITMAP next_bitmap; + + /* handshake message numbers */ + unsigned short handshake_write_seq; + unsigned short next_handshake_write_seq; + + unsigned short handshake_read_seq; + + /* Received handshake records (processed and unprocessed) */ + record_pqueue unprocessed_rcds; + record_pqueue processed_rcds; + + /* Buffered handshake messages */ + pqueue buffered_messages; + + /* Buffered (sent) handshake records */ + pqueue sent_messages; + + unsigned int mtu; /* max wire packet size */ + + struct hm_header_st w_msg_hdr; + struct hm_header_st r_msg_hdr; + + struct dtls1_timeout_st timeout; + + /* storage for Alert/Handshake protocol data received but not + * yet processed by ssl3_read_bytes: */ + unsigned char alert_fragment[DTLS1_AL_HEADER_LENGTH]; + unsigned int alert_fragment_len; + unsigned char handshake_fragment[DTLS1_HM_HEADER_LENGTH]; + unsigned int handshake_fragment_len; + + unsigned int retransmitting; + + } DTLS1_STATE; + +typedef struct dtls1_record_data_st + { + unsigned char *packet; + unsigned int packet_length; + SSL3_BUFFER rbuf; + SSL3_RECORD rrec; + } DTLS1_RECORD_DATA; + + +/* Timeout multipliers (timeout slice is defined in apps/timeouts.h */ +#define DTLS1_TMO_READ_COUNT 2 +#define DTLS1_TMO_WRITE_COUNT 2 + +#define DTLS1_TMO_ALERT_COUNT 12 + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/production/3rdparty/openssl/include/openssl/e_os2.h b/production/3rdparty/openssl/include/openssl/e_os2.h new file mode 100644 index 00000000..9da0b654 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/e_os2.h @@ -0,0 +1,279 @@ +/* e_os2.h */ +/* ==================================================================== + * Copyright (c) 1998-2000 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#include + +#ifndef HEADER_E_OS2_H +#define HEADER_E_OS2_H + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************************************************************** + * Detect operating systems. This probably needs completing. + * The result is that at least one OPENSSL_SYS_os macro should be defined. + * However, if none is defined, Unix is assumed. + **/ + +#define OPENSSL_SYS_UNIX + +/* ----------------------- Macintosh, before MacOS X ----------------------- */ +#if defined(__MWERKS__) && defined(macintosh) || defined(OPENSSL_SYSNAME_MAC) +# undef OPENSSL_SYS_UNIX +# define OPENSSL_SYS_MACINTOSH_CLASSIC +#endif + +/* ----------------------- NetWare ----------------------------------------- */ +#if defined(NETWARE) || defined(OPENSSL_SYSNAME_NETWARE) +# undef OPENSSL_SYS_UNIX +# define OPENSSL_SYS_NETWARE +#endif + +/* ---------------------- Microsoft operating systems ---------------------- */ + +/* Note that MSDOS actually denotes 32-bit environments running on top of + MS-DOS, such as DJGPP one. */ +#if defined(OPENSSL_SYSNAME_MSDOS) +# undef OPENSSL_SYS_UNIX +# define OPENSSL_SYS_MSDOS +#endif + +/* For 32 bit environment, there seems to be the CygWin environment and then + all the others that try to do the same thing Microsoft does... */ +#if defined(OPENSSL_SYSNAME_UWIN) +# undef OPENSSL_SYS_UNIX +# define OPENSSL_SYS_WIN32_UWIN +#else +# if defined(__CYGWIN32__) || defined(OPENSSL_SYSNAME_CYGWIN32) +# undef OPENSSL_SYS_UNIX +# define OPENSSL_SYS_WIN32_CYGWIN +# else +# if defined(_WIN32) || defined(OPENSSL_SYSNAME_WIN32) +# undef OPENSSL_SYS_UNIX +# define OPENSSL_SYS_WIN32 +# endif +# if defined(OPENSSL_SYSNAME_WINNT) +# undef OPENSSL_SYS_UNIX +# define OPENSSL_SYS_WINNT +# endif +# if defined(OPENSSL_SYSNAME_WINCE) +# undef OPENSSL_SYS_UNIX +# define OPENSSL_SYS_WINCE +# endif +# endif +#endif + +/* Anything that tries to look like Microsoft is "Windows" */ +#if defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_WINNT) || defined(OPENSSL_SYS_WINCE) +# undef OPENSSL_SYS_UNIX +# define OPENSSL_SYS_WINDOWS +# ifndef OPENSSL_SYS_MSDOS +# define OPENSSL_SYS_MSDOS +# endif +#endif + +/* DLL settings. This part is a bit tough, because it's up to the application + implementor how he or she will link the application, so it requires some + macro to be used. */ +#ifdef OPENSSL_SYS_WINDOWS +# ifndef OPENSSL_OPT_WINDLL +# if defined(_WINDLL) /* This is used when building OpenSSL to indicate that + DLL linkage should be used */ +# define OPENSSL_OPT_WINDLL +# endif +# endif +#endif + +/* -------------------------------- OpenVMS -------------------------------- */ +#if defined(__VMS) || defined(VMS) || defined(OPENSSL_SYSNAME_VMS) +# undef OPENSSL_SYS_UNIX +# define OPENSSL_SYS_VMS +# if defined(__DECC) +# define OPENSSL_SYS_VMS_DECC +# elif defined(__DECCXX) +# define OPENSSL_SYS_VMS_DECC +# define OPENSSL_SYS_VMS_DECCXX +# else +# define OPENSSL_SYS_VMS_NODECC +# endif +#endif + +/* --------------------------------- OS/2 ---------------------------------- */ +#if defined(__EMX__) || defined(__OS2__) +# undef OPENSSL_SYS_UNIX +# define OPENSSL_SYS_OS2 +#endif + +/* --------------------------------- Unix ---------------------------------- */ +#ifdef OPENSSL_SYS_UNIX +# if defined(linux) || defined(__linux__) || defined(OPENSSL_SYSNAME_LINUX) +# define OPENSSL_SYS_LINUX +# endif +# ifdef OPENSSL_SYSNAME_MPE +# define OPENSSL_SYS_MPE +# endif +# ifdef OPENSSL_SYSNAME_SNI +# define OPENSSL_SYS_SNI +# endif +# ifdef OPENSSL_SYSNAME_ULTRASPARC +# define OPENSSL_SYS_ULTRASPARC +# endif +# ifdef OPENSSL_SYSNAME_NEWS4 +# define OPENSSL_SYS_NEWS4 +# endif +# ifdef OPENSSL_SYSNAME_MACOSX +# define OPENSSL_SYS_MACOSX +# endif +# ifdef OPENSSL_SYSNAME_MACOSX_RHAPSODY +# define OPENSSL_SYS_MACOSX_RHAPSODY +# define OPENSSL_SYS_MACOSX +# endif +# ifdef OPENSSL_SYSNAME_SUNOS +# define OPENSSL_SYS_SUNOS +#endif +# if defined(_CRAY) || defined(OPENSSL_SYSNAME_CRAY) +# define OPENSSL_SYS_CRAY +# endif +# if defined(_AIX) || defined(OPENSSL_SYSNAME_AIX) +# define OPENSSL_SYS_AIX +# endif +#endif + +/* --------------------------------- VOS ----------------------------------- */ +#ifdef OPENSSL_SYSNAME_VOS +# define OPENSSL_SYS_VOS +#endif + +/* ------------------------------- VxWorks --------------------------------- */ +#ifdef OPENSSL_SYSNAME_VXWORKS +# define OPENSSL_SYS_VXWORKS +#endif + +/** + * That's it for OS-specific stuff + *****************************************************************************/ + + +/* Specials for I/O an exit */ +#ifdef OPENSSL_SYS_MSDOS +# define OPENSSL_UNISTD_IO +# define OPENSSL_DECLARE_EXIT extern void exit(int); +#else +# define OPENSSL_UNISTD_IO OPENSSL_UNISTD +# define OPENSSL_DECLARE_EXIT /* declared in unistd.h */ +#endif + +/* Definitions of OPENSSL_GLOBAL and OPENSSL_EXTERN, to define and declare + certain global symbols that, with some compilers under VMS, have to be + defined and declared explicitely with globaldef and globalref. + Definitions of OPENSSL_EXPORT and OPENSSL_IMPORT, to define and declare + DLL exports and imports for compilers under Win32. These are a little + more complicated to use. Basically, for any library that exports some + global variables, the following code must be present in the header file + that declares them, before OPENSSL_EXTERN is used: + + #ifdef SOME_BUILD_FLAG_MACRO + # undef OPENSSL_EXTERN + # define OPENSSL_EXTERN OPENSSL_EXPORT + #endif + + The default is to have OPENSSL_EXPORT, OPENSSL_IMPORT and OPENSSL_GLOBAL + have some generally sensible values, and for OPENSSL_EXTERN to have the + value OPENSSL_IMPORT. +*/ + +#if defined(OPENSSL_SYS_VMS_NODECC) +# define OPENSSL_EXPORT globalref +# define OPENSSL_IMPORT globalref +# define OPENSSL_GLOBAL globaldef +#elif defined(OPENSSL_SYS_WINDOWS) && defined(OPENSSL_OPT_WINDLL) +# define OPENSSL_EXPORT extern __declspec(dllexport) +# define OPENSSL_IMPORT extern __declspec(dllimport) +# define OPENSSL_GLOBAL +#else +# define OPENSSL_EXPORT extern +# define OPENSSL_IMPORT extern +# define OPENSSL_GLOBAL +#endif +#define OPENSSL_EXTERN OPENSSL_IMPORT + +/* Macros to allow global variables to be reached through function calls when + required (if a shared library version requvres it, for example. + The way it's done allows definitions like this: + + // in foobar.c + OPENSSL_IMPLEMENT_GLOBAL(int,foobar) = 0; + // in foobar.h + OPENSSL_DECLARE_GLOBAL(int,foobar); + #define foobar OPENSSL_GLOBAL_REF(foobar) +*/ +#ifdef OPENSSL_EXPORT_VAR_AS_FUNCTION +# define OPENSSL_IMPLEMENT_GLOBAL(type,name) \ + extern type _hide_##name; \ + type *_shadow_##name(void) { return &_hide_##name; } \ + static type _hide_##name +# define OPENSSL_DECLARE_GLOBAL(type,name) type *_shadow_##name(void) +# define OPENSSL_GLOBAL_REF(name) (*(_shadow_##name())) +#else +# define OPENSSL_IMPLEMENT_GLOBAL(type,name) OPENSSL_GLOBAL type _shadow_##name +# define OPENSSL_DECLARE_GLOBAL(type,name) OPENSSL_EXPORT type _shadow_##name +# define OPENSSL_GLOBAL_REF(name) _shadow_##name +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/ebcdic.h b/production/3rdparty/openssl/include/openssl/ebcdic.h new file mode 100644 index 00000000..6d65afcf --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/ebcdic.h @@ -0,0 +1,19 @@ +/* crypto/ebcdic.h */ + +#ifndef HEADER_EBCDIC_H +#define HEADER_EBCDIC_H + +#include + +/* Avoid name clashes with other applications */ +#define os_toascii _openssl_os_toascii +#define os_toebcdic _openssl_os_toebcdic +#define ebcdic2ascii _openssl_ebcdic2ascii +#define ascii2ebcdic _openssl_ascii2ebcdic + +extern const unsigned char os_toascii[256]; +extern const unsigned char os_toebcdic[256]; +void *ebcdic2ascii(void *dest, const void *srce, size_t count); +void *ascii2ebcdic(void *dest, const void *srce, size_t count); + +#endif diff --git a/production/3rdparty/openssl/include/openssl/ec.h b/production/3rdparty/openssl/include/openssl/ec.h new file mode 100644 index 00000000..919c7363 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/ec.h @@ -0,0 +1,518 @@ +/* crypto/ec/ec.h */ +/* + * Originally written by Bodo Moeller for the OpenSSL project. + */ +/* ==================================================================== + * Copyright (c) 1998-2003 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * + * Portions of the attached software ("Contribution") are developed by + * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project. + * + * The Contribution is licensed pursuant to the OpenSSL open source + * license provided above. + * + * The elliptic curve binary polynomial software is originally written by + * Sheueling Chang Shantz and Douglas Stebila of Sun Microsystems Laboratories. + * + */ + +#ifndef HEADER_EC_H +#define HEADER_EC_H + +#include + +#ifdef OPENSSL_NO_EC +#error EC is disabled. +#endif + +#include +#include +#ifndef OPENSSL_NO_DEPRECATED +#include +#endif + +#ifdef __cplusplus +extern "C" { +#elif defined(__SUNPRO_C) +# if __SUNPRO_C >= 0x520 +# pragma error_messages (off,E_ARRAY_OF_INCOMPLETE_NONAME,E_ARRAY_OF_INCOMPLETE) +# endif +#endif + + +typedef enum { + /* values as defined in X9.62 (ECDSA) and elsewhere */ + POINT_CONVERSION_COMPRESSED = 2, + POINT_CONVERSION_UNCOMPRESSED = 4, + POINT_CONVERSION_HYBRID = 6 +} point_conversion_form_t; + + +typedef struct ec_method_st EC_METHOD; + +typedef struct ec_group_st + /* + EC_METHOD *meth; + -- field definition + -- curve coefficients + -- optional generator with associated information (order, cofactor) + -- optional extra data (precomputed table for fast computation of multiples of generator) + -- ASN1 stuff + */ + EC_GROUP; + +typedef struct ec_point_st EC_POINT; + + +/* EC_METHODs for curves over GF(p). + * EC_GFp_simple_method provides the basis for the optimized methods. + */ +const EC_METHOD *EC_GFp_simple_method(void); +const EC_METHOD *EC_GFp_mont_method(void); +const EC_METHOD *EC_GFp_nist_method(void); + +/* EC_METHOD for curves over GF(2^m). + */ +const EC_METHOD *EC_GF2m_simple_method(void); + + +EC_GROUP *EC_GROUP_new(const EC_METHOD *); +void EC_GROUP_free(EC_GROUP *); +void EC_GROUP_clear_free(EC_GROUP *); +int EC_GROUP_copy(EC_GROUP *, const EC_GROUP *); +EC_GROUP *EC_GROUP_dup(const EC_GROUP *); + +const EC_METHOD *EC_GROUP_method_of(const EC_GROUP *); +int EC_METHOD_get_field_type(const EC_METHOD *); + +int EC_GROUP_set_generator(EC_GROUP *, const EC_POINT *generator, const BIGNUM *order, const BIGNUM *cofactor); +const EC_POINT *EC_GROUP_get0_generator(const EC_GROUP *); +int EC_GROUP_get_order(const EC_GROUP *, BIGNUM *order, BN_CTX *); +int EC_GROUP_get_cofactor(const EC_GROUP *, BIGNUM *cofactor, BN_CTX *); + +void EC_GROUP_set_curve_name(EC_GROUP *, int nid); +int EC_GROUP_get_curve_name(const EC_GROUP *); + +void EC_GROUP_set_asn1_flag(EC_GROUP *, int flag); +int EC_GROUP_get_asn1_flag(const EC_GROUP *); + +void EC_GROUP_set_point_conversion_form(EC_GROUP *, point_conversion_form_t); +point_conversion_form_t EC_GROUP_get_point_conversion_form(const EC_GROUP *); + +unsigned char *EC_GROUP_get0_seed(const EC_GROUP *); +size_t EC_GROUP_get_seed_len(const EC_GROUP *); +size_t EC_GROUP_set_seed(EC_GROUP *, const unsigned char *, size_t len); + +int EC_GROUP_set_curve_GFp(EC_GROUP *, const BIGNUM *p, const BIGNUM *a, const BIGNUM *b, BN_CTX *); +int EC_GROUP_get_curve_GFp(const EC_GROUP *, BIGNUM *p, BIGNUM *a, BIGNUM *b, BN_CTX *); +int EC_GROUP_set_curve_GF2m(EC_GROUP *, const BIGNUM *p, const BIGNUM *a, const BIGNUM *b, BN_CTX *); +int EC_GROUP_get_curve_GF2m(const EC_GROUP *, BIGNUM *p, BIGNUM *a, BIGNUM *b, BN_CTX *); + +/* returns the number of bits needed to represent a field element */ +int EC_GROUP_get_degree(const EC_GROUP *); + +/* EC_GROUP_check() returns 1 if 'group' defines a valid group, 0 otherwise */ +int EC_GROUP_check(const EC_GROUP *group, BN_CTX *ctx); +/* EC_GROUP_check_discriminant() returns 1 if the discriminant of the + * elliptic curve is not zero, 0 otherwise */ +int EC_GROUP_check_discriminant(const EC_GROUP *, BN_CTX *); + +/* EC_GROUP_cmp() returns 0 if both groups are equal and 1 otherwise */ +int EC_GROUP_cmp(const EC_GROUP *, const EC_GROUP *, BN_CTX *); + +/* EC_GROUP_new_GF*() calls EC_GROUP_new() and EC_GROUP_set_GF*() + * after choosing an appropriate EC_METHOD */ +EC_GROUP *EC_GROUP_new_curve_GFp(const BIGNUM *p, const BIGNUM *a, const BIGNUM *b, BN_CTX *); +EC_GROUP *EC_GROUP_new_curve_GF2m(const BIGNUM *p, const BIGNUM *a, const BIGNUM *b, BN_CTX *); + +/* EC_GROUP_new_by_curve_name() creates a EC_GROUP structure + * specified by a curve name (in form of a NID) */ +EC_GROUP *EC_GROUP_new_by_curve_name(int nid); +/* handling of internal curves */ +typedef struct { + int nid; + const char *comment; + } EC_builtin_curve; +/* EC_builtin_curves(EC_builtin_curve *r, size_t size) returns number + * of all available curves or zero if a error occurred. + * In case r ist not zero nitems EC_builtin_curve structures + * are filled with the data of the first nitems internal groups */ +size_t EC_get_builtin_curves(EC_builtin_curve *r, size_t nitems); + + +/* EC_POINT functions */ + +EC_POINT *EC_POINT_new(const EC_GROUP *); +void EC_POINT_free(EC_POINT *); +void EC_POINT_clear_free(EC_POINT *); +int EC_POINT_copy(EC_POINT *, const EC_POINT *); +EC_POINT *EC_POINT_dup(const EC_POINT *, const EC_GROUP *); + +const EC_METHOD *EC_POINT_method_of(const EC_POINT *); + +int EC_POINT_set_to_infinity(const EC_GROUP *, EC_POINT *); +int EC_POINT_set_Jprojective_coordinates_GFp(const EC_GROUP *, EC_POINT *, + const BIGNUM *x, const BIGNUM *y, const BIGNUM *z, BN_CTX *); +int EC_POINT_get_Jprojective_coordinates_GFp(const EC_GROUP *, const EC_POINT *, + BIGNUM *x, BIGNUM *y, BIGNUM *z, BN_CTX *); +int EC_POINT_set_affine_coordinates_GFp(const EC_GROUP *, EC_POINT *, + const BIGNUM *x, const BIGNUM *y, BN_CTX *); +int EC_POINT_get_affine_coordinates_GFp(const EC_GROUP *, const EC_POINT *, + BIGNUM *x, BIGNUM *y, BN_CTX *); +int EC_POINT_set_compressed_coordinates_GFp(const EC_GROUP *, EC_POINT *, + const BIGNUM *x, int y_bit, BN_CTX *); + +int EC_POINT_set_affine_coordinates_GF2m(const EC_GROUP *, EC_POINT *, + const BIGNUM *x, const BIGNUM *y, BN_CTX *); +int EC_POINT_get_affine_coordinates_GF2m(const EC_GROUP *, const EC_POINT *, + BIGNUM *x, BIGNUM *y, BN_CTX *); +int EC_POINT_set_compressed_coordinates_GF2m(const EC_GROUP *, EC_POINT *, + const BIGNUM *x, int y_bit, BN_CTX *); + +size_t EC_POINT_point2oct(const EC_GROUP *, const EC_POINT *, point_conversion_form_t form, + unsigned char *buf, size_t len, BN_CTX *); +int EC_POINT_oct2point(const EC_GROUP *, EC_POINT *, + const unsigned char *buf, size_t len, BN_CTX *); + +/* other interfaces to point2oct/oct2point: */ +BIGNUM *EC_POINT_point2bn(const EC_GROUP *, const EC_POINT *, + point_conversion_form_t form, BIGNUM *, BN_CTX *); +EC_POINT *EC_POINT_bn2point(const EC_GROUP *, const BIGNUM *, + EC_POINT *, BN_CTX *); +char *EC_POINT_point2hex(const EC_GROUP *, const EC_POINT *, + point_conversion_form_t form, BN_CTX *); +EC_POINT *EC_POINT_hex2point(const EC_GROUP *, const char *, + EC_POINT *, BN_CTX *); + +int EC_POINT_add(const EC_GROUP *, EC_POINT *r, const EC_POINT *a, const EC_POINT *b, BN_CTX *); +int EC_POINT_dbl(const EC_GROUP *, EC_POINT *r, const EC_POINT *a, BN_CTX *); +int EC_POINT_invert(const EC_GROUP *, EC_POINT *, BN_CTX *); + +int EC_POINT_is_at_infinity(const EC_GROUP *, const EC_POINT *); +int EC_POINT_is_on_curve(const EC_GROUP *, const EC_POINT *, BN_CTX *); +int EC_POINT_cmp(const EC_GROUP *, const EC_POINT *a, const EC_POINT *b, BN_CTX *); + +int EC_POINT_make_affine(const EC_GROUP *, EC_POINT *, BN_CTX *); +int EC_POINTs_make_affine(const EC_GROUP *, size_t num, EC_POINT *[], BN_CTX *); + + +int EC_POINTs_mul(const EC_GROUP *, EC_POINT *r, const BIGNUM *, size_t num, const EC_POINT *[], const BIGNUM *[], BN_CTX *); +int EC_POINT_mul(const EC_GROUP *, EC_POINT *r, const BIGNUM *, const EC_POINT *, const BIGNUM *, BN_CTX *); + +/* EC_GROUP_precompute_mult() stores multiples of generator for faster point multiplication */ +int EC_GROUP_precompute_mult(EC_GROUP *, BN_CTX *); +/* EC_GROUP_have_precompute_mult() reports whether such precomputation has been done */ +int EC_GROUP_have_precompute_mult(const EC_GROUP *); + + + +/* ASN1 stuff */ + +/* EC_GROUP_get_basis_type() returns the NID of the basis type + * used to represent the field elements */ +int EC_GROUP_get_basis_type(const EC_GROUP *); +int EC_GROUP_get_trinomial_basis(const EC_GROUP *, unsigned int *k); +int EC_GROUP_get_pentanomial_basis(const EC_GROUP *, unsigned int *k1, + unsigned int *k2, unsigned int *k3); + +#define OPENSSL_EC_NAMED_CURVE 0x001 + +typedef struct ecpk_parameters_st ECPKPARAMETERS; + +EC_GROUP *d2i_ECPKParameters(EC_GROUP **, const unsigned char **in, long len); +int i2d_ECPKParameters(const EC_GROUP *, unsigned char **out); + +#define d2i_ECPKParameters_bio(bp,x) ASN1_d2i_bio_of(EC_GROUP,NULL,d2i_ECPKParameters,bp,x) +#define i2d_ECPKParameters_bio(bp,x) ASN1_i2d_bio_of_const(EC_GROUP,i2d_ECPKParameters,bp,x) +#define d2i_ECPKParameters_fp(fp,x) (EC_GROUP *)ASN1_d2i_fp(NULL, \ + (char *(*)())d2i_ECPKParameters,(fp),(unsigned char **)(x)) +#define i2d_ECPKParameters_fp(fp,x) ASN1_i2d_fp(i2d_ECPKParameters,(fp), \ + (unsigned char *)(x)) + +#ifndef OPENSSL_NO_BIO +int ECPKParameters_print(BIO *bp, const EC_GROUP *x, int off); +#endif +#ifndef OPENSSL_NO_FP_API +int ECPKParameters_print_fp(FILE *fp, const EC_GROUP *x, int off); +#endif + +/* the EC_KEY stuff */ +typedef struct ec_key_st EC_KEY; + +/* some values for the encoding_flag */ +#define EC_PKEY_NO_PARAMETERS 0x001 +#define EC_PKEY_NO_PUBKEY 0x002 + +EC_KEY *EC_KEY_new(void); +EC_KEY *EC_KEY_new_by_curve_name(int nid); +void EC_KEY_free(EC_KEY *); +EC_KEY *EC_KEY_copy(EC_KEY *, const EC_KEY *); +EC_KEY *EC_KEY_dup(const EC_KEY *); + +int EC_KEY_up_ref(EC_KEY *); + +const EC_GROUP *EC_KEY_get0_group(const EC_KEY *); +int EC_KEY_set_group(EC_KEY *, const EC_GROUP *); +const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *); +int EC_KEY_set_private_key(EC_KEY *, const BIGNUM *); +const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *); +int EC_KEY_set_public_key(EC_KEY *, const EC_POINT *); +unsigned EC_KEY_get_enc_flags(const EC_KEY *); +void EC_KEY_set_enc_flags(EC_KEY *, unsigned int); +point_conversion_form_t EC_KEY_get_conv_form(const EC_KEY *); +void EC_KEY_set_conv_form(EC_KEY *, point_conversion_form_t); +/* functions to set/get method specific data */ +void *EC_KEY_get_key_method_data(EC_KEY *, + void *(*dup_func)(void *), void (*free_func)(void *), void (*clear_free_func)(void *)); +void EC_KEY_insert_key_method_data(EC_KEY *, void *data, + void *(*dup_func)(void *), void (*free_func)(void *), void (*clear_free_func)(void *)); +/* wrapper functions for the underlying EC_GROUP object */ +void EC_KEY_set_asn1_flag(EC_KEY *, int); +int EC_KEY_precompute_mult(EC_KEY *, BN_CTX *ctx); + +/* EC_KEY_generate_key() creates a ec private (public) key */ +int EC_KEY_generate_key(EC_KEY *); +/* EC_KEY_check_key() */ +int EC_KEY_check_key(const EC_KEY *); + +/* de- and encoding functions for SEC1 ECPrivateKey */ +EC_KEY *d2i_ECPrivateKey(EC_KEY **a, const unsigned char **in, long len); +int i2d_ECPrivateKey(EC_KEY *a, unsigned char **out); +/* de- and encoding functions for EC parameters */ +EC_KEY *d2i_ECParameters(EC_KEY **a, const unsigned char **in, long len); +int i2d_ECParameters(EC_KEY *a, unsigned char **out); +/* de- and encoding functions for EC public key + * (octet string, not DER -- hence 'o2i' and 'i2o') */ +EC_KEY *o2i_ECPublicKey(EC_KEY **a, const unsigned char **in, long len); +int i2o_ECPublicKey(EC_KEY *a, unsigned char **out); + +#ifndef OPENSSL_NO_BIO +int ECParameters_print(BIO *bp, const EC_KEY *x); +int EC_KEY_print(BIO *bp, const EC_KEY *x, int off); +#endif +#ifndef OPENSSL_NO_FP_API +int ECParameters_print_fp(FILE *fp, const EC_KEY *x); +int EC_KEY_print_fp(FILE *fp, const EC_KEY *x, int off); +#endif + +#define ECParameters_dup(x) ASN1_dup_of(EC_KEY,i2d_ECParameters,d2i_ECParameters,x) + +#ifndef __cplusplus +#if defined(__SUNPRO_C) +# if __SUNPRO_C >= 0x520 +# pragma error_messages (default,E_ARRAY_OF_INCOMPLETE_NONAME,E_ARRAY_OF_INCOMPLETE) +# endif +# endif +#endif + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_EC_strings(void); + +/* Error codes for the EC functions. */ + +/* Function codes. */ +#define EC_F_COMPUTE_WNAF 143 +#define EC_F_D2I_ECPARAMETERS 144 +#define EC_F_D2I_ECPKPARAMETERS 145 +#define EC_F_D2I_ECPRIVATEKEY 146 +#define EC_F_ECPARAMETERS_PRINT 147 +#define EC_F_ECPARAMETERS_PRINT_FP 148 +#define EC_F_ECPKPARAMETERS_PRINT 149 +#define EC_F_ECPKPARAMETERS_PRINT_FP 150 +#define EC_F_ECP_NIST_MOD_192 203 +#define EC_F_ECP_NIST_MOD_224 204 +#define EC_F_ECP_NIST_MOD_256 205 +#define EC_F_ECP_NIST_MOD_521 206 +#define EC_F_EC_ASN1_GROUP2CURVE 153 +#define EC_F_EC_ASN1_GROUP2FIELDID 154 +#define EC_F_EC_ASN1_GROUP2PARAMETERS 155 +#define EC_F_EC_ASN1_GROUP2PKPARAMETERS 156 +#define EC_F_EC_ASN1_PARAMETERS2GROUP 157 +#define EC_F_EC_ASN1_PKPARAMETERS2GROUP 158 +#define EC_F_EC_EX_DATA_SET_DATA 211 +#define EC_F_EC_GF2M_MONTGOMERY_POINT_MULTIPLY 208 +#define EC_F_EC_GF2M_SIMPLE_GROUP_CHECK_DISCRIMINANT 159 +#define EC_F_EC_GF2M_SIMPLE_GROUP_SET_CURVE 195 +#define EC_F_EC_GF2M_SIMPLE_OCT2POINT 160 +#define EC_F_EC_GF2M_SIMPLE_POINT2OCT 161 +#define EC_F_EC_GF2M_SIMPLE_POINT_GET_AFFINE_COORDINATES 162 +#define EC_F_EC_GF2M_SIMPLE_POINT_SET_AFFINE_COORDINATES 163 +#define EC_F_EC_GF2M_SIMPLE_SET_COMPRESSED_COORDINATES 164 +#define EC_F_EC_GFP_MONT_FIELD_DECODE 133 +#define EC_F_EC_GFP_MONT_FIELD_ENCODE 134 +#define EC_F_EC_GFP_MONT_FIELD_MUL 131 +#define EC_F_EC_GFP_MONT_FIELD_SET_TO_ONE 209 +#define EC_F_EC_GFP_MONT_FIELD_SQR 132 +#define EC_F_EC_GFP_MONT_GROUP_SET_CURVE 189 +#define EC_F_EC_GFP_MONT_GROUP_SET_CURVE_GFP 135 +#define EC_F_EC_GFP_NIST_FIELD_MUL 200 +#define EC_F_EC_GFP_NIST_FIELD_SQR 201 +#define EC_F_EC_GFP_NIST_GROUP_SET_CURVE 202 +#define EC_F_EC_GFP_SIMPLE_GROUP_CHECK_DISCRIMINANT 165 +#define EC_F_EC_GFP_SIMPLE_GROUP_SET_CURVE 166 +#define EC_F_EC_GFP_SIMPLE_GROUP_SET_CURVE_GFP 100 +#define EC_F_EC_GFP_SIMPLE_GROUP_SET_GENERATOR 101 +#define EC_F_EC_GFP_SIMPLE_MAKE_AFFINE 102 +#define EC_F_EC_GFP_SIMPLE_OCT2POINT 103 +#define EC_F_EC_GFP_SIMPLE_POINT2OCT 104 +#define EC_F_EC_GFP_SIMPLE_POINTS_MAKE_AFFINE 137 +#define EC_F_EC_GFP_SIMPLE_POINT_GET_AFFINE_COORDINATES 167 +#define EC_F_EC_GFP_SIMPLE_POINT_GET_AFFINE_COORDINATES_GFP 105 +#define EC_F_EC_GFP_SIMPLE_POINT_SET_AFFINE_COORDINATES 168 +#define EC_F_EC_GFP_SIMPLE_POINT_SET_AFFINE_COORDINATES_GFP 128 +#define EC_F_EC_GFP_SIMPLE_SET_COMPRESSED_COORDINATES 169 +#define EC_F_EC_GFP_SIMPLE_SET_COMPRESSED_COORDINATES_GFP 129 +#define EC_F_EC_GROUP_CHECK 170 +#define EC_F_EC_GROUP_CHECK_DISCRIMINANT 171 +#define EC_F_EC_GROUP_COPY 106 +#define EC_F_EC_GROUP_GET0_GENERATOR 139 +#define EC_F_EC_GROUP_GET_COFACTOR 140 +#define EC_F_EC_GROUP_GET_CURVE_GF2M 172 +#define EC_F_EC_GROUP_GET_CURVE_GFP 130 +#define EC_F_EC_GROUP_GET_DEGREE 173 +#define EC_F_EC_GROUP_GET_ORDER 141 +#define EC_F_EC_GROUP_GET_PENTANOMIAL_BASIS 193 +#define EC_F_EC_GROUP_GET_TRINOMIAL_BASIS 194 +#define EC_F_EC_GROUP_NEW 108 +#define EC_F_EC_GROUP_NEW_BY_CURVE_NAME 174 +#define EC_F_EC_GROUP_NEW_FROM_DATA 175 +#define EC_F_EC_GROUP_PRECOMPUTE_MULT 142 +#define EC_F_EC_GROUP_SET_CURVE_GF2M 176 +#define EC_F_EC_GROUP_SET_CURVE_GFP 109 +#define EC_F_EC_GROUP_SET_EXTRA_DATA 110 +#define EC_F_EC_GROUP_SET_GENERATOR 111 +#define EC_F_EC_KEY_CHECK_KEY 177 +#define EC_F_EC_KEY_COPY 178 +#define EC_F_EC_KEY_GENERATE_KEY 179 +#define EC_F_EC_KEY_NEW 182 +#define EC_F_EC_KEY_PRINT 180 +#define EC_F_EC_KEY_PRINT_FP 181 +#define EC_F_EC_POINTS_MAKE_AFFINE 136 +#define EC_F_EC_POINTS_MUL 138 +#define EC_F_EC_POINT_ADD 112 +#define EC_F_EC_POINT_CMP 113 +#define EC_F_EC_POINT_COPY 114 +#define EC_F_EC_POINT_DBL 115 +#define EC_F_EC_POINT_GET_AFFINE_COORDINATES_GF2M 183 +#define EC_F_EC_POINT_GET_AFFINE_COORDINATES_GFP 116 +#define EC_F_EC_POINT_GET_JPROJECTIVE_COORDINATES_GFP 117 +#define EC_F_EC_POINT_INVERT 210 +#define EC_F_EC_POINT_IS_AT_INFINITY 118 +#define EC_F_EC_POINT_IS_ON_CURVE 119 +#define EC_F_EC_POINT_MAKE_AFFINE 120 +#define EC_F_EC_POINT_MUL 184 +#define EC_F_EC_POINT_NEW 121 +#define EC_F_EC_POINT_OCT2POINT 122 +#define EC_F_EC_POINT_POINT2OCT 123 +#define EC_F_EC_POINT_SET_AFFINE_COORDINATES_GF2M 185 +#define EC_F_EC_POINT_SET_AFFINE_COORDINATES_GFP 124 +#define EC_F_EC_POINT_SET_COMPRESSED_COORDINATES_GF2M 186 +#define EC_F_EC_POINT_SET_COMPRESSED_COORDINATES_GFP 125 +#define EC_F_EC_POINT_SET_JPROJECTIVE_COORDINATES_GFP 126 +#define EC_F_EC_POINT_SET_TO_INFINITY 127 +#define EC_F_EC_PRE_COMP_DUP 207 +#define EC_F_EC_WNAF_MUL 187 +#define EC_F_EC_WNAF_PRECOMPUTE_MULT 188 +#define EC_F_I2D_ECPARAMETERS 190 +#define EC_F_I2D_ECPKPARAMETERS 191 +#define EC_F_I2D_ECPRIVATEKEY 192 +#define EC_F_I2O_ECPUBLICKEY 151 +#define EC_F_O2I_ECPUBLICKEY 152 + +/* Reason codes. */ +#define EC_R_ASN1_ERROR 115 +#define EC_R_ASN1_UNKNOWN_FIELD 116 +#define EC_R_BUFFER_TOO_SMALL 100 +#define EC_R_D2I_ECPKPARAMETERS_FAILURE 117 +#define EC_R_DISCRIMINANT_IS_ZERO 118 +#define EC_R_EC_GROUP_NEW_BY_NAME_FAILURE 119 +#define EC_R_GROUP2PKPARAMETERS_FAILURE 120 +#define EC_R_I2D_ECPKPARAMETERS_FAILURE 121 +#define EC_R_INCOMPATIBLE_OBJECTS 101 +#define EC_R_INVALID_ARGUMENT 112 +#define EC_R_INVALID_COMPRESSED_POINT 110 +#define EC_R_INVALID_COMPRESSION_BIT 109 +#define EC_R_INVALID_ENCODING 102 +#define EC_R_INVALID_FIELD 103 +#define EC_R_INVALID_FORM 104 +#define EC_R_INVALID_GROUP_ORDER 122 +#define EC_R_INVALID_PRIVATE_KEY 123 +#define EC_R_MISSING_PARAMETERS 124 +#define EC_R_MISSING_PRIVATE_KEY 125 +#define EC_R_NOT_A_NIST_PRIME 135 +#define EC_R_NOT_A_SUPPORTED_NIST_PRIME 136 +#define EC_R_NOT_IMPLEMENTED 126 +#define EC_R_NOT_INITIALIZED 111 +#define EC_R_NO_FIELD_MOD 133 +#define EC_R_PASSED_NULL_PARAMETER 134 +#define EC_R_PKPARAMETERS2GROUP_FAILURE 127 +#define EC_R_POINT_AT_INFINITY 106 +#define EC_R_POINT_IS_NOT_ON_CURVE 107 +#define EC_R_SLOT_FULL 108 +#define EC_R_UNDEFINED_GENERATOR 113 +#define EC_R_UNDEFINED_ORDER 128 +#define EC_R_UNKNOWN_GROUP 129 +#define EC_R_UNKNOWN_ORDER 114 +#define EC_R_UNSUPPORTED_FIELD 131 +#define EC_R_WRONG_ORDER 130 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/ecdh.h b/production/3rdparty/openssl/include/openssl/ecdh.h new file mode 100644 index 00000000..b4b58ee6 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/ecdh.h @@ -0,0 +1,123 @@ +/* crypto/ecdh/ecdh.h */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * + * The Elliptic Curve Public-Key Crypto Library (ECC Code) included + * herein is developed by SUN MICROSYSTEMS, INC., and is contributed + * to the OpenSSL project. + * + * The ECC Code is licensed pursuant to the OpenSSL open source + * license provided below. + * + * The ECDH software is originally written by Douglas Stebila of + * Sun Microsystems Laboratories. + * + */ +/* ==================================================================== + * Copyright (c) 2000-2002 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +#ifndef HEADER_ECDH_H +#define HEADER_ECDH_H + +#include + +#ifdef OPENSSL_NO_ECDH +#error ECDH is disabled. +#endif + +#include +#include +#ifndef OPENSSL_NO_DEPRECATED +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +const ECDH_METHOD *ECDH_OpenSSL(void); + +void ECDH_set_default_method(const ECDH_METHOD *); +const ECDH_METHOD *ECDH_get_default_method(void); +int ECDH_set_method(EC_KEY *, const ECDH_METHOD *); + +int ECDH_compute_key(void *out, size_t outlen, const EC_POINT *pub_key, EC_KEY *ecdh, + void *(*KDF)(const void *in, size_t inlen, void *out, size_t *outlen)); + +int ECDH_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new + *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); +int ECDH_set_ex_data(EC_KEY *d, int idx, void *arg); +void *ECDH_get_ex_data(EC_KEY *d, int idx); + + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_ECDH_strings(void); + +/* Error codes for the ECDH functions. */ + +/* Function codes. */ +#define ECDH_F_ECDH_COMPUTE_KEY 100 +#define ECDH_F_ECDH_DATA_NEW_METHOD 101 + +/* Reason codes. */ +#define ECDH_R_KDF_FAILED 102 +#define ECDH_R_NO_PRIVATE_VALUE 100 +#define ECDH_R_POINT_ARITHMETIC_FAILURE 101 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/ecdsa.h b/production/3rdparty/openssl/include/openssl/ecdsa.h new file mode 100644 index 00000000..76c5a4aa --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/ecdsa.h @@ -0,0 +1,270 @@ +/* crypto/ecdsa/ecdsa.h */ +/** + * \file crypto/ecdsa/ecdsa.h Include file for the OpenSSL ECDSA functions + * \author Written by Nils Larsch for the OpenSSL project + */ +/* ==================================================================== + * Copyright (c) 2000-2003 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +#ifndef HEADER_ECDSA_H +#define HEADER_ECDSA_H + +#include + +#ifdef OPENSSL_NO_ECDSA +#error ECDSA is disabled. +#endif + +#include +#include +#ifndef OPENSSL_NO_DEPRECATED +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ECDSA_SIG_st + { + BIGNUM *r; + BIGNUM *s; + } ECDSA_SIG; + +/** ECDSA_SIG *ECDSA_SIG_new(void) + * allocates and initialize a ECDSA_SIG structure + * \return pointer to a ECDSA_SIG structure or NULL if an error occurred + */ +ECDSA_SIG *ECDSA_SIG_new(void); + +/** ECDSA_SIG_free + * frees a ECDSA_SIG structure + * \param a pointer to the ECDSA_SIG structure + */ +void ECDSA_SIG_free(ECDSA_SIG *a); + +/** i2d_ECDSA_SIG + * DER encode content of ECDSA_SIG object (note: this function modifies *pp + * (*pp += length of the DER encoded signature)). + * \param a pointer to the ECDSA_SIG object + * \param pp pointer to a unsigned char pointer for the output or NULL + * \return the length of the DER encoded ECDSA_SIG object or 0 + */ +int i2d_ECDSA_SIG(const ECDSA_SIG *a, unsigned char **pp); + +/** d2i_ECDSA_SIG + * decodes a DER encoded ECDSA signature (note: this function changes *pp + * (*pp += len)). + * \param v pointer to ECDSA_SIG pointer (may be NULL) + * \param pp buffer with the DER encoded signature + * \param len bufferlength + * \return pointer to the decoded ECDSA_SIG structure (or NULL) + */ +ECDSA_SIG *d2i_ECDSA_SIG(ECDSA_SIG **v, const unsigned char **pp, long len); + +/** ECDSA_do_sign + * computes the ECDSA signature of the given hash value using + * the supplied private key and returns the created signature. + * \param dgst pointer to the hash value + * \param dgst_len length of the hash value + * \param eckey pointer to the EC_KEY object containing a private EC key + * \return pointer to a ECDSA_SIG structure or NULL + */ +ECDSA_SIG *ECDSA_do_sign(const unsigned char *dgst,int dgst_len,EC_KEY *eckey); + +/** ECDSA_do_sign_ex + * computes ECDSA signature of a given hash value using the supplied + * private key (note: sig must point to ECDSA_size(eckey) bytes of memory). + * \param dgst pointer to the hash value to sign + * \param dgstlen length of the hash value + * \param kinv optional pointer to a pre-computed inverse k + * \param rp optional pointer to the pre-computed rp value (see + * ECDSA_sign_setup + * \param eckey pointer to the EC_KEY object containing a private EC key + * \return pointer to a ECDSA_SIG structure or NULL + */ +ECDSA_SIG *ECDSA_do_sign_ex(const unsigned char *dgst, int dgstlen, + const BIGNUM *kinv, const BIGNUM *rp, EC_KEY *eckey); + +/** ECDSA_do_verify + * verifies that the supplied signature is a valid ECDSA + * signature of the supplied hash value using the supplied public key. + * \param dgst pointer to the hash value + * \param dgst_len length of the hash value + * \param sig pointer to the ECDSA_SIG structure + * \param eckey pointer to the EC_KEY object containing a public EC key + * \return 1 if the signature is valid, 0 if the signature is invalid and -1 on error + */ +int ECDSA_do_verify(const unsigned char *dgst, int dgst_len, + const ECDSA_SIG *sig, EC_KEY* eckey); + +const ECDSA_METHOD *ECDSA_OpenSSL(void); + +/** ECDSA_set_default_method + * sets the default ECDSA method + * \param meth the new default ECDSA_METHOD + */ +void ECDSA_set_default_method(const ECDSA_METHOD *meth); + +/** ECDSA_get_default_method + * returns the default ECDSA method + * \return pointer to ECDSA_METHOD structure containing the default method + */ +const ECDSA_METHOD *ECDSA_get_default_method(void); + +/** ECDSA_set_method + * sets method to be used for the ECDSA operations + * \param eckey pointer to the EC_KEY object + * \param meth pointer to the new method + * \return 1 on success and 0 otherwise + */ +int ECDSA_set_method(EC_KEY *eckey, const ECDSA_METHOD *meth); + +/** ECDSA_size + * returns the maximum length of the DER encoded signature + * \param eckey pointer to a EC_KEY object + * \return numbers of bytes required for the DER encoded signature + */ +int ECDSA_size(const EC_KEY *eckey); + +/** ECDSA_sign_setup + * precompute parts of the signing operation. + * \param eckey pointer to the EC_KEY object containing a private EC key + * \param ctx pointer to a BN_CTX object (may be NULL) + * \param kinv pointer to a BIGNUM pointer for the inverse of k + * \param rp pointer to a BIGNUM pointer for x coordinate of k * generator + * \return 1 on success and 0 otherwise + */ +int ECDSA_sign_setup(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, + BIGNUM **rp); + +/** ECDSA_sign + * computes ECDSA signature of a given hash value using the supplied + * private key (note: sig must point to ECDSA_size(eckey) bytes of memory). + * \param type this parameter is ignored + * \param dgst pointer to the hash value to sign + * \param dgstlen length of the hash value + * \param sig buffer to hold the DER encoded signature + * \param siglen pointer to the length of the returned signature + * \param eckey pointer to the EC_KEY object containing a private EC key + * \return 1 on success and 0 otherwise + */ +int ECDSA_sign(int type, const unsigned char *dgst, int dgstlen, + unsigned char *sig, unsigned int *siglen, EC_KEY *eckey); + + +/** ECDSA_sign_ex + * computes ECDSA signature of a given hash value using the supplied + * private key (note: sig must point to ECDSA_size(eckey) bytes of memory). + * \param type this parameter is ignored + * \param dgst pointer to the hash value to sign + * \param dgstlen length of the hash value + * \param sig buffer to hold the DER encoded signature + * \param siglen pointer to the length of the returned signature + * \param kinv optional pointer to a pre-computed inverse k + * \param rp optional pointer to the pre-computed rp value (see + * ECDSA_sign_setup + * \param eckey pointer to the EC_KEY object containing a private EC key + * \return 1 on success and 0 otherwise + */ +int ECDSA_sign_ex(int type, const unsigned char *dgst, int dgstlen, + unsigned char *sig, unsigned int *siglen, const BIGNUM *kinv, + const BIGNUM *rp, EC_KEY *eckey); + +/** ECDSA_verify + * verifies that the given signature is valid ECDSA signature + * of the supplied hash value using the specified public key. + * \param type this parameter is ignored + * \param dgst pointer to the hash value + * \param dgstlen length of the hash value + * \param sig pointer to the DER encoded signature + * \param siglen length of the DER encoded signature + * \param eckey pointer to the EC_KEY object containing a public EC key + * \return 1 if the signature is valid, 0 if the signature is invalid and -1 on error + */ +int ECDSA_verify(int type, const unsigned char *dgst, int dgstlen, + const unsigned char *sig, int siglen, EC_KEY *eckey); + +/* the standard ex_data functions */ +int ECDSA_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new + *new_func, CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); +int ECDSA_set_ex_data(EC_KEY *d, int idx, void *arg); +void *ECDSA_get_ex_data(EC_KEY *d, int idx); + + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_ECDSA_strings(void); + +/* Error codes for the ECDSA functions. */ + +/* Function codes. */ +#define ECDSA_F_ECDSA_DATA_NEW_METHOD 100 +#define ECDSA_F_ECDSA_DO_SIGN 101 +#define ECDSA_F_ECDSA_DO_VERIFY 102 +#define ECDSA_F_ECDSA_SIGN_SETUP 103 + +/* Reason codes. */ +#define ECDSA_R_BAD_SIGNATURE 100 +#define ECDSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE 101 +#define ECDSA_R_ERR_EC_LIB 102 +#define ECDSA_R_MISSING_PARAMETERS 103 +#define ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED 104 +#define ECDSA_R_SIGNATURE_MALLOC_FAILED 105 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/engine.h b/production/3rdparty/openssl/include/openssl/engine.h new file mode 100644 index 00000000..3ec59338 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/engine.h @@ -0,0 +1,785 @@ +/* openssl/engine.h */ +/* Written by Geoff Thorpe (geoff@geoffthorpe.net) for the OpenSSL + * project 2000. + */ +/* ==================================================================== + * Copyright (c) 1999-2004 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * ECDH support in OpenSSL originally developed by + * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. + */ + +#ifndef HEADER_ENGINE_H +#define HEADER_ENGINE_H + +#include + +#ifdef OPENSSL_NO_ENGINE +#error ENGINE is disabled. +#endif + +#ifndef OPENSSL_NO_DEPRECATED +#include +#ifndef OPENSSL_NO_RSA +#include +#endif +#ifndef OPENSSL_NO_DSA +#include +#endif +#ifndef OPENSSL_NO_DH +#include +#endif +#ifndef OPENSSL_NO_ECDH +#include +#endif +#ifndef OPENSSL_NO_ECDSA +#include +#endif +#include +#include +#include +#include +#endif + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* These flags are used to control combinations of algorithm (methods) + * by bitwise "OR"ing. */ +#define ENGINE_METHOD_RSA (unsigned int)0x0001 +#define ENGINE_METHOD_DSA (unsigned int)0x0002 +#define ENGINE_METHOD_DH (unsigned int)0x0004 +#define ENGINE_METHOD_RAND (unsigned int)0x0008 +#define ENGINE_METHOD_ECDH (unsigned int)0x0010 +#define ENGINE_METHOD_ECDSA (unsigned int)0x0020 +#define ENGINE_METHOD_CIPHERS (unsigned int)0x0040 +#define ENGINE_METHOD_DIGESTS (unsigned int)0x0080 +#define ENGINE_METHOD_STORE (unsigned int)0x0100 +/* Obvious all-or-nothing cases. */ +#define ENGINE_METHOD_ALL (unsigned int)0xFFFF +#define ENGINE_METHOD_NONE (unsigned int)0x0000 + +/* This(ese) flag(s) controls behaviour of the ENGINE_TABLE mechanism used + * internally to control registration of ENGINE implementations, and can be set + * by ENGINE_set_table_flags(). The "NOINIT" flag prevents attempts to + * initialise registered ENGINEs if they are not already initialised. */ +#define ENGINE_TABLE_FLAG_NOINIT (unsigned int)0x0001 + +/* ENGINE flags that can be set by ENGINE_set_flags(). */ +/* #define ENGINE_FLAGS_MALLOCED 0x0001 */ /* Not used */ + +/* This flag is for ENGINEs that wish to handle the various 'CMD'-related + * control commands on their own. Without this flag, ENGINE_ctrl() handles these + * control commands on behalf of the ENGINE using their "cmd_defns" data. */ +#define ENGINE_FLAGS_MANUAL_CMD_CTRL (int)0x0002 + +/* This flag is for ENGINEs who return new duplicate structures when found via + * "ENGINE_by_id()". When an ENGINE must store state (eg. if ENGINE_ctrl() + * commands are called in sequence as part of some stateful process like + * key-generation setup and execution), it can set this flag - then each attempt + * to obtain the ENGINE will result in it being copied into a new structure. + * Normally, ENGINEs don't declare this flag so ENGINE_by_id() just increments + * the existing ENGINE's structural reference count. */ +#define ENGINE_FLAGS_BY_ID_COPY (int)0x0004 + +/* ENGINEs can support their own command types, and these flags are used in + * ENGINE_CTRL_GET_CMD_FLAGS to indicate to the caller what kind of input each + * command expects. Currently only numeric and string input is supported. If a + * control command supports none of the _NUMERIC, _STRING, or _NO_INPUT options, + * then it is regarded as an "internal" control command - and not for use in + * config setting situations. As such, they're not available to the + * ENGINE_ctrl_cmd_string() function, only raw ENGINE_ctrl() access. Changes to + * this list of 'command types' should be reflected carefully in + * ENGINE_cmd_is_executable() and ENGINE_ctrl_cmd_string(). */ + +/* accepts a 'long' input value (3rd parameter to ENGINE_ctrl) */ +#define ENGINE_CMD_FLAG_NUMERIC (unsigned int)0x0001 +/* accepts string input (cast from 'void*' to 'const char *', 4th parameter to + * ENGINE_ctrl) */ +#define ENGINE_CMD_FLAG_STRING (unsigned int)0x0002 +/* Indicates that the control command takes *no* input. Ie. the control command + * is unparameterised. */ +#define ENGINE_CMD_FLAG_NO_INPUT (unsigned int)0x0004 +/* Indicates that the control command is internal. This control command won't + * be shown in any output, and is only usable through the ENGINE_ctrl_cmd() + * function. */ +#define ENGINE_CMD_FLAG_INTERNAL (unsigned int)0x0008 + +/* NB: These 3 control commands are deprecated and should not be used. ENGINEs + * relying on these commands should compile conditional support for + * compatibility (eg. if these symbols are defined) but should also migrate the + * same functionality to their own ENGINE-specific control functions that can be + * "discovered" by calling applications. The fact these control commands + * wouldn't be "executable" (ie. usable by text-based config) doesn't change the + * fact that application code can find and use them without requiring per-ENGINE + * hacking. */ + +/* These flags are used to tell the ctrl function what should be done. + * All command numbers are shared between all engines, even if some don't + * make sense to some engines. In such a case, they do nothing but return + * the error ENGINE_R_CTRL_COMMAND_NOT_IMPLEMENTED. */ +#define ENGINE_CTRL_SET_LOGSTREAM 1 +#define ENGINE_CTRL_SET_PASSWORD_CALLBACK 2 +#define ENGINE_CTRL_HUP 3 /* Close and reinitialise any + handles/connections etc. */ +#define ENGINE_CTRL_SET_USER_INTERFACE 4 /* Alternative to callback */ +#define ENGINE_CTRL_SET_CALLBACK_DATA 5 /* User-specific data, used + when calling the password + callback and the user + interface */ +#define ENGINE_CTRL_LOAD_CONFIGURATION 6 /* Load a configuration, given + a string that represents a + file name or so */ +#define ENGINE_CTRL_LOAD_SECTION 7 /* Load data from a given + section in the already loaded + configuration */ + +/* These control commands allow an application to deal with an arbitrary engine + * in a dynamic way. Warn: Negative return values indicate errors FOR THESE + * COMMANDS because zero is used to indicate 'end-of-list'. Other commands, + * including ENGINE-specific command types, return zero for an error. + * + * An ENGINE can choose to implement these ctrl functions, and can internally + * manage things however it chooses - it does so by setting the + * ENGINE_FLAGS_MANUAL_CMD_CTRL flag (using ENGINE_set_flags()). Otherwise the + * ENGINE_ctrl() code handles this on the ENGINE's behalf using the cmd_defns + * data (set using ENGINE_set_cmd_defns()). This means an ENGINE's ctrl() + * handler need only implement its own commands - the above "meta" commands will + * be taken care of. */ + +/* Returns non-zero if the supplied ENGINE has a ctrl() handler. If "not", then + * all the remaining control commands will return failure, so it is worth + * checking this first if the caller is trying to "discover" the engine's + * capabilities and doesn't want errors generated unnecessarily. */ +#define ENGINE_CTRL_HAS_CTRL_FUNCTION 10 +/* Returns a positive command number for the first command supported by the + * engine. Returns zero if no ctrl commands are supported. */ +#define ENGINE_CTRL_GET_FIRST_CMD_TYPE 11 +/* The 'long' argument specifies a command implemented by the engine, and the + * return value is the next command supported, or zero if there are no more. */ +#define ENGINE_CTRL_GET_NEXT_CMD_TYPE 12 +/* The 'void*' argument is a command name (cast from 'const char *'), and the + * return value is the command that corresponds to it. */ +#define ENGINE_CTRL_GET_CMD_FROM_NAME 13 +/* The next two allow a command to be converted into its corresponding string + * form. In each case, the 'long' argument supplies the command. In the NAME_LEN + * case, the return value is the length of the command name (not counting a + * trailing EOL). In the NAME case, the 'void*' argument must be a string buffer + * large enough, and it will be populated with the name of the command (WITH a + * trailing EOL). */ +#define ENGINE_CTRL_GET_NAME_LEN_FROM_CMD 14 +#define ENGINE_CTRL_GET_NAME_FROM_CMD 15 +/* The next two are similar but give a "short description" of a command. */ +#define ENGINE_CTRL_GET_DESC_LEN_FROM_CMD 16 +#define ENGINE_CTRL_GET_DESC_FROM_CMD 17 +/* With this command, the return value is the OR'd combination of + * ENGINE_CMD_FLAG_*** values that indicate what kind of input a given + * engine-specific ctrl command expects. */ +#define ENGINE_CTRL_GET_CMD_FLAGS 18 + +/* ENGINE implementations should start the numbering of their own control + * commands from this value. (ie. ENGINE_CMD_BASE, ENGINE_CMD_BASE + 1, etc). */ +#define ENGINE_CMD_BASE 200 + +/* NB: These 2 nCipher "chil" control commands are deprecated, and their + * functionality is now available through ENGINE-specific control commands + * (exposed through the above-mentioned 'CMD'-handling). Code using these 2 + * commands should be migrated to the more general command handling before these + * are removed. */ + +/* Flags specific to the nCipher "chil" engine */ +#define ENGINE_CTRL_CHIL_SET_FORKCHECK 100 + /* Depending on the value of the (long)i argument, this sets or + * unsets the SimpleForkCheck flag in the CHIL API to enable or + * disable checking and workarounds for applications that fork(). + */ +#define ENGINE_CTRL_CHIL_NO_LOCKING 101 + /* This prevents the initialisation function from providing mutex + * callbacks to the nCipher library. */ + +/* If an ENGINE supports its own specific control commands and wishes the + * framework to handle the above 'ENGINE_CMD_***'-manipulation commands on its + * behalf, it should supply a null-terminated array of ENGINE_CMD_DEFN entries + * to ENGINE_set_cmd_defns(). It should also implement a ctrl() handler that + * supports the stated commands (ie. the "cmd_num" entries as described by the + * array). NB: The array must be ordered in increasing order of cmd_num. + * "null-terminated" means that the last ENGINE_CMD_DEFN element has cmd_num set + * to zero and/or cmd_name set to NULL. */ +typedef struct ENGINE_CMD_DEFN_st + { + unsigned int cmd_num; /* The command number */ + const char *cmd_name; /* The command name itself */ + const char *cmd_desc; /* A short description of the command */ + unsigned int cmd_flags; /* The input the command expects */ + } ENGINE_CMD_DEFN; + +/* Generic function pointer */ +typedef int (*ENGINE_GEN_FUNC_PTR)(void); +/* Generic function pointer taking no arguments */ +typedef int (*ENGINE_GEN_INT_FUNC_PTR)(ENGINE *); +/* Specific control function pointer */ +typedef int (*ENGINE_CTRL_FUNC_PTR)(ENGINE *, int, long, void *, void (*f)(void)); +/* Generic load_key function pointer */ +typedef EVP_PKEY * (*ENGINE_LOAD_KEY_PTR)(ENGINE *, const char *, + UI_METHOD *ui_method, void *callback_data); +/* These callback types are for an ENGINE's handler for cipher and digest logic. + * These handlers have these prototypes; + * int foo(ENGINE *e, const EVP_CIPHER **cipher, const int **nids, int nid); + * int foo(ENGINE *e, const EVP_MD **digest, const int **nids, int nid); + * Looking at how to implement these handlers in the case of cipher support, if + * the framework wants the EVP_CIPHER for 'nid', it will call; + * foo(e, &p_evp_cipher, NULL, nid); (return zero for failure) + * If the framework wants a list of supported 'nid's, it will call; + * foo(e, NULL, &p_nids, 0); (returns number of 'nids' or -1 for error) + */ +/* Returns to a pointer to the array of supported cipher 'nid's. If the second + * parameter is non-NULL it is set to the size of the returned array. */ +typedef int (*ENGINE_CIPHERS_PTR)(ENGINE *, const EVP_CIPHER **, const int **, int); +typedef int (*ENGINE_DIGESTS_PTR)(ENGINE *, const EVP_MD **, const int **, int); + +/* STRUCTURE functions ... all of these functions deal with pointers to ENGINE + * structures where the pointers have a "structural reference". This means that + * their reference is to allowed access to the structure but it does not imply + * that the structure is functional. To simply increment or decrement the + * structural reference count, use ENGINE_by_id and ENGINE_free. NB: This is not + * required when iterating using ENGINE_get_next as it will automatically + * decrement the structural reference count of the "current" ENGINE and + * increment the structural reference count of the ENGINE it returns (unless it + * is NULL). */ + +/* Get the first/last "ENGINE" type available. */ +ENGINE *ENGINE_get_first(void); +ENGINE *ENGINE_get_last(void); +/* Iterate to the next/previous "ENGINE" type (NULL = end of the list). */ +ENGINE *ENGINE_get_next(ENGINE *e); +ENGINE *ENGINE_get_prev(ENGINE *e); +/* Add another "ENGINE" type into the array. */ +int ENGINE_add(ENGINE *e); +/* Remove an existing "ENGINE" type from the array. */ +int ENGINE_remove(ENGINE *e); +/* Retrieve an engine from the list by its unique "id" value. */ +ENGINE *ENGINE_by_id(const char *id); +/* Add all the built-in engines. */ +void ENGINE_load_openssl(void); +void ENGINE_load_dynamic(void); +#ifndef OPENSSL_NO_STATIC_ENGINE +void ENGINE_load_4758cca(void); +void ENGINE_load_aep(void); +void ENGINE_load_atalla(void); +void ENGINE_load_chil(void); +void ENGINE_load_cswift(void); +#ifndef OPENSSL_NO_GMP +void ENGINE_load_gmp(void); +#endif +void ENGINE_load_nuron(void); +void ENGINE_load_sureware(void); +void ENGINE_load_ubsec(void); +#endif +void ENGINE_load_cryptodev(void); +void ENGINE_load_padlock(void); +void ENGINE_load_builtin_engines(void); + +/* Get and set global flags (ENGINE_TABLE_FLAG_***) for the implementation + * "registry" handling. */ +unsigned int ENGINE_get_table_flags(void); +void ENGINE_set_table_flags(unsigned int flags); + +/* Manage registration of ENGINEs per "table". For each type, there are 3 + * functions; + * ENGINE_register_***(e) - registers the implementation from 'e' (if it has one) + * ENGINE_unregister_***(e) - unregister the implementation from 'e' + * ENGINE_register_all_***() - call ENGINE_register_***() for each 'e' in the list + * Cleanup is automatically registered from each table when required, so + * ENGINE_cleanup() will reverse any "register" operations. */ + +int ENGINE_register_RSA(ENGINE *e); +void ENGINE_unregister_RSA(ENGINE *e); +void ENGINE_register_all_RSA(void); + +int ENGINE_register_DSA(ENGINE *e); +void ENGINE_unregister_DSA(ENGINE *e); +void ENGINE_register_all_DSA(void); + +int ENGINE_register_ECDH(ENGINE *e); +void ENGINE_unregister_ECDH(ENGINE *e); +void ENGINE_register_all_ECDH(void); + +int ENGINE_register_ECDSA(ENGINE *e); +void ENGINE_unregister_ECDSA(ENGINE *e); +void ENGINE_register_all_ECDSA(void); + +int ENGINE_register_DH(ENGINE *e); +void ENGINE_unregister_DH(ENGINE *e); +void ENGINE_register_all_DH(void); + +int ENGINE_register_RAND(ENGINE *e); +void ENGINE_unregister_RAND(ENGINE *e); +void ENGINE_register_all_RAND(void); + +int ENGINE_register_STORE(ENGINE *e); +void ENGINE_unregister_STORE(ENGINE *e); +void ENGINE_register_all_STORE(void); + +int ENGINE_register_ciphers(ENGINE *e); +void ENGINE_unregister_ciphers(ENGINE *e); +void ENGINE_register_all_ciphers(void); + +int ENGINE_register_digests(ENGINE *e); +void ENGINE_unregister_digests(ENGINE *e); +void ENGINE_register_all_digests(void); + +/* These functions register all support from the above categories. Note, use of + * these functions can result in static linkage of code your application may not + * need. If you only need a subset of functionality, consider using more + * selective initialisation. */ +int ENGINE_register_complete(ENGINE *e); +int ENGINE_register_all_complete(void); + +/* Send parametrised control commands to the engine. The possibilities to send + * down an integer, a pointer to data or a function pointer are provided. Any of + * the parameters may or may not be NULL, depending on the command number. In + * actuality, this function only requires a structural (rather than functional) + * reference to an engine, but many control commands may require the engine be + * functional. The caller should be aware of trying commands that require an + * operational ENGINE, and only use functional references in such situations. */ +int ENGINE_ctrl(ENGINE *e, int cmd, long i, void *p, void (*f)(void)); + +/* This function tests if an ENGINE-specific command is usable as a "setting". + * Eg. in an application's config file that gets processed through + * ENGINE_ctrl_cmd_string(). If this returns zero, it is not available to + * ENGINE_ctrl_cmd_string(), only ENGINE_ctrl(). */ +int ENGINE_cmd_is_executable(ENGINE *e, int cmd); + +/* This function works like ENGINE_ctrl() with the exception of taking a + * command name instead of a command number, and can handle optional commands. + * See the comment on ENGINE_ctrl_cmd_string() for an explanation on how to + * use the cmd_name and cmd_optional. */ +int ENGINE_ctrl_cmd(ENGINE *e, const char *cmd_name, + long i, void *p, void (*f)(void), int cmd_optional); + +/* This function passes a command-name and argument to an ENGINE. The cmd_name + * is converted to a command number and the control command is called using + * 'arg' as an argument (unless the ENGINE doesn't support such a command, in + * which case no control command is called). The command is checked for input + * flags, and if necessary the argument will be converted to a numeric value. If + * cmd_optional is non-zero, then if the ENGINE doesn't support the given + * cmd_name the return value will be success anyway. This function is intended + * for applications to use so that users (or config files) can supply + * engine-specific config data to the ENGINE at run-time to control behaviour of + * specific engines. As such, it shouldn't be used for calling ENGINE_ctrl() + * functions that return data, deal with binary data, or that are otherwise + * supposed to be used directly through ENGINE_ctrl() in application code. Any + * "return" data from an ENGINE_ctrl() operation in this function will be lost - + * the return value is interpreted as failure if the return value is zero, + * success otherwise, and this function returns a boolean value as a result. In + * other words, vendors of 'ENGINE'-enabled devices should write ENGINE + * implementations with parameterisations that work in this scheme, so that + * compliant ENGINE-based applications can work consistently with the same + * configuration for the same ENGINE-enabled devices, across applications. */ +int ENGINE_ctrl_cmd_string(ENGINE *e, const char *cmd_name, const char *arg, + int cmd_optional); + +/* These functions are useful for manufacturing new ENGINE structures. They + * don't address reference counting at all - one uses them to populate an ENGINE + * structure with personalised implementations of things prior to using it + * directly or adding it to the builtin ENGINE list in OpenSSL. These are also + * here so that the ENGINE structure doesn't have to be exposed and break binary + * compatibility! */ +ENGINE *ENGINE_new(void); +int ENGINE_free(ENGINE *e); +int ENGINE_up_ref(ENGINE *e); +int ENGINE_set_id(ENGINE *e, const char *id); +int ENGINE_set_name(ENGINE *e, const char *name); +int ENGINE_set_RSA(ENGINE *e, const RSA_METHOD *rsa_meth); +int ENGINE_set_DSA(ENGINE *e, const DSA_METHOD *dsa_meth); +int ENGINE_set_ECDH(ENGINE *e, const ECDH_METHOD *ecdh_meth); +int ENGINE_set_ECDSA(ENGINE *e, const ECDSA_METHOD *ecdsa_meth); +int ENGINE_set_DH(ENGINE *e, const DH_METHOD *dh_meth); +int ENGINE_set_RAND(ENGINE *e, const RAND_METHOD *rand_meth); +int ENGINE_set_STORE(ENGINE *e, const STORE_METHOD *store_meth); +int ENGINE_set_destroy_function(ENGINE *e, ENGINE_GEN_INT_FUNC_PTR destroy_f); +int ENGINE_set_init_function(ENGINE *e, ENGINE_GEN_INT_FUNC_PTR init_f); +int ENGINE_set_finish_function(ENGINE *e, ENGINE_GEN_INT_FUNC_PTR finish_f); +int ENGINE_set_ctrl_function(ENGINE *e, ENGINE_CTRL_FUNC_PTR ctrl_f); +int ENGINE_set_load_privkey_function(ENGINE *e, ENGINE_LOAD_KEY_PTR loadpriv_f); +int ENGINE_set_load_pubkey_function(ENGINE *e, ENGINE_LOAD_KEY_PTR loadpub_f); +int ENGINE_set_ciphers(ENGINE *e, ENGINE_CIPHERS_PTR f); +int ENGINE_set_digests(ENGINE *e, ENGINE_DIGESTS_PTR f); +int ENGINE_set_flags(ENGINE *e, int flags); +int ENGINE_set_cmd_defns(ENGINE *e, const ENGINE_CMD_DEFN *defns); +/* These functions allow control over any per-structure ENGINE data. */ +int ENGINE_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, + CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); +int ENGINE_set_ex_data(ENGINE *e, int idx, void *arg); +void *ENGINE_get_ex_data(const ENGINE *e, int idx); + +/* This function cleans up anything that needs it. Eg. the ENGINE_add() function + * automatically ensures the list cleanup function is registered to be called + * from ENGINE_cleanup(). Similarly, all ENGINE_register_*** functions ensure + * ENGINE_cleanup() will clean up after them. */ +void ENGINE_cleanup(void); + +/* These return values from within the ENGINE structure. These can be useful + * with functional references as well as structural references - it depends + * which you obtained. Using the result for functional purposes if you only + * obtained a structural reference may be problematic! */ +const char *ENGINE_get_id(const ENGINE *e); +const char *ENGINE_get_name(const ENGINE *e); +const RSA_METHOD *ENGINE_get_RSA(const ENGINE *e); +const DSA_METHOD *ENGINE_get_DSA(const ENGINE *e); +const ECDH_METHOD *ENGINE_get_ECDH(const ENGINE *e); +const ECDSA_METHOD *ENGINE_get_ECDSA(const ENGINE *e); +const DH_METHOD *ENGINE_get_DH(const ENGINE *e); +const RAND_METHOD *ENGINE_get_RAND(const ENGINE *e); +const STORE_METHOD *ENGINE_get_STORE(const ENGINE *e); +ENGINE_GEN_INT_FUNC_PTR ENGINE_get_destroy_function(const ENGINE *e); +ENGINE_GEN_INT_FUNC_PTR ENGINE_get_init_function(const ENGINE *e); +ENGINE_GEN_INT_FUNC_PTR ENGINE_get_finish_function(const ENGINE *e); +ENGINE_CTRL_FUNC_PTR ENGINE_get_ctrl_function(const ENGINE *e); +ENGINE_LOAD_KEY_PTR ENGINE_get_load_privkey_function(const ENGINE *e); +ENGINE_LOAD_KEY_PTR ENGINE_get_load_pubkey_function(const ENGINE *e); +ENGINE_CIPHERS_PTR ENGINE_get_ciphers(const ENGINE *e); +ENGINE_DIGESTS_PTR ENGINE_get_digests(const ENGINE *e); +const EVP_CIPHER *ENGINE_get_cipher(ENGINE *e, int nid); +const EVP_MD *ENGINE_get_digest(ENGINE *e, int nid); +const ENGINE_CMD_DEFN *ENGINE_get_cmd_defns(const ENGINE *e); +int ENGINE_get_flags(const ENGINE *e); + +/* FUNCTIONAL functions. These functions deal with ENGINE structures + * that have (or will) be initialised for use. Broadly speaking, the + * structural functions are useful for iterating the list of available + * engine types, creating new engine types, and other "list" operations. + * These functions actually deal with ENGINEs that are to be used. As + * such these functions can fail (if applicable) when particular + * engines are unavailable - eg. if a hardware accelerator is not + * attached or not functioning correctly. Each ENGINE has 2 reference + * counts; structural and functional. Every time a functional reference + * is obtained or released, a corresponding structural reference is + * automatically obtained or released too. */ + +/* Initialise a engine type for use (or up its reference count if it's + * already in use). This will fail if the engine is not currently + * operational and cannot initialise. */ +int ENGINE_init(ENGINE *e); +/* Free a functional reference to a engine type. This does not require + * a corresponding call to ENGINE_free as it also releases a structural + * reference. */ +int ENGINE_finish(ENGINE *e); + +/* The following functions handle keys that are stored in some secondary + * location, handled by the engine. The storage may be on a card or + * whatever. */ +EVP_PKEY *ENGINE_load_private_key(ENGINE *e, const char *key_id, + UI_METHOD *ui_method, void *callback_data); +EVP_PKEY *ENGINE_load_public_key(ENGINE *e, const char *key_id, + UI_METHOD *ui_method, void *callback_data); + +/* This returns a pointer for the current ENGINE structure that + * is (by default) performing any RSA operations. The value returned + * is an incremented reference, so it should be free'd (ENGINE_finish) + * before it is discarded. */ +ENGINE *ENGINE_get_default_RSA(void); +/* Same for the other "methods" */ +ENGINE *ENGINE_get_default_DSA(void); +ENGINE *ENGINE_get_default_ECDH(void); +ENGINE *ENGINE_get_default_ECDSA(void); +ENGINE *ENGINE_get_default_DH(void); +ENGINE *ENGINE_get_default_RAND(void); +/* These functions can be used to get a functional reference to perform + * ciphering or digesting corresponding to "nid". */ +ENGINE *ENGINE_get_cipher_engine(int nid); +ENGINE *ENGINE_get_digest_engine(int nid); + +/* This sets a new default ENGINE structure for performing RSA + * operations. If the result is non-zero (success) then the ENGINE + * structure will have had its reference count up'd so the caller + * should still free their own reference 'e'. */ +int ENGINE_set_default_RSA(ENGINE *e); +int ENGINE_set_default_string(ENGINE *e, const char *def_list); +/* Same for the other "methods" */ +int ENGINE_set_default_DSA(ENGINE *e); +int ENGINE_set_default_ECDH(ENGINE *e); +int ENGINE_set_default_ECDSA(ENGINE *e); +int ENGINE_set_default_DH(ENGINE *e); +int ENGINE_set_default_RAND(ENGINE *e); +int ENGINE_set_default_ciphers(ENGINE *e); +int ENGINE_set_default_digests(ENGINE *e); + +/* The combination "set" - the flags are bitwise "OR"d from the + * ENGINE_METHOD_*** defines above. As with the "ENGINE_register_complete()" + * function, this function can result in unnecessary static linkage. If your + * application requires only specific functionality, consider using more + * selective functions. */ +int ENGINE_set_default(ENGINE *e, unsigned int flags); + +void ENGINE_add_conf_module(void); + +/* Deprecated functions ... */ +/* int ENGINE_clear_defaults(void); */ + +/**************************/ +/* DYNAMIC ENGINE SUPPORT */ +/**************************/ + +/* Binary/behaviour compatibility levels */ +#define OSSL_DYNAMIC_VERSION (unsigned long)0x00020000 +/* Binary versions older than this are too old for us (whether we're a loader or + * a loadee) */ +#define OSSL_DYNAMIC_OLDEST (unsigned long)0x00020000 + +/* When compiling an ENGINE entirely as an external shared library, loadable by + * the "dynamic" ENGINE, these types are needed. The 'dynamic_fns' structure + * type provides the calling application's (or library's) error functionality + * and memory management function pointers to the loaded library. These should + * be used/set in the loaded library code so that the loading application's + * 'state' will be used/changed in all operations. The 'static_state' pointer + * allows the loaded library to know if it shares the same static data as the + * calling application (or library), and thus whether these callbacks need to be + * set or not. */ +typedef void *(*dyn_MEM_malloc_cb)(size_t); +typedef void *(*dyn_MEM_realloc_cb)(void *, size_t); +typedef void (*dyn_MEM_free_cb)(void *); +typedef struct st_dynamic_MEM_fns { + dyn_MEM_malloc_cb malloc_cb; + dyn_MEM_realloc_cb realloc_cb; + dyn_MEM_free_cb free_cb; + } dynamic_MEM_fns; +/* FIXME: Perhaps the memory and locking code (crypto.h) should declare and use + * these types so we (and any other dependant code) can simplify a bit?? */ +typedef void (*dyn_lock_locking_cb)(int,int,const char *,int); +typedef int (*dyn_lock_add_lock_cb)(int*,int,int,const char *,int); +typedef struct CRYPTO_dynlock_value *(*dyn_dynlock_create_cb)( + const char *,int); +typedef void (*dyn_dynlock_lock_cb)(int,struct CRYPTO_dynlock_value *, + const char *,int); +typedef void (*dyn_dynlock_destroy_cb)(struct CRYPTO_dynlock_value *, + const char *,int); +typedef struct st_dynamic_LOCK_fns { + dyn_lock_locking_cb lock_locking_cb; + dyn_lock_add_lock_cb lock_add_lock_cb; + dyn_dynlock_create_cb dynlock_create_cb; + dyn_dynlock_lock_cb dynlock_lock_cb; + dyn_dynlock_destroy_cb dynlock_destroy_cb; + } dynamic_LOCK_fns; +/* The top-level structure */ +typedef struct st_dynamic_fns { + void *static_state; + const ERR_FNS *err_fns; + const CRYPTO_EX_DATA_IMPL *ex_data_fns; + dynamic_MEM_fns mem_fns; + dynamic_LOCK_fns lock_fns; + } dynamic_fns; + +/* The version checking function should be of this prototype. NB: The + * ossl_version value passed in is the OSSL_DYNAMIC_VERSION of the loading code. + * If this function returns zero, it indicates a (potential) version + * incompatibility and the loaded library doesn't believe it can proceed. + * Otherwise, the returned value is the (latest) version supported by the + * loading library. The loader may still decide that the loaded code's version + * is unsatisfactory and could veto the load. The function is expected to + * be implemented with the symbol name "v_check", and a default implementation + * can be fully instantiated with IMPLEMENT_DYNAMIC_CHECK_FN(). */ +typedef unsigned long (*dynamic_v_check_fn)(unsigned long ossl_version); +#define IMPLEMENT_DYNAMIC_CHECK_FN() \ + OPENSSL_EXPORT unsigned long v_check(unsigned long v) { \ + if(v >= OSSL_DYNAMIC_OLDEST) return OSSL_DYNAMIC_VERSION; \ + return 0; } + +/* This function is passed the ENGINE structure to initialise with its own + * function and command settings. It should not adjust the structural or + * functional reference counts. If this function returns zero, (a) the load will + * be aborted, (b) the previous ENGINE state will be memcpy'd back onto the + * structure, and (c) the shared library will be unloaded. So implementations + * should do their own internal cleanup in failure circumstances otherwise they + * could leak. The 'id' parameter, if non-NULL, represents the ENGINE id that + * the loader is looking for. If this is NULL, the shared library can choose to + * return failure or to initialise a 'default' ENGINE. If non-NULL, the shared + * library must initialise only an ENGINE matching the passed 'id'. The function + * is expected to be implemented with the symbol name "bind_engine". A standard + * implementation can be instantiated with IMPLEMENT_DYNAMIC_BIND_FN(fn) where + * the parameter 'fn' is a callback function that populates the ENGINE structure + * and returns an int value (zero for failure). 'fn' should have prototype; + * [static] int fn(ENGINE *e, const char *id); */ +typedef int (*dynamic_bind_engine)(ENGINE *e, const char *id, + const dynamic_fns *fns); +#define IMPLEMENT_DYNAMIC_BIND_FN(fn) \ + OPENSSL_EXPORT \ + int bind_engine(ENGINE *e, const char *id, const dynamic_fns *fns) { \ + if(ENGINE_get_static_state() == fns->static_state) goto skip_cbs; \ + if(!CRYPTO_set_mem_functions(fns->mem_fns.malloc_cb, \ + fns->mem_fns.realloc_cb, fns->mem_fns.free_cb)) \ + return 0; \ + CRYPTO_set_locking_callback(fns->lock_fns.lock_locking_cb); \ + CRYPTO_set_add_lock_callback(fns->lock_fns.lock_add_lock_cb); \ + CRYPTO_set_dynlock_create_callback(fns->lock_fns.dynlock_create_cb); \ + CRYPTO_set_dynlock_lock_callback(fns->lock_fns.dynlock_lock_cb); \ + CRYPTO_set_dynlock_destroy_callback(fns->lock_fns.dynlock_destroy_cb); \ + if(!CRYPTO_set_ex_data_implementation(fns->ex_data_fns)) \ + return 0; \ + if(!ERR_set_implementation(fns->err_fns)) return 0; \ + skip_cbs: \ + if(!fn(e,id)) return 0; \ + return 1; } + +/* If the loading application (or library) and the loaded ENGINE library share + * the same static data (eg. they're both dynamically linked to the same + * libcrypto.so) we need a way to avoid trying to set system callbacks - this + * would fail, and for the same reason that it's unnecessary to try. If the + * loaded ENGINE has (or gets from through the loader) its own copy of the + * libcrypto static data, we will need to set the callbacks. The easiest way to + * detect this is to have a function that returns a pointer to some static data + * and let the loading application and loaded ENGINE compare their respective + * values. */ +void *ENGINE_get_static_state(void); + +#if defined(__OpenBSD__) || defined(__FreeBSD__) +void ENGINE_setup_bsd_cryptodev(void); +#endif + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_ENGINE_strings(void); + +/* Error codes for the ENGINE functions. */ + +/* Function codes. */ +#define ENGINE_F_DYNAMIC_CTRL 180 +#define ENGINE_F_DYNAMIC_GET_DATA_CTX 181 +#define ENGINE_F_DYNAMIC_LOAD 182 +#define ENGINE_F_DYNAMIC_SET_DATA_CTX 183 +#define ENGINE_F_ENGINE_ADD 105 +#define ENGINE_F_ENGINE_BY_ID 106 +#define ENGINE_F_ENGINE_CMD_IS_EXECUTABLE 170 +#define ENGINE_F_ENGINE_CTRL 142 +#define ENGINE_F_ENGINE_CTRL_CMD 178 +#define ENGINE_F_ENGINE_CTRL_CMD_STRING 171 +#define ENGINE_F_ENGINE_FINISH 107 +#define ENGINE_F_ENGINE_FREE_UTIL 108 +#define ENGINE_F_ENGINE_GET_CIPHER 185 +#define ENGINE_F_ENGINE_GET_DEFAULT_TYPE 177 +#define ENGINE_F_ENGINE_GET_DIGEST 186 +#define ENGINE_F_ENGINE_GET_NEXT 115 +#define ENGINE_F_ENGINE_GET_PREV 116 +#define ENGINE_F_ENGINE_INIT 119 +#define ENGINE_F_ENGINE_LIST_ADD 120 +#define ENGINE_F_ENGINE_LIST_REMOVE 121 +#define ENGINE_F_ENGINE_LOAD_PRIVATE_KEY 150 +#define ENGINE_F_ENGINE_LOAD_PUBLIC_KEY 151 +#define ENGINE_F_ENGINE_NEW 122 +#define ENGINE_F_ENGINE_REMOVE 123 +#define ENGINE_F_ENGINE_SET_DEFAULT_STRING 189 +#define ENGINE_F_ENGINE_SET_DEFAULT_TYPE 126 +#define ENGINE_F_ENGINE_SET_ID 129 +#define ENGINE_F_ENGINE_SET_NAME 130 +#define ENGINE_F_ENGINE_TABLE_REGISTER 184 +#define ENGINE_F_ENGINE_UNLOAD_KEY 152 +#define ENGINE_F_ENGINE_UNLOCKED_FINISH 191 +#define ENGINE_F_ENGINE_UP_REF 190 +#define ENGINE_F_INT_CTRL_HELPER 172 +#define ENGINE_F_INT_ENGINE_CONFIGURE 188 +#define ENGINE_F_INT_ENGINE_MODULE_INIT 187 +#define ENGINE_F_LOG_MESSAGE 141 + +/* Reason codes. */ +#define ENGINE_R_ALREADY_LOADED 100 +#define ENGINE_R_ARGUMENT_IS_NOT_A_NUMBER 133 +#define ENGINE_R_CMD_NOT_EXECUTABLE 134 +#define ENGINE_R_COMMAND_TAKES_INPUT 135 +#define ENGINE_R_COMMAND_TAKES_NO_INPUT 136 +#define ENGINE_R_CONFLICTING_ENGINE_ID 103 +#define ENGINE_R_CTRL_COMMAND_NOT_IMPLEMENTED 119 +#define ENGINE_R_DH_NOT_IMPLEMENTED 139 +#define ENGINE_R_DSA_NOT_IMPLEMENTED 140 +#define ENGINE_R_DSO_FAILURE 104 +#define ENGINE_R_DSO_NOT_FOUND 132 +#define ENGINE_R_ENGINES_SECTION_ERROR 148 +#define ENGINE_R_ENGINE_IS_NOT_IN_LIST 105 +#define ENGINE_R_ENGINE_SECTION_ERROR 149 +#define ENGINE_R_FAILED_LOADING_PRIVATE_KEY 128 +#define ENGINE_R_FAILED_LOADING_PUBLIC_KEY 129 +#define ENGINE_R_FINISH_FAILED 106 +#define ENGINE_R_GET_HANDLE_FAILED 107 +#define ENGINE_R_ID_OR_NAME_MISSING 108 +#define ENGINE_R_INIT_FAILED 109 +#define ENGINE_R_INTERNAL_LIST_ERROR 110 +#define ENGINE_R_INVALID_ARGUMENT 143 +#define ENGINE_R_INVALID_CMD_NAME 137 +#define ENGINE_R_INVALID_CMD_NUMBER 138 +#define ENGINE_R_INVALID_INIT_VALUE 151 +#define ENGINE_R_INVALID_STRING 150 +#define ENGINE_R_NOT_INITIALISED 117 +#define ENGINE_R_NOT_LOADED 112 +#define ENGINE_R_NO_CONTROL_FUNCTION 120 +#define ENGINE_R_NO_INDEX 144 +#define ENGINE_R_NO_LOAD_FUNCTION 125 +#define ENGINE_R_NO_REFERENCE 130 +#define ENGINE_R_NO_SUCH_ENGINE 116 +#define ENGINE_R_NO_UNLOAD_FUNCTION 126 +#define ENGINE_R_PROVIDE_PARAMETERS 113 +#define ENGINE_R_RSA_NOT_IMPLEMENTED 141 +#define ENGINE_R_UNIMPLEMENTED_CIPHER 146 +#define ENGINE_R_UNIMPLEMENTED_DIGEST 147 +#define ENGINE_R_VERSION_INCOMPATIBILITY 145 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/err.h b/production/3rdparty/openssl/include/openssl/err.h new file mode 100644 index 00000000..b723cd97 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/err.h @@ -0,0 +1,318 @@ +/* crypto/err/err.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_ERR_H +#define HEADER_ERR_H + +#include + +#ifndef OPENSSL_NO_FP_API +#include +#include +#endif + +#include +#ifndef OPENSSL_NO_BIO +#include +#endif +#ifndef OPENSSL_NO_LHASH +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef OPENSSL_NO_ERR +#define ERR_PUT_error(a,b,c,d,e) ERR_put_error(a,b,c,d,e) +#else +#define ERR_PUT_error(a,b,c,d,e) ERR_put_error(a,b,c,NULL,0) +#endif + +#include + +#define ERR_TXT_MALLOCED 0x01 +#define ERR_TXT_STRING 0x02 + +#define ERR_FLAG_MARK 0x01 + +#define ERR_NUM_ERRORS 16 +typedef struct err_state_st + { + unsigned long pid; + int err_flags[ERR_NUM_ERRORS]; + unsigned long err_buffer[ERR_NUM_ERRORS]; + char *err_data[ERR_NUM_ERRORS]; + int err_data_flags[ERR_NUM_ERRORS]; + const char *err_file[ERR_NUM_ERRORS]; + int err_line[ERR_NUM_ERRORS]; + int top,bottom; + } ERR_STATE; + +/* library */ +#define ERR_LIB_NONE 1 +#define ERR_LIB_SYS 2 +#define ERR_LIB_BN 3 +#define ERR_LIB_RSA 4 +#define ERR_LIB_DH 5 +#define ERR_LIB_EVP 6 +#define ERR_LIB_BUF 7 +#define ERR_LIB_OBJ 8 +#define ERR_LIB_PEM 9 +#define ERR_LIB_DSA 10 +#define ERR_LIB_X509 11 +/* #define ERR_LIB_METH 12 */ +#define ERR_LIB_ASN1 13 +#define ERR_LIB_CONF 14 +#define ERR_LIB_CRYPTO 15 +#define ERR_LIB_EC 16 +#define ERR_LIB_SSL 20 +/* #define ERR_LIB_SSL23 21 */ +/* #define ERR_LIB_SSL2 22 */ +/* #define ERR_LIB_SSL3 23 */ +/* #define ERR_LIB_RSAREF 30 */ +/* #define ERR_LIB_PROXY 31 */ +#define ERR_LIB_BIO 32 +#define ERR_LIB_PKCS7 33 +#define ERR_LIB_X509V3 34 +#define ERR_LIB_PKCS12 35 +#define ERR_LIB_RAND 36 +#define ERR_LIB_DSO 37 +#define ERR_LIB_ENGINE 38 +#define ERR_LIB_OCSP 39 +#define ERR_LIB_UI 40 +#define ERR_LIB_COMP 41 +#define ERR_LIB_ECDSA 42 +#define ERR_LIB_ECDH 43 +#define ERR_LIB_STORE 44 + +#define ERR_LIB_USER 128 + +#define SYSerr(f,r) ERR_PUT_error(ERR_LIB_SYS,(f),(r),__FILE__,__LINE__) +#define BNerr(f,r) ERR_PUT_error(ERR_LIB_BN,(f),(r),__FILE__,__LINE__) +#define RSAerr(f,r) ERR_PUT_error(ERR_LIB_RSA,(f),(r),__FILE__,__LINE__) +#define DHerr(f,r) ERR_PUT_error(ERR_LIB_DH,(f),(r),__FILE__,__LINE__) +#define EVPerr(f,r) ERR_PUT_error(ERR_LIB_EVP,(f),(r),__FILE__,__LINE__) +#define BUFerr(f,r) ERR_PUT_error(ERR_LIB_BUF,(f),(r),__FILE__,__LINE__) +#define OBJerr(f,r) ERR_PUT_error(ERR_LIB_OBJ,(f),(r),__FILE__,__LINE__) +#define PEMerr(f,r) ERR_PUT_error(ERR_LIB_PEM,(f),(r),__FILE__,__LINE__) +#define DSAerr(f,r) ERR_PUT_error(ERR_LIB_DSA,(f),(r),__FILE__,__LINE__) +#define X509err(f,r) ERR_PUT_error(ERR_LIB_X509,(f),(r),__FILE__,__LINE__) +#define ASN1err(f,r) ERR_PUT_error(ERR_LIB_ASN1,(f),(r),__FILE__,__LINE__) +#define CONFerr(f,r) ERR_PUT_error(ERR_LIB_CONF,(f),(r),__FILE__,__LINE__) +#define CRYPTOerr(f,r) ERR_PUT_error(ERR_LIB_CRYPTO,(f),(r),__FILE__,__LINE__) +#define ECerr(f,r) ERR_PUT_error(ERR_LIB_EC,(f),(r),__FILE__,__LINE__) +#define SSLerr(f,r) ERR_PUT_error(ERR_LIB_SSL,(f),(r),__FILE__,__LINE__) +#define BIOerr(f,r) ERR_PUT_error(ERR_LIB_BIO,(f),(r),__FILE__,__LINE__) +#define PKCS7err(f,r) ERR_PUT_error(ERR_LIB_PKCS7,(f),(r),__FILE__,__LINE__) +#define X509V3err(f,r) ERR_PUT_error(ERR_LIB_X509V3,(f),(r),__FILE__,__LINE__) +#define PKCS12err(f,r) ERR_PUT_error(ERR_LIB_PKCS12,(f),(r),__FILE__,__LINE__) +#define RANDerr(f,r) ERR_PUT_error(ERR_LIB_RAND,(f),(r),__FILE__,__LINE__) +#define DSOerr(f,r) ERR_PUT_error(ERR_LIB_DSO,(f),(r),__FILE__,__LINE__) +#define ENGINEerr(f,r) ERR_PUT_error(ERR_LIB_ENGINE,(f),(r),__FILE__,__LINE__) +#define OCSPerr(f,r) ERR_PUT_error(ERR_LIB_OCSP,(f),(r),__FILE__,__LINE__) +#define UIerr(f,r) ERR_PUT_error(ERR_LIB_UI,(f),(r),__FILE__,__LINE__) +#define COMPerr(f,r) ERR_PUT_error(ERR_LIB_COMP,(f),(r),__FILE__,__LINE__) +#define ECDSAerr(f,r) ERR_PUT_error(ERR_LIB_ECDSA,(f),(r),__FILE__,__LINE__) +#define ECDHerr(f,r) ERR_PUT_error(ERR_LIB_ECDH,(f),(r),__FILE__,__LINE__) +#define STOREerr(f,r) ERR_PUT_error(ERR_LIB_STORE,(f),(r),__FILE__,__LINE__) + +/* Borland C seems too stupid to be able to shift and do longs in + * the pre-processor :-( */ +#define ERR_PACK(l,f,r) (((((unsigned long)l)&0xffL)*0x1000000)| \ + ((((unsigned long)f)&0xfffL)*0x1000)| \ + ((((unsigned long)r)&0xfffL))) +#define ERR_GET_LIB(l) (int)((((unsigned long)l)>>24L)&0xffL) +#define ERR_GET_FUNC(l) (int)((((unsigned long)l)>>12L)&0xfffL) +#define ERR_GET_REASON(l) (int)((l)&0xfffL) +#define ERR_FATAL_ERROR(l) (int)((l)&ERR_R_FATAL) + + +/* OS functions */ +#define SYS_F_FOPEN 1 +#define SYS_F_CONNECT 2 +#define SYS_F_GETSERVBYNAME 3 +#define SYS_F_SOCKET 4 +#define SYS_F_IOCTLSOCKET 5 +#define SYS_F_BIND 6 +#define SYS_F_LISTEN 7 +#define SYS_F_ACCEPT 8 +#define SYS_F_WSASTARTUP 9 /* Winsock stuff */ +#define SYS_F_OPENDIR 10 +#define SYS_F_FREAD 11 + + +/* reasons */ +#define ERR_R_SYS_LIB ERR_LIB_SYS /* 2 */ +#define ERR_R_BN_LIB ERR_LIB_BN /* 3 */ +#define ERR_R_RSA_LIB ERR_LIB_RSA /* 4 */ +#define ERR_R_DH_LIB ERR_LIB_DH /* 5 */ +#define ERR_R_EVP_LIB ERR_LIB_EVP /* 6 */ +#define ERR_R_BUF_LIB ERR_LIB_BUF /* 7 */ +#define ERR_R_OBJ_LIB ERR_LIB_OBJ /* 8 */ +#define ERR_R_PEM_LIB ERR_LIB_PEM /* 9 */ +#define ERR_R_DSA_LIB ERR_LIB_DSA /* 10 */ +#define ERR_R_X509_LIB ERR_LIB_X509 /* 11 */ +#define ERR_R_ASN1_LIB ERR_LIB_ASN1 /* 13 */ +#define ERR_R_CONF_LIB ERR_LIB_CONF /* 14 */ +#define ERR_R_CRYPTO_LIB ERR_LIB_CRYPTO /* 15 */ +#define ERR_R_EC_LIB ERR_LIB_EC /* 16 */ +#define ERR_R_SSL_LIB ERR_LIB_SSL /* 20 */ +#define ERR_R_BIO_LIB ERR_LIB_BIO /* 32 */ +#define ERR_R_PKCS7_LIB ERR_LIB_PKCS7 /* 33 */ +#define ERR_R_X509V3_LIB ERR_LIB_X509V3 /* 34 */ +#define ERR_R_PKCS12_LIB ERR_LIB_PKCS12 /* 35 */ +#define ERR_R_RAND_LIB ERR_LIB_RAND /* 36 */ +#define ERR_R_DSO_LIB ERR_LIB_DSO /* 37 */ +#define ERR_R_ENGINE_LIB ERR_LIB_ENGINE /* 38 */ +#define ERR_R_OCSP_LIB ERR_LIB_OCSP /* 39 */ +#define ERR_R_UI_LIB ERR_LIB_UI /* 40 */ +#define ERR_R_COMP_LIB ERR_LIB_COMP /* 41 */ +#define ERR_R_ECDSA_LIB ERR_LIB_ECDSA /* 42 */ +#define ERR_R_ECDH_LIB ERR_LIB_ECDH /* 43 */ +#define ERR_R_STORE_LIB ERR_LIB_STORE /* 44 */ + +#define ERR_R_NESTED_ASN1_ERROR 58 +#define ERR_R_BAD_ASN1_OBJECT_HEADER 59 +#define ERR_R_BAD_GET_ASN1_OBJECT_CALL 60 +#define ERR_R_EXPECTING_AN_ASN1_SEQUENCE 61 +#define ERR_R_ASN1_LENGTH_MISMATCH 62 +#define ERR_R_MISSING_ASN1_EOS 63 + +/* fatal error */ +#define ERR_R_FATAL 64 +#define ERR_R_MALLOC_FAILURE (1|ERR_R_FATAL) +#define ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED (2|ERR_R_FATAL) +#define ERR_R_PASSED_NULL_PARAMETER (3|ERR_R_FATAL) +#define ERR_R_INTERNAL_ERROR (4|ERR_R_FATAL) +#define ERR_R_DISABLED (5|ERR_R_FATAL) + +/* 99 is the maximum possible ERR_R_... code, higher values + * are reserved for the individual libraries */ + + +typedef struct ERR_string_data_st + { + unsigned long error; + const char *string; + } ERR_STRING_DATA; + +void ERR_put_error(int lib, int func,int reason,const char *file,int line); +void ERR_set_error_data(char *data,int flags); + +unsigned long ERR_get_error(void); +unsigned long ERR_get_error_line(const char **file,int *line); +unsigned long ERR_get_error_line_data(const char **file,int *line, + const char **data, int *flags); +unsigned long ERR_peek_error(void); +unsigned long ERR_peek_error_line(const char **file,int *line); +unsigned long ERR_peek_error_line_data(const char **file,int *line, + const char **data,int *flags); +unsigned long ERR_peek_last_error(void); +unsigned long ERR_peek_last_error_line(const char **file,int *line); +unsigned long ERR_peek_last_error_line_data(const char **file,int *line, + const char **data,int *flags); +void ERR_clear_error(void ); +char *ERR_error_string(unsigned long e,char *buf); +void ERR_error_string_n(unsigned long e, char *buf, size_t len); +const char *ERR_lib_error_string(unsigned long e); +const char *ERR_func_error_string(unsigned long e); +const char *ERR_reason_error_string(unsigned long e); +void ERR_print_errors_cb(int (*cb)(const char *str, size_t len, void *u), + void *u); +#ifndef OPENSSL_NO_FP_API +void ERR_print_errors_fp(FILE *fp); +#endif +#ifndef OPENSSL_NO_BIO +void ERR_print_errors(BIO *bp); +void ERR_add_error_data(int num, ...); +#endif +void ERR_load_strings(int lib,ERR_STRING_DATA str[]); +void ERR_unload_strings(int lib,ERR_STRING_DATA str[]); +void ERR_load_ERR_strings(void); +void ERR_load_crypto_strings(void); +void ERR_free_strings(void); + +void ERR_remove_state(unsigned long pid); /* if zero we look it up */ +ERR_STATE *ERR_get_state(void); + +#ifndef OPENSSL_NO_LHASH +LHASH *ERR_get_string_table(void); +LHASH *ERR_get_err_state_table(void); +void ERR_release_err_state_table(LHASH **hash); +#endif + +int ERR_get_next_error_library(void); + +int ERR_set_mark(void); +int ERR_pop_to_mark(void); + +/* Already defined in ossl_typ.h */ +/* typedef struct st_ERR_FNS ERR_FNS; */ +/* An application can use this function and provide the return value to loaded + * modules that should use the application's ERR state/functionality */ +const ERR_FNS *ERR_get_implementation(void); +/* A loaded module should call this function prior to any ERR operations using + * the application's "ERR_FNS". */ +int ERR_set_implementation(const ERR_FNS *fns); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/evp.h b/production/3rdparty/openssl/include/openssl/evp.h new file mode 100644 index 00000000..116a12ff --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/evp.h @@ -0,0 +1,941 @@ +/* crypto/evp/evp.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_ENVELOPE_H +#define HEADER_ENVELOPE_H + +#ifdef OPENSSL_ALGORITHM_DEFINES +# include +#else +# define OPENSSL_ALGORITHM_DEFINES +# include +# undef OPENSSL_ALGORITHM_DEFINES +#endif + +#include + +#include + +#ifndef OPENSSL_NO_BIO +#include +#endif + +/* +#define EVP_RC2_KEY_SIZE 16 +#define EVP_RC4_KEY_SIZE 16 +#define EVP_BLOWFISH_KEY_SIZE 16 +#define EVP_CAST5_KEY_SIZE 16 +#define EVP_RC5_32_12_16_KEY_SIZE 16 +*/ +#define EVP_MAX_MD_SIZE 64 /* longest known is SHA512 */ +#define EVP_MAX_KEY_LENGTH 32 +#define EVP_MAX_IV_LENGTH 16 +#define EVP_MAX_BLOCK_LENGTH 32 + +#define PKCS5_SALT_LEN 8 +/* Default PKCS#5 iteration count */ +#define PKCS5_DEFAULT_ITER 2048 + +#include + +#define EVP_PK_RSA 0x0001 +#define EVP_PK_DSA 0x0002 +#define EVP_PK_DH 0x0004 +#define EVP_PK_EC 0x0008 +#define EVP_PKT_SIGN 0x0010 +#define EVP_PKT_ENC 0x0020 +#define EVP_PKT_EXCH 0x0040 +#define EVP_PKS_RSA 0x0100 +#define EVP_PKS_DSA 0x0200 +#define EVP_PKS_EC 0x0400 +#define EVP_PKT_EXP 0x1000 /* <= 512 bit key */ + +#define EVP_PKEY_NONE NID_undef +#define EVP_PKEY_RSA NID_rsaEncryption +#define EVP_PKEY_RSA2 NID_rsa +#define EVP_PKEY_DSA NID_dsa +#define EVP_PKEY_DSA1 NID_dsa_2 +#define EVP_PKEY_DSA2 NID_dsaWithSHA +#define EVP_PKEY_DSA3 NID_dsaWithSHA1 +#define EVP_PKEY_DSA4 NID_dsaWithSHA1_2 +#define EVP_PKEY_DH NID_dhKeyAgreement +#define EVP_PKEY_EC NID_X9_62_id_ecPublicKey + +#ifdef __cplusplus +extern "C" { +#endif + +/* Type needs to be a bit field + * Sub-type needs to be for variations on the method, as in, can it do + * arbitrary encryption.... */ +struct evp_pkey_st + { + int type; + int save_type; + int references; + union { + char *ptr; +#ifndef OPENSSL_NO_RSA + struct rsa_st *rsa; /* RSA */ +#endif +#ifndef OPENSSL_NO_DSA + struct dsa_st *dsa; /* DSA */ +#endif +#ifndef OPENSSL_NO_DH + struct dh_st *dh; /* DH */ +#endif +#ifndef OPENSSL_NO_EC + struct ec_key_st *ec; /* ECC */ +#endif + } pkey; + int save_parameters; + STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */ + } /* EVP_PKEY */; + +#define EVP_PKEY_MO_SIGN 0x0001 +#define EVP_PKEY_MO_VERIFY 0x0002 +#define EVP_PKEY_MO_ENCRYPT 0x0004 +#define EVP_PKEY_MO_DECRYPT 0x0008 + +#if 0 +/* This structure is required to tie the message digest and signing together. + * The lookup can be done by md/pkey_method, oid, oid/pkey_method, or + * oid, md and pkey. + * This is required because for various smart-card perform the digest and + * signing/verification on-board. To handle this case, the specific + * EVP_MD and EVP_PKEY_METHODs need to be closely associated. + * When a PKEY is created, it will have a EVP_PKEY_METHOD associated with it. + * This can either be software or a token to provide the required low level + * routines. + */ +typedef struct evp_pkey_md_st + { + int oid; + EVP_MD *md; + EVP_PKEY_METHOD *pkey; + } EVP_PKEY_MD; + +#define EVP_rsa_md2() \ + EVP_PKEY_MD_add(NID_md2WithRSAEncryption,\ + EVP_rsa_pkcs1(),EVP_md2()) +#define EVP_rsa_md5() \ + EVP_PKEY_MD_add(NID_md5WithRSAEncryption,\ + EVP_rsa_pkcs1(),EVP_md5()) +#define EVP_rsa_sha0() \ + EVP_PKEY_MD_add(NID_shaWithRSAEncryption,\ + EVP_rsa_pkcs1(),EVP_sha()) +#define EVP_rsa_sha1() \ + EVP_PKEY_MD_add(NID_sha1WithRSAEncryption,\ + EVP_rsa_pkcs1(),EVP_sha1()) +#define EVP_rsa_ripemd160() \ + EVP_PKEY_MD_add(NID_ripemd160WithRSA,\ + EVP_rsa_pkcs1(),EVP_ripemd160()) +#define EVP_rsa_mdc2() \ + EVP_PKEY_MD_add(NID_mdc2WithRSA,\ + EVP_rsa_octet_string(),EVP_mdc2()) +#define EVP_dsa_sha() \ + EVP_PKEY_MD_add(NID_dsaWithSHA,\ + EVP_dsa(),EVP_sha()) +#define EVP_dsa_sha1() \ + EVP_PKEY_MD_add(NID_dsaWithSHA1,\ + EVP_dsa(),EVP_sha1()) + +typedef struct evp_pkey_method_st + { + char *name; + int flags; + int type; /* RSA, DSA, an SSLeay specific constant */ + int oid; /* For the pub-key type */ + int encrypt_oid; /* pub/priv key encryption */ + + int (*sign)(); + int (*verify)(); + struct { + int (*set)(); /* get and/or set the underlying type */ + int (*get)(); + int (*encrypt)(); + int (*decrypt)(); + int (*i2d)(); + int (*d2i)(); + int (*dup)(); + } pub,priv; + int (*set_asn1_parameters)(); + int (*get_asn1_parameters)(); + } EVP_PKEY_METHOD; +#endif + +#ifndef EVP_MD +struct env_md_st + { + int type; + int pkey_type; + int md_size; + unsigned long flags; + int (*init)(EVP_MD_CTX *ctx); + int (*update)(EVP_MD_CTX *ctx,const void *data,size_t count); + int (*final)(EVP_MD_CTX *ctx,unsigned char *md); + int (*copy)(EVP_MD_CTX *to,const EVP_MD_CTX *from); + int (*cleanup)(EVP_MD_CTX *ctx); + + /* FIXME: prototype these some day */ + int (*sign)(int type, const unsigned char *m, unsigned int m_length, + unsigned char *sigret, unsigned int *siglen, void *key); + int (*verify)(int type, const unsigned char *m, unsigned int m_length, + const unsigned char *sigbuf, unsigned int siglen, + void *key); + int required_pkey_type[5]; /*EVP_PKEY_xxx */ + int block_size; + int ctx_size; /* how big does the ctx->md_data need to be */ + } /* EVP_MD */; + +typedef int evp_sign_method(int type,const unsigned char *m, + unsigned int m_length,unsigned char *sigret, + unsigned int *siglen, void *key); +typedef int evp_verify_method(int type,const unsigned char *m, + unsigned int m_length,const unsigned char *sigbuf, + unsigned int siglen, void *key); + +#define EVP_MD_FLAG_ONESHOT 0x0001 /* digest can only handle a single + * block */ + +#define EVP_PKEY_NULL_method NULL,NULL,{0,0,0,0} + +#ifndef OPENSSL_NO_DSA +#define EVP_PKEY_DSA_method (evp_sign_method *)DSA_sign, \ + (evp_verify_method *)DSA_verify, \ + {EVP_PKEY_DSA,EVP_PKEY_DSA2,EVP_PKEY_DSA3, \ + EVP_PKEY_DSA4,0} +#else +#define EVP_PKEY_DSA_method EVP_PKEY_NULL_method +#endif + +#ifndef OPENSSL_NO_ECDSA +#define EVP_PKEY_ECDSA_method (evp_sign_method *)ECDSA_sign, \ + (evp_verify_method *)ECDSA_verify, \ + {EVP_PKEY_EC,0,0,0} +#else +#define EVP_PKEY_ECDSA_method EVP_PKEY_NULL_method +#endif + +#ifndef OPENSSL_NO_RSA +#define EVP_PKEY_RSA_method (evp_sign_method *)RSA_sign, \ + (evp_verify_method *)RSA_verify, \ + {EVP_PKEY_RSA,EVP_PKEY_RSA2,0,0} +#define EVP_PKEY_RSA_ASN1_OCTET_STRING_method \ + (evp_sign_method *)RSA_sign_ASN1_OCTET_STRING, \ + (evp_verify_method *)RSA_verify_ASN1_OCTET_STRING, \ + {EVP_PKEY_RSA,EVP_PKEY_RSA2,0,0} +#else +#define EVP_PKEY_RSA_method EVP_PKEY_NULL_method +#define EVP_PKEY_RSA_ASN1_OCTET_STRING_method EVP_PKEY_NULL_method +#endif + +#endif /* !EVP_MD */ + +struct env_md_ctx_st + { + const EVP_MD *digest; + ENGINE *engine; /* functional reference if 'digest' is ENGINE-provided */ + unsigned long flags; + void *md_data; + } /* EVP_MD_CTX */; + +/* values for EVP_MD_CTX flags */ + +#define EVP_MD_CTX_FLAG_ONESHOT 0x0001 /* digest update will be called + * once only */ +#define EVP_MD_CTX_FLAG_CLEANED 0x0002 /* context has already been + * cleaned */ +#define EVP_MD_CTX_FLAG_REUSE 0x0004 /* Don't free up ctx->md_data + * in EVP_MD_CTX_cleanup */ + +struct evp_cipher_st + { + int nid; + int block_size; + int key_len; /* Default value for variable length ciphers */ + int iv_len; + unsigned long flags; /* Various flags */ + int (*init)(EVP_CIPHER_CTX *ctx, const unsigned char *key, + const unsigned char *iv, int enc); /* init key */ + int (*do_cipher)(EVP_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, unsigned int inl);/* encrypt/decrypt data */ + int (*cleanup)(EVP_CIPHER_CTX *); /* cleanup ctx */ + int ctx_size; /* how big ctx->cipher_data needs to be */ + int (*set_asn1_parameters)(EVP_CIPHER_CTX *, ASN1_TYPE *); /* Populate a ASN1_TYPE with parameters */ + int (*get_asn1_parameters)(EVP_CIPHER_CTX *, ASN1_TYPE *); /* Get parameters from a ASN1_TYPE */ + int (*ctrl)(EVP_CIPHER_CTX *, int type, int arg, void *ptr); /* Miscellaneous operations */ + void *app_data; /* Application data */ + } /* EVP_CIPHER */; + +/* Values for cipher flags */ + +/* Modes for ciphers */ + +#define EVP_CIPH_STREAM_CIPHER 0x0 +#define EVP_CIPH_ECB_MODE 0x1 +#define EVP_CIPH_CBC_MODE 0x2 +#define EVP_CIPH_CFB_MODE 0x3 +#define EVP_CIPH_OFB_MODE 0x4 +#define EVP_CIPH_MODE 0x7 +/* Set if variable length cipher */ +#define EVP_CIPH_VARIABLE_LENGTH 0x8 +/* Set if the iv handling should be done by the cipher itself */ +#define EVP_CIPH_CUSTOM_IV 0x10 +/* Set if the cipher's init() function should be called if key is NULL */ +#define EVP_CIPH_ALWAYS_CALL_INIT 0x20 +/* Call ctrl() to init cipher parameters */ +#define EVP_CIPH_CTRL_INIT 0x40 +/* Don't use standard key length function */ +#define EVP_CIPH_CUSTOM_KEY_LENGTH 0x80 +/* Don't use standard block padding */ +#define EVP_CIPH_NO_PADDING 0x100 +/* cipher handles random key generation */ +#define EVP_CIPH_RAND_KEY 0x200 + +/* ctrl() values */ + +#define EVP_CTRL_INIT 0x0 +#define EVP_CTRL_SET_KEY_LENGTH 0x1 +#define EVP_CTRL_GET_RC2_KEY_BITS 0x2 +#define EVP_CTRL_SET_RC2_KEY_BITS 0x3 +#define EVP_CTRL_GET_RC5_ROUNDS 0x4 +#define EVP_CTRL_SET_RC5_ROUNDS 0x5 +#define EVP_CTRL_RAND_KEY 0x6 + +typedef struct evp_cipher_info_st + { + const EVP_CIPHER *cipher; + unsigned char iv[EVP_MAX_IV_LENGTH]; + } EVP_CIPHER_INFO; + +struct evp_cipher_ctx_st + { + const EVP_CIPHER *cipher; + ENGINE *engine; /* functional reference if 'cipher' is ENGINE-provided */ + int encrypt; /* encrypt or decrypt */ + int buf_len; /* number we have left */ + + unsigned char oiv[EVP_MAX_IV_LENGTH]; /* original iv */ + unsigned char iv[EVP_MAX_IV_LENGTH]; /* working iv */ + unsigned char buf[EVP_MAX_BLOCK_LENGTH];/* saved partial block */ + int num; /* used by cfb/ofb mode */ + + void *app_data; /* application stuff */ + int key_len; /* May change for variable length cipher */ + unsigned long flags; /* Various flags */ + void *cipher_data; /* per EVP data */ + int final_used; + int block_mask; + unsigned char final[EVP_MAX_BLOCK_LENGTH];/* possible final block */ + } /* EVP_CIPHER_CTX */; + +typedef struct evp_Encode_Ctx_st + { + int num; /* number saved in a partial encode/decode */ + int length; /* The length is either the output line length + * (in input bytes) or the shortest input line + * length that is ok. Once decoding begins, + * the length is adjusted up each time a longer + * line is decoded */ + unsigned char enc_data[80]; /* data to encode */ + int line_num; /* number read on current line */ + int expect_nl; + } EVP_ENCODE_CTX; + +/* Password based encryption function */ +typedef int (EVP_PBE_KEYGEN)(EVP_CIPHER_CTX *ctx, const char *pass, int passlen, + ASN1_TYPE *param, const EVP_CIPHER *cipher, + const EVP_MD *md, int en_de); + +#ifndef OPENSSL_NO_RSA +#define EVP_PKEY_assign_RSA(pkey,rsa) EVP_PKEY_assign((pkey),EVP_PKEY_RSA,\ + (char *)(rsa)) +#endif + +#ifndef OPENSSL_NO_DSA +#define EVP_PKEY_assign_DSA(pkey,dsa) EVP_PKEY_assign((pkey),EVP_PKEY_DSA,\ + (char *)(dsa)) +#endif + +#ifndef OPENSSL_NO_DH +#define EVP_PKEY_assign_DH(pkey,dh) EVP_PKEY_assign((pkey),EVP_PKEY_DH,\ + (char *)(dh)) +#endif + +#ifndef OPENSSL_NO_EC +#define EVP_PKEY_assign_EC_KEY(pkey,eckey) EVP_PKEY_assign((pkey),EVP_PKEY_EC,\ + (char *)(eckey)) +#endif + +/* Add some extra combinations */ +#define EVP_get_digestbynid(a) EVP_get_digestbyname(OBJ_nid2sn(a)) +#define EVP_get_digestbyobj(a) EVP_get_digestbynid(OBJ_obj2nid(a)) +#define EVP_get_cipherbynid(a) EVP_get_cipherbyname(OBJ_nid2sn(a)) +#define EVP_get_cipherbyobj(a) EVP_get_cipherbynid(OBJ_obj2nid(a)) + +#define EVP_MD_type(e) ((e)->type) +#define EVP_MD_nid(e) EVP_MD_type(e) +#define EVP_MD_name(e) OBJ_nid2sn(EVP_MD_nid(e)) +#define EVP_MD_pkey_type(e) ((e)->pkey_type) +#define EVP_MD_size(e) ((e)->md_size) +#define EVP_MD_block_size(e) ((e)->block_size) + +#define EVP_MD_CTX_md(e) ((e)->digest) +#define EVP_MD_CTX_size(e) EVP_MD_size((e)->digest) +#define EVP_MD_CTX_block_size(e) EVP_MD_block_size((e)->digest) +#define EVP_MD_CTX_type(e) EVP_MD_type((e)->digest) + +#define EVP_CIPHER_nid(e) ((e)->nid) +#define EVP_CIPHER_name(e) OBJ_nid2sn(EVP_CIPHER_nid(e)) +#define EVP_CIPHER_block_size(e) ((e)->block_size) +#define EVP_CIPHER_key_length(e) ((e)->key_len) +#define EVP_CIPHER_iv_length(e) ((e)->iv_len) +#define EVP_CIPHER_flags(e) ((e)->flags) +#define EVP_CIPHER_mode(e) (((e)->flags) & EVP_CIPH_MODE) + +#define EVP_CIPHER_CTX_cipher(e) ((e)->cipher) +#define EVP_CIPHER_CTX_nid(e) ((e)->cipher->nid) +#define EVP_CIPHER_CTX_block_size(e) ((e)->cipher->block_size) +#define EVP_CIPHER_CTX_key_length(e) ((e)->key_len) +#define EVP_CIPHER_CTX_iv_length(e) ((e)->cipher->iv_len) +#define EVP_CIPHER_CTX_get_app_data(e) ((e)->app_data) +#define EVP_CIPHER_CTX_set_app_data(e,d) ((e)->app_data=(char *)(d)) +#define EVP_CIPHER_CTX_type(c) EVP_CIPHER_type(EVP_CIPHER_CTX_cipher(c)) +#define EVP_CIPHER_CTX_flags(e) ((e)->cipher->flags) +#define EVP_CIPHER_CTX_mode(e) ((e)->cipher->flags & EVP_CIPH_MODE) + +#define EVP_ENCODE_LENGTH(l) (((l+2)/3*4)+(l/48+1)*2+80) +#define EVP_DECODE_LENGTH(l) ((l+3)/4*3+80) + +#define EVP_SignInit_ex(a,b,c) EVP_DigestInit_ex(a,b,c) +#define EVP_SignInit(a,b) EVP_DigestInit(a,b) +#define EVP_SignUpdate(a,b,c) EVP_DigestUpdate(a,b,c) +#define EVP_VerifyInit_ex(a,b,c) EVP_DigestInit_ex(a,b,c) +#define EVP_VerifyInit(a,b) EVP_DigestInit(a,b) +#define EVP_VerifyUpdate(a,b,c) EVP_DigestUpdate(a,b,c) +#define EVP_OpenUpdate(a,b,c,d,e) EVP_DecryptUpdate(a,b,c,d,e) +#define EVP_SealUpdate(a,b,c,d,e) EVP_EncryptUpdate(a,b,c,d,e) + +#ifdef CONST_STRICT +void BIO_set_md(BIO *,const EVP_MD *md); +#else +# define BIO_set_md(b,md) BIO_ctrl(b,BIO_C_SET_MD,0,(char *)md) +#endif +#define BIO_get_md(b,mdp) BIO_ctrl(b,BIO_C_GET_MD,0,(char *)mdp) +#define BIO_get_md_ctx(b,mdcp) BIO_ctrl(b,BIO_C_GET_MD_CTX,0,(char *)mdcp) +#define BIO_get_cipher_status(b) BIO_ctrl(b,BIO_C_GET_CIPHER_STATUS,0,NULL) +#define BIO_get_cipher_ctx(b,c_pp) BIO_ctrl(b,BIO_C_GET_CIPHER_CTX,0,(char *)c_pp) + +#define EVP_Cipher(c,o,i,l) (c)->cipher->do_cipher((c),(o),(i),(l)) + +#define EVP_add_cipher_alias(n,alias) \ + OBJ_NAME_add((alias),OBJ_NAME_TYPE_CIPHER_METH|OBJ_NAME_ALIAS,(n)) +#define EVP_add_digest_alias(n,alias) \ + OBJ_NAME_add((alias),OBJ_NAME_TYPE_MD_METH|OBJ_NAME_ALIAS,(n)) +#define EVP_delete_cipher_alias(alias) \ + OBJ_NAME_remove(alias,OBJ_NAME_TYPE_CIPHER_METH|OBJ_NAME_ALIAS); +#define EVP_delete_digest_alias(alias) \ + OBJ_NAME_remove(alias,OBJ_NAME_TYPE_MD_METH|OBJ_NAME_ALIAS); + +void EVP_MD_CTX_init(EVP_MD_CTX *ctx); +int EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx); +EVP_MD_CTX *EVP_MD_CTX_create(void); +void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx); +int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out,const EVP_MD_CTX *in); +#define EVP_MD_CTX_set_flags(ctx,flgs) ((ctx)->flags|=(flgs)) +#define EVP_MD_CTX_clear_flags(ctx,flgs) ((ctx)->flags&=~(flgs)) +#define EVP_MD_CTX_test_flags(ctx,flgs) ((ctx)->flags&(flgs)) +int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl); +int EVP_DigestUpdate(EVP_MD_CTX *ctx,const void *d, + size_t cnt); +int EVP_DigestFinal_ex(EVP_MD_CTX *ctx,unsigned char *md,unsigned int *s); +int EVP_Digest(const void *data, size_t count, + unsigned char *md, unsigned int *size, const EVP_MD *type, ENGINE *impl); + +int EVP_MD_CTX_copy(EVP_MD_CTX *out,const EVP_MD_CTX *in); +int EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type); +int EVP_DigestFinal(EVP_MD_CTX *ctx,unsigned char *md,unsigned int *s); + +int EVP_read_pw_string(char *buf,int length,const char *prompt,int verify); +void EVP_set_pw_prompt(const char *prompt); +char * EVP_get_pw_prompt(void); + +int EVP_BytesToKey(const EVP_CIPHER *type,const EVP_MD *md, + const unsigned char *salt, const unsigned char *data, + int datal, int count, unsigned char *key,unsigned char *iv); + +int EVP_EncryptInit(EVP_CIPHER_CTX *ctx,const EVP_CIPHER *cipher, + const unsigned char *key, const unsigned char *iv); +int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx,const EVP_CIPHER *cipher, ENGINE *impl, + const unsigned char *key, const unsigned char *iv); +int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, + int *outl, const unsigned char *in, int inl); +int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl); +int EVP_EncryptFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl); + +int EVP_DecryptInit(EVP_CIPHER_CTX *ctx,const EVP_CIPHER *cipher, + const unsigned char *key, const unsigned char *iv); +int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx,const EVP_CIPHER *cipher, ENGINE *impl, + const unsigned char *key, const unsigned char *iv); +int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, + int *outl, const unsigned char *in, int inl); +int EVP_DecryptFinal(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl); +int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl); + +int EVP_CipherInit(EVP_CIPHER_CTX *ctx,const EVP_CIPHER *cipher, + const unsigned char *key,const unsigned char *iv, + int enc); +int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx,const EVP_CIPHER *cipher, ENGINE *impl, + const unsigned char *key,const unsigned char *iv, + int enc); +int EVP_CipherUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, + int *outl, const unsigned char *in, int inl); +int EVP_CipherFinal(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl); +int EVP_CipherFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl); + +int EVP_SignFinal(EVP_MD_CTX *ctx,unsigned char *md,unsigned int *s, + EVP_PKEY *pkey); + +int EVP_VerifyFinal(EVP_MD_CTX *ctx,const unsigned char *sigbuf, + unsigned int siglen,EVP_PKEY *pkey); + +int EVP_OpenInit(EVP_CIPHER_CTX *ctx,const EVP_CIPHER *type, + const unsigned char *ek, int ekl, const unsigned char *iv, + EVP_PKEY *priv); +int EVP_OpenFinal(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl); + +int EVP_SealInit(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, + unsigned char **ek, int *ekl, unsigned char *iv, + EVP_PKEY **pubk, int npubk); +int EVP_SealFinal(EVP_CIPHER_CTX *ctx,unsigned char *out,int *outl); + +void EVP_EncodeInit(EVP_ENCODE_CTX *ctx); +void EVP_EncodeUpdate(EVP_ENCODE_CTX *ctx,unsigned char *out,int *outl, + const unsigned char *in,int inl); +void EVP_EncodeFinal(EVP_ENCODE_CTX *ctx,unsigned char *out,int *outl); +int EVP_EncodeBlock(unsigned char *t, const unsigned char *f, int n); + +void EVP_DecodeInit(EVP_ENCODE_CTX *ctx); +int EVP_DecodeUpdate(EVP_ENCODE_CTX *ctx,unsigned char *out,int *outl, + const unsigned char *in, int inl); +int EVP_DecodeFinal(EVP_ENCODE_CTX *ctx, unsigned + char *out, int *outl); +int EVP_DecodeBlock(unsigned char *t, const unsigned char *f, int n); + +void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *a); +int EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a); +EVP_CIPHER_CTX *EVP_CIPHER_CTX_new(void); +void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *a); +int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *x, int keylen); +int EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *c, int pad); +int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr); +int EVP_CIPHER_CTX_rand_key(EVP_CIPHER_CTX *ctx, unsigned char *key); + +#ifndef OPENSSL_NO_BIO +BIO_METHOD *BIO_f_md(void); +BIO_METHOD *BIO_f_base64(void); +BIO_METHOD *BIO_f_cipher(void); +BIO_METHOD *BIO_f_reliable(void); +void BIO_set_cipher(BIO *b,const EVP_CIPHER *c,const unsigned char *k, + const unsigned char *i, int enc); +#endif + +const EVP_MD *EVP_md_null(void); +#ifndef OPENSSL_NO_MD2 +const EVP_MD *EVP_md2(void); +#endif +#ifndef OPENSSL_NO_MD4 +const EVP_MD *EVP_md4(void); +#endif +#ifndef OPENSSL_NO_MD5 +const EVP_MD *EVP_md5(void); +#endif +#ifndef OPENSSL_NO_SHA +const EVP_MD *EVP_sha(void); +const EVP_MD *EVP_sha1(void); +const EVP_MD *EVP_dss(void); +const EVP_MD *EVP_dss1(void); +const EVP_MD *EVP_ecdsa(void); +#endif +#ifndef OPENSSL_NO_SHA256 +const EVP_MD *EVP_sha224(void); +const EVP_MD *EVP_sha256(void); +#endif +#ifndef OPENSSL_NO_SHA512 +const EVP_MD *EVP_sha384(void); +const EVP_MD *EVP_sha512(void); +#endif +#ifndef OPENSSL_NO_MDC2 +const EVP_MD *EVP_mdc2(void); +#endif +#ifndef OPENSSL_NO_RIPEMD +const EVP_MD *EVP_ripemd160(void); +#endif +const EVP_CIPHER *EVP_enc_null(void); /* does nothing :-) */ +#ifndef OPENSSL_NO_DES +const EVP_CIPHER *EVP_des_ecb(void); +const EVP_CIPHER *EVP_des_ede(void); +const EVP_CIPHER *EVP_des_ede3(void); +const EVP_CIPHER *EVP_des_ede_ecb(void); +const EVP_CIPHER *EVP_des_ede3_ecb(void); +const EVP_CIPHER *EVP_des_cfb64(void); +# define EVP_des_cfb EVP_des_cfb64 +const EVP_CIPHER *EVP_des_cfb1(void); +const EVP_CIPHER *EVP_des_cfb8(void); +const EVP_CIPHER *EVP_des_ede_cfb64(void); +# define EVP_des_ede_cfb EVP_des_ede_cfb64 +#if 0 +const EVP_CIPHER *EVP_des_ede_cfb1(void); +const EVP_CIPHER *EVP_des_ede_cfb8(void); +#endif +const EVP_CIPHER *EVP_des_ede3_cfb64(void); +# define EVP_des_ede3_cfb EVP_des_ede3_cfb64 +const EVP_CIPHER *EVP_des_ede3_cfb1(void); +const EVP_CIPHER *EVP_des_ede3_cfb8(void); +const EVP_CIPHER *EVP_des_ofb(void); +const EVP_CIPHER *EVP_des_ede_ofb(void); +const EVP_CIPHER *EVP_des_ede3_ofb(void); +const EVP_CIPHER *EVP_des_cbc(void); +const EVP_CIPHER *EVP_des_ede_cbc(void); +const EVP_CIPHER *EVP_des_ede3_cbc(void); +const EVP_CIPHER *EVP_desx_cbc(void); +/* This should now be supported through the dev_crypto ENGINE. But also, why are + * rc4 and md5 declarations made here inside a "NO_DES" precompiler branch? */ +#if 0 +# ifdef OPENSSL_OPENBSD_DEV_CRYPTO +const EVP_CIPHER *EVP_dev_crypto_des_ede3_cbc(void); +const EVP_CIPHER *EVP_dev_crypto_rc4(void); +const EVP_MD *EVP_dev_crypto_md5(void); +# endif +#endif +#endif +#ifndef OPENSSL_NO_RC4 +const EVP_CIPHER *EVP_rc4(void); +const EVP_CIPHER *EVP_rc4_40(void); +#endif +#ifndef OPENSSL_NO_IDEA +const EVP_CIPHER *EVP_idea_ecb(void); +const EVP_CIPHER *EVP_idea_cfb64(void); +# define EVP_idea_cfb EVP_idea_cfb64 +const EVP_CIPHER *EVP_idea_ofb(void); +const EVP_CIPHER *EVP_idea_cbc(void); +#endif +#ifndef OPENSSL_NO_RC2 +const EVP_CIPHER *EVP_rc2_ecb(void); +const EVP_CIPHER *EVP_rc2_cbc(void); +const EVP_CIPHER *EVP_rc2_40_cbc(void); +const EVP_CIPHER *EVP_rc2_64_cbc(void); +const EVP_CIPHER *EVP_rc2_cfb64(void); +# define EVP_rc2_cfb EVP_rc2_cfb64 +const EVP_CIPHER *EVP_rc2_ofb(void); +#endif +#ifndef OPENSSL_NO_BF +const EVP_CIPHER *EVP_bf_ecb(void); +const EVP_CIPHER *EVP_bf_cbc(void); +const EVP_CIPHER *EVP_bf_cfb64(void); +# define EVP_bf_cfb EVP_bf_cfb64 +const EVP_CIPHER *EVP_bf_ofb(void); +#endif +#ifndef OPENSSL_NO_CAST +const EVP_CIPHER *EVP_cast5_ecb(void); +const EVP_CIPHER *EVP_cast5_cbc(void); +const EVP_CIPHER *EVP_cast5_cfb64(void); +# define EVP_cast5_cfb EVP_cast5_cfb64 +const EVP_CIPHER *EVP_cast5_ofb(void); +#endif +#ifndef OPENSSL_NO_RC5 +const EVP_CIPHER *EVP_rc5_32_12_16_cbc(void); +const EVP_CIPHER *EVP_rc5_32_12_16_ecb(void); +const EVP_CIPHER *EVP_rc5_32_12_16_cfb64(void); +# define EVP_rc5_32_12_16_cfb EVP_rc5_32_12_16_cfb64 +const EVP_CIPHER *EVP_rc5_32_12_16_ofb(void); +#endif +#ifndef OPENSSL_NO_AES +const EVP_CIPHER *EVP_aes_128_ecb(void); +const EVP_CIPHER *EVP_aes_128_cbc(void); +const EVP_CIPHER *EVP_aes_128_cfb1(void); +const EVP_CIPHER *EVP_aes_128_cfb8(void); +const EVP_CIPHER *EVP_aes_128_cfb128(void); +# define EVP_aes_128_cfb EVP_aes_128_cfb128 +const EVP_CIPHER *EVP_aes_128_ofb(void); +#if 0 +const EVP_CIPHER *EVP_aes_128_ctr(void); +#endif +const EVP_CIPHER *EVP_aes_192_ecb(void); +const EVP_CIPHER *EVP_aes_192_cbc(void); +const EVP_CIPHER *EVP_aes_192_cfb1(void); +const EVP_CIPHER *EVP_aes_192_cfb8(void); +const EVP_CIPHER *EVP_aes_192_cfb128(void); +# define EVP_aes_192_cfb EVP_aes_192_cfb128 +const EVP_CIPHER *EVP_aes_192_ofb(void); +#if 0 +const EVP_CIPHER *EVP_aes_192_ctr(void); +#endif +const EVP_CIPHER *EVP_aes_256_ecb(void); +const EVP_CIPHER *EVP_aes_256_cbc(void); +const EVP_CIPHER *EVP_aes_256_cfb1(void); +const EVP_CIPHER *EVP_aes_256_cfb8(void); +const EVP_CIPHER *EVP_aes_256_cfb128(void); +# define EVP_aes_256_cfb EVP_aes_256_cfb128 +const EVP_CIPHER *EVP_aes_256_ofb(void); +#if 0 +const EVP_CIPHER *EVP_aes_256_ctr(void); +#endif +#endif + +void OPENSSL_add_all_algorithms_noconf(void); +void OPENSSL_add_all_algorithms_conf(void); + +#ifdef OPENSSL_LOAD_CONF +#define OpenSSL_add_all_algorithms() \ + OPENSSL_add_all_algorithms_conf() +#else +#define OpenSSL_add_all_algorithms() \ + OPENSSL_add_all_algorithms_noconf() +#endif + +void OpenSSL_add_all_ciphers(void); +void OpenSSL_add_all_digests(void); +#define SSLeay_add_all_algorithms() OpenSSL_add_all_algorithms() +#define SSLeay_add_all_ciphers() OpenSSL_add_all_ciphers() +#define SSLeay_add_all_digests() OpenSSL_add_all_digests() + +int EVP_add_cipher(const EVP_CIPHER *cipher); +int EVP_add_digest(const EVP_MD *digest); + +const EVP_CIPHER *EVP_get_cipherbyname(const char *name); +const EVP_MD *EVP_get_digestbyname(const char *name); +void EVP_cleanup(void); + +int EVP_PKEY_decrypt(unsigned char *dec_key, + const unsigned char *enc_key,int enc_key_len, + EVP_PKEY *private_key); +int EVP_PKEY_encrypt(unsigned char *enc_key, + const unsigned char *key,int key_len, + EVP_PKEY *pub_key); +int EVP_PKEY_type(int type); +int EVP_PKEY_bits(EVP_PKEY *pkey); +int EVP_PKEY_size(EVP_PKEY *pkey); +int EVP_PKEY_assign(EVP_PKEY *pkey,int type,char *key); + +#ifndef OPENSSL_NO_RSA +struct rsa_st; +int EVP_PKEY_set1_RSA(EVP_PKEY *pkey,struct rsa_st *key); +struct rsa_st *EVP_PKEY_get1_RSA(EVP_PKEY *pkey); +#endif +#ifndef OPENSSL_NO_DSA +struct dsa_st; +int EVP_PKEY_set1_DSA(EVP_PKEY *pkey,struct dsa_st *key); +struct dsa_st *EVP_PKEY_get1_DSA(EVP_PKEY *pkey); +#endif +#ifndef OPENSSL_NO_DH +struct dh_st; +int EVP_PKEY_set1_DH(EVP_PKEY *pkey,struct dh_st *key); +struct dh_st *EVP_PKEY_get1_DH(EVP_PKEY *pkey); +#endif +#ifndef OPENSSL_NO_EC +struct ec_key_st; +int EVP_PKEY_set1_EC_KEY(EVP_PKEY *pkey,struct ec_key_st *key); +struct ec_key_st *EVP_PKEY_get1_EC_KEY(EVP_PKEY *pkey); +#endif + +EVP_PKEY * EVP_PKEY_new(void); +void EVP_PKEY_free(EVP_PKEY *pkey); + +EVP_PKEY * d2i_PublicKey(int type,EVP_PKEY **a, const unsigned char **pp, + long length); +int i2d_PublicKey(EVP_PKEY *a, unsigned char **pp); + +EVP_PKEY * d2i_PrivateKey(int type,EVP_PKEY **a, const unsigned char **pp, + long length); +EVP_PKEY * d2i_AutoPrivateKey(EVP_PKEY **a, const unsigned char **pp, + long length); +int i2d_PrivateKey(EVP_PKEY *a, unsigned char **pp); + +int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from); +int EVP_PKEY_missing_parameters(const EVP_PKEY *pkey); +int EVP_PKEY_save_parameters(EVP_PKEY *pkey,int mode); +int EVP_PKEY_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b); + +int EVP_PKEY_cmp(const EVP_PKEY *a, const EVP_PKEY *b); + +int EVP_CIPHER_type(const EVP_CIPHER *ctx); + +/* calls methods */ +int EVP_CIPHER_param_to_asn1(EVP_CIPHER_CTX *c, ASN1_TYPE *type); +int EVP_CIPHER_asn1_to_param(EVP_CIPHER_CTX *c, ASN1_TYPE *type); + +/* These are used by EVP_CIPHER methods */ +int EVP_CIPHER_set_asn1_iv(EVP_CIPHER_CTX *c,ASN1_TYPE *type); +int EVP_CIPHER_get_asn1_iv(EVP_CIPHER_CTX *c,ASN1_TYPE *type); + +/* PKCS5 password based encryption */ +int PKCS5_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass, int passlen, + ASN1_TYPE *param, const EVP_CIPHER *cipher, const EVP_MD *md, + int en_de); +int PKCS5_PBKDF2_HMAC_SHA1(const char *pass, int passlen, + const unsigned char *salt, int saltlen, int iter, + int keylen, unsigned char *out); +int PKCS5_v2_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass, int passlen, + ASN1_TYPE *param, const EVP_CIPHER *cipher, const EVP_MD *md, + int en_de); + +void PKCS5_PBE_add(void); + +int EVP_PBE_CipherInit (ASN1_OBJECT *pbe_obj, const char *pass, int passlen, + ASN1_TYPE *param, EVP_CIPHER_CTX *ctx, int en_de); +int EVP_PBE_alg_add(int nid, const EVP_CIPHER *cipher, const EVP_MD *md, + EVP_PBE_KEYGEN *keygen); +void EVP_PBE_cleanup(void); + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_EVP_strings(void); + +/* Error codes for the EVP functions. */ + +/* Function codes. */ +#define EVP_F_AES_INIT_KEY 133 +#define EVP_F_D2I_PKEY 100 +#define EVP_F_DSAPKEY2PKCS8 134 +#define EVP_F_DSA_PKEY2PKCS8 135 +#define EVP_F_ECDSA_PKEY2PKCS8 129 +#define EVP_F_ECKEY_PKEY2PKCS8 132 +#define EVP_F_EVP_CIPHERINIT_EX 123 +#define EVP_F_EVP_CIPHER_CTX_CTRL 124 +#define EVP_F_EVP_CIPHER_CTX_SET_KEY_LENGTH 122 +#define EVP_F_EVP_DECRYPTFINAL_EX 101 +#define EVP_F_EVP_DIGESTINIT_EX 128 +#define EVP_F_EVP_ENCRYPTFINAL_EX 127 +#define EVP_F_EVP_MD_CTX_COPY_EX 110 +#define EVP_F_EVP_OPENINIT 102 +#define EVP_F_EVP_PBE_ALG_ADD 115 +#define EVP_F_EVP_PBE_CIPHERINIT 116 +#define EVP_F_EVP_PKCS82PKEY 111 +#define EVP_F_EVP_PKEY2PKCS8_BROKEN 113 +#define EVP_F_EVP_PKEY_COPY_PARAMETERS 103 +#define EVP_F_EVP_PKEY_DECRYPT 104 +#define EVP_F_EVP_PKEY_ENCRYPT 105 +#define EVP_F_EVP_PKEY_GET1_DH 119 +#define EVP_F_EVP_PKEY_GET1_DSA 120 +#define EVP_F_EVP_PKEY_GET1_ECDSA 130 +#define EVP_F_EVP_PKEY_GET1_EC_KEY 131 +#define EVP_F_EVP_PKEY_GET1_RSA 121 +#define EVP_F_EVP_PKEY_NEW 106 +#define EVP_F_EVP_RIJNDAEL 126 +#define EVP_F_EVP_SIGNFINAL 107 +#define EVP_F_EVP_VERIFYFINAL 108 +#define EVP_F_PKCS5_PBE_KEYIVGEN 117 +#define EVP_F_PKCS5_V2_PBE_KEYIVGEN 118 +#define EVP_F_PKCS8_SET_BROKEN 112 +#define EVP_F_RC2_MAGIC_TO_METH 109 +#define EVP_F_RC5_CTRL 125 + +/* Reason codes. */ +#define EVP_R_AES_KEY_SETUP_FAILED 143 +#define EVP_R_ASN1_LIB 140 +#define EVP_R_BAD_BLOCK_LENGTH 136 +#define EVP_R_BAD_DECRYPT 100 +#define EVP_R_BAD_KEY_LENGTH 137 +#define EVP_R_BN_DECODE_ERROR 112 +#define EVP_R_BN_PUBKEY_ERROR 113 +#define EVP_R_CIPHER_PARAMETER_ERROR 122 +#define EVP_R_CTRL_NOT_IMPLEMENTED 132 +#define EVP_R_CTRL_OPERATION_NOT_IMPLEMENTED 133 +#define EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH 138 +#define EVP_R_DECODE_ERROR 114 +#define EVP_R_DIFFERENT_KEY_TYPES 101 +#define EVP_R_ENCODE_ERROR 115 +#define EVP_R_EVP_PBE_CIPHERINIT_ERROR 119 +#define EVP_R_EXPECTING_AN_RSA_KEY 127 +#define EVP_R_EXPECTING_A_DH_KEY 128 +#define EVP_R_EXPECTING_A_DSA_KEY 129 +#define EVP_R_EXPECTING_A_ECDSA_KEY 141 +#define EVP_R_EXPECTING_A_EC_KEY 142 +#define EVP_R_INITIALIZATION_ERROR 134 +#define EVP_R_INPUT_NOT_INITIALIZED 111 +#define EVP_R_INVALID_KEY_LENGTH 130 +#define EVP_R_IV_TOO_LARGE 102 +#define EVP_R_KEYGEN_FAILURE 120 +#define EVP_R_MISSING_PARAMETERS 103 +#define EVP_R_NO_CIPHER_SET 131 +#define EVP_R_NO_DIGEST_SET 139 +#define EVP_R_NO_DSA_PARAMETERS 116 +#define EVP_R_NO_SIGN_FUNCTION_CONFIGURED 104 +#define EVP_R_NO_VERIFY_FUNCTION_CONFIGURED 105 +#define EVP_R_PKCS8_UNKNOWN_BROKEN_TYPE 117 +#define EVP_R_PUBLIC_KEY_NOT_RSA 106 +#define EVP_R_UNKNOWN_PBE_ALGORITHM 121 +#define EVP_R_UNSUPORTED_NUMBER_OF_ROUNDS 135 +#define EVP_R_UNSUPPORTED_CIPHER 107 +#define EVP_R_UNSUPPORTED_KEYLENGTH 123 +#define EVP_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION 124 +#define EVP_R_UNSUPPORTED_KEY_SIZE 108 +#define EVP_R_UNSUPPORTED_PRF 125 +#define EVP_R_UNSUPPORTED_PRIVATE_KEY_ALGORITHM 118 +#define EVP_R_UNSUPPORTED_SALT_TYPE 126 +#define EVP_R_WRONG_FINAL_BLOCK_LENGTH 109 +#define EVP_R_WRONG_PUBLIC_KEY_TYPE 110 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/hmac.h b/production/3rdparty/openssl/include/openssl/hmac.h new file mode 100644 index 00000000..719fc408 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/hmac.h @@ -0,0 +1,108 @@ +/* crypto/hmac/hmac.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +#ifndef HEADER_HMAC_H +#define HEADER_HMAC_H + +#include + +#ifdef OPENSSL_NO_HMAC +#error HMAC is disabled. +#endif + +#include + +#define HMAC_MAX_MD_CBLOCK 128 /* largest known is SHA512 */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct hmac_ctx_st + { + const EVP_MD *md; + EVP_MD_CTX md_ctx; + EVP_MD_CTX i_ctx; + EVP_MD_CTX o_ctx; + unsigned int key_length; + unsigned char key[HMAC_MAX_MD_CBLOCK]; + } HMAC_CTX; + +#define HMAC_size(e) (EVP_MD_size((e)->md)) + + +void HMAC_CTX_init(HMAC_CTX *ctx); +void HMAC_CTX_cleanup(HMAC_CTX *ctx); + +#define HMAC_cleanup(ctx) HMAC_CTX_cleanup(ctx) /* deprecated */ + +void HMAC_Init(HMAC_CTX *ctx, const void *key, int len, + const EVP_MD *md); /* deprecated */ +void HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int len, + const EVP_MD *md, ENGINE *impl); +void HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, size_t len); +void HMAC_Final(HMAC_CTX *ctx, unsigned char *md, unsigned int *len); +unsigned char *HMAC(const EVP_MD *evp_md, const void *key, int key_len, + const unsigned char *d, size_t n, unsigned char *md, + unsigned int *md_len); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/idea.h b/production/3rdparty/openssl/include/openssl/idea.h new file mode 100644 index 00000000..bf97a37e --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/idea.h @@ -0,0 +1,100 @@ +/* crypto/idea/idea.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_IDEA_H +#define HEADER_IDEA_H + +#include /* IDEA_INT, OPENSSL_NO_IDEA */ + +#ifdef OPENSSL_NO_IDEA +#error IDEA is disabled. +#endif + +#define IDEA_ENCRYPT 1 +#define IDEA_DECRYPT 0 + +#define IDEA_BLOCK 8 +#define IDEA_KEY_LENGTH 16 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct idea_key_st + { + IDEA_INT data[9][6]; + } IDEA_KEY_SCHEDULE; + +const char *idea_options(void); +void idea_ecb_encrypt(const unsigned char *in, unsigned char *out, + IDEA_KEY_SCHEDULE *ks); +void idea_set_encrypt_key(const unsigned char *key, IDEA_KEY_SCHEDULE *ks); +void idea_set_decrypt_key(const IDEA_KEY_SCHEDULE *ek, IDEA_KEY_SCHEDULE *dk); +void idea_cbc_encrypt(const unsigned char *in, unsigned char *out, + long length, IDEA_KEY_SCHEDULE *ks, unsigned char *iv,int enc); +void idea_cfb64_encrypt(const unsigned char *in, unsigned char *out, + long length, IDEA_KEY_SCHEDULE *ks, unsigned char *iv, + int *num,int enc); +void idea_ofb64_encrypt(const unsigned char *in, unsigned char *out, + long length, IDEA_KEY_SCHEDULE *ks, unsigned char *iv, int *num); +void idea_encrypt(unsigned long *in, IDEA_KEY_SCHEDULE *ks); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/krb5_asn.h b/production/3rdparty/openssl/include/openssl/krb5_asn.h new file mode 100644 index 00000000..41725d0d --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/krb5_asn.h @@ -0,0 +1,256 @@ +/* krb5_asn.h */ +/* Written by Vern Staats for the OpenSSL project, +** using ocsp/{*.h,*asn*.c} as a starting point +*/ + +/* ==================================================================== + * Copyright (c) 1998-2000 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#ifndef HEADER_KRB5_ASN_H +#define HEADER_KRB5_ASN_H + +/* +#include +*/ +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* ASN.1 from Kerberos RFC 1510 +*/ + +/* EncryptedData ::= SEQUENCE { +** etype[0] INTEGER, -- EncryptionType +** kvno[1] INTEGER OPTIONAL, +** cipher[2] OCTET STRING -- ciphertext +** } +*/ +typedef struct krb5_encdata_st + { + ASN1_INTEGER *etype; + ASN1_INTEGER *kvno; + ASN1_OCTET_STRING *cipher; + } KRB5_ENCDATA; + +DECLARE_STACK_OF(KRB5_ENCDATA) + +/* PrincipalName ::= SEQUENCE { +** name-type[0] INTEGER, +** name-string[1] SEQUENCE OF GeneralString +** } +*/ +typedef struct krb5_princname_st + { + ASN1_INTEGER *nametype; + STACK_OF(ASN1_GENERALSTRING) *namestring; + } KRB5_PRINCNAME; + +DECLARE_STACK_OF(KRB5_PRINCNAME) + + +/* Ticket ::= [APPLICATION 1] SEQUENCE { +** tkt-vno[0] INTEGER, +** realm[1] Realm, +** sname[2] PrincipalName, +** enc-part[3] EncryptedData +** } +*/ +typedef struct krb5_tktbody_st + { + ASN1_INTEGER *tktvno; + ASN1_GENERALSTRING *realm; + KRB5_PRINCNAME *sname; + KRB5_ENCDATA *encdata; + } KRB5_TKTBODY; + +typedef STACK_OF(KRB5_TKTBODY) KRB5_TICKET; +DECLARE_STACK_OF(KRB5_TKTBODY) + + +/* AP-REQ ::= [APPLICATION 14] SEQUENCE { +** pvno[0] INTEGER, +** msg-type[1] INTEGER, +** ap-options[2] APOptions, +** ticket[3] Ticket, +** authenticator[4] EncryptedData +** } +** +** APOptions ::= BIT STRING { +** reserved(0), use-session-key(1), mutual-required(2) } +*/ +typedef struct krb5_ap_req_st + { + ASN1_INTEGER *pvno; + ASN1_INTEGER *msgtype; + ASN1_BIT_STRING *apoptions; + KRB5_TICKET *ticket; + KRB5_ENCDATA *authenticator; + } KRB5_APREQBODY; + +typedef STACK_OF(KRB5_APREQBODY) KRB5_APREQ; +DECLARE_STACK_OF(KRB5_APREQBODY) + + +/* Authenticator Stuff */ + + +/* Checksum ::= SEQUENCE { +** cksumtype[0] INTEGER, +** checksum[1] OCTET STRING +** } +*/ +typedef struct krb5_checksum_st + { + ASN1_INTEGER *ctype; + ASN1_OCTET_STRING *checksum; + } KRB5_CHECKSUM; + +DECLARE_STACK_OF(KRB5_CHECKSUM) + + +/* EncryptionKey ::= SEQUENCE { +** keytype[0] INTEGER, +** keyvalue[1] OCTET STRING +** } +*/ +typedef struct krb5_encryptionkey_st + { + ASN1_INTEGER *ktype; + ASN1_OCTET_STRING *keyvalue; + } KRB5_ENCKEY; + +DECLARE_STACK_OF(KRB5_ENCKEY) + + +/* AuthorizationData ::= SEQUENCE OF SEQUENCE { +** ad-type[0] INTEGER, +** ad-data[1] OCTET STRING +** } +*/ +typedef struct krb5_authorization_st + { + ASN1_INTEGER *adtype; + ASN1_OCTET_STRING *addata; + } KRB5_AUTHDATA; + +DECLARE_STACK_OF(KRB5_AUTHDATA) + + +/* -- Unencrypted authenticator +** Authenticator ::= [APPLICATION 2] SEQUENCE { +** authenticator-vno[0] INTEGER, +** crealm[1] Realm, +** cname[2] PrincipalName, +** cksum[3] Checksum OPTIONAL, +** cusec[4] INTEGER, +** ctime[5] KerberosTime, +** subkey[6] EncryptionKey OPTIONAL, +** seq-number[7] INTEGER OPTIONAL, +** authorization-data[8] AuthorizationData OPTIONAL +** } +*/ +typedef struct krb5_authenticator_st + { + ASN1_INTEGER *avno; + ASN1_GENERALSTRING *crealm; + KRB5_PRINCNAME *cname; + KRB5_CHECKSUM *cksum; + ASN1_INTEGER *cusec; + ASN1_GENERALIZEDTIME *ctime; + KRB5_ENCKEY *subkey; + ASN1_INTEGER *seqnum; + KRB5_AUTHDATA *authorization; + } KRB5_AUTHENTBODY; + +typedef STACK_OF(KRB5_AUTHENTBODY) KRB5_AUTHENT; +DECLARE_STACK_OF(KRB5_AUTHENTBODY) + + +/* DECLARE_ASN1_FUNCTIONS(type) = DECLARE_ASN1_FUNCTIONS_name(type, type) = +** type *name##_new(void); +** void name##_free(type *a); +** DECLARE_ASN1_ENCODE_FUNCTIONS(type, name, name) = +** DECLARE_ASN1_ENCODE_FUNCTIONS(type, itname, name) = +** type *d2i_##name(type **a, const unsigned char **in, long len); +** int i2d_##name(type *a, unsigned char **out); +** DECLARE_ASN1_ITEM(itname) = OPENSSL_EXTERN const ASN1_ITEM itname##_it +*/ + +DECLARE_ASN1_FUNCTIONS(KRB5_ENCDATA) +DECLARE_ASN1_FUNCTIONS(KRB5_PRINCNAME) +DECLARE_ASN1_FUNCTIONS(KRB5_TKTBODY) +DECLARE_ASN1_FUNCTIONS(KRB5_APREQBODY) +DECLARE_ASN1_FUNCTIONS(KRB5_TICKET) +DECLARE_ASN1_FUNCTIONS(KRB5_APREQ) + +DECLARE_ASN1_FUNCTIONS(KRB5_CHECKSUM) +DECLARE_ASN1_FUNCTIONS(KRB5_ENCKEY) +DECLARE_ASN1_FUNCTIONS(KRB5_AUTHDATA) +DECLARE_ASN1_FUNCTIONS(KRB5_AUTHENTBODY) +DECLARE_ASN1_FUNCTIONS(KRB5_AUTHENT) + + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/production/3rdparty/openssl/include/openssl/kssl.h b/production/3rdparty/openssl/include/openssl/kssl.h new file mode 100644 index 00000000..a3d20e1c --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/kssl.h @@ -0,0 +1,179 @@ +/* ssl/kssl.h -*- mode: C; c-file-style: "eay" -*- */ +/* Written by Vern Staats for the OpenSSL project 2000. + * project 2000. + */ +/* ==================================================================== + * Copyright (c) 2000 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +/* +** 19990701 VRS Started. +*/ + +#ifndef KSSL_H +#define KSSL_H + +#include + +#ifndef OPENSSL_NO_KRB5 + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Depending on which KRB5 implementation used, some types from +** the other may be missing. Resolve that here and now +*/ +#ifdef KRB5_HEIMDAL +typedef unsigned char krb5_octet; +#define FAR +#else + +#ifndef FAR +#define FAR +#endif + +#endif + +/* Uncomment this to debug kssl problems or +** to trace usage of the Kerberos session key +** +** #define KSSL_DEBUG +*/ + +#ifndef KRB5SVC +#define KRB5SVC "host" +#endif + +#ifndef KRB5KEYTAB +#define KRB5KEYTAB "/etc/krb5.keytab" +#endif + +#ifndef KRB5SENDAUTH +#define KRB5SENDAUTH 1 +#endif + +#ifndef KRB5CHECKAUTH +#define KRB5CHECKAUTH 1 +#endif + +#ifndef KSSL_CLOCKSKEW +#define KSSL_CLOCKSKEW 300; +#endif + +#define KSSL_ERR_MAX 255 +typedef struct kssl_err_st { + int reason; + char text[KSSL_ERR_MAX+1]; + } KSSL_ERR; + + +/* Context for passing +** (1) Kerberos session key to SSL, and +** (2) Config data between application and SSL lib +*/ +typedef struct kssl_ctx_st + { + /* used by: disposition: */ + char *service_name; /* C,S default ok (kssl) */ + char *service_host; /* C input, REQUIRED */ + char *client_princ; /* S output from krb5 ticket */ + char *keytab_file; /* S NULL (/etc/krb5.keytab) */ + char *cred_cache; /* C NULL (default) */ + krb5_enctype enctype; + int length; + krb5_octet FAR *key; + } KSSL_CTX; + +#define KSSL_CLIENT 1 +#define KSSL_SERVER 2 +#define KSSL_SERVICE 3 +#define KSSL_KEYTAB 4 + +#define KSSL_CTX_OK 0 +#define KSSL_CTX_ERR 1 +#define KSSL_NOMEM 2 + +/* Public (for use by applications that use OpenSSL with Kerberos 5 support */ +krb5_error_code kssl_ctx_setstring(KSSL_CTX *kssl_ctx, int which, char *text); +KSSL_CTX *kssl_ctx_new(void); +KSSL_CTX *kssl_ctx_free(KSSL_CTX *kssl_ctx); +void kssl_ctx_show(KSSL_CTX *kssl_ctx); +krb5_error_code kssl_ctx_setprinc(KSSL_CTX *kssl_ctx, int which, + krb5_data *realm, krb5_data *entity, int nentities); +krb5_error_code kssl_cget_tkt(KSSL_CTX *kssl_ctx, krb5_data **enc_tktp, + krb5_data *authenp, KSSL_ERR *kssl_err); +krb5_error_code kssl_sget_tkt(KSSL_CTX *kssl_ctx, krb5_data *indata, + krb5_ticket_times *ttimes, KSSL_ERR *kssl_err); +krb5_error_code kssl_ctx_setkey(KSSL_CTX *kssl_ctx, krb5_keyblock *session); +void kssl_err_set(KSSL_ERR *kssl_err, int reason, char *text); +void kssl_krb5_free_data_contents(krb5_context context, krb5_data *data); +krb5_error_code kssl_build_principal_2(krb5_context context, + krb5_principal *princ, int rlen, const char *realm, + int slen, const char *svc, int hlen, const char *host); +krb5_error_code kssl_validate_times(krb5_timestamp atime, + krb5_ticket_times *ttimes); +krb5_error_code kssl_check_authent(KSSL_CTX *kssl_ctx, krb5_data *authentp, + krb5_timestamp *atimep, KSSL_ERR *kssl_err); +unsigned char *kssl_skip_confound(krb5_enctype enctype, unsigned char *authn); + +#ifdef __cplusplus +} +#endif +#endif /* OPENSSL_NO_KRB5 */ +#endif /* KSSL_H */ diff --git a/production/3rdparty/openssl/include/openssl/lhash.h b/production/3rdparty/openssl/include/openssl/lhash.h new file mode 100644 index 00000000..d392d0cd --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/lhash.h @@ -0,0 +1,200 @@ +/* crypto/lhash/lhash.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +/* Header for dynamic hash table routines + * Author - Eric Young + */ + +#ifndef HEADER_LHASH_H +#define HEADER_LHASH_H + +#include +#ifndef OPENSSL_NO_FP_API +#include +#endif + +#ifndef OPENSSL_NO_BIO +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct lhash_node_st + { + void *data; + struct lhash_node_st *next; +#ifndef OPENSSL_NO_HASH_COMP + unsigned long hash; +#endif + } LHASH_NODE; + +typedef int (*LHASH_COMP_FN_TYPE)(const void *, const void *); +typedef unsigned long (*LHASH_HASH_FN_TYPE)(const void *); +typedef void (*LHASH_DOALL_FN_TYPE)(void *); +typedef void (*LHASH_DOALL_ARG_FN_TYPE)(void *, void *); + +/* Macros for declaring and implementing type-safe wrappers for LHASH callbacks. + * This way, callbacks can be provided to LHASH structures without function + * pointer casting and the macro-defined callbacks provide per-variable casting + * before deferring to the underlying type-specific callbacks. NB: It is + * possible to place a "static" in front of both the DECLARE and IMPLEMENT + * macros if the functions are strictly internal. */ + +/* First: "hash" functions */ +#define DECLARE_LHASH_HASH_FN(f_name,o_type) \ + unsigned long f_name##_LHASH_HASH(const void *); +#define IMPLEMENT_LHASH_HASH_FN(f_name,o_type) \ + unsigned long f_name##_LHASH_HASH(const void *arg) { \ + o_type a = (o_type)arg; \ + return f_name(a); } +#define LHASH_HASH_FN(f_name) f_name##_LHASH_HASH + +/* Second: "compare" functions */ +#define DECLARE_LHASH_COMP_FN(f_name,o_type) \ + int f_name##_LHASH_COMP(const void *, const void *); +#define IMPLEMENT_LHASH_COMP_FN(f_name,o_type) \ + int f_name##_LHASH_COMP(const void *arg1, const void *arg2) { \ + o_type a = (o_type)arg1; \ + o_type b = (o_type)arg2; \ + return f_name(a,b); } +#define LHASH_COMP_FN(f_name) f_name##_LHASH_COMP + +/* Third: "doall" functions */ +#define DECLARE_LHASH_DOALL_FN(f_name,o_type) \ + void f_name##_LHASH_DOALL(void *); +#define IMPLEMENT_LHASH_DOALL_FN(f_name,o_type) \ + void f_name##_LHASH_DOALL(void *arg) { \ + o_type a = (o_type)arg; \ + f_name(a); } +#define LHASH_DOALL_FN(f_name) f_name##_LHASH_DOALL + +/* Fourth: "doall_arg" functions */ +#define DECLARE_LHASH_DOALL_ARG_FN(f_name,o_type,a_type) \ + void f_name##_LHASH_DOALL_ARG(void *, void *); +#define IMPLEMENT_LHASH_DOALL_ARG_FN(f_name,o_type,a_type) \ + void f_name##_LHASH_DOALL_ARG(void *arg1, void *arg2) { \ + o_type a = (o_type)arg1; \ + a_type b = (a_type)arg2; \ + f_name(a,b); } +#define LHASH_DOALL_ARG_FN(f_name) f_name##_LHASH_DOALL_ARG + +typedef struct lhash_st + { + LHASH_NODE **b; + LHASH_COMP_FN_TYPE comp; + LHASH_HASH_FN_TYPE hash; + unsigned int num_nodes; + unsigned int num_alloc_nodes; + unsigned int p; + unsigned int pmax; + unsigned long up_load; /* load times 256 */ + unsigned long down_load; /* load times 256 */ + unsigned long num_items; + + unsigned long num_expands; + unsigned long num_expand_reallocs; + unsigned long num_contracts; + unsigned long num_contract_reallocs; + unsigned long num_hash_calls; + unsigned long num_comp_calls; + unsigned long num_insert; + unsigned long num_replace; + unsigned long num_delete; + unsigned long num_no_delete; + unsigned long num_retrieve; + unsigned long num_retrieve_miss; + unsigned long num_hash_comps; + + int error; + } LHASH; + +#define LH_LOAD_MULT 256 + +/* Indicates a malloc() error in the last call, this is only bad + * in lh_insert(). */ +#define lh_error(lh) ((lh)->error) + +LHASH *lh_new(LHASH_HASH_FN_TYPE h, LHASH_COMP_FN_TYPE c); +void lh_free(LHASH *lh); +void *lh_insert(LHASH *lh, void *data); +void *lh_delete(LHASH *lh, const void *data); +void *lh_retrieve(LHASH *lh, const void *data); +void lh_doall(LHASH *lh, LHASH_DOALL_FN_TYPE func); +void lh_doall_arg(LHASH *lh, LHASH_DOALL_ARG_FN_TYPE func, void *arg); +unsigned long lh_strhash(const char *c); +unsigned long lh_num_items(const LHASH *lh); + +#ifndef OPENSSL_NO_FP_API +void lh_stats(const LHASH *lh, FILE *out); +void lh_node_stats(const LHASH *lh, FILE *out); +void lh_node_usage_stats(const LHASH *lh, FILE *out); +#endif + +#ifndef OPENSSL_NO_BIO +void lh_stats_bio(const LHASH *lh, BIO *out); +void lh_node_stats_bio(const LHASH *lh, BIO *out); +void lh_node_usage_stats_bio(const LHASH *lh, BIO *out); +#endif +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/production/3rdparty/openssl/include/openssl/md2.h b/production/3rdparty/openssl/include/openssl/md2.h new file mode 100644 index 00000000..5b71855c --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/md2.h @@ -0,0 +1,91 @@ +/* crypto/md/md2.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_MD2_H +#define HEADER_MD2_H + +#include /* OPENSSL_NO_MD2, MD2_INT */ +#ifdef OPENSSL_NO_MD2 +#error MD2 is disabled. +#endif + +#define MD2_DIGEST_LENGTH 16 +#define MD2_BLOCK 16 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MD2state_st + { + unsigned int num; + unsigned char data[MD2_BLOCK]; + MD2_INT cksm[MD2_BLOCK]; + MD2_INT state[MD2_BLOCK]; + } MD2_CTX; + +const char *MD2_options(void); +int MD2_Init(MD2_CTX *c); +int MD2_Update(MD2_CTX *c, const unsigned char *data, size_t len); +int MD2_Final(unsigned char *md, MD2_CTX *c); +unsigned char *MD2(const unsigned char *d, size_t n,unsigned char *md); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/md4.h b/production/3rdparty/openssl/include/openssl/md4.h new file mode 100644 index 00000000..b080cbdc --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/md4.h @@ -0,0 +1,116 @@ +/* crypto/md4/md4.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_MD4_H +#define HEADER_MD4_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef OPENSSL_NO_MD4 +#error MD4 is disabled. +#endif + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * ! MD4_LONG has to be at least 32 bits wide. If it's wider, then ! + * ! MD4_LONG_LOG2 has to be defined along. ! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + */ + +#if defined(OPENSSL_SYS_WIN16) || defined(__LP32__) +#define MD4_LONG unsigned long +#elif defined(OPENSSL_SYS_CRAY) || defined(__ILP64__) +#define MD4_LONG unsigned long +#define MD4_LONG_LOG2 3 +/* + * _CRAY note. I could declare short, but I have no idea what impact + * does it have on performance on none-T3E machines. I could declare + * int, but at least on C90 sizeof(int) can be chosen at compile time. + * So I've chosen long... + * + */ +#else +#define MD4_LONG unsigned int +#endif + +#define MD4_CBLOCK 64 +#define MD4_LBLOCK (MD4_CBLOCK/4) +#define MD4_DIGEST_LENGTH 16 + +typedef struct MD4state_st + { + MD4_LONG A,B,C,D; + MD4_LONG Nl,Nh; + MD4_LONG data[MD4_LBLOCK]; + unsigned int num; + } MD4_CTX; + +int MD4_Init(MD4_CTX *c); +int MD4_Update(MD4_CTX *c, const void *data, size_t len); +int MD4_Final(unsigned char *md, MD4_CTX *c); +unsigned char *MD4(const unsigned char *d, size_t n, unsigned char *md); +void MD4_Transform(MD4_CTX *c, const unsigned char *b); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/md5.h b/production/3rdparty/openssl/include/openssl/md5.h new file mode 100644 index 00000000..6d283fe9 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/md5.h @@ -0,0 +1,116 @@ +/* crypto/md5/md5.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_MD5_H +#define HEADER_MD5_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef OPENSSL_NO_MD5 +#error MD5 is disabled. +#endif + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * ! MD5_LONG has to be at least 32 bits wide. If it's wider, then ! + * ! MD5_LONG_LOG2 has to be defined along. ! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + */ + +#if defined(OPENSSL_SYS_WIN16) || defined(__LP32__) +#define MD5_LONG unsigned long +#elif defined(OPENSSL_SYS_CRAY) || defined(__ILP64__) +#define MD5_LONG unsigned long +#define MD5_LONG_LOG2 3 +/* + * _CRAY note. I could declare short, but I have no idea what impact + * does it have on performance on none-T3E machines. I could declare + * int, but at least on C90 sizeof(int) can be chosen at compile time. + * So I've chosen long... + * + */ +#else +#define MD5_LONG unsigned int +#endif + +#define MD5_CBLOCK 64 +#define MD5_LBLOCK (MD5_CBLOCK/4) +#define MD5_DIGEST_LENGTH 16 + +typedef struct MD5state_st + { + MD5_LONG A,B,C,D; + MD5_LONG Nl,Nh; + MD5_LONG data[MD5_LBLOCK]; + unsigned int num; + } MD5_CTX; + +int MD5_Init(MD5_CTX *c); +int MD5_Update(MD5_CTX *c, const void *data, size_t len); +int MD5_Final(unsigned char *md, MD5_CTX *c); +unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md); +void MD5_Transform(MD5_CTX *c, const unsigned char *b); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/obj_mac.h b/production/3rdparty/openssl/include/openssl/obj_mac.h new file mode 100644 index 00000000..e4d63e5e --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/obj_mac.h @@ -0,0 +1,3305 @@ +/* crypto/objects/obj_mac.h */ + +/* THIS FILE IS GENERATED FROM objects.txt by objects.pl via the + * following command: + * perl objects.pl objects.txt obj_mac.num obj_mac.h + */ + +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#define SN_undef "UNDEF" +#define LN_undef "undefined" +#define NID_undef 0 +#define OBJ_undef 0L + +#define SN_itu_t "ITU-T" +#define LN_itu_t "itu-t" +#define NID_itu_t 645 +#define OBJ_itu_t 0L + +#define NID_ccitt 404 +#define OBJ_ccitt OBJ_itu_t + +#define SN_iso "ISO" +#define LN_iso "iso" +#define NID_iso 181 +#define OBJ_iso 1L + +#define SN_joint_iso_itu_t "JOINT-ISO-ITU-T" +#define LN_joint_iso_itu_t "joint-iso-itu-t" +#define NID_joint_iso_itu_t 646 +#define OBJ_joint_iso_itu_t 2L + +#define NID_joint_iso_ccitt 393 +#define OBJ_joint_iso_ccitt OBJ_joint_iso_itu_t + +#define SN_member_body "member-body" +#define LN_member_body "ISO Member Body" +#define NID_member_body 182 +#define OBJ_member_body OBJ_iso,2L + +#define SN_identified_organization "identified-organization" +#define NID_identified_organization 676 +#define OBJ_identified_organization OBJ_iso,3L + +#define SN_certicom_arc "certicom-arc" +#define NID_certicom_arc 677 +#define OBJ_certicom_arc OBJ_identified_organization,132L + +#define SN_international_organizations "international-organizations" +#define LN_international_organizations "International Organizations" +#define NID_international_organizations 647 +#define OBJ_international_organizations OBJ_joint_iso_itu_t,23L + +#define SN_wap "wap" +#define NID_wap 678 +#define OBJ_wap OBJ_international_organizations,43L + +#define SN_wap_wsg "wap-wsg" +#define NID_wap_wsg 679 +#define OBJ_wap_wsg OBJ_wap,13L + +#define SN_selected_attribute_types "selected-attribute-types" +#define LN_selected_attribute_types "Selected Attribute Types" +#define NID_selected_attribute_types 394 +#define OBJ_selected_attribute_types OBJ_joint_iso_itu_t,5L,1L,5L + +#define SN_clearance "clearance" +#define NID_clearance 395 +#define OBJ_clearance OBJ_selected_attribute_types,55L + +#define SN_ISO_US "ISO-US" +#define LN_ISO_US "ISO US Member Body" +#define NID_ISO_US 183 +#define OBJ_ISO_US OBJ_member_body,840L + +#define SN_X9_57 "X9-57" +#define LN_X9_57 "X9.57" +#define NID_X9_57 184 +#define OBJ_X9_57 OBJ_ISO_US,10040L + +#define SN_X9cm "X9cm" +#define LN_X9cm "X9.57 CM ?" +#define NID_X9cm 185 +#define OBJ_X9cm OBJ_X9_57,4L + +#define SN_dsa "DSA" +#define LN_dsa "dsaEncryption" +#define NID_dsa 116 +#define OBJ_dsa OBJ_X9cm,1L + +#define SN_dsaWithSHA1 "DSA-SHA1" +#define LN_dsaWithSHA1 "dsaWithSHA1" +#define NID_dsaWithSHA1 113 +#define OBJ_dsaWithSHA1 OBJ_X9cm,3L + +#define SN_ansi_X9_62 "ansi-X9-62" +#define LN_ansi_X9_62 "ANSI X9.62" +#define NID_ansi_X9_62 405 +#define OBJ_ansi_X9_62 OBJ_ISO_US,10045L + +#define OBJ_X9_62_id_fieldType OBJ_ansi_X9_62,1L + +#define SN_X9_62_prime_field "prime-field" +#define NID_X9_62_prime_field 406 +#define OBJ_X9_62_prime_field OBJ_X9_62_id_fieldType,1L + +#define SN_X9_62_characteristic_two_field "characteristic-two-field" +#define NID_X9_62_characteristic_two_field 407 +#define OBJ_X9_62_characteristic_two_field OBJ_X9_62_id_fieldType,2L + +#define SN_X9_62_id_characteristic_two_basis "id-characteristic-two-basis" +#define NID_X9_62_id_characteristic_two_basis 680 +#define OBJ_X9_62_id_characteristic_two_basis OBJ_X9_62_characteristic_two_field,3L + +#define SN_X9_62_onBasis "onBasis" +#define NID_X9_62_onBasis 681 +#define OBJ_X9_62_onBasis OBJ_X9_62_id_characteristic_two_basis,1L + +#define SN_X9_62_tpBasis "tpBasis" +#define NID_X9_62_tpBasis 682 +#define OBJ_X9_62_tpBasis OBJ_X9_62_id_characteristic_two_basis,2L + +#define SN_X9_62_ppBasis "ppBasis" +#define NID_X9_62_ppBasis 683 +#define OBJ_X9_62_ppBasis OBJ_X9_62_id_characteristic_two_basis,3L + +#define OBJ_X9_62_id_publicKeyType OBJ_ansi_X9_62,2L + +#define SN_X9_62_id_ecPublicKey "id-ecPublicKey" +#define NID_X9_62_id_ecPublicKey 408 +#define OBJ_X9_62_id_ecPublicKey OBJ_X9_62_id_publicKeyType,1L + +#define OBJ_X9_62_ellipticCurve OBJ_ansi_X9_62,3L + +#define OBJ_X9_62_c_TwoCurve OBJ_X9_62_ellipticCurve,0L + +#define SN_X9_62_c2pnb163v1 "c2pnb163v1" +#define NID_X9_62_c2pnb163v1 684 +#define OBJ_X9_62_c2pnb163v1 OBJ_X9_62_c_TwoCurve,1L + +#define SN_X9_62_c2pnb163v2 "c2pnb163v2" +#define NID_X9_62_c2pnb163v2 685 +#define OBJ_X9_62_c2pnb163v2 OBJ_X9_62_c_TwoCurve,2L + +#define SN_X9_62_c2pnb163v3 "c2pnb163v3" +#define NID_X9_62_c2pnb163v3 686 +#define OBJ_X9_62_c2pnb163v3 OBJ_X9_62_c_TwoCurve,3L + +#define SN_X9_62_c2pnb176v1 "c2pnb176v1" +#define NID_X9_62_c2pnb176v1 687 +#define OBJ_X9_62_c2pnb176v1 OBJ_X9_62_c_TwoCurve,4L + +#define SN_X9_62_c2tnb191v1 "c2tnb191v1" +#define NID_X9_62_c2tnb191v1 688 +#define OBJ_X9_62_c2tnb191v1 OBJ_X9_62_c_TwoCurve,5L + +#define SN_X9_62_c2tnb191v2 "c2tnb191v2" +#define NID_X9_62_c2tnb191v2 689 +#define OBJ_X9_62_c2tnb191v2 OBJ_X9_62_c_TwoCurve,6L + +#define SN_X9_62_c2tnb191v3 "c2tnb191v3" +#define NID_X9_62_c2tnb191v3 690 +#define OBJ_X9_62_c2tnb191v3 OBJ_X9_62_c_TwoCurve,7L + +#define SN_X9_62_c2onb191v4 "c2onb191v4" +#define NID_X9_62_c2onb191v4 691 +#define OBJ_X9_62_c2onb191v4 OBJ_X9_62_c_TwoCurve,8L + +#define SN_X9_62_c2onb191v5 "c2onb191v5" +#define NID_X9_62_c2onb191v5 692 +#define OBJ_X9_62_c2onb191v5 OBJ_X9_62_c_TwoCurve,9L + +#define SN_X9_62_c2pnb208w1 "c2pnb208w1" +#define NID_X9_62_c2pnb208w1 693 +#define OBJ_X9_62_c2pnb208w1 OBJ_X9_62_c_TwoCurve,10L + +#define SN_X9_62_c2tnb239v1 "c2tnb239v1" +#define NID_X9_62_c2tnb239v1 694 +#define OBJ_X9_62_c2tnb239v1 OBJ_X9_62_c_TwoCurve,11L + +#define SN_X9_62_c2tnb239v2 "c2tnb239v2" +#define NID_X9_62_c2tnb239v2 695 +#define OBJ_X9_62_c2tnb239v2 OBJ_X9_62_c_TwoCurve,12L + +#define SN_X9_62_c2tnb239v3 "c2tnb239v3" +#define NID_X9_62_c2tnb239v3 696 +#define OBJ_X9_62_c2tnb239v3 OBJ_X9_62_c_TwoCurve,13L + +#define SN_X9_62_c2onb239v4 "c2onb239v4" +#define NID_X9_62_c2onb239v4 697 +#define OBJ_X9_62_c2onb239v4 OBJ_X9_62_c_TwoCurve,14L + +#define SN_X9_62_c2onb239v5 "c2onb239v5" +#define NID_X9_62_c2onb239v5 698 +#define OBJ_X9_62_c2onb239v5 OBJ_X9_62_c_TwoCurve,15L + +#define SN_X9_62_c2pnb272w1 "c2pnb272w1" +#define NID_X9_62_c2pnb272w1 699 +#define OBJ_X9_62_c2pnb272w1 OBJ_X9_62_c_TwoCurve,16L + +#define SN_X9_62_c2pnb304w1 "c2pnb304w1" +#define NID_X9_62_c2pnb304w1 700 +#define OBJ_X9_62_c2pnb304w1 OBJ_X9_62_c_TwoCurve,17L + +#define SN_X9_62_c2tnb359v1 "c2tnb359v1" +#define NID_X9_62_c2tnb359v1 701 +#define OBJ_X9_62_c2tnb359v1 OBJ_X9_62_c_TwoCurve,18L + +#define SN_X9_62_c2pnb368w1 "c2pnb368w1" +#define NID_X9_62_c2pnb368w1 702 +#define OBJ_X9_62_c2pnb368w1 OBJ_X9_62_c_TwoCurve,19L + +#define SN_X9_62_c2tnb431r1 "c2tnb431r1" +#define NID_X9_62_c2tnb431r1 703 +#define OBJ_X9_62_c2tnb431r1 OBJ_X9_62_c_TwoCurve,20L + +#define OBJ_X9_62_primeCurve OBJ_X9_62_ellipticCurve,1L + +#define SN_X9_62_prime192v1 "prime192v1" +#define NID_X9_62_prime192v1 409 +#define OBJ_X9_62_prime192v1 OBJ_X9_62_primeCurve,1L + +#define SN_X9_62_prime192v2 "prime192v2" +#define NID_X9_62_prime192v2 410 +#define OBJ_X9_62_prime192v2 OBJ_X9_62_primeCurve,2L + +#define SN_X9_62_prime192v3 "prime192v3" +#define NID_X9_62_prime192v3 411 +#define OBJ_X9_62_prime192v3 OBJ_X9_62_primeCurve,3L + +#define SN_X9_62_prime239v1 "prime239v1" +#define NID_X9_62_prime239v1 412 +#define OBJ_X9_62_prime239v1 OBJ_X9_62_primeCurve,4L + +#define SN_X9_62_prime239v2 "prime239v2" +#define NID_X9_62_prime239v2 413 +#define OBJ_X9_62_prime239v2 OBJ_X9_62_primeCurve,5L + +#define SN_X9_62_prime239v3 "prime239v3" +#define NID_X9_62_prime239v3 414 +#define OBJ_X9_62_prime239v3 OBJ_X9_62_primeCurve,6L + +#define SN_X9_62_prime256v1 "prime256v1" +#define NID_X9_62_prime256v1 415 +#define OBJ_X9_62_prime256v1 OBJ_X9_62_primeCurve,7L + +#define OBJ_X9_62_id_ecSigType OBJ_ansi_X9_62,4L + +#define SN_ecdsa_with_SHA1 "ecdsa-with-SHA1" +#define NID_ecdsa_with_SHA1 416 +#define OBJ_ecdsa_with_SHA1 OBJ_X9_62_id_ecSigType,1L + +#define OBJ_secg_ellipticCurve OBJ_certicom_arc,0L + +#define SN_secp112r1 "secp112r1" +#define NID_secp112r1 704 +#define OBJ_secp112r1 OBJ_secg_ellipticCurve,6L + +#define SN_secp112r2 "secp112r2" +#define NID_secp112r2 705 +#define OBJ_secp112r2 OBJ_secg_ellipticCurve,7L + +#define SN_secp128r1 "secp128r1" +#define NID_secp128r1 706 +#define OBJ_secp128r1 OBJ_secg_ellipticCurve,28L + +#define SN_secp128r2 "secp128r2" +#define NID_secp128r2 707 +#define OBJ_secp128r2 OBJ_secg_ellipticCurve,29L + +#define SN_secp160k1 "secp160k1" +#define NID_secp160k1 708 +#define OBJ_secp160k1 OBJ_secg_ellipticCurve,9L + +#define SN_secp160r1 "secp160r1" +#define NID_secp160r1 709 +#define OBJ_secp160r1 OBJ_secg_ellipticCurve,8L + +#define SN_secp160r2 "secp160r2" +#define NID_secp160r2 710 +#define OBJ_secp160r2 OBJ_secg_ellipticCurve,30L + +#define SN_secp192k1 "secp192k1" +#define NID_secp192k1 711 +#define OBJ_secp192k1 OBJ_secg_ellipticCurve,31L + +#define SN_secp224k1 "secp224k1" +#define NID_secp224k1 712 +#define OBJ_secp224k1 OBJ_secg_ellipticCurve,32L + +#define SN_secp224r1 "secp224r1" +#define NID_secp224r1 713 +#define OBJ_secp224r1 OBJ_secg_ellipticCurve,33L + +#define SN_secp256k1 "secp256k1" +#define NID_secp256k1 714 +#define OBJ_secp256k1 OBJ_secg_ellipticCurve,10L + +#define SN_secp384r1 "secp384r1" +#define NID_secp384r1 715 +#define OBJ_secp384r1 OBJ_secg_ellipticCurve,34L + +#define SN_secp521r1 "secp521r1" +#define NID_secp521r1 716 +#define OBJ_secp521r1 OBJ_secg_ellipticCurve,35L + +#define SN_sect113r1 "sect113r1" +#define NID_sect113r1 717 +#define OBJ_sect113r1 OBJ_secg_ellipticCurve,4L + +#define SN_sect113r2 "sect113r2" +#define NID_sect113r2 718 +#define OBJ_sect113r2 OBJ_secg_ellipticCurve,5L + +#define SN_sect131r1 "sect131r1" +#define NID_sect131r1 719 +#define OBJ_sect131r1 OBJ_secg_ellipticCurve,22L + +#define SN_sect131r2 "sect131r2" +#define NID_sect131r2 720 +#define OBJ_sect131r2 OBJ_secg_ellipticCurve,23L + +#define SN_sect163k1 "sect163k1" +#define NID_sect163k1 721 +#define OBJ_sect163k1 OBJ_secg_ellipticCurve,1L + +#define SN_sect163r1 "sect163r1" +#define NID_sect163r1 722 +#define OBJ_sect163r1 OBJ_secg_ellipticCurve,2L + +#define SN_sect163r2 "sect163r2" +#define NID_sect163r2 723 +#define OBJ_sect163r2 OBJ_secg_ellipticCurve,15L + +#define SN_sect193r1 "sect193r1" +#define NID_sect193r1 724 +#define OBJ_sect193r1 OBJ_secg_ellipticCurve,24L + +#define SN_sect193r2 "sect193r2" +#define NID_sect193r2 725 +#define OBJ_sect193r2 OBJ_secg_ellipticCurve,25L + +#define SN_sect233k1 "sect233k1" +#define NID_sect233k1 726 +#define OBJ_sect233k1 OBJ_secg_ellipticCurve,26L + +#define SN_sect233r1 "sect233r1" +#define NID_sect233r1 727 +#define OBJ_sect233r1 OBJ_secg_ellipticCurve,27L + +#define SN_sect239k1 "sect239k1" +#define NID_sect239k1 728 +#define OBJ_sect239k1 OBJ_secg_ellipticCurve,3L + +#define SN_sect283k1 "sect283k1" +#define NID_sect283k1 729 +#define OBJ_sect283k1 OBJ_secg_ellipticCurve,16L + +#define SN_sect283r1 "sect283r1" +#define NID_sect283r1 730 +#define OBJ_sect283r1 OBJ_secg_ellipticCurve,17L + +#define SN_sect409k1 "sect409k1" +#define NID_sect409k1 731 +#define OBJ_sect409k1 OBJ_secg_ellipticCurve,36L + +#define SN_sect409r1 "sect409r1" +#define NID_sect409r1 732 +#define OBJ_sect409r1 OBJ_secg_ellipticCurve,37L + +#define SN_sect571k1 "sect571k1" +#define NID_sect571k1 733 +#define OBJ_sect571k1 OBJ_secg_ellipticCurve,38L + +#define SN_sect571r1 "sect571r1" +#define NID_sect571r1 734 +#define OBJ_sect571r1 OBJ_secg_ellipticCurve,39L + +#define OBJ_wap_wsg_idm_ecid OBJ_wap_wsg,4L + +#define SN_wap_wsg_idm_ecid_wtls1 "wap-wsg-idm-ecid-wtls1" +#define NID_wap_wsg_idm_ecid_wtls1 735 +#define OBJ_wap_wsg_idm_ecid_wtls1 OBJ_wap_wsg_idm_ecid,1L + +#define SN_wap_wsg_idm_ecid_wtls3 "wap-wsg-idm-ecid-wtls3" +#define NID_wap_wsg_idm_ecid_wtls3 736 +#define OBJ_wap_wsg_idm_ecid_wtls3 OBJ_wap_wsg_idm_ecid,3L + +#define SN_wap_wsg_idm_ecid_wtls4 "wap-wsg-idm-ecid-wtls4" +#define NID_wap_wsg_idm_ecid_wtls4 737 +#define OBJ_wap_wsg_idm_ecid_wtls4 OBJ_wap_wsg_idm_ecid,4L + +#define SN_wap_wsg_idm_ecid_wtls5 "wap-wsg-idm-ecid-wtls5" +#define NID_wap_wsg_idm_ecid_wtls5 738 +#define OBJ_wap_wsg_idm_ecid_wtls5 OBJ_wap_wsg_idm_ecid,5L + +#define SN_wap_wsg_idm_ecid_wtls6 "wap-wsg-idm-ecid-wtls6" +#define NID_wap_wsg_idm_ecid_wtls6 739 +#define OBJ_wap_wsg_idm_ecid_wtls6 OBJ_wap_wsg_idm_ecid,6L + +#define SN_wap_wsg_idm_ecid_wtls7 "wap-wsg-idm-ecid-wtls7" +#define NID_wap_wsg_idm_ecid_wtls7 740 +#define OBJ_wap_wsg_idm_ecid_wtls7 OBJ_wap_wsg_idm_ecid,7L + +#define SN_wap_wsg_idm_ecid_wtls8 "wap-wsg-idm-ecid-wtls8" +#define NID_wap_wsg_idm_ecid_wtls8 741 +#define OBJ_wap_wsg_idm_ecid_wtls8 OBJ_wap_wsg_idm_ecid,8L + +#define SN_wap_wsg_idm_ecid_wtls9 "wap-wsg-idm-ecid-wtls9" +#define NID_wap_wsg_idm_ecid_wtls9 742 +#define OBJ_wap_wsg_idm_ecid_wtls9 OBJ_wap_wsg_idm_ecid,9L + +#define SN_wap_wsg_idm_ecid_wtls10 "wap-wsg-idm-ecid-wtls10" +#define NID_wap_wsg_idm_ecid_wtls10 743 +#define OBJ_wap_wsg_idm_ecid_wtls10 OBJ_wap_wsg_idm_ecid,10L + +#define SN_wap_wsg_idm_ecid_wtls11 "wap-wsg-idm-ecid-wtls11" +#define NID_wap_wsg_idm_ecid_wtls11 744 +#define OBJ_wap_wsg_idm_ecid_wtls11 OBJ_wap_wsg_idm_ecid,11L + +#define SN_wap_wsg_idm_ecid_wtls12 "wap-wsg-idm-ecid-wtls12" +#define NID_wap_wsg_idm_ecid_wtls12 745 +#define OBJ_wap_wsg_idm_ecid_wtls12 OBJ_wap_wsg_idm_ecid,12L + +#define SN_cast5_cbc "CAST5-CBC" +#define LN_cast5_cbc "cast5-cbc" +#define NID_cast5_cbc 108 +#define OBJ_cast5_cbc OBJ_ISO_US,113533L,7L,66L,10L + +#define SN_cast5_ecb "CAST5-ECB" +#define LN_cast5_ecb "cast5-ecb" +#define NID_cast5_ecb 109 + +#define SN_cast5_cfb64 "CAST5-CFB" +#define LN_cast5_cfb64 "cast5-cfb" +#define NID_cast5_cfb64 110 + +#define SN_cast5_ofb64 "CAST5-OFB" +#define LN_cast5_ofb64 "cast5-ofb" +#define NID_cast5_ofb64 111 + +#define LN_pbeWithMD5AndCast5_CBC "pbeWithMD5AndCast5CBC" +#define NID_pbeWithMD5AndCast5_CBC 112 +#define OBJ_pbeWithMD5AndCast5_CBC OBJ_ISO_US,113533L,7L,66L,12L + +#define SN_rsadsi "rsadsi" +#define LN_rsadsi "RSA Data Security, Inc." +#define NID_rsadsi 1 +#define OBJ_rsadsi OBJ_ISO_US,113549L + +#define SN_pkcs "pkcs" +#define LN_pkcs "RSA Data Security, Inc. PKCS" +#define NID_pkcs 2 +#define OBJ_pkcs OBJ_rsadsi,1L + +#define SN_pkcs1 "pkcs1" +#define NID_pkcs1 186 +#define OBJ_pkcs1 OBJ_pkcs,1L + +#define LN_rsaEncryption "rsaEncryption" +#define NID_rsaEncryption 6 +#define OBJ_rsaEncryption OBJ_pkcs1,1L + +#define SN_md2WithRSAEncryption "RSA-MD2" +#define LN_md2WithRSAEncryption "md2WithRSAEncryption" +#define NID_md2WithRSAEncryption 7 +#define OBJ_md2WithRSAEncryption OBJ_pkcs1,2L + +#define SN_md4WithRSAEncryption "RSA-MD4" +#define LN_md4WithRSAEncryption "md4WithRSAEncryption" +#define NID_md4WithRSAEncryption 396 +#define OBJ_md4WithRSAEncryption OBJ_pkcs1,3L + +#define SN_md5WithRSAEncryption "RSA-MD5" +#define LN_md5WithRSAEncryption "md5WithRSAEncryption" +#define NID_md5WithRSAEncryption 8 +#define OBJ_md5WithRSAEncryption OBJ_pkcs1,4L + +#define SN_sha1WithRSAEncryption "RSA-SHA1" +#define LN_sha1WithRSAEncryption "sha1WithRSAEncryption" +#define NID_sha1WithRSAEncryption 65 +#define OBJ_sha1WithRSAEncryption OBJ_pkcs1,5L + +#define SN_sha256WithRSAEncryption "RSA-SHA256" +#define LN_sha256WithRSAEncryption "sha256WithRSAEncryption" +#define NID_sha256WithRSAEncryption 668 +#define OBJ_sha256WithRSAEncryption OBJ_pkcs1,11L + +#define SN_sha384WithRSAEncryption "RSA-SHA384" +#define LN_sha384WithRSAEncryption "sha384WithRSAEncryption" +#define NID_sha384WithRSAEncryption 669 +#define OBJ_sha384WithRSAEncryption OBJ_pkcs1,12L + +#define SN_sha512WithRSAEncryption "RSA-SHA512" +#define LN_sha512WithRSAEncryption "sha512WithRSAEncryption" +#define NID_sha512WithRSAEncryption 670 +#define OBJ_sha512WithRSAEncryption OBJ_pkcs1,13L + +#define SN_sha224WithRSAEncryption "RSA-SHA224" +#define LN_sha224WithRSAEncryption "sha224WithRSAEncryption" +#define NID_sha224WithRSAEncryption 671 +#define OBJ_sha224WithRSAEncryption OBJ_pkcs1,14L + +#define SN_pkcs3 "pkcs3" +#define NID_pkcs3 27 +#define OBJ_pkcs3 OBJ_pkcs,3L + +#define LN_dhKeyAgreement "dhKeyAgreement" +#define NID_dhKeyAgreement 28 +#define OBJ_dhKeyAgreement OBJ_pkcs3,1L + +#define SN_pkcs5 "pkcs5" +#define NID_pkcs5 187 +#define OBJ_pkcs5 OBJ_pkcs,5L + +#define SN_pbeWithMD2AndDES_CBC "PBE-MD2-DES" +#define LN_pbeWithMD2AndDES_CBC "pbeWithMD2AndDES-CBC" +#define NID_pbeWithMD2AndDES_CBC 9 +#define OBJ_pbeWithMD2AndDES_CBC OBJ_pkcs5,1L + +#define SN_pbeWithMD5AndDES_CBC "PBE-MD5-DES" +#define LN_pbeWithMD5AndDES_CBC "pbeWithMD5AndDES-CBC" +#define NID_pbeWithMD5AndDES_CBC 10 +#define OBJ_pbeWithMD5AndDES_CBC OBJ_pkcs5,3L + +#define SN_pbeWithMD2AndRC2_CBC "PBE-MD2-RC2-64" +#define LN_pbeWithMD2AndRC2_CBC "pbeWithMD2AndRC2-CBC" +#define NID_pbeWithMD2AndRC2_CBC 168 +#define OBJ_pbeWithMD2AndRC2_CBC OBJ_pkcs5,4L + +#define SN_pbeWithMD5AndRC2_CBC "PBE-MD5-RC2-64" +#define LN_pbeWithMD5AndRC2_CBC "pbeWithMD5AndRC2-CBC" +#define NID_pbeWithMD5AndRC2_CBC 169 +#define OBJ_pbeWithMD5AndRC2_CBC OBJ_pkcs5,6L + +#define SN_pbeWithSHA1AndDES_CBC "PBE-SHA1-DES" +#define LN_pbeWithSHA1AndDES_CBC "pbeWithSHA1AndDES-CBC" +#define NID_pbeWithSHA1AndDES_CBC 170 +#define OBJ_pbeWithSHA1AndDES_CBC OBJ_pkcs5,10L + +#define SN_pbeWithSHA1AndRC2_CBC "PBE-SHA1-RC2-64" +#define LN_pbeWithSHA1AndRC2_CBC "pbeWithSHA1AndRC2-CBC" +#define NID_pbeWithSHA1AndRC2_CBC 68 +#define OBJ_pbeWithSHA1AndRC2_CBC OBJ_pkcs5,11L + +#define LN_id_pbkdf2 "PBKDF2" +#define NID_id_pbkdf2 69 +#define OBJ_id_pbkdf2 OBJ_pkcs5,12L + +#define LN_pbes2 "PBES2" +#define NID_pbes2 161 +#define OBJ_pbes2 OBJ_pkcs5,13L + +#define LN_pbmac1 "PBMAC1" +#define NID_pbmac1 162 +#define OBJ_pbmac1 OBJ_pkcs5,14L + +#define SN_pkcs7 "pkcs7" +#define NID_pkcs7 20 +#define OBJ_pkcs7 OBJ_pkcs,7L + +#define LN_pkcs7_data "pkcs7-data" +#define NID_pkcs7_data 21 +#define OBJ_pkcs7_data OBJ_pkcs7,1L + +#define LN_pkcs7_signed "pkcs7-signedData" +#define NID_pkcs7_signed 22 +#define OBJ_pkcs7_signed OBJ_pkcs7,2L + +#define LN_pkcs7_enveloped "pkcs7-envelopedData" +#define NID_pkcs7_enveloped 23 +#define OBJ_pkcs7_enveloped OBJ_pkcs7,3L + +#define LN_pkcs7_signedAndEnveloped "pkcs7-signedAndEnvelopedData" +#define NID_pkcs7_signedAndEnveloped 24 +#define OBJ_pkcs7_signedAndEnveloped OBJ_pkcs7,4L + +#define LN_pkcs7_digest "pkcs7-digestData" +#define NID_pkcs7_digest 25 +#define OBJ_pkcs7_digest OBJ_pkcs7,5L + +#define LN_pkcs7_encrypted "pkcs7-encryptedData" +#define NID_pkcs7_encrypted 26 +#define OBJ_pkcs7_encrypted OBJ_pkcs7,6L + +#define SN_pkcs9 "pkcs9" +#define NID_pkcs9 47 +#define OBJ_pkcs9 OBJ_pkcs,9L + +#define LN_pkcs9_emailAddress "emailAddress" +#define NID_pkcs9_emailAddress 48 +#define OBJ_pkcs9_emailAddress OBJ_pkcs9,1L + +#define LN_pkcs9_unstructuredName "unstructuredName" +#define NID_pkcs9_unstructuredName 49 +#define OBJ_pkcs9_unstructuredName OBJ_pkcs9,2L + +#define LN_pkcs9_contentType "contentType" +#define NID_pkcs9_contentType 50 +#define OBJ_pkcs9_contentType OBJ_pkcs9,3L + +#define LN_pkcs9_messageDigest "messageDigest" +#define NID_pkcs9_messageDigest 51 +#define OBJ_pkcs9_messageDigest OBJ_pkcs9,4L + +#define LN_pkcs9_signingTime "signingTime" +#define NID_pkcs9_signingTime 52 +#define OBJ_pkcs9_signingTime OBJ_pkcs9,5L + +#define LN_pkcs9_countersignature "countersignature" +#define NID_pkcs9_countersignature 53 +#define OBJ_pkcs9_countersignature OBJ_pkcs9,6L + +#define LN_pkcs9_challengePassword "challengePassword" +#define NID_pkcs9_challengePassword 54 +#define OBJ_pkcs9_challengePassword OBJ_pkcs9,7L + +#define LN_pkcs9_unstructuredAddress "unstructuredAddress" +#define NID_pkcs9_unstructuredAddress 55 +#define OBJ_pkcs9_unstructuredAddress OBJ_pkcs9,8L + +#define LN_pkcs9_extCertAttributes "extendedCertificateAttributes" +#define NID_pkcs9_extCertAttributes 56 +#define OBJ_pkcs9_extCertAttributes OBJ_pkcs9,9L + +#define SN_ext_req "extReq" +#define LN_ext_req "Extension Request" +#define NID_ext_req 172 +#define OBJ_ext_req OBJ_pkcs9,14L + +#define SN_SMIMECapabilities "SMIME-CAPS" +#define LN_SMIMECapabilities "S/MIME Capabilities" +#define NID_SMIMECapabilities 167 +#define OBJ_SMIMECapabilities OBJ_pkcs9,15L + +#define SN_SMIME "SMIME" +#define LN_SMIME "S/MIME" +#define NID_SMIME 188 +#define OBJ_SMIME OBJ_pkcs9,16L + +#define SN_id_smime_mod "id-smime-mod" +#define NID_id_smime_mod 189 +#define OBJ_id_smime_mod OBJ_SMIME,0L + +#define SN_id_smime_ct "id-smime-ct" +#define NID_id_smime_ct 190 +#define OBJ_id_smime_ct OBJ_SMIME,1L + +#define SN_id_smime_aa "id-smime-aa" +#define NID_id_smime_aa 191 +#define OBJ_id_smime_aa OBJ_SMIME,2L + +#define SN_id_smime_alg "id-smime-alg" +#define NID_id_smime_alg 192 +#define OBJ_id_smime_alg OBJ_SMIME,3L + +#define SN_id_smime_cd "id-smime-cd" +#define NID_id_smime_cd 193 +#define OBJ_id_smime_cd OBJ_SMIME,4L + +#define SN_id_smime_spq "id-smime-spq" +#define NID_id_smime_spq 194 +#define OBJ_id_smime_spq OBJ_SMIME,5L + +#define SN_id_smime_cti "id-smime-cti" +#define NID_id_smime_cti 195 +#define OBJ_id_smime_cti OBJ_SMIME,6L + +#define SN_id_smime_mod_cms "id-smime-mod-cms" +#define NID_id_smime_mod_cms 196 +#define OBJ_id_smime_mod_cms OBJ_id_smime_mod,1L + +#define SN_id_smime_mod_ess "id-smime-mod-ess" +#define NID_id_smime_mod_ess 197 +#define OBJ_id_smime_mod_ess OBJ_id_smime_mod,2L + +#define SN_id_smime_mod_oid "id-smime-mod-oid" +#define NID_id_smime_mod_oid 198 +#define OBJ_id_smime_mod_oid OBJ_id_smime_mod,3L + +#define SN_id_smime_mod_msg_v3 "id-smime-mod-msg-v3" +#define NID_id_smime_mod_msg_v3 199 +#define OBJ_id_smime_mod_msg_v3 OBJ_id_smime_mod,4L + +#define SN_id_smime_mod_ets_eSignature_88 "id-smime-mod-ets-eSignature-88" +#define NID_id_smime_mod_ets_eSignature_88 200 +#define OBJ_id_smime_mod_ets_eSignature_88 OBJ_id_smime_mod,5L + +#define SN_id_smime_mod_ets_eSignature_97 "id-smime-mod-ets-eSignature-97" +#define NID_id_smime_mod_ets_eSignature_97 201 +#define OBJ_id_smime_mod_ets_eSignature_97 OBJ_id_smime_mod,6L + +#define SN_id_smime_mod_ets_eSigPolicy_88 "id-smime-mod-ets-eSigPolicy-88" +#define NID_id_smime_mod_ets_eSigPolicy_88 202 +#define OBJ_id_smime_mod_ets_eSigPolicy_88 OBJ_id_smime_mod,7L + +#define SN_id_smime_mod_ets_eSigPolicy_97 "id-smime-mod-ets-eSigPolicy-97" +#define NID_id_smime_mod_ets_eSigPolicy_97 203 +#define OBJ_id_smime_mod_ets_eSigPolicy_97 OBJ_id_smime_mod,8L + +#define SN_id_smime_ct_receipt "id-smime-ct-receipt" +#define NID_id_smime_ct_receipt 204 +#define OBJ_id_smime_ct_receipt OBJ_id_smime_ct,1L + +#define SN_id_smime_ct_authData "id-smime-ct-authData" +#define NID_id_smime_ct_authData 205 +#define OBJ_id_smime_ct_authData OBJ_id_smime_ct,2L + +#define SN_id_smime_ct_publishCert "id-smime-ct-publishCert" +#define NID_id_smime_ct_publishCert 206 +#define OBJ_id_smime_ct_publishCert OBJ_id_smime_ct,3L + +#define SN_id_smime_ct_TSTInfo "id-smime-ct-TSTInfo" +#define NID_id_smime_ct_TSTInfo 207 +#define OBJ_id_smime_ct_TSTInfo OBJ_id_smime_ct,4L + +#define SN_id_smime_ct_TDTInfo "id-smime-ct-TDTInfo" +#define NID_id_smime_ct_TDTInfo 208 +#define OBJ_id_smime_ct_TDTInfo OBJ_id_smime_ct,5L + +#define SN_id_smime_ct_contentInfo "id-smime-ct-contentInfo" +#define NID_id_smime_ct_contentInfo 209 +#define OBJ_id_smime_ct_contentInfo OBJ_id_smime_ct,6L + +#define SN_id_smime_ct_DVCSRequestData "id-smime-ct-DVCSRequestData" +#define NID_id_smime_ct_DVCSRequestData 210 +#define OBJ_id_smime_ct_DVCSRequestData OBJ_id_smime_ct,7L + +#define SN_id_smime_ct_DVCSResponseData "id-smime-ct-DVCSResponseData" +#define NID_id_smime_ct_DVCSResponseData 211 +#define OBJ_id_smime_ct_DVCSResponseData OBJ_id_smime_ct,8L + +#define SN_id_smime_aa_receiptRequest "id-smime-aa-receiptRequest" +#define NID_id_smime_aa_receiptRequest 212 +#define OBJ_id_smime_aa_receiptRequest OBJ_id_smime_aa,1L + +#define SN_id_smime_aa_securityLabel "id-smime-aa-securityLabel" +#define NID_id_smime_aa_securityLabel 213 +#define OBJ_id_smime_aa_securityLabel OBJ_id_smime_aa,2L + +#define SN_id_smime_aa_mlExpandHistory "id-smime-aa-mlExpandHistory" +#define NID_id_smime_aa_mlExpandHistory 214 +#define OBJ_id_smime_aa_mlExpandHistory OBJ_id_smime_aa,3L + +#define SN_id_smime_aa_contentHint "id-smime-aa-contentHint" +#define NID_id_smime_aa_contentHint 215 +#define OBJ_id_smime_aa_contentHint OBJ_id_smime_aa,4L + +#define SN_id_smime_aa_msgSigDigest "id-smime-aa-msgSigDigest" +#define NID_id_smime_aa_msgSigDigest 216 +#define OBJ_id_smime_aa_msgSigDigest OBJ_id_smime_aa,5L + +#define SN_id_smime_aa_encapContentType "id-smime-aa-encapContentType" +#define NID_id_smime_aa_encapContentType 217 +#define OBJ_id_smime_aa_encapContentType OBJ_id_smime_aa,6L + +#define SN_id_smime_aa_contentIdentifier "id-smime-aa-contentIdentifier" +#define NID_id_smime_aa_contentIdentifier 218 +#define OBJ_id_smime_aa_contentIdentifier OBJ_id_smime_aa,7L + +#define SN_id_smime_aa_macValue "id-smime-aa-macValue" +#define NID_id_smime_aa_macValue 219 +#define OBJ_id_smime_aa_macValue OBJ_id_smime_aa,8L + +#define SN_id_smime_aa_equivalentLabels "id-smime-aa-equivalentLabels" +#define NID_id_smime_aa_equivalentLabels 220 +#define OBJ_id_smime_aa_equivalentLabels OBJ_id_smime_aa,9L + +#define SN_id_smime_aa_contentReference "id-smime-aa-contentReference" +#define NID_id_smime_aa_contentReference 221 +#define OBJ_id_smime_aa_contentReference OBJ_id_smime_aa,10L + +#define SN_id_smime_aa_encrypKeyPref "id-smime-aa-encrypKeyPref" +#define NID_id_smime_aa_encrypKeyPref 222 +#define OBJ_id_smime_aa_encrypKeyPref OBJ_id_smime_aa,11L + +#define SN_id_smime_aa_signingCertificate "id-smime-aa-signingCertificate" +#define NID_id_smime_aa_signingCertificate 223 +#define OBJ_id_smime_aa_signingCertificate OBJ_id_smime_aa,12L + +#define SN_id_smime_aa_smimeEncryptCerts "id-smime-aa-smimeEncryptCerts" +#define NID_id_smime_aa_smimeEncryptCerts 224 +#define OBJ_id_smime_aa_smimeEncryptCerts OBJ_id_smime_aa,13L + +#define SN_id_smime_aa_timeStampToken "id-smime-aa-timeStampToken" +#define NID_id_smime_aa_timeStampToken 225 +#define OBJ_id_smime_aa_timeStampToken OBJ_id_smime_aa,14L + +#define SN_id_smime_aa_ets_sigPolicyId "id-smime-aa-ets-sigPolicyId" +#define NID_id_smime_aa_ets_sigPolicyId 226 +#define OBJ_id_smime_aa_ets_sigPolicyId OBJ_id_smime_aa,15L + +#define SN_id_smime_aa_ets_commitmentType "id-smime-aa-ets-commitmentType" +#define NID_id_smime_aa_ets_commitmentType 227 +#define OBJ_id_smime_aa_ets_commitmentType OBJ_id_smime_aa,16L + +#define SN_id_smime_aa_ets_signerLocation "id-smime-aa-ets-signerLocation" +#define NID_id_smime_aa_ets_signerLocation 228 +#define OBJ_id_smime_aa_ets_signerLocation OBJ_id_smime_aa,17L + +#define SN_id_smime_aa_ets_signerAttr "id-smime-aa-ets-signerAttr" +#define NID_id_smime_aa_ets_signerAttr 229 +#define OBJ_id_smime_aa_ets_signerAttr OBJ_id_smime_aa,18L + +#define SN_id_smime_aa_ets_otherSigCert "id-smime-aa-ets-otherSigCert" +#define NID_id_smime_aa_ets_otherSigCert 230 +#define OBJ_id_smime_aa_ets_otherSigCert OBJ_id_smime_aa,19L + +#define SN_id_smime_aa_ets_contentTimestamp "id-smime-aa-ets-contentTimestamp" +#define NID_id_smime_aa_ets_contentTimestamp 231 +#define OBJ_id_smime_aa_ets_contentTimestamp OBJ_id_smime_aa,20L + +#define SN_id_smime_aa_ets_CertificateRefs "id-smime-aa-ets-CertificateRefs" +#define NID_id_smime_aa_ets_CertificateRefs 232 +#define OBJ_id_smime_aa_ets_CertificateRefs OBJ_id_smime_aa,21L + +#define SN_id_smime_aa_ets_RevocationRefs "id-smime-aa-ets-RevocationRefs" +#define NID_id_smime_aa_ets_RevocationRefs 233 +#define OBJ_id_smime_aa_ets_RevocationRefs OBJ_id_smime_aa,22L + +#define SN_id_smime_aa_ets_certValues "id-smime-aa-ets-certValues" +#define NID_id_smime_aa_ets_certValues 234 +#define OBJ_id_smime_aa_ets_certValues OBJ_id_smime_aa,23L + +#define SN_id_smime_aa_ets_revocationValues "id-smime-aa-ets-revocationValues" +#define NID_id_smime_aa_ets_revocationValues 235 +#define OBJ_id_smime_aa_ets_revocationValues OBJ_id_smime_aa,24L + +#define SN_id_smime_aa_ets_escTimeStamp "id-smime-aa-ets-escTimeStamp" +#define NID_id_smime_aa_ets_escTimeStamp 236 +#define OBJ_id_smime_aa_ets_escTimeStamp OBJ_id_smime_aa,25L + +#define SN_id_smime_aa_ets_certCRLTimestamp "id-smime-aa-ets-certCRLTimestamp" +#define NID_id_smime_aa_ets_certCRLTimestamp 237 +#define OBJ_id_smime_aa_ets_certCRLTimestamp OBJ_id_smime_aa,26L + +#define SN_id_smime_aa_ets_archiveTimeStamp "id-smime-aa-ets-archiveTimeStamp" +#define NID_id_smime_aa_ets_archiveTimeStamp 238 +#define OBJ_id_smime_aa_ets_archiveTimeStamp OBJ_id_smime_aa,27L + +#define SN_id_smime_aa_signatureType "id-smime-aa-signatureType" +#define NID_id_smime_aa_signatureType 239 +#define OBJ_id_smime_aa_signatureType OBJ_id_smime_aa,28L + +#define SN_id_smime_aa_dvcs_dvc "id-smime-aa-dvcs-dvc" +#define NID_id_smime_aa_dvcs_dvc 240 +#define OBJ_id_smime_aa_dvcs_dvc OBJ_id_smime_aa,29L + +#define SN_id_smime_alg_ESDHwith3DES "id-smime-alg-ESDHwith3DES" +#define NID_id_smime_alg_ESDHwith3DES 241 +#define OBJ_id_smime_alg_ESDHwith3DES OBJ_id_smime_alg,1L + +#define SN_id_smime_alg_ESDHwithRC2 "id-smime-alg-ESDHwithRC2" +#define NID_id_smime_alg_ESDHwithRC2 242 +#define OBJ_id_smime_alg_ESDHwithRC2 OBJ_id_smime_alg,2L + +#define SN_id_smime_alg_3DESwrap "id-smime-alg-3DESwrap" +#define NID_id_smime_alg_3DESwrap 243 +#define OBJ_id_smime_alg_3DESwrap OBJ_id_smime_alg,3L + +#define SN_id_smime_alg_RC2wrap "id-smime-alg-RC2wrap" +#define NID_id_smime_alg_RC2wrap 244 +#define OBJ_id_smime_alg_RC2wrap OBJ_id_smime_alg,4L + +#define SN_id_smime_alg_ESDH "id-smime-alg-ESDH" +#define NID_id_smime_alg_ESDH 245 +#define OBJ_id_smime_alg_ESDH OBJ_id_smime_alg,5L + +#define SN_id_smime_alg_CMS3DESwrap "id-smime-alg-CMS3DESwrap" +#define NID_id_smime_alg_CMS3DESwrap 246 +#define OBJ_id_smime_alg_CMS3DESwrap OBJ_id_smime_alg,6L + +#define SN_id_smime_alg_CMSRC2wrap "id-smime-alg-CMSRC2wrap" +#define NID_id_smime_alg_CMSRC2wrap 247 +#define OBJ_id_smime_alg_CMSRC2wrap OBJ_id_smime_alg,7L + +#define SN_id_smime_cd_ldap "id-smime-cd-ldap" +#define NID_id_smime_cd_ldap 248 +#define OBJ_id_smime_cd_ldap OBJ_id_smime_cd,1L + +#define SN_id_smime_spq_ets_sqt_uri "id-smime-spq-ets-sqt-uri" +#define NID_id_smime_spq_ets_sqt_uri 249 +#define OBJ_id_smime_spq_ets_sqt_uri OBJ_id_smime_spq,1L + +#define SN_id_smime_spq_ets_sqt_unotice "id-smime-spq-ets-sqt-unotice" +#define NID_id_smime_spq_ets_sqt_unotice 250 +#define OBJ_id_smime_spq_ets_sqt_unotice OBJ_id_smime_spq,2L + +#define SN_id_smime_cti_ets_proofOfOrigin "id-smime-cti-ets-proofOfOrigin" +#define NID_id_smime_cti_ets_proofOfOrigin 251 +#define OBJ_id_smime_cti_ets_proofOfOrigin OBJ_id_smime_cti,1L + +#define SN_id_smime_cti_ets_proofOfReceipt "id-smime-cti-ets-proofOfReceipt" +#define NID_id_smime_cti_ets_proofOfReceipt 252 +#define OBJ_id_smime_cti_ets_proofOfReceipt OBJ_id_smime_cti,2L + +#define SN_id_smime_cti_ets_proofOfDelivery "id-smime-cti-ets-proofOfDelivery" +#define NID_id_smime_cti_ets_proofOfDelivery 253 +#define OBJ_id_smime_cti_ets_proofOfDelivery OBJ_id_smime_cti,3L + +#define SN_id_smime_cti_ets_proofOfSender "id-smime-cti-ets-proofOfSender" +#define NID_id_smime_cti_ets_proofOfSender 254 +#define OBJ_id_smime_cti_ets_proofOfSender OBJ_id_smime_cti,4L + +#define SN_id_smime_cti_ets_proofOfApproval "id-smime-cti-ets-proofOfApproval" +#define NID_id_smime_cti_ets_proofOfApproval 255 +#define OBJ_id_smime_cti_ets_proofOfApproval OBJ_id_smime_cti,5L + +#define SN_id_smime_cti_ets_proofOfCreation "id-smime-cti-ets-proofOfCreation" +#define NID_id_smime_cti_ets_proofOfCreation 256 +#define OBJ_id_smime_cti_ets_proofOfCreation OBJ_id_smime_cti,6L + +#define LN_friendlyName "friendlyName" +#define NID_friendlyName 156 +#define OBJ_friendlyName OBJ_pkcs9,20L + +#define LN_localKeyID "localKeyID" +#define NID_localKeyID 157 +#define OBJ_localKeyID OBJ_pkcs9,21L + +#define SN_ms_csp_name "CSPName" +#define LN_ms_csp_name "Microsoft CSP Name" +#define NID_ms_csp_name 417 +#define OBJ_ms_csp_name 1L,3L,6L,1L,4L,1L,311L,17L,1L + +#define OBJ_certTypes OBJ_pkcs9,22L + +#define LN_x509Certificate "x509Certificate" +#define NID_x509Certificate 158 +#define OBJ_x509Certificate OBJ_certTypes,1L + +#define LN_sdsiCertificate "sdsiCertificate" +#define NID_sdsiCertificate 159 +#define OBJ_sdsiCertificate OBJ_certTypes,2L + +#define OBJ_crlTypes OBJ_pkcs9,23L + +#define LN_x509Crl "x509Crl" +#define NID_x509Crl 160 +#define OBJ_x509Crl OBJ_crlTypes,1L + +#define OBJ_pkcs12 OBJ_pkcs,12L + +#define OBJ_pkcs12_pbeids OBJ_pkcs12,1L + +#define SN_pbe_WithSHA1And128BitRC4 "PBE-SHA1-RC4-128" +#define LN_pbe_WithSHA1And128BitRC4 "pbeWithSHA1And128BitRC4" +#define NID_pbe_WithSHA1And128BitRC4 144 +#define OBJ_pbe_WithSHA1And128BitRC4 OBJ_pkcs12_pbeids,1L + +#define SN_pbe_WithSHA1And40BitRC4 "PBE-SHA1-RC4-40" +#define LN_pbe_WithSHA1And40BitRC4 "pbeWithSHA1And40BitRC4" +#define NID_pbe_WithSHA1And40BitRC4 145 +#define OBJ_pbe_WithSHA1And40BitRC4 OBJ_pkcs12_pbeids,2L + +#define SN_pbe_WithSHA1And3_Key_TripleDES_CBC "PBE-SHA1-3DES" +#define LN_pbe_WithSHA1And3_Key_TripleDES_CBC "pbeWithSHA1And3-KeyTripleDES-CBC" +#define NID_pbe_WithSHA1And3_Key_TripleDES_CBC 146 +#define OBJ_pbe_WithSHA1And3_Key_TripleDES_CBC OBJ_pkcs12_pbeids,3L + +#define SN_pbe_WithSHA1And2_Key_TripleDES_CBC "PBE-SHA1-2DES" +#define LN_pbe_WithSHA1And2_Key_TripleDES_CBC "pbeWithSHA1And2-KeyTripleDES-CBC" +#define NID_pbe_WithSHA1And2_Key_TripleDES_CBC 147 +#define OBJ_pbe_WithSHA1And2_Key_TripleDES_CBC OBJ_pkcs12_pbeids,4L + +#define SN_pbe_WithSHA1And128BitRC2_CBC "PBE-SHA1-RC2-128" +#define LN_pbe_WithSHA1And128BitRC2_CBC "pbeWithSHA1And128BitRC2-CBC" +#define NID_pbe_WithSHA1And128BitRC2_CBC 148 +#define OBJ_pbe_WithSHA1And128BitRC2_CBC OBJ_pkcs12_pbeids,5L + +#define SN_pbe_WithSHA1And40BitRC2_CBC "PBE-SHA1-RC2-40" +#define LN_pbe_WithSHA1And40BitRC2_CBC "pbeWithSHA1And40BitRC2-CBC" +#define NID_pbe_WithSHA1And40BitRC2_CBC 149 +#define OBJ_pbe_WithSHA1And40BitRC2_CBC OBJ_pkcs12_pbeids,6L + +#define OBJ_pkcs12_Version1 OBJ_pkcs12,10L + +#define OBJ_pkcs12_BagIds OBJ_pkcs12_Version1,1L + +#define LN_keyBag "keyBag" +#define NID_keyBag 150 +#define OBJ_keyBag OBJ_pkcs12_BagIds,1L + +#define LN_pkcs8ShroudedKeyBag "pkcs8ShroudedKeyBag" +#define NID_pkcs8ShroudedKeyBag 151 +#define OBJ_pkcs8ShroudedKeyBag OBJ_pkcs12_BagIds,2L + +#define LN_certBag "certBag" +#define NID_certBag 152 +#define OBJ_certBag OBJ_pkcs12_BagIds,3L + +#define LN_crlBag "crlBag" +#define NID_crlBag 153 +#define OBJ_crlBag OBJ_pkcs12_BagIds,4L + +#define LN_secretBag "secretBag" +#define NID_secretBag 154 +#define OBJ_secretBag OBJ_pkcs12_BagIds,5L + +#define LN_safeContentsBag "safeContentsBag" +#define NID_safeContentsBag 155 +#define OBJ_safeContentsBag OBJ_pkcs12_BagIds,6L + +#define SN_md2 "MD2" +#define LN_md2 "md2" +#define NID_md2 3 +#define OBJ_md2 OBJ_rsadsi,2L,2L + +#define SN_md4 "MD4" +#define LN_md4 "md4" +#define NID_md4 257 +#define OBJ_md4 OBJ_rsadsi,2L,4L + +#define SN_md5 "MD5" +#define LN_md5 "md5" +#define NID_md5 4 +#define OBJ_md5 OBJ_rsadsi,2L,5L + +#define SN_md5_sha1 "MD5-SHA1" +#define LN_md5_sha1 "md5-sha1" +#define NID_md5_sha1 114 + +#define LN_hmacWithSHA1 "hmacWithSHA1" +#define NID_hmacWithSHA1 163 +#define OBJ_hmacWithSHA1 OBJ_rsadsi,2L,7L + +#define SN_rc2_cbc "RC2-CBC" +#define LN_rc2_cbc "rc2-cbc" +#define NID_rc2_cbc 37 +#define OBJ_rc2_cbc OBJ_rsadsi,3L,2L + +#define SN_rc2_ecb "RC2-ECB" +#define LN_rc2_ecb "rc2-ecb" +#define NID_rc2_ecb 38 + +#define SN_rc2_cfb64 "RC2-CFB" +#define LN_rc2_cfb64 "rc2-cfb" +#define NID_rc2_cfb64 39 + +#define SN_rc2_ofb64 "RC2-OFB" +#define LN_rc2_ofb64 "rc2-ofb" +#define NID_rc2_ofb64 40 + +#define SN_rc2_40_cbc "RC2-40-CBC" +#define LN_rc2_40_cbc "rc2-40-cbc" +#define NID_rc2_40_cbc 98 + +#define SN_rc2_64_cbc "RC2-64-CBC" +#define LN_rc2_64_cbc "rc2-64-cbc" +#define NID_rc2_64_cbc 166 + +#define SN_rc4 "RC4" +#define LN_rc4 "rc4" +#define NID_rc4 5 +#define OBJ_rc4 OBJ_rsadsi,3L,4L + +#define SN_rc4_40 "RC4-40" +#define LN_rc4_40 "rc4-40" +#define NID_rc4_40 97 + +#define SN_des_ede3_cbc "DES-EDE3-CBC" +#define LN_des_ede3_cbc "des-ede3-cbc" +#define NID_des_ede3_cbc 44 +#define OBJ_des_ede3_cbc OBJ_rsadsi,3L,7L + +#define SN_rc5_cbc "RC5-CBC" +#define LN_rc5_cbc "rc5-cbc" +#define NID_rc5_cbc 120 +#define OBJ_rc5_cbc OBJ_rsadsi,3L,8L + +#define SN_rc5_ecb "RC5-ECB" +#define LN_rc5_ecb "rc5-ecb" +#define NID_rc5_ecb 121 + +#define SN_rc5_cfb64 "RC5-CFB" +#define LN_rc5_cfb64 "rc5-cfb" +#define NID_rc5_cfb64 122 + +#define SN_rc5_ofb64 "RC5-OFB" +#define LN_rc5_ofb64 "rc5-ofb" +#define NID_rc5_ofb64 123 + +#define SN_ms_ext_req "msExtReq" +#define LN_ms_ext_req "Microsoft Extension Request" +#define NID_ms_ext_req 171 +#define OBJ_ms_ext_req 1L,3L,6L,1L,4L,1L,311L,2L,1L,14L + +#define SN_ms_code_ind "msCodeInd" +#define LN_ms_code_ind "Microsoft Individual Code Signing" +#define NID_ms_code_ind 134 +#define OBJ_ms_code_ind 1L,3L,6L,1L,4L,1L,311L,2L,1L,21L + +#define SN_ms_code_com "msCodeCom" +#define LN_ms_code_com "Microsoft Commercial Code Signing" +#define NID_ms_code_com 135 +#define OBJ_ms_code_com 1L,3L,6L,1L,4L,1L,311L,2L,1L,22L + +#define SN_ms_ctl_sign "msCTLSign" +#define LN_ms_ctl_sign "Microsoft Trust List Signing" +#define NID_ms_ctl_sign 136 +#define OBJ_ms_ctl_sign 1L,3L,6L,1L,4L,1L,311L,10L,3L,1L + +#define SN_ms_sgc "msSGC" +#define LN_ms_sgc "Microsoft Server Gated Crypto" +#define NID_ms_sgc 137 +#define OBJ_ms_sgc 1L,3L,6L,1L,4L,1L,311L,10L,3L,3L + +#define SN_ms_efs "msEFS" +#define LN_ms_efs "Microsoft Encrypted File System" +#define NID_ms_efs 138 +#define OBJ_ms_efs 1L,3L,6L,1L,4L,1L,311L,10L,3L,4L + +#define SN_ms_smartcard_login "msSmartcardLogin" +#define LN_ms_smartcard_login "Microsoft Smartcardlogin" +#define NID_ms_smartcard_login 648 +#define OBJ_ms_smartcard_login 1L,3L,6L,1L,4L,1L,311L,20L,2L,2L + +#define SN_ms_upn "msUPN" +#define LN_ms_upn "Microsoft Universal Principal Name" +#define NID_ms_upn 649 +#define OBJ_ms_upn 1L,3L,6L,1L,4L,1L,311L,20L,2L,3L + +#define SN_idea_cbc "IDEA-CBC" +#define LN_idea_cbc "idea-cbc" +#define NID_idea_cbc 34 +#define OBJ_idea_cbc 1L,3L,6L,1L,4L,1L,188L,7L,1L,1L,2L + +#define SN_idea_ecb "IDEA-ECB" +#define LN_idea_ecb "idea-ecb" +#define NID_idea_ecb 36 + +#define SN_idea_cfb64 "IDEA-CFB" +#define LN_idea_cfb64 "idea-cfb" +#define NID_idea_cfb64 35 + +#define SN_idea_ofb64 "IDEA-OFB" +#define LN_idea_ofb64 "idea-ofb" +#define NID_idea_ofb64 46 + +#define SN_bf_cbc "BF-CBC" +#define LN_bf_cbc "bf-cbc" +#define NID_bf_cbc 91 +#define OBJ_bf_cbc 1L,3L,6L,1L,4L,1L,3029L,1L,2L + +#define SN_bf_ecb "BF-ECB" +#define LN_bf_ecb "bf-ecb" +#define NID_bf_ecb 92 + +#define SN_bf_cfb64 "BF-CFB" +#define LN_bf_cfb64 "bf-cfb" +#define NID_bf_cfb64 93 + +#define SN_bf_ofb64 "BF-OFB" +#define LN_bf_ofb64 "bf-ofb" +#define NID_bf_ofb64 94 + +#define SN_id_pkix "PKIX" +#define NID_id_pkix 127 +#define OBJ_id_pkix 1L,3L,6L,1L,5L,5L,7L + +#define SN_id_pkix_mod "id-pkix-mod" +#define NID_id_pkix_mod 258 +#define OBJ_id_pkix_mod OBJ_id_pkix,0L + +#define SN_id_pe "id-pe" +#define NID_id_pe 175 +#define OBJ_id_pe OBJ_id_pkix,1L + +#define SN_id_qt "id-qt" +#define NID_id_qt 259 +#define OBJ_id_qt OBJ_id_pkix,2L + +#define SN_id_kp "id-kp" +#define NID_id_kp 128 +#define OBJ_id_kp OBJ_id_pkix,3L + +#define SN_id_it "id-it" +#define NID_id_it 260 +#define OBJ_id_it OBJ_id_pkix,4L + +#define SN_id_pkip "id-pkip" +#define NID_id_pkip 261 +#define OBJ_id_pkip OBJ_id_pkix,5L + +#define SN_id_alg "id-alg" +#define NID_id_alg 262 +#define OBJ_id_alg OBJ_id_pkix,6L + +#define SN_id_cmc "id-cmc" +#define NID_id_cmc 263 +#define OBJ_id_cmc OBJ_id_pkix,7L + +#define SN_id_on "id-on" +#define NID_id_on 264 +#define OBJ_id_on OBJ_id_pkix,8L + +#define SN_id_pda "id-pda" +#define NID_id_pda 265 +#define OBJ_id_pda OBJ_id_pkix,9L + +#define SN_id_aca "id-aca" +#define NID_id_aca 266 +#define OBJ_id_aca OBJ_id_pkix,10L + +#define SN_id_qcs "id-qcs" +#define NID_id_qcs 267 +#define OBJ_id_qcs OBJ_id_pkix,11L + +#define SN_id_cct "id-cct" +#define NID_id_cct 268 +#define OBJ_id_cct OBJ_id_pkix,12L + +#define SN_id_ppl "id-ppl" +#define NID_id_ppl 662 +#define OBJ_id_ppl OBJ_id_pkix,21L + +#define SN_id_ad "id-ad" +#define NID_id_ad 176 +#define OBJ_id_ad OBJ_id_pkix,48L + +#define SN_id_pkix1_explicit_88 "id-pkix1-explicit-88" +#define NID_id_pkix1_explicit_88 269 +#define OBJ_id_pkix1_explicit_88 OBJ_id_pkix_mod,1L + +#define SN_id_pkix1_implicit_88 "id-pkix1-implicit-88" +#define NID_id_pkix1_implicit_88 270 +#define OBJ_id_pkix1_implicit_88 OBJ_id_pkix_mod,2L + +#define SN_id_pkix1_explicit_93 "id-pkix1-explicit-93" +#define NID_id_pkix1_explicit_93 271 +#define OBJ_id_pkix1_explicit_93 OBJ_id_pkix_mod,3L + +#define SN_id_pkix1_implicit_93 "id-pkix1-implicit-93" +#define NID_id_pkix1_implicit_93 272 +#define OBJ_id_pkix1_implicit_93 OBJ_id_pkix_mod,4L + +#define SN_id_mod_crmf "id-mod-crmf" +#define NID_id_mod_crmf 273 +#define OBJ_id_mod_crmf OBJ_id_pkix_mod,5L + +#define SN_id_mod_cmc "id-mod-cmc" +#define NID_id_mod_cmc 274 +#define OBJ_id_mod_cmc OBJ_id_pkix_mod,6L + +#define SN_id_mod_kea_profile_88 "id-mod-kea-profile-88" +#define NID_id_mod_kea_profile_88 275 +#define OBJ_id_mod_kea_profile_88 OBJ_id_pkix_mod,7L + +#define SN_id_mod_kea_profile_93 "id-mod-kea-profile-93" +#define NID_id_mod_kea_profile_93 276 +#define OBJ_id_mod_kea_profile_93 OBJ_id_pkix_mod,8L + +#define SN_id_mod_cmp "id-mod-cmp" +#define NID_id_mod_cmp 277 +#define OBJ_id_mod_cmp OBJ_id_pkix_mod,9L + +#define SN_id_mod_qualified_cert_88 "id-mod-qualified-cert-88" +#define NID_id_mod_qualified_cert_88 278 +#define OBJ_id_mod_qualified_cert_88 OBJ_id_pkix_mod,10L + +#define SN_id_mod_qualified_cert_93 "id-mod-qualified-cert-93" +#define NID_id_mod_qualified_cert_93 279 +#define OBJ_id_mod_qualified_cert_93 OBJ_id_pkix_mod,11L + +#define SN_id_mod_attribute_cert "id-mod-attribute-cert" +#define NID_id_mod_attribute_cert 280 +#define OBJ_id_mod_attribute_cert OBJ_id_pkix_mod,12L + +#define SN_id_mod_timestamp_protocol "id-mod-timestamp-protocol" +#define NID_id_mod_timestamp_protocol 281 +#define OBJ_id_mod_timestamp_protocol OBJ_id_pkix_mod,13L + +#define SN_id_mod_ocsp "id-mod-ocsp" +#define NID_id_mod_ocsp 282 +#define OBJ_id_mod_ocsp OBJ_id_pkix_mod,14L + +#define SN_id_mod_dvcs "id-mod-dvcs" +#define NID_id_mod_dvcs 283 +#define OBJ_id_mod_dvcs OBJ_id_pkix_mod,15L + +#define SN_id_mod_cmp2000 "id-mod-cmp2000" +#define NID_id_mod_cmp2000 284 +#define OBJ_id_mod_cmp2000 OBJ_id_pkix_mod,16L + +#define SN_info_access "authorityInfoAccess" +#define LN_info_access "Authority Information Access" +#define NID_info_access 177 +#define OBJ_info_access OBJ_id_pe,1L + +#define SN_biometricInfo "biometricInfo" +#define LN_biometricInfo "Biometric Info" +#define NID_biometricInfo 285 +#define OBJ_biometricInfo OBJ_id_pe,2L + +#define SN_qcStatements "qcStatements" +#define NID_qcStatements 286 +#define OBJ_qcStatements OBJ_id_pe,3L + +#define SN_ac_auditEntity "ac-auditEntity" +#define NID_ac_auditEntity 287 +#define OBJ_ac_auditEntity OBJ_id_pe,4L + +#define SN_ac_targeting "ac-targeting" +#define NID_ac_targeting 288 +#define OBJ_ac_targeting OBJ_id_pe,5L + +#define SN_aaControls "aaControls" +#define NID_aaControls 289 +#define OBJ_aaControls OBJ_id_pe,6L + +#define SN_sbgp_ipAddrBlock "sbgp-ipAddrBlock" +#define NID_sbgp_ipAddrBlock 290 +#define OBJ_sbgp_ipAddrBlock OBJ_id_pe,7L + +#define SN_sbgp_autonomousSysNum "sbgp-autonomousSysNum" +#define NID_sbgp_autonomousSysNum 291 +#define OBJ_sbgp_autonomousSysNum OBJ_id_pe,8L + +#define SN_sbgp_routerIdentifier "sbgp-routerIdentifier" +#define NID_sbgp_routerIdentifier 292 +#define OBJ_sbgp_routerIdentifier OBJ_id_pe,9L + +#define SN_ac_proxying "ac-proxying" +#define NID_ac_proxying 397 +#define OBJ_ac_proxying OBJ_id_pe,10L + +#define SN_sinfo_access "subjectInfoAccess" +#define LN_sinfo_access "Subject Information Access" +#define NID_sinfo_access 398 +#define OBJ_sinfo_access OBJ_id_pe,11L + +#define SN_proxyCertInfo "proxyCertInfo" +#define LN_proxyCertInfo "Proxy Certificate Information" +#define NID_proxyCertInfo 663 +#define OBJ_proxyCertInfo OBJ_id_pe,14L + +#define SN_id_qt_cps "id-qt-cps" +#define LN_id_qt_cps "Policy Qualifier CPS" +#define NID_id_qt_cps 164 +#define OBJ_id_qt_cps OBJ_id_qt,1L + +#define SN_id_qt_unotice "id-qt-unotice" +#define LN_id_qt_unotice "Policy Qualifier User Notice" +#define NID_id_qt_unotice 165 +#define OBJ_id_qt_unotice OBJ_id_qt,2L + +#define SN_textNotice "textNotice" +#define NID_textNotice 293 +#define OBJ_textNotice OBJ_id_qt,3L + +#define SN_server_auth "serverAuth" +#define LN_server_auth "TLS Web Server Authentication" +#define NID_server_auth 129 +#define OBJ_server_auth OBJ_id_kp,1L + +#define SN_client_auth "clientAuth" +#define LN_client_auth "TLS Web Client Authentication" +#define NID_client_auth 130 +#define OBJ_client_auth OBJ_id_kp,2L + +#define SN_code_sign "codeSigning" +#define LN_code_sign "Code Signing" +#define NID_code_sign 131 +#define OBJ_code_sign OBJ_id_kp,3L + +#define SN_email_protect "emailProtection" +#define LN_email_protect "E-mail Protection" +#define NID_email_protect 132 +#define OBJ_email_protect OBJ_id_kp,4L + +#define SN_ipsecEndSystem "ipsecEndSystem" +#define LN_ipsecEndSystem "IPSec End System" +#define NID_ipsecEndSystem 294 +#define OBJ_ipsecEndSystem OBJ_id_kp,5L + +#define SN_ipsecTunnel "ipsecTunnel" +#define LN_ipsecTunnel "IPSec Tunnel" +#define NID_ipsecTunnel 295 +#define OBJ_ipsecTunnel OBJ_id_kp,6L + +#define SN_ipsecUser "ipsecUser" +#define LN_ipsecUser "IPSec User" +#define NID_ipsecUser 296 +#define OBJ_ipsecUser OBJ_id_kp,7L + +#define SN_time_stamp "timeStamping" +#define LN_time_stamp "Time Stamping" +#define NID_time_stamp 133 +#define OBJ_time_stamp OBJ_id_kp,8L + +#define SN_OCSP_sign "OCSPSigning" +#define LN_OCSP_sign "OCSP Signing" +#define NID_OCSP_sign 180 +#define OBJ_OCSP_sign OBJ_id_kp,9L + +#define SN_dvcs "DVCS" +#define LN_dvcs "dvcs" +#define NID_dvcs 297 +#define OBJ_dvcs OBJ_id_kp,10L + +#define SN_id_it_caProtEncCert "id-it-caProtEncCert" +#define NID_id_it_caProtEncCert 298 +#define OBJ_id_it_caProtEncCert OBJ_id_it,1L + +#define SN_id_it_signKeyPairTypes "id-it-signKeyPairTypes" +#define NID_id_it_signKeyPairTypes 299 +#define OBJ_id_it_signKeyPairTypes OBJ_id_it,2L + +#define SN_id_it_encKeyPairTypes "id-it-encKeyPairTypes" +#define NID_id_it_encKeyPairTypes 300 +#define OBJ_id_it_encKeyPairTypes OBJ_id_it,3L + +#define SN_id_it_preferredSymmAlg "id-it-preferredSymmAlg" +#define NID_id_it_preferredSymmAlg 301 +#define OBJ_id_it_preferredSymmAlg OBJ_id_it,4L + +#define SN_id_it_caKeyUpdateInfo "id-it-caKeyUpdateInfo" +#define NID_id_it_caKeyUpdateInfo 302 +#define OBJ_id_it_caKeyUpdateInfo OBJ_id_it,5L + +#define SN_id_it_currentCRL "id-it-currentCRL" +#define NID_id_it_currentCRL 303 +#define OBJ_id_it_currentCRL OBJ_id_it,6L + +#define SN_id_it_unsupportedOIDs "id-it-unsupportedOIDs" +#define NID_id_it_unsupportedOIDs 304 +#define OBJ_id_it_unsupportedOIDs OBJ_id_it,7L + +#define SN_id_it_subscriptionRequest "id-it-subscriptionRequest" +#define NID_id_it_subscriptionRequest 305 +#define OBJ_id_it_subscriptionRequest OBJ_id_it,8L + +#define SN_id_it_subscriptionResponse "id-it-subscriptionResponse" +#define NID_id_it_subscriptionResponse 306 +#define OBJ_id_it_subscriptionResponse OBJ_id_it,9L + +#define SN_id_it_keyPairParamReq "id-it-keyPairParamReq" +#define NID_id_it_keyPairParamReq 307 +#define OBJ_id_it_keyPairParamReq OBJ_id_it,10L + +#define SN_id_it_keyPairParamRep "id-it-keyPairParamRep" +#define NID_id_it_keyPairParamRep 308 +#define OBJ_id_it_keyPairParamRep OBJ_id_it,11L + +#define SN_id_it_revPassphrase "id-it-revPassphrase" +#define NID_id_it_revPassphrase 309 +#define OBJ_id_it_revPassphrase OBJ_id_it,12L + +#define SN_id_it_implicitConfirm "id-it-implicitConfirm" +#define NID_id_it_implicitConfirm 310 +#define OBJ_id_it_implicitConfirm OBJ_id_it,13L + +#define SN_id_it_confirmWaitTime "id-it-confirmWaitTime" +#define NID_id_it_confirmWaitTime 311 +#define OBJ_id_it_confirmWaitTime OBJ_id_it,14L + +#define SN_id_it_origPKIMessage "id-it-origPKIMessage" +#define NID_id_it_origPKIMessage 312 +#define OBJ_id_it_origPKIMessage OBJ_id_it,15L + +#define SN_id_regCtrl "id-regCtrl" +#define NID_id_regCtrl 313 +#define OBJ_id_regCtrl OBJ_id_pkip,1L + +#define SN_id_regInfo "id-regInfo" +#define NID_id_regInfo 314 +#define OBJ_id_regInfo OBJ_id_pkip,2L + +#define SN_id_regCtrl_regToken "id-regCtrl-regToken" +#define NID_id_regCtrl_regToken 315 +#define OBJ_id_regCtrl_regToken OBJ_id_regCtrl,1L + +#define SN_id_regCtrl_authenticator "id-regCtrl-authenticator" +#define NID_id_regCtrl_authenticator 316 +#define OBJ_id_regCtrl_authenticator OBJ_id_regCtrl,2L + +#define SN_id_regCtrl_pkiPublicationInfo "id-regCtrl-pkiPublicationInfo" +#define NID_id_regCtrl_pkiPublicationInfo 317 +#define OBJ_id_regCtrl_pkiPublicationInfo OBJ_id_regCtrl,3L + +#define SN_id_regCtrl_pkiArchiveOptions "id-regCtrl-pkiArchiveOptions" +#define NID_id_regCtrl_pkiArchiveOptions 318 +#define OBJ_id_regCtrl_pkiArchiveOptions OBJ_id_regCtrl,4L + +#define SN_id_regCtrl_oldCertID "id-regCtrl-oldCertID" +#define NID_id_regCtrl_oldCertID 319 +#define OBJ_id_regCtrl_oldCertID OBJ_id_regCtrl,5L + +#define SN_id_regCtrl_protocolEncrKey "id-regCtrl-protocolEncrKey" +#define NID_id_regCtrl_protocolEncrKey 320 +#define OBJ_id_regCtrl_protocolEncrKey OBJ_id_regCtrl,6L + +#define SN_id_regInfo_utf8Pairs "id-regInfo-utf8Pairs" +#define NID_id_regInfo_utf8Pairs 321 +#define OBJ_id_regInfo_utf8Pairs OBJ_id_regInfo,1L + +#define SN_id_regInfo_certReq "id-regInfo-certReq" +#define NID_id_regInfo_certReq 322 +#define OBJ_id_regInfo_certReq OBJ_id_regInfo,2L + +#define SN_id_alg_des40 "id-alg-des40" +#define NID_id_alg_des40 323 +#define OBJ_id_alg_des40 OBJ_id_alg,1L + +#define SN_id_alg_noSignature "id-alg-noSignature" +#define NID_id_alg_noSignature 324 +#define OBJ_id_alg_noSignature OBJ_id_alg,2L + +#define SN_id_alg_dh_sig_hmac_sha1 "id-alg-dh-sig-hmac-sha1" +#define NID_id_alg_dh_sig_hmac_sha1 325 +#define OBJ_id_alg_dh_sig_hmac_sha1 OBJ_id_alg,3L + +#define SN_id_alg_dh_pop "id-alg-dh-pop" +#define NID_id_alg_dh_pop 326 +#define OBJ_id_alg_dh_pop OBJ_id_alg,4L + +#define SN_id_cmc_statusInfo "id-cmc-statusInfo" +#define NID_id_cmc_statusInfo 327 +#define OBJ_id_cmc_statusInfo OBJ_id_cmc,1L + +#define SN_id_cmc_identification "id-cmc-identification" +#define NID_id_cmc_identification 328 +#define OBJ_id_cmc_identification OBJ_id_cmc,2L + +#define SN_id_cmc_identityProof "id-cmc-identityProof" +#define NID_id_cmc_identityProof 329 +#define OBJ_id_cmc_identityProof OBJ_id_cmc,3L + +#define SN_id_cmc_dataReturn "id-cmc-dataReturn" +#define NID_id_cmc_dataReturn 330 +#define OBJ_id_cmc_dataReturn OBJ_id_cmc,4L + +#define SN_id_cmc_transactionId "id-cmc-transactionId" +#define NID_id_cmc_transactionId 331 +#define OBJ_id_cmc_transactionId OBJ_id_cmc,5L + +#define SN_id_cmc_senderNonce "id-cmc-senderNonce" +#define NID_id_cmc_senderNonce 332 +#define OBJ_id_cmc_senderNonce OBJ_id_cmc,6L + +#define SN_id_cmc_recipientNonce "id-cmc-recipientNonce" +#define NID_id_cmc_recipientNonce 333 +#define OBJ_id_cmc_recipientNonce OBJ_id_cmc,7L + +#define SN_id_cmc_addExtensions "id-cmc-addExtensions" +#define NID_id_cmc_addExtensions 334 +#define OBJ_id_cmc_addExtensions OBJ_id_cmc,8L + +#define SN_id_cmc_encryptedPOP "id-cmc-encryptedPOP" +#define NID_id_cmc_encryptedPOP 335 +#define OBJ_id_cmc_encryptedPOP OBJ_id_cmc,9L + +#define SN_id_cmc_decryptedPOP "id-cmc-decryptedPOP" +#define NID_id_cmc_decryptedPOP 336 +#define OBJ_id_cmc_decryptedPOP OBJ_id_cmc,10L + +#define SN_id_cmc_lraPOPWitness "id-cmc-lraPOPWitness" +#define NID_id_cmc_lraPOPWitness 337 +#define OBJ_id_cmc_lraPOPWitness OBJ_id_cmc,11L + +#define SN_id_cmc_getCert "id-cmc-getCert" +#define NID_id_cmc_getCert 338 +#define OBJ_id_cmc_getCert OBJ_id_cmc,15L + +#define SN_id_cmc_getCRL "id-cmc-getCRL" +#define NID_id_cmc_getCRL 339 +#define OBJ_id_cmc_getCRL OBJ_id_cmc,16L + +#define SN_id_cmc_revokeRequest "id-cmc-revokeRequest" +#define NID_id_cmc_revokeRequest 340 +#define OBJ_id_cmc_revokeRequest OBJ_id_cmc,17L + +#define SN_id_cmc_regInfo "id-cmc-regInfo" +#define NID_id_cmc_regInfo 341 +#define OBJ_id_cmc_regInfo OBJ_id_cmc,18L + +#define SN_id_cmc_responseInfo "id-cmc-responseInfo" +#define NID_id_cmc_responseInfo 342 +#define OBJ_id_cmc_responseInfo OBJ_id_cmc,19L + +#define SN_id_cmc_queryPending "id-cmc-queryPending" +#define NID_id_cmc_queryPending 343 +#define OBJ_id_cmc_queryPending OBJ_id_cmc,21L + +#define SN_id_cmc_popLinkRandom "id-cmc-popLinkRandom" +#define NID_id_cmc_popLinkRandom 344 +#define OBJ_id_cmc_popLinkRandom OBJ_id_cmc,22L + +#define SN_id_cmc_popLinkWitness "id-cmc-popLinkWitness" +#define NID_id_cmc_popLinkWitness 345 +#define OBJ_id_cmc_popLinkWitness OBJ_id_cmc,23L + +#define SN_id_cmc_confirmCertAcceptance "id-cmc-confirmCertAcceptance" +#define NID_id_cmc_confirmCertAcceptance 346 +#define OBJ_id_cmc_confirmCertAcceptance OBJ_id_cmc,24L + +#define SN_id_on_personalData "id-on-personalData" +#define NID_id_on_personalData 347 +#define OBJ_id_on_personalData OBJ_id_on,1L + +#define SN_id_pda_dateOfBirth "id-pda-dateOfBirth" +#define NID_id_pda_dateOfBirth 348 +#define OBJ_id_pda_dateOfBirth OBJ_id_pda,1L + +#define SN_id_pda_placeOfBirth "id-pda-placeOfBirth" +#define NID_id_pda_placeOfBirth 349 +#define OBJ_id_pda_placeOfBirth OBJ_id_pda,2L + +#define SN_id_pda_gender "id-pda-gender" +#define NID_id_pda_gender 351 +#define OBJ_id_pda_gender OBJ_id_pda,3L + +#define SN_id_pda_countryOfCitizenship "id-pda-countryOfCitizenship" +#define NID_id_pda_countryOfCitizenship 352 +#define OBJ_id_pda_countryOfCitizenship OBJ_id_pda,4L + +#define SN_id_pda_countryOfResidence "id-pda-countryOfResidence" +#define NID_id_pda_countryOfResidence 353 +#define OBJ_id_pda_countryOfResidence OBJ_id_pda,5L + +#define SN_id_aca_authenticationInfo "id-aca-authenticationInfo" +#define NID_id_aca_authenticationInfo 354 +#define OBJ_id_aca_authenticationInfo OBJ_id_aca,1L + +#define SN_id_aca_accessIdentity "id-aca-accessIdentity" +#define NID_id_aca_accessIdentity 355 +#define OBJ_id_aca_accessIdentity OBJ_id_aca,2L + +#define SN_id_aca_chargingIdentity "id-aca-chargingIdentity" +#define NID_id_aca_chargingIdentity 356 +#define OBJ_id_aca_chargingIdentity OBJ_id_aca,3L + +#define SN_id_aca_group "id-aca-group" +#define NID_id_aca_group 357 +#define OBJ_id_aca_group OBJ_id_aca,4L + +#define SN_id_aca_role "id-aca-role" +#define NID_id_aca_role 358 +#define OBJ_id_aca_role OBJ_id_aca,5L + +#define SN_id_aca_encAttrs "id-aca-encAttrs" +#define NID_id_aca_encAttrs 399 +#define OBJ_id_aca_encAttrs OBJ_id_aca,6L + +#define SN_id_qcs_pkixQCSyntax_v1 "id-qcs-pkixQCSyntax-v1" +#define NID_id_qcs_pkixQCSyntax_v1 359 +#define OBJ_id_qcs_pkixQCSyntax_v1 OBJ_id_qcs,1L + +#define SN_id_cct_crs "id-cct-crs" +#define NID_id_cct_crs 360 +#define OBJ_id_cct_crs OBJ_id_cct,1L + +#define SN_id_cct_PKIData "id-cct-PKIData" +#define NID_id_cct_PKIData 361 +#define OBJ_id_cct_PKIData OBJ_id_cct,2L + +#define SN_id_cct_PKIResponse "id-cct-PKIResponse" +#define NID_id_cct_PKIResponse 362 +#define OBJ_id_cct_PKIResponse OBJ_id_cct,3L + +#define SN_id_ppl_anyLanguage "id-ppl-anyLanguage" +#define LN_id_ppl_anyLanguage "Any language" +#define NID_id_ppl_anyLanguage 664 +#define OBJ_id_ppl_anyLanguage OBJ_id_ppl,0L + +#define SN_id_ppl_inheritAll "id-ppl-inheritAll" +#define LN_id_ppl_inheritAll "Inherit all" +#define NID_id_ppl_inheritAll 665 +#define OBJ_id_ppl_inheritAll OBJ_id_ppl,1L + +#define SN_Independent "id-ppl-independent" +#define LN_Independent "Independent" +#define NID_Independent 667 +#define OBJ_Independent OBJ_id_ppl,2L + +#define SN_ad_OCSP "OCSP" +#define LN_ad_OCSP "OCSP" +#define NID_ad_OCSP 178 +#define OBJ_ad_OCSP OBJ_id_ad,1L + +#define SN_ad_ca_issuers "caIssuers" +#define LN_ad_ca_issuers "CA Issuers" +#define NID_ad_ca_issuers 179 +#define OBJ_ad_ca_issuers OBJ_id_ad,2L + +#define SN_ad_timeStamping "ad_timestamping" +#define LN_ad_timeStamping "AD Time Stamping" +#define NID_ad_timeStamping 363 +#define OBJ_ad_timeStamping OBJ_id_ad,3L + +#define SN_ad_dvcs "AD_DVCS" +#define LN_ad_dvcs "ad dvcs" +#define NID_ad_dvcs 364 +#define OBJ_ad_dvcs OBJ_id_ad,4L + +#define OBJ_id_pkix_OCSP OBJ_ad_OCSP + +#define SN_id_pkix_OCSP_basic "basicOCSPResponse" +#define LN_id_pkix_OCSP_basic "Basic OCSP Response" +#define NID_id_pkix_OCSP_basic 365 +#define OBJ_id_pkix_OCSP_basic OBJ_id_pkix_OCSP,1L + +#define SN_id_pkix_OCSP_Nonce "Nonce" +#define LN_id_pkix_OCSP_Nonce "OCSP Nonce" +#define NID_id_pkix_OCSP_Nonce 366 +#define OBJ_id_pkix_OCSP_Nonce OBJ_id_pkix_OCSP,2L + +#define SN_id_pkix_OCSP_CrlID "CrlID" +#define LN_id_pkix_OCSP_CrlID "OCSP CRL ID" +#define NID_id_pkix_OCSP_CrlID 367 +#define OBJ_id_pkix_OCSP_CrlID OBJ_id_pkix_OCSP,3L + +#define SN_id_pkix_OCSP_acceptableResponses "acceptableResponses" +#define LN_id_pkix_OCSP_acceptableResponses "Acceptable OCSP Responses" +#define NID_id_pkix_OCSP_acceptableResponses 368 +#define OBJ_id_pkix_OCSP_acceptableResponses OBJ_id_pkix_OCSP,4L + +#define SN_id_pkix_OCSP_noCheck "noCheck" +#define LN_id_pkix_OCSP_noCheck "OCSP No Check" +#define NID_id_pkix_OCSP_noCheck 369 +#define OBJ_id_pkix_OCSP_noCheck OBJ_id_pkix_OCSP,5L + +#define SN_id_pkix_OCSP_archiveCutoff "archiveCutoff" +#define LN_id_pkix_OCSP_archiveCutoff "OCSP Archive Cutoff" +#define NID_id_pkix_OCSP_archiveCutoff 370 +#define OBJ_id_pkix_OCSP_archiveCutoff OBJ_id_pkix_OCSP,6L + +#define SN_id_pkix_OCSP_serviceLocator "serviceLocator" +#define LN_id_pkix_OCSP_serviceLocator "OCSP Service Locator" +#define NID_id_pkix_OCSP_serviceLocator 371 +#define OBJ_id_pkix_OCSP_serviceLocator OBJ_id_pkix_OCSP,7L + +#define SN_id_pkix_OCSP_extendedStatus "extendedStatus" +#define LN_id_pkix_OCSP_extendedStatus "Extended OCSP Status" +#define NID_id_pkix_OCSP_extendedStatus 372 +#define OBJ_id_pkix_OCSP_extendedStatus OBJ_id_pkix_OCSP,8L + +#define SN_id_pkix_OCSP_valid "valid" +#define NID_id_pkix_OCSP_valid 373 +#define OBJ_id_pkix_OCSP_valid OBJ_id_pkix_OCSP,9L + +#define SN_id_pkix_OCSP_path "path" +#define NID_id_pkix_OCSP_path 374 +#define OBJ_id_pkix_OCSP_path OBJ_id_pkix_OCSP,10L + +#define SN_id_pkix_OCSP_trustRoot "trustRoot" +#define LN_id_pkix_OCSP_trustRoot "Trust Root" +#define NID_id_pkix_OCSP_trustRoot 375 +#define OBJ_id_pkix_OCSP_trustRoot OBJ_id_pkix_OCSP,11L + +#define SN_algorithm "algorithm" +#define LN_algorithm "algorithm" +#define NID_algorithm 376 +#define OBJ_algorithm 1L,3L,14L,3L,2L + +#define SN_md5WithRSA "RSA-NP-MD5" +#define LN_md5WithRSA "md5WithRSA" +#define NID_md5WithRSA 104 +#define OBJ_md5WithRSA OBJ_algorithm,3L + +#define SN_des_ecb "DES-ECB" +#define LN_des_ecb "des-ecb" +#define NID_des_ecb 29 +#define OBJ_des_ecb OBJ_algorithm,6L + +#define SN_des_cbc "DES-CBC" +#define LN_des_cbc "des-cbc" +#define NID_des_cbc 31 +#define OBJ_des_cbc OBJ_algorithm,7L + +#define SN_des_ofb64 "DES-OFB" +#define LN_des_ofb64 "des-ofb" +#define NID_des_ofb64 45 +#define OBJ_des_ofb64 OBJ_algorithm,8L + +#define SN_des_cfb64 "DES-CFB" +#define LN_des_cfb64 "des-cfb" +#define NID_des_cfb64 30 +#define OBJ_des_cfb64 OBJ_algorithm,9L + +#define SN_rsaSignature "rsaSignature" +#define NID_rsaSignature 377 +#define OBJ_rsaSignature OBJ_algorithm,11L + +#define SN_dsa_2 "DSA-old" +#define LN_dsa_2 "dsaEncryption-old" +#define NID_dsa_2 67 +#define OBJ_dsa_2 OBJ_algorithm,12L + +#define SN_dsaWithSHA "DSA-SHA" +#define LN_dsaWithSHA "dsaWithSHA" +#define NID_dsaWithSHA 66 +#define OBJ_dsaWithSHA OBJ_algorithm,13L + +#define SN_shaWithRSAEncryption "RSA-SHA" +#define LN_shaWithRSAEncryption "shaWithRSAEncryption" +#define NID_shaWithRSAEncryption 42 +#define OBJ_shaWithRSAEncryption OBJ_algorithm,15L + +#define SN_des_ede_ecb "DES-EDE" +#define LN_des_ede_ecb "des-ede" +#define NID_des_ede_ecb 32 +#define OBJ_des_ede_ecb OBJ_algorithm,17L + +#define SN_des_ede3_ecb "DES-EDE3" +#define LN_des_ede3_ecb "des-ede3" +#define NID_des_ede3_ecb 33 + +#define SN_des_ede_cbc "DES-EDE-CBC" +#define LN_des_ede_cbc "des-ede-cbc" +#define NID_des_ede_cbc 43 + +#define SN_des_ede_cfb64 "DES-EDE-CFB" +#define LN_des_ede_cfb64 "des-ede-cfb" +#define NID_des_ede_cfb64 60 + +#define SN_des_ede3_cfb64 "DES-EDE3-CFB" +#define LN_des_ede3_cfb64 "des-ede3-cfb" +#define NID_des_ede3_cfb64 61 + +#define SN_des_ede_ofb64 "DES-EDE-OFB" +#define LN_des_ede_ofb64 "des-ede-ofb" +#define NID_des_ede_ofb64 62 + +#define SN_des_ede3_ofb64 "DES-EDE3-OFB" +#define LN_des_ede3_ofb64 "des-ede3-ofb" +#define NID_des_ede3_ofb64 63 + +#define SN_desx_cbc "DESX-CBC" +#define LN_desx_cbc "desx-cbc" +#define NID_desx_cbc 80 + +#define SN_sha "SHA" +#define LN_sha "sha" +#define NID_sha 41 +#define OBJ_sha OBJ_algorithm,18L + +#define SN_sha1 "SHA1" +#define LN_sha1 "sha1" +#define NID_sha1 64 +#define OBJ_sha1 OBJ_algorithm,26L + +#define SN_dsaWithSHA1_2 "DSA-SHA1-old" +#define LN_dsaWithSHA1_2 "dsaWithSHA1-old" +#define NID_dsaWithSHA1_2 70 +#define OBJ_dsaWithSHA1_2 OBJ_algorithm,27L + +#define SN_sha1WithRSA "RSA-SHA1-2" +#define LN_sha1WithRSA "sha1WithRSA" +#define NID_sha1WithRSA 115 +#define OBJ_sha1WithRSA OBJ_algorithm,29L + +#define SN_ripemd160 "RIPEMD160" +#define LN_ripemd160 "ripemd160" +#define NID_ripemd160 117 +#define OBJ_ripemd160 1L,3L,36L,3L,2L,1L + +#define SN_ripemd160WithRSA "RSA-RIPEMD160" +#define LN_ripemd160WithRSA "ripemd160WithRSA" +#define NID_ripemd160WithRSA 119 +#define OBJ_ripemd160WithRSA 1L,3L,36L,3L,3L,1L,2L + +#define SN_sxnet "SXNetID" +#define LN_sxnet "Strong Extranet ID" +#define NID_sxnet 143 +#define OBJ_sxnet 1L,3L,101L,1L,4L,1L + +#define SN_X500 "X500" +#define LN_X500 "directory services (X.500)" +#define NID_X500 11 +#define OBJ_X500 2L,5L + +#define SN_X509 "X509" +#define NID_X509 12 +#define OBJ_X509 OBJ_X500,4L + +#define SN_commonName "CN" +#define LN_commonName "commonName" +#define NID_commonName 13 +#define OBJ_commonName OBJ_X509,3L + +#define SN_surname "SN" +#define LN_surname "surname" +#define NID_surname 100 +#define OBJ_surname OBJ_X509,4L + +#define LN_serialNumber "serialNumber" +#define NID_serialNumber 105 +#define OBJ_serialNumber OBJ_X509,5L + +#define SN_countryName "C" +#define LN_countryName "countryName" +#define NID_countryName 14 +#define OBJ_countryName OBJ_X509,6L + +#define SN_localityName "L" +#define LN_localityName "localityName" +#define NID_localityName 15 +#define OBJ_localityName OBJ_X509,7L + +#define SN_stateOrProvinceName "ST" +#define LN_stateOrProvinceName "stateOrProvinceName" +#define NID_stateOrProvinceName 16 +#define OBJ_stateOrProvinceName OBJ_X509,8L + +#define LN_streetAddress "streetAddress" +#define NID_streetAddress 660 +#define OBJ_streetAddress OBJ_X509,9L + +#define SN_organizationName "O" +#define LN_organizationName "organizationName" +#define NID_organizationName 17 +#define OBJ_organizationName OBJ_X509,10L + +#define SN_organizationalUnitName "OU" +#define LN_organizationalUnitName "organizationalUnitName" +#define NID_organizationalUnitName 18 +#define OBJ_organizationalUnitName OBJ_X509,11L + +#define LN_title "title" +#define NID_title 106 +#define OBJ_title OBJ_X509,12L + +#define LN_description "description" +#define NID_description 107 +#define OBJ_description OBJ_X509,13L + +#define LN_postalCode "postalCode" +#define NID_postalCode 661 +#define OBJ_postalCode OBJ_X509,17L + +#define SN_name "name" +#define LN_name "name" +#define NID_name 173 +#define OBJ_name OBJ_X509,41L + +#define SN_givenName "GN" +#define LN_givenName "givenName" +#define NID_givenName 99 +#define OBJ_givenName OBJ_X509,42L + +#define LN_initials "initials" +#define NID_initials 101 +#define OBJ_initials OBJ_X509,43L + +#define LN_generationQualifier "generationQualifier" +#define NID_generationQualifier 509 +#define OBJ_generationQualifier OBJ_X509,44L + +#define LN_x500UniqueIdentifier "x500UniqueIdentifier" +#define NID_x500UniqueIdentifier 503 +#define OBJ_x500UniqueIdentifier OBJ_X509,45L + +#define SN_dnQualifier "dnQualifier" +#define LN_dnQualifier "dnQualifier" +#define NID_dnQualifier 174 +#define OBJ_dnQualifier OBJ_X509,46L + +#define LN_pseudonym "pseudonym" +#define NID_pseudonym 510 +#define OBJ_pseudonym OBJ_X509,65L + +#define SN_role "role" +#define LN_role "role" +#define NID_role 400 +#define OBJ_role OBJ_X509,72L + +#define SN_X500algorithms "X500algorithms" +#define LN_X500algorithms "directory services - algorithms" +#define NID_X500algorithms 378 +#define OBJ_X500algorithms OBJ_X500,8L + +#define SN_rsa "RSA" +#define LN_rsa "rsa" +#define NID_rsa 19 +#define OBJ_rsa OBJ_X500algorithms,1L,1L + +#define SN_mdc2WithRSA "RSA-MDC2" +#define LN_mdc2WithRSA "mdc2WithRSA" +#define NID_mdc2WithRSA 96 +#define OBJ_mdc2WithRSA OBJ_X500algorithms,3L,100L + +#define SN_mdc2 "MDC2" +#define LN_mdc2 "mdc2" +#define NID_mdc2 95 +#define OBJ_mdc2 OBJ_X500algorithms,3L,101L + +#define SN_id_ce "id-ce" +#define NID_id_ce 81 +#define OBJ_id_ce OBJ_X500,29L + +#define SN_subject_key_identifier "subjectKeyIdentifier" +#define LN_subject_key_identifier "X509v3 Subject Key Identifier" +#define NID_subject_key_identifier 82 +#define OBJ_subject_key_identifier OBJ_id_ce,14L + +#define SN_key_usage "keyUsage" +#define LN_key_usage "X509v3 Key Usage" +#define NID_key_usage 83 +#define OBJ_key_usage OBJ_id_ce,15L + +#define SN_private_key_usage_period "privateKeyUsagePeriod" +#define LN_private_key_usage_period "X509v3 Private Key Usage Period" +#define NID_private_key_usage_period 84 +#define OBJ_private_key_usage_period OBJ_id_ce,16L + +#define SN_subject_alt_name "subjectAltName" +#define LN_subject_alt_name "X509v3 Subject Alternative Name" +#define NID_subject_alt_name 85 +#define OBJ_subject_alt_name OBJ_id_ce,17L + +#define SN_issuer_alt_name "issuerAltName" +#define LN_issuer_alt_name "X509v3 Issuer Alternative Name" +#define NID_issuer_alt_name 86 +#define OBJ_issuer_alt_name OBJ_id_ce,18L + +#define SN_basic_constraints "basicConstraints" +#define LN_basic_constraints "X509v3 Basic Constraints" +#define NID_basic_constraints 87 +#define OBJ_basic_constraints OBJ_id_ce,19L + +#define SN_crl_number "crlNumber" +#define LN_crl_number "X509v3 CRL Number" +#define NID_crl_number 88 +#define OBJ_crl_number OBJ_id_ce,20L + +#define SN_crl_reason "CRLReason" +#define LN_crl_reason "X509v3 CRL Reason Code" +#define NID_crl_reason 141 +#define OBJ_crl_reason OBJ_id_ce,21L + +#define SN_invalidity_date "invalidityDate" +#define LN_invalidity_date "Invalidity Date" +#define NID_invalidity_date 142 +#define OBJ_invalidity_date OBJ_id_ce,24L + +#define SN_delta_crl "deltaCRL" +#define LN_delta_crl "X509v3 Delta CRL Indicator" +#define NID_delta_crl 140 +#define OBJ_delta_crl OBJ_id_ce,27L + +#define SN_name_constraints "nameConstraints" +#define LN_name_constraints "X509v3 Name Constraints" +#define NID_name_constraints 666 +#define OBJ_name_constraints OBJ_id_ce,30L + +#define SN_crl_distribution_points "crlDistributionPoints" +#define LN_crl_distribution_points "X509v3 CRL Distribution Points" +#define NID_crl_distribution_points 103 +#define OBJ_crl_distribution_points OBJ_id_ce,31L + +#define SN_certificate_policies "certificatePolicies" +#define LN_certificate_policies "X509v3 Certificate Policies" +#define NID_certificate_policies 89 +#define OBJ_certificate_policies OBJ_id_ce,32L + +#define SN_any_policy "anyPolicy" +#define LN_any_policy "X509v3 Any Policy" +#define NID_any_policy 746 +#define OBJ_any_policy OBJ_certificate_policies,0L + +#define SN_policy_mappings "policyMappings" +#define LN_policy_mappings "X509v3 Policy Mappings" +#define NID_policy_mappings 747 +#define OBJ_policy_mappings OBJ_id_ce,33L + +#define SN_authority_key_identifier "authorityKeyIdentifier" +#define LN_authority_key_identifier "X509v3 Authority Key Identifier" +#define NID_authority_key_identifier 90 +#define OBJ_authority_key_identifier OBJ_id_ce,35L + +#define SN_policy_constraints "policyConstraints" +#define LN_policy_constraints "X509v3 Policy Constraints" +#define NID_policy_constraints 401 +#define OBJ_policy_constraints OBJ_id_ce,36L + +#define SN_ext_key_usage "extendedKeyUsage" +#define LN_ext_key_usage "X509v3 Extended Key Usage" +#define NID_ext_key_usage 126 +#define OBJ_ext_key_usage OBJ_id_ce,37L + +#define SN_inhibit_any_policy "inhibitAnyPolicy" +#define LN_inhibit_any_policy "X509v3 Inhibit Any Policy" +#define NID_inhibit_any_policy 748 +#define OBJ_inhibit_any_policy OBJ_id_ce,54L + +#define SN_target_information "targetInformation" +#define LN_target_information "X509v3 AC Targeting" +#define NID_target_information 402 +#define OBJ_target_information OBJ_id_ce,55L + +#define SN_no_rev_avail "noRevAvail" +#define LN_no_rev_avail "X509v3 No Revocation Available" +#define NID_no_rev_avail 403 +#define OBJ_no_rev_avail OBJ_id_ce,56L + +#define SN_netscape "Netscape" +#define LN_netscape "Netscape Communications Corp." +#define NID_netscape 57 +#define OBJ_netscape 2L,16L,840L,1L,113730L + +#define SN_netscape_cert_extension "nsCertExt" +#define LN_netscape_cert_extension "Netscape Certificate Extension" +#define NID_netscape_cert_extension 58 +#define OBJ_netscape_cert_extension OBJ_netscape,1L + +#define SN_netscape_data_type "nsDataType" +#define LN_netscape_data_type "Netscape Data Type" +#define NID_netscape_data_type 59 +#define OBJ_netscape_data_type OBJ_netscape,2L + +#define SN_netscape_cert_type "nsCertType" +#define LN_netscape_cert_type "Netscape Cert Type" +#define NID_netscape_cert_type 71 +#define OBJ_netscape_cert_type OBJ_netscape_cert_extension,1L + +#define SN_netscape_base_url "nsBaseUrl" +#define LN_netscape_base_url "Netscape Base Url" +#define NID_netscape_base_url 72 +#define OBJ_netscape_base_url OBJ_netscape_cert_extension,2L + +#define SN_netscape_revocation_url "nsRevocationUrl" +#define LN_netscape_revocation_url "Netscape Revocation Url" +#define NID_netscape_revocation_url 73 +#define OBJ_netscape_revocation_url OBJ_netscape_cert_extension,3L + +#define SN_netscape_ca_revocation_url "nsCaRevocationUrl" +#define LN_netscape_ca_revocation_url "Netscape CA Revocation Url" +#define NID_netscape_ca_revocation_url 74 +#define OBJ_netscape_ca_revocation_url OBJ_netscape_cert_extension,4L + +#define SN_netscape_renewal_url "nsRenewalUrl" +#define LN_netscape_renewal_url "Netscape Renewal Url" +#define NID_netscape_renewal_url 75 +#define OBJ_netscape_renewal_url OBJ_netscape_cert_extension,7L + +#define SN_netscape_ca_policy_url "nsCaPolicyUrl" +#define LN_netscape_ca_policy_url "Netscape CA Policy Url" +#define NID_netscape_ca_policy_url 76 +#define OBJ_netscape_ca_policy_url OBJ_netscape_cert_extension,8L + +#define SN_netscape_ssl_server_name "nsSslServerName" +#define LN_netscape_ssl_server_name "Netscape SSL Server Name" +#define NID_netscape_ssl_server_name 77 +#define OBJ_netscape_ssl_server_name OBJ_netscape_cert_extension,12L + +#define SN_netscape_comment "nsComment" +#define LN_netscape_comment "Netscape Comment" +#define NID_netscape_comment 78 +#define OBJ_netscape_comment OBJ_netscape_cert_extension,13L + +#define SN_netscape_cert_sequence "nsCertSequence" +#define LN_netscape_cert_sequence "Netscape Certificate Sequence" +#define NID_netscape_cert_sequence 79 +#define OBJ_netscape_cert_sequence OBJ_netscape_data_type,5L + +#define SN_ns_sgc "nsSGC" +#define LN_ns_sgc "Netscape Server Gated Crypto" +#define NID_ns_sgc 139 +#define OBJ_ns_sgc OBJ_netscape,4L,1L + +#define SN_org "ORG" +#define LN_org "org" +#define NID_org 379 +#define OBJ_org OBJ_iso,3L + +#define SN_dod "DOD" +#define LN_dod "dod" +#define NID_dod 380 +#define OBJ_dod OBJ_org,6L + +#define SN_iana "IANA" +#define LN_iana "iana" +#define NID_iana 381 +#define OBJ_iana OBJ_dod,1L + +#define OBJ_internet OBJ_iana + +#define SN_Directory "directory" +#define LN_Directory "Directory" +#define NID_Directory 382 +#define OBJ_Directory OBJ_internet,1L + +#define SN_Management "mgmt" +#define LN_Management "Management" +#define NID_Management 383 +#define OBJ_Management OBJ_internet,2L + +#define SN_Experimental "experimental" +#define LN_Experimental "Experimental" +#define NID_Experimental 384 +#define OBJ_Experimental OBJ_internet,3L + +#define SN_Private "private" +#define LN_Private "Private" +#define NID_Private 385 +#define OBJ_Private OBJ_internet,4L + +#define SN_Security "security" +#define LN_Security "Security" +#define NID_Security 386 +#define OBJ_Security OBJ_internet,5L + +#define SN_SNMPv2 "snmpv2" +#define LN_SNMPv2 "SNMPv2" +#define NID_SNMPv2 387 +#define OBJ_SNMPv2 OBJ_internet,6L + +#define LN_Mail "Mail" +#define NID_Mail 388 +#define OBJ_Mail OBJ_internet,7L + +#define SN_Enterprises "enterprises" +#define LN_Enterprises "Enterprises" +#define NID_Enterprises 389 +#define OBJ_Enterprises OBJ_Private,1L + +#define SN_dcObject "dcobject" +#define LN_dcObject "dcObject" +#define NID_dcObject 390 +#define OBJ_dcObject OBJ_Enterprises,1466L,344L + +#define SN_mime_mhs "mime-mhs" +#define LN_mime_mhs "MIME MHS" +#define NID_mime_mhs 504 +#define OBJ_mime_mhs OBJ_Mail,1L + +#define SN_mime_mhs_headings "mime-mhs-headings" +#define LN_mime_mhs_headings "mime-mhs-headings" +#define NID_mime_mhs_headings 505 +#define OBJ_mime_mhs_headings OBJ_mime_mhs,1L + +#define SN_mime_mhs_bodies "mime-mhs-bodies" +#define LN_mime_mhs_bodies "mime-mhs-bodies" +#define NID_mime_mhs_bodies 506 +#define OBJ_mime_mhs_bodies OBJ_mime_mhs,2L + +#define SN_id_hex_partial_message "id-hex-partial-message" +#define LN_id_hex_partial_message "id-hex-partial-message" +#define NID_id_hex_partial_message 507 +#define OBJ_id_hex_partial_message OBJ_mime_mhs_headings,1L + +#define SN_id_hex_multipart_message "id-hex-multipart-message" +#define LN_id_hex_multipart_message "id-hex-multipart-message" +#define NID_id_hex_multipart_message 508 +#define OBJ_id_hex_multipart_message OBJ_mime_mhs_headings,2L + +#define SN_rle_compression "RLE" +#define LN_rle_compression "run length compression" +#define NID_rle_compression 124 +#define OBJ_rle_compression 1L,1L,1L,1L,666L,1L + +#define SN_zlib_compression "ZLIB" +#define LN_zlib_compression "zlib compression" +#define NID_zlib_compression 125 +#define OBJ_zlib_compression 1L,1L,1L,1L,666L,2L + +#define OBJ_csor 2L,16L,840L,1L,101L,3L + +#define OBJ_nistAlgorithms OBJ_csor,4L + +#define OBJ_aes OBJ_nistAlgorithms,1L + +#define SN_aes_128_ecb "AES-128-ECB" +#define LN_aes_128_ecb "aes-128-ecb" +#define NID_aes_128_ecb 418 +#define OBJ_aes_128_ecb OBJ_aes,1L + +#define SN_aes_128_cbc "AES-128-CBC" +#define LN_aes_128_cbc "aes-128-cbc" +#define NID_aes_128_cbc 419 +#define OBJ_aes_128_cbc OBJ_aes,2L + +#define SN_aes_128_ofb128 "AES-128-OFB" +#define LN_aes_128_ofb128 "aes-128-ofb" +#define NID_aes_128_ofb128 420 +#define OBJ_aes_128_ofb128 OBJ_aes,3L + +#define SN_aes_128_cfb128 "AES-128-CFB" +#define LN_aes_128_cfb128 "aes-128-cfb" +#define NID_aes_128_cfb128 421 +#define OBJ_aes_128_cfb128 OBJ_aes,4L + +#define SN_aes_192_ecb "AES-192-ECB" +#define LN_aes_192_ecb "aes-192-ecb" +#define NID_aes_192_ecb 422 +#define OBJ_aes_192_ecb OBJ_aes,21L + +#define SN_aes_192_cbc "AES-192-CBC" +#define LN_aes_192_cbc "aes-192-cbc" +#define NID_aes_192_cbc 423 +#define OBJ_aes_192_cbc OBJ_aes,22L + +#define SN_aes_192_ofb128 "AES-192-OFB" +#define LN_aes_192_ofb128 "aes-192-ofb" +#define NID_aes_192_ofb128 424 +#define OBJ_aes_192_ofb128 OBJ_aes,23L + +#define SN_aes_192_cfb128 "AES-192-CFB" +#define LN_aes_192_cfb128 "aes-192-cfb" +#define NID_aes_192_cfb128 425 +#define OBJ_aes_192_cfb128 OBJ_aes,24L + +#define SN_aes_256_ecb "AES-256-ECB" +#define LN_aes_256_ecb "aes-256-ecb" +#define NID_aes_256_ecb 426 +#define OBJ_aes_256_ecb OBJ_aes,41L + +#define SN_aes_256_cbc "AES-256-CBC" +#define LN_aes_256_cbc "aes-256-cbc" +#define NID_aes_256_cbc 427 +#define OBJ_aes_256_cbc OBJ_aes,42L + +#define SN_aes_256_ofb128 "AES-256-OFB" +#define LN_aes_256_ofb128 "aes-256-ofb" +#define NID_aes_256_ofb128 428 +#define OBJ_aes_256_ofb128 OBJ_aes,43L + +#define SN_aes_256_cfb128 "AES-256-CFB" +#define LN_aes_256_cfb128 "aes-256-cfb" +#define NID_aes_256_cfb128 429 +#define OBJ_aes_256_cfb128 OBJ_aes,44L + +#define SN_aes_128_cfb1 "AES-128-CFB1" +#define LN_aes_128_cfb1 "aes-128-cfb1" +#define NID_aes_128_cfb1 650 + +#define SN_aes_192_cfb1 "AES-192-CFB1" +#define LN_aes_192_cfb1 "aes-192-cfb1" +#define NID_aes_192_cfb1 651 + +#define SN_aes_256_cfb1 "AES-256-CFB1" +#define LN_aes_256_cfb1 "aes-256-cfb1" +#define NID_aes_256_cfb1 652 + +#define SN_aes_128_cfb8 "AES-128-CFB8" +#define LN_aes_128_cfb8 "aes-128-cfb8" +#define NID_aes_128_cfb8 653 + +#define SN_aes_192_cfb8 "AES-192-CFB8" +#define LN_aes_192_cfb8 "aes-192-cfb8" +#define NID_aes_192_cfb8 654 + +#define SN_aes_256_cfb8 "AES-256-CFB8" +#define LN_aes_256_cfb8 "aes-256-cfb8" +#define NID_aes_256_cfb8 655 + +#define SN_des_cfb1 "DES-CFB1" +#define LN_des_cfb1 "des-cfb1" +#define NID_des_cfb1 656 + +#define SN_des_cfb8 "DES-CFB8" +#define LN_des_cfb8 "des-cfb8" +#define NID_des_cfb8 657 + +#define SN_des_ede3_cfb1 "DES-EDE3-CFB1" +#define LN_des_ede3_cfb1 "des-ede3-cfb1" +#define NID_des_ede3_cfb1 658 + +#define SN_des_ede3_cfb8 "DES-EDE3-CFB8" +#define LN_des_ede3_cfb8 "des-ede3-cfb8" +#define NID_des_ede3_cfb8 659 + +#define OBJ_nist_hashalgs OBJ_nistAlgorithms,2L + +#define SN_sha256 "SHA256" +#define LN_sha256 "sha256" +#define NID_sha256 672 +#define OBJ_sha256 OBJ_nist_hashalgs,1L + +#define SN_sha384 "SHA384" +#define LN_sha384 "sha384" +#define NID_sha384 673 +#define OBJ_sha384 OBJ_nist_hashalgs,2L + +#define SN_sha512 "SHA512" +#define LN_sha512 "sha512" +#define NID_sha512 674 +#define OBJ_sha512 OBJ_nist_hashalgs,3L + +#define SN_sha224 "SHA224" +#define LN_sha224 "sha224" +#define NID_sha224 675 +#define OBJ_sha224 OBJ_nist_hashalgs,4L + +#define SN_hold_instruction_code "holdInstructionCode" +#define LN_hold_instruction_code "Hold Instruction Code" +#define NID_hold_instruction_code 430 +#define OBJ_hold_instruction_code OBJ_id_ce,23L + +#define OBJ_holdInstruction OBJ_X9_57,2L + +#define SN_hold_instruction_none "holdInstructionNone" +#define LN_hold_instruction_none "Hold Instruction None" +#define NID_hold_instruction_none 431 +#define OBJ_hold_instruction_none OBJ_holdInstruction,1L + +#define SN_hold_instruction_call_issuer "holdInstructionCallIssuer" +#define LN_hold_instruction_call_issuer "Hold Instruction Call Issuer" +#define NID_hold_instruction_call_issuer 432 +#define OBJ_hold_instruction_call_issuer OBJ_holdInstruction,2L + +#define SN_hold_instruction_reject "holdInstructionReject" +#define LN_hold_instruction_reject "Hold Instruction Reject" +#define NID_hold_instruction_reject 433 +#define OBJ_hold_instruction_reject OBJ_holdInstruction,3L + +#define SN_data "data" +#define NID_data 434 +#define OBJ_data OBJ_itu_t,9L + +#define SN_pss "pss" +#define NID_pss 435 +#define OBJ_pss OBJ_data,2342L + +#define SN_ucl "ucl" +#define NID_ucl 436 +#define OBJ_ucl OBJ_pss,19200300L + +#define SN_pilot "pilot" +#define NID_pilot 437 +#define OBJ_pilot OBJ_ucl,100L + +#define LN_pilotAttributeType "pilotAttributeType" +#define NID_pilotAttributeType 438 +#define OBJ_pilotAttributeType OBJ_pilot,1L + +#define LN_pilotAttributeSyntax "pilotAttributeSyntax" +#define NID_pilotAttributeSyntax 439 +#define OBJ_pilotAttributeSyntax OBJ_pilot,3L + +#define LN_pilotObjectClass "pilotObjectClass" +#define NID_pilotObjectClass 440 +#define OBJ_pilotObjectClass OBJ_pilot,4L + +#define LN_pilotGroups "pilotGroups" +#define NID_pilotGroups 441 +#define OBJ_pilotGroups OBJ_pilot,10L + +#define LN_iA5StringSyntax "iA5StringSyntax" +#define NID_iA5StringSyntax 442 +#define OBJ_iA5StringSyntax OBJ_pilotAttributeSyntax,4L + +#define LN_caseIgnoreIA5StringSyntax "caseIgnoreIA5StringSyntax" +#define NID_caseIgnoreIA5StringSyntax 443 +#define OBJ_caseIgnoreIA5StringSyntax OBJ_pilotAttributeSyntax,5L + +#define LN_pilotObject "pilotObject" +#define NID_pilotObject 444 +#define OBJ_pilotObject OBJ_pilotObjectClass,3L + +#define LN_pilotPerson "pilotPerson" +#define NID_pilotPerson 445 +#define OBJ_pilotPerson OBJ_pilotObjectClass,4L + +#define SN_account "account" +#define NID_account 446 +#define OBJ_account OBJ_pilotObjectClass,5L + +#define SN_document "document" +#define NID_document 447 +#define OBJ_document OBJ_pilotObjectClass,6L + +#define SN_room "room" +#define NID_room 448 +#define OBJ_room OBJ_pilotObjectClass,7L + +#define LN_documentSeries "documentSeries" +#define NID_documentSeries 449 +#define OBJ_documentSeries OBJ_pilotObjectClass,9L + +#define SN_Domain "domain" +#define LN_Domain "Domain" +#define NID_Domain 392 +#define OBJ_Domain OBJ_pilotObjectClass,13L + +#define LN_rFC822localPart "rFC822localPart" +#define NID_rFC822localPart 450 +#define OBJ_rFC822localPart OBJ_pilotObjectClass,14L + +#define LN_dNSDomain "dNSDomain" +#define NID_dNSDomain 451 +#define OBJ_dNSDomain OBJ_pilotObjectClass,15L + +#define LN_domainRelatedObject "domainRelatedObject" +#define NID_domainRelatedObject 452 +#define OBJ_domainRelatedObject OBJ_pilotObjectClass,17L + +#define LN_friendlyCountry "friendlyCountry" +#define NID_friendlyCountry 453 +#define OBJ_friendlyCountry OBJ_pilotObjectClass,18L + +#define LN_simpleSecurityObject "simpleSecurityObject" +#define NID_simpleSecurityObject 454 +#define OBJ_simpleSecurityObject OBJ_pilotObjectClass,19L + +#define LN_pilotOrganization "pilotOrganization" +#define NID_pilotOrganization 455 +#define OBJ_pilotOrganization OBJ_pilotObjectClass,20L + +#define LN_pilotDSA "pilotDSA" +#define NID_pilotDSA 456 +#define OBJ_pilotDSA OBJ_pilotObjectClass,21L + +#define LN_qualityLabelledData "qualityLabelledData" +#define NID_qualityLabelledData 457 +#define OBJ_qualityLabelledData OBJ_pilotObjectClass,22L + +#define SN_userId "UID" +#define LN_userId "userId" +#define NID_userId 458 +#define OBJ_userId OBJ_pilotAttributeType,1L + +#define LN_textEncodedORAddress "textEncodedORAddress" +#define NID_textEncodedORAddress 459 +#define OBJ_textEncodedORAddress OBJ_pilotAttributeType,2L + +#define SN_rfc822Mailbox "mail" +#define LN_rfc822Mailbox "rfc822Mailbox" +#define NID_rfc822Mailbox 460 +#define OBJ_rfc822Mailbox OBJ_pilotAttributeType,3L + +#define SN_info "info" +#define NID_info 461 +#define OBJ_info OBJ_pilotAttributeType,4L + +#define LN_favouriteDrink "favouriteDrink" +#define NID_favouriteDrink 462 +#define OBJ_favouriteDrink OBJ_pilotAttributeType,5L + +#define LN_roomNumber "roomNumber" +#define NID_roomNumber 463 +#define OBJ_roomNumber OBJ_pilotAttributeType,6L + +#define SN_photo "photo" +#define NID_photo 464 +#define OBJ_photo OBJ_pilotAttributeType,7L + +#define LN_userClass "userClass" +#define NID_userClass 465 +#define OBJ_userClass OBJ_pilotAttributeType,8L + +#define SN_host "host" +#define NID_host 466 +#define OBJ_host OBJ_pilotAttributeType,9L + +#define SN_manager "manager" +#define NID_manager 467 +#define OBJ_manager OBJ_pilotAttributeType,10L + +#define LN_documentIdentifier "documentIdentifier" +#define NID_documentIdentifier 468 +#define OBJ_documentIdentifier OBJ_pilotAttributeType,11L + +#define LN_documentTitle "documentTitle" +#define NID_documentTitle 469 +#define OBJ_documentTitle OBJ_pilotAttributeType,12L + +#define LN_documentVersion "documentVersion" +#define NID_documentVersion 470 +#define OBJ_documentVersion OBJ_pilotAttributeType,13L + +#define LN_documentAuthor "documentAuthor" +#define NID_documentAuthor 471 +#define OBJ_documentAuthor OBJ_pilotAttributeType,14L + +#define LN_documentLocation "documentLocation" +#define NID_documentLocation 472 +#define OBJ_documentLocation OBJ_pilotAttributeType,15L + +#define LN_homeTelephoneNumber "homeTelephoneNumber" +#define NID_homeTelephoneNumber 473 +#define OBJ_homeTelephoneNumber OBJ_pilotAttributeType,20L + +#define SN_secretary "secretary" +#define NID_secretary 474 +#define OBJ_secretary OBJ_pilotAttributeType,21L + +#define LN_otherMailbox "otherMailbox" +#define NID_otherMailbox 475 +#define OBJ_otherMailbox OBJ_pilotAttributeType,22L + +#define LN_lastModifiedTime "lastModifiedTime" +#define NID_lastModifiedTime 476 +#define OBJ_lastModifiedTime OBJ_pilotAttributeType,23L + +#define LN_lastModifiedBy "lastModifiedBy" +#define NID_lastModifiedBy 477 +#define OBJ_lastModifiedBy OBJ_pilotAttributeType,24L + +#define SN_domainComponent "DC" +#define LN_domainComponent "domainComponent" +#define NID_domainComponent 391 +#define OBJ_domainComponent OBJ_pilotAttributeType,25L + +#define LN_aRecord "aRecord" +#define NID_aRecord 478 +#define OBJ_aRecord OBJ_pilotAttributeType,26L + +#define LN_pilotAttributeType27 "pilotAttributeType27" +#define NID_pilotAttributeType27 479 +#define OBJ_pilotAttributeType27 OBJ_pilotAttributeType,27L + +#define LN_mXRecord "mXRecord" +#define NID_mXRecord 480 +#define OBJ_mXRecord OBJ_pilotAttributeType,28L + +#define LN_nSRecord "nSRecord" +#define NID_nSRecord 481 +#define OBJ_nSRecord OBJ_pilotAttributeType,29L + +#define LN_sOARecord "sOARecord" +#define NID_sOARecord 482 +#define OBJ_sOARecord OBJ_pilotAttributeType,30L + +#define LN_cNAMERecord "cNAMERecord" +#define NID_cNAMERecord 483 +#define OBJ_cNAMERecord OBJ_pilotAttributeType,31L + +#define LN_associatedDomain "associatedDomain" +#define NID_associatedDomain 484 +#define OBJ_associatedDomain OBJ_pilotAttributeType,37L + +#define LN_associatedName "associatedName" +#define NID_associatedName 485 +#define OBJ_associatedName OBJ_pilotAttributeType,38L + +#define LN_homePostalAddress "homePostalAddress" +#define NID_homePostalAddress 486 +#define OBJ_homePostalAddress OBJ_pilotAttributeType,39L + +#define LN_personalTitle "personalTitle" +#define NID_personalTitle 487 +#define OBJ_personalTitle OBJ_pilotAttributeType,40L + +#define LN_mobileTelephoneNumber "mobileTelephoneNumber" +#define NID_mobileTelephoneNumber 488 +#define OBJ_mobileTelephoneNumber OBJ_pilotAttributeType,41L + +#define LN_pagerTelephoneNumber "pagerTelephoneNumber" +#define NID_pagerTelephoneNumber 489 +#define OBJ_pagerTelephoneNumber OBJ_pilotAttributeType,42L + +#define LN_friendlyCountryName "friendlyCountryName" +#define NID_friendlyCountryName 490 +#define OBJ_friendlyCountryName OBJ_pilotAttributeType,43L + +#define LN_organizationalStatus "organizationalStatus" +#define NID_organizationalStatus 491 +#define OBJ_organizationalStatus OBJ_pilotAttributeType,45L + +#define LN_janetMailbox "janetMailbox" +#define NID_janetMailbox 492 +#define OBJ_janetMailbox OBJ_pilotAttributeType,46L + +#define LN_mailPreferenceOption "mailPreferenceOption" +#define NID_mailPreferenceOption 493 +#define OBJ_mailPreferenceOption OBJ_pilotAttributeType,47L + +#define LN_buildingName "buildingName" +#define NID_buildingName 494 +#define OBJ_buildingName OBJ_pilotAttributeType,48L + +#define LN_dSAQuality "dSAQuality" +#define NID_dSAQuality 495 +#define OBJ_dSAQuality OBJ_pilotAttributeType,49L + +#define LN_singleLevelQuality "singleLevelQuality" +#define NID_singleLevelQuality 496 +#define OBJ_singleLevelQuality OBJ_pilotAttributeType,50L + +#define LN_subtreeMinimumQuality "subtreeMinimumQuality" +#define NID_subtreeMinimumQuality 497 +#define OBJ_subtreeMinimumQuality OBJ_pilotAttributeType,51L + +#define LN_subtreeMaximumQuality "subtreeMaximumQuality" +#define NID_subtreeMaximumQuality 498 +#define OBJ_subtreeMaximumQuality OBJ_pilotAttributeType,52L + +#define LN_personalSignature "personalSignature" +#define NID_personalSignature 499 +#define OBJ_personalSignature OBJ_pilotAttributeType,53L + +#define LN_dITRedirect "dITRedirect" +#define NID_dITRedirect 500 +#define OBJ_dITRedirect OBJ_pilotAttributeType,54L + +#define SN_audio "audio" +#define NID_audio 501 +#define OBJ_audio OBJ_pilotAttributeType,55L + +#define LN_documentPublisher "documentPublisher" +#define NID_documentPublisher 502 +#define OBJ_documentPublisher OBJ_pilotAttributeType,56L + +#define SN_id_set "id-set" +#define LN_id_set "Secure Electronic Transactions" +#define NID_id_set 512 +#define OBJ_id_set OBJ_international_organizations,42L + +#define SN_set_ctype "set-ctype" +#define LN_set_ctype "content types" +#define NID_set_ctype 513 +#define OBJ_set_ctype OBJ_id_set,0L + +#define SN_set_msgExt "set-msgExt" +#define LN_set_msgExt "message extensions" +#define NID_set_msgExt 514 +#define OBJ_set_msgExt OBJ_id_set,1L + +#define SN_set_attr "set-attr" +#define NID_set_attr 515 +#define OBJ_set_attr OBJ_id_set,3L + +#define SN_set_policy "set-policy" +#define NID_set_policy 516 +#define OBJ_set_policy OBJ_id_set,5L + +#define SN_set_certExt "set-certExt" +#define LN_set_certExt "certificate extensions" +#define NID_set_certExt 517 +#define OBJ_set_certExt OBJ_id_set,7L + +#define SN_set_brand "set-brand" +#define NID_set_brand 518 +#define OBJ_set_brand OBJ_id_set,8L + +#define SN_setct_PANData "setct-PANData" +#define NID_setct_PANData 519 +#define OBJ_setct_PANData OBJ_set_ctype,0L + +#define SN_setct_PANToken "setct-PANToken" +#define NID_setct_PANToken 520 +#define OBJ_setct_PANToken OBJ_set_ctype,1L + +#define SN_setct_PANOnly "setct-PANOnly" +#define NID_setct_PANOnly 521 +#define OBJ_setct_PANOnly OBJ_set_ctype,2L + +#define SN_setct_OIData "setct-OIData" +#define NID_setct_OIData 522 +#define OBJ_setct_OIData OBJ_set_ctype,3L + +#define SN_setct_PI "setct-PI" +#define NID_setct_PI 523 +#define OBJ_setct_PI OBJ_set_ctype,4L + +#define SN_setct_PIData "setct-PIData" +#define NID_setct_PIData 524 +#define OBJ_setct_PIData OBJ_set_ctype,5L + +#define SN_setct_PIDataUnsigned "setct-PIDataUnsigned" +#define NID_setct_PIDataUnsigned 525 +#define OBJ_setct_PIDataUnsigned OBJ_set_ctype,6L + +#define SN_setct_HODInput "setct-HODInput" +#define NID_setct_HODInput 526 +#define OBJ_setct_HODInput OBJ_set_ctype,7L + +#define SN_setct_AuthResBaggage "setct-AuthResBaggage" +#define NID_setct_AuthResBaggage 527 +#define OBJ_setct_AuthResBaggage OBJ_set_ctype,8L + +#define SN_setct_AuthRevReqBaggage "setct-AuthRevReqBaggage" +#define NID_setct_AuthRevReqBaggage 528 +#define OBJ_setct_AuthRevReqBaggage OBJ_set_ctype,9L + +#define SN_setct_AuthRevResBaggage "setct-AuthRevResBaggage" +#define NID_setct_AuthRevResBaggage 529 +#define OBJ_setct_AuthRevResBaggage OBJ_set_ctype,10L + +#define SN_setct_CapTokenSeq "setct-CapTokenSeq" +#define NID_setct_CapTokenSeq 530 +#define OBJ_setct_CapTokenSeq OBJ_set_ctype,11L + +#define SN_setct_PInitResData "setct-PInitResData" +#define NID_setct_PInitResData 531 +#define OBJ_setct_PInitResData OBJ_set_ctype,12L + +#define SN_setct_PI_TBS "setct-PI-TBS" +#define NID_setct_PI_TBS 532 +#define OBJ_setct_PI_TBS OBJ_set_ctype,13L + +#define SN_setct_PResData "setct-PResData" +#define NID_setct_PResData 533 +#define OBJ_setct_PResData OBJ_set_ctype,14L + +#define SN_setct_AuthReqTBS "setct-AuthReqTBS" +#define NID_setct_AuthReqTBS 534 +#define OBJ_setct_AuthReqTBS OBJ_set_ctype,16L + +#define SN_setct_AuthResTBS "setct-AuthResTBS" +#define NID_setct_AuthResTBS 535 +#define OBJ_setct_AuthResTBS OBJ_set_ctype,17L + +#define SN_setct_AuthResTBSX "setct-AuthResTBSX" +#define NID_setct_AuthResTBSX 536 +#define OBJ_setct_AuthResTBSX OBJ_set_ctype,18L + +#define SN_setct_AuthTokenTBS "setct-AuthTokenTBS" +#define NID_setct_AuthTokenTBS 537 +#define OBJ_setct_AuthTokenTBS OBJ_set_ctype,19L + +#define SN_setct_CapTokenData "setct-CapTokenData" +#define NID_setct_CapTokenData 538 +#define OBJ_setct_CapTokenData OBJ_set_ctype,20L + +#define SN_setct_CapTokenTBS "setct-CapTokenTBS" +#define NID_setct_CapTokenTBS 539 +#define OBJ_setct_CapTokenTBS OBJ_set_ctype,21L + +#define SN_setct_AcqCardCodeMsg "setct-AcqCardCodeMsg" +#define NID_setct_AcqCardCodeMsg 540 +#define OBJ_setct_AcqCardCodeMsg OBJ_set_ctype,22L + +#define SN_setct_AuthRevReqTBS "setct-AuthRevReqTBS" +#define NID_setct_AuthRevReqTBS 541 +#define OBJ_setct_AuthRevReqTBS OBJ_set_ctype,23L + +#define SN_setct_AuthRevResData "setct-AuthRevResData" +#define NID_setct_AuthRevResData 542 +#define OBJ_setct_AuthRevResData OBJ_set_ctype,24L + +#define SN_setct_AuthRevResTBS "setct-AuthRevResTBS" +#define NID_setct_AuthRevResTBS 543 +#define OBJ_setct_AuthRevResTBS OBJ_set_ctype,25L + +#define SN_setct_CapReqTBS "setct-CapReqTBS" +#define NID_setct_CapReqTBS 544 +#define OBJ_setct_CapReqTBS OBJ_set_ctype,26L + +#define SN_setct_CapReqTBSX "setct-CapReqTBSX" +#define NID_setct_CapReqTBSX 545 +#define OBJ_setct_CapReqTBSX OBJ_set_ctype,27L + +#define SN_setct_CapResData "setct-CapResData" +#define NID_setct_CapResData 546 +#define OBJ_setct_CapResData OBJ_set_ctype,28L + +#define SN_setct_CapRevReqTBS "setct-CapRevReqTBS" +#define NID_setct_CapRevReqTBS 547 +#define OBJ_setct_CapRevReqTBS OBJ_set_ctype,29L + +#define SN_setct_CapRevReqTBSX "setct-CapRevReqTBSX" +#define NID_setct_CapRevReqTBSX 548 +#define OBJ_setct_CapRevReqTBSX OBJ_set_ctype,30L + +#define SN_setct_CapRevResData "setct-CapRevResData" +#define NID_setct_CapRevResData 549 +#define OBJ_setct_CapRevResData OBJ_set_ctype,31L + +#define SN_setct_CredReqTBS "setct-CredReqTBS" +#define NID_setct_CredReqTBS 550 +#define OBJ_setct_CredReqTBS OBJ_set_ctype,32L + +#define SN_setct_CredReqTBSX "setct-CredReqTBSX" +#define NID_setct_CredReqTBSX 551 +#define OBJ_setct_CredReqTBSX OBJ_set_ctype,33L + +#define SN_setct_CredResData "setct-CredResData" +#define NID_setct_CredResData 552 +#define OBJ_setct_CredResData OBJ_set_ctype,34L + +#define SN_setct_CredRevReqTBS "setct-CredRevReqTBS" +#define NID_setct_CredRevReqTBS 553 +#define OBJ_setct_CredRevReqTBS OBJ_set_ctype,35L + +#define SN_setct_CredRevReqTBSX "setct-CredRevReqTBSX" +#define NID_setct_CredRevReqTBSX 554 +#define OBJ_setct_CredRevReqTBSX OBJ_set_ctype,36L + +#define SN_setct_CredRevResData "setct-CredRevResData" +#define NID_setct_CredRevResData 555 +#define OBJ_setct_CredRevResData OBJ_set_ctype,37L + +#define SN_setct_PCertReqData "setct-PCertReqData" +#define NID_setct_PCertReqData 556 +#define OBJ_setct_PCertReqData OBJ_set_ctype,38L + +#define SN_setct_PCertResTBS "setct-PCertResTBS" +#define NID_setct_PCertResTBS 557 +#define OBJ_setct_PCertResTBS OBJ_set_ctype,39L + +#define SN_setct_BatchAdminReqData "setct-BatchAdminReqData" +#define NID_setct_BatchAdminReqData 558 +#define OBJ_setct_BatchAdminReqData OBJ_set_ctype,40L + +#define SN_setct_BatchAdminResData "setct-BatchAdminResData" +#define NID_setct_BatchAdminResData 559 +#define OBJ_setct_BatchAdminResData OBJ_set_ctype,41L + +#define SN_setct_CardCInitResTBS "setct-CardCInitResTBS" +#define NID_setct_CardCInitResTBS 560 +#define OBJ_setct_CardCInitResTBS OBJ_set_ctype,42L + +#define SN_setct_MeAqCInitResTBS "setct-MeAqCInitResTBS" +#define NID_setct_MeAqCInitResTBS 561 +#define OBJ_setct_MeAqCInitResTBS OBJ_set_ctype,43L + +#define SN_setct_RegFormResTBS "setct-RegFormResTBS" +#define NID_setct_RegFormResTBS 562 +#define OBJ_setct_RegFormResTBS OBJ_set_ctype,44L + +#define SN_setct_CertReqData "setct-CertReqData" +#define NID_setct_CertReqData 563 +#define OBJ_setct_CertReqData OBJ_set_ctype,45L + +#define SN_setct_CertReqTBS "setct-CertReqTBS" +#define NID_setct_CertReqTBS 564 +#define OBJ_setct_CertReqTBS OBJ_set_ctype,46L + +#define SN_setct_CertResData "setct-CertResData" +#define NID_setct_CertResData 565 +#define OBJ_setct_CertResData OBJ_set_ctype,47L + +#define SN_setct_CertInqReqTBS "setct-CertInqReqTBS" +#define NID_setct_CertInqReqTBS 566 +#define OBJ_setct_CertInqReqTBS OBJ_set_ctype,48L + +#define SN_setct_ErrorTBS "setct-ErrorTBS" +#define NID_setct_ErrorTBS 567 +#define OBJ_setct_ErrorTBS OBJ_set_ctype,49L + +#define SN_setct_PIDualSignedTBE "setct-PIDualSignedTBE" +#define NID_setct_PIDualSignedTBE 568 +#define OBJ_setct_PIDualSignedTBE OBJ_set_ctype,50L + +#define SN_setct_PIUnsignedTBE "setct-PIUnsignedTBE" +#define NID_setct_PIUnsignedTBE 569 +#define OBJ_setct_PIUnsignedTBE OBJ_set_ctype,51L + +#define SN_setct_AuthReqTBE "setct-AuthReqTBE" +#define NID_setct_AuthReqTBE 570 +#define OBJ_setct_AuthReqTBE OBJ_set_ctype,52L + +#define SN_setct_AuthResTBE "setct-AuthResTBE" +#define NID_setct_AuthResTBE 571 +#define OBJ_setct_AuthResTBE OBJ_set_ctype,53L + +#define SN_setct_AuthResTBEX "setct-AuthResTBEX" +#define NID_setct_AuthResTBEX 572 +#define OBJ_setct_AuthResTBEX OBJ_set_ctype,54L + +#define SN_setct_AuthTokenTBE "setct-AuthTokenTBE" +#define NID_setct_AuthTokenTBE 573 +#define OBJ_setct_AuthTokenTBE OBJ_set_ctype,55L + +#define SN_setct_CapTokenTBE "setct-CapTokenTBE" +#define NID_setct_CapTokenTBE 574 +#define OBJ_setct_CapTokenTBE OBJ_set_ctype,56L + +#define SN_setct_CapTokenTBEX "setct-CapTokenTBEX" +#define NID_setct_CapTokenTBEX 575 +#define OBJ_setct_CapTokenTBEX OBJ_set_ctype,57L + +#define SN_setct_AcqCardCodeMsgTBE "setct-AcqCardCodeMsgTBE" +#define NID_setct_AcqCardCodeMsgTBE 576 +#define OBJ_setct_AcqCardCodeMsgTBE OBJ_set_ctype,58L + +#define SN_setct_AuthRevReqTBE "setct-AuthRevReqTBE" +#define NID_setct_AuthRevReqTBE 577 +#define OBJ_setct_AuthRevReqTBE OBJ_set_ctype,59L + +#define SN_setct_AuthRevResTBE "setct-AuthRevResTBE" +#define NID_setct_AuthRevResTBE 578 +#define OBJ_setct_AuthRevResTBE OBJ_set_ctype,60L + +#define SN_setct_AuthRevResTBEB "setct-AuthRevResTBEB" +#define NID_setct_AuthRevResTBEB 579 +#define OBJ_setct_AuthRevResTBEB OBJ_set_ctype,61L + +#define SN_setct_CapReqTBE "setct-CapReqTBE" +#define NID_setct_CapReqTBE 580 +#define OBJ_setct_CapReqTBE OBJ_set_ctype,62L + +#define SN_setct_CapReqTBEX "setct-CapReqTBEX" +#define NID_setct_CapReqTBEX 581 +#define OBJ_setct_CapReqTBEX OBJ_set_ctype,63L + +#define SN_setct_CapResTBE "setct-CapResTBE" +#define NID_setct_CapResTBE 582 +#define OBJ_setct_CapResTBE OBJ_set_ctype,64L + +#define SN_setct_CapRevReqTBE "setct-CapRevReqTBE" +#define NID_setct_CapRevReqTBE 583 +#define OBJ_setct_CapRevReqTBE OBJ_set_ctype,65L + +#define SN_setct_CapRevReqTBEX "setct-CapRevReqTBEX" +#define NID_setct_CapRevReqTBEX 584 +#define OBJ_setct_CapRevReqTBEX OBJ_set_ctype,66L + +#define SN_setct_CapRevResTBE "setct-CapRevResTBE" +#define NID_setct_CapRevResTBE 585 +#define OBJ_setct_CapRevResTBE OBJ_set_ctype,67L + +#define SN_setct_CredReqTBE "setct-CredReqTBE" +#define NID_setct_CredReqTBE 586 +#define OBJ_setct_CredReqTBE OBJ_set_ctype,68L + +#define SN_setct_CredReqTBEX "setct-CredReqTBEX" +#define NID_setct_CredReqTBEX 587 +#define OBJ_setct_CredReqTBEX OBJ_set_ctype,69L + +#define SN_setct_CredResTBE "setct-CredResTBE" +#define NID_setct_CredResTBE 588 +#define OBJ_setct_CredResTBE OBJ_set_ctype,70L + +#define SN_setct_CredRevReqTBE "setct-CredRevReqTBE" +#define NID_setct_CredRevReqTBE 589 +#define OBJ_setct_CredRevReqTBE OBJ_set_ctype,71L + +#define SN_setct_CredRevReqTBEX "setct-CredRevReqTBEX" +#define NID_setct_CredRevReqTBEX 590 +#define OBJ_setct_CredRevReqTBEX OBJ_set_ctype,72L + +#define SN_setct_CredRevResTBE "setct-CredRevResTBE" +#define NID_setct_CredRevResTBE 591 +#define OBJ_setct_CredRevResTBE OBJ_set_ctype,73L + +#define SN_setct_BatchAdminReqTBE "setct-BatchAdminReqTBE" +#define NID_setct_BatchAdminReqTBE 592 +#define OBJ_setct_BatchAdminReqTBE OBJ_set_ctype,74L + +#define SN_setct_BatchAdminResTBE "setct-BatchAdminResTBE" +#define NID_setct_BatchAdminResTBE 593 +#define OBJ_setct_BatchAdminResTBE OBJ_set_ctype,75L + +#define SN_setct_RegFormReqTBE "setct-RegFormReqTBE" +#define NID_setct_RegFormReqTBE 594 +#define OBJ_setct_RegFormReqTBE OBJ_set_ctype,76L + +#define SN_setct_CertReqTBE "setct-CertReqTBE" +#define NID_setct_CertReqTBE 595 +#define OBJ_setct_CertReqTBE OBJ_set_ctype,77L + +#define SN_setct_CertReqTBEX "setct-CertReqTBEX" +#define NID_setct_CertReqTBEX 596 +#define OBJ_setct_CertReqTBEX OBJ_set_ctype,78L + +#define SN_setct_CertResTBE "setct-CertResTBE" +#define NID_setct_CertResTBE 597 +#define OBJ_setct_CertResTBE OBJ_set_ctype,79L + +#define SN_setct_CRLNotificationTBS "setct-CRLNotificationTBS" +#define NID_setct_CRLNotificationTBS 598 +#define OBJ_setct_CRLNotificationTBS OBJ_set_ctype,80L + +#define SN_setct_CRLNotificationResTBS "setct-CRLNotificationResTBS" +#define NID_setct_CRLNotificationResTBS 599 +#define OBJ_setct_CRLNotificationResTBS OBJ_set_ctype,81L + +#define SN_setct_BCIDistributionTBS "setct-BCIDistributionTBS" +#define NID_setct_BCIDistributionTBS 600 +#define OBJ_setct_BCIDistributionTBS OBJ_set_ctype,82L + +#define SN_setext_genCrypt "setext-genCrypt" +#define LN_setext_genCrypt "generic cryptogram" +#define NID_setext_genCrypt 601 +#define OBJ_setext_genCrypt OBJ_set_msgExt,1L + +#define SN_setext_miAuth "setext-miAuth" +#define LN_setext_miAuth "merchant initiated auth" +#define NID_setext_miAuth 602 +#define OBJ_setext_miAuth OBJ_set_msgExt,3L + +#define SN_setext_pinSecure "setext-pinSecure" +#define NID_setext_pinSecure 603 +#define OBJ_setext_pinSecure OBJ_set_msgExt,4L + +#define SN_setext_pinAny "setext-pinAny" +#define NID_setext_pinAny 604 +#define OBJ_setext_pinAny OBJ_set_msgExt,5L + +#define SN_setext_track2 "setext-track2" +#define NID_setext_track2 605 +#define OBJ_setext_track2 OBJ_set_msgExt,7L + +#define SN_setext_cv "setext-cv" +#define LN_setext_cv "additional verification" +#define NID_setext_cv 606 +#define OBJ_setext_cv OBJ_set_msgExt,8L + +#define SN_set_policy_root "set-policy-root" +#define NID_set_policy_root 607 +#define OBJ_set_policy_root OBJ_set_policy,0L + +#define SN_setCext_hashedRoot "setCext-hashedRoot" +#define NID_setCext_hashedRoot 608 +#define OBJ_setCext_hashedRoot OBJ_set_certExt,0L + +#define SN_setCext_certType "setCext-certType" +#define NID_setCext_certType 609 +#define OBJ_setCext_certType OBJ_set_certExt,1L + +#define SN_setCext_merchData "setCext-merchData" +#define NID_setCext_merchData 610 +#define OBJ_setCext_merchData OBJ_set_certExt,2L + +#define SN_setCext_cCertRequired "setCext-cCertRequired" +#define NID_setCext_cCertRequired 611 +#define OBJ_setCext_cCertRequired OBJ_set_certExt,3L + +#define SN_setCext_tunneling "setCext-tunneling" +#define NID_setCext_tunneling 612 +#define OBJ_setCext_tunneling OBJ_set_certExt,4L + +#define SN_setCext_setExt "setCext-setExt" +#define NID_setCext_setExt 613 +#define OBJ_setCext_setExt OBJ_set_certExt,5L + +#define SN_setCext_setQualf "setCext-setQualf" +#define NID_setCext_setQualf 614 +#define OBJ_setCext_setQualf OBJ_set_certExt,6L + +#define SN_setCext_PGWYcapabilities "setCext-PGWYcapabilities" +#define NID_setCext_PGWYcapabilities 615 +#define OBJ_setCext_PGWYcapabilities OBJ_set_certExt,7L + +#define SN_setCext_TokenIdentifier "setCext-TokenIdentifier" +#define NID_setCext_TokenIdentifier 616 +#define OBJ_setCext_TokenIdentifier OBJ_set_certExt,8L + +#define SN_setCext_Track2Data "setCext-Track2Data" +#define NID_setCext_Track2Data 617 +#define OBJ_setCext_Track2Data OBJ_set_certExt,9L + +#define SN_setCext_TokenType "setCext-TokenType" +#define NID_setCext_TokenType 618 +#define OBJ_setCext_TokenType OBJ_set_certExt,10L + +#define SN_setCext_IssuerCapabilities "setCext-IssuerCapabilities" +#define NID_setCext_IssuerCapabilities 619 +#define OBJ_setCext_IssuerCapabilities OBJ_set_certExt,11L + +#define SN_setAttr_Cert "setAttr-Cert" +#define NID_setAttr_Cert 620 +#define OBJ_setAttr_Cert OBJ_set_attr,0L + +#define SN_setAttr_PGWYcap "setAttr-PGWYcap" +#define LN_setAttr_PGWYcap "payment gateway capabilities" +#define NID_setAttr_PGWYcap 621 +#define OBJ_setAttr_PGWYcap OBJ_set_attr,1L + +#define SN_setAttr_TokenType "setAttr-TokenType" +#define NID_setAttr_TokenType 622 +#define OBJ_setAttr_TokenType OBJ_set_attr,2L + +#define SN_setAttr_IssCap "setAttr-IssCap" +#define LN_setAttr_IssCap "issuer capabilities" +#define NID_setAttr_IssCap 623 +#define OBJ_setAttr_IssCap OBJ_set_attr,3L + +#define SN_set_rootKeyThumb "set-rootKeyThumb" +#define NID_set_rootKeyThumb 624 +#define OBJ_set_rootKeyThumb OBJ_setAttr_Cert,0L + +#define SN_set_addPolicy "set-addPolicy" +#define NID_set_addPolicy 625 +#define OBJ_set_addPolicy OBJ_setAttr_Cert,1L + +#define SN_setAttr_Token_EMV "setAttr-Token-EMV" +#define NID_setAttr_Token_EMV 626 +#define OBJ_setAttr_Token_EMV OBJ_setAttr_TokenType,1L + +#define SN_setAttr_Token_B0Prime "setAttr-Token-B0Prime" +#define NID_setAttr_Token_B0Prime 627 +#define OBJ_setAttr_Token_B0Prime OBJ_setAttr_TokenType,2L + +#define SN_setAttr_IssCap_CVM "setAttr-IssCap-CVM" +#define NID_setAttr_IssCap_CVM 628 +#define OBJ_setAttr_IssCap_CVM OBJ_setAttr_IssCap,3L + +#define SN_setAttr_IssCap_T2 "setAttr-IssCap-T2" +#define NID_setAttr_IssCap_T2 629 +#define OBJ_setAttr_IssCap_T2 OBJ_setAttr_IssCap,4L + +#define SN_setAttr_IssCap_Sig "setAttr-IssCap-Sig" +#define NID_setAttr_IssCap_Sig 630 +#define OBJ_setAttr_IssCap_Sig OBJ_setAttr_IssCap,5L + +#define SN_setAttr_GenCryptgrm "setAttr-GenCryptgrm" +#define LN_setAttr_GenCryptgrm "generate cryptogram" +#define NID_setAttr_GenCryptgrm 631 +#define OBJ_setAttr_GenCryptgrm OBJ_setAttr_IssCap_CVM,1L + +#define SN_setAttr_T2Enc "setAttr-T2Enc" +#define LN_setAttr_T2Enc "encrypted track 2" +#define NID_setAttr_T2Enc 632 +#define OBJ_setAttr_T2Enc OBJ_setAttr_IssCap_T2,1L + +#define SN_setAttr_T2cleartxt "setAttr-T2cleartxt" +#define LN_setAttr_T2cleartxt "cleartext track 2" +#define NID_setAttr_T2cleartxt 633 +#define OBJ_setAttr_T2cleartxt OBJ_setAttr_IssCap_T2,2L + +#define SN_setAttr_TokICCsig "setAttr-TokICCsig" +#define LN_setAttr_TokICCsig "ICC or token signature" +#define NID_setAttr_TokICCsig 634 +#define OBJ_setAttr_TokICCsig OBJ_setAttr_IssCap_Sig,1L + +#define SN_setAttr_SecDevSig "setAttr-SecDevSig" +#define LN_setAttr_SecDevSig "secure device signature" +#define NID_setAttr_SecDevSig 635 +#define OBJ_setAttr_SecDevSig OBJ_setAttr_IssCap_Sig,2L + +#define SN_set_brand_IATA_ATA "set-brand-IATA-ATA" +#define NID_set_brand_IATA_ATA 636 +#define OBJ_set_brand_IATA_ATA OBJ_set_brand,1L + +#define SN_set_brand_Diners "set-brand-Diners" +#define NID_set_brand_Diners 637 +#define OBJ_set_brand_Diners OBJ_set_brand,30L + +#define SN_set_brand_AmericanExpress "set-brand-AmericanExpress" +#define NID_set_brand_AmericanExpress 638 +#define OBJ_set_brand_AmericanExpress OBJ_set_brand,34L + +#define SN_set_brand_JCB "set-brand-JCB" +#define NID_set_brand_JCB 639 +#define OBJ_set_brand_JCB OBJ_set_brand,35L + +#define SN_set_brand_Visa "set-brand-Visa" +#define NID_set_brand_Visa 640 +#define OBJ_set_brand_Visa OBJ_set_brand,4L + +#define SN_set_brand_MasterCard "set-brand-MasterCard" +#define NID_set_brand_MasterCard 641 +#define OBJ_set_brand_MasterCard OBJ_set_brand,5L + +#define SN_set_brand_Novus "set-brand-Novus" +#define NID_set_brand_Novus 642 +#define OBJ_set_brand_Novus OBJ_set_brand,6011L + +#define SN_des_cdmf "DES-CDMF" +#define LN_des_cdmf "des-cdmf" +#define NID_des_cdmf 643 +#define OBJ_des_cdmf OBJ_rsadsi,3L,10L + +#define SN_rsaOAEPEncryptionSET "rsaOAEPEncryptionSET" +#define NID_rsaOAEPEncryptionSET 644 +#define OBJ_rsaOAEPEncryptionSET OBJ_rsadsi,1L,1L,6L + +#define SN_ipsec3 "Oakley-EC2N-3" +#define LN_ipsec3 "ipsec3" +#define NID_ipsec3 749 + +#define SN_ipsec4 "Oakley-EC2N-4" +#define LN_ipsec4 "ipsec4" +#define NID_ipsec4 750 + diff --git a/production/3rdparty/openssl/include/openssl/objects.h b/production/3rdparty/openssl/include/openssl/objects.h new file mode 100644 index 00000000..7242f76f --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/objects.h @@ -0,0 +1,1049 @@ +/* crypto/objects/objects.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_OBJECTS_H +#define HEADER_OBJECTS_H + +#define USE_OBJ_MAC + +#ifdef USE_OBJ_MAC +#include +#else +#define SN_undef "UNDEF" +#define LN_undef "undefined" +#define NID_undef 0 +#define OBJ_undef 0L + +#define SN_Algorithm "Algorithm" +#define LN_algorithm "algorithm" +#define NID_algorithm 38 +#define OBJ_algorithm 1L,3L,14L,3L,2L + +#define LN_rsadsi "rsadsi" +#define NID_rsadsi 1 +#define OBJ_rsadsi 1L,2L,840L,113549L + +#define LN_pkcs "pkcs" +#define NID_pkcs 2 +#define OBJ_pkcs OBJ_rsadsi,1L + +#define SN_md2 "MD2" +#define LN_md2 "md2" +#define NID_md2 3 +#define OBJ_md2 OBJ_rsadsi,2L,2L + +#define SN_md5 "MD5" +#define LN_md5 "md5" +#define NID_md5 4 +#define OBJ_md5 OBJ_rsadsi,2L,5L + +#define SN_rc4 "RC4" +#define LN_rc4 "rc4" +#define NID_rc4 5 +#define OBJ_rc4 OBJ_rsadsi,3L,4L + +#define LN_rsaEncryption "rsaEncryption" +#define NID_rsaEncryption 6 +#define OBJ_rsaEncryption OBJ_pkcs,1L,1L + +#define SN_md2WithRSAEncryption "RSA-MD2" +#define LN_md2WithRSAEncryption "md2WithRSAEncryption" +#define NID_md2WithRSAEncryption 7 +#define OBJ_md2WithRSAEncryption OBJ_pkcs,1L,2L + +#define SN_md5WithRSAEncryption "RSA-MD5" +#define LN_md5WithRSAEncryption "md5WithRSAEncryption" +#define NID_md5WithRSAEncryption 8 +#define OBJ_md5WithRSAEncryption OBJ_pkcs,1L,4L + +#define SN_pbeWithMD2AndDES_CBC "PBE-MD2-DES" +#define LN_pbeWithMD2AndDES_CBC "pbeWithMD2AndDES-CBC" +#define NID_pbeWithMD2AndDES_CBC 9 +#define OBJ_pbeWithMD2AndDES_CBC OBJ_pkcs,5L,1L + +#define SN_pbeWithMD5AndDES_CBC "PBE-MD5-DES" +#define LN_pbeWithMD5AndDES_CBC "pbeWithMD5AndDES-CBC" +#define NID_pbeWithMD5AndDES_CBC 10 +#define OBJ_pbeWithMD5AndDES_CBC OBJ_pkcs,5L,3L + +#define LN_X500 "X500" +#define NID_X500 11 +#define OBJ_X500 2L,5L + +#define LN_X509 "X509" +#define NID_X509 12 +#define OBJ_X509 OBJ_X500,4L + +#define SN_commonName "CN" +#define LN_commonName "commonName" +#define NID_commonName 13 +#define OBJ_commonName OBJ_X509,3L + +#define SN_countryName "C" +#define LN_countryName "countryName" +#define NID_countryName 14 +#define OBJ_countryName OBJ_X509,6L + +#define SN_localityName "L" +#define LN_localityName "localityName" +#define NID_localityName 15 +#define OBJ_localityName OBJ_X509,7L + +/* Postal Address? PA */ + +/* should be "ST" (rfc1327) but MS uses 'S' */ +#define SN_stateOrProvinceName "ST" +#define LN_stateOrProvinceName "stateOrProvinceName" +#define NID_stateOrProvinceName 16 +#define OBJ_stateOrProvinceName OBJ_X509,8L + +#define SN_organizationName "O" +#define LN_organizationName "organizationName" +#define NID_organizationName 17 +#define OBJ_organizationName OBJ_X509,10L + +#define SN_organizationalUnitName "OU" +#define LN_organizationalUnitName "organizationalUnitName" +#define NID_organizationalUnitName 18 +#define OBJ_organizationalUnitName OBJ_X509,11L + +#define SN_rsa "RSA" +#define LN_rsa "rsa" +#define NID_rsa 19 +#define OBJ_rsa OBJ_X500,8L,1L,1L + +#define LN_pkcs7 "pkcs7" +#define NID_pkcs7 20 +#define OBJ_pkcs7 OBJ_pkcs,7L + +#define LN_pkcs7_data "pkcs7-data" +#define NID_pkcs7_data 21 +#define OBJ_pkcs7_data OBJ_pkcs7,1L + +#define LN_pkcs7_signed "pkcs7-signedData" +#define NID_pkcs7_signed 22 +#define OBJ_pkcs7_signed OBJ_pkcs7,2L + +#define LN_pkcs7_enveloped "pkcs7-envelopedData" +#define NID_pkcs7_enveloped 23 +#define OBJ_pkcs7_enveloped OBJ_pkcs7,3L + +#define LN_pkcs7_signedAndEnveloped "pkcs7-signedAndEnvelopedData" +#define NID_pkcs7_signedAndEnveloped 24 +#define OBJ_pkcs7_signedAndEnveloped OBJ_pkcs7,4L + +#define LN_pkcs7_digest "pkcs7-digestData" +#define NID_pkcs7_digest 25 +#define OBJ_pkcs7_digest OBJ_pkcs7,5L + +#define LN_pkcs7_encrypted "pkcs7-encryptedData" +#define NID_pkcs7_encrypted 26 +#define OBJ_pkcs7_encrypted OBJ_pkcs7,6L + +#define LN_pkcs3 "pkcs3" +#define NID_pkcs3 27 +#define OBJ_pkcs3 OBJ_pkcs,3L + +#define LN_dhKeyAgreement "dhKeyAgreement" +#define NID_dhKeyAgreement 28 +#define OBJ_dhKeyAgreement OBJ_pkcs3,1L + +#define SN_des_ecb "DES-ECB" +#define LN_des_ecb "des-ecb" +#define NID_des_ecb 29 +#define OBJ_des_ecb OBJ_algorithm,6L + +#define SN_des_cfb64 "DES-CFB" +#define LN_des_cfb64 "des-cfb" +#define NID_des_cfb64 30 +/* IV + num */ +#define OBJ_des_cfb64 OBJ_algorithm,9L + +#define SN_des_cbc "DES-CBC" +#define LN_des_cbc "des-cbc" +#define NID_des_cbc 31 +/* IV */ +#define OBJ_des_cbc OBJ_algorithm,7L + +#define SN_des_ede "DES-EDE" +#define LN_des_ede "des-ede" +#define NID_des_ede 32 +/* ?? */ +#define OBJ_des_ede OBJ_algorithm,17L + +#define SN_des_ede3 "DES-EDE3" +#define LN_des_ede3 "des-ede3" +#define NID_des_ede3 33 + +#define SN_idea_cbc "IDEA-CBC" +#define LN_idea_cbc "idea-cbc" +#define NID_idea_cbc 34 +#define OBJ_idea_cbc 1L,3L,6L,1L,4L,1L,188L,7L,1L,1L,2L + +#define SN_idea_cfb64 "IDEA-CFB" +#define LN_idea_cfb64 "idea-cfb" +#define NID_idea_cfb64 35 + +#define SN_idea_ecb "IDEA-ECB" +#define LN_idea_ecb "idea-ecb" +#define NID_idea_ecb 36 + +#define SN_rc2_cbc "RC2-CBC" +#define LN_rc2_cbc "rc2-cbc" +#define NID_rc2_cbc 37 +#define OBJ_rc2_cbc OBJ_rsadsi,3L,2L + +#define SN_rc2_ecb "RC2-ECB" +#define LN_rc2_ecb "rc2-ecb" +#define NID_rc2_ecb 38 + +#define SN_rc2_cfb64 "RC2-CFB" +#define LN_rc2_cfb64 "rc2-cfb" +#define NID_rc2_cfb64 39 + +#define SN_rc2_ofb64 "RC2-OFB" +#define LN_rc2_ofb64 "rc2-ofb" +#define NID_rc2_ofb64 40 + +#define SN_sha "SHA" +#define LN_sha "sha" +#define NID_sha 41 +#define OBJ_sha OBJ_algorithm,18L + +#define SN_shaWithRSAEncryption "RSA-SHA" +#define LN_shaWithRSAEncryption "shaWithRSAEncryption" +#define NID_shaWithRSAEncryption 42 +#define OBJ_shaWithRSAEncryption OBJ_algorithm,15L + +#define SN_des_ede_cbc "DES-EDE-CBC" +#define LN_des_ede_cbc "des-ede-cbc" +#define NID_des_ede_cbc 43 + +#define SN_des_ede3_cbc "DES-EDE3-CBC" +#define LN_des_ede3_cbc "des-ede3-cbc" +#define NID_des_ede3_cbc 44 +#define OBJ_des_ede3_cbc OBJ_rsadsi,3L,7L + +#define SN_des_ofb64 "DES-OFB" +#define LN_des_ofb64 "des-ofb" +#define NID_des_ofb64 45 +#define OBJ_des_ofb64 OBJ_algorithm,8L + +#define SN_idea_ofb64 "IDEA-OFB" +#define LN_idea_ofb64 "idea-ofb" +#define NID_idea_ofb64 46 + +#define LN_pkcs9 "pkcs9" +#define NID_pkcs9 47 +#define OBJ_pkcs9 OBJ_pkcs,9L + +#define SN_pkcs9_emailAddress "Email" +#define LN_pkcs9_emailAddress "emailAddress" +#define NID_pkcs9_emailAddress 48 +#define OBJ_pkcs9_emailAddress OBJ_pkcs9,1L + +#define LN_pkcs9_unstructuredName "unstructuredName" +#define NID_pkcs9_unstructuredName 49 +#define OBJ_pkcs9_unstructuredName OBJ_pkcs9,2L + +#define LN_pkcs9_contentType "contentType" +#define NID_pkcs9_contentType 50 +#define OBJ_pkcs9_contentType OBJ_pkcs9,3L + +#define LN_pkcs9_messageDigest "messageDigest" +#define NID_pkcs9_messageDigest 51 +#define OBJ_pkcs9_messageDigest OBJ_pkcs9,4L + +#define LN_pkcs9_signingTime "signingTime" +#define NID_pkcs9_signingTime 52 +#define OBJ_pkcs9_signingTime OBJ_pkcs9,5L + +#define LN_pkcs9_countersignature "countersignature" +#define NID_pkcs9_countersignature 53 +#define OBJ_pkcs9_countersignature OBJ_pkcs9,6L + +#define LN_pkcs9_challengePassword "challengePassword" +#define NID_pkcs9_challengePassword 54 +#define OBJ_pkcs9_challengePassword OBJ_pkcs9,7L + +#define LN_pkcs9_unstructuredAddress "unstructuredAddress" +#define NID_pkcs9_unstructuredAddress 55 +#define OBJ_pkcs9_unstructuredAddress OBJ_pkcs9,8L + +#define LN_pkcs9_extCertAttributes "extendedCertificateAttributes" +#define NID_pkcs9_extCertAttributes 56 +#define OBJ_pkcs9_extCertAttributes OBJ_pkcs9,9L + +#define SN_netscape "Netscape" +#define LN_netscape "Netscape Communications Corp." +#define NID_netscape 57 +#define OBJ_netscape 2L,16L,840L,1L,113730L + +#define SN_netscape_cert_extension "nsCertExt" +#define LN_netscape_cert_extension "Netscape Certificate Extension" +#define NID_netscape_cert_extension 58 +#define OBJ_netscape_cert_extension OBJ_netscape,1L + +#define SN_netscape_data_type "nsDataType" +#define LN_netscape_data_type "Netscape Data Type" +#define NID_netscape_data_type 59 +#define OBJ_netscape_data_type OBJ_netscape,2L + +#define SN_des_ede_cfb64 "DES-EDE-CFB" +#define LN_des_ede_cfb64 "des-ede-cfb" +#define NID_des_ede_cfb64 60 + +#define SN_des_ede3_cfb64 "DES-EDE3-CFB" +#define LN_des_ede3_cfb64 "des-ede3-cfb" +#define NID_des_ede3_cfb64 61 + +#define SN_des_ede_ofb64 "DES-EDE-OFB" +#define LN_des_ede_ofb64 "des-ede-ofb" +#define NID_des_ede_ofb64 62 + +#define SN_des_ede3_ofb64 "DES-EDE3-OFB" +#define LN_des_ede3_ofb64 "des-ede3-ofb" +#define NID_des_ede3_ofb64 63 + +/* I'm not sure about the object ID */ +#define SN_sha1 "SHA1" +#define LN_sha1 "sha1" +#define NID_sha1 64 +#define OBJ_sha1 OBJ_algorithm,26L +/* 28 Jun 1996 - eay */ +/* #define OBJ_sha1 1L,3L,14L,2L,26L,05L <- wrong */ + +#define SN_sha1WithRSAEncryption "RSA-SHA1" +#define LN_sha1WithRSAEncryption "sha1WithRSAEncryption" +#define NID_sha1WithRSAEncryption 65 +#define OBJ_sha1WithRSAEncryption OBJ_pkcs,1L,5L + +#define SN_dsaWithSHA "DSA-SHA" +#define LN_dsaWithSHA "dsaWithSHA" +#define NID_dsaWithSHA 66 +#define OBJ_dsaWithSHA OBJ_algorithm,13L + +#define SN_dsa_2 "DSA-old" +#define LN_dsa_2 "dsaEncryption-old" +#define NID_dsa_2 67 +#define OBJ_dsa_2 OBJ_algorithm,12L + +/* proposed by microsoft to RSA */ +#define SN_pbeWithSHA1AndRC2_CBC "PBE-SHA1-RC2-64" +#define LN_pbeWithSHA1AndRC2_CBC "pbeWithSHA1AndRC2-CBC" +#define NID_pbeWithSHA1AndRC2_CBC 68 +#define OBJ_pbeWithSHA1AndRC2_CBC OBJ_pkcs,5L,11L + +/* proposed by microsoft to RSA as pbeWithSHA1AndRC4: it is now + * defined explicitly in PKCS#5 v2.0 as id-PBKDF2 which is something + * completely different. + */ +#define LN_id_pbkdf2 "PBKDF2" +#define NID_id_pbkdf2 69 +#define OBJ_id_pbkdf2 OBJ_pkcs,5L,12L + +#define SN_dsaWithSHA1_2 "DSA-SHA1-old" +#define LN_dsaWithSHA1_2 "dsaWithSHA1-old" +#define NID_dsaWithSHA1_2 70 +/* Got this one from 'sdn706r20.pdf' which is actually an NSA document :-) */ +#define OBJ_dsaWithSHA1_2 OBJ_algorithm,27L + +#define SN_netscape_cert_type "nsCertType" +#define LN_netscape_cert_type "Netscape Cert Type" +#define NID_netscape_cert_type 71 +#define OBJ_netscape_cert_type OBJ_netscape_cert_extension,1L + +#define SN_netscape_base_url "nsBaseUrl" +#define LN_netscape_base_url "Netscape Base Url" +#define NID_netscape_base_url 72 +#define OBJ_netscape_base_url OBJ_netscape_cert_extension,2L + +#define SN_netscape_revocation_url "nsRevocationUrl" +#define LN_netscape_revocation_url "Netscape Revocation Url" +#define NID_netscape_revocation_url 73 +#define OBJ_netscape_revocation_url OBJ_netscape_cert_extension,3L + +#define SN_netscape_ca_revocation_url "nsCaRevocationUrl" +#define LN_netscape_ca_revocation_url "Netscape CA Revocation Url" +#define NID_netscape_ca_revocation_url 74 +#define OBJ_netscape_ca_revocation_url OBJ_netscape_cert_extension,4L + +#define SN_netscape_renewal_url "nsRenewalUrl" +#define LN_netscape_renewal_url "Netscape Renewal Url" +#define NID_netscape_renewal_url 75 +#define OBJ_netscape_renewal_url OBJ_netscape_cert_extension,7L + +#define SN_netscape_ca_policy_url "nsCaPolicyUrl" +#define LN_netscape_ca_policy_url "Netscape CA Policy Url" +#define NID_netscape_ca_policy_url 76 +#define OBJ_netscape_ca_policy_url OBJ_netscape_cert_extension,8L + +#define SN_netscape_ssl_server_name "nsSslServerName" +#define LN_netscape_ssl_server_name "Netscape SSL Server Name" +#define NID_netscape_ssl_server_name 77 +#define OBJ_netscape_ssl_server_name OBJ_netscape_cert_extension,12L + +#define SN_netscape_comment "nsComment" +#define LN_netscape_comment "Netscape Comment" +#define NID_netscape_comment 78 +#define OBJ_netscape_comment OBJ_netscape_cert_extension,13L + +#define SN_netscape_cert_sequence "nsCertSequence" +#define LN_netscape_cert_sequence "Netscape Certificate Sequence" +#define NID_netscape_cert_sequence 79 +#define OBJ_netscape_cert_sequence OBJ_netscape_data_type,5L + +#define SN_desx_cbc "DESX-CBC" +#define LN_desx_cbc "desx-cbc" +#define NID_desx_cbc 80 + +#define SN_id_ce "id-ce" +#define NID_id_ce 81 +#define OBJ_id_ce 2L,5L,29L + +#define SN_subject_key_identifier "subjectKeyIdentifier" +#define LN_subject_key_identifier "X509v3 Subject Key Identifier" +#define NID_subject_key_identifier 82 +#define OBJ_subject_key_identifier OBJ_id_ce,14L + +#define SN_key_usage "keyUsage" +#define LN_key_usage "X509v3 Key Usage" +#define NID_key_usage 83 +#define OBJ_key_usage OBJ_id_ce,15L + +#define SN_private_key_usage_period "privateKeyUsagePeriod" +#define LN_private_key_usage_period "X509v3 Private Key Usage Period" +#define NID_private_key_usage_period 84 +#define OBJ_private_key_usage_period OBJ_id_ce,16L + +#define SN_subject_alt_name "subjectAltName" +#define LN_subject_alt_name "X509v3 Subject Alternative Name" +#define NID_subject_alt_name 85 +#define OBJ_subject_alt_name OBJ_id_ce,17L + +#define SN_issuer_alt_name "issuerAltName" +#define LN_issuer_alt_name "X509v3 Issuer Alternative Name" +#define NID_issuer_alt_name 86 +#define OBJ_issuer_alt_name OBJ_id_ce,18L + +#define SN_basic_constraints "basicConstraints" +#define LN_basic_constraints "X509v3 Basic Constraints" +#define NID_basic_constraints 87 +#define OBJ_basic_constraints OBJ_id_ce,19L + +#define SN_crl_number "crlNumber" +#define LN_crl_number "X509v3 CRL Number" +#define NID_crl_number 88 +#define OBJ_crl_number OBJ_id_ce,20L + +#define SN_certificate_policies "certificatePolicies" +#define LN_certificate_policies "X509v3 Certificate Policies" +#define NID_certificate_policies 89 +#define OBJ_certificate_policies OBJ_id_ce,32L + +#define SN_authority_key_identifier "authorityKeyIdentifier" +#define LN_authority_key_identifier "X509v3 Authority Key Identifier" +#define NID_authority_key_identifier 90 +#define OBJ_authority_key_identifier OBJ_id_ce,35L + +#define SN_bf_cbc "BF-CBC" +#define LN_bf_cbc "bf-cbc" +#define NID_bf_cbc 91 +#define OBJ_bf_cbc 1L,3L,6L,1L,4L,1L,3029L,1L,2L + +#define SN_bf_ecb "BF-ECB" +#define LN_bf_ecb "bf-ecb" +#define NID_bf_ecb 92 + +#define SN_bf_cfb64 "BF-CFB" +#define LN_bf_cfb64 "bf-cfb" +#define NID_bf_cfb64 93 + +#define SN_bf_ofb64 "BF-OFB" +#define LN_bf_ofb64 "bf-ofb" +#define NID_bf_ofb64 94 + +#define SN_mdc2 "MDC2" +#define LN_mdc2 "mdc2" +#define NID_mdc2 95 +#define OBJ_mdc2 2L,5L,8L,3L,101L +/* An alternative? 1L,3L,14L,3L,2L,19L */ + +#define SN_mdc2WithRSA "RSA-MDC2" +#define LN_mdc2WithRSA "mdc2withRSA" +#define NID_mdc2WithRSA 96 +#define OBJ_mdc2WithRSA 2L,5L,8L,3L,100L + +#define SN_rc4_40 "RC4-40" +#define LN_rc4_40 "rc4-40" +#define NID_rc4_40 97 + +#define SN_rc2_40_cbc "RC2-40-CBC" +#define LN_rc2_40_cbc "rc2-40-cbc" +#define NID_rc2_40_cbc 98 + +#define SN_givenName "G" +#define LN_givenName "givenName" +#define NID_givenName 99 +#define OBJ_givenName OBJ_X509,42L + +#define SN_surname "S" +#define LN_surname "surname" +#define NID_surname 100 +#define OBJ_surname OBJ_X509,4L + +#define SN_initials "I" +#define LN_initials "initials" +#define NID_initials 101 +#define OBJ_initials OBJ_X509,43L + +#define SN_uniqueIdentifier "UID" +#define LN_uniqueIdentifier "uniqueIdentifier" +#define NID_uniqueIdentifier 102 +#define OBJ_uniqueIdentifier OBJ_X509,45L + +#define SN_crl_distribution_points "crlDistributionPoints" +#define LN_crl_distribution_points "X509v3 CRL Distribution Points" +#define NID_crl_distribution_points 103 +#define OBJ_crl_distribution_points OBJ_id_ce,31L + +#define SN_md5WithRSA "RSA-NP-MD5" +#define LN_md5WithRSA "md5WithRSA" +#define NID_md5WithRSA 104 +#define OBJ_md5WithRSA OBJ_algorithm,3L + +#define SN_serialNumber "SN" +#define LN_serialNumber "serialNumber" +#define NID_serialNumber 105 +#define OBJ_serialNumber OBJ_X509,5L + +#define SN_title "T" +#define LN_title "title" +#define NID_title 106 +#define OBJ_title OBJ_X509,12L + +#define SN_description "D" +#define LN_description "description" +#define NID_description 107 +#define OBJ_description OBJ_X509,13L + +/* CAST5 is CAST-128, I'm just sticking with the documentation */ +#define SN_cast5_cbc "CAST5-CBC" +#define LN_cast5_cbc "cast5-cbc" +#define NID_cast5_cbc 108 +#define OBJ_cast5_cbc 1L,2L,840L,113533L,7L,66L,10L + +#define SN_cast5_ecb "CAST5-ECB" +#define LN_cast5_ecb "cast5-ecb" +#define NID_cast5_ecb 109 + +#define SN_cast5_cfb64 "CAST5-CFB" +#define LN_cast5_cfb64 "cast5-cfb" +#define NID_cast5_cfb64 110 + +#define SN_cast5_ofb64 "CAST5-OFB" +#define LN_cast5_ofb64 "cast5-ofb" +#define NID_cast5_ofb64 111 + +#define LN_pbeWithMD5AndCast5_CBC "pbeWithMD5AndCast5CBC" +#define NID_pbeWithMD5AndCast5_CBC 112 +#define OBJ_pbeWithMD5AndCast5_CBC 1L,2L,840L,113533L,7L,66L,12L + +/* This is one sun will soon be using :-( + * id-dsa-with-sha1 ID ::= { + * iso(1) member-body(2) us(840) x9-57 (10040) x9cm(4) 3 } + */ +#define SN_dsaWithSHA1 "DSA-SHA1" +#define LN_dsaWithSHA1 "dsaWithSHA1" +#define NID_dsaWithSHA1 113 +#define OBJ_dsaWithSHA1 1L,2L,840L,10040L,4L,3L + +#define NID_md5_sha1 114 +#define SN_md5_sha1 "MD5-SHA1" +#define LN_md5_sha1 "md5-sha1" + +#define SN_sha1WithRSA "RSA-SHA1-2" +#define LN_sha1WithRSA "sha1WithRSA" +#define NID_sha1WithRSA 115 +#define OBJ_sha1WithRSA OBJ_algorithm,29L + +#define SN_dsa "DSA" +#define LN_dsa "dsaEncryption" +#define NID_dsa 116 +#define OBJ_dsa 1L,2L,840L,10040L,4L,1L + +#define SN_ripemd160 "RIPEMD160" +#define LN_ripemd160 "ripemd160" +#define NID_ripemd160 117 +#define OBJ_ripemd160 1L,3L,36L,3L,2L,1L + +/* The name should actually be rsaSignatureWithripemd160, but I'm going + * to continue using the convention I'm using with the other ciphers */ +#define SN_ripemd160WithRSA "RSA-RIPEMD160" +#define LN_ripemd160WithRSA "ripemd160WithRSA" +#define NID_ripemd160WithRSA 119 +#define OBJ_ripemd160WithRSA 1L,3L,36L,3L,3L,1L,2L + +/* Taken from rfc2040 + * RC5_CBC_Parameters ::= SEQUENCE { + * version INTEGER (v1_0(16)), + * rounds INTEGER (8..127), + * blockSizeInBits INTEGER (64, 128), + * iv OCTET STRING OPTIONAL + * } + */ +#define SN_rc5_cbc "RC5-CBC" +#define LN_rc5_cbc "rc5-cbc" +#define NID_rc5_cbc 120 +#define OBJ_rc5_cbc OBJ_rsadsi,3L,8L + +#define SN_rc5_ecb "RC5-ECB" +#define LN_rc5_ecb "rc5-ecb" +#define NID_rc5_ecb 121 + +#define SN_rc5_cfb64 "RC5-CFB" +#define LN_rc5_cfb64 "rc5-cfb" +#define NID_rc5_cfb64 122 + +#define SN_rc5_ofb64 "RC5-OFB" +#define LN_rc5_ofb64 "rc5-ofb" +#define NID_rc5_ofb64 123 + +#define SN_rle_compression "RLE" +#define LN_rle_compression "run length compression" +#define NID_rle_compression 124 +#define OBJ_rle_compression 1L,1L,1L,1L,666L,1L + +#define SN_zlib_compression "ZLIB" +#define LN_zlib_compression "zlib compression" +#define NID_zlib_compression 125 +#define OBJ_zlib_compression 1L,1L,1L,1L,666L,2L + +#define SN_ext_key_usage "extendedKeyUsage" +#define LN_ext_key_usage "X509v3 Extended Key Usage" +#define NID_ext_key_usage 126 +#define OBJ_ext_key_usage OBJ_id_ce,37 + +#define SN_id_pkix "PKIX" +#define NID_id_pkix 127 +#define OBJ_id_pkix 1L,3L,6L,1L,5L,5L,7L + +#define SN_id_kp "id-kp" +#define NID_id_kp 128 +#define OBJ_id_kp OBJ_id_pkix,3L + +/* PKIX extended key usage OIDs */ + +#define SN_server_auth "serverAuth" +#define LN_server_auth "TLS Web Server Authentication" +#define NID_server_auth 129 +#define OBJ_server_auth OBJ_id_kp,1L + +#define SN_client_auth "clientAuth" +#define LN_client_auth "TLS Web Client Authentication" +#define NID_client_auth 130 +#define OBJ_client_auth OBJ_id_kp,2L + +#define SN_code_sign "codeSigning" +#define LN_code_sign "Code Signing" +#define NID_code_sign 131 +#define OBJ_code_sign OBJ_id_kp,3L + +#define SN_email_protect "emailProtection" +#define LN_email_protect "E-mail Protection" +#define NID_email_protect 132 +#define OBJ_email_protect OBJ_id_kp,4L + +#define SN_time_stamp "timeStamping" +#define LN_time_stamp "Time Stamping" +#define NID_time_stamp 133 +#define OBJ_time_stamp OBJ_id_kp,8L + +/* Additional extended key usage OIDs: Microsoft */ + +#define SN_ms_code_ind "msCodeInd" +#define LN_ms_code_ind "Microsoft Individual Code Signing" +#define NID_ms_code_ind 134 +#define OBJ_ms_code_ind 1L,3L,6L,1L,4L,1L,311L,2L,1L,21L + +#define SN_ms_code_com "msCodeCom" +#define LN_ms_code_com "Microsoft Commercial Code Signing" +#define NID_ms_code_com 135 +#define OBJ_ms_code_com 1L,3L,6L,1L,4L,1L,311L,2L,1L,22L + +#define SN_ms_ctl_sign "msCTLSign" +#define LN_ms_ctl_sign "Microsoft Trust List Signing" +#define NID_ms_ctl_sign 136 +#define OBJ_ms_ctl_sign 1L,3L,6L,1L,4L,1L,311L,10L,3L,1L + +#define SN_ms_sgc "msSGC" +#define LN_ms_sgc "Microsoft Server Gated Crypto" +#define NID_ms_sgc 137 +#define OBJ_ms_sgc 1L,3L,6L,1L,4L,1L,311L,10L,3L,3L + +#define SN_ms_efs "msEFS" +#define LN_ms_efs "Microsoft Encrypted File System" +#define NID_ms_efs 138 +#define OBJ_ms_efs 1L,3L,6L,1L,4L,1L,311L,10L,3L,4L + +/* Additional usage: Netscape */ + +#define SN_ns_sgc "nsSGC" +#define LN_ns_sgc "Netscape Server Gated Crypto" +#define NID_ns_sgc 139 +#define OBJ_ns_sgc OBJ_netscape,4L,1L + +#define SN_delta_crl "deltaCRL" +#define LN_delta_crl "X509v3 Delta CRL Indicator" +#define NID_delta_crl 140 +#define OBJ_delta_crl OBJ_id_ce,27L + +#define SN_crl_reason "CRLReason" +#define LN_crl_reason "CRL Reason Code" +#define NID_crl_reason 141 +#define OBJ_crl_reason OBJ_id_ce,21L + +#define SN_invalidity_date "invalidityDate" +#define LN_invalidity_date "Invalidity Date" +#define NID_invalidity_date 142 +#define OBJ_invalidity_date OBJ_id_ce,24L + +#define SN_sxnet "SXNetID" +#define LN_sxnet "Strong Extranet ID" +#define NID_sxnet 143 +#define OBJ_sxnet 1L,3L,101L,1L,4L,1L + +/* PKCS12 and related OBJECT IDENTIFIERS */ + +#define OBJ_pkcs12 OBJ_pkcs,12L +#define OBJ_pkcs12_pbeids OBJ_pkcs12, 1 + +#define SN_pbe_WithSHA1And128BitRC4 "PBE-SHA1-RC4-128" +#define LN_pbe_WithSHA1And128BitRC4 "pbeWithSHA1And128BitRC4" +#define NID_pbe_WithSHA1And128BitRC4 144 +#define OBJ_pbe_WithSHA1And128BitRC4 OBJ_pkcs12_pbeids, 1L + +#define SN_pbe_WithSHA1And40BitRC4 "PBE-SHA1-RC4-40" +#define LN_pbe_WithSHA1And40BitRC4 "pbeWithSHA1And40BitRC4" +#define NID_pbe_WithSHA1And40BitRC4 145 +#define OBJ_pbe_WithSHA1And40BitRC4 OBJ_pkcs12_pbeids, 2L + +#define SN_pbe_WithSHA1And3_Key_TripleDES_CBC "PBE-SHA1-3DES" +#define LN_pbe_WithSHA1And3_Key_TripleDES_CBC "pbeWithSHA1And3-KeyTripleDES-CBC" +#define NID_pbe_WithSHA1And3_Key_TripleDES_CBC 146 +#define OBJ_pbe_WithSHA1And3_Key_TripleDES_CBC OBJ_pkcs12_pbeids, 3L + +#define SN_pbe_WithSHA1And2_Key_TripleDES_CBC "PBE-SHA1-2DES" +#define LN_pbe_WithSHA1And2_Key_TripleDES_CBC "pbeWithSHA1And2-KeyTripleDES-CBC" +#define NID_pbe_WithSHA1And2_Key_TripleDES_CBC 147 +#define OBJ_pbe_WithSHA1And2_Key_TripleDES_CBC OBJ_pkcs12_pbeids, 4L + +#define SN_pbe_WithSHA1And128BitRC2_CBC "PBE-SHA1-RC2-128" +#define LN_pbe_WithSHA1And128BitRC2_CBC "pbeWithSHA1And128BitRC2-CBC" +#define NID_pbe_WithSHA1And128BitRC2_CBC 148 +#define OBJ_pbe_WithSHA1And128BitRC2_CBC OBJ_pkcs12_pbeids, 5L + +#define SN_pbe_WithSHA1And40BitRC2_CBC "PBE-SHA1-RC2-40" +#define LN_pbe_WithSHA1And40BitRC2_CBC "pbeWithSHA1And40BitRC2-CBC" +#define NID_pbe_WithSHA1And40BitRC2_CBC 149 +#define OBJ_pbe_WithSHA1And40BitRC2_CBC OBJ_pkcs12_pbeids, 6L + +#define OBJ_pkcs12_Version1 OBJ_pkcs12, 10L + +#define OBJ_pkcs12_BagIds OBJ_pkcs12_Version1, 1L + +#define LN_keyBag "keyBag" +#define NID_keyBag 150 +#define OBJ_keyBag OBJ_pkcs12_BagIds, 1L + +#define LN_pkcs8ShroudedKeyBag "pkcs8ShroudedKeyBag" +#define NID_pkcs8ShroudedKeyBag 151 +#define OBJ_pkcs8ShroudedKeyBag OBJ_pkcs12_BagIds, 2L + +#define LN_certBag "certBag" +#define NID_certBag 152 +#define OBJ_certBag OBJ_pkcs12_BagIds, 3L + +#define LN_crlBag "crlBag" +#define NID_crlBag 153 +#define OBJ_crlBag OBJ_pkcs12_BagIds, 4L + +#define LN_secretBag "secretBag" +#define NID_secretBag 154 +#define OBJ_secretBag OBJ_pkcs12_BagIds, 5L + +#define LN_safeContentsBag "safeContentsBag" +#define NID_safeContentsBag 155 +#define OBJ_safeContentsBag OBJ_pkcs12_BagIds, 6L + +#define LN_friendlyName "friendlyName" +#define NID_friendlyName 156 +#define OBJ_friendlyName OBJ_pkcs9, 20L + +#define LN_localKeyID "localKeyID" +#define NID_localKeyID 157 +#define OBJ_localKeyID OBJ_pkcs9, 21L + +#define OBJ_certTypes OBJ_pkcs9, 22L + +#define LN_x509Certificate "x509Certificate" +#define NID_x509Certificate 158 +#define OBJ_x509Certificate OBJ_certTypes, 1L + +#define LN_sdsiCertificate "sdsiCertificate" +#define NID_sdsiCertificate 159 +#define OBJ_sdsiCertificate OBJ_certTypes, 2L + +#define OBJ_crlTypes OBJ_pkcs9, 23L + +#define LN_x509Crl "x509Crl" +#define NID_x509Crl 160 +#define OBJ_x509Crl OBJ_crlTypes, 1L + +/* PKCS#5 v2 OIDs */ + +#define LN_pbes2 "PBES2" +#define NID_pbes2 161 +#define OBJ_pbes2 OBJ_pkcs,5L,13L + +#define LN_pbmac1 "PBMAC1" +#define NID_pbmac1 162 +#define OBJ_pbmac1 OBJ_pkcs,5L,14L + +#define LN_hmacWithSHA1 "hmacWithSHA1" +#define NID_hmacWithSHA1 163 +#define OBJ_hmacWithSHA1 OBJ_rsadsi,2L,7L + +/* Policy Qualifier Ids */ + +#define LN_id_qt_cps "Policy Qualifier CPS" +#define SN_id_qt_cps "id-qt-cps" +#define NID_id_qt_cps 164 +#define OBJ_id_qt_cps OBJ_id_pkix,2L,1L + +#define LN_id_qt_unotice "Policy Qualifier User Notice" +#define SN_id_qt_unotice "id-qt-unotice" +#define NID_id_qt_unotice 165 +#define OBJ_id_qt_unotice OBJ_id_pkix,2L,2L + +#define SN_rc2_64_cbc "RC2-64-CBC" +#define LN_rc2_64_cbc "rc2-64-cbc" +#define NID_rc2_64_cbc 166 + +#define SN_SMIMECapabilities "SMIME-CAPS" +#define LN_SMIMECapabilities "S/MIME Capabilities" +#define NID_SMIMECapabilities 167 +#define OBJ_SMIMECapabilities OBJ_pkcs9,15L + +#define SN_pbeWithMD2AndRC2_CBC "PBE-MD2-RC2-64" +#define LN_pbeWithMD2AndRC2_CBC "pbeWithMD2AndRC2-CBC" +#define NID_pbeWithMD2AndRC2_CBC 168 +#define OBJ_pbeWithMD2AndRC2_CBC OBJ_pkcs,5L,4L + +#define SN_pbeWithMD5AndRC2_CBC "PBE-MD5-RC2-64" +#define LN_pbeWithMD5AndRC2_CBC "pbeWithMD5AndRC2-CBC" +#define NID_pbeWithMD5AndRC2_CBC 169 +#define OBJ_pbeWithMD5AndRC2_CBC OBJ_pkcs,5L,6L + +#define SN_pbeWithSHA1AndDES_CBC "PBE-SHA1-DES" +#define LN_pbeWithSHA1AndDES_CBC "pbeWithSHA1AndDES-CBC" +#define NID_pbeWithSHA1AndDES_CBC 170 +#define OBJ_pbeWithSHA1AndDES_CBC OBJ_pkcs,5L,10L + +/* Extension request OIDs */ + +#define LN_ms_ext_req "Microsoft Extension Request" +#define SN_ms_ext_req "msExtReq" +#define NID_ms_ext_req 171 +#define OBJ_ms_ext_req 1L,3L,6L,1L,4L,1L,311L,2L,1L,14L + +#define LN_ext_req "Extension Request" +#define SN_ext_req "extReq" +#define NID_ext_req 172 +#define OBJ_ext_req OBJ_pkcs9,14L + +#define SN_name "name" +#define LN_name "name" +#define NID_name 173 +#define OBJ_name OBJ_X509,41L + +#define SN_dnQualifier "dnQualifier" +#define LN_dnQualifier "dnQualifier" +#define NID_dnQualifier 174 +#define OBJ_dnQualifier OBJ_X509,46L + +#define SN_id_pe "id-pe" +#define NID_id_pe 175 +#define OBJ_id_pe OBJ_id_pkix,1L + +#define SN_id_ad "id-ad" +#define NID_id_ad 176 +#define OBJ_id_ad OBJ_id_pkix,48L + +#define SN_info_access "authorityInfoAccess" +#define LN_info_access "Authority Information Access" +#define NID_info_access 177 +#define OBJ_info_access OBJ_id_pe,1L + +#define SN_ad_OCSP "OCSP" +#define LN_ad_OCSP "OCSP" +#define NID_ad_OCSP 178 +#define OBJ_ad_OCSP OBJ_id_ad,1L + +#define SN_ad_ca_issuers "caIssuers" +#define LN_ad_ca_issuers "CA Issuers" +#define NID_ad_ca_issuers 179 +#define OBJ_ad_ca_issuers OBJ_id_ad,2L + +#define SN_OCSP_sign "OCSPSigning" +#define LN_OCSP_sign "OCSP Signing" +#define NID_OCSP_sign 180 +#define OBJ_OCSP_sign OBJ_id_kp,9L +#endif /* USE_OBJ_MAC */ + +#include +#include + +#define OBJ_NAME_TYPE_UNDEF 0x00 +#define OBJ_NAME_TYPE_MD_METH 0x01 +#define OBJ_NAME_TYPE_CIPHER_METH 0x02 +#define OBJ_NAME_TYPE_PKEY_METH 0x03 +#define OBJ_NAME_TYPE_COMP_METH 0x04 +#define OBJ_NAME_TYPE_NUM 0x05 + +#define OBJ_NAME_ALIAS 0x8000 + +#define OBJ_BSEARCH_VALUE_ON_NOMATCH 0x01 +#define OBJ_BSEARCH_FIRST_VALUE_ON_MATCH 0x02 + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct obj_name_st + { + int type; + int alias; + const char *name; + const char *data; + } OBJ_NAME; + +#define OBJ_create_and_add_object(a,b,c) OBJ_create(a,b,c) + + +int OBJ_NAME_init(void); +int OBJ_NAME_new_index(unsigned long (*hash_func)(const char *), + int (*cmp_func)(const char *, const char *), + void (*free_func)(const char *, int, const char *)); +const char *OBJ_NAME_get(const char *name,int type); +int OBJ_NAME_add(const char *name,int type,const char *data); +int OBJ_NAME_remove(const char *name,int type); +void OBJ_NAME_cleanup(int type); /* -1 for everything */ +void OBJ_NAME_do_all(int type,void (*fn)(const OBJ_NAME *,void *arg), + void *arg); +void OBJ_NAME_do_all_sorted(int type,void (*fn)(const OBJ_NAME *,void *arg), + void *arg); + +ASN1_OBJECT * OBJ_dup(const ASN1_OBJECT *o); +ASN1_OBJECT * OBJ_nid2obj(int n); +const char * OBJ_nid2ln(int n); +const char * OBJ_nid2sn(int n); +int OBJ_obj2nid(const ASN1_OBJECT *o); +ASN1_OBJECT * OBJ_txt2obj(const char *s, int no_name); +int OBJ_obj2txt(char *buf, int buf_len, const ASN1_OBJECT *a, int no_name); +int OBJ_txt2nid(const char *s); +int OBJ_ln2nid(const char *s); +int OBJ_sn2nid(const char *s); +int OBJ_cmp(const ASN1_OBJECT *a,const ASN1_OBJECT *b); +const char * OBJ_bsearch(const char *key,const char *base,int num,int size, + int (*cmp)(const void *, const void *)); +const char * OBJ_bsearch_ex(const char *key,const char *base,int num, + int size, int (*cmp)(const void *, const void *), int flags); + +int OBJ_new_nid(int num); +int OBJ_add_object(const ASN1_OBJECT *obj); +int OBJ_create(const char *oid,const char *sn,const char *ln); +void OBJ_cleanup(void ); +int OBJ_create_objects(BIO *in); + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_OBJ_strings(void); + +/* Error codes for the OBJ functions. */ + +/* Function codes. */ +#define OBJ_F_OBJ_ADD_OBJECT 105 +#define OBJ_F_OBJ_CREATE 100 +#define OBJ_F_OBJ_DUP 101 +#define OBJ_F_OBJ_NAME_NEW_INDEX 106 +#define OBJ_F_OBJ_NID2LN 102 +#define OBJ_F_OBJ_NID2OBJ 103 +#define OBJ_F_OBJ_NID2SN 104 + +/* Reason codes. */ +#define OBJ_R_MALLOC_FAILURE 100 +#define OBJ_R_UNKNOWN_NID 101 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/ocsp.h b/production/3rdparty/openssl/include/openssl/ocsp.h new file mode 100644 index 00000000..53f3364a --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/ocsp.h @@ -0,0 +1,614 @@ +/* ocsp.h */ +/* Written by Tom Titchener for the OpenSSL + * project. */ + +/* History: + This file was transfered to Richard Levitte from CertCo by Kathy + Weinhold in mid-spring 2000 to be included in OpenSSL or released + as a patch kit. */ + +/* ==================================================================== + * Copyright (c) 1998-2000 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#ifndef HEADER_OCSP_H +#define HEADER_OCSP_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Various flags and values */ + +#define OCSP_DEFAULT_NONCE_LENGTH 16 + +#define OCSP_NOCERTS 0x1 +#define OCSP_NOINTERN 0x2 +#define OCSP_NOSIGS 0x4 +#define OCSP_NOCHAIN 0x8 +#define OCSP_NOVERIFY 0x10 +#define OCSP_NOEXPLICIT 0x20 +#define OCSP_NOCASIGN 0x40 +#define OCSP_NODELEGATED 0x80 +#define OCSP_NOCHECKS 0x100 +#define OCSP_TRUSTOTHER 0x200 +#define OCSP_RESPID_KEY 0x400 +#define OCSP_NOTIME 0x800 + +/* CertID ::= SEQUENCE { + * hashAlgorithm AlgorithmIdentifier, + * issuerNameHash OCTET STRING, -- Hash of Issuer's DN + * issuerKeyHash OCTET STRING, -- Hash of Issuers public key (excluding the tag & length fields) + * serialNumber CertificateSerialNumber } + */ +typedef struct ocsp_cert_id_st + { + X509_ALGOR *hashAlgorithm; + ASN1_OCTET_STRING *issuerNameHash; + ASN1_OCTET_STRING *issuerKeyHash; + ASN1_INTEGER *serialNumber; + } OCSP_CERTID; + +DECLARE_STACK_OF(OCSP_CERTID) + +/* Request ::= SEQUENCE { + * reqCert CertID, + * singleRequestExtensions [0] EXPLICIT Extensions OPTIONAL } + */ +typedef struct ocsp_one_request_st + { + OCSP_CERTID *reqCert; + STACK_OF(X509_EXTENSION) *singleRequestExtensions; + } OCSP_ONEREQ; + +DECLARE_STACK_OF(OCSP_ONEREQ) +DECLARE_ASN1_SET_OF(OCSP_ONEREQ) + + +/* TBSRequest ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * requestorName [1] EXPLICIT GeneralName OPTIONAL, + * requestList SEQUENCE OF Request, + * requestExtensions [2] EXPLICIT Extensions OPTIONAL } + */ +typedef struct ocsp_req_info_st + { + ASN1_INTEGER *version; + GENERAL_NAME *requestorName; + STACK_OF(OCSP_ONEREQ) *requestList; + STACK_OF(X509_EXTENSION) *requestExtensions; + } OCSP_REQINFO; + +/* Signature ::= SEQUENCE { + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + */ +typedef struct ocsp_signature_st + { + X509_ALGOR *signatureAlgorithm; + ASN1_BIT_STRING *signature; + STACK_OF(X509) *certs; + } OCSP_SIGNATURE; + +/* OCSPRequest ::= SEQUENCE { + * tbsRequest TBSRequest, + * optionalSignature [0] EXPLICIT Signature OPTIONAL } + */ +typedef struct ocsp_request_st + { + OCSP_REQINFO *tbsRequest; + OCSP_SIGNATURE *optionalSignature; /* OPTIONAL */ + } OCSP_REQUEST; + +/* OCSPResponseStatus ::= ENUMERATED { + * successful (0), --Response has valid confirmations + * malformedRequest (1), --Illegal confirmation request + * internalError (2), --Internal error in issuer + * tryLater (3), --Try again later + * --(4) is not used + * sigRequired (5), --Must sign the request + * unauthorized (6) --Request unauthorized + * } + */ +#define OCSP_RESPONSE_STATUS_SUCCESSFUL 0 +#define OCSP_RESPONSE_STATUS_MALFORMEDREQUEST 1 +#define OCSP_RESPONSE_STATUS_INTERNALERROR 2 +#define OCSP_RESPONSE_STATUS_TRYLATER 3 +#define OCSP_RESPONSE_STATUS_SIGREQUIRED 5 +#define OCSP_RESPONSE_STATUS_UNAUTHORIZED 6 + +/* ResponseBytes ::= SEQUENCE { + * responseType OBJECT IDENTIFIER, + * response OCTET STRING } + */ +typedef struct ocsp_resp_bytes_st + { + ASN1_OBJECT *responseType; + ASN1_OCTET_STRING *response; + } OCSP_RESPBYTES; + +/* OCSPResponse ::= SEQUENCE { + * responseStatus OCSPResponseStatus, + * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } + */ +typedef struct ocsp_response_st + { + ASN1_ENUMERATED *responseStatus; + OCSP_RESPBYTES *responseBytes; + } OCSP_RESPONSE; + +/* ResponderID ::= CHOICE { + * byName [1] Name, + * byKey [2] KeyHash } + */ +#define V_OCSP_RESPID_NAME 0 +#define V_OCSP_RESPID_KEY 1 +typedef struct ocsp_responder_id_st + { + int type; + union { + X509_NAME* byName; + ASN1_OCTET_STRING *byKey; + } value; + } OCSP_RESPID; +/* KeyHash ::= OCTET STRING --SHA-1 hash of responder's public key + * --(excluding the tag and length fields) + */ + +/* RevokedInfo ::= SEQUENCE { + * revocationTime GeneralizedTime, + * revocationReason [0] EXPLICIT CRLReason OPTIONAL } + */ +typedef struct ocsp_revoked_info_st + { + ASN1_GENERALIZEDTIME *revocationTime; + ASN1_ENUMERATED *revocationReason; + } OCSP_REVOKEDINFO; + +/* CertStatus ::= CHOICE { + * good [0] IMPLICIT NULL, + * revoked [1] IMPLICIT RevokedInfo, + * unknown [2] IMPLICIT UnknownInfo } + */ +#define V_OCSP_CERTSTATUS_GOOD 0 +#define V_OCSP_CERTSTATUS_REVOKED 1 +#define V_OCSP_CERTSTATUS_UNKNOWN 2 +typedef struct ocsp_cert_status_st + { + int type; + union { + ASN1_NULL *good; + OCSP_REVOKEDINFO *revoked; + ASN1_NULL *unknown; + } value; + } OCSP_CERTSTATUS; + +/* SingleResponse ::= SEQUENCE { + * certID CertID, + * certStatus CertStatus, + * thisUpdate GeneralizedTime, + * nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, + * singleExtensions [1] EXPLICIT Extensions OPTIONAL } + */ +typedef struct ocsp_single_response_st + { + OCSP_CERTID *certId; + OCSP_CERTSTATUS *certStatus; + ASN1_GENERALIZEDTIME *thisUpdate; + ASN1_GENERALIZEDTIME *nextUpdate; + STACK_OF(X509_EXTENSION) *singleExtensions; + } OCSP_SINGLERESP; + +DECLARE_STACK_OF(OCSP_SINGLERESP) +DECLARE_ASN1_SET_OF(OCSP_SINGLERESP) + +/* ResponseData ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * responderID ResponderID, + * producedAt GeneralizedTime, + * responses SEQUENCE OF SingleResponse, + * responseExtensions [1] EXPLICIT Extensions OPTIONAL } + */ +typedef struct ocsp_response_data_st + { + ASN1_INTEGER *version; + OCSP_RESPID *responderId; + ASN1_GENERALIZEDTIME *producedAt; + STACK_OF(OCSP_SINGLERESP) *responses; + STACK_OF(X509_EXTENSION) *responseExtensions; + } OCSP_RESPDATA; + +/* BasicOCSPResponse ::= SEQUENCE { + * tbsResponseData ResponseData, + * signatureAlgorithm AlgorithmIdentifier, + * signature BIT STRING, + * certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } + */ + /* Note 1: + The value for "signature" is specified in the OCSP rfc2560 as follows: + "The value for the signature SHALL be computed on the hash of the DER + encoding ResponseData." This means that you must hash the DER-encoded + tbsResponseData, and then run it through a crypto-signing function, which + will (at least w/RSA) do a hash-'n'-private-encrypt operation. This seems + a bit odd, but that's the spec. Also note that the data structures do not + leave anywhere to independently specify the algorithm used for the initial + hash. So, we look at the signature-specification algorithm, and try to do + something intelligent. -- Kathy Weinhold, CertCo */ + /* Note 2: + It seems that the mentioned passage from RFC 2560 (section 4.2.1) is open + for interpretation. I've done tests against another responder, and found + that it doesn't do the double hashing that the RFC seems to say one + should. Therefore, all relevant functions take a flag saying which + variant should be used. -- Richard Levitte, OpenSSL team and CeloCom */ +typedef struct ocsp_basic_response_st + { + OCSP_RESPDATA *tbsResponseData; + X509_ALGOR *signatureAlgorithm; + ASN1_BIT_STRING *signature; + STACK_OF(X509) *certs; + } OCSP_BASICRESP; + +/* + * CRLReason ::= ENUMERATED { + * unspecified (0), + * keyCompromise (1), + * cACompromise (2), + * affiliationChanged (3), + * superseded (4), + * cessationOfOperation (5), + * certificateHold (6), + * removeFromCRL (8) } + */ +#define OCSP_REVOKED_STATUS_NOSTATUS -1 +#define OCSP_REVOKED_STATUS_UNSPECIFIED 0 +#define OCSP_REVOKED_STATUS_KEYCOMPROMISE 1 +#define OCSP_REVOKED_STATUS_CACOMPROMISE 2 +#define OCSP_REVOKED_STATUS_AFFILIATIONCHANGED 3 +#define OCSP_REVOKED_STATUS_SUPERSEDED 4 +#define OCSP_REVOKED_STATUS_CESSATIONOFOPERATION 5 +#define OCSP_REVOKED_STATUS_CERTIFICATEHOLD 6 +#define OCSP_REVOKED_STATUS_REMOVEFROMCRL 8 + +/* CrlID ::= SEQUENCE { + * crlUrl [0] EXPLICIT IA5String OPTIONAL, + * crlNum [1] EXPLICIT INTEGER OPTIONAL, + * crlTime [2] EXPLICIT GeneralizedTime OPTIONAL } + */ +typedef struct ocsp_crl_id_st + { + ASN1_IA5STRING *crlUrl; + ASN1_INTEGER *crlNum; + ASN1_GENERALIZEDTIME *crlTime; + } OCSP_CRLID; + +/* ServiceLocator ::= SEQUENCE { + * issuer Name, + * locator AuthorityInfoAccessSyntax OPTIONAL } + */ +typedef struct ocsp_service_locator_st + { + X509_NAME* issuer; + STACK_OF(ACCESS_DESCRIPTION) *locator; + } OCSP_SERVICELOC; + +#define PEM_STRING_OCSP_REQUEST "OCSP REQUEST" +#define PEM_STRING_OCSP_RESPONSE "OCSP RESPONSE" + +#define d2i_OCSP_REQUEST_bio(bp,p) ASN1_d2i_bio_of(OCSP_REQUEST,OCSP_REQUEST_new,d2i_OCSP_REQUEST,bp,p) + +#define d2i_OCSP_RESPONSE_bio(bp,p) ASN1_d2i_bio_of(OCSP_RESPONSE,OCSP_RESPONSE_new,d2i_OCSP_RESPONSE,bp,p) + +#define PEM_read_bio_OCSP_REQUEST(bp,x,cb) (OCSP_REQUEST *)PEM_ASN1_read_bio( \ + (char *(*)())d2i_OCSP_REQUEST,PEM_STRING_OCSP_REQUEST,bp,(char **)x,cb,NULL) + +#define PEM_read_bio_OCSP_RESPONSE(bp,x,cb)(OCSP_RESPONSE *)PEM_ASN1_read_bio(\ + (char *(*)())d2i_OCSP_RESPONSE,PEM_STRING_OCSP_RESPONSE,bp,(char **)x,cb,NULL) + +#define PEM_write_bio_OCSP_REQUEST(bp,o) \ + PEM_ASN1_write_bio((int (*)())i2d_OCSP_REQUEST,PEM_STRING_OCSP_REQUEST,\ + bp,(char *)o, NULL,NULL,0,NULL,NULL) + +#define PEM_write_bio_OCSP_RESPONSE(bp,o) \ + PEM_ASN1_write_bio((int (*)())i2d_OCSP_RESPONSE,PEM_STRING_OCSP_RESPONSE,\ + bp,(char *)o, NULL,NULL,0,NULL,NULL) + +#define i2d_OCSP_RESPONSE_bio(bp,o) ASN1_i2d_bio_of(OCSP_RESPONSE,i2d_OCSP_RESPONSE,bp,o) + +#define i2d_OCSP_REQUEST_bio(bp,o) ASN1_i2d_bio_of(OCSP_REQUEST,i2d_OCSP_REQUEST,bp,o) + +#define OCSP_REQUEST_sign(o,pkey,md) \ + ASN1_item_sign(ASN1_ITEM_rptr(OCSP_REQINFO),\ + o->optionalSignature->signatureAlgorithm,NULL,\ + o->optionalSignature->signature,o->tbsRequest,pkey,md) + +#define OCSP_BASICRESP_sign(o,pkey,md,d) \ + ASN1_item_sign(ASN1_ITEM_rptr(OCSP_RESPDATA),o->signatureAlgorithm,NULL,\ + o->signature,o->tbsResponseData,pkey,md) + +#define OCSP_REQUEST_verify(a,r) ASN1_item_verify(ASN1_ITEM_rptr(OCSP_REQINFO),\ + a->optionalSignature->signatureAlgorithm,\ + a->optionalSignature->signature,a->tbsRequest,r) + +#define OCSP_BASICRESP_verify(a,r,d) ASN1_item_verify(ASN1_ITEM_rptr(OCSP_RESPDATA),\ + a->signatureAlgorithm,a->signature,a->tbsResponseData,r) + +#define ASN1_BIT_STRING_digest(data,type,md,len) \ + ASN1_item_digest(ASN1_ITEM_rptr(ASN1_BIT_STRING),type,data,md,len) + +#define OCSP_CERTID_dup(cid) ASN1_dup_of(OCSP_CERTID,i2d_OCSP_CERTID,d2i_OCSP_CERTID,cid) + +#define OCSP_CERTSTATUS_dup(cs)\ + (OCSP_CERTSTATUS*)ASN1_dup((int(*)())i2d_OCSP_CERTSTATUS,\ + (char *(*)())d2i_OCSP_CERTSTATUS,(char *)(cs)) + +OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, char *path, OCSP_REQUEST *req); + +OCSP_CERTID *OCSP_cert_to_id(const EVP_MD *dgst, X509 *subject, X509 *issuer); + +OCSP_CERTID *OCSP_cert_id_new(const EVP_MD *dgst, + X509_NAME *issuerName, + ASN1_BIT_STRING* issuerKey, + ASN1_INTEGER *serialNumber); + +OCSP_ONEREQ *OCSP_request_add0_id(OCSP_REQUEST *req, OCSP_CERTID *cid); + +int OCSP_request_add1_nonce(OCSP_REQUEST *req, unsigned char *val, int len); +int OCSP_basic_add1_nonce(OCSP_BASICRESP *resp, unsigned char *val, int len); +int OCSP_check_nonce(OCSP_REQUEST *req, OCSP_BASICRESP *bs); +int OCSP_copy_nonce(OCSP_BASICRESP *resp, OCSP_REQUEST *req); + +int OCSP_request_set1_name(OCSP_REQUEST *req, X509_NAME *nm); +int OCSP_request_add1_cert(OCSP_REQUEST *req, X509 *cert); + +int OCSP_request_sign(OCSP_REQUEST *req, + X509 *signer, + EVP_PKEY *key, + const EVP_MD *dgst, + STACK_OF(X509) *certs, + unsigned long flags); + +int OCSP_response_status(OCSP_RESPONSE *resp); +OCSP_BASICRESP *OCSP_response_get1_basic(OCSP_RESPONSE *resp); + +int OCSP_resp_count(OCSP_BASICRESP *bs); +OCSP_SINGLERESP *OCSP_resp_get0(OCSP_BASICRESP *bs, int idx); +int OCSP_resp_find(OCSP_BASICRESP *bs, OCSP_CERTID *id, int last); +int OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason, + ASN1_GENERALIZEDTIME **revtime, + ASN1_GENERALIZEDTIME **thisupd, + ASN1_GENERALIZEDTIME **nextupd); +int OCSP_resp_find_status(OCSP_BASICRESP *bs, OCSP_CERTID *id, int *status, + int *reason, + ASN1_GENERALIZEDTIME **revtime, + ASN1_GENERALIZEDTIME **thisupd, + ASN1_GENERALIZEDTIME **nextupd); +int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, + ASN1_GENERALIZEDTIME *nextupd, + long sec, long maxsec); + +int OCSP_request_verify(OCSP_REQUEST *req, STACK_OF(X509) *certs, X509_STORE *store, unsigned long flags); + +int OCSP_parse_url(char *url, char **phost, char **pport, char **ppath, int *pssl); + +int OCSP_id_issuer_cmp(OCSP_CERTID *a, OCSP_CERTID *b); +int OCSP_id_cmp(OCSP_CERTID *a, OCSP_CERTID *b); + +int OCSP_request_onereq_count(OCSP_REQUEST *req); +OCSP_ONEREQ *OCSP_request_onereq_get0(OCSP_REQUEST *req, int i); +OCSP_CERTID *OCSP_onereq_get0_id(OCSP_ONEREQ *one); +int OCSP_id_get0_info(ASN1_OCTET_STRING **piNameHash, ASN1_OBJECT **pmd, + ASN1_OCTET_STRING **pikeyHash, + ASN1_INTEGER **pserial, OCSP_CERTID *cid); +int OCSP_request_is_signed(OCSP_REQUEST *req); +OCSP_RESPONSE *OCSP_response_create(int status, OCSP_BASICRESP *bs); +OCSP_SINGLERESP *OCSP_basic_add1_status(OCSP_BASICRESP *rsp, + OCSP_CERTID *cid, + int status, int reason, + ASN1_TIME *revtime, + ASN1_TIME *thisupd, ASN1_TIME *nextupd); +int OCSP_basic_add1_cert(OCSP_BASICRESP *resp, X509 *cert); +int OCSP_basic_sign(OCSP_BASICRESP *brsp, + X509 *signer, EVP_PKEY *key, const EVP_MD *dgst, + STACK_OF(X509) *certs, unsigned long flags); + +ASN1_STRING *ASN1_STRING_encode(ASN1_STRING *s, i2d_of_void *i2d, + void *data, STACK_OF(ASN1_OBJECT) *sk); +#define ASN1_STRING_encode_of(type,s,i2d,data,sk) \ +((ASN1_STRING *(*)(ASN1_STRING *,I2D_OF(type),type *,STACK_OF(ASN1_OBJECT) *))openssl_fcast(ASN1_STRING_encode))(s,i2d,data,sk) + +X509_EXTENSION *OCSP_crlID_new(char *url, long *n, char *tim); + +X509_EXTENSION *OCSP_accept_responses_new(char **oids); + +X509_EXTENSION *OCSP_archive_cutoff_new(char* tim); + +X509_EXTENSION *OCSP_url_svcloc_new(X509_NAME* issuer, char **urls); + +int OCSP_REQUEST_get_ext_count(OCSP_REQUEST *x); +int OCSP_REQUEST_get_ext_by_NID(OCSP_REQUEST *x, int nid, int lastpos); +int OCSP_REQUEST_get_ext_by_OBJ(OCSP_REQUEST *x, ASN1_OBJECT *obj, int lastpos); +int OCSP_REQUEST_get_ext_by_critical(OCSP_REQUEST *x, int crit, int lastpos); +X509_EXTENSION *OCSP_REQUEST_get_ext(OCSP_REQUEST *x, int loc); +X509_EXTENSION *OCSP_REQUEST_delete_ext(OCSP_REQUEST *x, int loc); +void *OCSP_REQUEST_get1_ext_d2i(OCSP_REQUEST *x, int nid, int *crit, int *idx); +int OCSP_REQUEST_add1_ext_i2d(OCSP_REQUEST *x, int nid, void *value, int crit, + unsigned long flags); +int OCSP_REQUEST_add_ext(OCSP_REQUEST *x, X509_EXTENSION *ex, int loc); + +int OCSP_ONEREQ_get_ext_count(OCSP_ONEREQ *x); +int OCSP_ONEREQ_get_ext_by_NID(OCSP_ONEREQ *x, int nid, int lastpos); +int OCSP_ONEREQ_get_ext_by_OBJ(OCSP_ONEREQ *x, ASN1_OBJECT *obj, int lastpos); +int OCSP_ONEREQ_get_ext_by_critical(OCSP_ONEREQ *x, int crit, int lastpos); +X509_EXTENSION *OCSP_ONEREQ_get_ext(OCSP_ONEREQ *x, int loc); +X509_EXTENSION *OCSP_ONEREQ_delete_ext(OCSP_ONEREQ *x, int loc); +void *OCSP_ONEREQ_get1_ext_d2i(OCSP_ONEREQ *x, int nid, int *crit, int *idx); +int OCSP_ONEREQ_add1_ext_i2d(OCSP_ONEREQ *x, int nid, void *value, int crit, + unsigned long flags); +int OCSP_ONEREQ_add_ext(OCSP_ONEREQ *x, X509_EXTENSION *ex, int loc); + +int OCSP_BASICRESP_get_ext_count(OCSP_BASICRESP *x); +int OCSP_BASICRESP_get_ext_by_NID(OCSP_BASICRESP *x, int nid, int lastpos); +int OCSP_BASICRESP_get_ext_by_OBJ(OCSP_BASICRESP *x, ASN1_OBJECT *obj, int lastpos); +int OCSP_BASICRESP_get_ext_by_critical(OCSP_BASICRESP *x, int crit, int lastpos); +X509_EXTENSION *OCSP_BASICRESP_get_ext(OCSP_BASICRESP *x, int loc); +X509_EXTENSION *OCSP_BASICRESP_delete_ext(OCSP_BASICRESP *x, int loc); +void *OCSP_BASICRESP_get1_ext_d2i(OCSP_BASICRESP *x, int nid, int *crit, int *idx); +int OCSP_BASICRESP_add1_ext_i2d(OCSP_BASICRESP *x, int nid, void *value, int crit, + unsigned long flags); +int OCSP_BASICRESP_add_ext(OCSP_BASICRESP *x, X509_EXTENSION *ex, int loc); + +int OCSP_SINGLERESP_get_ext_count(OCSP_SINGLERESP *x); +int OCSP_SINGLERESP_get_ext_by_NID(OCSP_SINGLERESP *x, int nid, int lastpos); +int OCSP_SINGLERESP_get_ext_by_OBJ(OCSP_SINGLERESP *x, ASN1_OBJECT *obj, int lastpos); +int OCSP_SINGLERESP_get_ext_by_critical(OCSP_SINGLERESP *x, int crit, int lastpos); +X509_EXTENSION *OCSP_SINGLERESP_get_ext(OCSP_SINGLERESP *x, int loc); +X509_EXTENSION *OCSP_SINGLERESP_delete_ext(OCSP_SINGLERESP *x, int loc); +void *OCSP_SINGLERESP_get1_ext_d2i(OCSP_SINGLERESP *x, int nid, int *crit, int *idx); +int OCSP_SINGLERESP_add1_ext_i2d(OCSP_SINGLERESP *x, int nid, void *value, int crit, + unsigned long flags); +int OCSP_SINGLERESP_add_ext(OCSP_SINGLERESP *x, X509_EXTENSION *ex, int loc); + +DECLARE_ASN1_FUNCTIONS(OCSP_SINGLERESP) +DECLARE_ASN1_FUNCTIONS(OCSP_CERTSTATUS) +DECLARE_ASN1_FUNCTIONS(OCSP_REVOKEDINFO) +DECLARE_ASN1_FUNCTIONS(OCSP_BASICRESP) +DECLARE_ASN1_FUNCTIONS(OCSP_RESPDATA) +DECLARE_ASN1_FUNCTIONS(OCSP_RESPID) +DECLARE_ASN1_FUNCTIONS(OCSP_RESPONSE) +DECLARE_ASN1_FUNCTIONS(OCSP_RESPBYTES) +DECLARE_ASN1_FUNCTIONS(OCSP_ONEREQ) +DECLARE_ASN1_FUNCTIONS(OCSP_CERTID) +DECLARE_ASN1_FUNCTIONS(OCSP_REQUEST) +DECLARE_ASN1_FUNCTIONS(OCSP_SIGNATURE) +DECLARE_ASN1_FUNCTIONS(OCSP_REQINFO) +DECLARE_ASN1_FUNCTIONS(OCSP_CRLID) +DECLARE_ASN1_FUNCTIONS(OCSP_SERVICELOC) + +char *OCSP_response_status_str(long s); +char *OCSP_cert_status_str(long s); +char *OCSP_crl_reason_str(long s); + +int OCSP_REQUEST_print(BIO *bp, OCSP_REQUEST* a, unsigned long flags); +int OCSP_RESPONSE_print(BIO *bp, OCSP_RESPONSE* o, unsigned long flags); + +int OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, + X509_STORE *st, unsigned long flags); + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_OCSP_strings(void); + +/* Error codes for the OCSP functions. */ + +/* Function codes. */ +#define OCSP_F_ASN1_STRING_ENCODE 100 +#define OCSP_F_D2I_OCSP_NONCE 102 +#define OCSP_F_OCSP_BASIC_ADD1_STATUS 103 +#define OCSP_F_OCSP_BASIC_SIGN 104 +#define OCSP_F_OCSP_BASIC_VERIFY 105 +#define OCSP_F_OCSP_CERT_ID_NEW 101 +#define OCSP_F_OCSP_CHECK_DELEGATED 106 +#define OCSP_F_OCSP_CHECK_IDS 107 +#define OCSP_F_OCSP_CHECK_ISSUER 108 +#define OCSP_F_OCSP_CHECK_VALIDITY 115 +#define OCSP_F_OCSP_MATCH_ISSUERID 109 +#define OCSP_F_OCSP_PARSE_URL 114 +#define OCSP_F_OCSP_REQUEST_SIGN 110 +#define OCSP_F_OCSP_REQUEST_VERIFY 116 +#define OCSP_F_OCSP_RESPONSE_GET1_BASIC 111 +#define OCSP_F_OCSP_SENDREQ_BIO 112 +#define OCSP_F_REQUEST_VERIFY 113 + +/* Reason codes. */ +#define OCSP_R_BAD_DATA 100 +#define OCSP_R_CERTIFICATE_VERIFY_ERROR 101 +#define OCSP_R_DIGEST_ERR 102 +#define OCSP_R_ERROR_IN_NEXTUPDATE_FIELD 122 +#define OCSP_R_ERROR_IN_THISUPDATE_FIELD 123 +#define OCSP_R_ERROR_PARSING_URL 121 +#define OCSP_R_MISSING_OCSPSIGNING_USAGE 103 +#define OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE 124 +#define OCSP_R_NOT_BASIC_RESPONSE 104 +#define OCSP_R_NO_CERTIFICATES_IN_CHAIN 105 +#define OCSP_R_NO_CONTENT 106 +#define OCSP_R_NO_PUBLIC_KEY 107 +#define OCSP_R_NO_RESPONSE_DATA 108 +#define OCSP_R_NO_REVOKED_TIME 109 +#define OCSP_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE 110 +#define OCSP_R_REQUEST_NOT_SIGNED 128 +#define OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA 111 +#define OCSP_R_ROOT_CA_NOT_TRUSTED 112 +#define OCSP_R_SERVER_READ_ERROR 113 +#define OCSP_R_SERVER_RESPONSE_ERROR 114 +#define OCSP_R_SERVER_RESPONSE_PARSE_ERROR 115 +#define OCSP_R_SERVER_WRITE_ERROR 116 +#define OCSP_R_SIGNATURE_FAILURE 117 +#define OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND 118 +#define OCSP_R_STATUS_EXPIRED 125 +#define OCSP_R_STATUS_NOT_YET_VALID 126 +#define OCSP_R_STATUS_TOO_OLD 127 +#define OCSP_R_UNKNOWN_MESSAGE_DIGEST 119 +#define OCSP_R_UNKNOWN_NID 120 +#define OCSP_R_UNSUPPORTED_REQUESTORNAME_TYPE 129 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/opensslconf.h b/production/3rdparty/openssl/include/openssl/opensslconf.h new file mode 100644 index 00000000..1f5b6e60 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/opensslconf.h @@ -0,0 +1,206 @@ +/* opensslconf.h */ +/* WARNING: Generated automatically from opensslconf.h.in by Configure. */ + +/* OpenSSL was configured with the following options: */ +#ifndef OPENSSL_SYSNAME_WIN32 +# define OPENSSL_SYSNAME_WIN32 +#endif +#ifndef OPENSSL_DOING_MAKEDEPEND + +#ifndef OPENSSL_NO_GMP +# define OPENSSL_NO_GMP +#endif +#ifndef OPENSSL_NO_KRB5 +# define OPENSSL_NO_KRB5 +#endif +#ifndef OPENSSL_NO_MDC2 +# define OPENSSL_NO_MDC2 +#endif +#ifndef OPENSSL_NO_RC5 +# define OPENSSL_NO_RC5 +#endif + +#endif /* OPENSSL_DOING_MAKEDEPEND */ +#ifndef OPENSSL_THREADS +# define OPENSSL_THREADS +#endif + +/* The OPENSSL_NO_* macros are also defined as NO_* if the application + asks for it. This is a transient feature that is provided for those + who haven't had the time to do the appropriate changes in their + applications. */ +#ifdef OPENSSL_ALGORITHM_DEFINES +# if defined(OPENSSL_NO_GMP) && !defined(NO_GMP) +# define NO_GMP +# endif +# if defined(OPENSSL_NO_KRB5) && !defined(NO_KRB5) +# define NO_KRB5 +# endif +# if defined(OPENSSL_NO_MDC2) && !defined(NO_MDC2) +# define NO_MDC2 +# endif +# if defined(OPENSSL_NO_RC5) && !defined(NO_RC5) +# define NO_RC5 +# endif +#endif + +/* crypto/opensslconf.h.in */ + +/* Generate 80386 code? */ +#undef I386_ONLY + +#if !(defined(VMS) || defined(__VMS)) /* VMS uses logical names instead */ +#if defined(HEADER_CRYPTLIB_H) && !defined(OPENSSLDIR) +#define ENGINESDIR "c:/home/openssl_built/lib/engines" +#define OPENSSLDIR "c:/home/openssl_built/ssl" +#endif +#endif + +#undef OPENSSL_UNISTD +#define OPENSSL_UNISTD + +#undef OPENSSL_EXPORT_VAR_AS_FUNCTION +#define OPENSSL_EXPORT_VAR_AS_FUNCTION + +#if defined(HEADER_IDEA_H) && !defined(IDEA_INT) +#define IDEA_INT unsigned int +#endif + +#if defined(HEADER_MD2_H) && !defined(MD2_INT) +#define MD2_INT unsigned int +#endif + +#if defined(HEADER_RC2_H) && !defined(RC2_INT) +/* I need to put in a mod for the alpha - eay */ +#define RC2_INT unsigned int +#endif + +#if defined(HEADER_RC4_H) +#if !defined(RC4_INT) +/* using int types make the structure larger but make the code faster + * on most boxes I have tested - up to %20 faster. */ +/* + * I don't know what does "most" mean, but declaring "int" is a must on: + * - Intel P6 because partial register stalls are very expensive; + * - elder Alpha because it lacks byte load/store instructions; + */ +#define RC4_INT unsigned int +#endif +#if !defined(RC4_CHUNK) +/* + * This enables code handling data aligned at natural CPU word + * boundary. See crypto/rc4/rc4_enc.c for further details. + */ +#undef RC4_CHUNK +#endif +#endif + +#if (defined(HEADER_NEW_DES_H) || defined(HEADER_DES_H)) && !defined(DES_LONG) +/* If this is set to 'unsigned int' on a DEC Alpha, this gives about a + * %20 speed up (longs are 8 bytes, int's are 4). */ +#ifndef DES_LONG +#define DES_LONG unsigned long +#endif +#endif + +#if defined(HEADER_BN_H) && !defined(CONFIG_HEADER_BN_H) +#define CONFIG_HEADER_BN_H +#define BN_LLONG + +/* Should we define BN_DIV2W here? */ + +/* Only one for the following should be defined */ +/* The prime number generation stuff may not work when + * EIGHT_BIT but I don't care since I've only used this mode + * for debuging the bignum libraries */ +#undef SIXTY_FOUR_BIT_LONG +#undef SIXTY_FOUR_BIT +#define THIRTY_TWO_BIT +#undef SIXTEEN_BIT +#undef EIGHT_BIT +#endif + +#if defined(HEADER_RC4_LOCL_H) && !defined(CONFIG_HEADER_RC4_LOCL_H) +#define CONFIG_HEADER_RC4_LOCL_H +/* if this is defined data[i] is used instead of *data, this is a %20 + * speedup on x86 */ +#define RC4_INDEX +#endif + +#if defined(HEADER_BF_LOCL_H) && !defined(CONFIG_HEADER_BF_LOCL_H) +#define CONFIG_HEADER_BF_LOCL_H +#undef BF_PTR +#endif /* HEADER_BF_LOCL_H */ + +#if defined(HEADER_DES_LOCL_H) && !defined(CONFIG_HEADER_DES_LOCL_H) +#define CONFIG_HEADER_DES_LOCL_H +#ifndef DES_DEFAULT_OPTIONS +/* the following is tweaked from a config script, that is why it is a + * protected undef/define */ +#ifndef DES_PTR +#undef DES_PTR +#endif + +/* This helps C compiler generate the correct code for multiple functional + * units. It reduces register dependancies at the expense of 2 more + * registers */ +#ifndef DES_RISC1 +#undef DES_RISC1 +#endif + +#ifndef DES_RISC2 +#undef DES_RISC2 +#endif + +#if defined(DES_RISC1) && defined(DES_RISC2) +YOU SHOULD NOT HAVE BOTH DES_RISC1 AND DES_RISC2 DEFINED!!!!! +#endif + +/* Unroll the inner loop, this sometimes helps, sometimes hinders. + * Very mucy CPU dependant */ +#ifndef DES_UNROLL +#undef DES_UNROLL +#endif + +/* These default values were supplied by + * Peter Gutman + * They are only used if nothing else has been defined */ +#if !defined(DES_PTR) && !defined(DES_RISC1) && !defined(DES_RISC2) && !defined(DES_UNROLL) +/* Special defines which change the way the code is built depending on the + CPU and OS. For SGI machines you can use _MIPS_SZLONG (32 or 64) to find + even newer MIPS CPU's, but at the moment one size fits all for + optimization options. Older Sparc's work better with only UNROLL, but + there's no way to tell at compile time what it is you're running on */ + +#if defined( sun ) /* Newer Sparc's */ +# define DES_PTR +# define DES_RISC1 +# define DES_UNROLL +#elif defined( __ultrix ) /* Older MIPS */ +# define DES_PTR +# define DES_RISC2 +# define DES_UNROLL +#elif defined( __osf1__ ) /* Alpha */ +# define DES_PTR +# define DES_RISC2 +#elif defined ( _AIX ) /* RS6000 */ + /* Unknown */ +#elif defined( __hpux ) /* HP-PA */ + /* Unknown */ +#elif defined( __aux ) /* 68K */ + /* Unknown */ +#elif defined( __dgux ) /* 88K (but P6 in latest boxes) */ +# define DES_UNROLL +#elif defined( __sgi ) /* Newer MIPS */ +# define DES_PTR +# define DES_RISC2 +# define DES_UNROLL +#elif defined(i386) || defined(__i386__) /* x86 boxes, should be gcc */ +# define DES_PTR +# define DES_RISC1 +# define DES_UNROLL +#endif /* Systems-specific speed defines */ +#endif + +#endif /* DES_DEFAULT_OPTIONS */ +#endif /* HEADER_DES_LOCL_H */ diff --git a/production/3rdparty/openssl/include/openssl/opensslv.h b/production/3rdparty/openssl/include/openssl/opensslv.h new file mode 100644 index 00000000..c303b06b --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/opensslv.h @@ -0,0 +1,89 @@ +#ifndef HEADER_OPENSSLV_H +#define HEADER_OPENSSLV_H + +/* Numeric release version identifier: + * MNNFFPPS: major minor fix patch status + * The status nibble has one of the values 0 for development, 1 to e for betas + * 1 to 14, and f for release. The patch level is exactly that. + * For example: + * 0.9.3-dev 0x00903000 + * 0.9.3-beta1 0x00903001 + * 0.9.3-beta2-dev 0x00903002 + * 0.9.3-beta2 0x00903002 (same as ...beta2-dev) + * 0.9.3 0x0090300f + * 0.9.3a 0x0090301f + * 0.9.4 0x0090400f + * 1.2.3z 0x102031af + * + * For continuity reasons (because 0.9.5 is already out, and is coded + * 0x00905100), between 0.9.5 and 0.9.6 the coding of the patch level + * part is slightly different, by setting the highest bit. This means + * that 0.9.5a looks like this: 0x0090581f. At 0.9.6, we can start + * with 0x0090600S... + * + * (Prior to 0.9.3-dev a different scheme was used: 0.9.2b is 0x0922.) + * (Prior to 0.9.5a beta1, a different scheme was used: MMNNFFRBB for + * major minor fix final patch/beta) + */ +#define OPENSSL_VERSION_NUMBER 0x0090802fL +#ifdef OPENSSL_FIPS +#define OPENSSL_VERSION_TEXT "OpenSSL 0.9.8b-fips 04 May 2006" +#else +#define OPENSSL_VERSION_TEXT "OpenSSL 0.9.8b 04 May 2006" +#endif +#define OPENSSL_VERSION_PTEXT " part of " OPENSSL_VERSION_TEXT + + +/* The macros below are to be used for shared library (.so, .dll, ...) + * versioning. That kind of versioning works a bit differently between + * operating systems. The most usual scheme is to set a major and a minor + * number, and have the runtime loader check that the major number is equal + * to what it was at application link time, while the minor number has to + * be greater or equal to what it was at application link time. With this + * scheme, the version number is usually part of the file name, like this: + * + * libcrypto.so.0.9 + * + * Some unixen also make a softlink with the major verson number only: + * + * libcrypto.so.0 + * + * On Tru64 and IRIX 6.x it works a little bit differently. There, the + * shared library version is stored in the file, and is actually a series + * of versions, separated by colons. The rightmost version present in the + * library when linking an application is stored in the application to be + * matched at run time. When the application is run, a check is done to + * see if the library version stored in the application matches any of the + * versions in the version string of the library itself. + * This version string can be constructed in any way, depending on what + * kind of matching is desired. However, to implement the same scheme as + * the one used in the other unixen, all compatible versions, from lowest + * to highest, should be part of the string. Consecutive builds would + * give the following versions strings: + * + * 3.0 + * 3.0:3.1 + * 3.0:3.1:3.2 + * 4.0 + * 4.0:4.1 + * + * Notice how version 4 is completely incompatible with version, and + * therefore give the breach you can see. + * + * There may be other schemes as well that I haven't yet discovered. + * + * So, here's the way it works here: first of all, the library version + * number doesn't need at all to match the overall OpenSSL version. + * However, it's nice and more understandable if it actually does. + * The current library version is stored in the macro SHLIB_VERSION_NUMBER, + * which is just a piece of text in the format "M.m.e" (Major, minor, edit). + * For the sake of Tru64, IRIX, and any other OS that behaves in similar ways, + * we need to keep a history of version numbers, which is done in the + * macro SHLIB_VERSION_HISTORY. The numbers are separated by colons and + * should only keep the versions that are binary compatible with the current. + */ +#define SHLIB_VERSION_HISTORY "" +#define SHLIB_VERSION_NUMBER "0.9.8" + + +#endif /* HEADER_OPENSSLV_H */ diff --git a/production/3rdparty/openssl/include/openssl/ossl_typ.h b/production/3rdparty/openssl/include/openssl/ossl_typ.h new file mode 100644 index 00000000..9c335a18 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/ossl_typ.h @@ -0,0 +1,174 @@ +/* ==================================================================== + * Copyright (c) 1998-2001 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#ifndef HEADER_OPENSSL_TYPES_H +#define HEADER_OPENSSL_TYPES_H + +#include + +#ifdef NO_ASN1_TYPEDEFS +#define ASN1_INTEGER ASN1_STRING +#define ASN1_ENUMERATED ASN1_STRING +#define ASN1_BIT_STRING ASN1_STRING +#define ASN1_OCTET_STRING ASN1_STRING +#define ASN1_PRINTABLESTRING ASN1_STRING +#define ASN1_T61STRING ASN1_STRING +#define ASN1_IA5STRING ASN1_STRING +#define ASN1_UTCTIME ASN1_STRING +#define ASN1_GENERALIZEDTIME ASN1_STRING +#define ASN1_TIME ASN1_STRING +#define ASN1_GENERALSTRING ASN1_STRING +#define ASN1_UNIVERSALSTRING ASN1_STRING +#define ASN1_BMPSTRING ASN1_STRING +#define ASN1_VISIBLESTRING ASN1_STRING +#define ASN1_UTF8STRING ASN1_STRING +#define ASN1_BOOLEAN int +#define ASN1_NULL int +#else +typedef struct asn1_string_st ASN1_INTEGER; +typedef struct asn1_string_st ASN1_ENUMERATED; +typedef struct asn1_string_st ASN1_BIT_STRING; +typedef struct asn1_string_st ASN1_OCTET_STRING; +typedef struct asn1_string_st ASN1_PRINTABLESTRING; +typedef struct asn1_string_st ASN1_T61STRING; +typedef struct asn1_string_st ASN1_IA5STRING; +typedef struct asn1_string_st ASN1_GENERALSTRING; +typedef struct asn1_string_st ASN1_UNIVERSALSTRING; +typedef struct asn1_string_st ASN1_BMPSTRING; +typedef struct asn1_string_st ASN1_UTCTIME; +typedef struct asn1_string_st ASN1_TIME; +typedef struct asn1_string_st ASN1_GENERALIZEDTIME; +typedef struct asn1_string_st ASN1_VISIBLESTRING; +typedef struct asn1_string_st ASN1_UTF8STRING; +typedef int ASN1_BOOLEAN; +typedef int ASN1_NULL; +#endif + +#ifdef OPENSSL_SYS_WIN32 +#undef X509_NAME +#undef X509_CERT_PAIR +#undef PKCS7_ISSUER_AND_SERIAL +#endif + +#ifdef BIGNUM +#undef BIGNUM +#endif +typedef struct bignum_st BIGNUM; +typedef struct bignum_ctx BN_CTX; +typedef struct bn_blinding_st BN_BLINDING; +typedef struct bn_mont_ctx_st BN_MONT_CTX; +typedef struct bn_recp_ctx_st BN_RECP_CTX; +typedef struct bn_gencb_st BN_GENCB; + +typedef struct buf_mem_st BUF_MEM; + +typedef struct evp_cipher_st EVP_CIPHER; +typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX; +typedef struct env_md_st EVP_MD; +typedef struct env_md_ctx_st EVP_MD_CTX; +typedef struct evp_pkey_st EVP_PKEY; + +typedef struct dh_st DH; +typedef struct dh_method DH_METHOD; + +typedef struct dsa_st DSA; +typedef struct dsa_method DSA_METHOD; + +typedef struct rsa_st RSA; +typedef struct rsa_meth_st RSA_METHOD; + +typedef struct rand_meth_st RAND_METHOD; + +typedef struct ecdh_method ECDH_METHOD; +typedef struct ecdsa_method ECDSA_METHOD; + +typedef struct x509_st X509; +typedef struct X509_algor_st X509_ALGOR; +typedef struct X509_crl_st X509_CRL; +typedef struct X509_name_st X509_NAME; +typedef struct x509_store_st X509_STORE; +typedef struct x509_store_ctx_st X509_STORE_CTX; + +typedef struct v3_ext_ctx X509V3_CTX; +typedef struct conf_st CONF; + +typedef struct store_st STORE; +typedef struct store_method_st STORE_METHOD; + +typedef struct ui_st UI; +typedef struct ui_method_st UI_METHOD; + +typedef struct st_ERR_FNS ERR_FNS; + +typedef struct engine_st ENGINE; + +typedef struct X509_POLICY_NODE_st X509_POLICY_NODE; +typedef struct X509_POLICY_LEVEL_st X509_POLICY_LEVEL; +typedef struct X509_POLICY_TREE_st X509_POLICY_TREE; +typedef struct X509_POLICY_CACHE_st X509_POLICY_CACHE; + + /* If placed in pkcs12.h, we end up with a circular depency with pkcs7.h */ +#define DECLARE_PKCS12_STACK_OF(type) /* Nothing */ +#define IMPLEMENT_PKCS12_STACK_OF(type) /* Nothing */ + +typedef struct crypto_ex_data_st CRYPTO_EX_DATA; +/* Callback types for crypto.h */ +typedef int CRYPTO_EX_new(void *parent, void *ptr, CRYPTO_EX_DATA *ad, + int idx, long argl, void *argp); +typedef void CRYPTO_EX_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, + int idx, long argl, void *argp); +typedef int CRYPTO_EX_dup(CRYPTO_EX_DATA *to, CRYPTO_EX_DATA *from, void *from_d, + int idx, long argl, void *argp); + +#endif /* def HEADER_OPENSSL_TYPES_H */ diff --git a/production/3rdparty/openssl/include/openssl/pem.h b/production/3rdparty/openssl/include/openssl/pem.h new file mode 100644 index 00000000..7db6b423 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/pem.h @@ -0,0 +1,737 @@ +/* crypto/pem/pem.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_PEM_H +#define HEADER_PEM_H + +#include +#ifndef OPENSSL_NO_BIO +#include +#endif +#ifndef OPENSSL_NO_STACK +#include +#endif +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PEM_BUFSIZE 1024 + +#define PEM_OBJ_UNDEF 0 +#define PEM_OBJ_X509 1 +#define PEM_OBJ_X509_REQ 2 +#define PEM_OBJ_CRL 3 +#define PEM_OBJ_SSL_SESSION 4 +#define PEM_OBJ_PRIV_KEY 10 +#define PEM_OBJ_PRIV_RSA 11 +#define PEM_OBJ_PRIV_DSA 12 +#define PEM_OBJ_PRIV_DH 13 +#define PEM_OBJ_PUB_RSA 14 +#define PEM_OBJ_PUB_DSA 15 +#define PEM_OBJ_PUB_DH 16 +#define PEM_OBJ_DHPARAMS 17 +#define PEM_OBJ_DSAPARAMS 18 +#define PEM_OBJ_PRIV_RSA_PUBLIC 19 +#define PEM_OBJ_PRIV_ECDSA 20 +#define PEM_OBJ_PUB_ECDSA 21 +#define PEM_OBJ_ECPARAMETERS 22 + +#define PEM_ERROR 30 +#define PEM_DEK_DES_CBC 40 +#define PEM_DEK_IDEA_CBC 45 +#define PEM_DEK_DES_EDE 50 +#define PEM_DEK_DES_ECB 60 +#define PEM_DEK_RSA 70 +#define PEM_DEK_RSA_MD2 80 +#define PEM_DEK_RSA_MD5 90 + +#define PEM_MD_MD2 NID_md2 +#define PEM_MD_MD5 NID_md5 +#define PEM_MD_SHA NID_sha +#define PEM_MD_MD2_RSA NID_md2WithRSAEncryption +#define PEM_MD_MD5_RSA NID_md5WithRSAEncryption +#define PEM_MD_SHA_RSA NID_sha1WithRSAEncryption + +#define PEM_STRING_X509_OLD "X509 CERTIFICATE" +#define PEM_STRING_X509 "CERTIFICATE" +#define PEM_STRING_X509_PAIR "CERTIFICATE PAIR" +#define PEM_STRING_X509_TRUSTED "TRUSTED CERTIFICATE" +#define PEM_STRING_X509_REQ_OLD "NEW CERTIFICATE REQUEST" +#define PEM_STRING_X509_REQ "CERTIFICATE REQUEST" +#define PEM_STRING_X509_CRL "X509 CRL" +#define PEM_STRING_EVP_PKEY "ANY PRIVATE KEY" +#define PEM_STRING_PUBLIC "PUBLIC KEY" +#define PEM_STRING_RSA "RSA PRIVATE KEY" +#define PEM_STRING_RSA_PUBLIC "RSA PUBLIC KEY" +#define PEM_STRING_DSA "DSA PRIVATE KEY" +#define PEM_STRING_DSA_PUBLIC "DSA PUBLIC KEY" +#define PEM_STRING_PKCS7 "PKCS7" +#define PEM_STRING_PKCS8 "ENCRYPTED PRIVATE KEY" +#define PEM_STRING_PKCS8INF "PRIVATE KEY" +#define PEM_STRING_DHPARAMS "DH PARAMETERS" +#define PEM_STRING_SSL_SESSION "SSL SESSION PARAMETERS" +#define PEM_STRING_DSAPARAMS "DSA PARAMETERS" +#define PEM_STRING_ECDSA_PUBLIC "ECDSA PUBLIC KEY" +#define PEM_STRING_ECPARAMETERS "EC PARAMETERS" +#define PEM_STRING_ECPRIVATEKEY "EC PRIVATE KEY" + + /* Note that this structure is initialised by PEM_SealInit and cleaned up + by PEM_SealFinal (at least for now) */ +typedef struct PEM_Encode_Seal_st + { + EVP_ENCODE_CTX encode; + EVP_MD_CTX md; + EVP_CIPHER_CTX cipher; + } PEM_ENCODE_SEAL_CTX; + +/* enc_type is one off */ +#define PEM_TYPE_ENCRYPTED 10 +#define PEM_TYPE_MIC_ONLY 20 +#define PEM_TYPE_MIC_CLEAR 30 +#define PEM_TYPE_CLEAR 40 + +typedef struct pem_recip_st + { + char *name; + X509_NAME *dn; + + int cipher; + int key_enc; + /* char iv[8]; unused and wrong size */ + } PEM_USER; + +typedef struct pem_ctx_st + { + int type; /* what type of object */ + + struct { + int version; + int mode; + } proc_type; + + char *domain; + + struct { + int cipher; + /* unused, and wrong size + unsigned char iv[8]; */ + } DEK_info; + + PEM_USER *originator; + + int num_recipient; + PEM_USER **recipient; + +#ifndef OPENSSL_NO_STACK + STACK *x509_chain; /* certificate chain */ +#else + char *x509_chain; /* certificate chain */ +#endif + EVP_MD *md; /* signature type */ + + int md_enc; /* is the md encrypted or not? */ + int md_len; /* length of md_data */ + char *md_data; /* message digest, could be pkey encrypted */ + + EVP_CIPHER *dec; /* date encryption cipher */ + int key_len; /* key length */ + unsigned char *key; /* key */ + /* unused, and wrong size + unsigned char iv[8]; */ + + + int data_enc; /* is the data encrypted */ + int data_len; + unsigned char *data; + } PEM_CTX; + +/* These macros make the PEM_read/PEM_write functions easier to maintain and + * write. Now they are all implemented with either: + * IMPLEMENT_PEM_rw(...) or IMPLEMENT_PEM_rw_cb(...) + */ + +#ifdef OPENSSL_NO_FP_API + +#define IMPLEMENT_PEM_read_fp(name, type, str, asn1) /**/ +#define IMPLEMENT_PEM_write_fp(name, type, str, asn1) /**/ +#define IMPLEMENT_PEM_write_cb_fp(name, type, str, asn1) /**/ + +#else + +#define IMPLEMENT_PEM_read_fp(name, type, str, asn1) \ +type *PEM_read_##name(FILE *fp, type **x, pem_password_cb *cb, void *u)\ +{ \ +return(((type *(*)(D2I_OF(type),char *,FILE *,type **,pem_password_cb *,void *))openssl_fcast(PEM_ASN1_read))(d2i_##asn1, str,fp,x,cb,u)); \ +} \ + +#define IMPLEMENT_PEM_write_fp(name, type, str, asn1) \ +int PEM_write_##name(FILE *fp, type *x) \ +{ \ +return(((int (*)(I2D_OF(type),const char *,FILE *,type *, const EVP_CIPHER *,unsigned char *,int, pem_password_cb *,void *))openssl_fcast(PEM_ASN1_write))(i2d_##asn1,str,fp,x,NULL,NULL,0,NULL,NULL)); \ +} + +#define IMPLEMENT_PEM_write_fp_const(name, type, str, asn1) \ +int PEM_write_##name(FILE *fp, const type *x) \ +{ \ +return(((int (*)(I2D_OF_const(type),const char *,FILE *, const type *, const EVP_CIPHER *,unsigned char *,int, pem_password_cb *,void *))openssl_fcast(PEM_ASN1_write))(i2d_##asn1,str,fp,x,NULL,NULL,0,NULL,NULL)); \ +} + +#define IMPLEMENT_PEM_write_cb_fp(name, type, str, asn1) \ +int PEM_write_##name(FILE *fp, type *x, const EVP_CIPHER *enc, \ + unsigned char *kstr, int klen, pem_password_cb *cb, \ + void *u) \ + { \ + return(((int (*)(I2D_OF(type),const char *,FILE *,type *, const EVP_CIPHER *,unsigned char *,int, pem_password_cb *,void *))openssl_fcast(PEM_ASN1_write))(i2d_##asn1,str,fp,x,enc,kstr,klen,cb,u)); \ + } + +#define IMPLEMENT_PEM_write_cb_fp_const(name, type, str, asn1) \ +int PEM_write_##name(FILE *fp, type *x, const EVP_CIPHER *enc, \ + unsigned char *kstr, int klen, pem_password_cb *cb, \ + void *u) \ + { \ + return(((int (*)(I2D_OF_const(type),const char *,FILE *,type *, const EVP_CIPHER *,unsigned char *,int, pem_password_cb *,void *))openssl_fcast(PEM_ASN1_write))(i2d_##asn1,str,fp,x,enc,kstr,klen,cb,u)); \ + } + +#endif + +#define IMPLEMENT_PEM_read_bio(name, type, str, asn1) \ +type *PEM_read_bio_##name(BIO *bp, type **x, pem_password_cb *cb, void *u)\ +{ \ +return(((type *(*)(D2I_OF(type),const char *,BIO *,type **,pem_password_cb *,void *))openssl_fcast(PEM_ASN1_read_bio))(d2i_##asn1, str,bp,x,cb,u)); \ +} + +#define IMPLEMENT_PEM_write_bio(name, type, str, asn1) \ +int PEM_write_bio_##name(BIO *bp, type *x) \ +{ \ +return(((int (*)(I2D_OF(type),const char *,BIO *,type *, const EVP_CIPHER *,unsigned char *,int, pem_password_cb *,void *))openssl_fcast(PEM_ASN1_write_bio))(i2d_##asn1,str,bp,x,NULL,NULL,0,NULL,NULL)); \ +} + +#define IMPLEMENT_PEM_write_bio_const(name, type, str, asn1) \ +int PEM_write_bio_##name(BIO *bp, const type *x) \ +{ \ +return(((int (*)(I2D_OF_const(type),const char *,BIO *,const type *, const EVP_CIPHER *,unsigned char *,int, pem_password_cb *,void *))openssl_fcast(PEM_ASN1_write_bio))(i2d_##asn1,str,bp,x,NULL,NULL,0,NULL,NULL)); \ +} + +#define IMPLEMENT_PEM_write_cb_bio(name, type, str, asn1) \ +int PEM_write_bio_##name(BIO *bp, type *x, const EVP_CIPHER *enc, \ + unsigned char *kstr, int klen, pem_password_cb *cb, void *u) \ + { \ + return(((int (*)(I2D_OF(type),const char *,BIO *,type *,const EVP_CIPHER *,unsigned char *,int,pem_password_cb *,void *))openssl_fcast(PEM_ASN1_write_bio))(i2d_##asn1,str,bp,x,enc,kstr,klen,cb,u)); \ + } + +#define IMPLEMENT_PEM_write_cb_bio_const(name, type, str, asn1) \ +int PEM_write_bio_##name(BIO *bp, type *x, const EVP_CIPHER *enc, \ + unsigned char *kstr, int klen, pem_password_cb *cb, void *u) \ + { \ + return(((int (*)(I2D_OF_const(type),const char *,BIO *,type *,const EVP_CIPHER *,unsigned char *,int,pem_password_cb *,void *))openssl_fcast(PEM_ASN1_write_bio))(i2d_##asn1,str,bp,x,enc,kstr,klen,cb,u)); \ + } + +#define IMPLEMENT_PEM_write(name, type, str, asn1) \ + IMPLEMENT_PEM_write_bio(name, type, str, asn1) \ + IMPLEMENT_PEM_write_fp(name, type, str, asn1) + +#define IMPLEMENT_PEM_write_const(name, type, str, asn1) \ + IMPLEMENT_PEM_write_bio_const(name, type, str, asn1) \ + IMPLEMENT_PEM_write_fp_const(name, type, str, asn1) + +#define IMPLEMENT_PEM_write_cb(name, type, str, asn1) \ + IMPLEMENT_PEM_write_cb_bio(name, type, str, asn1) \ + IMPLEMENT_PEM_write_cb_fp(name, type, str, asn1) + +#define IMPLEMENT_PEM_write_cb_const(name, type, str, asn1) \ + IMPLEMENT_PEM_write_cb_bio_const(name, type, str, asn1) \ + IMPLEMENT_PEM_write_cb_fp_const(name, type, str, asn1) + +#define IMPLEMENT_PEM_read(name, type, str, asn1) \ + IMPLEMENT_PEM_read_bio(name, type, str, asn1) \ + IMPLEMENT_PEM_read_fp(name, type, str, asn1) + +#define IMPLEMENT_PEM_rw(name, type, str, asn1) \ + IMPLEMENT_PEM_read(name, type, str, asn1) \ + IMPLEMENT_PEM_write(name, type, str, asn1) + +#define IMPLEMENT_PEM_rw_const(name, type, str, asn1) \ + IMPLEMENT_PEM_read(name, type, str, asn1) \ + IMPLEMENT_PEM_write_const(name, type, str, asn1) + +#define IMPLEMENT_PEM_rw_cb(name, type, str, asn1) \ + IMPLEMENT_PEM_read(name, type, str, asn1) \ + IMPLEMENT_PEM_write_cb(name, type, str, asn1) + +/* These are the same except they are for the declarations */ + +#if defined(OPENSSL_SYS_WIN16) || defined(OPENSSL_NO_FP_API) + +#define DECLARE_PEM_read_fp(name, type) /**/ +#define DECLARE_PEM_write_fp(name, type) /**/ +#define DECLARE_PEM_write_cb_fp(name, type) /**/ + +#else + +#define DECLARE_PEM_read_fp(name, type) \ + type *PEM_read_##name(FILE *fp, type **x, pem_password_cb *cb, void *u); + +#define DECLARE_PEM_write_fp(name, type) \ + int PEM_write_##name(FILE *fp, type *x); + +#define DECLARE_PEM_write_fp_const(name, type) \ + int PEM_write_##name(FILE *fp, const type *x); + +#define DECLARE_PEM_write_cb_fp(name, type) \ + int PEM_write_##name(FILE *fp, type *x, const EVP_CIPHER *enc, \ + unsigned char *kstr, int klen, pem_password_cb *cb, void *u); + +#endif + +#ifndef OPENSSL_NO_BIO +#define DECLARE_PEM_read_bio(name, type) \ + type *PEM_read_bio_##name(BIO *bp, type **x, pem_password_cb *cb, void *u); + +#define DECLARE_PEM_write_bio(name, type) \ + int PEM_write_bio_##name(BIO *bp, type *x); + +#define DECLARE_PEM_write_bio_const(name, type) \ + int PEM_write_bio_##name(BIO *bp, const type *x); + +#define DECLARE_PEM_write_cb_bio(name, type) \ + int PEM_write_bio_##name(BIO *bp, type *x, const EVP_CIPHER *enc, \ + unsigned char *kstr, int klen, pem_password_cb *cb, void *u); + +#else + +#define DECLARE_PEM_read_bio(name, type) /**/ +#define DECLARE_PEM_write_bio(name, type) /**/ +#define DECLARE_PEM_write_cb_bio(name, type) /**/ + +#endif + +#define DECLARE_PEM_write(name, type) \ + DECLARE_PEM_write_bio(name, type) \ + DECLARE_PEM_write_fp(name, type) + +#define DECLARE_PEM_write_const(name, type) \ + DECLARE_PEM_write_bio_const(name, type) \ + DECLARE_PEM_write_fp_const(name, type) + +#define DECLARE_PEM_write_cb(name, type) \ + DECLARE_PEM_write_cb_bio(name, type) \ + DECLARE_PEM_write_cb_fp(name, type) + +#define DECLARE_PEM_read(name, type) \ + DECLARE_PEM_read_bio(name, type) \ + DECLARE_PEM_read_fp(name, type) + +#define DECLARE_PEM_rw(name, type) \ + DECLARE_PEM_read(name, type) \ + DECLARE_PEM_write(name, type) + +#define DECLARE_PEM_rw_const(name, type) \ + DECLARE_PEM_read(name, type) \ + DECLARE_PEM_write_const(name, type) + +#define DECLARE_PEM_rw_cb(name, type) \ + DECLARE_PEM_read(name, type) \ + DECLARE_PEM_write_cb(name, type) + +#ifdef SSLEAY_MACROS + +#define PEM_write_SSL_SESSION(fp,x) \ + PEM_ASN1_write((int (*)())i2d_SSL_SESSION, \ + PEM_STRING_SSL_SESSION,fp, (char *)x, NULL,NULL,0,NULL,NULL) +#define PEM_write_X509(fp,x) \ + PEM_ASN1_write((int (*)())i2d_X509,PEM_STRING_X509,fp, \ + (char *)x, NULL,NULL,0,NULL,NULL) +#define PEM_write_X509_REQ(fp,x) PEM_ASN1_write( \ + (int (*)())i2d_X509_REQ,PEM_STRING_X509_REQ,fp,(char *)x, \ + NULL,NULL,0,NULL,NULL) +#define PEM_write_X509_CRL(fp,x) \ + PEM_ASN1_write((int (*)())i2d_X509_CRL,PEM_STRING_X509_CRL, \ + fp,(char *)x, NULL,NULL,0,NULL,NULL) +#define PEM_write_RSAPrivateKey(fp,x,enc,kstr,klen,cb,u) \ + PEM_ASN1_write((int (*)())i2d_RSAPrivateKey,PEM_STRING_RSA,fp,\ + (char *)x,enc,kstr,klen,cb,u) +#define PEM_write_RSAPublicKey(fp,x) \ + PEM_ASN1_write((int (*)())i2d_RSAPublicKey,\ + PEM_STRING_RSA_PUBLIC,fp,(char *)x,NULL,NULL,0,NULL,NULL) +#define PEM_write_DSAPrivateKey(fp,x,enc,kstr,klen,cb,u) \ + PEM_ASN1_write((int (*)())i2d_DSAPrivateKey,PEM_STRING_DSA,fp,\ + (char *)x,enc,kstr,klen,cb,u) +#define PEM_write_PrivateKey(bp,x,enc,kstr,klen,cb,u) \ + PEM_ASN1_write((int (*)())i2d_PrivateKey,\ + (((x)->type == EVP_PKEY_DSA)?PEM_STRING_DSA:PEM_STRING_RSA),\ + bp,(char *)x,enc,kstr,klen,cb,u) +#define PEM_write_PKCS7(fp,x) \ + PEM_ASN1_write((int (*)())i2d_PKCS7,PEM_STRING_PKCS7,fp, \ + (char *)x, NULL,NULL,0,NULL,NULL) +#define PEM_write_DHparams(fp,x) \ + PEM_ASN1_write((int (*)())i2d_DHparams,PEM_STRING_DHPARAMS,fp,\ + (char *)x,NULL,NULL,0,NULL,NULL) + +#define PEM_write_NETSCAPE_CERT_SEQUENCE(fp,x) \ + PEM_ASN1_write((int (*)())i2d_NETSCAPE_CERT_SEQUENCE, \ + PEM_STRING_X509,fp, \ + (char *)x, NULL,NULL,0,NULL,NULL) + +#define PEM_read_SSL_SESSION(fp,x,cb,u) (SSL_SESSION *)PEM_ASN1_read( \ + (char *(*)())d2i_SSL_SESSION,PEM_STRING_SSL_SESSION,fp,(char **)x,cb,u) +#define PEM_read_X509(fp,x,cb,u) (X509 *)PEM_ASN1_read( \ + (char *(*)())d2i_X509,PEM_STRING_X509,fp,(char **)x,cb,u) +#define PEM_read_X509_REQ(fp,x,cb,u) (X509_REQ *)PEM_ASN1_read( \ + (char *(*)())d2i_X509_REQ,PEM_STRING_X509_REQ,fp,(char **)x,cb,u) +#define PEM_read_X509_CRL(fp,x,cb,u) (X509_CRL *)PEM_ASN1_read( \ + (char *(*)())d2i_X509_CRL,PEM_STRING_X509_CRL,fp,(char **)x,cb,u) +#define PEM_read_RSAPrivateKey(fp,x,cb,u) (RSA *)PEM_ASN1_read( \ + (char *(*)())d2i_RSAPrivateKey,PEM_STRING_RSA,fp,(char **)x,cb,u) +#define PEM_read_RSAPublicKey(fp,x,cb,u) (RSA *)PEM_ASN1_read( \ + (char *(*)())d2i_RSAPublicKey,PEM_STRING_RSA_PUBLIC,fp,(char **)x,cb,u) +#define PEM_read_DSAPrivateKey(fp,x,cb,u) (DSA *)PEM_ASN1_read( \ + (char *(*)())d2i_DSAPrivateKey,PEM_STRING_DSA,fp,(char **)x,cb,u) +#define PEM_read_PrivateKey(fp,x,cb,u) (EVP_PKEY *)PEM_ASN1_read( \ + (char *(*)())d2i_PrivateKey,PEM_STRING_EVP_PKEY,fp,(char **)x,cb,u) +#define PEM_read_PKCS7(fp,x,cb,u) (PKCS7 *)PEM_ASN1_read( \ + (char *(*)())d2i_PKCS7,PEM_STRING_PKCS7,fp,(char **)x,cb,u) +#define PEM_read_DHparams(fp,x,cb,u) (DH *)PEM_ASN1_read( \ + (char *(*)())d2i_DHparams,PEM_STRING_DHPARAMS,fp,(char **)x,cb,u) + +#define PEM_read_NETSCAPE_CERT_SEQUENCE(fp,x,cb,u) \ + (NETSCAPE_CERT_SEQUENCE *)PEM_ASN1_read( \ + (char *(*)())d2i_NETSCAPE_CERT_SEQUENCE,PEM_STRING_X509,fp,\ + (char **)x,cb,u) + +#define PEM_write_bio_X509(bp,x) \ + PEM_ASN1_write_bio((int (*)())i2d_X509,PEM_STRING_X509,bp, \ + (char *)x, NULL,NULL,0,NULL,NULL) +#define PEM_write_bio_X509_REQ(bp,x) PEM_ASN1_write_bio( \ + (int (*)())i2d_X509_REQ,PEM_STRING_X509_REQ,bp,(char *)x, \ + NULL,NULL,0,NULL,NULL) +#define PEM_write_bio_X509_CRL(bp,x) \ + PEM_ASN1_write_bio((int (*)())i2d_X509_CRL,PEM_STRING_X509_CRL,\ + bp,(char *)x, NULL,NULL,0,NULL,NULL) +#define PEM_write_bio_RSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \ + PEM_ASN1_write_bio((int (*)())i2d_RSAPrivateKey,PEM_STRING_RSA,\ + bp,(char *)x,enc,kstr,klen,cb,u) +#define PEM_write_bio_RSAPublicKey(bp,x) \ + PEM_ASN1_write_bio((int (*)())i2d_RSAPublicKey, \ + PEM_STRING_RSA_PUBLIC,\ + bp,(char *)x,NULL,NULL,0,NULL,NULL) +#define PEM_write_bio_DSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \ + PEM_ASN1_write_bio((int (*)())i2d_DSAPrivateKey,PEM_STRING_DSA,\ + bp,(char *)x,enc,kstr,klen,cb,u) +#define PEM_write_bio_PrivateKey(bp,x,enc,kstr,klen,cb,u) \ + PEM_ASN1_write_bio((int (*)())i2d_PrivateKey,\ + (((x)->type == EVP_PKEY_DSA)?PEM_STRING_DSA:PEM_STRING_RSA),\ + bp,(char *)x,enc,kstr,klen,cb,u) +#define PEM_write_bio_PKCS7(bp,x) \ + PEM_ASN1_write_bio((int (*)())i2d_PKCS7,PEM_STRING_PKCS7,bp, \ + (char *)x, NULL,NULL,0,NULL,NULL) +#define PEM_write_bio_DHparams(bp,x) \ + PEM_ASN1_write_bio((int (*)())i2d_DHparams,PEM_STRING_DHPARAMS,\ + bp,(char *)x,NULL,NULL,0,NULL,NULL) +#define PEM_write_bio_DSAparams(bp,x) \ + PEM_ASN1_write_bio((int (*)())i2d_DSAparams, \ + PEM_STRING_DSAPARAMS,bp,(char *)x,NULL,NULL,0,NULL,NULL) + +#define PEM_write_bio_NETSCAPE_CERT_SEQUENCE(bp,x) \ + PEM_ASN1_write_bio((int (*)())i2d_NETSCAPE_CERT_SEQUENCE, \ + PEM_STRING_X509,bp, \ + (char *)x, NULL,NULL,0,NULL,NULL) + +#define PEM_read_bio_X509(bp,x,cb,u) (X509 *)PEM_ASN1_read_bio( \ + (char *(*)())d2i_X509,PEM_STRING_X509,bp,(char **)x,cb,u) +#define PEM_read_bio_X509_REQ(bp,x,cb,u) (X509_REQ *)PEM_ASN1_read_bio( \ + (char *(*)())d2i_X509_REQ,PEM_STRING_X509_REQ,bp,(char **)x,cb,u) +#define PEM_read_bio_X509_CRL(bp,x,cb,u) (X509_CRL *)PEM_ASN1_read_bio( \ + (char *(*)())d2i_X509_CRL,PEM_STRING_X509_CRL,bp,(char **)x,cb,u) +#define PEM_read_bio_RSAPrivateKey(bp,x,cb,u) (RSA *)PEM_ASN1_read_bio( \ + (char *(*)())d2i_RSAPrivateKey,PEM_STRING_RSA,bp,(char **)x,cb,u) +#define PEM_read_bio_RSAPublicKey(bp,x,cb,u) (RSA *)PEM_ASN1_read_bio( \ + (char *(*)())d2i_RSAPublicKey,PEM_STRING_RSA_PUBLIC,bp,(char **)x,cb,u) +#define PEM_read_bio_DSAPrivateKey(bp,x,cb,u) (DSA *)PEM_ASN1_read_bio( \ + (char *(*)())d2i_DSAPrivateKey,PEM_STRING_DSA,bp,(char **)x,cb,u) +#define PEM_read_bio_PrivateKey(bp,x,cb,u) (EVP_PKEY *)PEM_ASN1_read_bio( \ + (char *(*)())d2i_PrivateKey,PEM_STRING_EVP_PKEY,bp,(char **)x,cb,u) + +#define PEM_read_bio_PKCS7(bp,x,cb,u) (PKCS7 *)PEM_ASN1_read_bio( \ + (char *(*)())d2i_PKCS7,PEM_STRING_PKCS7,bp,(char **)x,cb,u) +#define PEM_read_bio_DHparams(bp,x,cb,u) (DH *)PEM_ASN1_read_bio( \ + (char *(*)())d2i_DHparams,PEM_STRING_DHPARAMS,bp,(char **)x,cb,u) +#define PEM_read_bio_DSAparams(bp,x,cb,u) (DSA *)PEM_ASN1_read_bio( \ + (char *(*)())d2i_DSAparams,PEM_STRING_DSAPARAMS,bp,(char **)x,cb,u) + +#define PEM_read_bio_NETSCAPE_CERT_SEQUENCE(bp,x,cb,u) \ + (NETSCAPE_CERT_SEQUENCE *)PEM_ASN1_read_bio( \ + (char *(*)())d2i_NETSCAPE_CERT_SEQUENCE,PEM_STRING_X509,bp,\ + (char **)x,cb,u) + +#endif + +#if 1 +/* "userdata": new with OpenSSL 0.9.4 */ +typedef int pem_password_cb(char *buf, int size, int rwflag, void *userdata); +#else +/* OpenSSL 0.9.3, 0.9.3a */ +typedef int pem_password_cb(char *buf, int size, int rwflag); +#endif + +int PEM_get_EVP_CIPHER_INFO(char *header, EVP_CIPHER_INFO *cipher); +int PEM_do_header (EVP_CIPHER_INFO *cipher, unsigned char *data,long *len, + pem_password_cb *callback,void *u); + +#ifndef OPENSSL_NO_BIO +int PEM_read_bio(BIO *bp, char **name, char **header, + unsigned char **data,long *len); +int PEM_write_bio(BIO *bp,const char *name,char *hdr,unsigned char *data, + long len); +int PEM_bytes_read_bio(unsigned char **pdata, long *plen, char **pnm, const char *name, BIO *bp, + pem_password_cb *cb, void *u); +void * PEM_ASN1_read_bio(d2i_of_void *d2i, const char *name, BIO *bp, + void **x, pem_password_cb *cb, void *u); +#define PEM_ASN1_read_bio_of(type,d2i,name,bp,x,cb,u) \ +((type *(*)(D2I_OF(type),const char *,BIO *,type **,pem_password_cb *,void *))openssl_fcast(PEM_ASN1_read_bio))(d2i,name,bp,x,cb,u) +int PEM_ASN1_write_bio(i2d_of_void *i2d,const char *name,BIO *bp,char *x, + const EVP_CIPHER *enc,unsigned char *kstr,int klen, + pem_password_cb *cb, void *u); +#define PEM_ASN1_write_bio_of(type,i2d,name,bp,x,enc,kstr,klen,cb,u) \ + ((int (*)(I2D_OF(type),const char *,BIO *,type *, const EVP_CIPHER *,unsigned char *,int, pem_password_cb *,void *))openssl_fcast(PEM_ASN1_write_bio))(i2d,name,bp,x,enc,kstr,klen,cb,u) + +STACK_OF(X509_INFO) * PEM_X509_INFO_read_bio(BIO *bp, STACK_OF(X509_INFO) *sk, pem_password_cb *cb, void *u); +int PEM_X509_INFO_write_bio(BIO *bp,X509_INFO *xi, EVP_CIPHER *enc, + unsigned char *kstr, int klen, pem_password_cb *cd, void *u); +#endif + +#ifndef OPENSSL_SYS_WIN16 +int PEM_read(FILE *fp, char **name, char **header, + unsigned char **data,long *len); +int PEM_write(FILE *fp,char *name,char *hdr,unsigned char *data,long len); +void * PEM_ASN1_read(d2i_of_void *d2i, const char *name, FILE *fp, void **x, + pem_password_cb *cb, void *u); +int PEM_ASN1_write(i2d_of_void *i2d,const char *name,FILE *fp, + char *x,const EVP_CIPHER *enc,unsigned char *kstr, + int klen,pem_password_cb *callback, void *u); +STACK_OF(X509_INFO) * PEM_X509_INFO_read(FILE *fp, STACK_OF(X509_INFO) *sk, + pem_password_cb *cb, void *u); +#endif + +int PEM_SealInit(PEM_ENCODE_SEAL_CTX *ctx, EVP_CIPHER *type, + EVP_MD *md_type, unsigned char **ek, int *ekl, + unsigned char *iv, EVP_PKEY **pubk, int npubk); +void PEM_SealUpdate(PEM_ENCODE_SEAL_CTX *ctx, unsigned char *out, int *outl, + unsigned char *in, int inl); +int PEM_SealFinal(PEM_ENCODE_SEAL_CTX *ctx, unsigned char *sig,int *sigl, + unsigned char *out, int *outl, EVP_PKEY *priv); + +void PEM_SignInit(EVP_MD_CTX *ctx, EVP_MD *type); +void PEM_SignUpdate(EVP_MD_CTX *ctx,unsigned char *d,unsigned int cnt); +int PEM_SignFinal(EVP_MD_CTX *ctx, unsigned char *sigret, + unsigned int *siglen, EVP_PKEY *pkey); + +int PEM_def_callback(char *buf, int num, int w, void *key); +void PEM_proc_type(char *buf, int type); +void PEM_dek_info(char *buf, const char *type, int len, char *str); + +#ifndef SSLEAY_MACROS + +#include + +DECLARE_PEM_rw(X509, X509) + +DECLARE_PEM_rw(X509_AUX, X509) + +DECLARE_PEM_rw(X509_CERT_PAIR, X509_CERT_PAIR) + +DECLARE_PEM_rw(X509_REQ, X509_REQ) +DECLARE_PEM_write(X509_REQ_NEW, X509_REQ) + +DECLARE_PEM_rw(X509_CRL, X509_CRL) + +DECLARE_PEM_rw(PKCS7, PKCS7) + +DECLARE_PEM_rw(NETSCAPE_CERT_SEQUENCE, NETSCAPE_CERT_SEQUENCE) + +DECLARE_PEM_rw(PKCS8, X509_SIG) + +DECLARE_PEM_rw(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO) + +#ifndef OPENSSL_NO_RSA + +DECLARE_PEM_rw_cb(RSAPrivateKey, RSA) + +DECLARE_PEM_rw_const(RSAPublicKey, RSA) +DECLARE_PEM_rw(RSA_PUBKEY, RSA) + +#endif + +#ifndef OPENSSL_NO_DSA + +DECLARE_PEM_rw_cb(DSAPrivateKey, DSA) + +DECLARE_PEM_rw(DSA_PUBKEY, DSA) + +DECLARE_PEM_rw_const(DSAparams, DSA) + +#endif + +#ifndef OPENSSL_NO_EC +DECLARE_PEM_rw_const(ECPKParameters, EC_GROUP) +DECLARE_PEM_rw_cb(ECPrivateKey, EC_KEY) +DECLARE_PEM_rw(EC_PUBKEY, EC_KEY) +#endif + +#ifndef OPENSSL_NO_DH + +DECLARE_PEM_rw_const(DHparams, DH) + +#endif + +DECLARE_PEM_rw_cb(PrivateKey, EVP_PKEY) + +DECLARE_PEM_rw(PUBKEY, EVP_PKEY) + +int PEM_write_bio_PKCS8PrivateKey_nid(BIO *bp, EVP_PKEY *x, int nid, + char *kstr, int klen, + pem_password_cb *cb, void *u); +int PEM_write_bio_PKCS8PrivateKey(BIO *, EVP_PKEY *, const EVP_CIPHER *, + char *, int, pem_password_cb *, void *); +int i2d_PKCS8PrivateKey_bio(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc, + char *kstr, int klen, + pem_password_cb *cb, void *u); +int i2d_PKCS8PrivateKey_nid_bio(BIO *bp, EVP_PKEY *x, int nid, + char *kstr, int klen, + pem_password_cb *cb, void *u); +EVP_PKEY *d2i_PKCS8PrivateKey_bio(BIO *bp, EVP_PKEY **x, pem_password_cb *cb, void *u); + +int i2d_PKCS8PrivateKey_fp(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc, + char *kstr, int klen, + pem_password_cb *cb, void *u); +int i2d_PKCS8PrivateKey_nid_fp(FILE *fp, EVP_PKEY *x, int nid, + char *kstr, int klen, + pem_password_cb *cb, void *u); +int PEM_write_PKCS8PrivateKey_nid(FILE *fp, EVP_PKEY *x, int nid, + char *kstr, int klen, + pem_password_cb *cb, void *u); + +EVP_PKEY *d2i_PKCS8PrivateKey_fp(FILE *fp, EVP_PKEY **x, pem_password_cb *cb, void *u); + +int PEM_write_PKCS8PrivateKey(FILE *fp,EVP_PKEY *x,const EVP_CIPHER *enc, + char *kstr,int klen, pem_password_cb *cd, void *u); + +#endif /* SSLEAY_MACROS */ + + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_PEM_strings(void); + +/* Error codes for the PEM functions. */ + +/* Function codes. */ +#define PEM_F_D2I_PKCS8PRIVATEKEY_BIO 120 +#define PEM_F_D2I_PKCS8PRIVATEKEY_FP 121 +#define PEM_F_DO_PK8PKEY 126 +#define PEM_F_DO_PK8PKEY_FP 125 +#define PEM_F_LOAD_IV 101 +#define PEM_F_PEM_ASN1_READ 102 +#define PEM_F_PEM_ASN1_READ_BIO 103 +#define PEM_F_PEM_ASN1_WRITE 104 +#define PEM_F_PEM_ASN1_WRITE_BIO 105 +#define PEM_F_PEM_DEF_CALLBACK 100 +#define PEM_F_PEM_DO_HEADER 106 +#define PEM_F_PEM_F_PEM_WRITE_PKCS8PRIVATEKEY 118 +#define PEM_F_PEM_GET_EVP_CIPHER_INFO 107 +#define PEM_F_PEM_PK8PKEY 119 +#define PEM_F_PEM_READ 108 +#define PEM_F_PEM_READ_BIO 109 +#define PEM_F_PEM_READ_BIO_PRIVATEKEY 123 +#define PEM_F_PEM_READ_PRIVATEKEY 124 +#define PEM_F_PEM_SEALFINAL 110 +#define PEM_F_PEM_SEALINIT 111 +#define PEM_F_PEM_SIGNFINAL 112 +#define PEM_F_PEM_WRITE 113 +#define PEM_F_PEM_WRITE_BIO 114 +#define PEM_F_PEM_X509_INFO_READ 115 +#define PEM_F_PEM_X509_INFO_READ_BIO 116 +#define PEM_F_PEM_X509_INFO_WRITE_BIO 117 + +/* Reason codes. */ +#define PEM_R_BAD_BASE64_DECODE 100 +#define PEM_R_BAD_DECRYPT 101 +#define PEM_R_BAD_END_LINE 102 +#define PEM_R_BAD_IV_CHARS 103 +#define PEM_R_BAD_PASSWORD_READ 104 +#define PEM_R_ERROR_CONVERTING_PRIVATE_KEY 115 +#define PEM_R_NOT_DEK_INFO 105 +#define PEM_R_NOT_ENCRYPTED 106 +#define PEM_R_NOT_PROC_TYPE 107 +#define PEM_R_NO_START_LINE 108 +#define PEM_R_PROBLEMS_GETTING_PASSWORD 109 +#define PEM_R_PUBLIC_KEY_NO_RSA 110 +#define PEM_R_READ_KEY 111 +#define PEM_R_SHORT_HEADER 112 +#define PEM_R_UNSUPPORTED_CIPHER 113 +#define PEM_R_UNSUPPORTED_ENCRYPTION 114 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/pem2.h b/production/3rdparty/openssl/include/openssl/pem2.h new file mode 100644 index 00000000..f31790d6 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/pem2.h @@ -0,0 +1,70 @@ +/* ==================================================================== + * Copyright (c) 1999 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +/* + * This header only exists to break a circular dependency between pem and err + * Ben 30 Jan 1999. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HEADER_PEM_H +void ERR_load_PEM_strings(void); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/production/3rdparty/openssl/include/openssl/pkcs12.h b/production/3rdparty/openssl/include/openssl/pkcs12.h new file mode 100644 index 00000000..a2d7e359 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/pkcs12.h @@ -0,0 +1,333 @@ +/* pkcs12.h */ +/* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL + * project 1999. + */ +/* ==================================================================== + * Copyright (c) 1999 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#ifndef HEADER_PKCS12_H +#define HEADER_PKCS12_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PKCS12_KEY_ID 1 +#define PKCS12_IV_ID 2 +#define PKCS12_MAC_ID 3 + +/* Default iteration count */ +#ifndef PKCS12_DEFAULT_ITER +#define PKCS12_DEFAULT_ITER PKCS5_DEFAULT_ITER +#endif + +#define PKCS12_MAC_KEY_LENGTH 20 + +#define PKCS12_SALT_LEN 8 + +/* Uncomment out next line for unicode password and names, otherwise ASCII */ + +/*#define PBE_UNICODE*/ + +#ifdef PBE_UNICODE +#define PKCS12_key_gen PKCS12_key_gen_uni +#define PKCS12_add_friendlyname PKCS12_add_friendlyname_uni +#else +#define PKCS12_key_gen PKCS12_key_gen_asc +#define PKCS12_add_friendlyname PKCS12_add_friendlyname_asc +#endif + +/* MS key usage constants */ + +#define KEY_EX 0x10 +#define KEY_SIG 0x80 + +typedef struct { +X509_SIG *dinfo; +ASN1_OCTET_STRING *salt; +ASN1_INTEGER *iter; /* defaults to 1 */ +} PKCS12_MAC_DATA; + +typedef struct { +ASN1_INTEGER *version; +PKCS12_MAC_DATA *mac; +PKCS7 *authsafes; +} PKCS12; + +PREDECLARE_STACK_OF(PKCS12_SAFEBAG) + +typedef struct { +ASN1_OBJECT *type; +union { + struct pkcs12_bag_st *bag; /* secret, crl and certbag */ + struct pkcs8_priv_key_info_st *keybag; /* keybag */ + X509_SIG *shkeybag; /* shrouded key bag */ + STACK_OF(PKCS12_SAFEBAG) *safes; + ASN1_TYPE *other; +}value; +STACK_OF(X509_ATTRIBUTE) *attrib; +} PKCS12_SAFEBAG; + +DECLARE_STACK_OF(PKCS12_SAFEBAG) +DECLARE_ASN1_SET_OF(PKCS12_SAFEBAG) +DECLARE_PKCS12_STACK_OF(PKCS12_SAFEBAG) + +typedef struct pkcs12_bag_st { +ASN1_OBJECT *type; +union { + ASN1_OCTET_STRING *x509cert; + ASN1_OCTET_STRING *x509crl; + ASN1_OCTET_STRING *octet; + ASN1_IA5STRING *sdsicert; + ASN1_TYPE *other; /* Secret or other bag */ +}value; +} PKCS12_BAGS; + +#define PKCS12_ERROR 0 +#define PKCS12_OK 1 + +/* Compatibility macros */ + +#define M_PKCS12_x5092certbag PKCS12_x5092certbag +#define M_PKCS12_x509crl2certbag PKCS12_x509crl2certbag + +#define M_PKCS12_certbag2x509 PKCS12_certbag2x509 +#define M_PKCS12_certbag2x509crl PKCS12_certbag2x509crl + +#define M_PKCS12_unpack_p7data PKCS12_unpack_p7data +#define M_PKCS12_pack_authsafes PKCS12_pack_authsafes +#define M_PKCS12_unpack_authsafes PKCS12_unpack_authsafes +#define M_PKCS12_unpack_p7encdata PKCS12_unpack_p7encdata + +#define M_PKCS12_decrypt_skey PKCS12_decrypt_skey +#define M_PKCS8_decrypt PKCS8_decrypt + +#define M_PKCS12_bag_type(bg) OBJ_obj2nid((bg)->type) +#define M_PKCS12_cert_bag_type(bg) OBJ_obj2nid((bg)->value.bag->type) +#define M_PKCS12_crl_bag_type M_PKCS12_cert_bag_type + +#define PKCS12_get_attr(bag, attr_nid) \ + PKCS12_get_attr_gen(bag->attrib, attr_nid) + +#define PKCS8_get_attr(p8, attr_nid) \ + PKCS12_get_attr_gen(p8->attributes, attr_nid) + +#define PKCS12_mac_present(p12) ((p12)->mac ? 1 : 0) + + +PKCS12_SAFEBAG *PKCS12_x5092certbag(X509 *x509); +PKCS12_SAFEBAG *PKCS12_x509crl2certbag(X509_CRL *crl); +X509 *PKCS12_certbag2x509(PKCS12_SAFEBAG *bag); +X509_CRL *PKCS12_certbag2x509crl(PKCS12_SAFEBAG *bag); + +PKCS12_SAFEBAG *PKCS12_item_pack_safebag(void *obj, const ASN1_ITEM *it, int nid1, + int nid2); +PKCS12_SAFEBAG *PKCS12_MAKE_KEYBAG(PKCS8_PRIV_KEY_INFO *p8); +PKCS8_PRIV_KEY_INFO *PKCS8_decrypt(X509_SIG *p8, const char *pass, int passlen); +PKCS8_PRIV_KEY_INFO *PKCS12_decrypt_skey(PKCS12_SAFEBAG *bag, const char *pass, + int passlen); +X509_SIG *PKCS8_encrypt(int pbe_nid, const EVP_CIPHER *cipher, + const char *pass, int passlen, + unsigned char *salt, int saltlen, int iter, + PKCS8_PRIV_KEY_INFO *p8); +PKCS12_SAFEBAG *PKCS12_MAKE_SHKEYBAG(int pbe_nid, const char *pass, + int passlen, unsigned char *salt, + int saltlen, int iter, + PKCS8_PRIV_KEY_INFO *p8); +PKCS7 *PKCS12_pack_p7data(STACK_OF(PKCS12_SAFEBAG) *sk); +STACK_OF(PKCS12_SAFEBAG) *PKCS12_unpack_p7data(PKCS7 *p7); +PKCS7 *PKCS12_pack_p7encdata(int pbe_nid, const char *pass, int passlen, + unsigned char *salt, int saltlen, int iter, + STACK_OF(PKCS12_SAFEBAG) *bags); +STACK_OF(PKCS12_SAFEBAG) *PKCS12_unpack_p7encdata(PKCS7 *p7, const char *pass, int passlen); + +int PKCS12_pack_authsafes(PKCS12 *p12, STACK_OF(PKCS7) *safes); +STACK_OF(PKCS7) *PKCS12_unpack_authsafes(PKCS12 *p12); + +int PKCS12_add_localkeyid(PKCS12_SAFEBAG *bag, unsigned char *name, int namelen); +int PKCS12_add_friendlyname_asc(PKCS12_SAFEBAG *bag, const char *name, + int namelen); +int PKCS12_add_CSPName_asc(PKCS12_SAFEBAG *bag, const char *name, + int namelen); +int PKCS12_add_friendlyname_uni(PKCS12_SAFEBAG *bag, const unsigned char *name, + int namelen); +int PKCS8_add_keyusage(PKCS8_PRIV_KEY_INFO *p8, int usage); +ASN1_TYPE *PKCS12_get_attr_gen(STACK_OF(X509_ATTRIBUTE) *attrs, int attr_nid); +char *PKCS12_get_friendlyname(PKCS12_SAFEBAG *bag); +unsigned char *PKCS12_pbe_crypt(X509_ALGOR *algor, const char *pass, + int passlen, unsigned char *in, int inlen, + unsigned char **data, int *datalen, int en_de); +void * PKCS12_item_decrypt_d2i(X509_ALGOR *algor, const ASN1_ITEM *it, + const char *pass, int passlen, ASN1_OCTET_STRING *oct, int zbuf); +ASN1_OCTET_STRING *PKCS12_item_i2d_encrypt(X509_ALGOR *algor, const ASN1_ITEM *it, + const char *pass, int passlen, + void *obj, int zbuf); +PKCS12 *PKCS12_init(int mode); +int PKCS12_key_gen_asc(const char *pass, int passlen, unsigned char *salt, + int saltlen, int id, int iter, int n, + unsigned char *out, const EVP_MD *md_type); +int PKCS12_key_gen_uni(unsigned char *pass, int passlen, unsigned char *salt, int saltlen, int id, int iter, int n, unsigned char *out, const EVP_MD *md_type); +int PKCS12_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass, int passlen, + ASN1_TYPE *param, const EVP_CIPHER *cipher, const EVP_MD *md_type, + int en_de); +int PKCS12_gen_mac(PKCS12 *p12, const char *pass, int passlen, + unsigned char *mac, unsigned int *maclen); +int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen); +int PKCS12_set_mac(PKCS12 *p12, const char *pass, int passlen, + unsigned char *salt, int saltlen, int iter, + const EVP_MD *md_type); +int PKCS12_setup_mac(PKCS12 *p12, int iter, unsigned char *salt, + int saltlen, const EVP_MD *md_type); +unsigned char *asc2uni(const char *asc, int asclen, unsigned char **uni, int *unilen); +char *uni2asc(unsigned char *uni, int unilen); + +DECLARE_ASN1_FUNCTIONS(PKCS12) +DECLARE_ASN1_FUNCTIONS(PKCS12_MAC_DATA) +DECLARE_ASN1_FUNCTIONS(PKCS12_SAFEBAG) +DECLARE_ASN1_FUNCTIONS(PKCS12_BAGS) + +DECLARE_ASN1_ITEM(PKCS12_SAFEBAGS) +DECLARE_ASN1_ITEM(PKCS12_AUTHSAFES) + +void PKCS12_PBE_add(void); +int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, + STACK_OF(X509) **ca); +PKCS12 *PKCS12_create(char *pass, char *name, EVP_PKEY *pkey, X509 *cert, + STACK_OF(X509) *ca, int nid_key, int nid_cert, int iter, + int mac_iter, int keytype); + +PKCS12_SAFEBAG *PKCS12_add_cert(STACK_OF(PKCS12_SAFEBAG) **pbags, X509 *cert); +PKCS12_SAFEBAG *PKCS12_add_key(STACK_OF(PKCS12_SAFEBAG) **pbags, EVP_PKEY *key, + int key_usage, int iter, + int key_nid, char *pass); +int PKCS12_add_safe(STACK_OF(PKCS7) **psafes, STACK_OF(PKCS12_SAFEBAG) *bags, + int safe_nid, int iter, char *pass); +PKCS12 *PKCS12_add_safes(STACK_OF(PKCS7) *safes, int p7_nid); + +int i2d_PKCS12_bio(BIO *bp, PKCS12 *p12); +int i2d_PKCS12_fp(FILE *fp, PKCS12 *p12); +PKCS12 *d2i_PKCS12_bio(BIO *bp, PKCS12 **p12); +PKCS12 *d2i_PKCS12_fp(FILE *fp, PKCS12 **p12); +int PKCS12_newpass(PKCS12 *p12, char *oldpass, char *newpass); + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_PKCS12_strings(void); + +/* Error codes for the PKCS12 functions. */ + +/* Function codes. */ +#define PKCS12_F_PARSE_BAG 129 +#define PKCS12_F_PARSE_BAGS 103 +#define PKCS12_F_PKCS12_ADD_FRIENDLYNAME 100 +#define PKCS12_F_PKCS12_ADD_FRIENDLYNAME_ASC 127 +#define PKCS12_F_PKCS12_ADD_FRIENDLYNAME_UNI 102 +#define PKCS12_F_PKCS12_ADD_LOCALKEYID 104 +#define PKCS12_F_PKCS12_CREATE 105 +#define PKCS12_F_PKCS12_GEN_MAC 107 +#define PKCS12_F_PKCS12_INIT 109 +#define PKCS12_F_PKCS12_ITEM_DECRYPT_D2I 106 +#define PKCS12_F_PKCS12_ITEM_I2D_ENCRYPT 108 +#define PKCS12_F_PKCS12_ITEM_PACK_SAFEBAG 117 +#define PKCS12_F_PKCS12_KEY_GEN_ASC 110 +#define PKCS12_F_PKCS12_KEY_GEN_UNI 111 +#define PKCS12_F_PKCS12_MAKE_KEYBAG 112 +#define PKCS12_F_PKCS12_MAKE_SHKEYBAG 113 +#define PKCS12_F_PKCS12_NEWPASS 128 +#define PKCS12_F_PKCS12_PACK_P7DATA 114 +#define PKCS12_F_PKCS12_PACK_P7ENCDATA 115 +#define PKCS12_F_PKCS12_PARSE 118 +#define PKCS12_F_PKCS12_PBE_CRYPT 119 +#define PKCS12_F_PKCS12_PBE_KEYIVGEN 120 +#define PKCS12_F_PKCS12_SETUP_MAC 122 +#define PKCS12_F_PKCS12_SET_MAC 123 +#define PKCS12_F_PKCS12_UNPACK_AUTHSAFES 130 +#define PKCS12_F_PKCS12_UNPACK_P7DATA 131 +#define PKCS12_F_PKCS12_VERIFY_MAC 126 +#define PKCS12_F_PKCS8_ADD_KEYUSAGE 124 +#define PKCS12_F_PKCS8_ENCRYPT 125 + +/* Reason codes. */ +#define PKCS12_R_CANT_PACK_STRUCTURE 100 +#define PKCS12_R_CONTENT_TYPE_NOT_DATA 121 +#define PKCS12_R_DECODE_ERROR 101 +#define PKCS12_R_ENCODE_ERROR 102 +#define PKCS12_R_ENCRYPT_ERROR 103 +#define PKCS12_R_ERROR_SETTING_ENCRYPTED_DATA_TYPE 120 +#define PKCS12_R_INVALID_NULL_ARGUMENT 104 +#define PKCS12_R_INVALID_NULL_PKCS12_POINTER 105 +#define PKCS12_R_IV_GEN_ERROR 106 +#define PKCS12_R_KEY_GEN_ERROR 107 +#define PKCS12_R_MAC_ABSENT 108 +#define PKCS12_R_MAC_GENERATION_ERROR 109 +#define PKCS12_R_MAC_SETUP_ERROR 110 +#define PKCS12_R_MAC_STRING_SET_ERROR 111 +#define PKCS12_R_MAC_VERIFY_ERROR 112 +#define PKCS12_R_MAC_VERIFY_FAILURE 113 +#define PKCS12_R_PARSE_ERROR 114 +#define PKCS12_R_PKCS12_ALGOR_CIPHERINIT_ERROR 115 +#define PKCS12_R_PKCS12_CIPHERFINAL_ERROR 116 +#define PKCS12_R_PKCS12_PBE_CRYPT_ERROR 117 +#define PKCS12_R_UNKNOWN_DIGEST_ALGORITHM 118 +#define PKCS12_R_UNSUPPORTED_PKCS12_MODE 119 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/pkcs7.h b/production/3rdparty/openssl/include/openssl/pkcs7.h new file mode 100644 index 00000000..cc092d26 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/pkcs7.h @@ -0,0 +1,464 @@ +/* crypto/pkcs7/pkcs7.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_PKCS7_H +#define HEADER_PKCS7_H + +#include +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef OPENSSL_SYS_WIN32 +/* Under Win32 thes are defined in wincrypt.h */ +#undef PKCS7_ISSUER_AND_SERIAL +#undef PKCS7_SIGNER_INFO +#endif + +/* +Encryption_ID DES-CBC +Digest_ID MD5 +Digest_Encryption_ID rsaEncryption +Key_Encryption_ID rsaEncryption +*/ + +typedef struct pkcs7_issuer_and_serial_st + { + X509_NAME *issuer; + ASN1_INTEGER *serial; + } PKCS7_ISSUER_AND_SERIAL; + +typedef struct pkcs7_signer_info_st + { + ASN1_INTEGER *version; /* version 1 */ + PKCS7_ISSUER_AND_SERIAL *issuer_and_serial; + X509_ALGOR *digest_alg; + STACK_OF(X509_ATTRIBUTE) *auth_attr; /* [ 0 ] */ + X509_ALGOR *digest_enc_alg; + ASN1_OCTET_STRING *enc_digest; + STACK_OF(X509_ATTRIBUTE) *unauth_attr; /* [ 1 ] */ + + /* The private key to sign with */ + EVP_PKEY *pkey; + } PKCS7_SIGNER_INFO; + +DECLARE_STACK_OF(PKCS7_SIGNER_INFO) +DECLARE_ASN1_SET_OF(PKCS7_SIGNER_INFO) + +typedef struct pkcs7_recip_info_st + { + ASN1_INTEGER *version; /* version 0 */ + PKCS7_ISSUER_AND_SERIAL *issuer_and_serial; + X509_ALGOR *key_enc_algor; + ASN1_OCTET_STRING *enc_key; + X509 *cert; /* get the pub-key from this */ + } PKCS7_RECIP_INFO; + +DECLARE_STACK_OF(PKCS7_RECIP_INFO) +DECLARE_ASN1_SET_OF(PKCS7_RECIP_INFO) + +typedef struct pkcs7_signed_st + { + ASN1_INTEGER *version; /* version 1 */ + STACK_OF(X509_ALGOR) *md_algs; /* md used */ + STACK_OF(X509) *cert; /* [ 0 ] */ + STACK_OF(X509_CRL) *crl; /* [ 1 ] */ + STACK_OF(PKCS7_SIGNER_INFO) *signer_info; + + struct pkcs7_st *contents; + } PKCS7_SIGNED; +/* The above structure is very very similar to PKCS7_SIGN_ENVELOPE. + * How about merging the two */ + +typedef struct pkcs7_enc_content_st + { + ASN1_OBJECT *content_type; + X509_ALGOR *algorithm; + ASN1_OCTET_STRING *enc_data; /* [ 0 ] */ + const EVP_CIPHER *cipher; + } PKCS7_ENC_CONTENT; + +typedef struct pkcs7_enveloped_st + { + ASN1_INTEGER *version; /* version 0 */ + STACK_OF(PKCS7_RECIP_INFO) *recipientinfo; + PKCS7_ENC_CONTENT *enc_data; + } PKCS7_ENVELOPE; + +typedef struct pkcs7_signedandenveloped_st + { + ASN1_INTEGER *version; /* version 1 */ + STACK_OF(X509_ALGOR) *md_algs; /* md used */ + STACK_OF(X509) *cert; /* [ 0 ] */ + STACK_OF(X509_CRL) *crl; /* [ 1 ] */ + STACK_OF(PKCS7_SIGNER_INFO) *signer_info; + + PKCS7_ENC_CONTENT *enc_data; + STACK_OF(PKCS7_RECIP_INFO) *recipientinfo; + } PKCS7_SIGN_ENVELOPE; + +typedef struct pkcs7_digest_st + { + ASN1_INTEGER *version; /* version 0 */ + X509_ALGOR *md; /* md used */ + struct pkcs7_st *contents; + ASN1_OCTET_STRING *digest; + } PKCS7_DIGEST; + +typedef struct pkcs7_encrypted_st + { + ASN1_INTEGER *version; /* version 0 */ + PKCS7_ENC_CONTENT *enc_data; + } PKCS7_ENCRYPT; + +typedef struct pkcs7_st + { + /* The following is non NULL if it contains ASN1 encoding of + * this structure */ + unsigned char *asn1; + long length; + +#define PKCS7_S_HEADER 0 +#define PKCS7_S_BODY 1 +#define PKCS7_S_TAIL 2 + int state; /* used during processing */ + + int detached; + + ASN1_OBJECT *type; + /* content as defined by the type */ + /* all encryption/message digests are applied to the 'contents', + * leaving out the 'type' field. */ + union { + char *ptr; + + /* NID_pkcs7_data */ + ASN1_OCTET_STRING *data; + + /* NID_pkcs7_signed */ + PKCS7_SIGNED *sign; + + /* NID_pkcs7_enveloped */ + PKCS7_ENVELOPE *enveloped; + + /* NID_pkcs7_signedAndEnveloped */ + PKCS7_SIGN_ENVELOPE *signed_and_enveloped; + + /* NID_pkcs7_digest */ + PKCS7_DIGEST *digest; + + /* NID_pkcs7_encrypted */ + PKCS7_ENCRYPT *encrypted; + + /* Anything else */ + ASN1_TYPE *other; + } d; + } PKCS7; + +DECLARE_STACK_OF(PKCS7) +DECLARE_ASN1_SET_OF(PKCS7) +DECLARE_PKCS12_STACK_OF(PKCS7) + +#define PKCS7_OP_SET_DETACHED_SIGNATURE 1 +#define PKCS7_OP_GET_DETACHED_SIGNATURE 2 + +#define PKCS7_get_signed_attributes(si) ((si)->auth_attr) +#define PKCS7_get_attributes(si) ((si)->unauth_attr) + +#define PKCS7_type_is_signed(a) (OBJ_obj2nid((a)->type) == NID_pkcs7_signed) +#define PKCS7_type_is_encrypted(a) (OBJ_obj2nid((a)->type) == NID_pkcs7_encrypted) +#define PKCS7_type_is_enveloped(a) (OBJ_obj2nid((a)->type) == NID_pkcs7_enveloped) +#define PKCS7_type_is_signedAndEnveloped(a) \ + (OBJ_obj2nid((a)->type) == NID_pkcs7_signedAndEnveloped) +#define PKCS7_type_is_data(a) (OBJ_obj2nid((a)->type) == NID_pkcs7_data) + +#define PKCS7_type_is_digest(a) (OBJ_obj2nid((a)->type) == NID_pkcs7_digest) + +#define PKCS7_set_detached(p,v) \ + PKCS7_ctrl(p,PKCS7_OP_SET_DETACHED_SIGNATURE,v,NULL) +#define PKCS7_get_detached(p) \ + PKCS7_ctrl(p,PKCS7_OP_GET_DETACHED_SIGNATURE,0,NULL) + +#define PKCS7_is_detached(p7) (PKCS7_type_is_signed(p7) && PKCS7_get_detached(p7)) + +#ifdef SSLEAY_MACROS +#ifndef PKCS7_ISSUER_AND_SERIAL_digest +#define PKCS7_ISSUER_AND_SERIAL_digest(data,type,md,len) \ + ASN1_digest((int (*)())i2d_PKCS7_ISSUER_AND_SERIAL,type,\ + (char *)data,md,len) +#endif +#endif + +/* S/MIME related flags */ + +#define PKCS7_TEXT 0x1 +#define PKCS7_NOCERTS 0x2 +#define PKCS7_NOSIGS 0x4 +#define PKCS7_NOCHAIN 0x8 +#define PKCS7_NOINTERN 0x10 +#define PKCS7_NOVERIFY 0x20 +#define PKCS7_DETACHED 0x40 +#define PKCS7_BINARY 0x80 +#define PKCS7_NOATTR 0x100 +#define PKCS7_NOSMIMECAP 0x200 +#define PKCS7_NOOLDMIMETYPE 0x400 +#define PKCS7_CRLFEOL 0x800 +#define PKCS7_STREAM 0x1000 +#define PKCS7_NOCRL 0x2000 + +/* Flags: for compatibility with older code */ + +#define SMIME_TEXT PKCS7_TEXT +#define SMIME_NOCERTS PKCS7_NOCERTS +#define SMIME_NOSIGS PKCS7_NOSIGS +#define SMIME_NOCHAIN PKCS7_NOCHAIN +#define SMIME_NOINTERN PKCS7_NOINTERN +#define SMIME_NOVERIFY PKCS7_NOVERIFY +#define SMIME_DETACHED PKCS7_DETACHED +#define SMIME_BINARY PKCS7_BINARY +#define SMIME_NOATTR PKCS7_NOATTR + +DECLARE_ASN1_FUNCTIONS(PKCS7_ISSUER_AND_SERIAL) + +#ifndef SSLEAY_MACROS +int PKCS7_ISSUER_AND_SERIAL_digest(PKCS7_ISSUER_AND_SERIAL *data,const EVP_MD *type, + unsigned char *md,unsigned int *len); +#ifndef OPENSSL_NO_FP_API +PKCS7 *d2i_PKCS7_fp(FILE *fp,PKCS7 **p7); +int i2d_PKCS7_fp(FILE *fp,PKCS7 *p7); +#endif +PKCS7 *PKCS7_dup(PKCS7 *p7); +PKCS7 *d2i_PKCS7_bio(BIO *bp,PKCS7 **p7); +int i2d_PKCS7_bio(BIO *bp,PKCS7 *p7); +#endif + +DECLARE_ASN1_FUNCTIONS(PKCS7_SIGNER_INFO) +DECLARE_ASN1_FUNCTIONS(PKCS7_RECIP_INFO) +DECLARE_ASN1_FUNCTIONS(PKCS7_SIGNED) +DECLARE_ASN1_FUNCTIONS(PKCS7_ENC_CONTENT) +DECLARE_ASN1_FUNCTIONS(PKCS7_ENVELOPE) +DECLARE_ASN1_FUNCTIONS(PKCS7_SIGN_ENVELOPE) +DECLARE_ASN1_FUNCTIONS(PKCS7_DIGEST) +DECLARE_ASN1_FUNCTIONS(PKCS7_ENCRYPT) +DECLARE_ASN1_FUNCTIONS(PKCS7) + +DECLARE_ASN1_ITEM(PKCS7_ATTR_SIGN) +DECLARE_ASN1_ITEM(PKCS7_ATTR_VERIFY) + +DECLARE_ASN1_NDEF_FUNCTION(PKCS7) + +long PKCS7_ctrl(PKCS7 *p7, int cmd, long larg, char *parg); + +int PKCS7_set_type(PKCS7 *p7, int type); +int PKCS7_set0_type_other(PKCS7 *p7, int type, ASN1_TYPE *other); +int PKCS7_set_content(PKCS7 *p7, PKCS7 *p7_data); +int PKCS7_SIGNER_INFO_set(PKCS7_SIGNER_INFO *p7i, X509 *x509, EVP_PKEY *pkey, + const EVP_MD *dgst); +int PKCS7_add_signer(PKCS7 *p7, PKCS7_SIGNER_INFO *p7i); +int PKCS7_add_certificate(PKCS7 *p7, X509 *x509); +int PKCS7_add_crl(PKCS7 *p7, X509_CRL *x509); +int PKCS7_content_new(PKCS7 *p7, int nid); +int PKCS7_dataVerify(X509_STORE *cert_store, X509_STORE_CTX *ctx, + BIO *bio, PKCS7 *p7, PKCS7_SIGNER_INFO *si); +int PKCS7_signatureVerify(BIO *bio, PKCS7 *p7, PKCS7_SIGNER_INFO *si, + X509 *x509); + +BIO *PKCS7_dataInit(PKCS7 *p7, BIO *bio); +int PKCS7_dataFinal(PKCS7 *p7, BIO *bio); +BIO *PKCS7_dataDecode(PKCS7 *p7, EVP_PKEY *pkey, BIO *in_bio, X509 *pcert); + + +PKCS7_SIGNER_INFO *PKCS7_add_signature(PKCS7 *p7, X509 *x509, + EVP_PKEY *pkey, const EVP_MD *dgst); +X509 *PKCS7_cert_from_signer_info(PKCS7 *p7, PKCS7_SIGNER_INFO *si); +int PKCS7_set_digest(PKCS7 *p7, const EVP_MD *md); +STACK_OF(PKCS7_SIGNER_INFO) *PKCS7_get_signer_info(PKCS7 *p7); + +PKCS7_RECIP_INFO *PKCS7_add_recipient(PKCS7 *p7, X509 *x509); +int PKCS7_add_recipient_info(PKCS7 *p7, PKCS7_RECIP_INFO *ri); +int PKCS7_RECIP_INFO_set(PKCS7_RECIP_INFO *p7i, X509 *x509); +int PKCS7_set_cipher(PKCS7 *p7, const EVP_CIPHER *cipher); + +PKCS7_ISSUER_AND_SERIAL *PKCS7_get_issuer_and_serial(PKCS7 *p7, int idx); +ASN1_OCTET_STRING *PKCS7_digest_from_attributes(STACK_OF(X509_ATTRIBUTE) *sk); +int PKCS7_add_signed_attribute(PKCS7_SIGNER_INFO *p7si,int nid,int type, + void *data); +int PKCS7_add_attribute (PKCS7_SIGNER_INFO *p7si, int nid, int atrtype, + void *value); +ASN1_TYPE *PKCS7_get_attribute(PKCS7_SIGNER_INFO *si, int nid); +ASN1_TYPE *PKCS7_get_signed_attribute(PKCS7_SIGNER_INFO *si, int nid); +int PKCS7_set_signed_attributes(PKCS7_SIGNER_INFO *p7si, + STACK_OF(X509_ATTRIBUTE) *sk); +int PKCS7_set_attributes(PKCS7_SIGNER_INFO *p7si,STACK_OF(X509_ATTRIBUTE) *sk); + + +PKCS7 *PKCS7_sign(X509 *signcert, EVP_PKEY *pkey, STACK_OF(X509) *certs, + BIO *data, int flags); +int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store, + BIO *indata, BIO *out, int flags); +STACK_OF(X509) *PKCS7_get0_signers(PKCS7 *p7, STACK_OF(X509) *certs, int flags); +PKCS7 *PKCS7_encrypt(STACK_OF(X509) *certs, BIO *in, const EVP_CIPHER *cipher, + int flags); +int PKCS7_decrypt(PKCS7 *p7, EVP_PKEY *pkey, X509 *cert, BIO *data, int flags); + +int PKCS7_add_attrib_smimecap(PKCS7_SIGNER_INFO *si, + STACK_OF(X509_ALGOR) *cap); +STACK_OF(X509_ALGOR) *PKCS7_get_smimecap(PKCS7_SIGNER_INFO *si); +int PKCS7_simple_smimecap(STACK_OF(X509_ALGOR) *sk, int nid, int arg); + +int SMIME_write_PKCS7(BIO *bio, PKCS7 *p7, BIO *data, int flags); +PKCS7 *SMIME_read_PKCS7(BIO *bio, BIO **bcont); +int SMIME_crlf_copy(BIO *in, BIO *out, int flags); +int SMIME_text(BIO *in, BIO *out); + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_PKCS7_strings(void); + +/* Error codes for the PKCS7 functions. */ + +/* Function codes. */ +#define PKCS7_F_B64_READ_PKCS7 120 +#define PKCS7_F_B64_WRITE_PKCS7 121 +#define PKCS7_F_PKCS7_ADD_ATTRIB_SMIMECAP 118 +#define PKCS7_F_PKCS7_ADD_CERTIFICATE 100 +#define PKCS7_F_PKCS7_ADD_CRL 101 +#define PKCS7_F_PKCS7_ADD_RECIPIENT_INFO 102 +#define PKCS7_F_PKCS7_ADD_SIGNER 103 +#define PKCS7_F_PKCS7_BIO_ADD_DIGEST 125 +#define PKCS7_F_PKCS7_CTRL 104 +#define PKCS7_F_PKCS7_DATADECODE 112 +#define PKCS7_F_PKCS7_DATAFINAL 128 +#define PKCS7_F_PKCS7_DATAINIT 105 +#define PKCS7_F_PKCS7_DATASIGN 106 +#define PKCS7_F_PKCS7_DATAVERIFY 107 +#define PKCS7_F_PKCS7_DECRYPT 114 +#define PKCS7_F_PKCS7_ENCRYPT 115 +#define PKCS7_F_PKCS7_FIND_DIGEST 127 +#define PKCS7_F_PKCS7_GET0_SIGNERS 124 +#define PKCS7_F_PKCS7_SET_CIPHER 108 +#define PKCS7_F_PKCS7_SET_CONTENT 109 +#define PKCS7_F_PKCS7_SET_DIGEST 126 +#define PKCS7_F_PKCS7_SET_TYPE 110 +#define PKCS7_F_PKCS7_SIGN 116 +#define PKCS7_F_PKCS7_SIGNATUREVERIFY 113 +#define PKCS7_F_PKCS7_SIMPLE_SMIMECAP 119 +#define PKCS7_F_PKCS7_VERIFY 117 +#define PKCS7_F_SMIME_READ_PKCS7 122 +#define PKCS7_F_SMIME_TEXT 123 + +/* Reason codes. */ +#define PKCS7_R_CERTIFICATE_VERIFY_ERROR 117 +#define PKCS7_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER 144 +#define PKCS7_R_CIPHER_NOT_INITIALIZED 116 +#define PKCS7_R_CONTENT_AND_DATA_PRESENT 118 +#define PKCS7_R_DECODE_ERROR 130 +#define PKCS7_R_DECRYPTED_KEY_IS_WRONG_LENGTH 100 +#define PKCS7_R_DECRYPT_ERROR 119 +#define PKCS7_R_DIGEST_FAILURE 101 +#define PKCS7_R_ERROR_ADDING_RECIPIENT 120 +#define PKCS7_R_ERROR_SETTING_CIPHER 121 +#define PKCS7_R_INVALID_MIME_TYPE 131 +#define PKCS7_R_INVALID_NULL_POINTER 143 +#define PKCS7_R_MIME_NO_CONTENT_TYPE 132 +#define PKCS7_R_MIME_PARSE_ERROR 133 +#define PKCS7_R_MIME_SIG_PARSE_ERROR 134 +#define PKCS7_R_MISSING_CERIPEND_INFO 103 +#define PKCS7_R_NO_CONTENT 122 +#define PKCS7_R_NO_CONTENT_TYPE 135 +#define PKCS7_R_NO_MULTIPART_BODY_FAILURE 136 +#define PKCS7_R_NO_MULTIPART_BOUNDARY 137 +#define PKCS7_R_NO_RECIPIENT_MATCHES_CERTIFICATE 115 +#define PKCS7_R_NO_RECIPIENT_MATCHES_KEY 146 +#define PKCS7_R_NO_SIGNATURES_ON_DATA 123 +#define PKCS7_R_NO_SIGNERS 142 +#define PKCS7_R_NO_SIG_CONTENT_TYPE 138 +#define PKCS7_R_OPERATION_NOT_SUPPORTED_ON_THIS_TYPE 104 +#define PKCS7_R_PKCS7_ADD_SIGNATURE_ERROR 124 +#define PKCS7_R_PKCS7_DATAFINAL 126 +#define PKCS7_R_PKCS7_DATAFINAL_ERROR 125 +#define PKCS7_R_PKCS7_DATASIGN 145 +#define PKCS7_R_PKCS7_PARSE_ERROR 139 +#define PKCS7_R_PKCS7_SIG_PARSE_ERROR 140 +#define PKCS7_R_PRIVATE_KEY_DOES_NOT_MATCH_CERTIFICATE 127 +#define PKCS7_R_SIGNATURE_FAILURE 105 +#define PKCS7_R_SIGNER_CERTIFICATE_NOT_FOUND 128 +#define PKCS7_R_SIG_INVALID_MIME_TYPE 141 +#define PKCS7_R_SMIME_TEXT_ERROR 129 +#define PKCS7_R_UNABLE_TO_FIND_CERTIFICATE 106 +#define PKCS7_R_UNABLE_TO_FIND_MEM_BIO 107 +#define PKCS7_R_UNABLE_TO_FIND_MESSAGE_DIGEST 108 +#define PKCS7_R_UNKNOWN_DIGEST_TYPE 109 +#define PKCS7_R_UNKNOWN_OPERATION 110 +#define PKCS7_R_UNSUPPORTED_CIPHER_TYPE 111 +#define PKCS7_R_UNSUPPORTED_CONTENT_TYPE 112 +#define PKCS7_R_WRONG_CONTENT_TYPE 113 +#define PKCS7_R_WRONG_PKCS7_TYPE 114 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/pq_compat.h b/production/3rdparty/openssl/include/openssl/pq_compat.h new file mode 100644 index 00000000..28c58a02 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/pq_compat.h @@ -0,0 +1,147 @@ +/* crypto/pqueue/pqueue_compat.h */ +/* + * DTLS implementation written by Nagendra Modadugu + * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. + */ +/* ==================================================================== + * Copyright (c) 1999-2005 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#include "opensslconf.h" +#include + +/* + * The purpose of this header file is for supporting 64-bit integer + * manipulation on 32-bit (and lower) machines. Currently the only + * such environment is VMS, Utrix and those with smaller default integer + * sizes than 32 bits. For all such environment, we fall back to using + * BIGNUM. We may need to fine tune the conditions for systems that + * are incorrectly configured. + * + * The only clients of this code are (1) pqueue for priority, and + * (2) DTLS, for sequence number manipulation. + */ + +#if (defined(THIRTY_TWO_BIT) && !defined(BN_LLONG)) || defined(SIXTEEN_BIT) || defined(EIGHT_BIT) + +#define PQ_64BIT_IS_INTEGER 0 +#define PQ_64BIT_IS_BIGNUM 1 + +#define PQ_64BIT BIGNUM +#define PQ_64BIT_CTX BN_CTX + +#define pq_64bit_init(x) BN_init(x) +#define pq_64bit_free(x) BN_free(x) + +#define pq_64bit_ctx_new(ctx) BN_CTX_new() +#define pq_64bit_ctx_free(x) BN_CTX_free(x) + +#define pq_64bit_assign(x, y) BN_copy(x, y) +#define pq_64bit_assign_word(x, y) BN_set_word(x, y) +#define pq_64bit_gt(x, y) BN_ucmp(x, y) >= 1 ? 1 : 0 +#define pq_64bit_eq(x, y) BN_ucmp(x, y) == 0 ? 1 : 0 +#define pq_64bit_add_word(x, w) BN_add_word(x, w) +#define pq_64bit_sub(r, x, y) BN_sub(r, x, y) +#define pq_64bit_sub_word(x, w) BN_sub_word(x, w) +#define pq_64bit_mod(r, x, n, ctx) BN_mod(r, x, n, ctx) + +#define pq_64bit_bin2num(bn, bytes, len) BN_bin2bn(bytes, len, bn) +#define pq_64bit_num2bin(bn, bytes) BN_bn2bin(bn, bytes) +#define pq_64bit_get_word(x) BN_get_word(x) +#define pq_64bit_is_bit_set(x, offset) BN_is_bit_set(x, offset) +#define pq_64bit_lshift(r, x, shift) BN_lshift(r, x, shift) +#define pq_64bit_set_bit(x, num) BN_set_bit(x, num) +#define pq_64bit_get_length(x) BN_num_bits((x)) + +#else + +#define PQ_64BIT_IS_INTEGER 1 +#define PQ_64BIT_IS_BIGNUM 0 + +#if defined(SIXTY_FOUR_BIT) +#define PQ_64BIT BN_ULONG +#define PQ_64BIT_PRINT "%lld" +#elif defined(SIXTY_FOUR_BIT_LONG) +#define PQ_64BIT BN_ULONG +#define PQ_64BIT_PRINT "%ld" +#elif defined(THIRTY_TWO_BIT) +#define PQ_64BIT BN_ULLONG +#define PQ_64BIT_PRINT "%lld" +#endif + +#define PQ_64BIT_CTX void + +#define pq_64bit_init(x) +#define pq_64bit_free(x) +#define pq_64bit_ctx_new(ctx) (ctx) +#define pq_64bit_ctx_free(x) + +#define pq_64bit_assign(x, y) (*(x) = *(y)) +#define pq_64bit_assign_word(x, y) (*(x) = y) +#define pq_64bit_gt(x, y) (*(x) > *(y)) +#define pq_64bit_eq(x, y) (*(x) == *(y)) +#define pq_64bit_add_word(x, w) (*(x) = (*(x) + (w))) +#define pq_64bit_sub(r, x, y) (*(r) = (*(x) - *(y))) +#define pq_64bit_sub_word(x, w) (*(x) = (*(x) - (w))) +#define pq_64bit_mod(r, x, n, ctx) + +#define pq_64bit_bin2num(num, bytes, len) bytes_to_long_long(bytes, num) +#define pq_64bit_num2bin(num, bytes) long_long_to_bytes(num, bytes) +#define pq_64bit_get_word(x) *(x) +#define pq_64bit_lshift(r, x, shift) (*(r) = (*(x) << (shift))) +#define pq_64bit_set_bit(x, num) do { \ + PQ_64BIT mask = 1; \ + mask = mask << (num); \ + *(x) |= mask; \ + } while(0) +#endif /* OPENSSL_SYS_VMS */ diff --git a/production/3rdparty/openssl/include/openssl/pqueue.h b/production/3rdparty/openssl/include/openssl/pqueue.h new file mode 100644 index 00000000..02386d13 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/pqueue.h @@ -0,0 +1,95 @@ +/* crypto/pqueue/pqueue.h */ +/* + * DTLS implementation written by Nagendra Modadugu + * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. + */ +/* ==================================================================== + * Copyright (c) 1999-2005 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#ifndef HEADER_PQUEUE_H +#define HEADER_PQUEUE_H + +#include +#include +#include + +#include + +typedef struct _pqueue *pqueue; + +typedef struct _pitem + { + PQ_64BIT priority; + void *data; + struct _pitem *next; + } pitem; + +typedef struct _pitem *piterator; + +pitem *pitem_new(PQ_64BIT priority, void *data); +void pitem_free(pitem *item); + +pqueue pqueue_new(void); +void pqueue_free(pqueue pq); + +pitem *pqueue_insert(pqueue pq, pitem *item); +pitem *pqueue_peek(pqueue pq); +pitem *pqueue_pop(pqueue pq); +pitem *pqueue_find(pqueue pq, PQ_64BIT priority); +pitem *pqueue_iterator(pqueue pq); +pitem *pqueue_next(piterator *iter); + +void pqueue_print(pqueue pq); + +#endif /* ! HEADER_PQUEUE_H */ diff --git a/production/3rdparty/openssl/include/openssl/rand.h b/production/3rdparty/openssl/include/openssl/rand.h new file mode 100644 index 00000000..ac6c0217 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/rand.h @@ -0,0 +1,140 @@ +/* crypto/rand/rand.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_RAND_H +#define HEADER_RAND_H + +#include +#include +#include + +#if defined(OPENSSL_SYS_WINDOWS) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(OPENSSL_FIPS) +#define FIPS_RAND_SIZE_T size_t +#endif + +/* Already defined in ossl_typ.h */ +/* typedef struct rand_meth_st RAND_METHOD; */ + +struct rand_meth_st + { + void (*seed)(const void *buf, int num); + int (*bytes)(unsigned char *buf, int num); + void (*cleanup)(void); + void (*add)(const void *buf, int num, double entropy); + int (*pseudorand)(unsigned char *buf, int num); + int (*status)(void); + }; + +#ifdef BN_DEBUG +extern int rand_predictable; +#endif + +int RAND_set_rand_method(const RAND_METHOD *meth); +const RAND_METHOD *RAND_get_rand_method(void); +#ifndef OPENSSL_NO_ENGINE +int RAND_set_rand_engine(ENGINE *engine); +#endif +RAND_METHOD *RAND_SSLeay(void); +void RAND_cleanup(void ); +int RAND_bytes(unsigned char *buf,int num); +int RAND_pseudo_bytes(unsigned char *buf,int num); +void RAND_seed(const void *buf,int num); +void RAND_add(const void *buf,int num,double entropy); +int RAND_load_file(const char *file,long max_bytes); +int RAND_write_file(const char *file); +const char *RAND_file_name(char *file,size_t num); +int RAND_status(void); +int RAND_query_egd_bytes(const char *path, unsigned char *buf, int bytes); +int RAND_egd(const char *path); +int RAND_egd_bytes(const char *path,int bytes); +int RAND_poll(void); + +#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) + +void RAND_screen(void); +int RAND_event(UINT, WPARAM, LPARAM); + +#endif + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_RAND_strings(void); + +/* Error codes for the RAND functions. */ + +/* Function codes. */ +#define RAND_F_RAND_GET_RAND_METHOD 101 +#define RAND_F_SSLEAY_RAND_BYTES 100 + +/* Reason codes. */ +#define RAND_R_PRNG_NOT_SEEDED 100 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/rc2.h b/production/3rdparty/openssl/include/openssl/rc2.h new file mode 100644 index 00000000..34c83623 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/rc2.h @@ -0,0 +1,101 @@ +/* crypto/rc2/rc2.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_RC2_H +#define HEADER_RC2_H + +#include /* OPENSSL_NO_RC2, RC2_INT */ +#ifdef OPENSSL_NO_RC2 +#error RC2 is disabled. +#endif + +#define RC2_ENCRYPT 1 +#define RC2_DECRYPT 0 + +#define RC2_BLOCK 8 +#define RC2_KEY_LENGTH 16 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct rc2_key_st + { + RC2_INT data[64]; + } RC2_KEY; + + +void RC2_set_key(RC2_KEY *key, int len, const unsigned char *data,int bits); +void RC2_ecb_encrypt(const unsigned char *in,unsigned char *out,RC2_KEY *key, + int enc); +void RC2_encrypt(unsigned long *data,RC2_KEY *key); +void RC2_decrypt(unsigned long *data,RC2_KEY *key); +void RC2_cbc_encrypt(const unsigned char *in, unsigned char *out, long length, + RC2_KEY *ks, unsigned char *iv, int enc); +void RC2_cfb64_encrypt(const unsigned char *in, unsigned char *out, + long length, RC2_KEY *schedule, unsigned char *ivec, + int *num, int enc); +void RC2_ofb64_encrypt(const unsigned char *in, unsigned char *out, + long length, RC2_KEY *schedule, unsigned char *ivec, + int *num); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/rc4.h b/production/3rdparty/openssl/include/openssl/rc4.h new file mode 100644 index 00000000..7aec04fe --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/rc4.h @@ -0,0 +1,87 @@ +/* crypto/rc4/rc4.h */ +/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_RC4_H +#define HEADER_RC4_H + +#include /* OPENSSL_NO_RC4, RC4_INT */ +#ifdef OPENSSL_NO_RC4 +#error RC4 is disabled. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct rc4_key_st + { + RC4_INT x,y; + RC4_INT data[256]; + } RC4_KEY; + + +const char *RC4_options(void); +void RC4_set_key(RC4_KEY *key, int len, const unsigned char *data); +void RC4(RC4_KEY *key, unsigned long len, const unsigned char *indata, + unsigned char *outdata); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/ripemd.h b/production/3rdparty/openssl/include/openssl/ripemd.h new file mode 100644 index 00000000..06bd6718 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/ripemd.h @@ -0,0 +1,103 @@ +/* crypto/ripemd/ripemd.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_RIPEMD_H +#define HEADER_RIPEMD_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef OPENSSL_NO_RIPEMD +#error RIPEMD is disabled. +#endif + +#if defined(OPENSSL_SYS_WIN16) || defined(__LP32__) +#define RIPEMD160_LONG unsigned long +#elif defined(OPENSSL_SYS_CRAY) || defined(__ILP64__) +#define RIPEMD160_LONG unsigned long +#define RIPEMD160_LONG_LOG2 3 +#else +#define RIPEMD160_LONG unsigned int +#endif + +#define RIPEMD160_CBLOCK 64 +#define RIPEMD160_LBLOCK (RIPEMD160_CBLOCK/4) +#define RIPEMD160_DIGEST_LENGTH 20 + +typedef struct RIPEMD160state_st + { + RIPEMD160_LONG A,B,C,D,E; + RIPEMD160_LONG Nl,Nh; + RIPEMD160_LONG data[RIPEMD160_LBLOCK]; + unsigned int num; + } RIPEMD160_CTX; + +int RIPEMD160_Init(RIPEMD160_CTX *c); +int RIPEMD160_Update(RIPEMD160_CTX *c, const void *data, size_t len); +int RIPEMD160_Final(unsigned char *md, RIPEMD160_CTX *c); +unsigned char *RIPEMD160(const unsigned char *d, size_t n, + unsigned char *md); +void RIPEMD160_Transform(RIPEMD160_CTX *c, const unsigned char *b); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/rsa.h b/production/3rdparty/openssl/include/openssl/rsa.h new file mode 100644 index 00000000..d302254b --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/rsa.h @@ -0,0 +1,429 @@ +/* crypto/rsa/rsa.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_RSA_H +#define HEADER_RSA_H + +#include + +#ifndef OPENSSL_NO_BIO +#include +#endif +#include +#include +#ifndef OPENSSL_NO_DEPRECATED +#include +#endif + +#ifdef OPENSSL_NO_RSA +#error RSA is disabled. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Declared already in ossl_typ.h */ +/* typedef struct rsa_st RSA; */ +/* typedef struct rsa_meth_st RSA_METHOD; */ + +struct rsa_meth_st + { + const char *name; + int (*rsa_pub_enc)(int flen,const unsigned char *from, + unsigned char *to, + RSA *rsa,int padding); + int (*rsa_pub_dec)(int flen,const unsigned char *from, + unsigned char *to, + RSA *rsa,int padding); + int (*rsa_priv_enc)(int flen,const unsigned char *from, + unsigned char *to, + RSA *rsa,int padding); + int (*rsa_priv_dec)(int flen,const unsigned char *from, + unsigned char *to, + RSA *rsa,int padding); + int (*rsa_mod_exp)(BIGNUM *r0,const BIGNUM *I,RSA *rsa,BN_CTX *ctx); /* Can be null */ + int (*bn_mod_exp)(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, + const BIGNUM *m, BN_CTX *ctx, + BN_MONT_CTX *m_ctx); /* Can be null */ + int (*init)(RSA *rsa); /* called at new */ + int (*finish)(RSA *rsa); /* called at free */ + int flags; /* RSA_METHOD_FLAG_* things */ + char *app_data; /* may be needed! */ +/* New sign and verify functions: some libraries don't allow arbitrary data + * to be signed/verified: this allows them to be used. Note: for this to work + * the RSA_public_decrypt() and RSA_private_encrypt() should *NOT* be used + * RSA_sign(), RSA_verify() should be used instead. Note: for backwards + * compatibility this functionality is only enabled if the RSA_FLAG_SIGN_VER + * option is set in 'flags'. + */ + int (*rsa_sign)(int type, + const unsigned char *m, unsigned int m_length, + unsigned char *sigret, unsigned int *siglen, const RSA *rsa); + int (*rsa_verify)(int dtype, + const unsigned char *m, unsigned int m_length, + unsigned char *sigbuf, unsigned int siglen, const RSA *rsa); +/* If this callback is NULL, the builtin software RSA key-gen will be used. This + * is for behavioural compatibility whilst the code gets rewired, but one day + * it would be nice to assume there are no such things as "builtin software" + * implementations. */ + int (*rsa_keygen)(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb); + }; + +struct rsa_st + { + /* The first parameter is used to pickup errors where + * this is passed instead of aEVP_PKEY, it is set to 0 */ + int pad; + long version; + const RSA_METHOD *meth; + /* functional reference if 'meth' is ENGINE-provided */ + ENGINE *engine; + BIGNUM *n; + BIGNUM *e; + BIGNUM *d; + BIGNUM *p; + BIGNUM *q; + BIGNUM *dmp1; + BIGNUM *dmq1; + BIGNUM *iqmp; + /* be careful using this if the RSA structure is shared */ + CRYPTO_EX_DATA ex_data; + int references; + int flags; + + /* Used to cache montgomery values */ + BN_MONT_CTX *_method_mod_n; + BN_MONT_CTX *_method_mod_p; + BN_MONT_CTX *_method_mod_q; + + /* all BIGNUM values are actually in the following data, if it is not + * NULL */ + char *bignum_data; + BN_BLINDING *blinding; + BN_BLINDING *mt_blinding; + }; + +#define RSA_3 0x3L +#define RSA_F4 0x10001L + +#define RSA_METHOD_FLAG_NO_CHECK 0x0001 /* don't check pub/private match */ + +#define RSA_FLAG_CACHE_PUBLIC 0x0002 +#define RSA_FLAG_CACHE_PRIVATE 0x0004 +#define RSA_FLAG_BLINDING 0x0008 +#define RSA_FLAG_THREAD_SAFE 0x0010 +/* This flag means the private key operations will be handled by rsa_mod_exp + * and that they do not depend on the private key components being present: + * for example a key stored in external hardware. Without this flag bn_mod_exp + * gets called when private key components are absent. + */ +#define RSA_FLAG_EXT_PKEY 0x0020 + +/* This flag in the RSA_METHOD enables the new rsa_sign, rsa_verify functions. + */ +#define RSA_FLAG_SIGN_VER 0x0040 + +#define RSA_FLAG_NO_BLINDING 0x0080 /* new with 0.9.6j and 0.9.7b; the built-in + * RSA implementation now uses blinding by + * default (ignoring RSA_FLAG_BLINDING), + * but other engines might not need it + */ +#define RSA_FLAG_NO_EXP_CONSTTIME 0x0100 /* new with 0.9.7h; the built-in RSA + * implementation now uses constant time + * modular exponentiation for secret exponents + * by default. This flag causes the + * faster variable sliding window method to + * be used for all exponents. + */ + +#define RSA_PKCS1_PADDING 1 +#define RSA_SSLV23_PADDING 2 +#define RSA_NO_PADDING 3 +#define RSA_PKCS1_OAEP_PADDING 4 +#define RSA_X931_PADDING 5 + +#define RSA_PKCS1_PADDING_SIZE 11 + +#define RSA_set_app_data(s,arg) RSA_set_ex_data(s,0,arg) +#define RSA_get_app_data(s) RSA_get_ex_data(s,0) + +RSA * RSA_new(void); +RSA * RSA_new_method(ENGINE *engine); +int RSA_size(const RSA *); + +/* Deprecated version */ +#ifndef OPENSSL_NO_DEPRECATED +RSA * RSA_generate_key(int bits, unsigned long e,void + (*callback)(int,int,void *),void *cb_arg); +#endif /* !defined(OPENSSL_NO_DEPRECATED) */ + +/* New version */ +int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb); + +int RSA_check_key(const RSA *); + /* next 4 return -1 on error */ +int RSA_public_encrypt(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa,int padding); +int RSA_private_encrypt(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa,int padding); +int RSA_public_decrypt(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa,int padding); +int RSA_private_decrypt(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa,int padding); +void RSA_free (RSA *r); +/* "up" the RSA object's reference count */ +int RSA_up_ref(RSA *r); + +int RSA_flags(const RSA *r); + +void RSA_set_default_method(const RSA_METHOD *meth); +const RSA_METHOD *RSA_get_default_method(void); +const RSA_METHOD *RSA_get_method(const RSA *rsa); +int RSA_set_method(RSA *rsa, const RSA_METHOD *meth); + +/* This function needs the memory locking malloc callbacks to be installed */ +int RSA_memory_lock(RSA *r); + +/* these are the actual SSLeay RSA functions */ +const RSA_METHOD *RSA_PKCS1_SSLeay(void); + +const RSA_METHOD *RSA_null_method(void); + +DECLARE_ASN1_ENCODE_FUNCTIONS_const(RSA, RSAPublicKey) +DECLARE_ASN1_ENCODE_FUNCTIONS_const(RSA, RSAPrivateKey) + +#ifndef OPENSSL_NO_FP_API +int RSA_print_fp(FILE *fp, const RSA *r,int offset); +#endif + +#ifndef OPENSSL_NO_BIO +int RSA_print(BIO *bp, const RSA *r,int offset); +#endif + +int i2d_RSA_NET(const RSA *a, unsigned char **pp, + int (*cb)(char *buf, int len, const char *prompt, int verify), + int sgckey); +RSA *d2i_RSA_NET(RSA **a, const unsigned char **pp, long length, + int (*cb)(char *buf, int len, const char *prompt, int verify), + int sgckey); + +int i2d_Netscape_RSA(const RSA *a, unsigned char **pp, + int (*cb)(char *buf, int len, const char *prompt, + int verify)); +RSA *d2i_Netscape_RSA(RSA **a, const unsigned char **pp, long length, + int (*cb)(char *buf, int len, const char *prompt, + int verify)); + +/* The following 2 functions sign and verify a X509_SIG ASN1 object + * inside PKCS#1 padded RSA encryption */ +int RSA_sign(int type, const unsigned char *m, unsigned int m_length, + unsigned char *sigret, unsigned int *siglen, RSA *rsa); +int RSA_verify(int type, const unsigned char *m, unsigned int m_length, + unsigned char *sigbuf, unsigned int siglen, RSA *rsa); + +/* The following 2 function sign and verify a ASN1_OCTET_STRING + * object inside PKCS#1 padded RSA encryption */ +int RSA_sign_ASN1_OCTET_STRING(int type, + const unsigned char *m, unsigned int m_length, + unsigned char *sigret, unsigned int *siglen, RSA *rsa); +int RSA_verify_ASN1_OCTET_STRING(int type, + const unsigned char *m, unsigned int m_length, + unsigned char *sigbuf, unsigned int siglen, RSA *rsa); + +int RSA_blinding_on(RSA *rsa, BN_CTX *ctx); +void RSA_blinding_off(RSA *rsa); +BN_BLINDING *RSA_setup_blinding(RSA *rsa, BN_CTX *ctx); + +int RSA_padding_add_PKCS1_type_1(unsigned char *to,int tlen, + const unsigned char *f,int fl); +int RSA_padding_check_PKCS1_type_1(unsigned char *to,int tlen, + const unsigned char *f,int fl,int rsa_len); +int RSA_padding_add_PKCS1_type_2(unsigned char *to,int tlen, + const unsigned char *f,int fl); +int RSA_padding_check_PKCS1_type_2(unsigned char *to,int tlen, + const unsigned char *f,int fl,int rsa_len); +int PKCS1_MGF1(unsigned char *mask, long len, + const unsigned char *seed, long seedlen, const EVP_MD *dgst); +int RSA_padding_add_PKCS1_OAEP(unsigned char *to,int tlen, + const unsigned char *f,int fl, + const unsigned char *p,int pl); +int RSA_padding_check_PKCS1_OAEP(unsigned char *to,int tlen, + const unsigned char *f,int fl,int rsa_len, + const unsigned char *p,int pl); +int RSA_padding_add_SSLv23(unsigned char *to,int tlen, + const unsigned char *f,int fl); +int RSA_padding_check_SSLv23(unsigned char *to,int tlen, + const unsigned char *f,int fl,int rsa_len); +int RSA_padding_add_none(unsigned char *to,int tlen, + const unsigned char *f,int fl); +int RSA_padding_check_none(unsigned char *to,int tlen, + const unsigned char *f,int fl,int rsa_len); +int RSA_padding_add_X931(unsigned char *to,int tlen, + const unsigned char *f,int fl); +int RSA_padding_check_X931(unsigned char *to,int tlen, + const unsigned char *f,int fl,int rsa_len); +int RSA_X931_hash_id(int nid); + +int RSA_verify_PKCS1_PSS(RSA *rsa, const unsigned char *mHash, + const EVP_MD *Hash, const unsigned char *EM, int sLen); +int RSA_padding_add_PKCS1_PSS(RSA *rsa, unsigned char *EM, + const unsigned char *mHash, + const EVP_MD *Hash, int sLen); + +int RSA_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, + CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); +int RSA_set_ex_data(RSA *r,int idx,void *arg); +void *RSA_get_ex_data(const RSA *r, int idx); + +RSA *RSAPublicKey_dup(RSA *rsa); +RSA *RSAPrivateKey_dup(RSA *rsa); + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_RSA_strings(void); + +/* Error codes for the RSA functions. */ + +/* Function codes. */ +#define RSA_F_MEMORY_LOCK 100 +#define RSA_F_RSA_BUILTIN_KEYGEN 129 +#define RSA_F_RSA_CHECK_KEY 123 +#define RSA_F_RSA_EAY_PRIVATE_DECRYPT 101 +#define RSA_F_RSA_EAY_PRIVATE_ENCRYPT 102 +#define RSA_F_RSA_EAY_PUBLIC_DECRYPT 103 +#define RSA_F_RSA_EAY_PUBLIC_ENCRYPT 104 +#define RSA_F_RSA_GENERATE_KEY 105 +#define RSA_F_RSA_MEMORY_LOCK 130 +#define RSA_F_RSA_NEW_METHOD 106 +#define RSA_F_RSA_NULL 124 +#define RSA_F_RSA_NULL_MOD_EXP 131 +#define RSA_F_RSA_NULL_PRIVATE_DECRYPT 132 +#define RSA_F_RSA_NULL_PRIVATE_ENCRYPT 133 +#define RSA_F_RSA_NULL_PUBLIC_DECRYPT 134 +#define RSA_F_RSA_NULL_PUBLIC_ENCRYPT 135 +#define RSA_F_RSA_PADDING_ADD_NONE 107 +#define RSA_F_RSA_PADDING_ADD_PKCS1_OAEP 121 +#define RSA_F_RSA_PADDING_ADD_PKCS1_PSS 125 +#define RSA_F_RSA_PADDING_ADD_PKCS1_TYPE_1 108 +#define RSA_F_RSA_PADDING_ADD_PKCS1_TYPE_2 109 +#define RSA_F_RSA_PADDING_ADD_SSLV23 110 +#define RSA_F_RSA_PADDING_ADD_X931 127 +#define RSA_F_RSA_PADDING_CHECK_NONE 111 +#define RSA_F_RSA_PADDING_CHECK_PKCS1_OAEP 122 +#define RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_1 112 +#define RSA_F_RSA_PADDING_CHECK_PKCS1_TYPE_2 113 +#define RSA_F_RSA_PADDING_CHECK_SSLV23 114 +#define RSA_F_RSA_PADDING_CHECK_X931 128 +#define RSA_F_RSA_PRINT 115 +#define RSA_F_RSA_PRINT_FP 116 +#define RSA_F_RSA_SETUP_BLINDING 136 +#define RSA_F_RSA_SIGN 117 +#define RSA_F_RSA_SIGN_ASN1_OCTET_STRING 118 +#define RSA_F_RSA_VERIFY 119 +#define RSA_F_RSA_VERIFY_ASN1_OCTET_STRING 120 +#define RSA_F_RSA_VERIFY_PKCS1_PSS 126 + +/* Reason codes. */ +#define RSA_R_ALGORITHM_MISMATCH 100 +#define RSA_R_BAD_E_VALUE 101 +#define RSA_R_BAD_FIXED_HEADER_DECRYPT 102 +#define RSA_R_BAD_PAD_BYTE_COUNT 103 +#define RSA_R_BAD_SIGNATURE 104 +#define RSA_R_BLOCK_TYPE_IS_NOT_01 106 +#define RSA_R_BLOCK_TYPE_IS_NOT_02 107 +#define RSA_R_DATA_GREATER_THAN_MOD_LEN 108 +#define RSA_R_DATA_TOO_LARGE 109 +#define RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE 110 +#define RSA_R_DATA_TOO_LARGE_FOR_MODULUS 132 +#define RSA_R_DATA_TOO_SMALL 111 +#define RSA_R_DATA_TOO_SMALL_FOR_KEY_SIZE 122 +#define RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY 112 +#define RSA_R_DMP1_NOT_CONGRUENT_TO_D 124 +#define RSA_R_DMQ1_NOT_CONGRUENT_TO_D 125 +#define RSA_R_D_E_NOT_CONGRUENT_TO_1 123 +#define RSA_R_FIRST_OCTET_INVALID 133 +#define RSA_R_INVALID_HEADER 137 +#define RSA_R_INVALID_MESSAGE_LENGTH 131 +#define RSA_R_INVALID_PADDING 138 +#define RSA_R_INVALID_TRAILER 139 +#define RSA_R_IQMP_NOT_INVERSE_OF_Q 126 +#define RSA_R_KEY_SIZE_TOO_SMALL 120 +#define RSA_R_LAST_OCTET_INVALID 134 +#define RSA_R_NO_PUBLIC_EXPONENT 140 +#define RSA_R_NULL_BEFORE_BLOCK_MISSING 113 +#define RSA_R_N_DOES_NOT_EQUAL_P_Q 127 +#define RSA_R_OAEP_DECODING_ERROR 121 +#define RSA_R_PADDING_CHECK_FAILED 114 +#define RSA_R_P_NOT_PRIME 128 +#define RSA_R_Q_NOT_PRIME 129 +#define RSA_R_RSA_OPERATIONS_NOT_SUPPORTED 130 +#define RSA_R_SLEN_CHECK_FAILED 136 +#define RSA_R_SLEN_RECOVERY_FAILED 135 +#define RSA_R_SSLV3_ROLLBACK_ATTACK 115 +#define RSA_R_THE_ASN1_OBJECT_IDENTIFIER_IS_NOT_KNOWN_FOR_THIS_MD 116 +#define RSA_R_UNKNOWN_ALGORITHM_TYPE 117 +#define RSA_R_UNKNOWN_PADDING_TYPE 118 +#define RSA_R_WRONG_SIGNATURE_LENGTH 119 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/safestack.h b/production/3rdparty/openssl/include/openssl/safestack.h new file mode 100644 index 00000000..e5f5be9f --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/safestack.h @@ -0,0 +1,1784 @@ +/* ==================================================================== + * Copyright (c) 1999 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#ifndef HEADER_SAFESTACK_H +#define HEADER_SAFESTACK_H + +#include + +typedef void (*openssl_fptr)(void); +#define openssl_fcast(f) ((openssl_fptr)f) + +#ifdef DEBUG_SAFESTACK + +#define STACK_OF(type) struct stack_st_##type +#define PREDECLARE_STACK_OF(type) STACK_OF(type); + +#define DECLARE_STACK_OF(type) \ +STACK_OF(type) \ + { \ + STACK stack; \ + }; + +#define IMPLEMENT_STACK_OF(type) /* nada (obsolete in new safestack approach)*/ + +/* SKM_sk_... stack macros are internal to safestack.h: + * never use them directly, use sk__... instead */ +#define SKM_sk_new(type, cmp) \ + ((STACK_OF(type) * (*)(int (*)(const type * const *, const type * const *)))openssl_fcast(sk_new))(cmp) +#define SKM_sk_new_null(type) \ + ((STACK_OF(type) * (*)(void))openssl_fcast(sk_new_null))() +#define SKM_sk_free(type, st) \ + ((void (*)(STACK_OF(type) *))openssl_fcast(sk_free))(st) +#define SKM_sk_num(type, st) \ + ((int (*)(const STACK_OF(type) *))openssl_fcast(sk_num))(st) +#define SKM_sk_value(type, st,i) \ + ((type * (*)(const STACK_OF(type) *, int))openssl_fcast(sk_value))(st, i) +#define SKM_sk_set(type, st,i,val) \ + ((type * (*)(STACK_OF(type) *, int, type *))openssl_fcast(sk_set))(st, i, val) +#define SKM_sk_zero(type, st) \ + ((void (*)(STACK_OF(type) *))openssl_fcast(sk_zero))(st) +#define SKM_sk_push(type, st,val) \ + ((int (*)(STACK_OF(type) *, type *))openssl_fcast(sk_push))(st, val) +#define SKM_sk_unshift(type, st,val) \ + ((int (*)(STACK_OF(type) *, type *))openssl_fcast(sk_unshift))(st, val) +#define SKM_sk_find(type, st,val) \ + ((int (*)(STACK_OF(type) *, type *))openssl_fcast(sk_find))(st, val) +#define SKM_sk_delete(type, st,i) \ + ((type * (*)(STACK_OF(type) *, int))openssl_fcast(sk_delete))(st, i) +#define SKM_sk_delete_ptr(type, st,ptr) \ + ((type * (*)(STACK_OF(type) *, type *))openssl_fcast(sk_delete_ptr))(st, ptr) +#define SKM_sk_insert(type, st,val,i) \ + ((int (*)(STACK_OF(type) *, type *, int))openssl_fcast(sk_insert))(st, val, i) +#define SKM_sk_set_cmp_func(type, st,cmp) \ + ((int (*(*)(STACK_OF(type) *, int (*)(const type * const *, const type * const *))) \ + (const type * const *, const type * const *))openssl_fcast(sk_set_cmp_func))\ + (st, cmp) +#define SKM_sk_dup(type, st) \ + ((STACK_OF(type) *(*)(STACK_OF(type) *))openssl_fcast(sk_dup))(st) +#define SKM_sk_pop_free(type, st,free_func) \ + ((void (*)(STACK_OF(type) *, void (*)(type *)))openssl_fcast(sk_pop_free))\ + (st, free_func) +#define SKM_sk_shift(type, st) \ + ((type * (*)(STACK_OF(type) *))openssl_fcast(sk_shift))(st) +#define SKM_sk_pop(type, st) \ + ((type * (*)(STACK_OF(type) *))openssl_fcast(sk_pop))(st) +#define SKM_sk_sort(type, st) \ + ((void (*)(STACK_OF(type) *))openssl_fcast(sk_sort))(st) +#define SKM_sk_is_sorted(type, st) \ + ((int (*)(const STACK_OF(type) *))openssl_fcast(sk_is_sorted))(st) + +#define SKM_ASN1_SET_OF_d2i(type, st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ +((STACK_OF(type) * (*) (STACK_OF(type) **,const unsigned char **, long , \ + type *(*)(type **, const unsigned char **,long), \ + void (*)(type *), int ,int )) openssl_fcast(d2i_ASN1_SET)) \ + (st,pp,length, d2i_func, free_func, ex_tag,ex_class) +#define SKM_ASN1_SET_OF_i2d(type, st, pp, i2d_func, ex_tag, ex_class, is_set) \ + ((int (*)(STACK_OF(type) *,unsigned char **, \ + int (*)(type *,unsigned char **), int , int , int)) openssl_fcast(i2d_ASN1_SET)) \ + (st,pp,i2d_func,ex_tag,ex_class,is_set) + +#define SKM_ASN1_seq_pack(type, st, i2d_func, buf, len) \ + ((unsigned char *(*)(STACK_OF(type) *, \ + int (*)(type *,unsigned char **), unsigned char **,int *)) openssl_fcast(ASN1_seq_pack)) \ + (st, i2d_func, buf, len) +#define SKM_ASN1_seq_unpack(type, buf, len, d2i_func, free_func) \ + ((STACK_OF(type) * (*)(const unsigned char *,int, \ + type *(*)(type **,const unsigned char **, long), \ + void (*)(type *)))openssl_fcast(ASN1_seq_unpack)) \ + (buf,len,d2i_func, free_func) + +#define SKM_PKCS12_decrypt_d2i(type, algor, d2i_func, free_func, pass, passlen, oct, seq) \ + ((STACK_OF(type) * (*)(X509_ALGOR *, \ + type *(*)(type **, const unsigned char **, long), \ + void (*)(type *), \ + const char *, int, \ + ASN1_STRING *, int))PKCS12_decrypt_d2i) \ + (algor,d2i_func,free_func,pass,passlen,oct,seq) + +#else + +#define STACK_OF(type) STACK +#define PREDECLARE_STACK_OF(type) /* nada */ +#define DECLARE_STACK_OF(type) /* nada */ +#define IMPLEMENT_STACK_OF(type) /* nada */ + +#define SKM_sk_new(type, cmp) \ + sk_new((int (*)(const char * const *, const char * const *))(cmp)) +#define SKM_sk_new_null(type) \ + sk_new_null() +#define SKM_sk_free(type, st) \ + sk_free(st) +#define SKM_sk_num(type, st) \ + sk_num(st) +#define SKM_sk_value(type, st,i) \ + ((type *)sk_value(st, i)) +#define SKM_sk_set(type, st,i,val) \ + ((type *)sk_set(st, i,(char *)val)) +#define SKM_sk_zero(type, st) \ + sk_zero(st) +#define SKM_sk_push(type, st,val) \ + sk_push(st, (char *)val) +#define SKM_sk_unshift(type, st,val) \ + sk_unshift(st, val) +#define SKM_sk_find(type, st,val) \ + sk_find(st, (char *)val) +#define SKM_sk_delete(type, st,i) \ + ((type *)sk_delete(st, i)) +#define SKM_sk_delete_ptr(type, st,ptr) \ + ((type *)sk_delete_ptr(st,(char *)ptr)) +#define SKM_sk_insert(type, st,val,i) \ + sk_insert(st, (char *)val, i) +#define SKM_sk_set_cmp_func(type, st,cmp) \ + ((int (*)(const type * const *,const type * const *)) \ + sk_set_cmp_func(st, (int (*)(const char * const *, const char * const *))(cmp))) +#define SKM_sk_dup(type, st) \ + sk_dup(st) +#define SKM_sk_pop_free(type, st,free_func) \ + sk_pop_free(st, (void (*)(void *))free_func) +#define SKM_sk_shift(type, st) \ + ((type *)sk_shift(st)) +#define SKM_sk_pop(type, st) \ + ((type *)sk_pop(st)) +#define SKM_sk_sort(type, st) \ + sk_sort(st) +#define SKM_sk_is_sorted(type, st) \ + sk_is_sorted(st) + +#define SKM_ASN1_SET_OF_d2i(type, st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + d2i_ASN1_SET(st,pp,length, (void *(*)(void ** ,const unsigned char ** ,long))d2i_func, (void (*)(void *))free_func, ex_tag,ex_class) +#define SKM_ASN1_SET_OF_i2d(type, st, pp, i2d_func, ex_tag, ex_class, is_set) \ + i2d_ASN1_SET(st,pp,(int (*)(void *, unsigned char **))i2d_func,ex_tag,ex_class,is_set) + +#define SKM_ASN1_seq_pack(type, st, i2d_func, buf, len) \ + ASN1_seq_pack(st, (int (*)(void *, unsigned char **))i2d_func, buf, len) +#define SKM_ASN1_seq_unpack(type, buf, len, d2i_func, free_func) \ + ASN1_seq_unpack(buf,len,(void *(*)(void **,const unsigned char **,long))d2i_func, (void(*)(void *))free_func) + +#define SKM_PKCS12_decrypt_d2i(type, algor, d2i_func, free_func, pass, passlen, oct, seq) \ + ((STACK *)PKCS12_decrypt_d2i(algor,(char *(*)())d2i_func, (void(*)(void *))free_func,pass,passlen,oct,seq)) + +#endif + +/* This block of defines is updated by util/mkstack.pl, please do not touch! */ +#define sk_ACCESS_DESCRIPTION_new(st) SKM_sk_new(ACCESS_DESCRIPTION, (st)) +#define sk_ACCESS_DESCRIPTION_new_null() SKM_sk_new_null(ACCESS_DESCRIPTION) +#define sk_ACCESS_DESCRIPTION_free(st) SKM_sk_free(ACCESS_DESCRIPTION, (st)) +#define sk_ACCESS_DESCRIPTION_num(st) SKM_sk_num(ACCESS_DESCRIPTION, (st)) +#define sk_ACCESS_DESCRIPTION_value(st, i) SKM_sk_value(ACCESS_DESCRIPTION, (st), (i)) +#define sk_ACCESS_DESCRIPTION_set(st, i, val) SKM_sk_set(ACCESS_DESCRIPTION, (st), (i), (val)) +#define sk_ACCESS_DESCRIPTION_zero(st) SKM_sk_zero(ACCESS_DESCRIPTION, (st)) +#define sk_ACCESS_DESCRIPTION_push(st, val) SKM_sk_push(ACCESS_DESCRIPTION, (st), (val)) +#define sk_ACCESS_DESCRIPTION_unshift(st, val) SKM_sk_unshift(ACCESS_DESCRIPTION, (st), (val)) +#define sk_ACCESS_DESCRIPTION_find(st, val) SKM_sk_find(ACCESS_DESCRIPTION, (st), (val)) +#define sk_ACCESS_DESCRIPTION_find_ex(st, val) SKM_sk_find_ex(ACCESS_DESCRIPTION, (st), (val)) +#define sk_ACCESS_DESCRIPTION_delete(st, i) SKM_sk_delete(ACCESS_DESCRIPTION, (st), (i)) +#define sk_ACCESS_DESCRIPTION_delete_ptr(st, ptr) SKM_sk_delete_ptr(ACCESS_DESCRIPTION, (st), (ptr)) +#define sk_ACCESS_DESCRIPTION_insert(st, val, i) SKM_sk_insert(ACCESS_DESCRIPTION, (st), (val), (i)) +#define sk_ACCESS_DESCRIPTION_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(ACCESS_DESCRIPTION, (st), (cmp)) +#define sk_ACCESS_DESCRIPTION_dup(st) SKM_sk_dup(ACCESS_DESCRIPTION, st) +#define sk_ACCESS_DESCRIPTION_pop_free(st, free_func) SKM_sk_pop_free(ACCESS_DESCRIPTION, (st), (free_func)) +#define sk_ACCESS_DESCRIPTION_shift(st) SKM_sk_shift(ACCESS_DESCRIPTION, (st)) +#define sk_ACCESS_DESCRIPTION_pop(st) SKM_sk_pop(ACCESS_DESCRIPTION, (st)) +#define sk_ACCESS_DESCRIPTION_sort(st) SKM_sk_sort(ACCESS_DESCRIPTION, (st)) +#define sk_ACCESS_DESCRIPTION_is_sorted(st) SKM_sk_is_sorted(ACCESS_DESCRIPTION, (st)) + +#define sk_ASN1_GENERALSTRING_new(st) SKM_sk_new(ASN1_GENERALSTRING, (st)) +#define sk_ASN1_GENERALSTRING_new_null() SKM_sk_new_null(ASN1_GENERALSTRING) +#define sk_ASN1_GENERALSTRING_free(st) SKM_sk_free(ASN1_GENERALSTRING, (st)) +#define sk_ASN1_GENERALSTRING_num(st) SKM_sk_num(ASN1_GENERALSTRING, (st)) +#define sk_ASN1_GENERALSTRING_value(st, i) SKM_sk_value(ASN1_GENERALSTRING, (st), (i)) +#define sk_ASN1_GENERALSTRING_set(st, i, val) SKM_sk_set(ASN1_GENERALSTRING, (st), (i), (val)) +#define sk_ASN1_GENERALSTRING_zero(st) SKM_sk_zero(ASN1_GENERALSTRING, (st)) +#define sk_ASN1_GENERALSTRING_push(st, val) SKM_sk_push(ASN1_GENERALSTRING, (st), (val)) +#define sk_ASN1_GENERALSTRING_unshift(st, val) SKM_sk_unshift(ASN1_GENERALSTRING, (st), (val)) +#define sk_ASN1_GENERALSTRING_find(st, val) SKM_sk_find(ASN1_GENERALSTRING, (st), (val)) +#define sk_ASN1_GENERALSTRING_find_ex(st, val) SKM_sk_find_ex(ASN1_GENERALSTRING, (st), (val)) +#define sk_ASN1_GENERALSTRING_delete(st, i) SKM_sk_delete(ASN1_GENERALSTRING, (st), (i)) +#define sk_ASN1_GENERALSTRING_delete_ptr(st, ptr) SKM_sk_delete_ptr(ASN1_GENERALSTRING, (st), (ptr)) +#define sk_ASN1_GENERALSTRING_insert(st, val, i) SKM_sk_insert(ASN1_GENERALSTRING, (st), (val), (i)) +#define sk_ASN1_GENERALSTRING_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(ASN1_GENERALSTRING, (st), (cmp)) +#define sk_ASN1_GENERALSTRING_dup(st) SKM_sk_dup(ASN1_GENERALSTRING, st) +#define sk_ASN1_GENERALSTRING_pop_free(st, free_func) SKM_sk_pop_free(ASN1_GENERALSTRING, (st), (free_func)) +#define sk_ASN1_GENERALSTRING_shift(st) SKM_sk_shift(ASN1_GENERALSTRING, (st)) +#define sk_ASN1_GENERALSTRING_pop(st) SKM_sk_pop(ASN1_GENERALSTRING, (st)) +#define sk_ASN1_GENERALSTRING_sort(st) SKM_sk_sort(ASN1_GENERALSTRING, (st)) +#define sk_ASN1_GENERALSTRING_is_sorted(st) SKM_sk_is_sorted(ASN1_GENERALSTRING, (st)) + +#define sk_ASN1_INTEGER_new(st) SKM_sk_new(ASN1_INTEGER, (st)) +#define sk_ASN1_INTEGER_new_null() SKM_sk_new_null(ASN1_INTEGER) +#define sk_ASN1_INTEGER_free(st) SKM_sk_free(ASN1_INTEGER, (st)) +#define sk_ASN1_INTEGER_num(st) SKM_sk_num(ASN1_INTEGER, (st)) +#define sk_ASN1_INTEGER_value(st, i) SKM_sk_value(ASN1_INTEGER, (st), (i)) +#define sk_ASN1_INTEGER_set(st, i, val) SKM_sk_set(ASN1_INTEGER, (st), (i), (val)) +#define sk_ASN1_INTEGER_zero(st) SKM_sk_zero(ASN1_INTEGER, (st)) +#define sk_ASN1_INTEGER_push(st, val) SKM_sk_push(ASN1_INTEGER, (st), (val)) +#define sk_ASN1_INTEGER_unshift(st, val) SKM_sk_unshift(ASN1_INTEGER, (st), (val)) +#define sk_ASN1_INTEGER_find(st, val) SKM_sk_find(ASN1_INTEGER, (st), (val)) +#define sk_ASN1_INTEGER_find_ex(st, val) SKM_sk_find_ex(ASN1_INTEGER, (st), (val)) +#define sk_ASN1_INTEGER_delete(st, i) SKM_sk_delete(ASN1_INTEGER, (st), (i)) +#define sk_ASN1_INTEGER_delete_ptr(st, ptr) SKM_sk_delete_ptr(ASN1_INTEGER, (st), (ptr)) +#define sk_ASN1_INTEGER_insert(st, val, i) SKM_sk_insert(ASN1_INTEGER, (st), (val), (i)) +#define sk_ASN1_INTEGER_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(ASN1_INTEGER, (st), (cmp)) +#define sk_ASN1_INTEGER_dup(st) SKM_sk_dup(ASN1_INTEGER, st) +#define sk_ASN1_INTEGER_pop_free(st, free_func) SKM_sk_pop_free(ASN1_INTEGER, (st), (free_func)) +#define sk_ASN1_INTEGER_shift(st) SKM_sk_shift(ASN1_INTEGER, (st)) +#define sk_ASN1_INTEGER_pop(st) SKM_sk_pop(ASN1_INTEGER, (st)) +#define sk_ASN1_INTEGER_sort(st) SKM_sk_sort(ASN1_INTEGER, (st)) +#define sk_ASN1_INTEGER_is_sorted(st) SKM_sk_is_sorted(ASN1_INTEGER, (st)) + +#define sk_ASN1_OBJECT_new(st) SKM_sk_new(ASN1_OBJECT, (st)) +#define sk_ASN1_OBJECT_new_null() SKM_sk_new_null(ASN1_OBJECT) +#define sk_ASN1_OBJECT_free(st) SKM_sk_free(ASN1_OBJECT, (st)) +#define sk_ASN1_OBJECT_num(st) SKM_sk_num(ASN1_OBJECT, (st)) +#define sk_ASN1_OBJECT_value(st, i) SKM_sk_value(ASN1_OBJECT, (st), (i)) +#define sk_ASN1_OBJECT_set(st, i, val) SKM_sk_set(ASN1_OBJECT, (st), (i), (val)) +#define sk_ASN1_OBJECT_zero(st) SKM_sk_zero(ASN1_OBJECT, (st)) +#define sk_ASN1_OBJECT_push(st, val) SKM_sk_push(ASN1_OBJECT, (st), (val)) +#define sk_ASN1_OBJECT_unshift(st, val) SKM_sk_unshift(ASN1_OBJECT, (st), (val)) +#define sk_ASN1_OBJECT_find(st, val) SKM_sk_find(ASN1_OBJECT, (st), (val)) +#define sk_ASN1_OBJECT_find_ex(st, val) SKM_sk_find_ex(ASN1_OBJECT, (st), (val)) +#define sk_ASN1_OBJECT_delete(st, i) SKM_sk_delete(ASN1_OBJECT, (st), (i)) +#define sk_ASN1_OBJECT_delete_ptr(st, ptr) SKM_sk_delete_ptr(ASN1_OBJECT, (st), (ptr)) +#define sk_ASN1_OBJECT_insert(st, val, i) SKM_sk_insert(ASN1_OBJECT, (st), (val), (i)) +#define sk_ASN1_OBJECT_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(ASN1_OBJECT, (st), (cmp)) +#define sk_ASN1_OBJECT_dup(st) SKM_sk_dup(ASN1_OBJECT, st) +#define sk_ASN1_OBJECT_pop_free(st, free_func) SKM_sk_pop_free(ASN1_OBJECT, (st), (free_func)) +#define sk_ASN1_OBJECT_shift(st) SKM_sk_shift(ASN1_OBJECT, (st)) +#define sk_ASN1_OBJECT_pop(st) SKM_sk_pop(ASN1_OBJECT, (st)) +#define sk_ASN1_OBJECT_sort(st) SKM_sk_sort(ASN1_OBJECT, (st)) +#define sk_ASN1_OBJECT_is_sorted(st) SKM_sk_is_sorted(ASN1_OBJECT, (st)) + +#define sk_ASN1_STRING_TABLE_new(st) SKM_sk_new(ASN1_STRING_TABLE, (st)) +#define sk_ASN1_STRING_TABLE_new_null() SKM_sk_new_null(ASN1_STRING_TABLE) +#define sk_ASN1_STRING_TABLE_free(st) SKM_sk_free(ASN1_STRING_TABLE, (st)) +#define sk_ASN1_STRING_TABLE_num(st) SKM_sk_num(ASN1_STRING_TABLE, (st)) +#define sk_ASN1_STRING_TABLE_value(st, i) SKM_sk_value(ASN1_STRING_TABLE, (st), (i)) +#define sk_ASN1_STRING_TABLE_set(st, i, val) SKM_sk_set(ASN1_STRING_TABLE, (st), (i), (val)) +#define sk_ASN1_STRING_TABLE_zero(st) SKM_sk_zero(ASN1_STRING_TABLE, (st)) +#define sk_ASN1_STRING_TABLE_push(st, val) SKM_sk_push(ASN1_STRING_TABLE, (st), (val)) +#define sk_ASN1_STRING_TABLE_unshift(st, val) SKM_sk_unshift(ASN1_STRING_TABLE, (st), (val)) +#define sk_ASN1_STRING_TABLE_find(st, val) SKM_sk_find(ASN1_STRING_TABLE, (st), (val)) +#define sk_ASN1_STRING_TABLE_find_ex(st, val) SKM_sk_find_ex(ASN1_STRING_TABLE, (st), (val)) +#define sk_ASN1_STRING_TABLE_delete(st, i) SKM_sk_delete(ASN1_STRING_TABLE, (st), (i)) +#define sk_ASN1_STRING_TABLE_delete_ptr(st, ptr) SKM_sk_delete_ptr(ASN1_STRING_TABLE, (st), (ptr)) +#define sk_ASN1_STRING_TABLE_insert(st, val, i) SKM_sk_insert(ASN1_STRING_TABLE, (st), (val), (i)) +#define sk_ASN1_STRING_TABLE_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(ASN1_STRING_TABLE, (st), (cmp)) +#define sk_ASN1_STRING_TABLE_dup(st) SKM_sk_dup(ASN1_STRING_TABLE, st) +#define sk_ASN1_STRING_TABLE_pop_free(st, free_func) SKM_sk_pop_free(ASN1_STRING_TABLE, (st), (free_func)) +#define sk_ASN1_STRING_TABLE_shift(st) SKM_sk_shift(ASN1_STRING_TABLE, (st)) +#define sk_ASN1_STRING_TABLE_pop(st) SKM_sk_pop(ASN1_STRING_TABLE, (st)) +#define sk_ASN1_STRING_TABLE_sort(st) SKM_sk_sort(ASN1_STRING_TABLE, (st)) +#define sk_ASN1_STRING_TABLE_is_sorted(st) SKM_sk_is_sorted(ASN1_STRING_TABLE, (st)) + +#define sk_ASN1_TYPE_new(st) SKM_sk_new(ASN1_TYPE, (st)) +#define sk_ASN1_TYPE_new_null() SKM_sk_new_null(ASN1_TYPE) +#define sk_ASN1_TYPE_free(st) SKM_sk_free(ASN1_TYPE, (st)) +#define sk_ASN1_TYPE_num(st) SKM_sk_num(ASN1_TYPE, (st)) +#define sk_ASN1_TYPE_value(st, i) SKM_sk_value(ASN1_TYPE, (st), (i)) +#define sk_ASN1_TYPE_set(st, i, val) SKM_sk_set(ASN1_TYPE, (st), (i), (val)) +#define sk_ASN1_TYPE_zero(st) SKM_sk_zero(ASN1_TYPE, (st)) +#define sk_ASN1_TYPE_push(st, val) SKM_sk_push(ASN1_TYPE, (st), (val)) +#define sk_ASN1_TYPE_unshift(st, val) SKM_sk_unshift(ASN1_TYPE, (st), (val)) +#define sk_ASN1_TYPE_find(st, val) SKM_sk_find(ASN1_TYPE, (st), (val)) +#define sk_ASN1_TYPE_find_ex(st, val) SKM_sk_find_ex(ASN1_TYPE, (st), (val)) +#define sk_ASN1_TYPE_delete(st, i) SKM_sk_delete(ASN1_TYPE, (st), (i)) +#define sk_ASN1_TYPE_delete_ptr(st, ptr) SKM_sk_delete_ptr(ASN1_TYPE, (st), (ptr)) +#define sk_ASN1_TYPE_insert(st, val, i) SKM_sk_insert(ASN1_TYPE, (st), (val), (i)) +#define sk_ASN1_TYPE_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(ASN1_TYPE, (st), (cmp)) +#define sk_ASN1_TYPE_dup(st) SKM_sk_dup(ASN1_TYPE, st) +#define sk_ASN1_TYPE_pop_free(st, free_func) SKM_sk_pop_free(ASN1_TYPE, (st), (free_func)) +#define sk_ASN1_TYPE_shift(st) SKM_sk_shift(ASN1_TYPE, (st)) +#define sk_ASN1_TYPE_pop(st) SKM_sk_pop(ASN1_TYPE, (st)) +#define sk_ASN1_TYPE_sort(st) SKM_sk_sort(ASN1_TYPE, (st)) +#define sk_ASN1_TYPE_is_sorted(st) SKM_sk_is_sorted(ASN1_TYPE, (st)) + +#define sk_ASN1_VALUE_new(st) SKM_sk_new(ASN1_VALUE, (st)) +#define sk_ASN1_VALUE_new_null() SKM_sk_new_null(ASN1_VALUE) +#define sk_ASN1_VALUE_free(st) SKM_sk_free(ASN1_VALUE, (st)) +#define sk_ASN1_VALUE_num(st) SKM_sk_num(ASN1_VALUE, (st)) +#define sk_ASN1_VALUE_value(st, i) SKM_sk_value(ASN1_VALUE, (st), (i)) +#define sk_ASN1_VALUE_set(st, i, val) SKM_sk_set(ASN1_VALUE, (st), (i), (val)) +#define sk_ASN1_VALUE_zero(st) SKM_sk_zero(ASN1_VALUE, (st)) +#define sk_ASN1_VALUE_push(st, val) SKM_sk_push(ASN1_VALUE, (st), (val)) +#define sk_ASN1_VALUE_unshift(st, val) SKM_sk_unshift(ASN1_VALUE, (st), (val)) +#define sk_ASN1_VALUE_find(st, val) SKM_sk_find(ASN1_VALUE, (st), (val)) +#define sk_ASN1_VALUE_find_ex(st, val) SKM_sk_find_ex(ASN1_VALUE, (st), (val)) +#define sk_ASN1_VALUE_delete(st, i) SKM_sk_delete(ASN1_VALUE, (st), (i)) +#define sk_ASN1_VALUE_delete_ptr(st, ptr) SKM_sk_delete_ptr(ASN1_VALUE, (st), (ptr)) +#define sk_ASN1_VALUE_insert(st, val, i) SKM_sk_insert(ASN1_VALUE, (st), (val), (i)) +#define sk_ASN1_VALUE_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(ASN1_VALUE, (st), (cmp)) +#define sk_ASN1_VALUE_dup(st) SKM_sk_dup(ASN1_VALUE, st) +#define sk_ASN1_VALUE_pop_free(st, free_func) SKM_sk_pop_free(ASN1_VALUE, (st), (free_func)) +#define sk_ASN1_VALUE_shift(st) SKM_sk_shift(ASN1_VALUE, (st)) +#define sk_ASN1_VALUE_pop(st) SKM_sk_pop(ASN1_VALUE, (st)) +#define sk_ASN1_VALUE_sort(st) SKM_sk_sort(ASN1_VALUE, (st)) +#define sk_ASN1_VALUE_is_sorted(st) SKM_sk_is_sorted(ASN1_VALUE, (st)) + +#define sk_BIO_new(st) SKM_sk_new(BIO, (st)) +#define sk_BIO_new_null() SKM_sk_new_null(BIO) +#define sk_BIO_free(st) SKM_sk_free(BIO, (st)) +#define sk_BIO_num(st) SKM_sk_num(BIO, (st)) +#define sk_BIO_value(st, i) SKM_sk_value(BIO, (st), (i)) +#define sk_BIO_set(st, i, val) SKM_sk_set(BIO, (st), (i), (val)) +#define sk_BIO_zero(st) SKM_sk_zero(BIO, (st)) +#define sk_BIO_push(st, val) SKM_sk_push(BIO, (st), (val)) +#define sk_BIO_unshift(st, val) SKM_sk_unshift(BIO, (st), (val)) +#define sk_BIO_find(st, val) SKM_sk_find(BIO, (st), (val)) +#define sk_BIO_find_ex(st, val) SKM_sk_find_ex(BIO, (st), (val)) +#define sk_BIO_delete(st, i) SKM_sk_delete(BIO, (st), (i)) +#define sk_BIO_delete_ptr(st, ptr) SKM_sk_delete_ptr(BIO, (st), (ptr)) +#define sk_BIO_insert(st, val, i) SKM_sk_insert(BIO, (st), (val), (i)) +#define sk_BIO_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(BIO, (st), (cmp)) +#define sk_BIO_dup(st) SKM_sk_dup(BIO, st) +#define sk_BIO_pop_free(st, free_func) SKM_sk_pop_free(BIO, (st), (free_func)) +#define sk_BIO_shift(st) SKM_sk_shift(BIO, (st)) +#define sk_BIO_pop(st) SKM_sk_pop(BIO, (st)) +#define sk_BIO_sort(st) SKM_sk_sort(BIO, (st)) +#define sk_BIO_is_sorted(st) SKM_sk_is_sorted(BIO, (st)) + +#define sk_CONF_IMODULE_new(st) SKM_sk_new(CONF_IMODULE, (st)) +#define sk_CONF_IMODULE_new_null() SKM_sk_new_null(CONF_IMODULE) +#define sk_CONF_IMODULE_free(st) SKM_sk_free(CONF_IMODULE, (st)) +#define sk_CONF_IMODULE_num(st) SKM_sk_num(CONF_IMODULE, (st)) +#define sk_CONF_IMODULE_value(st, i) SKM_sk_value(CONF_IMODULE, (st), (i)) +#define sk_CONF_IMODULE_set(st, i, val) SKM_sk_set(CONF_IMODULE, (st), (i), (val)) +#define sk_CONF_IMODULE_zero(st) SKM_sk_zero(CONF_IMODULE, (st)) +#define sk_CONF_IMODULE_push(st, val) SKM_sk_push(CONF_IMODULE, (st), (val)) +#define sk_CONF_IMODULE_unshift(st, val) SKM_sk_unshift(CONF_IMODULE, (st), (val)) +#define sk_CONF_IMODULE_find(st, val) SKM_sk_find(CONF_IMODULE, (st), (val)) +#define sk_CONF_IMODULE_find_ex(st, val) SKM_sk_find_ex(CONF_IMODULE, (st), (val)) +#define sk_CONF_IMODULE_delete(st, i) SKM_sk_delete(CONF_IMODULE, (st), (i)) +#define sk_CONF_IMODULE_delete_ptr(st, ptr) SKM_sk_delete_ptr(CONF_IMODULE, (st), (ptr)) +#define sk_CONF_IMODULE_insert(st, val, i) SKM_sk_insert(CONF_IMODULE, (st), (val), (i)) +#define sk_CONF_IMODULE_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(CONF_IMODULE, (st), (cmp)) +#define sk_CONF_IMODULE_dup(st) SKM_sk_dup(CONF_IMODULE, st) +#define sk_CONF_IMODULE_pop_free(st, free_func) SKM_sk_pop_free(CONF_IMODULE, (st), (free_func)) +#define sk_CONF_IMODULE_shift(st) SKM_sk_shift(CONF_IMODULE, (st)) +#define sk_CONF_IMODULE_pop(st) SKM_sk_pop(CONF_IMODULE, (st)) +#define sk_CONF_IMODULE_sort(st) SKM_sk_sort(CONF_IMODULE, (st)) +#define sk_CONF_IMODULE_is_sorted(st) SKM_sk_is_sorted(CONF_IMODULE, (st)) + +#define sk_CONF_MODULE_new(st) SKM_sk_new(CONF_MODULE, (st)) +#define sk_CONF_MODULE_new_null() SKM_sk_new_null(CONF_MODULE) +#define sk_CONF_MODULE_free(st) SKM_sk_free(CONF_MODULE, (st)) +#define sk_CONF_MODULE_num(st) SKM_sk_num(CONF_MODULE, (st)) +#define sk_CONF_MODULE_value(st, i) SKM_sk_value(CONF_MODULE, (st), (i)) +#define sk_CONF_MODULE_set(st, i, val) SKM_sk_set(CONF_MODULE, (st), (i), (val)) +#define sk_CONF_MODULE_zero(st) SKM_sk_zero(CONF_MODULE, (st)) +#define sk_CONF_MODULE_push(st, val) SKM_sk_push(CONF_MODULE, (st), (val)) +#define sk_CONF_MODULE_unshift(st, val) SKM_sk_unshift(CONF_MODULE, (st), (val)) +#define sk_CONF_MODULE_find(st, val) SKM_sk_find(CONF_MODULE, (st), (val)) +#define sk_CONF_MODULE_find_ex(st, val) SKM_sk_find_ex(CONF_MODULE, (st), (val)) +#define sk_CONF_MODULE_delete(st, i) SKM_sk_delete(CONF_MODULE, (st), (i)) +#define sk_CONF_MODULE_delete_ptr(st, ptr) SKM_sk_delete_ptr(CONF_MODULE, (st), (ptr)) +#define sk_CONF_MODULE_insert(st, val, i) SKM_sk_insert(CONF_MODULE, (st), (val), (i)) +#define sk_CONF_MODULE_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(CONF_MODULE, (st), (cmp)) +#define sk_CONF_MODULE_dup(st) SKM_sk_dup(CONF_MODULE, st) +#define sk_CONF_MODULE_pop_free(st, free_func) SKM_sk_pop_free(CONF_MODULE, (st), (free_func)) +#define sk_CONF_MODULE_shift(st) SKM_sk_shift(CONF_MODULE, (st)) +#define sk_CONF_MODULE_pop(st) SKM_sk_pop(CONF_MODULE, (st)) +#define sk_CONF_MODULE_sort(st) SKM_sk_sort(CONF_MODULE, (st)) +#define sk_CONF_MODULE_is_sorted(st) SKM_sk_is_sorted(CONF_MODULE, (st)) + +#define sk_CONF_VALUE_new(st) SKM_sk_new(CONF_VALUE, (st)) +#define sk_CONF_VALUE_new_null() SKM_sk_new_null(CONF_VALUE) +#define sk_CONF_VALUE_free(st) SKM_sk_free(CONF_VALUE, (st)) +#define sk_CONF_VALUE_num(st) SKM_sk_num(CONF_VALUE, (st)) +#define sk_CONF_VALUE_value(st, i) SKM_sk_value(CONF_VALUE, (st), (i)) +#define sk_CONF_VALUE_set(st, i, val) SKM_sk_set(CONF_VALUE, (st), (i), (val)) +#define sk_CONF_VALUE_zero(st) SKM_sk_zero(CONF_VALUE, (st)) +#define sk_CONF_VALUE_push(st, val) SKM_sk_push(CONF_VALUE, (st), (val)) +#define sk_CONF_VALUE_unshift(st, val) SKM_sk_unshift(CONF_VALUE, (st), (val)) +#define sk_CONF_VALUE_find(st, val) SKM_sk_find(CONF_VALUE, (st), (val)) +#define sk_CONF_VALUE_find_ex(st, val) SKM_sk_find_ex(CONF_VALUE, (st), (val)) +#define sk_CONF_VALUE_delete(st, i) SKM_sk_delete(CONF_VALUE, (st), (i)) +#define sk_CONF_VALUE_delete_ptr(st, ptr) SKM_sk_delete_ptr(CONF_VALUE, (st), (ptr)) +#define sk_CONF_VALUE_insert(st, val, i) SKM_sk_insert(CONF_VALUE, (st), (val), (i)) +#define sk_CONF_VALUE_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(CONF_VALUE, (st), (cmp)) +#define sk_CONF_VALUE_dup(st) SKM_sk_dup(CONF_VALUE, st) +#define sk_CONF_VALUE_pop_free(st, free_func) SKM_sk_pop_free(CONF_VALUE, (st), (free_func)) +#define sk_CONF_VALUE_shift(st) SKM_sk_shift(CONF_VALUE, (st)) +#define sk_CONF_VALUE_pop(st) SKM_sk_pop(CONF_VALUE, (st)) +#define sk_CONF_VALUE_sort(st) SKM_sk_sort(CONF_VALUE, (st)) +#define sk_CONF_VALUE_is_sorted(st) SKM_sk_is_sorted(CONF_VALUE, (st)) + +#define sk_CRYPTO_EX_DATA_FUNCS_new(st) SKM_sk_new(CRYPTO_EX_DATA_FUNCS, (st)) +#define sk_CRYPTO_EX_DATA_FUNCS_new_null() SKM_sk_new_null(CRYPTO_EX_DATA_FUNCS) +#define sk_CRYPTO_EX_DATA_FUNCS_free(st) SKM_sk_free(CRYPTO_EX_DATA_FUNCS, (st)) +#define sk_CRYPTO_EX_DATA_FUNCS_num(st) SKM_sk_num(CRYPTO_EX_DATA_FUNCS, (st)) +#define sk_CRYPTO_EX_DATA_FUNCS_value(st, i) SKM_sk_value(CRYPTO_EX_DATA_FUNCS, (st), (i)) +#define sk_CRYPTO_EX_DATA_FUNCS_set(st, i, val) SKM_sk_set(CRYPTO_EX_DATA_FUNCS, (st), (i), (val)) +#define sk_CRYPTO_EX_DATA_FUNCS_zero(st) SKM_sk_zero(CRYPTO_EX_DATA_FUNCS, (st)) +#define sk_CRYPTO_EX_DATA_FUNCS_push(st, val) SKM_sk_push(CRYPTO_EX_DATA_FUNCS, (st), (val)) +#define sk_CRYPTO_EX_DATA_FUNCS_unshift(st, val) SKM_sk_unshift(CRYPTO_EX_DATA_FUNCS, (st), (val)) +#define sk_CRYPTO_EX_DATA_FUNCS_find(st, val) SKM_sk_find(CRYPTO_EX_DATA_FUNCS, (st), (val)) +#define sk_CRYPTO_EX_DATA_FUNCS_find_ex(st, val) SKM_sk_find_ex(CRYPTO_EX_DATA_FUNCS, (st), (val)) +#define sk_CRYPTO_EX_DATA_FUNCS_delete(st, i) SKM_sk_delete(CRYPTO_EX_DATA_FUNCS, (st), (i)) +#define sk_CRYPTO_EX_DATA_FUNCS_delete_ptr(st, ptr) SKM_sk_delete_ptr(CRYPTO_EX_DATA_FUNCS, (st), (ptr)) +#define sk_CRYPTO_EX_DATA_FUNCS_insert(st, val, i) SKM_sk_insert(CRYPTO_EX_DATA_FUNCS, (st), (val), (i)) +#define sk_CRYPTO_EX_DATA_FUNCS_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(CRYPTO_EX_DATA_FUNCS, (st), (cmp)) +#define sk_CRYPTO_EX_DATA_FUNCS_dup(st) SKM_sk_dup(CRYPTO_EX_DATA_FUNCS, st) +#define sk_CRYPTO_EX_DATA_FUNCS_pop_free(st, free_func) SKM_sk_pop_free(CRYPTO_EX_DATA_FUNCS, (st), (free_func)) +#define sk_CRYPTO_EX_DATA_FUNCS_shift(st) SKM_sk_shift(CRYPTO_EX_DATA_FUNCS, (st)) +#define sk_CRYPTO_EX_DATA_FUNCS_pop(st) SKM_sk_pop(CRYPTO_EX_DATA_FUNCS, (st)) +#define sk_CRYPTO_EX_DATA_FUNCS_sort(st) SKM_sk_sort(CRYPTO_EX_DATA_FUNCS, (st)) +#define sk_CRYPTO_EX_DATA_FUNCS_is_sorted(st) SKM_sk_is_sorted(CRYPTO_EX_DATA_FUNCS, (st)) + +#define sk_CRYPTO_dynlock_new(st) SKM_sk_new(CRYPTO_dynlock, (st)) +#define sk_CRYPTO_dynlock_new_null() SKM_sk_new_null(CRYPTO_dynlock) +#define sk_CRYPTO_dynlock_free(st) SKM_sk_free(CRYPTO_dynlock, (st)) +#define sk_CRYPTO_dynlock_num(st) SKM_sk_num(CRYPTO_dynlock, (st)) +#define sk_CRYPTO_dynlock_value(st, i) SKM_sk_value(CRYPTO_dynlock, (st), (i)) +#define sk_CRYPTO_dynlock_set(st, i, val) SKM_sk_set(CRYPTO_dynlock, (st), (i), (val)) +#define sk_CRYPTO_dynlock_zero(st) SKM_sk_zero(CRYPTO_dynlock, (st)) +#define sk_CRYPTO_dynlock_push(st, val) SKM_sk_push(CRYPTO_dynlock, (st), (val)) +#define sk_CRYPTO_dynlock_unshift(st, val) SKM_sk_unshift(CRYPTO_dynlock, (st), (val)) +#define sk_CRYPTO_dynlock_find(st, val) SKM_sk_find(CRYPTO_dynlock, (st), (val)) +#define sk_CRYPTO_dynlock_find_ex(st, val) SKM_sk_find_ex(CRYPTO_dynlock, (st), (val)) +#define sk_CRYPTO_dynlock_delete(st, i) SKM_sk_delete(CRYPTO_dynlock, (st), (i)) +#define sk_CRYPTO_dynlock_delete_ptr(st, ptr) SKM_sk_delete_ptr(CRYPTO_dynlock, (st), (ptr)) +#define sk_CRYPTO_dynlock_insert(st, val, i) SKM_sk_insert(CRYPTO_dynlock, (st), (val), (i)) +#define sk_CRYPTO_dynlock_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(CRYPTO_dynlock, (st), (cmp)) +#define sk_CRYPTO_dynlock_dup(st) SKM_sk_dup(CRYPTO_dynlock, st) +#define sk_CRYPTO_dynlock_pop_free(st, free_func) SKM_sk_pop_free(CRYPTO_dynlock, (st), (free_func)) +#define sk_CRYPTO_dynlock_shift(st) SKM_sk_shift(CRYPTO_dynlock, (st)) +#define sk_CRYPTO_dynlock_pop(st) SKM_sk_pop(CRYPTO_dynlock, (st)) +#define sk_CRYPTO_dynlock_sort(st) SKM_sk_sort(CRYPTO_dynlock, (st)) +#define sk_CRYPTO_dynlock_is_sorted(st) SKM_sk_is_sorted(CRYPTO_dynlock, (st)) + +#define sk_DIST_POINT_new(st) SKM_sk_new(DIST_POINT, (st)) +#define sk_DIST_POINT_new_null() SKM_sk_new_null(DIST_POINT) +#define sk_DIST_POINT_free(st) SKM_sk_free(DIST_POINT, (st)) +#define sk_DIST_POINT_num(st) SKM_sk_num(DIST_POINT, (st)) +#define sk_DIST_POINT_value(st, i) SKM_sk_value(DIST_POINT, (st), (i)) +#define sk_DIST_POINT_set(st, i, val) SKM_sk_set(DIST_POINT, (st), (i), (val)) +#define sk_DIST_POINT_zero(st) SKM_sk_zero(DIST_POINT, (st)) +#define sk_DIST_POINT_push(st, val) SKM_sk_push(DIST_POINT, (st), (val)) +#define sk_DIST_POINT_unshift(st, val) SKM_sk_unshift(DIST_POINT, (st), (val)) +#define sk_DIST_POINT_find(st, val) SKM_sk_find(DIST_POINT, (st), (val)) +#define sk_DIST_POINT_find_ex(st, val) SKM_sk_find_ex(DIST_POINT, (st), (val)) +#define sk_DIST_POINT_delete(st, i) SKM_sk_delete(DIST_POINT, (st), (i)) +#define sk_DIST_POINT_delete_ptr(st, ptr) SKM_sk_delete_ptr(DIST_POINT, (st), (ptr)) +#define sk_DIST_POINT_insert(st, val, i) SKM_sk_insert(DIST_POINT, (st), (val), (i)) +#define sk_DIST_POINT_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(DIST_POINT, (st), (cmp)) +#define sk_DIST_POINT_dup(st) SKM_sk_dup(DIST_POINT, st) +#define sk_DIST_POINT_pop_free(st, free_func) SKM_sk_pop_free(DIST_POINT, (st), (free_func)) +#define sk_DIST_POINT_shift(st) SKM_sk_shift(DIST_POINT, (st)) +#define sk_DIST_POINT_pop(st) SKM_sk_pop(DIST_POINT, (st)) +#define sk_DIST_POINT_sort(st) SKM_sk_sort(DIST_POINT, (st)) +#define sk_DIST_POINT_is_sorted(st) SKM_sk_is_sorted(DIST_POINT, (st)) + +#define sk_ENGINE_new(st) SKM_sk_new(ENGINE, (st)) +#define sk_ENGINE_new_null() SKM_sk_new_null(ENGINE) +#define sk_ENGINE_free(st) SKM_sk_free(ENGINE, (st)) +#define sk_ENGINE_num(st) SKM_sk_num(ENGINE, (st)) +#define sk_ENGINE_value(st, i) SKM_sk_value(ENGINE, (st), (i)) +#define sk_ENGINE_set(st, i, val) SKM_sk_set(ENGINE, (st), (i), (val)) +#define sk_ENGINE_zero(st) SKM_sk_zero(ENGINE, (st)) +#define sk_ENGINE_push(st, val) SKM_sk_push(ENGINE, (st), (val)) +#define sk_ENGINE_unshift(st, val) SKM_sk_unshift(ENGINE, (st), (val)) +#define sk_ENGINE_find(st, val) SKM_sk_find(ENGINE, (st), (val)) +#define sk_ENGINE_find_ex(st, val) SKM_sk_find_ex(ENGINE, (st), (val)) +#define sk_ENGINE_delete(st, i) SKM_sk_delete(ENGINE, (st), (i)) +#define sk_ENGINE_delete_ptr(st, ptr) SKM_sk_delete_ptr(ENGINE, (st), (ptr)) +#define sk_ENGINE_insert(st, val, i) SKM_sk_insert(ENGINE, (st), (val), (i)) +#define sk_ENGINE_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(ENGINE, (st), (cmp)) +#define sk_ENGINE_dup(st) SKM_sk_dup(ENGINE, st) +#define sk_ENGINE_pop_free(st, free_func) SKM_sk_pop_free(ENGINE, (st), (free_func)) +#define sk_ENGINE_shift(st) SKM_sk_shift(ENGINE, (st)) +#define sk_ENGINE_pop(st) SKM_sk_pop(ENGINE, (st)) +#define sk_ENGINE_sort(st) SKM_sk_sort(ENGINE, (st)) +#define sk_ENGINE_is_sorted(st) SKM_sk_is_sorted(ENGINE, (st)) + +#define sk_ENGINE_CLEANUP_ITEM_new(st) SKM_sk_new(ENGINE_CLEANUP_ITEM, (st)) +#define sk_ENGINE_CLEANUP_ITEM_new_null() SKM_sk_new_null(ENGINE_CLEANUP_ITEM) +#define sk_ENGINE_CLEANUP_ITEM_free(st) SKM_sk_free(ENGINE_CLEANUP_ITEM, (st)) +#define sk_ENGINE_CLEANUP_ITEM_num(st) SKM_sk_num(ENGINE_CLEANUP_ITEM, (st)) +#define sk_ENGINE_CLEANUP_ITEM_value(st, i) SKM_sk_value(ENGINE_CLEANUP_ITEM, (st), (i)) +#define sk_ENGINE_CLEANUP_ITEM_set(st, i, val) SKM_sk_set(ENGINE_CLEANUP_ITEM, (st), (i), (val)) +#define sk_ENGINE_CLEANUP_ITEM_zero(st) SKM_sk_zero(ENGINE_CLEANUP_ITEM, (st)) +#define sk_ENGINE_CLEANUP_ITEM_push(st, val) SKM_sk_push(ENGINE_CLEANUP_ITEM, (st), (val)) +#define sk_ENGINE_CLEANUP_ITEM_unshift(st, val) SKM_sk_unshift(ENGINE_CLEANUP_ITEM, (st), (val)) +#define sk_ENGINE_CLEANUP_ITEM_find(st, val) SKM_sk_find(ENGINE_CLEANUP_ITEM, (st), (val)) +#define sk_ENGINE_CLEANUP_ITEM_find_ex(st, val) SKM_sk_find_ex(ENGINE_CLEANUP_ITEM, (st), (val)) +#define sk_ENGINE_CLEANUP_ITEM_delete(st, i) SKM_sk_delete(ENGINE_CLEANUP_ITEM, (st), (i)) +#define sk_ENGINE_CLEANUP_ITEM_delete_ptr(st, ptr) SKM_sk_delete_ptr(ENGINE_CLEANUP_ITEM, (st), (ptr)) +#define sk_ENGINE_CLEANUP_ITEM_insert(st, val, i) SKM_sk_insert(ENGINE_CLEANUP_ITEM, (st), (val), (i)) +#define sk_ENGINE_CLEANUP_ITEM_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(ENGINE_CLEANUP_ITEM, (st), (cmp)) +#define sk_ENGINE_CLEANUP_ITEM_dup(st) SKM_sk_dup(ENGINE_CLEANUP_ITEM, st) +#define sk_ENGINE_CLEANUP_ITEM_pop_free(st, free_func) SKM_sk_pop_free(ENGINE_CLEANUP_ITEM, (st), (free_func)) +#define sk_ENGINE_CLEANUP_ITEM_shift(st) SKM_sk_shift(ENGINE_CLEANUP_ITEM, (st)) +#define sk_ENGINE_CLEANUP_ITEM_pop(st) SKM_sk_pop(ENGINE_CLEANUP_ITEM, (st)) +#define sk_ENGINE_CLEANUP_ITEM_sort(st) SKM_sk_sort(ENGINE_CLEANUP_ITEM, (st)) +#define sk_ENGINE_CLEANUP_ITEM_is_sorted(st) SKM_sk_is_sorted(ENGINE_CLEANUP_ITEM, (st)) + +#define sk_GENERAL_NAME_new(st) SKM_sk_new(GENERAL_NAME, (st)) +#define sk_GENERAL_NAME_new_null() SKM_sk_new_null(GENERAL_NAME) +#define sk_GENERAL_NAME_free(st) SKM_sk_free(GENERAL_NAME, (st)) +#define sk_GENERAL_NAME_num(st) SKM_sk_num(GENERAL_NAME, (st)) +#define sk_GENERAL_NAME_value(st, i) SKM_sk_value(GENERAL_NAME, (st), (i)) +#define sk_GENERAL_NAME_set(st, i, val) SKM_sk_set(GENERAL_NAME, (st), (i), (val)) +#define sk_GENERAL_NAME_zero(st) SKM_sk_zero(GENERAL_NAME, (st)) +#define sk_GENERAL_NAME_push(st, val) SKM_sk_push(GENERAL_NAME, (st), (val)) +#define sk_GENERAL_NAME_unshift(st, val) SKM_sk_unshift(GENERAL_NAME, (st), (val)) +#define sk_GENERAL_NAME_find(st, val) SKM_sk_find(GENERAL_NAME, (st), (val)) +#define sk_GENERAL_NAME_find_ex(st, val) SKM_sk_find_ex(GENERAL_NAME, (st), (val)) +#define sk_GENERAL_NAME_delete(st, i) SKM_sk_delete(GENERAL_NAME, (st), (i)) +#define sk_GENERAL_NAME_delete_ptr(st, ptr) SKM_sk_delete_ptr(GENERAL_NAME, (st), (ptr)) +#define sk_GENERAL_NAME_insert(st, val, i) SKM_sk_insert(GENERAL_NAME, (st), (val), (i)) +#define sk_GENERAL_NAME_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(GENERAL_NAME, (st), (cmp)) +#define sk_GENERAL_NAME_dup(st) SKM_sk_dup(GENERAL_NAME, st) +#define sk_GENERAL_NAME_pop_free(st, free_func) SKM_sk_pop_free(GENERAL_NAME, (st), (free_func)) +#define sk_GENERAL_NAME_shift(st) SKM_sk_shift(GENERAL_NAME, (st)) +#define sk_GENERAL_NAME_pop(st) SKM_sk_pop(GENERAL_NAME, (st)) +#define sk_GENERAL_NAME_sort(st) SKM_sk_sort(GENERAL_NAME, (st)) +#define sk_GENERAL_NAME_is_sorted(st) SKM_sk_is_sorted(GENERAL_NAME, (st)) + +#define sk_GENERAL_SUBTREE_new(st) SKM_sk_new(GENERAL_SUBTREE, (st)) +#define sk_GENERAL_SUBTREE_new_null() SKM_sk_new_null(GENERAL_SUBTREE) +#define sk_GENERAL_SUBTREE_free(st) SKM_sk_free(GENERAL_SUBTREE, (st)) +#define sk_GENERAL_SUBTREE_num(st) SKM_sk_num(GENERAL_SUBTREE, (st)) +#define sk_GENERAL_SUBTREE_value(st, i) SKM_sk_value(GENERAL_SUBTREE, (st), (i)) +#define sk_GENERAL_SUBTREE_set(st, i, val) SKM_sk_set(GENERAL_SUBTREE, (st), (i), (val)) +#define sk_GENERAL_SUBTREE_zero(st) SKM_sk_zero(GENERAL_SUBTREE, (st)) +#define sk_GENERAL_SUBTREE_push(st, val) SKM_sk_push(GENERAL_SUBTREE, (st), (val)) +#define sk_GENERAL_SUBTREE_unshift(st, val) SKM_sk_unshift(GENERAL_SUBTREE, (st), (val)) +#define sk_GENERAL_SUBTREE_find(st, val) SKM_sk_find(GENERAL_SUBTREE, (st), (val)) +#define sk_GENERAL_SUBTREE_find_ex(st, val) SKM_sk_find_ex(GENERAL_SUBTREE, (st), (val)) +#define sk_GENERAL_SUBTREE_delete(st, i) SKM_sk_delete(GENERAL_SUBTREE, (st), (i)) +#define sk_GENERAL_SUBTREE_delete_ptr(st, ptr) SKM_sk_delete_ptr(GENERAL_SUBTREE, (st), (ptr)) +#define sk_GENERAL_SUBTREE_insert(st, val, i) SKM_sk_insert(GENERAL_SUBTREE, (st), (val), (i)) +#define sk_GENERAL_SUBTREE_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(GENERAL_SUBTREE, (st), (cmp)) +#define sk_GENERAL_SUBTREE_dup(st) SKM_sk_dup(GENERAL_SUBTREE, st) +#define sk_GENERAL_SUBTREE_pop_free(st, free_func) SKM_sk_pop_free(GENERAL_SUBTREE, (st), (free_func)) +#define sk_GENERAL_SUBTREE_shift(st) SKM_sk_shift(GENERAL_SUBTREE, (st)) +#define sk_GENERAL_SUBTREE_pop(st) SKM_sk_pop(GENERAL_SUBTREE, (st)) +#define sk_GENERAL_SUBTREE_sort(st) SKM_sk_sort(GENERAL_SUBTREE, (st)) +#define sk_GENERAL_SUBTREE_is_sorted(st) SKM_sk_is_sorted(GENERAL_SUBTREE, (st)) + +#define sk_KRB5_APREQBODY_new(st) SKM_sk_new(KRB5_APREQBODY, (st)) +#define sk_KRB5_APREQBODY_new_null() SKM_sk_new_null(KRB5_APREQBODY) +#define sk_KRB5_APREQBODY_free(st) SKM_sk_free(KRB5_APREQBODY, (st)) +#define sk_KRB5_APREQBODY_num(st) SKM_sk_num(KRB5_APREQBODY, (st)) +#define sk_KRB5_APREQBODY_value(st, i) SKM_sk_value(KRB5_APREQBODY, (st), (i)) +#define sk_KRB5_APREQBODY_set(st, i, val) SKM_sk_set(KRB5_APREQBODY, (st), (i), (val)) +#define sk_KRB5_APREQBODY_zero(st) SKM_sk_zero(KRB5_APREQBODY, (st)) +#define sk_KRB5_APREQBODY_push(st, val) SKM_sk_push(KRB5_APREQBODY, (st), (val)) +#define sk_KRB5_APREQBODY_unshift(st, val) SKM_sk_unshift(KRB5_APREQBODY, (st), (val)) +#define sk_KRB5_APREQBODY_find(st, val) SKM_sk_find(KRB5_APREQBODY, (st), (val)) +#define sk_KRB5_APREQBODY_find_ex(st, val) SKM_sk_find_ex(KRB5_APREQBODY, (st), (val)) +#define sk_KRB5_APREQBODY_delete(st, i) SKM_sk_delete(KRB5_APREQBODY, (st), (i)) +#define sk_KRB5_APREQBODY_delete_ptr(st, ptr) SKM_sk_delete_ptr(KRB5_APREQBODY, (st), (ptr)) +#define sk_KRB5_APREQBODY_insert(st, val, i) SKM_sk_insert(KRB5_APREQBODY, (st), (val), (i)) +#define sk_KRB5_APREQBODY_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(KRB5_APREQBODY, (st), (cmp)) +#define sk_KRB5_APREQBODY_dup(st) SKM_sk_dup(KRB5_APREQBODY, st) +#define sk_KRB5_APREQBODY_pop_free(st, free_func) SKM_sk_pop_free(KRB5_APREQBODY, (st), (free_func)) +#define sk_KRB5_APREQBODY_shift(st) SKM_sk_shift(KRB5_APREQBODY, (st)) +#define sk_KRB5_APREQBODY_pop(st) SKM_sk_pop(KRB5_APREQBODY, (st)) +#define sk_KRB5_APREQBODY_sort(st) SKM_sk_sort(KRB5_APREQBODY, (st)) +#define sk_KRB5_APREQBODY_is_sorted(st) SKM_sk_is_sorted(KRB5_APREQBODY, (st)) + +#define sk_KRB5_AUTHDATA_new(st) SKM_sk_new(KRB5_AUTHDATA, (st)) +#define sk_KRB5_AUTHDATA_new_null() SKM_sk_new_null(KRB5_AUTHDATA) +#define sk_KRB5_AUTHDATA_free(st) SKM_sk_free(KRB5_AUTHDATA, (st)) +#define sk_KRB5_AUTHDATA_num(st) SKM_sk_num(KRB5_AUTHDATA, (st)) +#define sk_KRB5_AUTHDATA_value(st, i) SKM_sk_value(KRB5_AUTHDATA, (st), (i)) +#define sk_KRB5_AUTHDATA_set(st, i, val) SKM_sk_set(KRB5_AUTHDATA, (st), (i), (val)) +#define sk_KRB5_AUTHDATA_zero(st) SKM_sk_zero(KRB5_AUTHDATA, (st)) +#define sk_KRB5_AUTHDATA_push(st, val) SKM_sk_push(KRB5_AUTHDATA, (st), (val)) +#define sk_KRB5_AUTHDATA_unshift(st, val) SKM_sk_unshift(KRB5_AUTHDATA, (st), (val)) +#define sk_KRB5_AUTHDATA_find(st, val) SKM_sk_find(KRB5_AUTHDATA, (st), (val)) +#define sk_KRB5_AUTHDATA_find_ex(st, val) SKM_sk_find_ex(KRB5_AUTHDATA, (st), (val)) +#define sk_KRB5_AUTHDATA_delete(st, i) SKM_sk_delete(KRB5_AUTHDATA, (st), (i)) +#define sk_KRB5_AUTHDATA_delete_ptr(st, ptr) SKM_sk_delete_ptr(KRB5_AUTHDATA, (st), (ptr)) +#define sk_KRB5_AUTHDATA_insert(st, val, i) SKM_sk_insert(KRB5_AUTHDATA, (st), (val), (i)) +#define sk_KRB5_AUTHDATA_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(KRB5_AUTHDATA, (st), (cmp)) +#define sk_KRB5_AUTHDATA_dup(st) SKM_sk_dup(KRB5_AUTHDATA, st) +#define sk_KRB5_AUTHDATA_pop_free(st, free_func) SKM_sk_pop_free(KRB5_AUTHDATA, (st), (free_func)) +#define sk_KRB5_AUTHDATA_shift(st) SKM_sk_shift(KRB5_AUTHDATA, (st)) +#define sk_KRB5_AUTHDATA_pop(st) SKM_sk_pop(KRB5_AUTHDATA, (st)) +#define sk_KRB5_AUTHDATA_sort(st) SKM_sk_sort(KRB5_AUTHDATA, (st)) +#define sk_KRB5_AUTHDATA_is_sorted(st) SKM_sk_is_sorted(KRB5_AUTHDATA, (st)) + +#define sk_KRB5_AUTHENTBODY_new(st) SKM_sk_new(KRB5_AUTHENTBODY, (st)) +#define sk_KRB5_AUTHENTBODY_new_null() SKM_sk_new_null(KRB5_AUTHENTBODY) +#define sk_KRB5_AUTHENTBODY_free(st) SKM_sk_free(KRB5_AUTHENTBODY, (st)) +#define sk_KRB5_AUTHENTBODY_num(st) SKM_sk_num(KRB5_AUTHENTBODY, (st)) +#define sk_KRB5_AUTHENTBODY_value(st, i) SKM_sk_value(KRB5_AUTHENTBODY, (st), (i)) +#define sk_KRB5_AUTHENTBODY_set(st, i, val) SKM_sk_set(KRB5_AUTHENTBODY, (st), (i), (val)) +#define sk_KRB5_AUTHENTBODY_zero(st) SKM_sk_zero(KRB5_AUTHENTBODY, (st)) +#define sk_KRB5_AUTHENTBODY_push(st, val) SKM_sk_push(KRB5_AUTHENTBODY, (st), (val)) +#define sk_KRB5_AUTHENTBODY_unshift(st, val) SKM_sk_unshift(KRB5_AUTHENTBODY, (st), (val)) +#define sk_KRB5_AUTHENTBODY_find(st, val) SKM_sk_find(KRB5_AUTHENTBODY, (st), (val)) +#define sk_KRB5_AUTHENTBODY_find_ex(st, val) SKM_sk_find_ex(KRB5_AUTHENTBODY, (st), (val)) +#define sk_KRB5_AUTHENTBODY_delete(st, i) SKM_sk_delete(KRB5_AUTHENTBODY, (st), (i)) +#define sk_KRB5_AUTHENTBODY_delete_ptr(st, ptr) SKM_sk_delete_ptr(KRB5_AUTHENTBODY, (st), (ptr)) +#define sk_KRB5_AUTHENTBODY_insert(st, val, i) SKM_sk_insert(KRB5_AUTHENTBODY, (st), (val), (i)) +#define sk_KRB5_AUTHENTBODY_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(KRB5_AUTHENTBODY, (st), (cmp)) +#define sk_KRB5_AUTHENTBODY_dup(st) SKM_sk_dup(KRB5_AUTHENTBODY, st) +#define sk_KRB5_AUTHENTBODY_pop_free(st, free_func) SKM_sk_pop_free(KRB5_AUTHENTBODY, (st), (free_func)) +#define sk_KRB5_AUTHENTBODY_shift(st) SKM_sk_shift(KRB5_AUTHENTBODY, (st)) +#define sk_KRB5_AUTHENTBODY_pop(st) SKM_sk_pop(KRB5_AUTHENTBODY, (st)) +#define sk_KRB5_AUTHENTBODY_sort(st) SKM_sk_sort(KRB5_AUTHENTBODY, (st)) +#define sk_KRB5_AUTHENTBODY_is_sorted(st) SKM_sk_is_sorted(KRB5_AUTHENTBODY, (st)) + +#define sk_KRB5_CHECKSUM_new(st) SKM_sk_new(KRB5_CHECKSUM, (st)) +#define sk_KRB5_CHECKSUM_new_null() SKM_sk_new_null(KRB5_CHECKSUM) +#define sk_KRB5_CHECKSUM_free(st) SKM_sk_free(KRB5_CHECKSUM, (st)) +#define sk_KRB5_CHECKSUM_num(st) SKM_sk_num(KRB5_CHECKSUM, (st)) +#define sk_KRB5_CHECKSUM_value(st, i) SKM_sk_value(KRB5_CHECKSUM, (st), (i)) +#define sk_KRB5_CHECKSUM_set(st, i, val) SKM_sk_set(KRB5_CHECKSUM, (st), (i), (val)) +#define sk_KRB5_CHECKSUM_zero(st) SKM_sk_zero(KRB5_CHECKSUM, (st)) +#define sk_KRB5_CHECKSUM_push(st, val) SKM_sk_push(KRB5_CHECKSUM, (st), (val)) +#define sk_KRB5_CHECKSUM_unshift(st, val) SKM_sk_unshift(KRB5_CHECKSUM, (st), (val)) +#define sk_KRB5_CHECKSUM_find(st, val) SKM_sk_find(KRB5_CHECKSUM, (st), (val)) +#define sk_KRB5_CHECKSUM_find_ex(st, val) SKM_sk_find_ex(KRB5_CHECKSUM, (st), (val)) +#define sk_KRB5_CHECKSUM_delete(st, i) SKM_sk_delete(KRB5_CHECKSUM, (st), (i)) +#define sk_KRB5_CHECKSUM_delete_ptr(st, ptr) SKM_sk_delete_ptr(KRB5_CHECKSUM, (st), (ptr)) +#define sk_KRB5_CHECKSUM_insert(st, val, i) SKM_sk_insert(KRB5_CHECKSUM, (st), (val), (i)) +#define sk_KRB5_CHECKSUM_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(KRB5_CHECKSUM, (st), (cmp)) +#define sk_KRB5_CHECKSUM_dup(st) SKM_sk_dup(KRB5_CHECKSUM, st) +#define sk_KRB5_CHECKSUM_pop_free(st, free_func) SKM_sk_pop_free(KRB5_CHECKSUM, (st), (free_func)) +#define sk_KRB5_CHECKSUM_shift(st) SKM_sk_shift(KRB5_CHECKSUM, (st)) +#define sk_KRB5_CHECKSUM_pop(st) SKM_sk_pop(KRB5_CHECKSUM, (st)) +#define sk_KRB5_CHECKSUM_sort(st) SKM_sk_sort(KRB5_CHECKSUM, (st)) +#define sk_KRB5_CHECKSUM_is_sorted(st) SKM_sk_is_sorted(KRB5_CHECKSUM, (st)) + +#define sk_KRB5_ENCDATA_new(st) SKM_sk_new(KRB5_ENCDATA, (st)) +#define sk_KRB5_ENCDATA_new_null() SKM_sk_new_null(KRB5_ENCDATA) +#define sk_KRB5_ENCDATA_free(st) SKM_sk_free(KRB5_ENCDATA, (st)) +#define sk_KRB5_ENCDATA_num(st) SKM_sk_num(KRB5_ENCDATA, (st)) +#define sk_KRB5_ENCDATA_value(st, i) SKM_sk_value(KRB5_ENCDATA, (st), (i)) +#define sk_KRB5_ENCDATA_set(st, i, val) SKM_sk_set(KRB5_ENCDATA, (st), (i), (val)) +#define sk_KRB5_ENCDATA_zero(st) SKM_sk_zero(KRB5_ENCDATA, (st)) +#define sk_KRB5_ENCDATA_push(st, val) SKM_sk_push(KRB5_ENCDATA, (st), (val)) +#define sk_KRB5_ENCDATA_unshift(st, val) SKM_sk_unshift(KRB5_ENCDATA, (st), (val)) +#define sk_KRB5_ENCDATA_find(st, val) SKM_sk_find(KRB5_ENCDATA, (st), (val)) +#define sk_KRB5_ENCDATA_find_ex(st, val) SKM_sk_find_ex(KRB5_ENCDATA, (st), (val)) +#define sk_KRB5_ENCDATA_delete(st, i) SKM_sk_delete(KRB5_ENCDATA, (st), (i)) +#define sk_KRB5_ENCDATA_delete_ptr(st, ptr) SKM_sk_delete_ptr(KRB5_ENCDATA, (st), (ptr)) +#define sk_KRB5_ENCDATA_insert(st, val, i) SKM_sk_insert(KRB5_ENCDATA, (st), (val), (i)) +#define sk_KRB5_ENCDATA_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(KRB5_ENCDATA, (st), (cmp)) +#define sk_KRB5_ENCDATA_dup(st) SKM_sk_dup(KRB5_ENCDATA, st) +#define sk_KRB5_ENCDATA_pop_free(st, free_func) SKM_sk_pop_free(KRB5_ENCDATA, (st), (free_func)) +#define sk_KRB5_ENCDATA_shift(st) SKM_sk_shift(KRB5_ENCDATA, (st)) +#define sk_KRB5_ENCDATA_pop(st) SKM_sk_pop(KRB5_ENCDATA, (st)) +#define sk_KRB5_ENCDATA_sort(st) SKM_sk_sort(KRB5_ENCDATA, (st)) +#define sk_KRB5_ENCDATA_is_sorted(st) SKM_sk_is_sorted(KRB5_ENCDATA, (st)) + +#define sk_KRB5_ENCKEY_new(st) SKM_sk_new(KRB5_ENCKEY, (st)) +#define sk_KRB5_ENCKEY_new_null() SKM_sk_new_null(KRB5_ENCKEY) +#define sk_KRB5_ENCKEY_free(st) SKM_sk_free(KRB5_ENCKEY, (st)) +#define sk_KRB5_ENCKEY_num(st) SKM_sk_num(KRB5_ENCKEY, (st)) +#define sk_KRB5_ENCKEY_value(st, i) SKM_sk_value(KRB5_ENCKEY, (st), (i)) +#define sk_KRB5_ENCKEY_set(st, i, val) SKM_sk_set(KRB5_ENCKEY, (st), (i), (val)) +#define sk_KRB5_ENCKEY_zero(st) SKM_sk_zero(KRB5_ENCKEY, (st)) +#define sk_KRB5_ENCKEY_push(st, val) SKM_sk_push(KRB5_ENCKEY, (st), (val)) +#define sk_KRB5_ENCKEY_unshift(st, val) SKM_sk_unshift(KRB5_ENCKEY, (st), (val)) +#define sk_KRB5_ENCKEY_find(st, val) SKM_sk_find(KRB5_ENCKEY, (st), (val)) +#define sk_KRB5_ENCKEY_find_ex(st, val) SKM_sk_find_ex(KRB5_ENCKEY, (st), (val)) +#define sk_KRB5_ENCKEY_delete(st, i) SKM_sk_delete(KRB5_ENCKEY, (st), (i)) +#define sk_KRB5_ENCKEY_delete_ptr(st, ptr) SKM_sk_delete_ptr(KRB5_ENCKEY, (st), (ptr)) +#define sk_KRB5_ENCKEY_insert(st, val, i) SKM_sk_insert(KRB5_ENCKEY, (st), (val), (i)) +#define sk_KRB5_ENCKEY_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(KRB5_ENCKEY, (st), (cmp)) +#define sk_KRB5_ENCKEY_dup(st) SKM_sk_dup(KRB5_ENCKEY, st) +#define sk_KRB5_ENCKEY_pop_free(st, free_func) SKM_sk_pop_free(KRB5_ENCKEY, (st), (free_func)) +#define sk_KRB5_ENCKEY_shift(st) SKM_sk_shift(KRB5_ENCKEY, (st)) +#define sk_KRB5_ENCKEY_pop(st) SKM_sk_pop(KRB5_ENCKEY, (st)) +#define sk_KRB5_ENCKEY_sort(st) SKM_sk_sort(KRB5_ENCKEY, (st)) +#define sk_KRB5_ENCKEY_is_sorted(st) SKM_sk_is_sorted(KRB5_ENCKEY, (st)) + +#define sk_KRB5_PRINCNAME_new(st) SKM_sk_new(KRB5_PRINCNAME, (st)) +#define sk_KRB5_PRINCNAME_new_null() SKM_sk_new_null(KRB5_PRINCNAME) +#define sk_KRB5_PRINCNAME_free(st) SKM_sk_free(KRB5_PRINCNAME, (st)) +#define sk_KRB5_PRINCNAME_num(st) SKM_sk_num(KRB5_PRINCNAME, (st)) +#define sk_KRB5_PRINCNAME_value(st, i) SKM_sk_value(KRB5_PRINCNAME, (st), (i)) +#define sk_KRB5_PRINCNAME_set(st, i, val) SKM_sk_set(KRB5_PRINCNAME, (st), (i), (val)) +#define sk_KRB5_PRINCNAME_zero(st) SKM_sk_zero(KRB5_PRINCNAME, (st)) +#define sk_KRB5_PRINCNAME_push(st, val) SKM_sk_push(KRB5_PRINCNAME, (st), (val)) +#define sk_KRB5_PRINCNAME_unshift(st, val) SKM_sk_unshift(KRB5_PRINCNAME, (st), (val)) +#define sk_KRB5_PRINCNAME_find(st, val) SKM_sk_find(KRB5_PRINCNAME, (st), (val)) +#define sk_KRB5_PRINCNAME_find_ex(st, val) SKM_sk_find_ex(KRB5_PRINCNAME, (st), (val)) +#define sk_KRB5_PRINCNAME_delete(st, i) SKM_sk_delete(KRB5_PRINCNAME, (st), (i)) +#define sk_KRB5_PRINCNAME_delete_ptr(st, ptr) SKM_sk_delete_ptr(KRB5_PRINCNAME, (st), (ptr)) +#define sk_KRB5_PRINCNAME_insert(st, val, i) SKM_sk_insert(KRB5_PRINCNAME, (st), (val), (i)) +#define sk_KRB5_PRINCNAME_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(KRB5_PRINCNAME, (st), (cmp)) +#define sk_KRB5_PRINCNAME_dup(st) SKM_sk_dup(KRB5_PRINCNAME, st) +#define sk_KRB5_PRINCNAME_pop_free(st, free_func) SKM_sk_pop_free(KRB5_PRINCNAME, (st), (free_func)) +#define sk_KRB5_PRINCNAME_shift(st) SKM_sk_shift(KRB5_PRINCNAME, (st)) +#define sk_KRB5_PRINCNAME_pop(st) SKM_sk_pop(KRB5_PRINCNAME, (st)) +#define sk_KRB5_PRINCNAME_sort(st) SKM_sk_sort(KRB5_PRINCNAME, (st)) +#define sk_KRB5_PRINCNAME_is_sorted(st) SKM_sk_is_sorted(KRB5_PRINCNAME, (st)) + +#define sk_KRB5_TKTBODY_new(st) SKM_sk_new(KRB5_TKTBODY, (st)) +#define sk_KRB5_TKTBODY_new_null() SKM_sk_new_null(KRB5_TKTBODY) +#define sk_KRB5_TKTBODY_free(st) SKM_sk_free(KRB5_TKTBODY, (st)) +#define sk_KRB5_TKTBODY_num(st) SKM_sk_num(KRB5_TKTBODY, (st)) +#define sk_KRB5_TKTBODY_value(st, i) SKM_sk_value(KRB5_TKTBODY, (st), (i)) +#define sk_KRB5_TKTBODY_set(st, i, val) SKM_sk_set(KRB5_TKTBODY, (st), (i), (val)) +#define sk_KRB5_TKTBODY_zero(st) SKM_sk_zero(KRB5_TKTBODY, (st)) +#define sk_KRB5_TKTBODY_push(st, val) SKM_sk_push(KRB5_TKTBODY, (st), (val)) +#define sk_KRB5_TKTBODY_unshift(st, val) SKM_sk_unshift(KRB5_TKTBODY, (st), (val)) +#define sk_KRB5_TKTBODY_find(st, val) SKM_sk_find(KRB5_TKTBODY, (st), (val)) +#define sk_KRB5_TKTBODY_find_ex(st, val) SKM_sk_find_ex(KRB5_TKTBODY, (st), (val)) +#define sk_KRB5_TKTBODY_delete(st, i) SKM_sk_delete(KRB5_TKTBODY, (st), (i)) +#define sk_KRB5_TKTBODY_delete_ptr(st, ptr) SKM_sk_delete_ptr(KRB5_TKTBODY, (st), (ptr)) +#define sk_KRB5_TKTBODY_insert(st, val, i) SKM_sk_insert(KRB5_TKTBODY, (st), (val), (i)) +#define sk_KRB5_TKTBODY_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(KRB5_TKTBODY, (st), (cmp)) +#define sk_KRB5_TKTBODY_dup(st) SKM_sk_dup(KRB5_TKTBODY, st) +#define sk_KRB5_TKTBODY_pop_free(st, free_func) SKM_sk_pop_free(KRB5_TKTBODY, (st), (free_func)) +#define sk_KRB5_TKTBODY_shift(st) SKM_sk_shift(KRB5_TKTBODY, (st)) +#define sk_KRB5_TKTBODY_pop(st) SKM_sk_pop(KRB5_TKTBODY, (st)) +#define sk_KRB5_TKTBODY_sort(st) SKM_sk_sort(KRB5_TKTBODY, (st)) +#define sk_KRB5_TKTBODY_is_sorted(st) SKM_sk_is_sorted(KRB5_TKTBODY, (st)) + +#define sk_MIME_HEADER_new(st) SKM_sk_new(MIME_HEADER, (st)) +#define sk_MIME_HEADER_new_null() SKM_sk_new_null(MIME_HEADER) +#define sk_MIME_HEADER_free(st) SKM_sk_free(MIME_HEADER, (st)) +#define sk_MIME_HEADER_num(st) SKM_sk_num(MIME_HEADER, (st)) +#define sk_MIME_HEADER_value(st, i) SKM_sk_value(MIME_HEADER, (st), (i)) +#define sk_MIME_HEADER_set(st, i, val) SKM_sk_set(MIME_HEADER, (st), (i), (val)) +#define sk_MIME_HEADER_zero(st) SKM_sk_zero(MIME_HEADER, (st)) +#define sk_MIME_HEADER_push(st, val) SKM_sk_push(MIME_HEADER, (st), (val)) +#define sk_MIME_HEADER_unshift(st, val) SKM_sk_unshift(MIME_HEADER, (st), (val)) +#define sk_MIME_HEADER_find(st, val) SKM_sk_find(MIME_HEADER, (st), (val)) +#define sk_MIME_HEADER_find_ex(st, val) SKM_sk_find_ex(MIME_HEADER, (st), (val)) +#define sk_MIME_HEADER_delete(st, i) SKM_sk_delete(MIME_HEADER, (st), (i)) +#define sk_MIME_HEADER_delete_ptr(st, ptr) SKM_sk_delete_ptr(MIME_HEADER, (st), (ptr)) +#define sk_MIME_HEADER_insert(st, val, i) SKM_sk_insert(MIME_HEADER, (st), (val), (i)) +#define sk_MIME_HEADER_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(MIME_HEADER, (st), (cmp)) +#define sk_MIME_HEADER_dup(st) SKM_sk_dup(MIME_HEADER, st) +#define sk_MIME_HEADER_pop_free(st, free_func) SKM_sk_pop_free(MIME_HEADER, (st), (free_func)) +#define sk_MIME_HEADER_shift(st) SKM_sk_shift(MIME_HEADER, (st)) +#define sk_MIME_HEADER_pop(st) SKM_sk_pop(MIME_HEADER, (st)) +#define sk_MIME_HEADER_sort(st) SKM_sk_sort(MIME_HEADER, (st)) +#define sk_MIME_HEADER_is_sorted(st) SKM_sk_is_sorted(MIME_HEADER, (st)) + +#define sk_MIME_PARAM_new(st) SKM_sk_new(MIME_PARAM, (st)) +#define sk_MIME_PARAM_new_null() SKM_sk_new_null(MIME_PARAM) +#define sk_MIME_PARAM_free(st) SKM_sk_free(MIME_PARAM, (st)) +#define sk_MIME_PARAM_num(st) SKM_sk_num(MIME_PARAM, (st)) +#define sk_MIME_PARAM_value(st, i) SKM_sk_value(MIME_PARAM, (st), (i)) +#define sk_MIME_PARAM_set(st, i, val) SKM_sk_set(MIME_PARAM, (st), (i), (val)) +#define sk_MIME_PARAM_zero(st) SKM_sk_zero(MIME_PARAM, (st)) +#define sk_MIME_PARAM_push(st, val) SKM_sk_push(MIME_PARAM, (st), (val)) +#define sk_MIME_PARAM_unshift(st, val) SKM_sk_unshift(MIME_PARAM, (st), (val)) +#define sk_MIME_PARAM_find(st, val) SKM_sk_find(MIME_PARAM, (st), (val)) +#define sk_MIME_PARAM_find_ex(st, val) SKM_sk_find_ex(MIME_PARAM, (st), (val)) +#define sk_MIME_PARAM_delete(st, i) SKM_sk_delete(MIME_PARAM, (st), (i)) +#define sk_MIME_PARAM_delete_ptr(st, ptr) SKM_sk_delete_ptr(MIME_PARAM, (st), (ptr)) +#define sk_MIME_PARAM_insert(st, val, i) SKM_sk_insert(MIME_PARAM, (st), (val), (i)) +#define sk_MIME_PARAM_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(MIME_PARAM, (st), (cmp)) +#define sk_MIME_PARAM_dup(st) SKM_sk_dup(MIME_PARAM, st) +#define sk_MIME_PARAM_pop_free(st, free_func) SKM_sk_pop_free(MIME_PARAM, (st), (free_func)) +#define sk_MIME_PARAM_shift(st) SKM_sk_shift(MIME_PARAM, (st)) +#define sk_MIME_PARAM_pop(st) SKM_sk_pop(MIME_PARAM, (st)) +#define sk_MIME_PARAM_sort(st) SKM_sk_sort(MIME_PARAM, (st)) +#define sk_MIME_PARAM_is_sorted(st) SKM_sk_is_sorted(MIME_PARAM, (st)) + +#define sk_NAME_FUNCS_new(st) SKM_sk_new(NAME_FUNCS, (st)) +#define sk_NAME_FUNCS_new_null() SKM_sk_new_null(NAME_FUNCS) +#define sk_NAME_FUNCS_free(st) SKM_sk_free(NAME_FUNCS, (st)) +#define sk_NAME_FUNCS_num(st) SKM_sk_num(NAME_FUNCS, (st)) +#define sk_NAME_FUNCS_value(st, i) SKM_sk_value(NAME_FUNCS, (st), (i)) +#define sk_NAME_FUNCS_set(st, i, val) SKM_sk_set(NAME_FUNCS, (st), (i), (val)) +#define sk_NAME_FUNCS_zero(st) SKM_sk_zero(NAME_FUNCS, (st)) +#define sk_NAME_FUNCS_push(st, val) SKM_sk_push(NAME_FUNCS, (st), (val)) +#define sk_NAME_FUNCS_unshift(st, val) SKM_sk_unshift(NAME_FUNCS, (st), (val)) +#define sk_NAME_FUNCS_find(st, val) SKM_sk_find(NAME_FUNCS, (st), (val)) +#define sk_NAME_FUNCS_find_ex(st, val) SKM_sk_find_ex(NAME_FUNCS, (st), (val)) +#define sk_NAME_FUNCS_delete(st, i) SKM_sk_delete(NAME_FUNCS, (st), (i)) +#define sk_NAME_FUNCS_delete_ptr(st, ptr) SKM_sk_delete_ptr(NAME_FUNCS, (st), (ptr)) +#define sk_NAME_FUNCS_insert(st, val, i) SKM_sk_insert(NAME_FUNCS, (st), (val), (i)) +#define sk_NAME_FUNCS_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(NAME_FUNCS, (st), (cmp)) +#define sk_NAME_FUNCS_dup(st) SKM_sk_dup(NAME_FUNCS, st) +#define sk_NAME_FUNCS_pop_free(st, free_func) SKM_sk_pop_free(NAME_FUNCS, (st), (free_func)) +#define sk_NAME_FUNCS_shift(st) SKM_sk_shift(NAME_FUNCS, (st)) +#define sk_NAME_FUNCS_pop(st) SKM_sk_pop(NAME_FUNCS, (st)) +#define sk_NAME_FUNCS_sort(st) SKM_sk_sort(NAME_FUNCS, (st)) +#define sk_NAME_FUNCS_is_sorted(st) SKM_sk_is_sorted(NAME_FUNCS, (st)) + +#define sk_OCSP_CERTID_new(st) SKM_sk_new(OCSP_CERTID, (st)) +#define sk_OCSP_CERTID_new_null() SKM_sk_new_null(OCSP_CERTID) +#define sk_OCSP_CERTID_free(st) SKM_sk_free(OCSP_CERTID, (st)) +#define sk_OCSP_CERTID_num(st) SKM_sk_num(OCSP_CERTID, (st)) +#define sk_OCSP_CERTID_value(st, i) SKM_sk_value(OCSP_CERTID, (st), (i)) +#define sk_OCSP_CERTID_set(st, i, val) SKM_sk_set(OCSP_CERTID, (st), (i), (val)) +#define sk_OCSP_CERTID_zero(st) SKM_sk_zero(OCSP_CERTID, (st)) +#define sk_OCSP_CERTID_push(st, val) SKM_sk_push(OCSP_CERTID, (st), (val)) +#define sk_OCSP_CERTID_unshift(st, val) SKM_sk_unshift(OCSP_CERTID, (st), (val)) +#define sk_OCSP_CERTID_find(st, val) SKM_sk_find(OCSP_CERTID, (st), (val)) +#define sk_OCSP_CERTID_find_ex(st, val) SKM_sk_find_ex(OCSP_CERTID, (st), (val)) +#define sk_OCSP_CERTID_delete(st, i) SKM_sk_delete(OCSP_CERTID, (st), (i)) +#define sk_OCSP_CERTID_delete_ptr(st, ptr) SKM_sk_delete_ptr(OCSP_CERTID, (st), (ptr)) +#define sk_OCSP_CERTID_insert(st, val, i) SKM_sk_insert(OCSP_CERTID, (st), (val), (i)) +#define sk_OCSP_CERTID_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(OCSP_CERTID, (st), (cmp)) +#define sk_OCSP_CERTID_dup(st) SKM_sk_dup(OCSP_CERTID, st) +#define sk_OCSP_CERTID_pop_free(st, free_func) SKM_sk_pop_free(OCSP_CERTID, (st), (free_func)) +#define sk_OCSP_CERTID_shift(st) SKM_sk_shift(OCSP_CERTID, (st)) +#define sk_OCSP_CERTID_pop(st) SKM_sk_pop(OCSP_CERTID, (st)) +#define sk_OCSP_CERTID_sort(st) SKM_sk_sort(OCSP_CERTID, (st)) +#define sk_OCSP_CERTID_is_sorted(st) SKM_sk_is_sorted(OCSP_CERTID, (st)) + +#define sk_OCSP_ONEREQ_new(st) SKM_sk_new(OCSP_ONEREQ, (st)) +#define sk_OCSP_ONEREQ_new_null() SKM_sk_new_null(OCSP_ONEREQ) +#define sk_OCSP_ONEREQ_free(st) SKM_sk_free(OCSP_ONEREQ, (st)) +#define sk_OCSP_ONEREQ_num(st) SKM_sk_num(OCSP_ONEREQ, (st)) +#define sk_OCSP_ONEREQ_value(st, i) SKM_sk_value(OCSP_ONEREQ, (st), (i)) +#define sk_OCSP_ONEREQ_set(st, i, val) SKM_sk_set(OCSP_ONEREQ, (st), (i), (val)) +#define sk_OCSP_ONEREQ_zero(st) SKM_sk_zero(OCSP_ONEREQ, (st)) +#define sk_OCSP_ONEREQ_push(st, val) SKM_sk_push(OCSP_ONEREQ, (st), (val)) +#define sk_OCSP_ONEREQ_unshift(st, val) SKM_sk_unshift(OCSP_ONEREQ, (st), (val)) +#define sk_OCSP_ONEREQ_find(st, val) SKM_sk_find(OCSP_ONEREQ, (st), (val)) +#define sk_OCSP_ONEREQ_find_ex(st, val) SKM_sk_find_ex(OCSP_ONEREQ, (st), (val)) +#define sk_OCSP_ONEREQ_delete(st, i) SKM_sk_delete(OCSP_ONEREQ, (st), (i)) +#define sk_OCSP_ONEREQ_delete_ptr(st, ptr) SKM_sk_delete_ptr(OCSP_ONEREQ, (st), (ptr)) +#define sk_OCSP_ONEREQ_insert(st, val, i) SKM_sk_insert(OCSP_ONEREQ, (st), (val), (i)) +#define sk_OCSP_ONEREQ_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(OCSP_ONEREQ, (st), (cmp)) +#define sk_OCSP_ONEREQ_dup(st) SKM_sk_dup(OCSP_ONEREQ, st) +#define sk_OCSP_ONEREQ_pop_free(st, free_func) SKM_sk_pop_free(OCSP_ONEREQ, (st), (free_func)) +#define sk_OCSP_ONEREQ_shift(st) SKM_sk_shift(OCSP_ONEREQ, (st)) +#define sk_OCSP_ONEREQ_pop(st) SKM_sk_pop(OCSP_ONEREQ, (st)) +#define sk_OCSP_ONEREQ_sort(st) SKM_sk_sort(OCSP_ONEREQ, (st)) +#define sk_OCSP_ONEREQ_is_sorted(st) SKM_sk_is_sorted(OCSP_ONEREQ, (st)) + +#define sk_OCSP_SINGLERESP_new(st) SKM_sk_new(OCSP_SINGLERESP, (st)) +#define sk_OCSP_SINGLERESP_new_null() SKM_sk_new_null(OCSP_SINGLERESP) +#define sk_OCSP_SINGLERESP_free(st) SKM_sk_free(OCSP_SINGLERESP, (st)) +#define sk_OCSP_SINGLERESP_num(st) SKM_sk_num(OCSP_SINGLERESP, (st)) +#define sk_OCSP_SINGLERESP_value(st, i) SKM_sk_value(OCSP_SINGLERESP, (st), (i)) +#define sk_OCSP_SINGLERESP_set(st, i, val) SKM_sk_set(OCSP_SINGLERESP, (st), (i), (val)) +#define sk_OCSP_SINGLERESP_zero(st) SKM_sk_zero(OCSP_SINGLERESP, (st)) +#define sk_OCSP_SINGLERESP_push(st, val) SKM_sk_push(OCSP_SINGLERESP, (st), (val)) +#define sk_OCSP_SINGLERESP_unshift(st, val) SKM_sk_unshift(OCSP_SINGLERESP, (st), (val)) +#define sk_OCSP_SINGLERESP_find(st, val) SKM_sk_find(OCSP_SINGLERESP, (st), (val)) +#define sk_OCSP_SINGLERESP_find_ex(st, val) SKM_sk_find_ex(OCSP_SINGLERESP, (st), (val)) +#define sk_OCSP_SINGLERESP_delete(st, i) SKM_sk_delete(OCSP_SINGLERESP, (st), (i)) +#define sk_OCSP_SINGLERESP_delete_ptr(st, ptr) SKM_sk_delete_ptr(OCSP_SINGLERESP, (st), (ptr)) +#define sk_OCSP_SINGLERESP_insert(st, val, i) SKM_sk_insert(OCSP_SINGLERESP, (st), (val), (i)) +#define sk_OCSP_SINGLERESP_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(OCSP_SINGLERESP, (st), (cmp)) +#define sk_OCSP_SINGLERESP_dup(st) SKM_sk_dup(OCSP_SINGLERESP, st) +#define sk_OCSP_SINGLERESP_pop_free(st, free_func) SKM_sk_pop_free(OCSP_SINGLERESP, (st), (free_func)) +#define sk_OCSP_SINGLERESP_shift(st) SKM_sk_shift(OCSP_SINGLERESP, (st)) +#define sk_OCSP_SINGLERESP_pop(st) SKM_sk_pop(OCSP_SINGLERESP, (st)) +#define sk_OCSP_SINGLERESP_sort(st) SKM_sk_sort(OCSP_SINGLERESP, (st)) +#define sk_OCSP_SINGLERESP_is_sorted(st) SKM_sk_is_sorted(OCSP_SINGLERESP, (st)) + +#define sk_PKCS12_SAFEBAG_new(st) SKM_sk_new(PKCS12_SAFEBAG, (st)) +#define sk_PKCS12_SAFEBAG_new_null() SKM_sk_new_null(PKCS12_SAFEBAG) +#define sk_PKCS12_SAFEBAG_free(st) SKM_sk_free(PKCS12_SAFEBAG, (st)) +#define sk_PKCS12_SAFEBAG_num(st) SKM_sk_num(PKCS12_SAFEBAG, (st)) +#define sk_PKCS12_SAFEBAG_value(st, i) SKM_sk_value(PKCS12_SAFEBAG, (st), (i)) +#define sk_PKCS12_SAFEBAG_set(st, i, val) SKM_sk_set(PKCS12_SAFEBAG, (st), (i), (val)) +#define sk_PKCS12_SAFEBAG_zero(st) SKM_sk_zero(PKCS12_SAFEBAG, (st)) +#define sk_PKCS12_SAFEBAG_push(st, val) SKM_sk_push(PKCS12_SAFEBAG, (st), (val)) +#define sk_PKCS12_SAFEBAG_unshift(st, val) SKM_sk_unshift(PKCS12_SAFEBAG, (st), (val)) +#define sk_PKCS12_SAFEBAG_find(st, val) SKM_sk_find(PKCS12_SAFEBAG, (st), (val)) +#define sk_PKCS12_SAFEBAG_find_ex(st, val) SKM_sk_find_ex(PKCS12_SAFEBAG, (st), (val)) +#define sk_PKCS12_SAFEBAG_delete(st, i) SKM_sk_delete(PKCS12_SAFEBAG, (st), (i)) +#define sk_PKCS12_SAFEBAG_delete_ptr(st, ptr) SKM_sk_delete_ptr(PKCS12_SAFEBAG, (st), (ptr)) +#define sk_PKCS12_SAFEBAG_insert(st, val, i) SKM_sk_insert(PKCS12_SAFEBAG, (st), (val), (i)) +#define sk_PKCS12_SAFEBAG_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(PKCS12_SAFEBAG, (st), (cmp)) +#define sk_PKCS12_SAFEBAG_dup(st) SKM_sk_dup(PKCS12_SAFEBAG, st) +#define sk_PKCS12_SAFEBAG_pop_free(st, free_func) SKM_sk_pop_free(PKCS12_SAFEBAG, (st), (free_func)) +#define sk_PKCS12_SAFEBAG_shift(st) SKM_sk_shift(PKCS12_SAFEBAG, (st)) +#define sk_PKCS12_SAFEBAG_pop(st) SKM_sk_pop(PKCS12_SAFEBAG, (st)) +#define sk_PKCS12_SAFEBAG_sort(st) SKM_sk_sort(PKCS12_SAFEBAG, (st)) +#define sk_PKCS12_SAFEBAG_is_sorted(st) SKM_sk_is_sorted(PKCS12_SAFEBAG, (st)) + +#define sk_PKCS7_new(st) SKM_sk_new(PKCS7, (st)) +#define sk_PKCS7_new_null() SKM_sk_new_null(PKCS7) +#define sk_PKCS7_free(st) SKM_sk_free(PKCS7, (st)) +#define sk_PKCS7_num(st) SKM_sk_num(PKCS7, (st)) +#define sk_PKCS7_value(st, i) SKM_sk_value(PKCS7, (st), (i)) +#define sk_PKCS7_set(st, i, val) SKM_sk_set(PKCS7, (st), (i), (val)) +#define sk_PKCS7_zero(st) SKM_sk_zero(PKCS7, (st)) +#define sk_PKCS7_push(st, val) SKM_sk_push(PKCS7, (st), (val)) +#define sk_PKCS7_unshift(st, val) SKM_sk_unshift(PKCS7, (st), (val)) +#define sk_PKCS7_find(st, val) SKM_sk_find(PKCS7, (st), (val)) +#define sk_PKCS7_find_ex(st, val) SKM_sk_find_ex(PKCS7, (st), (val)) +#define sk_PKCS7_delete(st, i) SKM_sk_delete(PKCS7, (st), (i)) +#define sk_PKCS7_delete_ptr(st, ptr) SKM_sk_delete_ptr(PKCS7, (st), (ptr)) +#define sk_PKCS7_insert(st, val, i) SKM_sk_insert(PKCS7, (st), (val), (i)) +#define sk_PKCS7_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(PKCS7, (st), (cmp)) +#define sk_PKCS7_dup(st) SKM_sk_dup(PKCS7, st) +#define sk_PKCS7_pop_free(st, free_func) SKM_sk_pop_free(PKCS7, (st), (free_func)) +#define sk_PKCS7_shift(st) SKM_sk_shift(PKCS7, (st)) +#define sk_PKCS7_pop(st) SKM_sk_pop(PKCS7, (st)) +#define sk_PKCS7_sort(st) SKM_sk_sort(PKCS7, (st)) +#define sk_PKCS7_is_sorted(st) SKM_sk_is_sorted(PKCS7, (st)) + +#define sk_PKCS7_RECIP_INFO_new(st) SKM_sk_new(PKCS7_RECIP_INFO, (st)) +#define sk_PKCS7_RECIP_INFO_new_null() SKM_sk_new_null(PKCS7_RECIP_INFO) +#define sk_PKCS7_RECIP_INFO_free(st) SKM_sk_free(PKCS7_RECIP_INFO, (st)) +#define sk_PKCS7_RECIP_INFO_num(st) SKM_sk_num(PKCS7_RECIP_INFO, (st)) +#define sk_PKCS7_RECIP_INFO_value(st, i) SKM_sk_value(PKCS7_RECIP_INFO, (st), (i)) +#define sk_PKCS7_RECIP_INFO_set(st, i, val) SKM_sk_set(PKCS7_RECIP_INFO, (st), (i), (val)) +#define sk_PKCS7_RECIP_INFO_zero(st) SKM_sk_zero(PKCS7_RECIP_INFO, (st)) +#define sk_PKCS7_RECIP_INFO_push(st, val) SKM_sk_push(PKCS7_RECIP_INFO, (st), (val)) +#define sk_PKCS7_RECIP_INFO_unshift(st, val) SKM_sk_unshift(PKCS7_RECIP_INFO, (st), (val)) +#define sk_PKCS7_RECIP_INFO_find(st, val) SKM_sk_find(PKCS7_RECIP_INFO, (st), (val)) +#define sk_PKCS7_RECIP_INFO_find_ex(st, val) SKM_sk_find_ex(PKCS7_RECIP_INFO, (st), (val)) +#define sk_PKCS7_RECIP_INFO_delete(st, i) SKM_sk_delete(PKCS7_RECIP_INFO, (st), (i)) +#define sk_PKCS7_RECIP_INFO_delete_ptr(st, ptr) SKM_sk_delete_ptr(PKCS7_RECIP_INFO, (st), (ptr)) +#define sk_PKCS7_RECIP_INFO_insert(st, val, i) SKM_sk_insert(PKCS7_RECIP_INFO, (st), (val), (i)) +#define sk_PKCS7_RECIP_INFO_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(PKCS7_RECIP_INFO, (st), (cmp)) +#define sk_PKCS7_RECIP_INFO_dup(st) SKM_sk_dup(PKCS7_RECIP_INFO, st) +#define sk_PKCS7_RECIP_INFO_pop_free(st, free_func) SKM_sk_pop_free(PKCS7_RECIP_INFO, (st), (free_func)) +#define sk_PKCS7_RECIP_INFO_shift(st) SKM_sk_shift(PKCS7_RECIP_INFO, (st)) +#define sk_PKCS7_RECIP_INFO_pop(st) SKM_sk_pop(PKCS7_RECIP_INFO, (st)) +#define sk_PKCS7_RECIP_INFO_sort(st) SKM_sk_sort(PKCS7_RECIP_INFO, (st)) +#define sk_PKCS7_RECIP_INFO_is_sorted(st) SKM_sk_is_sorted(PKCS7_RECIP_INFO, (st)) + +#define sk_PKCS7_SIGNER_INFO_new(st) SKM_sk_new(PKCS7_SIGNER_INFO, (st)) +#define sk_PKCS7_SIGNER_INFO_new_null() SKM_sk_new_null(PKCS7_SIGNER_INFO) +#define sk_PKCS7_SIGNER_INFO_free(st) SKM_sk_free(PKCS7_SIGNER_INFO, (st)) +#define sk_PKCS7_SIGNER_INFO_num(st) SKM_sk_num(PKCS7_SIGNER_INFO, (st)) +#define sk_PKCS7_SIGNER_INFO_value(st, i) SKM_sk_value(PKCS7_SIGNER_INFO, (st), (i)) +#define sk_PKCS7_SIGNER_INFO_set(st, i, val) SKM_sk_set(PKCS7_SIGNER_INFO, (st), (i), (val)) +#define sk_PKCS7_SIGNER_INFO_zero(st) SKM_sk_zero(PKCS7_SIGNER_INFO, (st)) +#define sk_PKCS7_SIGNER_INFO_push(st, val) SKM_sk_push(PKCS7_SIGNER_INFO, (st), (val)) +#define sk_PKCS7_SIGNER_INFO_unshift(st, val) SKM_sk_unshift(PKCS7_SIGNER_INFO, (st), (val)) +#define sk_PKCS7_SIGNER_INFO_find(st, val) SKM_sk_find(PKCS7_SIGNER_INFO, (st), (val)) +#define sk_PKCS7_SIGNER_INFO_find_ex(st, val) SKM_sk_find_ex(PKCS7_SIGNER_INFO, (st), (val)) +#define sk_PKCS7_SIGNER_INFO_delete(st, i) SKM_sk_delete(PKCS7_SIGNER_INFO, (st), (i)) +#define sk_PKCS7_SIGNER_INFO_delete_ptr(st, ptr) SKM_sk_delete_ptr(PKCS7_SIGNER_INFO, (st), (ptr)) +#define sk_PKCS7_SIGNER_INFO_insert(st, val, i) SKM_sk_insert(PKCS7_SIGNER_INFO, (st), (val), (i)) +#define sk_PKCS7_SIGNER_INFO_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(PKCS7_SIGNER_INFO, (st), (cmp)) +#define sk_PKCS7_SIGNER_INFO_dup(st) SKM_sk_dup(PKCS7_SIGNER_INFO, st) +#define sk_PKCS7_SIGNER_INFO_pop_free(st, free_func) SKM_sk_pop_free(PKCS7_SIGNER_INFO, (st), (free_func)) +#define sk_PKCS7_SIGNER_INFO_shift(st) SKM_sk_shift(PKCS7_SIGNER_INFO, (st)) +#define sk_PKCS7_SIGNER_INFO_pop(st) SKM_sk_pop(PKCS7_SIGNER_INFO, (st)) +#define sk_PKCS7_SIGNER_INFO_sort(st) SKM_sk_sort(PKCS7_SIGNER_INFO, (st)) +#define sk_PKCS7_SIGNER_INFO_is_sorted(st) SKM_sk_is_sorted(PKCS7_SIGNER_INFO, (st)) + +#define sk_POLICYINFO_new(st) SKM_sk_new(POLICYINFO, (st)) +#define sk_POLICYINFO_new_null() SKM_sk_new_null(POLICYINFO) +#define sk_POLICYINFO_free(st) SKM_sk_free(POLICYINFO, (st)) +#define sk_POLICYINFO_num(st) SKM_sk_num(POLICYINFO, (st)) +#define sk_POLICYINFO_value(st, i) SKM_sk_value(POLICYINFO, (st), (i)) +#define sk_POLICYINFO_set(st, i, val) SKM_sk_set(POLICYINFO, (st), (i), (val)) +#define sk_POLICYINFO_zero(st) SKM_sk_zero(POLICYINFO, (st)) +#define sk_POLICYINFO_push(st, val) SKM_sk_push(POLICYINFO, (st), (val)) +#define sk_POLICYINFO_unshift(st, val) SKM_sk_unshift(POLICYINFO, (st), (val)) +#define sk_POLICYINFO_find(st, val) SKM_sk_find(POLICYINFO, (st), (val)) +#define sk_POLICYINFO_find_ex(st, val) SKM_sk_find_ex(POLICYINFO, (st), (val)) +#define sk_POLICYINFO_delete(st, i) SKM_sk_delete(POLICYINFO, (st), (i)) +#define sk_POLICYINFO_delete_ptr(st, ptr) SKM_sk_delete_ptr(POLICYINFO, (st), (ptr)) +#define sk_POLICYINFO_insert(st, val, i) SKM_sk_insert(POLICYINFO, (st), (val), (i)) +#define sk_POLICYINFO_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(POLICYINFO, (st), (cmp)) +#define sk_POLICYINFO_dup(st) SKM_sk_dup(POLICYINFO, st) +#define sk_POLICYINFO_pop_free(st, free_func) SKM_sk_pop_free(POLICYINFO, (st), (free_func)) +#define sk_POLICYINFO_shift(st) SKM_sk_shift(POLICYINFO, (st)) +#define sk_POLICYINFO_pop(st) SKM_sk_pop(POLICYINFO, (st)) +#define sk_POLICYINFO_sort(st) SKM_sk_sort(POLICYINFO, (st)) +#define sk_POLICYINFO_is_sorted(st) SKM_sk_is_sorted(POLICYINFO, (st)) + +#define sk_POLICYQUALINFO_new(st) SKM_sk_new(POLICYQUALINFO, (st)) +#define sk_POLICYQUALINFO_new_null() SKM_sk_new_null(POLICYQUALINFO) +#define sk_POLICYQUALINFO_free(st) SKM_sk_free(POLICYQUALINFO, (st)) +#define sk_POLICYQUALINFO_num(st) SKM_sk_num(POLICYQUALINFO, (st)) +#define sk_POLICYQUALINFO_value(st, i) SKM_sk_value(POLICYQUALINFO, (st), (i)) +#define sk_POLICYQUALINFO_set(st, i, val) SKM_sk_set(POLICYQUALINFO, (st), (i), (val)) +#define sk_POLICYQUALINFO_zero(st) SKM_sk_zero(POLICYQUALINFO, (st)) +#define sk_POLICYQUALINFO_push(st, val) SKM_sk_push(POLICYQUALINFO, (st), (val)) +#define sk_POLICYQUALINFO_unshift(st, val) SKM_sk_unshift(POLICYQUALINFO, (st), (val)) +#define sk_POLICYQUALINFO_find(st, val) SKM_sk_find(POLICYQUALINFO, (st), (val)) +#define sk_POLICYQUALINFO_find_ex(st, val) SKM_sk_find_ex(POLICYQUALINFO, (st), (val)) +#define sk_POLICYQUALINFO_delete(st, i) SKM_sk_delete(POLICYQUALINFO, (st), (i)) +#define sk_POLICYQUALINFO_delete_ptr(st, ptr) SKM_sk_delete_ptr(POLICYQUALINFO, (st), (ptr)) +#define sk_POLICYQUALINFO_insert(st, val, i) SKM_sk_insert(POLICYQUALINFO, (st), (val), (i)) +#define sk_POLICYQUALINFO_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(POLICYQUALINFO, (st), (cmp)) +#define sk_POLICYQUALINFO_dup(st) SKM_sk_dup(POLICYQUALINFO, st) +#define sk_POLICYQUALINFO_pop_free(st, free_func) SKM_sk_pop_free(POLICYQUALINFO, (st), (free_func)) +#define sk_POLICYQUALINFO_shift(st) SKM_sk_shift(POLICYQUALINFO, (st)) +#define sk_POLICYQUALINFO_pop(st) SKM_sk_pop(POLICYQUALINFO, (st)) +#define sk_POLICYQUALINFO_sort(st) SKM_sk_sort(POLICYQUALINFO, (st)) +#define sk_POLICYQUALINFO_is_sorted(st) SKM_sk_is_sorted(POLICYQUALINFO, (st)) + +#define sk_POLICY_MAPPING_new(st) SKM_sk_new(POLICY_MAPPING, (st)) +#define sk_POLICY_MAPPING_new_null() SKM_sk_new_null(POLICY_MAPPING) +#define sk_POLICY_MAPPING_free(st) SKM_sk_free(POLICY_MAPPING, (st)) +#define sk_POLICY_MAPPING_num(st) SKM_sk_num(POLICY_MAPPING, (st)) +#define sk_POLICY_MAPPING_value(st, i) SKM_sk_value(POLICY_MAPPING, (st), (i)) +#define sk_POLICY_MAPPING_set(st, i, val) SKM_sk_set(POLICY_MAPPING, (st), (i), (val)) +#define sk_POLICY_MAPPING_zero(st) SKM_sk_zero(POLICY_MAPPING, (st)) +#define sk_POLICY_MAPPING_push(st, val) SKM_sk_push(POLICY_MAPPING, (st), (val)) +#define sk_POLICY_MAPPING_unshift(st, val) SKM_sk_unshift(POLICY_MAPPING, (st), (val)) +#define sk_POLICY_MAPPING_find(st, val) SKM_sk_find(POLICY_MAPPING, (st), (val)) +#define sk_POLICY_MAPPING_find_ex(st, val) SKM_sk_find_ex(POLICY_MAPPING, (st), (val)) +#define sk_POLICY_MAPPING_delete(st, i) SKM_sk_delete(POLICY_MAPPING, (st), (i)) +#define sk_POLICY_MAPPING_delete_ptr(st, ptr) SKM_sk_delete_ptr(POLICY_MAPPING, (st), (ptr)) +#define sk_POLICY_MAPPING_insert(st, val, i) SKM_sk_insert(POLICY_MAPPING, (st), (val), (i)) +#define sk_POLICY_MAPPING_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(POLICY_MAPPING, (st), (cmp)) +#define sk_POLICY_MAPPING_dup(st) SKM_sk_dup(POLICY_MAPPING, st) +#define sk_POLICY_MAPPING_pop_free(st, free_func) SKM_sk_pop_free(POLICY_MAPPING, (st), (free_func)) +#define sk_POLICY_MAPPING_shift(st) SKM_sk_shift(POLICY_MAPPING, (st)) +#define sk_POLICY_MAPPING_pop(st) SKM_sk_pop(POLICY_MAPPING, (st)) +#define sk_POLICY_MAPPING_sort(st) SKM_sk_sort(POLICY_MAPPING, (st)) +#define sk_POLICY_MAPPING_is_sorted(st) SKM_sk_is_sorted(POLICY_MAPPING, (st)) + +#define sk_SSL_CIPHER_new(st) SKM_sk_new(SSL_CIPHER, (st)) +#define sk_SSL_CIPHER_new_null() SKM_sk_new_null(SSL_CIPHER) +#define sk_SSL_CIPHER_free(st) SKM_sk_free(SSL_CIPHER, (st)) +#define sk_SSL_CIPHER_num(st) SKM_sk_num(SSL_CIPHER, (st)) +#define sk_SSL_CIPHER_value(st, i) SKM_sk_value(SSL_CIPHER, (st), (i)) +#define sk_SSL_CIPHER_set(st, i, val) SKM_sk_set(SSL_CIPHER, (st), (i), (val)) +#define sk_SSL_CIPHER_zero(st) SKM_sk_zero(SSL_CIPHER, (st)) +#define sk_SSL_CIPHER_push(st, val) SKM_sk_push(SSL_CIPHER, (st), (val)) +#define sk_SSL_CIPHER_unshift(st, val) SKM_sk_unshift(SSL_CIPHER, (st), (val)) +#define sk_SSL_CIPHER_find(st, val) SKM_sk_find(SSL_CIPHER, (st), (val)) +#define sk_SSL_CIPHER_find_ex(st, val) SKM_sk_find_ex(SSL_CIPHER, (st), (val)) +#define sk_SSL_CIPHER_delete(st, i) SKM_sk_delete(SSL_CIPHER, (st), (i)) +#define sk_SSL_CIPHER_delete_ptr(st, ptr) SKM_sk_delete_ptr(SSL_CIPHER, (st), (ptr)) +#define sk_SSL_CIPHER_insert(st, val, i) SKM_sk_insert(SSL_CIPHER, (st), (val), (i)) +#define sk_SSL_CIPHER_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(SSL_CIPHER, (st), (cmp)) +#define sk_SSL_CIPHER_dup(st) SKM_sk_dup(SSL_CIPHER, st) +#define sk_SSL_CIPHER_pop_free(st, free_func) SKM_sk_pop_free(SSL_CIPHER, (st), (free_func)) +#define sk_SSL_CIPHER_shift(st) SKM_sk_shift(SSL_CIPHER, (st)) +#define sk_SSL_CIPHER_pop(st) SKM_sk_pop(SSL_CIPHER, (st)) +#define sk_SSL_CIPHER_sort(st) SKM_sk_sort(SSL_CIPHER, (st)) +#define sk_SSL_CIPHER_is_sorted(st) SKM_sk_is_sorted(SSL_CIPHER, (st)) + +#define sk_SSL_COMP_new(st) SKM_sk_new(SSL_COMP, (st)) +#define sk_SSL_COMP_new_null() SKM_sk_new_null(SSL_COMP) +#define sk_SSL_COMP_free(st) SKM_sk_free(SSL_COMP, (st)) +#define sk_SSL_COMP_num(st) SKM_sk_num(SSL_COMP, (st)) +#define sk_SSL_COMP_value(st, i) SKM_sk_value(SSL_COMP, (st), (i)) +#define sk_SSL_COMP_set(st, i, val) SKM_sk_set(SSL_COMP, (st), (i), (val)) +#define sk_SSL_COMP_zero(st) SKM_sk_zero(SSL_COMP, (st)) +#define sk_SSL_COMP_push(st, val) SKM_sk_push(SSL_COMP, (st), (val)) +#define sk_SSL_COMP_unshift(st, val) SKM_sk_unshift(SSL_COMP, (st), (val)) +#define sk_SSL_COMP_find(st, val) SKM_sk_find(SSL_COMP, (st), (val)) +#define sk_SSL_COMP_find_ex(st, val) SKM_sk_find_ex(SSL_COMP, (st), (val)) +#define sk_SSL_COMP_delete(st, i) SKM_sk_delete(SSL_COMP, (st), (i)) +#define sk_SSL_COMP_delete_ptr(st, ptr) SKM_sk_delete_ptr(SSL_COMP, (st), (ptr)) +#define sk_SSL_COMP_insert(st, val, i) SKM_sk_insert(SSL_COMP, (st), (val), (i)) +#define sk_SSL_COMP_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(SSL_COMP, (st), (cmp)) +#define sk_SSL_COMP_dup(st) SKM_sk_dup(SSL_COMP, st) +#define sk_SSL_COMP_pop_free(st, free_func) SKM_sk_pop_free(SSL_COMP, (st), (free_func)) +#define sk_SSL_COMP_shift(st) SKM_sk_shift(SSL_COMP, (st)) +#define sk_SSL_COMP_pop(st) SKM_sk_pop(SSL_COMP, (st)) +#define sk_SSL_COMP_sort(st) SKM_sk_sort(SSL_COMP, (st)) +#define sk_SSL_COMP_is_sorted(st) SKM_sk_is_sorted(SSL_COMP, (st)) + +#define sk_STORE_OBJECT_new(st) SKM_sk_new(STORE_OBJECT, (st)) +#define sk_STORE_OBJECT_new_null() SKM_sk_new_null(STORE_OBJECT) +#define sk_STORE_OBJECT_free(st) SKM_sk_free(STORE_OBJECT, (st)) +#define sk_STORE_OBJECT_num(st) SKM_sk_num(STORE_OBJECT, (st)) +#define sk_STORE_OBJECT_value(st, i) SKM_sk_value(STORE_OBJECT, (st), (i)) +#define sk_STORE_OBJECT_set(st, i, val) SKM_sk_set(STORE_OBJECT, (st), (i), (val)) +#define sk_STORE_OBJECT_zero(st) SKM_sk_zero(STORE_OBJECT, (st)) +#define sk_STORE_OBJECT_push(st, val) SKM_sk_push(STORE_OBJECT, (st), (val)) +#define sk_STORE_OBJECT_unshift(st, val) SKM_sk_unshift(STORE_OBJECT, (st), (val)) +#define sk_STORE_OBJECT_find(st, val) SKM_sk_find(STORE_OBJECT, (st), (val)) +#define sk_STORE_OBJECT_find_ex(st, val) SKM_sk_find_ex(STORE_OBJECT, (st), (val)) +#define sk_STORE_OBJECT_delete(st, i) SKM_sk_delete(STORE_OBJECT, (st), (i)) +#define sk_STORE_OBJECT_delete_ptr(st, ptr) SKM_sk_delete_ptr(STORE_OBJECT, (st), (ptr)) +#define sk_STORE_OBJECT_insert(st, val, i) SKM_sk_insert(STORE_OBJECT, (st), (val), (i)) +#define sk_STORE_OBJECT_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(STORE_OBJECT, (st), (cmp)) +#define sk_STORE_OBJECT_dup(st) SKM_sk_dup(STORE_OBJECT, st) +#define sk_STORE_OBJECT_pop_free(st, free_func) SKM_sk_pop_free(STORE_OBJECT, (st), (free_func)) +#define sk_STORE_OBJECT_shift(st) SKM_sk_shift(STORE_OBJECT, (st)) +#define sk_STORE_OBJECT_pop(st) SKM_sk_pop(STORE_OBJECT, (st)) +#define sk_STORE_OBJECT_sort(st) SKM_sk_sort(STORE_OBJECT, (st)) +#define sk_STORE_OBJECT_is_sorted(st) SKM_sk_is_sorted(STORE_OBJECT, (st)) + +#define sk_SXNETID_new(st) SKM_sk_new(SXNETID, (st)) +#define sk_SXNETID_new_null() SKM_sk_new_null(SXNETID) +#define sk_SXNETID_free(st) SKM_sk_free(SXNETID, (st)) +#define sk_SXNETID_num(st) SKM_sk_num(SXNETID, (st)) +#define sk_SXNETID_value(st, i) SKM_sk_value(SXNETID, (st), (i)) +#define sk_SXNETID_set(st, i, val) SKM_sk_set(SXNETID, (st), (i), (val)) +#define sk_SXNETID_zero(st) SKM_sk_zero(SXNETID, (st)) +#define sk_SXNETID_push(st, val) SKM_sk_push(SXNETID, (st), (val)) +#define sk_SXNETID_unshift(st, val) SKM_sk_unshift(SXNETID, (st), (val)) +#define sk_SXNETID_find(st, val) SKM_sk_find(SXNETID, (st), (val)) +#define sk_SXNETID_find_ex(st, val) SKM_sk_find_ex(SXNETID, (st), (val)) +#define sk_SXNETID_delete(st, i) SKM_sk_delete(SXNETID, (st), (i)) +#define sk_SXNETID_delete_ptr(st, ptr) SKM_sk_delete_ptr(SXNETID, (st), (ptr)) +#define sk_SXNETID_insert(st, val, i) SKM_sk_insert(SXNETID, (st), (val), (i)) +#define sk_SXNETID_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(SXNETID, (st), (cmp)) +#define sk_SXNETID_dup(st) SKM_sk_dup(SXNETID, st) +#define sk_SXNETID_pop_free(st, free_func) SKM_sk_pop_free(SXNETID, (st), (free_func)) +#define sk_SXNETID_shift(st) SKM_sk_shift(SXNETID, (st)) +#define sk_SXNETID_pop(st) SKM_sk_pop(SXNETID, (st)) +#define sk_SXNETID_sort(st) SKM_sk_sort(SXNETID, (st)) +#define sk_SXNETID_is_sorted(st) SKM_sk_is_sorted(SXNETID, (st)) + +#define sk_UI_STRING_new(st) SKM_sk_new(UI_STRING, (st)) +#define sk_UI_STRING_new_null() SKM_sk_new_null(UI_STRING) +#define sk_UI_STRING_free(st) SKM_sk_free(UI_STRING, (st)) +#define sk_UI_STRING_num(st) SKM_sk_num(UI_STRING, (st)) +#define sk_UI_STRING_value(st, i) SKM_sk_value(UI_STRING, (st), (i)) +#define sk_UI_STRING_set(st, i, val) SKM_sk_set(UI_STRING, (st), (i), (val)) +#define sk_UI_STRING_zero(st) SKM_sk_zero(UI_STRING, (st)) +#define sk_UI_STRING_push(st, val) SKM_sk_push(UI_STRING, (st), (val)) +#define sk_UI_STRING_unshift(st, val) SKM_sk_unshift(UI_STRING, (st), (val)) +#define sk_UI_STRING_find(st, val) SKM_sk_find(UI_STRING, (st), (val)) +#define sk_UI_STRING_find_ex(st, val) SKM_sk_find_ex(UI_STRING, (st), (val)) +#define sk_UI_STRING_delete(st, i) SKM_sk_delete(UI_STRING, (st), (i)) +#define sk_UI_STRING_delete_ptr(st, ptr) SKM_sk_delete_ptr(UI_STRING, (st), (ptr)) +#define sk_UI_STRING_insert(st, val, i) SKM_sk_insert(UI_STRING, (st), (val), (i)) +#define sk_UI_STRING_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(UI_STRING, (st), (cmp)) +#define sk_UI_STRING_dup(st) SKM_sk_dup(UI_STRING, st) +#define sk_UI_STRING_pop_free(st, free_func) SKM_sk_pop_free(UI_STRING, (st), (free_func)) +#define sk_UI_STRING_shift(st) SKM_sk_shift(UI_STRING, (st)) +#define sk_UI_STRING_pop(st) SKM_sk_pop(UI_STRING, (st)) +#define sk_UI_STRING_sort(st) SKM_sk_sort(UI_STRING, (st)) +#define sk_UI_STRING_is_sorted(st) SKM_sk_is_sorted(UI_STRING, (st)) + +#define sk_X509_new(st) SKM_sk_new(X509, (st)) +#define sk_X509_new_null() SKM_sk_new_null(X509) +#define sk_X509_free(st) SKM_sk_free(X509, (st)) +#define sk_X509_num(st) SKM_sk_num(X509, (st)) +#define sk_X509_value(st, i) SKM_sk_value(X509, (st), (i)) +#define sk_X509_set(st, i, val) SKM_sk_set(X509, (st), (i), (val)) +#define sk_X509_zero(st) SKM_sk_zero(X509, (st)) +#define sk_X509_push(st, val) SKM_sk_push(X509, (st), (val)) +#define sk_X509_unshift(st, val) SKM_sk_unshift(X509, (st), (val)) +#define sk_X509_find(st, val) SKM_sk_find(X509, (st), (val)) +#define sk_X509_find_ex(st, val) SKM_sk_find_ex(X509, (st), (val)) +#define sk_X509_delete(st, i) SKM_sk_delete(X509, (st), (i)) +#define sk_X509_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509, (st), (ptr)) +#define sk_X509_insert(st, val, i) SKM_sk_insert(X509, (st), (val), (i)) +#define sk_X509_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509, (st), (cmp)) +#define sk_X509_dup(st) SKM_sk_dup(X509, st) +#define sk_X509_pop_free(st, free_func) SKM_sk_pop_free(X509, (st), (free_func)) +#define sk_X509_shift(st) SKM_sk_shift(X509, (st)) +#define sk_X509_pop(st) SKM_sk_pop(X509, (st)) +#define sk_X509_sort(st) SKM_sk_sort(X509, (st)) +#define sk_X509_is_sorted(st) SKM_sk_is_sorted(X509, (st)) + +#define sk_X509V3_EXT_METHOD_new(st) SKM_sk_new(X509V3_EXT_METHOD, (st)) +#define sk_X509V3_EXT_METHOD_new_null() SKM_sk_new_null(X509V3_EXT_METHOD) +#define sk_X509V3_EXT_METHOD_free(st) SKM_sk_free(X509V3_EXT_METHOD, (st)) +#define sk_X509V3_EXT_METHOD_num(st) SKM_sk_num(X509V3_EXT_METHOD, (st)) +#define sk_X509V3_EXT_METHOD_value(st, i) SKM_sk_value(X509V3_EXT_METHOD, (st), (i)) +#define sk_X509V3_EXT_METHOD_set(st, i, val) SKM_sk_set(X509V3_EXT_METHOD, (st), (i), (val)) +#define sk_X509V3_EXT_METHOD_zero(st) SKM_sk_zero(X509V3_EXT_METHOD, (st)) +#define sk_X509V3_EXT_METHOD_push(st, val) SKM_sk_push(X509V3_EXT_METHOD, (st), (val)) +#define sk_X509V3_EXT_METHOD_unshift(st, val) SKM_sk_unshift(X509V3_EXT_METHOD, (st), (val)) +#define sk_X509V3_EXT_METHOD_find(st, val) SKM_sk_find(X509V3_EXT_METHOD, (st), (val)) +#define sk_X509V3_EXT_METHOD_find_ex(st, val) SKM_sk_find_ex(X509V3_EXT_METHOD, (st), (val)) +#define sk_X509V3_EXT_METHOD_delete(st, i) SKM_sk_delete(X509V3_EXT_METHOD, (st), (i)) +#define sk_X509V3_EXT_METHOD_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509V3_EXT_METHOD, (st), (ptr)) +#define sk_X509V3_EXT_METHOD_insert(st, val, i) SKM_sk_insert(X509V3_EXT_METHOD, (st), (val), (i)) +#define sk_X509V3_EXT_METHOD_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509V3_EXT_METHOD, (st), (cmp)) +#define sk_X509V3_EXT_METHOD_dup(st) SKM_sk_dup(X509V3_EXT_METHOD, st) +#define sk_X509V3_EXT_METHOD_pop_free(st, free_func) SKM_sk_pop_free(X509V3_EXT_METHOD, (st), (free_func)) +#define sk_X509V3_EXT_METHOD_shift(st) SKM_sk_shift(X509V3_EXT_METHOD, (st)) +#define sk_X509V3_EXT_METHOD_pop(st) SKM_sk_pop(X509V3_EXT_METHOD, (st)) +#define sk_X509V3_EXT_METHOD_sort(st) SKM_sk_sort(X509V3_EXT_METHOD, (st)) +#define sk_X509V3_EXT_METHOD_is_sorted(st) SKM_sk_is_sorted(X509V3_EXT_METHOD, (st)) + +#define sk_X509_ALGOR_new(st) SKM_sk_new(X509_ALGOR, (st)) +#define sk_X509_ALGOR_new_null() SKM_sk_new_null(X509_ALGOR) +#define sk_X509_ALGOR_free(st) SKM_sk_free(X509_ALGOR, (st)) +#define sk_X509_ALGOR_num(st) SKM_sk_num(X509_ALGOR, (st)) +#define sk_X509_ALGOR_value(st, i) SKM_sk_value(X509_ALGOR, (st), (i)) +#define sk_X509_ALGOR_set(st, i, val) SKM_sk_set(X509_ALGOR, (st), (i), (val)) +#define sk_X509_ALGOR_zero(st) SKM_sk_zero(X509_ALGOR, (st)) +#define sk_X509_ALGOR_push(st, val) SKM_sk_push(X509_ALGOR, (st), (val)) +#define sk_X509_ALGOR_unshift(st, val) SKM_sk_unshift(X509_ALGOR, (st), (val)) +#define sk_X509_ALGOR_find(st, val) SKM_sk_find(X509_ALGOR, (st), (val)) +#define sk_X509_ALGOR_find_ex(st, val) SKM_sk_find_ex(X509_ALGOR, (st), (val)) +#define sk_X509_ALGOR_delete(st, i) SKM_sk_delete(X509_ALGOR, (st), (i)) +#define sk_X509_ALGOR_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_ALGOR, (st), (ptr)) +#define sk_X509_ALGOR_insert(st, val, i) SKM_sk_insert(X509_ALGOR, (st), (val), (i)) +#define sk_X509_ALGOR_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_ALGOR, (st), (cmp)) +#define sk_X509_ALGOR_dup(st) SKM_sk_dup(X509_ALGOR, st) +#define sk_X509_ALGOR_pop_free(st, free_func) SKM_sk_pop_free(X509_ALGOR, (st), (free_func)) +#define sk_X509_ALGOR_shift(st) SKM_sk_shift(X509_ALGOR, (st)) +#define sk_X509_ALGOR_pop(st) SKM_sk_pop(X509_ALGOR, (st)) +#define sk_X509_ALGOR_sort(st) SKM_sk_sort(X509_ALGOR, (st)) +#define sk_X509_ALGOR_is_sorted(st) SKM_sk_is_sorted(X509_ALGOR, (st)) + +#define sk_X509_ATTRIBUTE_new(st) SKM_sk_new(X509_ATTRIBUTE, (st)) +#define sk_X509_ATTRIBUTE_new_null() SKM_sk_new_null(X509_ATTRIBUTE) +#define sk_X509_ATTRIBUTE_free(st) SKM_sk_free(X509_ATTRIBUTE, (st)) +#define sk_X509_ATTRIBUTE_num(st) SKM_sk_num(X509_ATTRIBUTE, (st)) +#define sk_X509_ATTRIBUTE_value(st, i) SKM_sk_value(X509_ATTRIBUTE, (st), (i)) +#define sk_X509_ATTRIBUTE_set(st, i, val) SKM_sk_set(X509_ATTRIBUTE, (st), (i), (val)) +#define sk_X509_ATTRIBUTE_zero(st) SKM_sk_zero(X509_ATTRIBUTE, (st)) +#define sk_X509_ATTRIBUTE_push(st, val) SKM_sk_push(X509_ATTRIBUTE, (st), (val)) +#define sk_X509_ATTRIBUTE_unshift(st, val) SKM_sk_unshift(X509_ATTRIBUTE, (st), (val)) +#define sk_X509_ATTRIBUTE_find(st, val) SKM_sk_find(X509_ATTRIBUTE, (st), (val)) +#define sk_X509_ATTRIBUTE_find_ex(st, val) SKM_sk_find_ex(X509_ATTRIBUTE, (st), (val)) +#define sk_X509_ATTRIBUTE_delete(st, i) SKM_sk_delete(X509_ATTRIBUTE, (st), (i)) +#define sk_X509_ATTRIBUTE_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_ATTRIBUTE, (st), (ptr)) +#define sk_X509_ATTRIBUTE_insert(st, val, i) SKM_sk_insert(X509_ATTRIBUTE, (st), (val), (i)) +#define sk_X509_ATTRIBUTE_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_ATTRIBUTE, (st), (cmp)) +#define sk_X509_ATTRIBUTE_dup(st) SKM_sk_dup(X509_ATTRIBUTE, st) +#define sk_X509_ATTRIBUTE_pop_free(st, free_func) SKM_sk_pop_free(X509_ATTRIBUTE, (st), (free_func)) +#define sk_X509_ATTRIBUTE_shift(st) SKM_sk_shift(X509_ATTRIBUTE, (st)) +#define sk_X509_ATTRIBUTE_pop(st) SKM_sk_pop(X509_ATTRIBUTE, (st)) +#define sk_X509_ATTRIBUTE_sort(st) SKM_sk_sort(X509_ATTRIBUTE, (st)) +#define sk_X509_ATTRIBUTE_is_sorted(st) SKM_sk_is_sorted(X509_ATTRIBUTE, (st)) + +#define sk_X509_CRL_new(st) SKM_sk_new(X509_CRL, (st)) +#define sk_X509_CRL_new_null() SKM_sk_new_null(X509_CRL) +#define sk_X509_CRL_free(st) SKM_sk_free(X509_CRL, (st)) +#define sk_X509_CRL_num(st) SKM_sk_num(X509_CRL, (st)) +#define sk_X509_CRL_value(st, i) SKM_sk_value(X509_CRL, (st), (i)) +#define sk_X509_CRL_set(st, i, val) SKM_sk_set(X509_CRL, (st), (i), (val)) +#define sk_X509_CRL_zero(st) SKM_sk_zero(X509_CRL, (st)) +#define sk_X509_CRL_push(st, val) SKM_sk_push(X509_CRL, (st), (val)) +#define sk_X509_CRL_unshift(st, val) SKM_sk_unshift(X509_CRL, (st), (val)) +#define sk_X509_CRL_find(st, val) SKM_sk_find(X509_CRL, (st), (val)) +#define sk_X509_CRL_find_ex(st, val) SKM_sk_find_ex(X509_CRL, (st), (val)) +#define sk_X509_CRL_delete(st, i) SKM_sk_delete(X509_CRL, (st), (i)) +#define sk_X509_CRL_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_CRL, (st), (ptr)) +#define sk_X509_CRL_insert(st, val, i) SKM_sk_insert(X509_CRL, (st), (val), (i)) +#define sk_X509_CRL_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_CRL, (st), (cmp)) +#define sk_X509_CRL_dup(st) SKM_sk_dup(X509_CRL, st) +#define sk_X509_CRL_pop_free(st, free_func) SKM_sk_pop_free(X509_CRL, (st), (free_func)) +#define sk_X509_CRL_shift(st) SKM_sk_shift(X509_CRL, (st)) +#define sk_X509_CRL_pop(st) SKM_sk_pop(X509_CRL, (st)) +#define sk_X509_CRL_sort(st) SKM_sk_sort(X509_CRL, (st)) +#define sk_X509_CRL_is_sorted(st) SKM_sk_is_sorted(X509_CRL, (st)) + +#define sk_X509_EXTENSION_new(st) SKM_sk_new(X509_EXTENSION, (st)) +#define sk_X509_EXTENSION_new_null() SKM_sk_new_null(X509_EXTENSION) +#define sk_X509_EXTENSION_free(st) SKM_sk_free(X509_EXTENSION, (st)) +#define sk_X509_EXTENSION_num(st) SKM_sk_num(X509_EXTENSION, (st)) +#define sk_X509_EXTENSION_value(st, i) SKM_sk_value(X509_EXTENSION, (st), (i)) +#define sk_X509_EXTENSION_set(st, i, val) SKM_sk_set(X509_EXTENSION, (st), (i), (val)) +#define sk_X509_EXTENSION_zero(st) SKM_sk_zero(X509_EXTENSION, (st)) +#define sk_X509_EXTENSION_push(st, val) SKM_sk_push(X509_EXTENSION, (st), (val)) +#define sk_X509_EXTENSION_unshift(st, val) SKM_sk_unshift(X509_EXTENSION, (st), (val)) +#define sk_X509_EXTENSION_find(st, val) SKM_sk_find(X509_EXTENSION, (st), (val)) +#define sk_X509_EXTENSION_find_ex(st, val) SKM_sk_find_ex(X509_EXTENSION, (st), (val)) +#define sk_X509_EXTENSION_delete(st, i) SKM_sk_delete(X509_EXTENSION, (st), (i)) +#define sk_X509_EXTENSION_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_EXTENSION, (st), (ptr)) +#define sk_X509_EXTENSION_insert(st, val, i) SKM_sk_insert(X509_EXTENSION, (st), (val), (i)) +#define sk_X509_EXTENSION_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_EXTENSION, (st), (cmp)) +#define sk_X509_EXTENSION_dup(st) SKM_sk_dup(X509_EXTENSION, st) +#define sk_X509_EXTENSION_pop_free(st, free_func) SKM_sk_pop_free(X509_EXTENSION, (st), (free_func)) +#define sk_X509_EXTENSION_shift(st) SKM_sk_shift(X509_EXTENSION, (st)) +#define sk_X509_EXTENSION_pop(st) SKM_sk_pop(X509_EXTENSION, (st)) +#define sk_X509_EXTENSION_sort(st) SKM_sk_sort(X509_EXTENSION, (st)) +#define sk_X509_EXTENSION_is_sorted(st) SKM_sk_is_sorted(X509_EXTENSION, (st)) + +#define sk_X509_INFO_new(st) SKM_sk_new(X509_INFO, (st)) +#define sk_X509_INFO_new_null() SKM_sk_new_null(X509_INFO) +#define sk_X509_INFO_free(st) SKM_sk_free(X509_INFO, (st)) +#define sk_X509_INFO_num(st) SKM_sk_num(X509_INFO, (st)) +#define sk_X509_INFO_value(st, i) SKM_sk_value(X509_INFO, (st), (i)) +#define sk_X509_INFO_set(st, i, val) SKM_sk_set(X509_INFO, (st), (i), (val)) +#define sk_X509_INFO_zero(st) SKM_sk_zero(X509_INFO, (st)) +#define sk_X509_INFO_push(st, val) SKM_sk_push(X509_INFO, (st), (val)) +#define sk_X509_INFO_unshift(st, val) SKM_sk_unshift(X509_INFO, (st), (val)) +#define sk_X509_INFO_find(st, val) SKM_sk_find(X509_INFO, (st), (val)) +#define sk_X509_INFO_find_ex(st, val) SKM_sk_find_ex(X509_INFO, (st), (val)) +#define sk_X509_INFO_delete(st, i) SKM_sk_delete(X509_INFO, (st), (i)) +#define sk_X509_INFO_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_INFO, (st), (ptr)) +#define sk_X509_INFO_insert(st, val, i) SKM_sk_insert(X509_INFO, (st), (val), (i)) +#define sk_X509_INFO_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_INFO, (st), (cmp)) +#define sk_X509_INFO_dup(st) SKM_sk_dup(X509_INFO, st) +#define sk_X509_INFO_pop_free(st, free_func) SKM_sk_pop_free(X509_INFO, (st), (free_func)) +#define sk_X509_INFO_shift(st) SKM_sk_shift(X509_INFO, (st)) +#define sk_X509_INFO_pop(st) SKM_sk_pop(X509_INFO, (st)) +#define sk_X509_INFO_sort(st) SKM_sk_sort(X509_INFO, (st)) +#define sk_X509_INFO_is_sorted(st) SKM_sk_is_sorted(X509_INFO, (st)) + +#define sk_X509_LOOKUP_new(st) SKM_sk_new(X509_LOOKUP, (st)) +#define sk_X509_LOOKUP_new_null() SKM_sk_new_null(X509_LOOKUP) +#define sk_X509_LOOKUP_free(st) SKM_sk_free(X509_LOOKUP, (st)) +#define sk_X509_LOOKUP_num(st) SKM_sk_num(X509_LOOKUP, (st)) +#define sk_X509_LOOKUP_value(st, i) SKM_sk_value(X509_LOOKUP, (st), (i)) +#define sk_X509_LOOKUP_set(st, i, val) SKM_sk_set(X509_LOOKUP, (st), (i), (val)) +#define sk_X509_LOOKUP_zero(st) SKM_sk_zero(X509_LOOKUP, (st)) +#define sk_X509_LOOKUP_push(st, val) SKM_sk_push(X509_LOOKUP, (st), (val)) +#define sk_X509_LOOKUP_unshift(st, val) SKM_sk_unshift(X509_LOOKUP, (st), (val)) +#define sk_X509_LOOKUP_find(st, val) SKM_sk_find(X509_LOOKUP, (st), (val)) +#define sk_X509_LOOKUP_find_ex(st, val) SKM_sk_find_ex(X509_LOOKUP, (st), (val)) +#define sk_X509_LOOKUP_delete(st, i) SKM_sk_delete(X509_LOOKUP, (st), (i)) +#define sk_X509_LOOKUP_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_LOOKUP, (st), (ptr)) +#define sk_X509_LOOKUP_insert(st, val, i) SKM_sk_insert(X509_LOOKUP, (st), (val), (i)) +#define sk_X509_LOOKUP_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_LOOKUP, (st), (cmp)) +#define sk_X509_LOOKUP_dup(st) SKM_sk_dup(X509_LOOKUP, st) +#define sk_X509_LOOKUP_pop_free(st, free_func) SKM_sk_pop_free(X509_LOOKUP, (st), (free_func)) +#define sk_X509_LOOKUP_shift(st) SKM_sk_shift(X509_LOOKUP, (st)) +#define sk_X509_LOOKUP_pop(st) SKM_sk_pop(X509_LOOKUP, (st)) +#define sk_X509_LOOKUP_sort(st) SKM_sk_sort(X509_LOOKUP, (st)) +#define sk_X509_LOOKUP_is_sorted(st) SKM_sk_is_sorted(X509_LOOKUP, (st)) + +#define sk_X509_NAME_new(st) SKM_sk_new(X509_NAME, (st)) +#define sk_X509_NAME_new_null() SKM_sk_new_null(X509_NAME) +#define sk_X509_NAME_free(st) SKM_sk_free(X509_NAME, (st)) +#define sk_X509_NAME_num(st) SKM_sk_num(X509_NAME, (st)) +#define sk_X509_NAME_value(st, i) SKM_sk_value(X509_NAME, (st), (i)) +#define sk_X509_NAME_set(st, i, val) SKM_sk_set(X509_NAME, (st), (i), (val)) +#define sk_X509_NAME_zero(st) SKM_sk_zero(X509_NAME, (st)) +#define sk_X509_NAME_push(st, val) SKM_sk_push(X509_NAME, (st), (val)) +#define sk_X509_NAME_unshift(st, val) SKM_sk_unshift(X509_NAME, (st), (val)) +#define sk_X509_NAME_find(st, val) SKM_sk_find(X509_NAME, (st), (val)) +#define sk_X509_NAME_find_ex(st, val) SKM_sk_find_ex(X509_NAME, (st), (val)) +#define sk_X509_NAME_delete(st, i) SKM_sk_delete(X509_NAME, (st), (i)) +#define sk_X509_NAME_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_NAME, (st), (ptr)) +#define sk_X509_NAME_insert(st, val, i) SKM_sk_insert(X509_NAME, (st), (val), (i)) +#define sk_X509_NAME_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_NAME, (st), (cmp)) +#define sk_X509_NAME_dup(st) SKM_sk_dup(X509_NAME, st) +#define sk_X509_NAME_pop_free(st, free_func) SKM_sk_pop_free(X509_NAME, (st), (free_func)) +#define sk_X509_NAME_shift(st) SKM_sk_shift(X509_NAME, (st)) +#define sk_X509_NAME_pop(st) SKM_sk_pop(X509_NAME, (st)) +#define sk_X509_NAME_sort(st) SKM_sk_sort(X509_NAME, (st)) +#define sk_X509_NAME_is_sorted(st) SKM_sk_is_sorted(X509_NAME, (st)) + +#define sk_X509_NAME_ENTRY_new(st) SKM_sk_new(X509_NAME_ENTRY, (st)) +#define sk_X509_NAME_ENTRY_new_null() SKM_sk_new_null(X509_NAME_ENTRY) +#define sk_X509_NAME_ENTRY_free(st) SKM_sk_free(X509_NAME_ENTRY, (st)) +#define sk_X509_NAME_ENTRY_num(st) SKM_sk_num(X509_NAME_ENTRY, (st)) +#define sk_X509_NAME_ENTRY_value(st, i) SKM_sk_value(X509_NAME_ENTRY, (st), (i)) +#define sk_X509_NAME_ENTRY_set(st, i, val) SKM_sk_set(X509_NAME_ENTRY, (st), (i), (val)) +#define sk_X509_NAME_ENTRY_zero(st) SKM_sk_zero(X509_NAME_ENTRY, (st)) +#define sk_X509_NAME_ENTRY_push(st, val) SKM_sk_push(X509_NAME_ENTRY, (st), (val)) +#define sk_X509_NAME_ENTRY_unshift(st, val) SKM_sk_unshift(X509_NAME_ENTRY, (st), (val)) +#define sk_X509_NAME_ENTRY_find(st, val) SKM_sk_find(X509_NAME_ENTRY, (st), (val)) +#define sk_X509_NAME_ENTRY_find_ex(st, val) SKM_sk_find_ex(X509_NAME_ENTRY, (st), (val)) +#define sk_X509_NAME_ENTRY_delete(st, i) SKM_sk_delete(X509_NAME_ENTRY, (st), (i)) +#define sk_X509_NAME_ENTRY_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_NAME_ENTRY, (st), (ptr)) +#define sk_X509_NAME_ENTRY_insert(st, val, i) SKM_sk_insert(X509_NAME_ENTRY, (st), (val), (i)) +#define sk_X509_NAME_ENTRY_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_NAME_ENTRY, (st), (cmp)) +#define sk_X509_NAME_ENTRY_dup(st) SKM_sk_dup(X509_NAME_ENTRY, st) +#define sk_X509_NAME_ENTRY_pop_free(st, free_func) SKM_sk_pop_free(X509_NAME_ENTRY, (st), (free_func)) +#define sk_X509_NAME_ENTRY_shift(st) SKM_sk_shift(X509_NAME_ENTRY, (st)) +#define sk_X509_NAME_ENTRY_pop(st) SKM_sk_pop(X509_NAME_ENTRY, (st)) +#define sk_X509_NAME_ENTRY_sort(st) SKM_sk_sort(X509_NAME_ENTRY, (st)) +#define sk_X509_NAME_ENTRY_is_sorted(st) SKM_sk_is_sorted(X509_NAME_ENTRY, (st)) + +#define sk_X509_OBJECT_new(st) SKM_sk_new(X509_OBJECT, (st)) +#define sk_X509_OBJECT_new_null() SKM_sk_new_null(X509_OBJECT) +#define sk_X509_OBJECT_free(st) SKM_sk_free(X509_OBJECT, (st)) +#define sk_X509_OBJECT_num(st) SKM_sk_num(X509_OBJECT, (st)) +#define sk_X509_OBJECT_value(st, i) SKM_sk_value(X509_OBJECT, (st), (i)) +#define sk_X509_OBJECT_set(st, i, val) SKM_sk_set(X509_OBJECT, (st), (i), (val)) +#define sk_X509_OBJECT_zero(st) SKM_sk_zero(X509_OBJECT, (st)) +#define sk_X509_OBJECT_push(st, val) SKM_sk_push(X509_OBJECT, (st), (val)) +#define sk_X509_OBJECT_unshift(st, val) SKM_sk_unshift(X509_OBJECT, (st), (val)) +#define sk_X509_OBJECT_find(st, val) SKM_sk_find(X509_OBJECT, (st), (val)) +#define sk_X509_OBJECT_find_ex(st, val) SKM_sk_find_ex(X509_OBJECT, (st), (val)) +#define sk_X509_OBJECT_delete(st, i) SKM_sk_delete(X509_OBJECT, (st), (i)) +#define sk_X509_OBJECT_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_OBJECT, (st), (ptr)) +#define sk_X509_OBJECT_insert(st, val, i) SKM_sk_insert(X509_OBJECT, (st), (val), (i)) +#define sk_X509_OBJECT_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_OBJECT, (st), (cmp)) +#define sk_X509_OBJECT_dup(st) SKM_sk_dup(X509_OBJECT, st) +#define sk_X509_OBJECT_pop_free(st, free_func) SKM_sk_pop_free(X509_OBJECT, (st), (free_func)) +#define sk_X509_OBJECT_shift(st) SKM_sk_shift(X509_OBJECT, (st)) +#define sk_X509_OBJECT_pop(st) SKM_sk_pop(X509_OBJECT, (st)) +#define sk_X509_OBJECT_sort(st) SKM_sk_sort(X509_OBJECT, (st)) +#define sk_X509_OBJECT_is_sorted(st) SKM_sk_is_sorted(X509_OBJECT, (st)) + +#define sk_X509_POLICY_DATA_new(st) SKM_sk_new(X509_POLICY_DATA, (st)) +#define sk_X509_POLICY_DATA_new_null() SKM_sk_new_null(X509_POLICY_DATA) +#define sk_X509_POLICY_DATA_free(st) SKM_sk_free(X509_POLICY_DATA, (st)) +#define sk_X509_POLICY_DATA_num(st) SKM_sk_num(X509_POLICY_DATA, (st)) +#define sk_X509_POLICY_DATA_value(st, i) SKM_sk_value(X509_POLICY_DATA, (st), (i)) +#define sk_X509_POLICY_DATA_set(st, i, val) SKM_sk_set(X509_POLICY_DATA, (st), (i), (val)) +#define sk_X509_POLICY_DATA_zero(st) SKM_sk_zero(X509_POLICY_DATA, (st)) +#define sk_X509_POLICY_DATA_push(st, val) SKM_sk_push(X509_POLICY_DATA, (st), (val)) +#define sk_X509_POLICY_DATA_unshift(st, val) SKM_sk_unshift(X509_POLICY_DATA, (st), (val)) +#define sk_X509_POLICY_DATA_find(st, val) SKM_sk_find(X509_POLICY_DATA, (st), (val)) +#define sk_X509_POLICY_DATA_find_ex(st, val) SKM_sk_find_ex(X509_POLICY_DATA, (st), (val)) +#define sk_X509_POLICY_DATA_delete(st, i) SKM_sk_delete(X509_POLICY_DATA, (st), (i)) +#define sk_X509_POLICY_DATA_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_POLICY_DATA, (st), (ptr)) +#define sk_X509_POLICY_DATA_insert(st, val, i) SKM_sk_insert(X509_POLICY_DATA, (st), (val), (i)) +#define sk_X509_POLICY_DATA_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_POLICY_DATA, (st), (cmp)) +#define sk_X509_POLICY_DATA_dup(st) SKM_sk_dup(X509_POLICY_DATA, st) +#define sk_X509_POLICY_DATA_pop_free(st, free_func) SKM_sk_pop_free(X509_POLICY_DATA, (st), (free_func)) +#define sk_X509_POLICY_DATA_shift(st) SKM_sk_shift(X509_POLICY_DATA, (st)) +#define sk_X509_POLICY_DATA_pop(st) SKM_sk_pop(X509_POLICY_DATA, (st)) +#define sk_X509_POLICY_DATA_sort(st) SKM_sk_sort(X509_POLICY_DATA, (st)) +#define sk_X509_POLICY_DATA_is_sorted(st) SKM_sk_is_sorted(X509_POLICY_DATA, (st)) + +#define sk_X509_POLICY_NODE_new(st) SKM_sk_new(X509_POLICY_NODE, (st)) +#define sk_X509_POLICY_NODE_new_null() SKM_sk_new_null(X509_POLICY_NODE) +#define sk_X509_POLICY_NODE_free(st) SKM_sk_free(X509_POLICY_NODE, (st)) +#define sk_X509_POLICY_NODE_num(st) SKM_sk_num(X509_POLICY_NODE, (st)) +#define sk_X509_POLICY_NODE_value(st, i) SKM_sk_value(X509_POLICY_NODE, (st), (i)) +#define sk_X509_POLICY_NODE_set(st, i, val) SKM_sk_set(X509_POLICY_NODE, (st), (i), (val)) +#define sk_X509_POLICY_NODE_zero(st) SKM_sk_zero(X509_POLICY_NODE, (st)) +#define sk_X509_POLICY_NODE_push(st, val) SKM_sk_push(X509_POLICY_NODE, (st), (val)) +#define sk_X509_POLICY_NODE_unshift(st, val) SKM_sk_unshift(X509_POLICY_NODE, (st), (val)) +#define sk_X509_POLICY_NODE_find(st, val) SKM_sk_find(X509_POLICY_NODE, (st), (val)) +#define sk_X509_POLICY_NODE_find_ex(st, val) SKM_sk_find_ex(X509_POLICY_NODE, (st), (val)) +#define sk_X509_POLICY_NODE_delete(st, i) SKM_sk_delete(X509_POLICY_NODE, (st), (i)) +#define sk_X509_POLICY_NODE_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_POLICY_NODE, (st), (ptr)) +#define sk_X509_POLICY_NODE_insert(st, val, i) SKM_sk_insert(X509_POLICY_NODE, (st), (val), (i)) +#define sk_X509_POLICY_NODE_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_POLICY_NODE, (st), (cmp)) +#define sk_X509_POLICY_NODE_dup(st) SKM_sk_dup(X509_POLICY_NODE, st) +#define sk_X509_POLICY_NODE_pop_free(st, free_func) SKM_sk_pop_free(X509_POLICY_NODE, (st), (free_func)) +#define sk_X509_POLICY_NODE_shift(st) SKM_sk_shift(X509_POLICY_NODE, (st)) +#define sk_X509_POLICY_NODE_pop(st) SKM_sk_pop(X509_POLICY_NODE, (st)) +#define sk_X509_POLICY_NODE_sort(st) SKM_sk_sort(X509_POLICY_NODE, (st)) +#define sk_X509_POLICY_NODE_is_sorted(st) SKM_sk_is_sorted(X509_POLICY_NODE, (st)) + +#define sk_X509_POLICY_REF_new(st) SKM_sk_new(X509_POLICY_REF, (st)) +#define sk_X509_POLICY_REF_new_null() SKM_sk_new_null(X509_POLICY_REF) +#define sk_X509_POLICY_REF_free(st) SKM_sk_free(X509_POLICY_REF, (st)) +#define sk_X509_POLICY_REF_num(st) SKM_sk_num(X509_POLICY_REF, (st)) +#define sk_X509_POLICY_REF_value(st, i) SKM_sk_value(X509_POLICY_REF, (st), (i)) +#define sk_X509_POLICY_REF_set(st, i, val) SKM_sk_set(X509_POLICY_REF, (st), (i), (val)) +#define sk_X509_POLICY_REF_zero(st) SKM_sk_zero(X509_POLICY_REF, (st)) +#define sk_X509_POLICY_REF_push(st, val) SKM_sk_push(X509_POLICY_REF, (st), (val)) +#define sk_X509_POLICY_REF_unshift(st, val) SKM_sk_unshift(X509_POLICY_REF, (st), (val)) +#define sk_X509_POLICY_REF_find(st, val) SKM_sk_find(X509_POLICY_REF, (st), (val)) +#define sk_X509_POLICY_REF_find_ex(st, val) SKM_sk_find_ex(X509_POLICY_REF, (st), (val)) +#define sk_X509_POLICY_REF_delete(st, i) SKM_sk_delete(X509_POLICY_REF, (st), (i)) +#define sk_X509_POLICY_REF_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_POLICY_REF, (st), (ptr)) +#define sk_X509_POLICY_REF_insert(st, val, i) SKM_sk_insert(X509_POLICY_REF, (st), (val), (i)) +#define sk_X509_POLICY_REF_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_POLICY_REF, (st), (cmp)) +#define sk_X509_POLICY_REF_dup(st) SKM_sk_dup(X509_POLICY_REF, st) +#define sk_X509_POLICY_REF_pop_free(st, free_func) SKM_sk_pop_free(X509_POLICY_REF, (st), (free_func)) +#define sk_X509_POLICY_REF_shift(st) SKM_sk_shift(X509_POLICY_REF, (st)) +#define sk_X509_POLICY_REF_pop(st) SKM_sk_pop(X509_POLICY_REF, (st)) +#define sk_X509_POLICY_REF_sort(st) SKM_sk_sort(X509_POLICY_REF, (st)) +#define sk_X509_POLICY_REF_is_sorted(st) SKM_sk_is_sorted(X509_POLICY_REF, (st)) + +#define sk_X509_PURPOSE_new(st) SKM_sk_new(X509_PURPOSE, (st)) +#define sk_X509_PURPOSE_new_null() SKM_sk_new_null(X509_PURPOSE) +#define sk_X509_PURPOSE_free(st) SKM_sk_free(X509_PURPOSE, (st)) +#define sk_X509_PURPOSE_num(st) SKM_sk_num(X509_PURPOSE, (st)) +#define sk_X509_PURPOSE_value(st, i) SKM_sk_value(X509_PURPOSE, (st), (i)) +#define sk_X509_PURPOSE_set(st, i, val) SKM_sk_set(X509_PURPOSE, (st), (i), (val)) +#define sk_X509_PURPOSE_zero(st) SKM_sk_zero(X509_PURPOSE, (st)) +#define sk_X509_PURPOSE_push(st, val) SKM_sk_push(X509_PURPOSE, (st), (val)) +#define sk_X509_PURPOSE_unshift(st, val) SKM_sk_unshift(X509_PURPOSE, (st), (val)) +#define sk_X509_PURPOSE_find(st, val) SKM_sk_find(X509_PURPOSE, (st), (val)) +#define sk_X509_PURPOSE_find_ex(st, val) SKM_sk_find_ex(X509_PURPOSE, (st), (val)) +#define sk_X509_PURPOSE_delete(st, i) SKM_sk_delete(X509_PURPOSE, (st), (i)) +#define sk_X509_PURPOSE_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_PURPOSE, (st), (ptr)) +#define sk_X509_PURPOSE_insert(st, val, i) SKM_sk_insert(X509_PURPOSE, (st), (val), (i)) +#define sk_X509_PURPOSE_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_PURPOSE, (st), (cmp)) +#define sk_X509_PURPOSE_dup(st) SKM_sk_dup(X509_PURPOSE, st) +#define sk_X509_PURPOSE_pop_free(st, free_func) SKM_sk_pop_free(X509_PURPOSE, (st), (free_func)) +#define sk_X509_PURPOSE_shift(st) SKM_sk_shift(X509_PURPOSE, (st)) +#define sk_X509_PURPOSE_pop(st) SKM_sk_pop(X509_PURPOSE, (st)) +#define sk_X509_PURPOSE_sort(st) SKM_sk_sort(X509_PURPOSE, (st)) +#define sk_X509_PURPOSE_is_sorted(st) SKM_sk_is_sorted(X509_PURPOSE, (st)) + +#define sk_X509_REVOKED_new(st) SKM_sk_new(X509_REVOKED, (st)) +#define sk_X509_REVOKED_new_null() SKM_sk_new_null(X509_REVOKED) +#define sk_X509_REVOKED_free(st) SKM_sk_free(X509_REVOKED, (st)) +#define sk_X509_REVOKED_num(st) SKM_sk_num(X509_REVOKED, (st)) +#define sk_X509_REVOKED_value(st, i) SKM_sk_value(X509_REVOKED, (st), (i)) +#define sk_X509_REVOKED_set(st, i, val) SKM_sk_set(X509_REVOKED, (st), (i), (val)) +#define sk_X509_REVOKED_zero(st) SKM_sk_zero(X509_REVOKED, (st)) +#define sk_X509_REVOKED_push(st, val) SKM_sk_push(X509_REVOKED, (st), (val)) +#define sk_X509_REVOKED_unshift(st, val) SKM_sk_unshift(X509_REVOKED, (st), (val)) +#define sk_X509_REVOKED_find(st, val) SKM_sk_find(X509_REVOKED, (st), (val)) +#define sk_X509_REVOKED_find_ex(st, val) SKM_sk_find_ex(X509_REVOKED, (st), (val)) +#define sk_X509_REVOKED_delete(st, i) SKM_sk_delete(X509_REVOKED, (st), (i)) +#define sk_X509_REVOKED_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_REVOKED, (st), (ptr)) +#define sk_X509_REVOKED_insert(st, val, i) SKM_sk_insert(X509_REVOKED, (st), (val), (i)) +#define sk_X509_REVOKED_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_REVOKED, (st), (cmp)) +#define sk_X509_REVOKED_dup(st) SKM_sk_dup(X509_REVOKED, st) +#define sk_X509_REVOKED_pop_free(st, free_func) SKM_sk_pop_free(X509_REVOKED, (st), (free_func)) +#define sk_X509_REVOKED_shift(st) SKM_sk_shift(X509_REVOKED, (st)) +#define sk_X509_REVOKED_pop(st) SKM_sk_pop(X509_REVOKED, (st)) +#define sk_X509_REVOKED_sort(st) SKM_sk_sort(X509_REVOKED, (st)) +#define sk_X509_REVOKED_is_sorted(st) SKM_sk_is_sorted(X509_REVOKED, (st)) + +#define sk_X509_TRUST_new(st) SKM_sk_new(X509_TRUST, (st)) +#define sk_X509_TRUST_new_null() SKM_sk_new_null(X509_TRUST) +#define sk_X509_TRUST_free(st) SKM_sk_free(X509_TRUST, (st)) +#define sk_X509_TRUST_num(st) SKM_sk_num(X509_TRUST, (st)) +#define sk_X509_TRUST_value(st, i) SKM_sk_value(X509_TRUST, (st), (i)) +#define sk_X509_TRUST_set(st, i, val) SKM_sk_set(X509_TRUST, (st), (i), (val)) +#define sk_X509_TRUST_zero(st) SKM_sk_zero(X509_TRUST, (st)) +#define sk_X509_TRUST_push(st, val) SKM_sk_push(X509_TRUST, (st), (val)) +#define sk_X509_TRUST_unshift(st, val) SKM_sk_unshift(X509_TRUST, (st), (val)) +#define sk_X509_TRUST_find(st, val) SKM_sk_find(X509_TRUST, (st), (val)) +#define sk_X509_TRUST_find_ex(st, val) SKM_sk_find_ex(X509_TRUST, (st), (val)) +#define sk_X509_TRUST_delete(st, i) SKM_sk_delete(X509_TRUST, (st), (i)) +#define sk_X509_TRUST_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_TRUST, (st), (ptr)) +#define sk_X509_TRUST_insert(st, val, i) SKM_sk_insert(X509_TRUST, (st), (val), (i)) +#define sk_X509_TRUST_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_TRUST, (st), (cmp)) +#define sk_X509_TRUST_dup(st) SKM_sk_dup(X509_TRUST, st) +#define sk_X509_TRUST_pop_free(st, free_func) SKM_sk_pop_free(X509_TRUST, (st), (free_func)) +#define sk_X509_TRUST_shift(st) SKM_sk_shift(X509_TRUST, (st)) +#define sk_X509_TRUST_pop(st) SKM_sk_pop(X509_TRUST, (st)) +#define sk_X509_TRUST_sort(st) SKM_sk_sort(X509_TRUST, (st)) +#define sk_X509_TRUST_is_sorted(st) SKM_sk_is_sorted(X509_TRUST, (st)) + +#define sk_X509_VERIFY_PARAM_new(st) SKM_sk_new(X509_VERIFY_PARAM, (st)) +#define sk_X509_VERIFY_PARAM_new_null() SKM_sk_new_null(X509_VERIFY_PARAM) +#define sk_X509_VERIFY_PARAM_free(st) SKM_sk_free(X509_VERIFY_PARAM, (st)) +#define sk_X509_VERIFY_PARAM_num(st) SKM_sk_num(X509_VERIFY_PARAM, (st)) +#define sk_X509_VERIFY_PARAM_value(st, i) SKM_sk_value(X509_VERIFY_PARAM, (st), (i)) +#define sk_X509_VERIFY_PARAM_set(st, i, val) SKM_sk_set(X509_VERIFY_PARAM, (st), (i), (val)) +#define sk_X509_VERIFY_PARAM_zero(st) SKM_sk_zero(X509_VERIFY_PARAM, (st)) +#define sk_X509_VERIFY_PARAM_push(st, val) SKM_sk_push(X509_VERIFY_PARAM, (st), (val)) +#define sk_X509_VERIFY_PARAM_unshift(st, val) SKM_sk_unshift(X509_VERIFY_PARAM, (st), (val)) +#define sk_X509_VERIFY_PARAM_find(st, val) SKM_sk_find(X509_VERIFY_PARAM, (st), (val)) +#define sk_X509_VERIFY_PARAM_find_ex(st, val) SKM_sk_find_ex(X509_VERIFY_PARAM, (st), (val)) +#define sk_X509_VERIFY_PARAM_delete(st, i) SKM_sk_delete(X509_VERIFY_PARAM, (st), (i)) +#define sk_X509_VERIFY_PARAM_delete_ptr(st, ptr) SKM_sk_delete_ptr(X509_VERIFY_PARAM, (st), (ptr)) +#define sk_X509_VERIFY_PARAM_insert(st, val, i) SKM_sk_insert(X509_VERIFY_PARAM, (st), (val), (i)) +#define sk_X509_VERIFY_PARAM_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(X509_VERIFY_PARAM, (st), (cmp)) +#define sk_X509_VERIFY_PARAM_dup(st) SKM_sk_dup(X509_VERIFY_PARAM, st) +#define sk_X509_VERIFY_PARAM_pop_free(st, free_func) SKM_sk_pop_free(X509_VERIFY_PARAM, (st), (free_func)) +#define sk_X509_VERIFY_PARAM_shift(st) SKM_sk_shift(X509_VERIFY_PARAM, (st)) +#define sk_X509_VERIFY_PARAM_pop(st) SKM_sk_pop(X509_VERIFY_PARAM, (st)) +#define sk_X509_VERIFY_PARAM_sort(st) SKM_sk_sort(X509_VERIFY_PARAM, (st)) +#define sk_X509_VERIFY_PARAM_is_sorted(st) SKM_sk_is_sorted(X509_VERIFY_PARAM, (st)) + +#define d2i_ASN1_SET_OF_ACCESS_DESCRIPTION(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(ACCESS_DESCRIPTION, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_ACCESS_DESCRIPTION(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(ACCESS_DESCRIPTION, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_ACCESS_DESCRIPTION(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(ACCESS_DESCRIPTION, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_ACCESS_DESCRIPTION(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(ACCESS_DESCRIPTION, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_ASN1_INTEGER(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(ASN1_INTEGER, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_ASN1_INTEGER(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(ASN1_INTEGER, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_ASN1_INTEGER(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(ASN1_INTEGER, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_ASN1_INTEGER(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(ASN1_INTEGER, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_ASN1_OBJECT(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(ASN1_OBJECT, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_ASN1_OBJECT(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(ASN1_OBJECT, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_ASN1_OBJECT(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(ASN1_OBJECT, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_ASN1_OBJECT(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(ASN1_OBJECT, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_ASN1_TYPE(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(ASN1_TYPE, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_ASN1_TYPE(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(ASN1_TYPE, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_ASN1_TYPE(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(ASN1_TYPE, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_ASN1_TYPE(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(ASN1_TYPE, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_DIST_POINT(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(DIST_POINT, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_DIST_POINT(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(DIST_POINT, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_DIST_POINT(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(DIST_POINT, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_DIST_POINT(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(DIST_POINT, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_GENERAL_NAME(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(GENERAL_NAME, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_GENERAL_NAME(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(GENERAL_NAME, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_GENERAL_NAME(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(GENERAL_NAME, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_GENERAL_NAME(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(GENERAL_NAME, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_OCSP_ONEREQ(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(OCSP_ONEREQ, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_OCSP_ONEREQ(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(OCSP_ONEREQ, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_OCSP_ONEREQ(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(OCSP_ONEREQ, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_OCSP_ONEREQ(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(OCSP_ONEREQ, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_OCSP_SINGLERESP(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(OCSP_SINGLERESP, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_OCSP_SINGLERESP(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(OCSP_SINGLERESP, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_OCSP_SINGLERESP(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(OCSP_SINGLERESP, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_OCSP_SINGLERESP(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(OCSP_SINGLERESP, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_PKCS12_SAFEBAG(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(PKCS12_SAFEBAG, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_PKCS12_SAFEBAG(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(PKCS12_SAFEBAG, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_PKCS12_SAFEBAG(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(PKCS12_SAFEBAG, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_PKCS12_SAFEBAG(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(PKCS12_SAFEBAG, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_PKCS7(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(PKCS7, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_PKCS7(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(PKCS7, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_PKCS7(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(PKCS7, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_PKCS7(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(PKCS7, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_PKCS7_RECIP_INFO(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(PKCS7_RECIP_INFO, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_PKCS7_RECIP_INFO(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(PKCS7_RECIP_INFO, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_PKCS7_RECIP_INFO(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(PKCS7_RECIP_INFO, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_PKCS7_RECIP_INFO(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(PKCS7_RECIP_INFO, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_PKCS7_SIGNER_INFO(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(PKCS7_SIGNER_INFO, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_PKCS7_SIGNER_INFO(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(PKCS7_SIGNER_INFO, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_PKCS7_SIGNER_INFO(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(PKCS7_SIGNER_INFO, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_PKCS7_SIGNER_INFO(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(PKCS7_SIGNER_INFO, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_POLICYINFO(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(POLICYINFO, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_POLICYINFO(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(POLICYINFO, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_POLICYINFO(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(POLICYINFO, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_POLICYINFO(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(POLICYINFO, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_POLICYQUALINFO(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(POLICYQUALINFO, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_POLICYQUALINFO(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(POLICYQUALINFO, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_POLICYQUALINFO(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(POLICYQUALINFO, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_POLICYQUALINFO(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(POLICYQUALINFO, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_SXNETID(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(SXNETID, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_SXNETID(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(SXNETID, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_SXNETID(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(SXNETID, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_SXNETID(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(SXNETID, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_X509(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(X509, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_X509(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(X509, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_X509(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(X509, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_X509(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(X509, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_X509_ALGOR(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(X509_ALGOR, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_X509_ALGOR(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(X509_ALGOR, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_X509_ALGOR(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(X509_ALGOR, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_X509_ALGOR(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(X509_ALGOR, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_X509_ATTRIBUTE(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(X509_ATTRIBUTE, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_X509_ATTRIBUTE(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(X509_ATTRIBUTE, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_X509_ATTRIBUTE(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(X509_ATTRIBUTE, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_X509_ATTRIBUTE(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(X509_ATTRIBUTE, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_X509_CRL(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(X509_CRL, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_X509_CRL(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(X509_CRL, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_X509_CRL(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(X509_CRL, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_X509_CRL(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(X509_CRL, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_X509_EXTENSION(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(X509_EXTENSION, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_X509_EXTENSION(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(X509_EXTENSION, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_X509_EXTENSION(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(X509_EXTENSION, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_X509_EXTENSION(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(X509_EXTENSION, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_X509_NAME_ENTRY(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(X509_NAME_ENTRY, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_X509_NAME_ENTRY(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(X509_NAME_ENTRY, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_X509_NAME_ENTRY(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(X509_NAME_ENTRY, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_X509_NAME_ENTRY(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(X509_NAME_ENTRY, (buf), (len), (d2i_func), (free_func)) + +#define d2i_ASN1_SET_OF_X509_REVOKED(st, pp, length, d2i_func, free_func, ex_tag, ex_class) \ + SKM_ASN1_SET_OF_d2i(X509_REVOKED, (st), (pp), (length), (d2i_func), (free_func), (ex_tag), (ex_class)) +#define i2d_ASN1_SET_OF_X509_REVOKED(st, pp, i2d_func, ex_tag, ex_class, is_set) \ + SKM_ASN1_SET_OF_i2d(X509_REVOKED, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set)) +#define ASN1_seq_pack_X509_REVOKED(st, i2d_func, buf, len) \ + SKM_ASN1_seq_pack(X509_REVOKED, (st), (i2d_func), (buf), (len)) +#define ASN1_seq_unpack_X509_REVOKED(buf, len, d2i_func, free_func) \ + SKM_ASN1_seq_unpack(X509_REVOKED, (buf), (len), (d2i_func), (free_func)) + +#define PKCS12_decrypt_d2i_PKCS12_SAFEBAG(algor, d2i_func, free_func, pass, passlen, oct, seq) \ + SKM_PKCS12_decrypt_d2i(PKCS12_SAFEBAG, (algor), (d2i_func), (free_func), (pass), (passlen), (oct), (seq)) + +#define PKCS12_decrypt_d2i_PKCS7(algor, d2i_func, free_func, pass, passlen, oct, seq) \ + SKM_PKCS12_decrypt_d2i(PKCS7, (algor), (d2i_func), (free_func), (pass), (passlen), (oct), (seq)) +/* End of util/mkstack.pl block, you may now edit :-) */ + +#endif /* !defined HEADER_SAFESTACK_H */ diff --git a/production/3rdparty/openssl/include/openssl/sha.h b/production/3rdparty/openssl/include/openssl/sha.h new file mode 100644 index 00000000..a83bd3ca --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/sha.h @@ -0,0 +1,199 @@ +/* crypto/sha/sha.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_SHA_H +#define HEADER_SHA_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(OPENSSL_NO_SHA) || (defined(OPENSSL_NO_SHA0) && defined(OPENSSL_NO_SHA1)) +#error SHA is disabled. +#endif + +#if defined(OPENSSL_FIPS) +#define FIPS_SHA_SIZE_T size_t +#endif + +/* + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * ! SHA_LONG has to be at least 32 bits wide. If it's wider, then ! + * ! SHA_LONG_LOG2 has to be defined along. ! + * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + */ + +#if defined(OPENSSL_SYS_WIN16) || defined(__LP32__) +#define SHA_LONG unsigned long +#elif defined(OPENSSL_SYS_CRAY) || defined(__ILP64__) +#define SHA_LONG unsigned long +#define SHA_LONG_LOG2 3 +#else +#define SHA_LONG unsigned int +#endif + +#define SHA_LBLOCK 16 +#define SHA_CBLOCK (SHA_LBLOCK*4) /* SHA treats input data as a + * contiguous array of 32 bit + * wide big-endian values. */ +#define SHA_LAST_BLOCK (SHA_CBLOCK-8) +#define SHA_DIGEST_LENGTH 20 + +typedef struct SHAstate_st + { + SHA_LONG h0,h1,h2,h3,h4; + SHA_LONG Nl,Nh; + SHA_LONG data[SHA_LBLOCK]; + unsigned int num; + } SHA_CTX; + +#ifndef OPENSSL_NO_SHA0 +int SHA_Init(SHA_CTX *c); +int SHA_Update(SHA_CTX *c, const void *data, size_t len); +int SHA_Final(unsigned char *md, SHA_CTX *c); +unsigned char *SHA(const unsigned char *d, size_t n, unsigned char *md); +void SHA_Transform(SHA_CTX *c, const unsigned char *data); +#endif +#ifndef OPENSSL_NO_SHA1 +int SHA1_Init(SHA_CTX *c); +int SHA1_Update(SHA_CTX *c, const void *data, size_t len); +int SHA1_Final(unsigned char *md, SHA_CTX *c); +unsigned char *SHA1(const unsigned char *d, size_t n, unsigned char *md); +void SHA1_Transform(SHA_CTX *c, const unsigned char *data); +#endif + +#define SHA256_CBLOCK (SHA_LBLOCK*4) /* SHA-256 treats input data as a + * contiguous array of 32 bit + * wide big-endian values. */ +#define SHA224_DIGEST_LENGTH 28 +#define SHA256_DIGEST_LENGTH 32 + +typedef struct SHA256state_st + { + SHA_LONG h[8]; + SHA_LONG Nl,Nh; + SHA_LONG data[SHA_LBLOCK]; + unsigned int num,md_len; + } SHA256_CTX; + +#ifndef OPENSSL_NO_SHA256 +int SHA224_Init(SHA256_CTX *c); +int SHA224_Update(SHA256_CTX *c, const void *data, size_t len); +int SHA224_Final(unsigned char *md, SHA256_CTX *c); +unsigned char *SHA224(const unsigned char *d, size_t n,unsigned char *md); +int SHA256_Init(SHA256_CTX *c); +int SHA256_Update(SHA256_CTX *c, const void *data, size_t len); +int SHA256_Final(unsigned char *md, SHA256_CTX *c); +unsigned char *SHA256(const unsigned char *d, size_t n,unsigned char *md); +void SHA256_Transform(SHA256_CTX *c, const unsigned char *data); +#endif + +#define SHA384_DIGEST_LENGTH 48 +#define SHA512_DIGEST_LENGTH 64 + +#ifndef OPENSSL_NO_SHA512 +/* + * Unlike 32-bit digest algorithms, SHA-512 *relies* on SHA_LONG64 + * being exactly 64-bit wide. See Implementation Notes in sha512.c + * for further details. + */ +#define SHA512_CBLOCK (SHA_LBLOCK*8) /* SHA-512 treats input data as a + * contiguous array of 64 bit + * wide big-endian values. */ +#if (defined(_WIN32) || defined(_WIN64)) && !defined(__MINGW32__) +#define SHA_LONG64 unsigned __int64 +#define U64(C) C##UI64 +#elif defined(__arch64__) +#define SHA_LONG64 unsigned long +#define U64(C) C##UL +#else +#define SHA_LONG64 unsigned long long +#define U64(C) C##ULL +#endif + +typedef struct SHA512state_st + { + SHA_LONG64 h[8]; + SHA_LONG64 Nl,Nh; + union { + SHA_LONG64 d[SHA_LBLOCK]; + unsigned char p[SHA512_CBLOCK]; + } u; + unsigned int num,md_len; + } SHA512_CTX; +#endif + +#ifndef OPENSSL_NO_SHA512 +int SHA384_Init(SHA512_CTX *c); +int SHA384_Update(SHA512_CTX *c, const void *data, size_t len); +int SHA384_Final(unsigned char *md, SHA512_CTX *c); +unsigned char *SHA384(const unsigned char *d, size_t n,unsigned char *md); +int SHA512_Init(SHA512_CTX *c); +int SHA512_Update(SHA512_CTX *c, const void *data, size_t len); +int SHA512_Final(unsigned char *md, SHA512_CTX *c); +unsigned char *SHA512(const unsigned char *d, size_t n,unsigned char *md); +void SHA512_Transform(SHA512_CTX *c, const unsigned char *data); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/ssl.h b/production/3rdparty/openssl/include/openssl/ssl.h new file mode 100644 index 00000000..c87e5f84 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/ssl.h @@ -0,0 +1,1955 @@ +/* ssl/ssl.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2001 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright (c) 1998-2002 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * ECC cipher suite support in OpenSSL originally developed by + * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. + */ + +#ifndef HEADER_SSL_H +#define HEADER_SSL_H + +#include + +#ifndef OPENSSL_NO_COMP +#include +#endif +#ifndef OPENSSL_NO_BIO +#include +#endif +#ifndef OPENSSL_NO_DEPRECATED +#ifndef OPENSSL_NO_X509 +#include +#endif +#include +#include +#include +#endif +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* SSLeay version number for ASN.1 encoding of the session information */ +/* Version 0 - initial version + * Version 1 - added the optional peer certificate + */ +#define SSL_SESSION_ASN1_VERSION 0x0001 + +/* text strings for the ciphers */ +#define SSL_TXT_NULL_WITH_MD5 SSL2_TXT_NULL_WITH_MD5 +#define SSL_TXT_RC4_128_WITH_MD5 SSL2_TXT_RC4_128_WITH_MD5 +#define SSL_TXT_RC4_128_EXPORT40_WITH_MD5 SSL2_TXT_RC4_128_EXPORT40_WITH_MD5 +#define SSL_TXT_RC2_128_CBC_WITH_MD5 SSL2_TXT_RC2_128_CBC_WITH_MD5 +#define SSL_TXT_RC2_128_CBC_EXPORT40_WITH_MD5 SSL2_TXT_RC2_128_CBC_EXPORT40_WITH_MD5 +#define SSL_TXT_IDEA_128_CBC_WITH_MD5 SSL2_TXT_IDEA_128_CBC_WITH_MD5 +#define SSL_TXT_DES_64_CBC_WITH_MD5 SSL2_TXT_DES_64_CBC_WITH_MD5 +#define SSL_TXT_DES_64_CBC_WITH_SHA SSL2_TXT_DES_64_CBC_WITH_SHA +#define SSL_TXT_DES_192_EDE3_CBC_WITH_MD5 SSL2_TXT_DES_192_EDE3_CBC_WITH_MD5 +#define SSL_TXT_DES_192_EDE3_CBC_WITH_SHA SSL2_TXT_DES_192_EDE3_CBC_WITH_SHA + +/* VRS Additional Kerberos5 entries + */ +#define SSL_TXT_KRB5_DES_64_CBC_SHA SSL3_TXT_KRB5_DES_64_CBC_SHA +#define SSL_TXT_KRB5_DES_192_CBC3_SHA SSL3_TXT_KRB5_DES_192_CBC3_SHA +#define SSL_TXT_KRB5_RC4_128_SHA SSL3_TXT_KRB5_RC4_128_SHA +#define SSL_TXT_KRB5_IDEA_128_CBC_SHA SSL3_TXT_KRB5_IDEA_128_CBC_SHA +#define SSL_TXT_KRB5_DES_64_CBC_MD5 SSL3_TXT_KRB5_DES_64_CBC_MD5 +#define SSL_TXT_KRB5_DES_192_CBC3_MD5 SSL3_TXT_KRB5_DES_192_CBC3_MD5 +#define SSL_TXT_KRB5_RC4_128_MD5 SSL3_TXT_KRB5_RC4_128_MD5 +#define SSL_TXT_KRB5_IDEA_128_CBC_MD5 SSL3_TXT_KRB5_IDEA_128_CBC_MD5 + +#define SSL_TXT_KRB5_DES_40_CBC_SHA SSL3_TXT_KRB5_DES_40_CBC_SHA +#define SSL_TXT_KRB5_RC2_40_CBC_SHA SSL3_TXT_KRB5_RC2_40_CBC_SHA +#define SSL_TXT_KRB5_RC4_40_SHA SSL3_TXT_KRB5_RC4_40_SHA +#define SSL_TXT_KRB5_DES_40_CBC_MD5 SSL3_TXT_KRB5_DES_40_CBC_MD5 +#define SSL_TXT_KRB5_RC2_40_CBC_MD5 SSL3_TXT_KRB5_RC2_40_CBC_MD5 +#define SSL_TXT_KRB5_RC4_40_MD5 SSL3_TXT_KRB5_RC4_40_MD5 + +#define SSL_TXT_KRB5_DES_40_CBC_SHA SSL3_TXT_KRB5_DES_40_CBC_SHA +#define SSL_TXT_KRB5_DES_40_CBC_MD5 SSL3_TXT_KRB5_DES_40_CBC_MD5 +#define SSL_TXT_KRB5_DES_64_CBC_SHA SSL3_TXT_KRB5_DES_64_CBC_SHA +#define SSL_TXT_KRB5_DES_64_CBC_MD5 SSL3_TXT_KRB5_DES_64_CBC_MD5 +#define SSL_TXT_KRB5_DES_192_CBC3_SHA SSL3_TXT_KRB5_DES_192_CBC3_SHA +#define SSL_TXT_KRB5_DES_192_CBC3_MD5 SSL3_TXT_KRB5_DES_192_CBC3_MD5 +#define SSL_MAX_KRB5_PRINCIPAL_LENGTH 256 + +#define SSL_MAX_SSL_SESSION_ID_LENGTH 32 +#define SSL_MAX_SID_CTX_LENGTH 32 + +#define SSL_MIN_RSA_MODULUS_LENGTH_IN_BYTES (512/8) +#define SSL_MAX_KEY_ARG_LENGTH 8 +#define SSL_MAX_MASTER_KEY_LENGTH 48 + +/* These are used to specify which ciphers to use and not to use */ +#define SSL_TXT_LOW "LOW" +#define SSL_TXT_MEDIUM "MEDIUM" +#define SSL_TXT_HIGH "HIGH" +#define SSL_TXT_kFZA "kFZA" +#define SSL_TXT_aFZA "aFZA" +#define SSL_TXT_eFZA "eFZA" +#define SSL_TXT_FZA "FZA" + +#define SSL_TXT_aNULL "aNULL" +#define SSL_TXT_eNULL "eNULL" +#define SSL_TXT_NULL "NULL" + +#define SSL_TXT_kKRB5 "kKRB5" +#define SSL_TXT_aKRB5 "aKRB5" +#define SSL_TXT_KRB5 "KRB5" + +#define SSL_TXT_kRSA "kRSA" +#define SSL_TXT_kDHr "kDHr" +#define SSL_TXT_kDHd "kDHd" +#define SSL_TXT_kEDH "kEDH" +#define SSL_TXT_aRSA "aRSA" +#define SSL_TXT_aDSS "aDSS" +#define SSL_TXT_aDH "aDH" +#define SSL_TXT_DSS "DSS" +#define SSL_TXT_DH "DH" +#define SSL_TXT_EDH "EDH" +#define SSL_TXT_ADH "ADH" +#define SSL_TXT_RSA "RSA" +#define SSL_TXT_DES "DES" +#define SSL_TXT_3DES "3DES" +#define SSL_TXT_RC4 "RC4" +#define SSL_TXT_RC2 "RC2" +#define SSL_TXT_IDEA "IDEA" +#define SSL_TXT_AES "AES" +#define SSL_TXT_MD5 "MD5" +#define SSL_TXT_SHA1 "SHA1" +#define SSL_TXT_SHA "SHA" +#define SSL_TXT_EXP "EXP" +#define SSL_TXT_EXPORT "EXPORT" +#define SSL_TXT_EXP40 "EXPORT40" +#define SSL_TXT_EXP56 "EXPORT56" +#define SSL_TXT_SSLV2 "SSLv2" +#define SSL_TXT_SSLV3 "SSLv3" +#define SSL_TXT_TLSV1 "TLSv1" +#define SSL_TXT_ALL "ALL" +#define SSL_TXT_ECC "ECCdraft" /* ECC ciphersuites are not yet official */ + +/* + * COMPLEMENTOF* definitions. These identifiers are used to (de-select) + * ciphers normally not being used. + * Example: "RC4" will activate all ciphers using RC4 including ciphers + * without authentication, which would normally disabled by DEFAULT (due + * the "!ADH" being part of default). Therefore "RC4:!COMPLEMENTOFDEFAULT" + * will make sure that it is also disabled in the specific selection. + * COMPLEMENTOF* identifiers are portable between version, as adjustments + * to the default cipher setup will also be included here. + * + * COMPLEMENTOFDEFAULT does not experience the same special treatment that + * DEFAULT gets, as only selection is being done and no sorting as needed + * for DEFAULT. + */ +#define SSL_TXT_CMPALL "COMPLEMENTOFALL" +#define SSL_TXT_CMPDEF "COMPLEMENTOFDEFAULT" + +/* The following cipher list is used by default. + * It also is substituted when an application-defined cipher list string + * starts with 'DEFAULT'. */ +#define SSL_DEFAULT_CIPHER_LIST "ALL:!ADH:+RC4:@STRENGTH" /* low priority for RC4 */ + +/* Used in SSL_set_shutdown()/SSL_get_shutdown(); */ +#define SSL_SENT_SHUTDOWN 1 +#define SSL_RECEIVED_SHUTDOWN 2 + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if (defined(OPENSSL_NO_RSA) || defined(OPENSSL_NO_MD5)) && !defined(OPENSSL_NO_SSL2) +#define OPENSSL_NO_SSL2 +#endif + +#define SSL_FILETYPE_ASN1 X509_FILETYPE_ASN1 +#define SSL_FILETYPE_PEM X509_FILETYPE_PEM + +/* This is needed to stop compilers complaining about the + * 'struct ssl_st *' function parameters used to prototype callbacks + * in SSL_CTX. */ +typedef struct ssl_st *ssl_crock_st; + +/* used to hold info on the particular ciphers used */ +typedef struct ssl_cipher_st + { + int valid; + const char *name; /* text name */ + unsigned long id; /* id, 4 bytes, first is version */ + unsigned long algorithms; /* what ciphers are used */ + unsigned long algo_strength; /* strength and export flags */ + unsigned long algorithm2; /* Extra flags */ + int strength_bits; /* Number of bits really used */ + int alg_bits; /* Number of bits for algorithm */ + unsigned long mask; /* used for matching */ + unsigned long mask_strength; /* also used for matching */ + } SSL_CIPHER; + +DECLARE_STACK_OF(SSL_CIPHER) + +typedef struct ssl_st SSL; +typedef struct ssl_ctx_st SSL_CTX; + +/* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ +typedef struct ssl_method_st + { + int version; + int (*ssl_new)(SSL *s); + void (*ssl_clear)(SSL *s); + void (*ssl_free)(SSL *s); + int (*ssl_accept)(SSL *s); + int (*ssl_connect)(SSL *s); + int (*ssl_read)(SSL *s,void *buf,int len); + int (*ssl_peek)(SSL *s,void *buf,int len); + int (*ssl_write)(SSL *s,const void *buf,int len); + int (*ssl_shutdown)(SSL *s); + int (*ssl_renegotiate)(SSL *s); + int (*ssl_renegotiate_check)(SSL *s); + long (*ssl_get_message)(SSL *s, int st1, int stn, int mt, long + max, int *ok); + int (*ssl_read_bytes)(SSL *s, int type, unsigned char *buf, int len, + int peek); + int (*ssl_write_bytes)(SSL *s, int type, const void *buf_, int len); + int (*ssl_dispatch_alert)(SSL *s); + long (*ssl_ctrl)(SSL *s,int cmd,long larg,void *parg); + long (*ssl_ctx_ctrl)(SSL_CTX *ctx,int cmd,long larg,void *parg); + SSL_CIPHER *(*get_cipher_by_char)(const unsigned char *ptr); + int (*put_cipher_by_char)(const SSL_CIPHER *cipher,unsigned char *ptr); + int (*ssl_pending)(const SSL *s); + int (*num_ciphers)(void); + SSL_CIPHER *(*get_cipher)(unsigned ncipher); + struct ssl_method_st *(*get_ssl_method)(int version); + long (*get_timeout)(void); + struct ssl3_enc_method *ssl3_enc; /* Extra SSLv3/TLS stuff */ + int (*ssl_version)(void); + long (*ssl_callback_ctrl)(SSL *s, int cb_id, void (*fp)(void)); + long (*ssl_ctx_callback_ctrl)(SSL_CTX *s, int cb_id, void (*fp)(void)); + } SSL_METHOD; + +/* Lets make this into an ASN.1 type structure as follows + * SSL_SESSION_ID ::= SEQUENCE { + * version INTEGER, -- structure version number + * SSLversion INTEGER, -- SSL version number + * Cipher OCTET_STRING, -- the 3 byte cipher ID + * Session_ID OCTET_STRING, -- the Session ID + * Master_key OCTET_STRING, -- the master key + * KRB5_principal OCTET_STRING -- optional Kerberos principal + * Key_Arg [ 0 ] IMPLICIT OCTET_STRING, -- the optional Key argument + * Time [ 1 ] EXPLICIT INTEGER, -- optional Start Time + * Timeout [ 2 ] EXPLICIT INTEGER, -- optional Timeout ins seconds + * Peer [ 3 ] EXPLICIT X509, -- optional Peer Certificate + * Session_ID_context [ 4 ] EXPLICIT OCTET_STRING, -- the Session ID context + * Verify_result [ 5 ] EXPLICIT INTEGER -- X509_V_... code for `Peer' + * Compression [6] IMPLICIT ASN1_OBJECT -- compression OID XXXXX + * } + * Look in ssl/ssl_asn1.c for more details + * I'm using EXPLICIT tags so I can read the damn things using asn1parse :-). + */ +typedef struct ssl_session_st + { + int ssl_version; /* what ssl version session info is + * being kept in here? */ + + /* only really used in SSLv2 */ + unsigned int key_arg_length; + unsigned char key_arg[SSL_MAX_KEY_ARG_LENGTH]; + int master_key_length; + unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH]; + /* session_id - valid? */ + unsigned int session_id_length; + unsigned char session_id[SSL_MAX_SSL_SESSION_ID_LENGTH]; + /* this is used to determine whether the session is being reused in + * the appropriate context. It is up to the application to set this, + * via SSL_new */ + unsigned int sid_ctx_length; + unsigned char sid_ctx[SSL_MAX_SID_CTX_LENGTH]; + +#ifndef OPENSSL_NO_KRB5 + unsigned int krb5_client_princ_len; + unsigned char krb5_client_princ[SSL_MAX_KRB5_PRINCIPAL_LENGTH]; +#endif /* OPENSSL_NO_KRB5 */ + + int not_resumable; + + /* The cert is the certificate used to establish this connection */ + struct sess_cert_st /* SESS_CERT */ *sess_cert; + + /* This is the cert for the other end. + * On clients, it will be the same as sess_cert->peer_key->x509 + * (the latter is not enough as sess_cert is not retained + * in the external representation of sessions, see ssl_asn1.c). */ + X509 *peer; + /* when app_verify_callback accepts a session where the peer's certificate + * is not ok, we must remember the error for session reuse: */ + long verify_result; /* only for servers */ + + int references; + long timeout; + long time; + + int compress_meth; /* Need to lookup the method */ + + SSL_CIPHER *cipher; + unsigned long cipher_id; /* when ASN.1 loaded, this + * needs to be used to load + * the 'cipher' structure */ + + STACK_OF(SSL_CIPHER) *ciphers; /* shared ciphers? */ + + CRYPTO_EX_DATA ex_data; /* application specific data */ + + /* These are used to make removal of session-ids more + * efficient and to implement a maximum cache size. */ + struct ssl_session_st *prev,*next; + } SSL_SESSION; + + +#define SSL_OP_MICROSOFT_SESS_ID_BUG 0x00000001L +#define SSL_OP_NETSCAPE_CHALLENGE_BUG 0x00000002L +#define SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG 0x00000008L +#define SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG 0x00000010L +#define SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER 0x00000020L +#define SSL_OP_MSIE_SSLV2_RSA_PADDING 0x00000040L /* no effect since 0.9.7h and 0.9.8b */ +#define SSL_OP_SSLEAY_080_CLIENT_DH_BUG 0x00000080L +#define SSL_OP_TLS_D5_BUG 0x00000100L +#define SSL_OP_TLS_BLOCK_PADDING_BUG 0x00000200L + +/* Disable SSL 3.0/TLS 1.0 CBC vulnerability workaround that was added + * in OpenSSL 0.9.6d. Usually (depending on the application protocol) + * the workaround is not needed. Unfortunately some broken SSL/TLS + * implementations cannot handle it at all, which is why we include + * it in SSL_OP_ALL. */ +#define SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS 0x00000800L /* added in 0.9.6e */ + +/* SSL_OP_ALL: various bug workarounds that should be rather harmless. + * This used to be 0x000FFFFFL before 0.9.7. */ +#define SSL_OP_ALL 0x00000FFFL + +/* DTLS options */ +#define SSL_OP_NO_QUERY_MTU 0x00001000L +/* Turn on Cookie Exchange (on relevant for servers) */ +#define SSL_OP_COOKIE_EXCHANGE 0x00002000L + +/* As server, disallow session resumption on renegotiation */ +#define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION 0x00010000L +/* If set, always create a new key when using tmp_ecdh parameters */ +#define SSL_OP_SINGLE_ECDH_USE 0x00080000L +/* If set, always create a new key when using tmp_dh parameters */ +#define SSL_OP_SINGLE_DH_USE 0x00100000L +/* Set to always use the tmp_rsa key when doing RSA operations, + * even when this violates protocol specs */ +#define SSL_OP_EPHEMERAL_RSA 0x00200000L +/* Set on servers to choose the cipher according to the server's + * preferences */ +#define SSL_OP_CIPHER_SERVER_PREFERENCE 0x00400000L +/* If set, a server will allow a client to issue a SSLv3.0 version number + * as latest version supported in the premaster secret, even when TLSv1.0 + * (version 3.1) was announced in the client hello. Normally this is + * forbidden to prevent version rollback attacks. */ +#define SSL_OP_TLS_ROLLBACK_BUG 0x00800000L + +#define SSL_OP_NO_SSLv2 0x01000000L +#define SSL_OP_NO_SSLv3 0x02000000L +#define SSL_OP_NO_TLSv1 0x04000000L + +/* The next flag deliberately changes the ciphertest, this is a check + * for the PKCS#1 attack */ +#define SSL_OP_PKCS1_CHECK_1 0x08000000L +#define SSL_OP_PKCS1_CHECK_2 0x10000000L +#define SSL_OP_NETSCAPE_CA_DN_BUG 0x20000000L +#define SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG 0x40000000L + + +/* Allow SSL_write(..., n) to return r with 0 < r < n (i.e. report success + * when just a single record has been written): */ +#define SSL_MODE_ENABLE_PARTIAL_WRITE 0x00000001L +/* Make it possible to retry SSL_write() with changed buffer location + * (buffer contents must stay the same!); this is not the default to avoid + * the misconception that non-blocking SSL_write() behaves like + * non-blocking write(): */ +#define SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER 0x00000002L +/* Never bother the application with retries if the transport + * is blocking: */ +#define SSL_MODE_AUTO_RETRY 0x00000004L +/* Don't attempt to automatically build certificate chain */ +#define SSL_MODE_NO_AUTO_CHAIN 0x00000008L + + +/* Note: SSL[_CTX]_set_{options,mode} use |= op on the previous value, + * they cannot be used to clear bits. */ + +#define SSL_CTX_set_options(ctx,op) \ + SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL) +#define SSL_CTX_get_options(ctx) \ + SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,0,NULL) +#define SSL_set_options(ssl,op) \ + SSL_ctrl((ssl),SSL_CTRL_OPTIONS,(op),NULL) +#define SSL_get_options(ssl) \ + SSL_ctrl((ssl),SSL_CTRL_OPTIONS,0,NULL) + +#define SSL_CTX_set_mode(ctx,op) \ + SSL_CTX_ctrl((ctx),SSL_CTRL_MODE,(op),NULL) +#define SSL_CTX_get_mode(ctx) \ + SSL_CTX_ctrl((ctx),SSL_CTRL_MODE,0,NULL) +#define SSL_set_mode(ssl,op) \ + SSL_ctrl((ssl),SSL_CTRL_MODE,(op),NULL) +#define SSL_get_mode(ssl) \ + SSL_ctrl((ssl),SSL_CTRL_MODE,0,NULL) +#define SSL_set_mtu(ssl, mtu) \ + SSL_ctrl((ssl),SSL_CTRL_SET_MTU,(mtu),NULL) + + +void SSL_CTX_set_msg_callback(SSL_CTX *ctx, void (*cb)(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg)); +void SSL_set_msg_callback(SSL *ssl, void (*cb)(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg)); +#define SSL_CTX_set_msg_callback_arg(ctx, arg) SSL_CTX_ctrl((ctx), SSL_CTRL_SET_MSG_CALLBACK_ARG, 0, (arg)) +#define SSL_set_msg_callback_arg(ssl, arg) SSL_ctrl((ssl), SSL_CTRL_SET_MSG_CALLBACK_ARG, 0, (arg)) + + + +#if defined(OPENSSL_SYS_MSDOS) && !defined(OPENSSL_SYS_WIN32) +#define SSL_MAX_CERT_LIST_DEFAULT 1024*30 /* 30k max cert list :-) */ +#else +#define SSL_MAX_CERT_LIST_DEFAULT 1024*100 /* 100k max cert list :-) */ +#endif + +#define SSL_SESSION_CACHE_MAX_SIZE_DEFAULT (1024*20) + +/* This callback type is used inside SSL_CTX, SSL, and in the functions that set + * them. It is used to override the generation of SSL/TLS session IDs in a + * server. Return value should be zero on an error, non-zero to proceed. Also, + * callbacks should themselves check if the id they generate is unique otherwise + * the SSL handshake will fail with an error - callbacks can do this using the + * 'ssl' value they're passed by; + * SSL_has_matching_session_id(ssl, id, *id_len) + * The length value passed in is set at the maximum size the session ID can be. + * In SSLv2 this is 16 bytes, whereas SSLv3/TLSv1 it is 32 bytes. The callback + * can alter this length to be less if desired, but under SSLv2 session IDs are + * supposed to be fixed at 16 bytes so the id will be padded after the callback + * returns in this case. It is also an error for the callback to set the size to + * zero. */ +typedef int (*GEN_SESSION_CB)(const SSL *ssl, unsigned char *id, + unsigned int *id_len); + +typedef struct ssl_comp_st + { + int id; + const char *name; +#ifndef OPENSSL_NO_COMP + COMP_METHOD *method; +#else + char *method; +#endif + } SSL_COMP; + +DECLARE_STACK_OF(SSL_COMP) + +struct ssl_ctx_st + { + SSL_METHOD *method; + + STACK_OF(SSL_CIPHER) *cipher_list; + /* same as above but sorted for lookup */ + STACK_OF(SSL_CIPHER) *cipher_list_by_id; + + struct x509_store_st /* X509_STORE */ *cert_store; + struct lhash_st /* LHASH */ *sessions; /* a set of SSL_SESSIONs */ + /* Most session-ids that will be cached, default is + * SSL_SESSION_CACHE_MAX_SIZE_DEFAULT. 0 is unlimited. */ + unsigned long session_cache_size; + struct ssl_session_st *session_cache_head; + struct ssl_session_st *session_cache_tail; + + /* This can have one of 2 values, ored together, + * SSL_SESS_CACHE_CLIENT, + * SSL_SESS_CACHE_SERVER, + * Default is SSL_SESSION_CACHE_SERVER, which means only + * SSL_accept which cache SSL_SESSIONS. */ + int session_cache_mode; + + /* If timeout is not 0, it is the default timeout value set + * when SSL_new() is called. This has been put in to make + * life easier to set things up */ + long session_timeout; + + /* If this callback is not null, it will be called each + * time a session id is added to the cache. If this function + * returns 1, it means that the callback will do a + * SSL_SESSION_free() when it has finished using it. Otherwise, + * on 0, it means the callback has finished with it. + * If remove_session_cb is not null, it will be called when + * a session-id is removed from the cache. After the call, + * OpenSSL will SSL_SESSION_free() it. */ + int (*new_session_cb)(struct ssl_st *ssl,SSL_SESSION *sess); + void (*remove_session_cb)(struct ssl_ctx_st *ctx,SSL_SESSION *sess); + SSL_SESSION *(*get_session_cb)(struct ssl_st *ssl, + unsigned char *data,int len,int *copy); + + struct + { + int sess_connect; /* SSL new conn - started */ + int sess_connect_renegotiate;/* SSL reneg - requested */ + int sess_connect_good; /* SSL new conne/reneg - finished */ + int sess_accept; /* SSL new accept - started */ + int sess_accept_renegotiate;/* SSL reneg - requested */ + int sess_accept_good; /* SSL accept/reneg - finished */ + int sess_miss; /* session lookup misses */ + int sess_timeout; /* reuse attempt on timeouted session */ + int sess_cache_full; /* session removed due to full cache */ + int sess_hit; /* session reuse actually done */ + int sess_cb_hit; /* session-id that was not + * in the cache was + * passed back via the callback. This + * indicates that the application is + * supplying session-id's from other + * processes - spooky :-) */ + } stats; + + int references; + + /* if defined, these override the X509_verify_cert() calls */ + int (*app_verify_callback)(X509_STORE_CTX *, void *); + void *app_verify_arg; + /* before OpenSSL 0.9.7, 'app_verify_arg' was ignored + * ('app_verify_callback' was called with just one argument) */ + + /* Default password callback. */ + pem_password_cb *default_passwd_callback; + + /* Default password callback user data. */ + void *default_passwd_callback_userdata; + + /* get client cert callback */ + int (*client_cert_cb)(SSL *ssl, X509 **x509, EVP_PKEY **pkey); + + /* cookie generate callback */ + int (*app_gen_cookie_cb)(SSL *ssl, unsigned char *cookie, + unsigned int *cookie_len); + + /* verify cookie callback */ + int (*app_verify_cookie_cb)(SSL *ssl, unsigned char *cookie, + unsigned int cookie_len); + + CRYPTO_EX_DATA ex_data; + + const EVP_MD *rsa_md5;/* For SSLv2 - name is 'ssl2-md5' */ + const EVP_MD *md5; /* For SSLv3/TLSv1 'ssl3-md5' */ + const EVP_MD *sha1; /* For SSLv3/TLSv1 'ssl3->sha1' */ + + STACK_OF(X509) *extra_certs; + STACK_OF(SSL_COMP) *comp_methods; /* stack of SSL_COMP, SSLv3/TLSv1 */ + + + /* Default values used when no per-SSL value is defined follow */ + + void (*info_callback)(const SSL *ssl,int type,int val); /* used if SSL's info_callback is NULL */ + + /* what we put in client cert requests */ + STACK_OF(X509_NAME) *client_CA; + + + /* Default values to use in SSL structures follow (these are copied by SSL_new) */ + + unsigned long options; + unsigned long mode; + long max_cert_list; + + struct cert_st /* CERT */ *cert; + int read_ahead; + + /* callback that allows applications to peek at protocol messages */ + void (*msg_callback)(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg); + void *msg_callback_arg; + + int verify_mode; + unsigned int sid_ctx_length; + unsigned char sid_ctx[SSL_MAX_SID_CTX_LENGTH]; + int (*default_verify_callback)(int ok,X509_STORE_CTX *ctx); /* called 'verify_callback' in the SSL */ + + /* Default generate session ID callback. */ + GEN_SESSION_CB generate_session_id; + + X509_VERIFY_PARAM *param; + +#if 0 + int purpose; /* Purpose setting */ + int trust; /* Trust setting */ +#endif + + int quiet_shutdown; + }; + +#define SSL_SESS_CACHE_OFF 0x0000 +#define SSL_SESS_CACHE_CLIENT 0x0001 +#define SSL_SESS_CACHE_SERVER 0x0002 +#define SSL_SESS_CACHE_BOTH (SSL_SESS_CACHE_CLIENT|SSL_SESS_CACHE_SERVER) +#define SSL_SESS_CACHE_NO_AUTO_CLEAR 0x0080 +/* enough comments already ... see SSL_CTX_set_session_cache_mode(3) */ +#define SSL_SESS_CACHE_NO_INTERNAL_LOOKUP 0x0100 +#define SSL_SESS_CACHE_NO_INTERNAL_STORE 0x0200 +#define SSL_SESS_CACHE_NO_INTERNAL \ + (SSL_SESS_CACHE_NO_INTERNAL_LOOKUP|SSL_SESS_CACHE_NO_INTERNAL_STORE) + + struct lhash_st *SSL_CTX_sessions(SSL_CTX *ctx); +#define SSL_CTX_sess_number(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_NUMBER,0,NULL) +#define SSL_CTX_sess_connect(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_CONNECT,0,NULL) +#define SSL_CTX_sess_connect_good(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_CONNECT_GOOD,0,NULL) +#define SSL_CTX_sess_connect_renegotiate(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_CONNECT_RENEGOTIATE,0,NULL) +#define SSL_CTX_sess_accept(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_ACCEPT,0,NULL) +#define SSL_CTX_sess_accept_renegotiate(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_ACCEPT_RENEGOTIATE,0,NULL) +#define SSL_CTX_sess_accept_good(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_ACCEPT_GOOD,0,NULL) +#define SSL_CTX_sess_hits(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_HIT,0,NULL) +#define SSL_CTX_sess_cb_hits(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_CB_HIT,0,NULL) +#define SSL_CTX_sess_misses(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_MISSES,0,NULL) +#define SSL_CTX_sess_timeouts(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_TIMEOUTS,0,NULL) +#define SSL_CTX_sess_cache_full(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SESS_CACHE_FULL,0,NULL) + +#define SSL_CTX_sess_set_new_cb(ctx,cb) ((ctx)->new_session_cb=(cb)) +#define SSL_CTX_sess_get_new_cb(ctx) ((ctx)->new_session_cb) +#define SSL_CTX_sess_set_remove_cb(ctx,cb) ((ctx)->remove_session_cb=(cb)) +#define SSL_CTX_sess_get_remove_cb(ctx) ((ctx)->remove_session_cb) +#define SSL_CTX_sess_set_get_cb(ctx,cb) ((ctx)->get_session_cb=(cb)) +#define SSL_CTX_sess_get_get_cb(ctx) ((ctx)->get_session_cb) +#define SSL_CTX_set_info_callback(ctx,cb) ((ctx)->info_callback=(cb)) +#define SSL_CTX_get_info_callback(ctx) ((ctx)->info_callback) +#define SSL_CTX_set_client_cert_cb(ctx,cb) ((ctx)->client_cert_cb=(cb)) +#define SSL_CTX_get_client_cert_cb(ctx) ((ctx)->client_cert_cb) +#define SSL_CTX_set_cookie_generate_cb(ctx,cb) ((ctx)->app_gen_cookie_cb=(cb)) +#define SSL_CTX_set_cookie_verify_cb(ctx,cb) ((ctx)->app_verify_cookie_cb=(cb)) + +#define SSL_NOTHING 1 +#define SSL_WRITING 2 +#define SSL_READING 3 +#define SSL_X509_LOOKUP 4 + +/* These will only be used when doing non-blocking IO */ +#define SSL_want_nothing(s) (SSL_want(s) == SSL_NOTHING) +#define SSL_want_read(s) (SSL_want(s) == SSL_READING) +#define SSL_want_write(s) (SSL_want(s) == SSL_WRITING) +#define SSL_want_x509_lookup(s) (SSL_want(s) == SSL_X509_LOOKUP) + +struct ssl_st + { + /* protocol version + * (one of SSL2_VERSION, SSL3_VERSION, TLS1_VERSION, DTLS1_VERSION) + */ + int version; + int type; /* SSL_ST_CONNECT or SSL_ST_ACCEPT */ + + SSL_METHOD *method; /* SSLv3 */ + + /* There are 2 BIO's even though they are normally both the + * same. This is so data can be read and written to different + * handlers */ + +#ifndef OPENSSL_NO_BIO + BIO *rbio; /* used by SSL_read */ + BIO *wbio; /* used by SSL_write */ + BIO *bbio; /* used during session-id reuse to concatenate + * messages */ +#else + char *rbio; /* used by SSL_read */ + char *wbio; /* used by SSL_write */ + char *bbio; +#endif + /* This holds a variable that indicates what we were doing + * when a 0 or -1 is returned. This is needed for + * non-blocking IO so we know what request needs re-doing when + * in SSL_accept or SSL_connect */ + int rwstate; + + /* true when we are actually in SSL_accept() or SSL_connect() */ + int in_handshake; + int (*handshake_func)(SSL *); + + /* Imagine that here's a boolean member "init" that is + * switched as soon as SSL_set_{accept/connect}_state + * is called for the first time, so that "state" and + * "handshake_func" are properly initialized. But as + * handshake_func is == 0 until then, we use this + * test instead of an "init" member. + */ + + int server; /* are we the server side? - mostly used by SSL_clear*/ + + int new_session;/* 1 if we are to use a new session. + * 2 if we are a server and are inside a handshake + * (i.e. not just sending a HelloRequest) + * NB: For servers, the 'new' session may actually be a previously + * cached session or even the previous session unless + * SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION is set */ + int quiet_shutdown;/* don't send shutdown packets */ + int shutdown; /* we have shut things down, 0x01 sent, 0x02 + * for received */ + int state; /* where we are */ + int rstate; /* where we are when reading */ + + BUF_MEM *init_buf; /* buffer used during init */ + void *init_msg; /* pointer to handshake message body, set by ssl3_get_message() */ + int init_num; /* amount read/written */ + int init_off; /* amount read/written */ + + /* used internally to point at a raw packet */ + unsigned char *packet; + unsigned int packet_length; + + struct ssl2_state_st *s2; /* SSLv2 variables */ + struct ssl3_state_st *s3; /* SSLv3 variables */ + struct dtls1_state_st *d1; /* DTLSv1 variables */ + + int read_ahead; /* Read as many input bytes as possible + * (for non-blocking reads) */ + + /* callback that allows applications to peek at protocol messages */ + void (*msg_callback)(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg); + void *msg_callback_arg; + + int hit; /* reusing a previous session */ + + X509_VERIFY_PARAM *param; + +#if 0 + int purpose; /* Purpose setting */ + int trust; /* Trust setting */ +#endif + + /* crypto */ + STACK_OF(SSL_CIPHER) *cipher_list; + STACK_OF(SSL_CIPHER) *cipher_list_by_id; + + /* These are the ones being used, the ones in SSL_SESSION are + * the ones to be 'copied' into these ones */ + + EVP_CIPHER_CTX *enc_read_ctx; /* cryptographic state */ + const EVP_MD *read_hash; /* used for mac generation */ +#ifndef OPENSSL_NO_COMP + COMP_CTX *expand; /* uncompress */ +#else + char *expand; +#endif + + EVP_CIPHER_CTX *enc_write_ctx; /* cryptographic state */ + const EVP_MD *write_hash; /* used for mac generation */ +#ifndef OPENSSL_NO_COMP + COMP_CTX *compress; /* compression */ +#else + char *compress; +#endif + + /* session info */ + + /* client cert? */ + /* This is used to hold the server certificate used */ + struct cert_st /* CERT */ *cert; + + /* the session_id_context is used to ensure sessions are only reused + * in the appropriate context */ + unsigned int sid_ctx_length; + unsigned char sid_ctx[SSL_MAX_SID_CTX_LENGTH]; + + /* This can also be in the session once a session is established */ + SSL_SESSION *session; + + /* Default generate session ID callback. */ + GEN_SESSION_CB generate_session_id; + + /* Used in SSL2 and SSL3 */ + int verify_mode; /* 0 don't care about verify failure. + * 1 fail if verify fails */ + int (*verify_callback)(int ok,X509_STORE_CTX *ctx); /* fail if callback returns 0 */ + + void (*info_callback)(const SSL *ssl,int type,int val); /* optional informational callback */ + + int error; /* error bytes to be written */ + int error_code; /* actual code */ + +#ifndef OPENSSL_NO_KRB5 + KSSL_CTX *kssl_ctx; /* Kerberos 5 context */ +#endif /* OPENSSL_NO_KRB5 */ + + SSL_CTX *ctx; + /* set this flag to 1 and a sleep(1) is put into all SSL_read() + * and SSL_write() calls, good for nbio debuging :-) */ + int debug; + + /* extra application data */ + long verify_result; + CRYPTO_EX_DATA ex_data; + + /* for server side, keep the list of CA_dn we can use */ + STACK_OF(X509_NAME) *client_CA; + + int references; + unsigned long options; /* protocol behaviour */ + unsigned long mode; /* API behaviour */ + long max_cert_list; + int first_packet; + int client_version; /* what was passed, used for + * SSLv3/TLS rollback check */ + }; + +#ifdef __cplusplus +} +#endif + +#include +#include +#include /* This is mostly sslv3 with a few tweaks */ +#include /* Datagram TLS */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* compatibility */ +#define SSL_set_app_data(s,arg) (SSL_set_ex_data(s,0,(char *)arg)) +#define SSL_get_app_data(s) (SSL_get_ex_data(s,0)) +#define SSL_SESSION_set_app_data(s,a) (SSL_SESSION_set_ex_data(s,0,(char *)a)) +#define SSL_SESSION_get_app_data(s) (SSL_SESSION_get_ex_data(s,0)) +#define SSL_CTX_get_app_data(ctx) (SSL_CTX_get_ex_data(ctx,0)) +#define SSL_CTX_set_app_data(ctx,arg) (SSL_CTX_set_ex_data(ctx,0,(char *)arg)) + +/* The following are the possible values for ssl->state are are + * used to indicate where we are up to in the SSL connection establishment. + * The macros that follow are about the only things you should need to use + * and even then, only when using non-blocking IO. + * It can also be useful to work out where you were when the connection + * failed */ + +#define SSL_ST_CONNECT 0x1000 +#define SSL_ST_ACCEPT 0x2000 +#define SSL_ST_MASK 0x0FFF +#define SSL_ST_INIT (SSL_ST_CONNECT|SSL_ST_ACCEPT) +#define SSL_ST_BEFORE 0x4000 +#define SSL_ST_OK 0x03 +#define SSL_ST_RENEGOTIATE (0x04|SSL_ST_INIT) + +#define SSL_CB_LOOP 0x01 +#define SSL_CB_EXIT 0x02 +#define SSL_CB_READ 0x04 +#define SSL_CB_WRITE 0x08 +#define SSL_CB_ALERT 0x4000 /* used in callback */ +#define SSL_CB_READ_ALERT (SSL_CB_ALERT|SSL_CB_READ) +#define SSL_CB_WRITE_ALERT (SSL_CB_ALERT|SSL_CB_WRITE) +#define SSL_CB_ACCEPT_LOOP (SSL_ST_ACCEPT|SSL_CB_LOOP) +#define SSL_CB_ACCEPT_EXIT (SSL_ST_ACCEPT|SSL_CB_EXIT) +#define SSL_CB_CONNECT_LOOP (SSL_ST_CONNECT|SSL_CB_LOOP) +#define SSL_CB_CONNECT_EXIT (SSL_ST_CONNECT|SSL_CB_EXIT) +#define SSL_CB_HANDSHAKE_START 0x10 +#define SSL_CB_HANDSHAKE_DONE 0x20 + +/* Is the SSL_connection established? */ +#define SSL_get_state(a) SSL_state(a) +#define SSL_is_init_finished(a) (SSL_state(a) == SSL_ST_OK) +#define SSL_in_init(a) (SSL_state(a)&SSL_ST_INIT) +#define SSL_in_before(a) (SSL_state(a)&SSL_ST_BEFORE) +#define SSL_in_connect_init(a) (SSL_state(a)&SSL_ST_CONNECT) +#define SSL_in_accept_init(a) (SSL_state(a)&SSL_ST_ACCEPT) + +/* The following 2 states are kept in ssl->rstate when reads fail, + * you should not need these */ +#define SSL_ST_READ_HEADER 0xF0 +#define SSL_ST_READ_BODY 0xF1 +#define SSL_ST_READ_DONE 0xF2 + +/* Obtain latest Finished message + * -- that we sent (SSL_get_finished) + * -- that we expected from peer (SSL_get_peer_finished). + * Returns length (0 == no Finished so far), copies up to 'count' bytes. */ +size_t SSL_get_finished(const SSL *s, void *buf, size_t count); +size_t SSL_get_peer_finished(const SSL *s, void *buf, size_t count); + +/* use either SSL_VERIFY_NONE or SSL_VERIFY_PEER, the last 2 options + * are 'ored' with SSL_VERIFY_PEER if they are desired */ +#define SSL_VERIFY_NONE 0x00 +#define SSL_VERIFY_PEER 0x01 +#define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02 +#define SSL_VERIFY_CLIENT_ONCE 0x04 + +#define OpenSSL_add_ssl_algorithms() SSL_library_init() +#define SSLeay_add_ssl_algorithms() SSL_library_init() + +/* this is for backward compatibility */ +#if 0 /* NEW_SSLEAY */ +#define SSL_CTX_set_default_verify(a,b,c) SSL_CTX_set_verify(a,b,c) +#define SSL_set_pref_cipher(c,n) SSL_set_cipher_list(c,n) +#define SSL_add_session(a,b) SSL_CTX_add_session((a),(b)) +#define SSL_remove_session(a,b) SSL_CTX_remove_session((a),(b)) +#define SSL_flush_sessions(a,b) SSL_CTX_flush_sessions((a),(b)) +#endif +/* More backward compatibility */ +#define SSL_get_cipher(s) \ + SSL_CIPHER_get_name(SSL_get_current_cipher(s)) +#define SSL_get_cipher_bits(s,np) \ + SSL_CIPHER_get_bits(SSL_get_current_cipher(s),np) +#define SSL_get_cipher_version(s) \ + SSL_CIPHER_get_version(SSL_get_current_cipher(s)) +#define SSL_get_cipher_name(s) \ + SSL_CIPHER_get_name(SSL_get_current_cipher(s)) +#define SSL_get_time(a) SSL_SESSION_get_time(a) +#define SSL_set_time(a,b) SSL_SESSION_set_time((a),(b)) +#define SSL_get_timeout(a) SSL_SESSION_get_timeout(a) +#define SSL_set_timeout(a,b) SSL_SESSION_set_timeout((a),(b)) + +#if 1 /*SSLEAY_MACROS*/ +#define d2i_SSL_SESSION_bio(bp,s_id) ASN1_d2i_bio_of(SSL_SESSION,SSL_SESSION_new,d2i_SSL_SESSION,bp,s_id) +#define i2d_SSL_SESSION_bio(bp,s_id) ASN1_i2d_bio_of(SSL_SESSION,i2d_SSL_SESSION,bp,s_id) +#define PEM_read_SSL_SESSION(fp,x,cb,u) (SSL_SESSION *)PEM_ASN1_read( \ + (char *(*)())d2i_SSL_SESSION,PEM_STRING_SSL_SESSION,fp,(char **)x,cb,u) +#define PEM_read_bio_SSL_SESSION(bp,x,cb,u) PEM_ASN1_read_bio_of(SSL_SESSION,d2i_SSL_SESSION,PEM_STRING_SSL_SESSION,bp,x,cb,u) +#define PEM_write_SSL_SESSION(fp,x) \ + PEM_ASN1_write((int (*)())i2d_SSL_SESSION, \ + PEM_STRING_SSL_SESSION,fp, (char *)x, NULL,NULL,0,NULL,NULL) +#define PEM_write_bio_SSL_SESSION(bp,x) \ + PEM_ASN1_write_bio_of(SSL_SESSION,i2d_SSL_SESSION,PEM_STRING_SSL_SESSION,bp,x,NULL,NULL,0,NULL,NULL) +#endif + +#define SSL_AD_REASON_OFFSET 1000 +/* These alert types are for SSLv3 and TLSv1 */ +#define SSL_AD_CLOSE_NOTIFY SSL3_AD_CLOSE_NOTIFY +#define SSL_AD_UNEXPECTED_MESSAGE SSL3_AD_UNEXPECTED_MESSAGE /* fatal */ +#define SSL_AD_BAD_RECORD_MAC SSL3_AD_BAD_RECORD_MAC /* fatal */ +#define SSL_AD_DECRYPTION_FAILED TLS1_AD_DECRYPTION_FAILED +#define SSL_AD_RECORD_OVERFLOW TLS1_AD_RECORD_OVERFLOW +#define SSL_AD_DECOMPRESSION_FAILURE SSL3_AD_DECOMPRESSION_FAILURE/* fatal */ +#define SSL_AD_HANDSHAKE_FAILURE SSL3_AD_HANDSHAKE_FAILURE/* fatal */ +#define SSL_AD_NO_CERTIFICATE SSL3_AD_NO_CERTIFICATE /* Not for TLS */ +#define SSL_AD_BAD_CERTIFICATE SSL3_AD_BAD_CERTIFICATE +#define SSL_AD_UNSUPPORTED_CERTIFICATE SSL3_AD_UNSUPPORTED_CERTIFICATE +#define SSL_AD_CERTIFICATE_REVOKED SSL3_AD_CERTIFICATE_REVOKED +#define SSL_AD_CERTIFICATE_EXPIRED SSL3_AD_CERTIFICATE_EXPIRED +#define SSL_AD_CERTIFICATE_UNKNOWN SSL3_AD_CERTIFICATE_UNKNOWN +#define SSL_AD_ILLEGAL_PARAMETER SSL3_AD_ILLEGAL_PARAMETER /* fatal */ +#define SSL_AD_UNKNOWN_CA TLS1_AD_UNKNOWN_CA /* fatal */ +#define SSL_AD_ACCESS_DENIED TLS1_AD_ACCESS_DENIED /* fatal */ +#define SSL_AD_DECODE_ERROR TLS1_AD_DECODE_ERROR /* fatal */ +#define SSL_AD_DECRYPT_ERROR TLS1_AD_DECRYPT_ERROR +#define SSL_AD_EXPORT_RESTRICTION TLS1_AD_EXPORT_RESTRICTION/* fatal */ +#define SSL_AD_PROTOCOL_VERSION TLS1_AD_PROTOCOL_VERSION /* fatal */ +#define SSL_AD_INSUFFICIENT_SECURITY TLS1_AD_INSUFFICIENT_SECURITY/* fatal */ +#define SSL_AD_INTERNAL_ERROR TLS1_AD_INTERNAL_ERROR /* fatal */ +#define SSL_AD_USER_CANCELLED TLS1_AD_USER_CANCELLED +#define SSL_AD_NO_RENEGOTIATION TLS1_AD_NO_RENEGOTIATION + +#define SSL_ERROR_NONE 0 +#define SSL_ERROR_SSL 1 +#define SSL_ERROR_WANT_READ 2 +#define SSL_ERROR_WANT_WRITE 3 +#define SSL_ERROR_WANT_X509_LOOKUP 4 +#define SSL_ERROR_SYSCALL 5 /* look at error stack/return value/errno */ +#define SSL_ERROR_ZERO_RETURN 6 +#define SSL_ERROR_WANT_CONNECT 7 +#define SSL_ERROR_WANT_ACCEPT 8 + +#define SSL_CTRL_NEED_TMP_RSA 1 +#define SSL_CTRL_SET_TMP_RSA 2 +#define SSL_CTRL_SET_TMP_DH 3 +#define SSL_CTRL_SET_TMP_ECDH 4 +#define SSL_CTRL_SET_TMP_RSA_CB 5 +#define SSL_CTRL_SET_TMP_DH_CB 6 +#define SSL_CTRL_SET_TMP_ECDH_CB 7 + +#define SSL_CTRL_GET_SESSION_REUSED 8 +#define SSL_CTRL_GET_CLIENT_CERT_REQUEST 9 +#define SSL_CTRL_GET_NUM_RENEGOTIATIONS 10 +#define SSL_CTRL_CLEAR_NUM_RENEGOTIATIONS 11 +#define SSL_CTRL_GET_TOTAL_RENEGOTIATIONS 12 +#define SSL_CTRL_GET_FLAGS 13 +#define SSL_CTRL_EXTRA_CHAIN_CERT 14 + +#define SSL_CTRL_SET_MSG_CALLBACK 15 +#define SSL_CTRL_SET_MSG_CALLBACK_ARG 16 + +/* only applies to datagram connections */ +#define SSL_CTRL_SET_MTU 17 +/* Stats */ +#define SSL_CTRL_SESS_NUMBER 20 +#define SSL_CTRL_SESS_CONNECT 21 +#define SSL_CTRL_SESS_CONNECT_GOOD 22 +#define SSL_CTRL_SESS_CONNECT_RENEGOTIATE 23 +#define SSL_CTRL_SESS_ACCEPT 24 +#define SSL_CTRL_SESS_ACCEPT_GOOD 25 +#define SSL_CTRL_SESS_ACCEPT_RENEGOTIATE 26 +#define SSL_CTRL_SESS_HIT 27 +#define SSL_CTRL_SESS_CB_HIT 28 +#define SSL_CTRL_SESS_MISSES 29 +#define SSL_CTRL_SESS_TIMEOUTS 30 +#define SSL_CTRL_SESS_CACHE_FULL 31 +#define SSL_CTRL_OPTIONS 32 +#define SSL_CTRL_MODE 33 + +#define SSL_CTRL_GET_READ_AHEAD 40 +#define SSL_CTRL_SET_READ_AHEAD 41 +#define SSL_CTRL_SET_SESS_CACHE_SIZE 42 +#define SSL_CTRL_GET_SESS_CACHE_SIZE 43 +#define SSL_CTRL_SET_SESS_CACHE_MODE 44 +#define SSL_CTRL_GET_SESS_CACHE_MODE 45 + +#define SSL_CTRL_GET_MAX_CERT_LIST 50 +#define SSL_CTRL_SET_MAX_CERT_LIST 51 + +#define SSL_session_reused(ssl) \ + SSL_ctrl((ssl),SSL_CTRL_GET_SESSION_REUSED,0,NULL) +#define SSL_num_renegotiations(ssl) \ + SSL_ctrl((ssl),SSL_CTRL_GET_NUM_RENEGOTIATIONS,0,NULL) +#define SSL_clear_num_renegotiations(ssl) \ + SSL_ctrl((ssl),SSL_CTRL_CLEAR_NUM_RENEGOTIATIONS,0,NULL) +#define SSL_total_renegotiations(ssl) \ + SSL_ctrl((ssl),SSL_CTRL_GET_TOTAL_RENEGOTIATIONS,0,NULL) + +#define SSL_CTX_need_tmp_RSA(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_NEED_TMP_RSA,0,NULL) +#define SSL_CTX_set_tmp_rsa(ctx,rsa) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TMP_RSA,0,(char *)rsa) +#define SSL_CTX_set_tmp_dh(ctx,dh) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TMP_DH,0,(char *)dh) +#define SSL_CTX_set_tmp_ecdh(ctx,ecdh) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TMP_ECDH,0,(char *)ecdh) + +#define SSL_need_tmp_RSA(ssl) \ + SSL_ctrl(ssl,SSL_CTRL_NEED_TMP_RSA,0,NULL) +#define SSL_set_tmp_rsa(ssl,rsa) \ + SSL_ctrl(ssl,SSL_CTRL_SET_TMP_RSA,0,(char *)rsa) +#define SSL_set_tmp_dh(ssl,dh) \ + SSL_ctrl(ssl,SSL_CTRL_SET_TMP_DH,0,(char *)dh) +#define SSL_set_tmp_ecdh(ssl,ecdh) \ + SSL_ctrl(ssl,SSL_CTRL_SET_TMP_ECDH,0,(char *)ecdh) + +#define SSL_CTX_add_extra_chain_cert(ctx,x509) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_EXTRA_CHAIN_CERT,0,(char *)x509) + +#ifndef OPENSSL_NO_BIO +BIO_METHOD *BIO_f_ssl(void); +BIO *BIO_new_ssl(SSL_CTX *ctx,int client); +BIO *BIO_new_ssl_connect(SSL_CTX *ctx); +BIO *BIO_new_buffer_ssl_connect(SSL_CTX *ctx); +int BIO_ssl_copy_session_id(BIO *to,BIO *from); +void BIO_ssl_shutdown(BIO *ssl_bio); + +#endif + +int SSL_CTX_set_cipher_list(SSL_CTX *,const char *str); +SSL_CTX *SSL_CTX_new(SSL_METHOD *meth); +void SSL_CTX_free(SSL_CTX *); +long SSL_CTX_set_timeout(SSL_CTX *ctx,long t); +long SSL_CTX_get_timeout(const SSL_CTX *ctx); +X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *); +void SSL_CTX_set_cert_store(SSL_CTX *,X509_STORE *); +int SSL_want(const SSL *s); +int SSL_clear(SSL *s); + +void SSL_CTX_flush_sessions(SSL_CTX *ctx,long tm); + +SSL_CIPHER *SSL_get_current_cipher(const SSL *s); +int SSL_CIPHER_get_bits(const SSL_CIPHER *c,int *alg_bits); +char * SSL_CIPHER_get_version(const SSL_CIPHER *c); +const char * SSL_CIPHER_get_name(const SSL_CIPHER *c); + +int SSL_get_fd(const SSL *s); +int SSL_get_rfd(const SSL *s); +int SSL_get_wfd(const SSL *s); +const char * SSL_get_cipher_list(const SSL *s,int n); +char * SSL_get_shared_ciphers(const SSL *s, char *buf, int len); +int SSL_get_read_ahead(const SSL * s); +int SSL_pending(const SSL *s); +#ifndef OPENSSL_NO_SOCK +int SSL_set_fd(SSL *s, int fd); +int SSL_set_rfd(SSL *s, int fd); +int SSL_set_wfd(SSL *s, int fd); +#endif +#ifndef OPENSSL_NO_BIO +void SSL_set_bio(SSL *s, BIO *rbio,BIO *wbio); +BIO * SSL_get_rbio(const SSL *s); +BIO * SSL_get_wbio(const SSL *s); +#endif +int SSL_set_cipher_list(SSL *s, const char *str); +void SSL_set_read_ahead(SSL *s, int yes); +int SSL_get_verify_mode(const SSL *s); +int SSL_get_verify_depth(const SSL *s); +int (*SSL_get_verify_callback(const SSL *s))(int,X509_STORE_CTX *); +void SSL_set_verify(SSL *s, int mode, + int (*callback)(int ok,X509_STORE_CTX *ctx)); +void SSL_set_verify_depth(SSL *s, int depth); +#ifndef OPENSSL_NO_RSA +int SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa); +#endif +int SSL_use_RSAPrivateKey_ASN1(SSL *ssl, unsigned char *d, long len); +int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey); +int SSL_use_PrivateKey_ASN1(int pk,SSL *ssl, const unsigned char *d, long len); +int SSL_use_certificate(SSL *ssl, X509 *x); +int SSL_use_certificate_ASN1(SSL *ssl, const unsigned char *d, int len); + +#ifndef OPENSSL_NO_STDIO +int SSL_use_RSAPrivateKey_file(SSL *ssl, const char *file, int type); +int SSL_use_PrivateKey_file(SSL *ssl, const char *file, int type); +int SSL_use_certificate_file(SSL *ssl, const char *file, int type); +int SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type); +int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type); +int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type); +int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file); /* PEM type */ +STACK_OF(X509_NAME) *SSL_load_client_CA_file(const char *file); +int SSL_add_file_cert_subjects_to_stack(STACK_OF(X509_NAME) *stackCAs, + const char *file); +#ifndef OPENSSL_SYS_VMS +#ifndef OPENSSL_SYS_MACINTOSH_CLASSIC /* XXXXX: Better scheme needed! [was: #ifndef MAC_OS_pre_X] */ +int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *stackCAs, + const char *dir); +#endif +#endif + +#endif + +void SSL_load_error_strings(void ); +const char *SSL_state_string(const SSL *s); +const char *SSL_rstate_string(const SSL *s); +const char *SSL_state_string_long(const SSL *s); +const char *SSL_rstate_string_long(const SSL *s); +long SSL_SESSION_get_time(const SSL_SESSION *s); +long SSL_SESSION_set_time(SSL_SESSION *s, long t); +long SSL_SESSION_get_timeout(const SSL_SESSION *s); +long SSL_SESSION_set_timeout(SSL_SESSION *s, long t); +void SSL_copy_session_id(SSL *to,const SSL *from); + +SSL_SESSION *SSL_SESSION_new(void); +unsigned long SSL_SESSION_hash(const SSL_SESSION *a); +int SSL_SESSION_cmp(const SSL_SESSION *a,const SSL_SESSION *b); +const unsigned char *SSL_SESSION_get_id(const SSL_SESSION *s, unsigned int *len); +#ifndef OPENSSL_NO_FP_API +int SSL_SESSION_print_fp(FILE *fp,const SSL_SESSION *ses); +#endif +#ifndef OPENSSL_NO_BIO +int SSL_SESSION_print(BIO *fp,const SSL_SESSION *ses); +#endif +void SSL_SESSION_free(SSL_SESSION *ses); +int i2d_SSL_SESSION(SSL_SESSION *in,unsigned char **pp); +int SSL_set_session(SSL *to, SSL_SESSION *session); +int SSL_CTX_add_session(SSL_CTX *s, SSL_SESSION *c); +int SSL_CTX_remove_session(SSL_CTX *,SSL_SESSION *c); +int SSL_CTX_set_generate_session_id(SSL_CTX *, GEN_SESSION_CB); +int SSL_set_generate_session_id(SSL *, GEN_SESSION_CB); +int SSL_has_matching_session_id(const SSL *ssl, const unsigned char *id, + unsigned int id_len); +SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a,const unsigned char **pp, + long length); + +#ifdef HEADER_X509_H +X509 * SSL_get_peer_certificate(const SSL *s); +#endif + +STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *s); + +int SSL_CTX_get_verify_mode(const SSL_CTX *ctx); +int SSL_CTX_get_verify_depth(const SSL_CTX *ctx); +int (*SSL_CTX_get_verify_callback(const SSL_CTX *ctx))(int,X509_STORE_CTX *); +void SSL_CTX_set_verify(SSL_CTX *ctx,int mode, + int (*callback)(int, X509_STORE_CTX *)); +void SSL_CTX_set_verify_depth(SSL_CTX *ctx,int depth); +void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx, int (*cb)(X509_STORE_CTX *,void *), void *arg); +#ifndef OPENSSL_NO_RSA +int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa); +#endif +int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const unsigned char *d, long len); +int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey); +int SSL_CTX_use_PrivateKey_ASN1(int pk,SSL_CTX *ctx, + const unsigned char *d, long len); +int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x); +int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const unsigned char *d); + +void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb); +void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *u); + +int SSL_CTX_check_private_key(const SSL_CTX *ctx); +int SSL_check_private_key(const SSL *ctx); + +int SSL_CTX_set_session_id_context(SSL_CTX *ctx,const unsigned char *sid_ctx, + unsigned int sid_ctx_len); + +SSL * SSL_new(SSL_CTX *ctx); +int SSL_set_session_id_context(SSL *ssl,const unsigned char *sid_ctx, + unsigned int sid_ctx_len); + +int SSL_CTX_set_purpose(SSL_CTX *s, int purpose); +int SSL_set_purpose(SSL *s, int purpose); +int SSL_CTX_set_trust(SSL_CTX *s, int trust); +int SSL_set_trust(SSL *s, int trust); + +void SSL_free(SSL *ssl); +int SSL_accept(SSL *ssl); +int SSL_connect(SSL *ssl); +int SSL_read(SSL *ssl,void *buf,int num); +int SSL_peek(SSL *ssl,void *buf,int num); +int SSL_write(SSL *ssl,const void *buf,int num); +long SSL_ctrl(SSL *ssl,int cmd, long larg, void *parg); +long SSL_callback_ctrl(SSL *, int, void (*)(void)); +long SSL_CTX_ctrl(SSL_CTX *ctx,int cmd, long larg, void *parg); +long SSL_CTX_callback_ctrl(SSL_CTX *, int, void (*)(void)); + +int SSL_get_error(const SSL *s,int ret_code); +const char *SSL_get_version(const SSL *s); + +/* This sets the 'default' SSL version that SSL_new() will create */ +int SSL_CTX_set_ssl_version(SSL_CTX *ctx,SSL_METHOD *meth); + +SSL_METHOD *SSLv2_method(void); /* SSLv2 */ +SSL_METHOD *SSLv2_server_method(void); /* SSLv2 */ +SSL_METHOD *SSLv2_client_method(void); /* SSLv2 */ + +SSL_METHOD *SSLv3_method(void); /* SSLv3 */ +SSL_METHOD *SSLv3_server_method(void); /* SSLv3 */ +SSL_METHOD *SSLv3_client_method(void); /* SSLv3 */ + +SSL_METHOD *SSLv23_method(void); /* SSLv3 but can rollback to v2 */ +SSL_METHOD *SSLv23_server_method(void); /* SSLv3 but can rollback to v2 */ +SSL_METHOD *SSLv23_client_method(void); /* SSLv3 but can rollback to v2 */ + +SSL_METHOD *TLSv1_method(void); /* TLSv1.0 */ +SSL_METHOD *TLSv1_server_method(void); /* TLSv1.0 */ +SSL_METHOD *TLSv1_client_method(void); /* TLSv1.0 */ + +SSL_METHOD *DTLSv1_method(void); /* DTLSv1.0 */ +SSL_METHOD *DTLSv1_server_method(void); /* DTLSv1.0 */ +SSL_METHOD *DTLSv1_client_method(void); /* DTLSv1.0 */ + +STACK_OF(SSL_CIPHER) *SSL_get_ciphers(const SSL *s); + +int SSL_do_handshake(SSL *s); +int SSL_renegotiate(SSL *s); +int SSL_renegotiate_pending(SSL *s); +int SSL_shutdown(SSL *s); + +SSL_METHOD *SSL_get_ssl_method(SSL *s); +int SSL_set_ssl_method(SSL *s,SSL_METHOD *method); +const char *SSL_alert_type_string_long(int value); +const char *SSL_alert_type_string(int value); +const char *SSL_alert_desc_string_long(int value); +const char *SSL_alert_desc_string(int value); + +void SSL_set_client_CA_list(SSL *s, STACK_OF(X509_NAME) *name_list); +void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list); +STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *s); +STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *s); +int SSL_add_client_CA(SSL *ssl,X509 *x); +int SSL_CTX_add_client_CA(SSL_CTX *ctx,X509 *x); + +void SSL_set_connect_state(SSL *s); +void SSL_set_accept_state(SSL *s); + +long SSL_get_default_timeout(const SSL *s); + +int SSL_library_init(void ); + +char *SSL_CIPHER_description(SSL_CIPHER *,char *buf,int size); +STACK_OF(X509_NAME) *SSL_dup_CA_list(STACK_OF(X509_NAME) *sk); + +SSL *SSL_dup(SSL *ssl); + +X509 *SSL_get_certificate(const SSL *ssl); +/* EVP_PKEY */ struct evp_pkey_st *SSL_get_privatekey(SSL *ssl); + +void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx,int mode); +int SSL_CTX_get_quiet_shutdown(const SSL_CTX *ctx); +void SSL_set_quiet_shutdown(SSL *ssl,int mode); +int SSL_get_quiet_shutdown(const SSL *ssl); +void SSL_set_shutdown(SSL *ssl,int mode); +int SSL_get_shutdown(const SSL *ssl); +int SSL_version(const SSL *ssl); +int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx); +int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, + const char *CApath); +#define SSL_get0_session SSL_get_session /* just peek at pointer */ +SSL_SESSION *SSL_get_session(const SSL *ssl); +SSL_SESSION *SSL_get1_session(SSL *ssl); /* obtain a reference count */ +SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl); +void SSL_set_info_callback(SSL *ssl, + void (*cb)(const SSL *ssl,int type,int val)); +void (*SSL_get_info_callback(const SSL *ssl))(const SSL *ssl,int type,int val); +int SSL_state(const SSL *ssl); + +void SSL_set_verify_result(SSL *ssl,long v); +long SSL_get_verify_result(const SSL *ssl); + +int SSL_set_ex_data(SSL *ssl,int idx,void *data); +void *SSL_get_ex_data(const SSL *ssl,int idx); +int SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, + CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); + +int SSL_SESSION_set_ex_data(SSL_SESSION *ss,int idx,void *data); +void *SSL_SESSION_get_ex_data(const SSL_SESSION *ss,int idx); +int SSL_SESSION_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, + CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); + +int SSL_CTX_set_ex_data(SSL_CTX *ssl,int idx,void *data); +void *SSL_CTX_get_ex_data(const SSL_CTX *ssl,int idx); +int SSL_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, + CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); + +int SSL_get_ex_data_X509_STORE_CTX_idx(void ); + +#define SSL_CTX_sess_set_cache_size(ctx,t) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_SESS_CACHE_SIZE,t,NULL) +#define SSL_CTX_sess_get_cache_size(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_GET_SESS_CACHE_SIZE,0,NULL) +#define SSL_CTX_set_session_cache_mode(ctx,m) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_SESS_CACHE_MODE,m,NULL) +#define SSL_CTX_get_session_cache_mode(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_GET_SESS_CACHE_MODE,0,NULL) + +#define SSL_CTX_get_default_read_ahead(ctx) SSL_CTX_get_read_ahead(ctx) +#define SSL_CTX_set_default_read_ahead(ctx,m) SSL_CTX_set_read_ahead(ctx,m) +#define SSL_CTX_get_read_ahead(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_GET_READ_AHEAD,0,NULL) +#define SSL_CTX_set_read_ahead(ctx,m) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_READ_AHEAD,m,NULL) +#define SSL_CTX_get_max_cert_list(ctx) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_GET_MAX_CERT_LIST,0,NULL) +#define SSL_CTX_set_max_cert_list(ctx,m) \ + SSL_CTX_ctrl(ctx,SSL_CTRL_SET_MAX_CERT_LIST,m,NULL) +#define SSL_get_max_cert_list(ssl) \ + SSL_ctrl(ssl,SSL_CTRL_GET_MAX_CERT_LIST,0,NULL) +#define SSL_set_max_cert_list(ssl,m) \ + SSL_ctrl(ssl,SSL_CTRL_SET_MAX_CERT_LIST,m,NULL) + + /* NB: the keylength is only applicable when is_export is true */ +#ifndef OPENSSL_NO_RSA +void SSL_CTX_set_tmp_rsa_callback(SSL_CTX *ctx, + RSA *(*cb)(SSL *ssl,int is_export, + int keylength)); + +void SSL_set_tmp_rsa_callback(SSL *ssl, + RSA *(*cb)(SSL *ssl,int is_export, + int keylength)); +#endif +#ifndef OPENSSL_NO_DH +void SSL_CTX_set_tmp_dh_callback(SSL_CTX *ctx, + DH *(*dh)(SSL *ssl,int is_export, + int keylength)); +void SSL_set_tmp_dh_callback(SSL *ssl, + DH *(*dh)(SSL *ssl,int is_export, + int keylength)); +#endif +#ifndef OPENSSL_NO_ECDH +void SSL_CTX_set_tmp_ecdh_callback(SSL_CTX *ctx, + EC_KEY *(*ecdh)(SSL *ssl,int is_export, + int keylength)); +void SSL_set_tmp_ecdh_callback(SSL *ssl, + EC_KEY *(*ecdh)(SSL *ssl,int is_export, + int keylength)); +#endif + +#ifndef OPENSSL_NO_COMP +const COMP_METHOD *SSL_get_current_compression(SSL *s); +const COMP_METHOD *SSL_get_current_expansion(SSL *s); +const char *SSL_COMP_get_name(const COMP_METHOD *comp); +STACK_OF(SSL_COMP) *SSL_COMP_get_compression_methods(void); +int SSL_COMP_add_compression_method(int id,COMP_METHOD *cm); +#else +const void *SSL_get_current_compression(SSL *s); +const void *SSL_get_current_expansion(SSL *s); +const char *SSL_COMP_get_name(const void *comp); +void *SSL_COMP_get_compression_methods(void); +int SSL_COMP_add_compression_method(int id,void *cm); +#endif + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_SSL_strings(void); + +/* Error codes for the SSL functions. */ + +/* Function codes. */ +#define SSL_F_CLIENT_CERTIFICATE 100 +#define SSL_F_CLIENT_FINISHED 167 +#define SSL_F_CLIENT_HELLO 101 +#define SSL_F_CLIENT_MASTER_KEY 102 +#define SSL_F_D2I_SSL_SESSION 103 +#define SSL_F_DO_DTLS1_WRITE 245 +#define SSL_F_DO_SSL3_WRITE 104 +#define SSL_F_DTLS1_ACCEPT 246 +#define SSL_F_DTLS1_BUFFER_RECORD 247 +#define SSL_F_DTLS1_CLIENT_HELLO 248 +#define SSL_F_DTLS1_CONNECT 249 +#define SSL_F_DTLS1_ENC 250 +#define SSL_F_DTLS1_GET_HELLO_VERIFY 251 +#define SSL_F_DTLS1_GET_MESSAGE 252 +#define SSL_F_DTLS1_GET_MESSAGE_FRAGMENT 253 +#define SSL_F_DTLS1_GET_RECORD 254 +#define SSL_F_DTLS1_OUTPUT_CERT_CHAIN 255 +#define SSL_F_DTLS1_PROCESS_OUT_OF_SEQ_MESSAGE 256 +#define SSL_F_DTLS1_PROCESS_RECORD 257 +#define SSL_F_DTLS1_READ_BYTES 258 +#define SSL_F_DTLS1_READ_FAILED 259 +#define SSL_F_DTLS1_SEND_CERTIFICATE_REQUEST 260 +#define SSL_F_DTLS1_SEND_CLIENT_CERTIFICATE 261 +#define SSL_F_DTLS1_SEND_CLIENT_KEY_EXCHANGE 262 +#define SSL_F_DTLS1_SEND_CLIENT_VERIFY 263 +#define SSL_F_DTLS1_SEND_HELLO_VERIFY_REQUEST 264 +#define SSL_F_DTLS1_SEND_SERVER_CERTIFICATE 265 +#define SSL_F_DTLS1_SEND_SERVER_HELLO 266 +#define SSL_F_DTLS1_SEND_SERVER_KEY_EXCHANGE 267 +#define SSL_F_DTLS1_WRITE_APP_DATA_BYTES 268 +#define SSL_F_GET_CLIENT_FINISHED 105 +#define SSL_F_GET_CLIENT_HELLO 106 +#define SSL_F_GET_CLIENT_MASTER_KEY 107 +#define SSL_F_GET_SERVER_FINISHED 108 +#define SSL_F_GET_SERVER_HELLO 109 +#define SSL_F_GET_SERVER_VERIFY 110 +#define SSL_F_I2D_SSL_SESSION 111 +#define SSL_F_READ_N 112 +#define SSL_F_REQUEST_CERTIFICATE 113 +#define SSL_F_SERVER_FINISH 239 +#define SSL_F_SERVER_HELLO 114 +#define SSL_F_SERVER_VERIFY 240 +#define SSL_F_SSL23_ACCEPT 115 +#define SSL_F_SSL23_CLIENT_HELLO 116 +#define SSL_F_SSL23_CONNECT 117 +#define SSL_F_SSL23_GET_CLIENT_HELLO 118 +#define SSL_F_SSL23_GET_SERVER_HELLO 119 +#define SSL_F_SSL23_PEEK 237 +#define SSL_F_SSL23_READ 120 +#define SSL_F_SSL23_WRITE 121 +#define SSL_F_SSL2_ACCEPT 122 +#define SSL_F_SSL2_CONNECT 123 +#define SSL_F_SSL2_ENC_INIT 124 +#define SSL_F_SSL2_GENERATE_KEY_MATERIAL 241 +#define SSL_F_SSL2_PEEK 234 +#define SSL_F_SSL2_READ 125 +#define SSL_F_SSL2_READ_INTERNAL 236 +#define SSL_F_SSL2_SET_CERTIFICATE 126 +#define SSL_F_SSL2_WRITE 127 +#define SSL_F_SSL3_ACCEPT 128 +#define SSL_F_SSL3_CALLBACK_CTRL 233 +#define SSL_F_SSL3_CHANGE_CIPHER_STATE 129 +#define SSL_F_SSL3_CHECK_CERT_AND_ALGORITHM 130 +#define SSL_F_SSL3_CLIENT_HELLO 131 +#define SSL_F_SSL3_CONNECT 132 +#define SSL_F_SSL3_CTRL 213 +#define SSL_F_SSL3_CTX_CTRL 133 +#define SSL_F_SSL3_ENC 134 +#define SSL_F_SSL3_GENERATE_KEY_BLOCK 238 +#define SSL_F_SSL3_GET_CERTIFICATE_REQUEST 135 +#define SSL_F_SSL3_GET_CERT_VERIFY 136 +#define SSL_F_SSL3_GET_CLIENT_CERTIFICATE 137 +#define SSL_F_SSL3_GET_CLIENT_HELLO 138 +#define SSL_F_SSL3_GET_CLIENT_KEY_EXCHANGE 139 +#define SSL_F_SSL3_GET_FINISHED 140 +#define SSL_F_SSL3_GET_KEY_EXCHANGE 141 +#define SSL_F_SSL3_GET_MESSAGE 142 +#define SSL_F_SSL3_GET_RECORD 143 +#define SSL_F_SSL3_GET_SERVER_CERTIFICATE 144 +#define SSL_F_SSL3_GET_SERVER_DONE 145 +#define SSL_F_SSL3_GET_SERVER_HELLO 146 +#define SSL_F_SSL3_OUTPUT_CERT_CHAIN 147 +#define SSL_F_SSL3_PEEK 235 +#define SSL_F_SSL3_READ_BYTES 148 +#define SSL_F_SSL3_READ_N 149 +#define SSL_F_SSL3_SEND_CERTIFICATE_REQUEST 150 +#define SSL_F_SSL3_SEND_CLIENT_CERTIFICATE 151 +#define SSL_F_SSL3_SEND_CLIENT_KEY_EXCHANGE 152 +#define SSL_F_SSL3_SEND_CLIENT_VERIFY 153 +#define SSL_F_SSL3_SEND_SERVER_CERTIFICATE 154 +#define SSL_F_SSL3_SEND_SERVER_HELLO 242 +#define SSL_F_SSL3_SEND_SERVER_KEY_EXCHANGE 155 +#define SSL_F_SSL3_SETUP_BUFFERS 156 +#define SSL_F_SSL3_SETUP_KEY_BLOCK 157 +#define SSL_F_SSL3_WRITE_BYTES 158 +#define SSL_F_SSL3_WRITE_PENDING 159 +#define SSL_F_SSL_ADD_DIR_CERT_SUBJECTS_TO_STACK 215 +#define SSL_F_SSL_ADD_FILE_CERT_SUBJECTS_TO_STACK 216 +#define SSL_F_SSL_BAD_METHOD 160 +#define SSL_F_SSL_BYTES_TO_CIPHER_LIST 161 +#define SSL_F_SSL_CERT_DUP 221 +#define SSL_F_SSL_CERT_INST 222 +#define SSL_F_SSL_CERT_INSTANTIATE 214 +#define SSL_F_SSL_CERT_NEW 162 +#define SSL_F_SSL_CHECK_PRIVATE_KEY 163 +#define SSL_F_SSL_CIPHER_PROCESS_RULESTR 230 +#define SSL_F_SSL_CIPHER_STRENGTH_SORT 231 +#define SSL_F_SSL_CLEAR 164 +#define SSL_F_SSL_COMP_ADD_COMPRESSION_METHOD 165 +#define SSL_F_SSL_CREATE_CIPHER_LIST 166 +#define SSL_F_SSL_CTRL 232 +#define SSL_F_SSL_CTX_CHECK_PRIVATE_KEY 168 +#define SSL_F_SSL_CTX_NEW 169 +#define SSL_F_SSL_CTX_SET_CIPHER_LIST 269 +#define SSL_F_SSL_CTX_SET_PURPOSE 226 +#define SSL_F_SSL_CTX_SET_SESSION_ID_CONTEXT 219 +#define SSL_F_SSL_CTX_SET_SSL_VERSION 170 +#define SSL_F_SSL_CTX_SET_TRUST 229 +#define SSL_F_SSL_CTX_USE_CERTIFICATE 171 +#define SSL_F_SSL_CTX_USE_CERTIFICATE_ASN1 172 +#define SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE 220 +#define SSL_F_SSL_CTX_USE_CERTIFICATE_FILE 173 +#define SSL_F_SSL_CTX_USE_PRIVATEKEY 174 +#define SSL_F_SSL_CTX_USE_PRIVATEKEY_ASN1 175 +#define SSL_F_SSL_CTX_USE_PRIVATEKEY_FILE 176 +#define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY 177 +#define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_ASN1 178 +#define SSL_F_SSL_CTX_USE_RSAPRIVATEKEY_FILE 179 +#define SSL_F_SSL_DO_HANDSHAKE 180 +#define SSL_F_SSL_GET_NEW_SESSION 181 +#define SSL_F_SSL_GET_PREV_SESSION 217 +#define SSL_F_SSL_GET_SERVER_SEND_CERT 182 +#define SSL_F_SSL_GET_SIGN_PKEY 183 +#define SSL_F_SSL_INIT_WBIO_BUFFER 184 +#define SSL_F_SSL_LOAD_CLIENT_CA_FILE 185 +#define SSL_F_SSL_NEW 186 +#define SSL_F_SSL_PEEK 270 +#define SSL_F_SSL_READ 223 +#define SSL_F_SSL_RSA_PRIVATE_DECRYPT 187 +#define SSL_F_SSL_RSA_PUBLIC_ENCRYPT 188 +#define SSL_F_SSL_SESSION_NEW 189 +#define SSL_F_SSL_SESSION_PRINT_FP 190 +#define SSL_F_SSL_SESS_CERT_NEW 225 +#define SSL_F_SSL_SET_CERT 191 +#define SSL_F_SSL_SET_CIPHER_LIST 271 +#define SSL_F_SSL_SET_FD 192 +#define SSL_F_SSL_SET_PKEY 193 +#define SSL_F_SSL_SET_PURPOSE 227 +#define SSL_F_SSL_SET_RFD 194 +#define SSL_F_SSL_SET_SESSION 195 +#define SSL_F_SSL_SET_SESSION_ID_CONTEXT 218 +#define SSL_F_SSL_SET_TRUST 228 +#define SSL_F_SSL_SET_WFD 196 +#define SSL_F_SSL_SHUTDOWN 224 +#define SSL_F_SSL_UNDEFINED_CONST_FUNCTION 243 +#define SSL_F_SSL_UNDEFINED_FUNCTION 197 +#define SSL_F_SSL_UNDEFINED_VOID_FUNCTION 244 +#define SSL_F_SSL_USE_CERTIFICATE 198 +#define SSL_F_SSL_USE_CERTIFICATE_ASN1 199 +#define SSL_F_SSL_USE_CERTIFICATE_FILE 200 +#define SSL_F_SSL_USE_PRIVATEKEY 201 +#define SSL_F_SSL_USE_PRIVATEKEY_ASN1 202 +#define SSL_F_SSL_USE_PRIVATEKEY_FILE 203 +#define SSL_F_SSL_USE_RSAPRIVATEKEY 204 +#define SSL_F_SSL_USE_RSAPRIVATEKEY_ASN1 205 +#define SSL_F_SSL_USE_RSAPRIVATEKEY_FILE 206 +#define SSL_F_SSL_VERIFY_CERT_CHAIN 207 +#define SSL_F_SSL_WRITE 208 +#define SSL_F_TLS1_CHANGE_CIPHER_STATE 209 +#define SSL_F_TLS1_ENC 210 +#define SSL_F_TLS1_SETUP_KEY_BLOCK 211 +#define SSL_F_WRITE_PENDING 212 + +/* Reason codes. */ +#define SSL_R_APP_DATA_IN_HANDSHAKE 100 +#define SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT 272 +#define SSL_R_BAD_ALERT_RECORD 101 +#define SSL_R_BAD_AUTHENTICATION_TYPE 102 +#define SSL_R_BAD_CHANGE_CIPHER_SPEC 103 +#define SSL_R_BAD_CHECKSUM 104 +#define SSL_R_BAD_DATA_RETURNED_BY_CALLBACK 106 +#define SSL_R_BAD_DECOMPRESSION 107 +#define SSL_R_BAD_DH_G_LENGTH 108 +#define SSL_R_BAD_DH_PUB_KEY_LENGTH 109 +#define SSL_R_BAD_DH_P_LENGTH 110 +#define SSL_R_BAD_DIGEST_LENGTH 111 +#define SSL_R_BAD_DSA_SIGNATURE 112 +#define SSL_R_BAD_ECC_CERT 304 +#define SSL_R_BAD_ECDSA_SIGNATURE 305 +#define SSL_R_BAD_ECPOINT 306 +#define SSL_R_BAD_HELLO_REQUEST 105 +#define SSL_R_BAD_LENGTH 271 +#define SSL_R_BAD_MAC_DECODE 113 +#define SSL_R_BAD_MESSAGE_TYPE 114 +#define SSL_R_BAD_PACKET_LENGTH 115 +#define SSL_R_BAD_PROTOCOL_VERSION_NUMBER 116 +#define SSL_R_BAD_RESPONSE_ARGUMENT 117 +#define SSL_R_BAD_RSA_DECRYPT 118 +#define SSL_R_BAD_RSA_ENCRYPT 119 +#define SSL_R_BAD_RSA_E_LENGTH 120 +#define SSL_R_BAD_RSA_MODULUS_LENGTH 121 +#define SSL_R_BAD_RSA_SIGNATURE 122 +#define SSL_R_BAD_SIGNATURE 123 +#define SSL_R_BAD_SSL_FILETYPE 124 +#define SSL_R_BAD_SSL_SESSION_ID_LENGTH 125 +#define SSL_R_BAD_STATE 126 +#define SSL_R_BAD_WRITE_RETRY 127 +#define SSL_R_BIO_NOT_SET 128 +#define SSL_R_BLOCK_CIPHER_PAD_IS_WRONG 129 +#define SSL_R_BN_LIB 130 +#define SSL_R_CA_DN_LENGTH_MISMATCH 131 +#define SSL_R_CA_DN_TOO_LONG 132 +#define SSL_R_CCS_RECEIVED_EARLY 133 +#define SSL_R_CERTIFICATE_VERIFY_FAILED 134 +#define SSL_R_CERT_LENGTH_MISMATCH 135 +#define SSL_R_CHALLENGE_IS_DIFFERENT 136 +#define SSL_R_CIPHER_CODE_WRONG_LENGTH 137 +#define SSL_R_CIPHER_OR_HASH_UNAVAILABLE 138 +#define SSL_R_CIPHER_TABLE_SRC_ERROR 139 +#define SSL_R_COMPRESSED_LENGTH_TOO_LONG 140 +#define SSL_R_COMPRESSION_FAILURE 141 +#define SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE 307 +#define SSL_R_COMPRESSION_LIBRARY_ERROR 142 +#define SSL_R_CONNECTION_ID_IS_DIFFERENT 143 +#define SSL_R_CONNECTION_TYPE_NOT_SET 144 +#define SSL_R_COOKIE_MISMATCH 308 +#define SSL_R_DATA_BETWEEN_CCS_AND_FINISHED 145 +#define SSL_R_DATA_LENGTH_TOO_LONG 146 +#define SSL_R_DECRYPTION_FAILED 147 +#define SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC 281 +#define SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG 148 +#define SSL_R_DIGEST_CHECK_FAILED 149 +#define SSL_R_DUPLICATE_COMPRESSION_ID 309 +#define SSL_R_ECGROUP_TOO_LARGE_FOR_CIPHER 310 +#define SSL_R_ENCRYPTED_LENGTH_TOO_LONG 150 +#define SSL_R_ERROR_GENERATING_TMP_RSA_KEY 282 +#define SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST 151 +#define SSL_R_EXCESSIVE_MESSAGE_SIZE 152 +#define SSL_R_EXTRA_DATA_IN_MESSAGE 153 +#define SSL_R_GOT_A_FIN_BEFORE_A_CCS 154 +#define SSL_R_HTTPS_PROXY_REQUEST 155 +#define SSL_R_HTTP_REQUEST 156 +#define SSL_R_ILLEGAL_PADDING 283 +#define SSL_R_INVALID_CHALLENGE_LENGTH 158 +#define SSL_R_INVALID_COMMAND 280 +#define SSL_R_INVALID_PURPOSE 278 +#define SSL_R_INVALID_TRUST 279 +#define SSL_R_KEY_ARG_TOO_LONG 284 +#define SSL_R_KRB5 285 +#define SSL_R_KRB5_C_CC_PRINC 286 +#define SSL_R_KRB5_C_GET_CRED 287 +#define SSL_R_KRB5_C_INIT 288 +#define SSL_R_KRB5_C_MK_REQ 289 +#define SSL_R_KRB5_S_BAD_TICKET 290 +#define SSL_R_KRB5_S_INIT 291 +#define SSL_R_KRB5_S_RD_REQ 292 +#define SSL_R_KRB5_S_TKT_EXPIRED 293 +#define SSL_R_KRB5_S_TKT_NYV 294 +#define SSL_R_KRB5_S_TKT_SKEW 295 +#define SSL_R_LENGTH_MISMATCH 159 +#define SSL_R_LENGTH_TOO_SHORT 160 +#define SSL_R_LIBRARY_BUG 274 +#define SSL_R_LIBRARY_HAS_NO_CIPHERS 161 +#define SSL_R_MESSAGE_TOO_LONG 296 +#define SSL_R_MISSING_DH_DSA_CERT 162 +#define SSL_R_MISSING_DH_KEY 163 +#define SSL_R_MISSING_DH_RSA_CERT 164 +#define SSL_R_MISSING_DSA_SIGNING_CERT 165 +#define SSL_R_MISSING_EXPORT_TMP_DH_KEY 166 +#define SSL_R_MISSING_EXPORT_TMP_RSA_KEY 167 +#define SSL_R_MISSING_RSA_CERTIFICATE 168 +#define SSL_R_MISSING_RSA_ENCRYPTING_CERT 169 +#define SSL_R_MISSING_RSA_SIGNING_CERT 170 +#define SSL_R_MISSING_TMP_DH_KEY 171 +#define SSL_R_MISSING_TMP_ECDH_KEY 311 +#define SSL_R_MISSING_TMP_RSA_KEY 172 +#define SSL_R_MISSING_TMP_RSA_PKEY 173 +#define SSL_R_MISSING_VERIFY_MESSAGE 174 +#define SSL_R_NON_SSLV2_INITIAL_PACKET 175 +#define SSL_R_NO_CERTIFICATES_RETURNED 176 +#define SSL_R_NO_CERTIFICATE_ASSIGNED 177 +#define SSL_R_NO_CERTIFICATE_RETURNED 178 +#define SSL_R_NO_CERTIFICATE_SET 179 +#define SSL_R_NO_CERTIFICATE_SPECIFIED 180 +#define SSL_R_NO_CIPHERS_AVAILABLE 181 +#define SSL_R_NO_CIPHERS_PASSED 182 +#define SSL_R_NO_CIPHERS_SPECIFIED 183 +#define SSL_R_NO_CIPHER_LIST 184 +#define SSL_R_NO_CIPHER_MATCH 185 +#define SSL_R_NO_CLIENT_CERT_RECEIVED 186 +#define SSL_R_NO_COMPRESSION_SPECIFIED 187 +#define SSL_R_NO_METHOD_SPECIFIED 188 +#define SSL_R_NO_PRIVATEKEY 189 +#define SSL_R_NO_PRIVATE_KEY_ASSIGNED 190 +#define SSL_R_NO_PROTOCOLS_AVAILABLE 191 +#define SSL_R_NO_PUBLICKEY 192 +#define SSL_R_NO_SHARED_CIPHER 193 +#define SSL_R_NO_VERIFY_CALLBACK 194 +#define SSL_R_NULL_SSL_CTX 195 +#define SSL_R_NULL_SSL_METHOD_PASSED 196 +#define SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED 197 +#define SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE 297 +#define SSL_R_PACKET_LENGTH_TOO_LONG 198 +#define SSL_R_PATH_TOO_LONG 270 +#define SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE 199 +#define SSL_R_PEER_ERROR 200 +#define SSL_R_PEER_ERROR_CERTIFICATE 201 +#define SSL_R_PEER_ERROR_NO_CERTIFICATE 202 +#define SSL_R_PEER_ERROR_NO_CIPHER 203 +#define SSL_R_PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE 204 +#define SSL_R_PRE_MAC_LENGTH_TOO_LONG 205 +#define SSL_R_PROBLEMS_MAPPING_CIPHER_FUNCTIONS 206 +#define SSL_R_PROTOCOL_IS_SHUTDOWN 207 +#define SSL_R_PUBLIC_KEY_ENCRYPT_ERROR 208 +#define SSL_R_PUBLIC_KEY_IS_NOT_RSA 209 +#define SSL_R_PUBLIC_KEY_NOT_RSA 210 +#define SSL_R_READ_BIO_NOT_SET 211 +#define SSL_R_READ_TIMEOUT_EXPIRED 312 +#define SSL_R_READ_WRONG_PACKET_TYPE 212 +#define SSL_R_RECORD_LENGTH_MISMATCH 213 +#define SSL_R_RECORD_TOO_LARGE 214 +#define SSL_R_RECORD_TOO_SMALL 298 +#define SSL_R_REQUIRED_CIPHER_MISSING 215 +#define SSL_R_REUSE_CERT_LENGTH_NOT_ZERO 216 +#define SSL_R_REUSE_CERT_TYPE_NOT_ZERO 217 +#define SSL_R_REUSE_CIPHER_LIST_NOT_ZERO 218 +#define SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED 277 +#define SSL_R_SHORT_READ 219 +#define SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE 220 +#define SSL_R_SSL23_DOING_SESSION_ID_REUSE 221 +#define SSL_R_SSL2_CONNECTION_ID_TOO_LONG 299 +#define SSL_R_SSL3_SESSION_ID_TOO_LONG 300 +#define SSL_R_SSL3_SESSION_ID_TOO_SHORT 222 +#define SSL_R_SSLV3_ALERT_BAD_CERTIFICATE 1042 +#define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020 +#define SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED 1045 +#define SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED 1044 +#define SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN 1046 +#define SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE 1030 +#define SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE 1040 +#define SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER 1047 +#define SSL_R_SSLV3_ALERT_NO_CERTIFICATE 1041 +#define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010 +#define SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE 1043 +#define SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION 228 +#define SSL_R_SSL_HANDSHAKE_FAILURE 229 +#define SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS 230 +#define SSL_R_SSL_SESSION_ID_CALLBACK_FAILED 301 +#define SSL_R_SSL_SESSION_ID_CONFLICT 302 +#define SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG 273 +#define SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH 303 +#define SSL_R_SSL_SESSION_ID_IS_DIFFERENT 231 +#define SSL_R_TLSV1_ALERT_ACCESS_DENIED 1049 +#define SSL_R_TLSV1_ALERT_DECODE_ERROR 1050 +#define SSL_R_TLSV1_ALERT_DECRYPTION_FAILED 1021 +#define SSL_R_TLSV1_ALERT_DECRYPT_ERROR 1051 +#define SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION 1060 +#define SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY 1071 +#define SSL_R_TLSV1_ALERT_INTERNAL_ERROR 1080 +#define SSL_R_TLSV1_ALERT_NO_RENEGOTIATION 1100 +#define SSL_R_TLSV1_ALERT_PROTOCOL_VERSION 1070 +#define SSL_R_TLSV1_ALERT_RECORD_OVERFLOW 1022 +#define SSL_R_TLSV1_ALERT_UNKNOWN_CA 1048 +#define SSL_R_TLSV1_ALERT_USER_CANCELLED 1090 +#define SSL_R_TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER 232 +#define SSL_R_TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST 233 +#define SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG 234 +#define SSL_R_TRIED_TO_USE_UNSUPPORTED_CIPHER 235 +#define SSL_R_UNABLE_TO_DECODE_DH_CERTS 236 +#define SSL_R_UNABLE_TO_DECODE_ECDH_CERTS 313 +#define SSL_R_UNABLE_TO_EXTRACT_PUBLIC_KEY 237 +#define SSL_R_UNABLE_TO_FIND_DH_PARAMETERS 238 +#define SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS 314 +#define SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS 239 +#define SSL_R_UNABLE_TO_FIND_SSL_METHOD 240 +#define SSL_R_UNABLE_TO_LOAD_SSL2_MD5_ROUTINES 241 +#define SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES 242 +#define SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES 243 +#define SSL_R_UNEXPECTED_MESSAGE 244 +#define SSL_R_UNEXPECTED_RECORD 245 +#define SSL_R_UNINITIALIZED 276 +#define SSL_R_UNKNOWN_ALERT_TYPE 246 +#define SSL_R_UNKNOWN_CERTIFICATE_TYPE 247 +#define SSL_R_UNKNOWN_CIPHER_RETURNED 248 +#define SSL_R_UNKNOWN_CIPHER_TYPE 249 +#define SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE 250 +#define SSL_R_UNKNOWN_PKEY_TYPE 251 +#define SSL_R_UNKNOWN_PROTOCOL 252 +#define SSL_R_UNKNOWN_REMOTE_ERROR_TYPE 253 +#define SSL_R_UNKNOWN_SSL_VERSION 254 +#define SSL_R_UNKNOWN_STATE 255 +#define SSL_R_UNSUPPORTED_CIPHER 256 +#define SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM 257 +#define SSL_R_UNSUPPORTED_ELLIPTIC_CURVE 315 +#define SSL_R_UNSUPPORTED_PROTOCOL 258 +#define SSL_R_UNSUPPORTED_SSL_VERSION 259 +#define SSL_R_WRITE_BIO_NOT_SET 260 +#define SSL_R_WRONG_CIPHER_RETURNED 261 +#define SSL_R_WRONG_MESSAGE_TYPE 262 +#define SSL_R_WRONG_NUMBER_OF_KEY_BITS 263 +#define SSL_R_WRONG_SIGNATURE_LENGTH 264 +#define SSL_R_WRONG_SIGNATURE_SIZE 265 +#define SSL_R_WRONG_SSL_VERSION 266 +#define SSL_R_WRONG_VERSION_NUMBER 267 +#define SSL_R_X509_LIB 268 +#define SSL_R_X509_VERIFICATION_SETUP_PROBLEMS 269 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/ssl2.h b/production/3rdparty/openssl/include/openssl/ssl2.h new file mode 100644 index 00000000..99a52ea0 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/ssl2.h @@ -0,0 +1,268 @@ +/* ssl/ssl2.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_SSL2_H +#define HEADER_SSL2_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Protocol Version Codes */ +#define SSL2_VERSION 0x0002 +#define SSL2_VERSION_MAJOR 0x00 +#define SSL2_VERSION_MINOR 0x02 +/* #define SSL2_CLIENT_VERSION 0x0002 */ +/* #define SSL2_SERVER_VERSION 0x0002 */ + +/* Protocol Message Codes */ +#define SSL2_MT_ERROR 0 +#define SSL2_MT_CLIENT_HELLO 1 +#define SSL2_MT_CLIENT_MASTER_KEY 2 +#define SSL2_MT_CLIENT_FINISHED 3 +#define SSL2_MT_SERVER_HELLO 4 +#define SSL2_MT_SERVER_VERIFY 5 +#define SSL2_MT_SERVER_FINISHED 6 +#define SSL2_MT_REQUEST_CERTIFICATE 7 +#define SSL2_MT_CLIENT_CERTIFICATE 8 + +/* Error Message Codes */ +#define SSL2_PE_UNDEFINED_ERROR 0x0000 +#define SSL2_PE_NO_CIPHER 0x0001 +#define SSL2_PE_NO_CERTIFICATE 0x0002 +#define SSL2_PE_BAD_CERTIFICATE 0x0004 +#define SSL2_PE_UNSUPPORTED_CERTIFICATE_TYPE 0x0006 + +/* Cipher Kind Values */ +#define SSL2_CK_NULL_WITH_MD5 0x02000000 /* v3 */ +#define SSL2_CK_RC4_128_WITH_MD5 0x02010080 +#define SSL2_CK_RC4_128_EXPORT40_WITH_MD5 0x02020080 +#define SSL2_CK_RC2_128_CBC_WITH_MD5 0x02030080 +#define SSL2_CK_RC2_128_CBC_EXPORT40_WITH_MD5 0x02040080 +#define SSL2_CK_IDEA_128_CBC_WITH_MD5 0x02050080 +#define SSL2_CK_DES_64_CBC_WITH_MD5 0x02060040 +#define SSL2_CK_DES_64_CBC_WITH_SHA 0x02060140 /* v3 */ +#define SSL2_CK_DES_192_EDE3_CBC_WITH_MD5 0x020700c0 +#define SSL2_CK_DES_192_EDE3_CBC_WITH_SHA 0x020701c0 /* v3 */ +#define SSL2_CK_RC4_64_WITH_MD5 0x02080080 /* MS hack */ + +#define SSL2_CK_DES_64_CFB64_WITH_MD5_1 0x02ff0800 /* SSLeay */ +#define SSL2_CK_NULL 0x02ff0810 /* SSLeay */ + +#define SSL2_TXT_DES_64_CFB64_WITH_MD5_1 "DES-CFB-M1" +#define SSL2_TXT_NULL_WITH_MD5 "NULL-MD5" +#define SSL2_TXT_RC4_128_WITH_MD5 "RC4-MD5" +#define SSL2_TXT_RC4_128_EXPORT40_WITH_MD5 "EXP-RC4-MD5" +#define SSL2_TXT_RC2_128_CBC_WITH_MD5 "RC2-CBC-MD5" +#define SSL2_TXT_RC2_128_CBC_EXPORT40_WITH_MD5 "EXP-RC2-CBC-MD5" +#define SSL2_TXT_IDEA_128_CBC_WITH_MD5 "IDEA-CBC-MD5" +#define SSL2_TXT_DES_64_CBC_WITH_MD5 "DES-CBC-MD5" +#define SSL2_TXT_DES_64_CBC_WITH_SHA "DES-CBC-SHA" +#define SSL2_TXT_DES_192_EDE3_CBC_WITH_MD5 "DES-CBC3-MD5" +#define SSL2_TXT_DES_192_EDE3_CBC_WITH_SHA "DES-CBC3-SHA" +#define SSL2_TXT_RC4_64_WITH_MD5 "RC4-64-MD5" + +#define SSL2_TXT_NULL "NULL" + +/* Flags for the SSL_CIPHER.algorithm2 field */ +#define SSL2_CF_5_BYTE_ENC 0x01 +#define SSL2_CF_8_BYTE_ENC 0x02 + +/* Certificate Type Codes */ +#define SSL2_CT_X509_CERTIFICATE 0x01 + +/* Authentication Type Code */ +#define SSL2_AT_MD5_WITH_RSA_ENCRYPTION 0x01 + +#define SSL2_MAX_SSL_SESSION_ID_LENGTH 32 + +/* Upper/Lower Bounds */ +#define SSL2_MAX_MASTER_KEY_LENGTH_IN_BITS 256 +#ifdef OPENSSL_SYS_MPE +#define SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER 29998u +#else +#define SSL2_MAX_RECORD_LENGTH_2_BYTE_HEADER 32767u /* 2^15-1 */ +#endif +#define SSL2_MAX_RECORD_LENGTH_3_BYTE_HEADER 16383 /* 2^14-1 */ + +#define SSL2_CHALLENGE_LENGTH 16 +/*#define SSL2_CHALLENGE_LENGTH 32 */ +#define SSL2_MIN_CHALLENGE_LENGTH 16 +#define SSL2_MAX_CHALLENGE_LENGTH 32 +#define SSL2_CONNECTION_ID_LENGTH 16 +#define SSL2_MAX_CONNECTION_ID_LENGTH 16 +#define SSL2_SSL_SESSION_ID_LENGTH 16 +#define SSL2_MAX_CERT_CHALLENGE_LENGTH 32 +#define SSL2_MIN_CERT_CHALLENGE_LENGTH 16 +#define SSL2_MAX_KEY_MATERIAL_LENGTH 24 + +#ifndef HEADER_SSL_LOCL_H +#define CERT char +#endif + +typedef struct ssl2_state_st + { + int three_byte_header; + int clear_text; /* clear text */ + int escape; /* not used in SSLv2 */ + int ssl2_rollback; /* used if SSLv23 rolled back to SSLv2 */ + + /* non-blocking io info, used to make sure the same + * args were passwd */ + unsigned int wnum; /* number of bytes sent so far */ + int wpend_tot; + const unsigned char *wpend_buf; + + int wpend_off; /* offset to data to write */ + int wpend_len; /* number of bytes passwd to write */ + int wpend_ret; /* number of bytes to return to caller */ + + /* buffer raw data */ + int rbuf_left; + int rbuf_offs; + unsigned char *rbuf; + unsigned char *wbuf; + + unsigned char *write_ptr;/* used to point to the start due to + * 2/3 byte header. */ + + unsigned int padding; + unsigned int rlength; /* passed to ssl2_enc */ + int ract_data_length; /* Set when things are encrypted. */ + unsigned int wlength; /* passed to ssl2_enc */ + int wact_data_length; /* Set when things are decrypted. */ + unsigned char *ract_data; + unsigned char *wact_data; + unsigned char *mac_data; + + unsigned char *read_key; + unsigned char *write_key; + + /* Stuff specifically to do with this SSL session */ + unsigned int challenge_length; + unsigned char challenge[SSL2_MAX_CHALLENGE_LENGTH]; + unsigned int conn_id_length; + unsigned char conn_id[SSL2_MAX_CONNECTION_ID_LENGTH]; + unsigned int key_material_length; + unsigned char key_material[SSL2_MAX_KEY_MATERIAL_LENGTH*2]; + + unsigned long read_sequence; + unsigned long write_sequence; + + struct { + unsigned int conn_id_length; + unsigned int cert_type; + unsigned int cert_length; + unsigned int csl; + unsigned int clear; + unsigned int enc; + unsigned char ccl[SSL2_MAX_CERT_CHALLENGE_LENGTH]; + unsigned int cipher_spec_length; + unsigned int session_id_length; + unsigned int clen; + unsigned int rlen; + } tmp; + } SSL2_STATE; + +/* SSLv2 */ +/* client */ +#define SSL2_ST_SEND_CLIENT_HELLO_A (0x10|SSL_ST_CONNECT) +#define SSL2_ST_SEND_CLIENT_HELLO_B (0x11|SSL_ST_CONNECT) +#define SSL2_ST_GET_SERVER_HELLO_A (0x20|SSL_ST_CONNECT) +#define SSL2_ST_GET_SERVER_HELLO_B (0x21|SSL_ST_CONNECT) +#define SSL2_ST_SEND_CLIENT_MASTER_KEY_A (0x30|SSL_ST_CONNECT) +#define SSL2_ST_SEND_CLIENT_MASTER_KEY_B (0x31|SSL_ST_CONNECT) +#define SSL2_ST_SEND_CLIENT_FINISHED_A (0x40|SSL_ST_CONNECT) +#define SSL2_ST_SEND_CLIENT_FINISHED_B (0x41|SSL_ST_CONNECT) +#define SSL2_ST_SEND_CLIENT_CERTIFICATE_A (0x50|SSL_ST_CONNECT) +#define SSL2_ST_SEND_CLIENT_CERTIFICATE_B (0x51|SSL_ST_CONNECT) +#define SSL2_ST_SEND_CLIENT_CERTIFICATE_C (0x52|SSL_ST_CONNECT) +#define SSL2_ST_SEND_CLIENT_CERTIFICATE_D (0x53|SSL_ST_CONNECT) +#define SSL2_ST_GET_SERVER_VERIFY_A (0x60|SSL_ST_CONNECT) +#define SSL2_ST_GET_SERVER_VERIFY_B (0x61|SSL_ST_CONNECT) +#define SSL2_ST_GET_SERVER_FINISHED_A (0x70|SSL_ST_CONNECT) +#define SSL2_ST_GET_SERVER_FINISHED_B (0x71|SSL_ST_CONNECT) +#define SSL2_ST_CLIENT_START_ENCRYPTION (0x80|SSL_ST_CONNECT) +#define SSL2_ST_X509_GET_CLIENT_CERTIFICATE (0x90|SSL_ST_CONNECT) +/* server */ +#define SSL2_ST_GET_CLIENT_HELLO_A (0x10|SSL_ST_ACCEPT) +#define SSL2_ST_GET_CLIENT_HELLO_B (0x11|SSL_ST_ACCEPT) +#define SSL2_ST_GET_CLIENT_HELLO_C (0x12|SSL_ST_ACCEPT) +#define SSL2_ST_SEND_SERVER_HELLO_A (0x20|SSL_ST_ACCEPT) +#define SSL2_ST_SEND_SERVER_HELLO_B (0x21|SSL_ST_ACCEPT) +#define SSL2_ST_GET_CLIENT_MASTER_KEY_A (0x30|SSL_ST_ACCEPT) +#define SSL2_ST_GET_CLIENT_MASTER_KEY_B (0x31|SSL_ST_ACCEPT) +#define SSL2_ST_SEND_SERVER_VERIFY_A (0x40|SSL_ST_ACCEPT) +#define SSL2_ST_SEND_SERVER_VERIFY_B (0x41|SSL_ST_ACCEPT) +#define SSL2_ST_SEND_SERVER_VERIFY_C (0x42|SSL_ST_ACCEPT) +#define SSL2_ST_GET_CLIENT_FINISHED_A (0x50|SSL_ST_ACCEPT) +#define SSL2_ST_GET_CLIENT_FINISHED_B (0x51|SSL_ST_ACCEPT) +#define SSL2_ST_SEND_SERVER_FINISHED_A (0x60|SSL_ST_ACCEPT) +#define SSL2_ST_SEND_SERVER_FINISHED_B (0x61|SSL_ST_ACCEPT) +#define SSL2_ST_SEND_REQUEST_CERTIFICATE_A (0x70|SSL_ST_ACCEPT) +#define SSL2_ST_SEND_REQUEST_CERTIFICATE_B (0x71|SSL_ST_ACCEPT) +#define SSL2_ST_SEND_REQUEST_CERTIFICATE_C (0x72|SSL_ST_ACCEPT) +#define SSL2_ST_SEND_REQUEST_CERTIFICATE_D (0x73|SSL_ST_ACCEPT) +#define SSL2_ST_SERVER_START_ENCRYPTION (0x80|SSL_ST_ACCEPT) +#define SSL2_ST_X509_GET_SERVER_CERTIFICATE (0x90|SSL_ST_ACCEPT) + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/production/3rdparty/openssl/include/openssl/ssl23.h b/production/3rdparty/openssl/include/openssl/ssl23.h new file mode 100644 index 00000000..d3228983 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/ssl23.h @@ -0,0 +1,83 @@ +/* ssl/ssl23.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_SSL23_H +#define HEADER_SSL23_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*client */ +/* write to server */ +#define SSL23_ST_CW_CLNT_HELLO_A (0x210|SSL_ST_CONNECT) +#define SSL23_ST_CW_CLNT_HELLO_B (0x211|SSL_ST_CONNECT) +/* read from server */ +#define SSL23_ST_CR_SRVR_HELLO_A (0x220|SSL_ST_CONNECT) +#define SSL23_ST_CR_SRVR_HELLO_B (0x221|SSL_ST_CONNECT) + +/* server */ +/* read from client */ +#define SSL23_ST_SR_CLNT_HELLO_A (0x210|SSL_ST_ACCEPT) +#define SSL23_ST_SR_CLNT_HELLO_B (0x211|SSL_ST_ACCEPT) + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/production/3rdparty/openssl/include/openssl/ssl3.h b/production/3rdparty/openssl/include/openssl/ssl3.h new file mode 100644 index 00000000..bacaff15 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/ssl3.h @@ -0,0 +1,555 @@ +/* ssl/ssl3.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2002 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * ECC cipher suite support in OpenSSL originally developed by + * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. + */ + +#ifndef HEADER_SSL3_H +#define HEADER_SSL3_H + +#ifndef OPENSSL_NO_COMP +#include +#endif +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SSL3_CK_RSA_NULL_MD5 0x03000001 +#define SSL3_CK_RSA_NULL_SHA 0x03000002 +#define SSL3_CK_RSA_RC4_40_MD5 0x03000003 +#define SSL3_CK_RSA_RC4_128_MD5 0x03000004 +#define SSL3_CK_RSA_RC4_128_SHA 0x03000005 +#define SSL3_CK_RSA_RC2_40_MD5 0x03000006 +#define SSL3_CK_RSA_IDEA_128_SHA 0x03000007 +#define SSL3_CK_RSA_DES_40_CBC_SHA 0x03000008 +#define SSL3_CK_RSA_DES_64_CBC_SHA 0x03000009 +#define SSL3_CK_RSA_DES_192_CBC3_SHA 0x0300000A + +#define SSL3_CK_DH_DSS_DES_40_CBC_SHA 0x0300000B +#define SSL3_CK_DH_DSS_DES_64_CBC_SHA 0x0300000C +#define SSL3_CK_DH_DSS_DES_192_CBC3_SHA 0x0300000D +#define SSL3_CK_DH_RSA_DES_40_CBC_SHA 0x0300000E +#define SSL3_CK_DH_RSA_DES_64_CBC_SHA 0x0300000F +#define SSL3_CK_DH_RSA_DES_192_CBC3_SHA 0x03000010 + +#define SSL3_CK_EDH_DSS_DES_40_CBC_SHA 0x03000011 +#define SSL3_CK_EDH_DSS_DES_64_CBC_SHA 0x03000012 +#define SSL3_CK_EDH_DSS_DES_192_CBC3_SHA 0x03000013 +#define SSL3_CK_EDH_RSA_DES_40_CBC_SHA 0x03000014 +#define SSL3_CK_EDH_RSA_DES_64_CBC_SHA 0x03000015 +#define SSL3_CK_EDH_RSA_DES_192_CBC3_SHA 0x03000016 + +#define SSL3_CK_ADH_RC4_40_MD5 0x03000017 +#define SSL3_CK_ADH_RC4_128_MD5 0x03000018 +#define SSL3_CK_ADH_DES_40_CBC_SHA 0x03000019 +#define SSL3_CK_ADH_DES_64_CBC_SHA 0x0300001A +#define SSL3_CK_ADH_DES_192_CBC_SHA 0x0300001B + +#define SSL3_CK_FZA_DMS_NULL_SHA 0x0300001C +#define SSL3_CK_FZA_DMS_FZA_SHA 0x0300001D +#if 0 /* Because it clashes with KRB5, is never used any more, and is safe + to remove according to David Hopwood + of the ietf-tls list */ +#define SSL3_CK_FZA_DMS_RC4_SHA 0x0300001E +#endif + +/* VRS Additional Kerberos5 entries + */ +#define SSL3_CK_KRB5_DES_64_CBC_SHA 0x0300001E +#define SSL3_CK_KRB5_DES_192_CBC3_SHA 0x0300001F +#define SSL3_CK_KRB5_RC4_128_SHA 0x03000020 +#define SSL3_CK_KRB5_IDEA_128_CBC_SHA 0x03000021 +#define SSL3_CK_KRB5_DES_64_CBC_MD5 0x03000022 +#define SSL3_CK_KRB5_DES_192_CBC3_MD5 0x03000023 +#define SSL3_CK_KRB5_RC4_128_MD5 0x03000024 +#define SSL3_CK_KRB5_IDEA_128_CBC_MD5 0x03000025 + +#define SSL3_CK_KRB5_DES_40_CBC_SHA 0x03000026 +#define SSL3_CK_KRB5_RC2_40_CBC_SHA 0x03000027 +#define SSL3_CK_KRB5_RC4_40_SHA 0x03000028 +#define SSL3_CK_KRB5_DES_40_CBC_MD5 0x03000029 +#define SSL3_CK_KRB5_RC2_40_CBC_MD5 0x0300002A +#define SSL3_CK_KRB5_RC4_40_MD5 0x0300002B + +#define SSL3_TXT_RSA_NULL_MD5 "NULL-MD5" +#define SSL3_TXT_RSA_NULL_SHA "NULL-SHA" +#define SSL3_TXT_RSA_RC4_40_MD5 "EXP-RC4-MD5" +#define SSL3_TXT_RSA_RC4_128_MD5 "RC4-MD5" +#define SSL3_TXT_RSA_RC4_128_SHA "RC4-SHA" +#define SSL3_TXT_RSA_RC2_40_MD5 "EXP-RC2-CBC-MD5" +#define SSL3_TXT_RSA_IDEA_128_SHA "IDEA-CBC-SHA" +#define SSL3_TXT_RSA_DES_40_CBC_SHA "EXP-DES-CBC-SHA" +#define SSL3_TXT_RSA_DES_64_CBC_SHA "DES-CBC-SHA" +#define SSL3_TXT_RSA_DES_192_CBC3_SHA "DES-CBC3-SHA" + +#define SSL3_TXT_DH_DSS_DES_40_CBC_SHA "EXP-DH-DSS-DES-CBC-SHA" +#define SSL3_TXT_DH_DSS_DES_64_CBC_SHA "DH-DSS-DES-CBC-SHA" +#define SSL3_TXT_DH_DSS_DES_192_CBC3_SHA "DH-DSS-DES-CBC3-SHA" +#define SSL3_TXT_DH_RSA_DES_40_CBC_SHA "EXP-DH-RSA-DES-CBC-SHA" +#define SSL3_TXT_DH_RSA_DES_64_CBC_SHA "DH-RSA-DES-CBC-SHA" +#define SSL3_TXT_DH_RSA_DES_192_CBC3_SHA "DH-RSA-DES-CBC3-SHA" + +#define SSL3_TXT_EDH_DSS_DES_40_CBC_SHA "EXP-EDH-DSS-DES-CBC-SHA" +#define SSL3_TXT_EDH_DSS_DES_64_CBC_SHA "EDH-DSS-DES-CBC-SHA" +#define SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA "EDH-DSS-DES-CBC3-SHA" +#define SSL3_TXT_EDH_RSA_DES_40_CBC_SHA "EXP-EDH-RSA-DES-CBC-SHA" +#define SSL3_TXT_EDH_RSA_DES_64_CBC_SHA "EDH-RSA-DES-CBC-SHA" +#define SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA "EDH-RSA-DES-CBC3-SHA" + +#define SSL3_TXT_ADH_RC4_40_MD5 "EXP-ADH-RC4-MD5" +#define SSL3_TXT_ADH_RC4_128_MD5 "ADH-RC4-MD5" +#define SSL3_TXT_ADH_DES_40_CBC_SHA "EXP-ADH-DES-CBC-SHA" +#define SSL3_TXT_ADH_DES_64_CBC_SHA "ADH-DES-CBC-SHA" +#define SSL3_TXT_ADH_DES_192_CBC_SHA "ADH-DES-CBC3-SHA" + +#define SSL3_TXT_FZA_DMS_NULL_SHA "FZA-NULL-SHA" +#define SSL3_TXT_FZA_DMS_FZA_SHA "FZA-FZA-CBC-SHA" +#define SSL3_TXT_FZA_DMS_RC4_SHA "FZA-RC4-SHA" + +#define SSL3_TXT_KRB5_DES_64_CBC_SHA "KRB5-DES-CBC-SHA" +#define SSL3_TXT_KRB5_DES_192_CBC3_SHA "KRB5-DES-CBC3-SHA" +#define SSL3_TXT_KRB5_RC4_128_SHA "KRB5-RC4-SHA" +#define SSL3_TXT_KRB5_IDEA_128_CBC_SHA "KRB5-IDEA-CBC-SHA" +#define SSL3_TXT_KRB5_DES_64_CBC_MD5 "KRB5-DES-CBC-MD5" +#define SSL3_TXT_KRB5_DES_192_CBC3_MD5 "KRB5-DES-CBC3-MD5" +#define SSL3_TXT_KRB5_RC4_128_MD5 "KRB5-RC4-MD5" +#define SSL3_TXT_KRB5_IDEA_128_CBC_MD5 "KRB5-IDEA-CBC-MD5" + +#define SSL3_TXT_KRB5_DES_40_CBC_SHA "EXP-KRB5-DES-CBC-SHA" +#define SSL3_TXT_KRB5_RC2_40_CBC_SHA "EXP-KRB5-RC2-CBC-SHA" +#define SSL3_TXT_KRB5_RC4_40_SHA "EXP-KRB5-RC4-SHA" +#define SSL3_TXT_KRB5_DES_40_CBC_MD5 "EXP-KRB5-DES-CBC-MD5" +#define SSL3_TXT_KRB5_RC2_40_CBC_MD5 "EXP-KRB5-RC2-CBC-MD5" +#define SSL3_TXT_KRB5_RC4_40_MD5 "EXP-KRB5-RC4-MD5" + +#define SSL3_SSL_SESSION_ID_LENGTH 32 +#define SSL3_MAX_SSL_SESSION_ID_LENGTH 32 + +#define SSL3_MASTER_SECRET_SIZE 48 +#define SSL3_RANDOM_SIZE 32 +#define SSL3_SESSION_ID_SIZE 32 +#define SSL3_RT_HEADER_LENGTH 5 + +/* Due to MS stuffing up, this can change.... */ +#if defined(OPENSSL_SYS_WIN16) || \ + (defined(OPENSSL_SYS_MSDOS) && !defined(OPENSSL_SYS_WIN32)) +#define SSL3_RT_MAX_EXTRA (14000) +#else +#define SSL3_RT_MAX_EXTRA (16384) +#endif + +#define SSL3_RT_MAX_PLAIN_LENGTH 16384 +#ifdef OPENSSL_NO_COMP +#define SSL3_RT_MAX_COMPRESSED_LENGTH SSL3_RT_MAX_PLAIN_LENGTH +#else +#define SSL3_RT_MAX_COMPRESSED_LENGTH (1024+SSL3_RT_MAX_PLAIN_LENGTH) +#endif +#define SSL3_RT_MAX_ENCRYPTED_LENGTH (1024+SSL3_RT_MAX_COMPRESSED_LENGTH) +#define SSL3_RT_MAX_PACKET_SIZE (SSL3_RT_MAX_ENCRYPTED_LENGTH+SSL3_RT_HEADER_LENGTH) +#define SSL3_RT_MAX_DATA_SIZE (1024*1024) + +#define SSL3_MD_CLIENT_FINISHED_CONST "\x43\x4C\x4E\x54" +#define SSL3_MD_SERVER_FINISHED_CONST "\x53\x52\x56\x52" + +#define SSL3_VERSION 0x0300 +#define SSL3_VERSION_MAJOR 0x03 +#define SSL3_VERSION_MINOR 0x00 + +#define SSL3_RT_CHANGE_CIPHER_SPEC 20 +#define SSL3_RT_ALERT 21 +#define SSL3_RT_HANDSHAKE 22 +#define SSL3_RT_APPLICATION_DATA 23 + +#define SSL3_AL_WARNING 1 +#define SSL3_AL_FATAL 2 + +#define SSL3_AD_CLOSE_NOTIFY 0 +#define SSL3_AD_UNEXPECTED_MESSAGE 10 /* fatal */ +#define SSL3_AD_BAD_RECORD_MAC 20 /* fatal */ +#define SSL3_AD_DECOMPRESSION_FAILURE 30 /* fatal */ +#define SSL3_AD_HANDSHAKE_FAILURE 40 /* fatal */ +#define SSL3_AD_NO_CERTIFICATE 41 +#define SSL3_AD_BAD_CERTIFICATE 42 +#define SSL3_AD_UNSUPPORTED_CERTIFICATE 43 +#define SSL3_AD_CERTIFICATE_REVOKED 44 +#define SSL3_AD_CERTIFICATE_EXPIRED 45 +#define SSL3_AD_CERTIFICATE_UNKNOWN 46 +#define SSL3_AD_ILLEGAL_PARAMETER 47 /* fatal */ + +typedef struct ssl3_record_st + { +/*r */ int type; /* type of record */ +/*rw*/ unsigned int length; /* How many bytes available */ +/*r */ unsigned int off; /* read/write offset into 'buf' */ +/*rw*/ unsigned char *data; /* pointer to the record data */ +/*rw*/ unsigned char *input; /* where the decode bytes are */ +/*r */ unsigned char *comp; /* only used with decompression - malloc()ed */ +/*r */ unsigned long epoch; /* epoch number, needed by DTLS1 */ +/*r */ PQ_64BIT seq_num; /* sequence number, needed by DTLS1 */ + } SSL3_RECORD; + +typedef struct ssl3_buffer_st + { + unsigned char *buf; /* at least SSL3_RT_MAX_PACKET_SIZE bytes, + * see ssl3_setup_buffers() */ + size_t len; /* buffer size */ + int offset; /* where to 'copy from' */ + int left; /* how many bytes left */ + } SSL3_BUFFER; + +#define SSL3_CT_RSA_SIGN 1 +#define SSL3_CT_DSS_SIGN 2 +#define SSL3_CT_RSA_FIXED_DH 3 +#define SSL3_CT_DSS_FIXED_DH 4 +#define SSL3_CT_RSA_EPHEMERAL_DH 5 +#define SSL3_CT_DSS_EPHEMERAL_DH 6 +#define SSL3_CT_FORTEZZA_DMS 20 +/* SSL3_CT_NUMBER is used to size arrays and it must be large + * enough to contain all of the cert types defined either for + * SSLv3 and TLSv1. + */ +#define SSL3_CT_NUMBER 7 + + +#define SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS 0x0001 +#define SSL3_FLAGS_DELAY_CLIENT_FINISHED 0x0002 +#define SSL3_FLAGS_POP_BUFFER 0x0004 +#define TLS1_FLAGS_TLS_PADDING_BUG 0x0008 + +typedef struct ssl3_state_st + { + long flags; + int delay_buf_pop_ret; + + unsigned char read_sequence[8]; + unsigned char read_mac_secret[EVP_MAX_MD_SIZE]; + unsigned char write_sequence[8]; + unsigned char write_mac_secret[EVP_MAX_MD_SIZE]; + + unsigned char server_random[SSL3_RANDOM_SIZE]; + unsigned char client_random[SSL3_RANDOM_SIZE]; + + /* flags for countermeasure against known-IV weakness */ + int need_empty_fragments; + int empty_fragment_done; + + SSL3_BUFFER rbuf; /* read IO goes into here */ + SSL3_BUFFER wbuf; /* write IO goes into here */ + + SSL3_RECORD rrec; /* each decoded record goes in here */ + SSL3_RECORD wrec; /* goes out from here */ + + /* storage for Alert/Handshake protocol data received but not + * yet processed by ssl3_read_bytes: */ + unsigned char alert_fragment[2]; + unsigned int alert_fragment_len; + unsigned char handshake_fragment[4]; + unsigned int handshake_fragment_len; + + /* partial write - check the numbers match */ + unsigned int wnum; /* number of bytes sent so far */ + int wpend_tot; /* number bytes written */ + int wpend_type; + int wpend_ret; /* number of bytes submitted */ + const unsigned char *wpend_buf; + + /* used during startup, digest all incoming/outgoing packets */ + EVP_MD_CTX finish_dgst1; + EVP_MD_CTX finish_dgst2; + + /* this is set whenerver we see a change_cipher_spec message + * come in when we are not looking for one */ + int change_cipher_spec; + + int warn_alert; + int fatal_alert; + /* we allow one fatal and one warning alert to be outstanding, + * send close alert via the warning alert */ + int alert_dispatch; + unsigned char send_alert[2]; + + /* This flag is set when we should renegotiate ASAP, basically when + * there is no more data in the read or write buffers */ + int renegotiate; + int total_renegotiations; + int num_renegotiations; + + int in_read_app_data; + + struct { + /* actually only needs to be 16+20 */ + unsigned char cert_verify_md[EVP_MAX_MD_SIZE*2]; + + /* actually only need to be 16+20 for SSLv3 and 12 for TLS */ + unsigned char finish_md[EVP_MAX_MD_SIZE*2]; + int finish_md_len; + unsigned char peer_finish_md[EVP_MAX_MD_SIZE*2]; + int peer_finish_md_len; + + unsigned long message_size; + int message_type; + + /* used to hold the new cipher we are going to use */ + SSL_CIPHER *new_cipher; +#ifndef OPENSSL_NO_DH + DH *dh; +#endif + +#ifndef OPENSSL_NO_ECDH + EC_KEY *ecdh; /* holds short lived ECDH key */ +#endif + + /* used when SSL_ST_FLUSH_DATA is entered */ + int next_state; + + int reuse_message; + + /* used for certificate requests */ + int cert_req; + int ctype_num; + char ctype[SSL3_CT_NUMBER]; + STACK_OF(X509_NAME) *ca_names; + + int use_rsa_tmp; + + int key_block_length; + unsigned char *key_block; + + const EVP_CIPHER *new_sym_enc; + const EVP_MD *new_hash; +#ifndef OPENSSL_NO_COMP + const SSL_COMP *new_compression; +#else + char *new_compression; +#endif + int cert_request; + } tmp; + + } SSL3_STATE; + + +/* SSLv3 */ +/*client */ +/* extra state */ +#define SSL3_ST_CW_FLUSH (0x100|SSL_ST_CONNECT) +/* write to server */ +#define SSL3_ST_CW_CLNT_HELLO_A (0x110|SSL_ST_CONNECT) +#define SSL3_ST_CW_CLNT_HELLO_B (0x111|SSL_ST_CONNECT) +/* read from server */ +#define SSL3_ST_CR_SRVR_HELLO_A (0x120|SSL_ST_CONNECT) +#define SSL3_ST_CR_SRVR_HELLO_B (0x121|SSL_ST_CONNECT) +#define DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A (0x126|SSL_ST_CONNECT) +#define DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B (0x127|SSL_ST_CONNECT) +#define SSL3_ST_CR_CERT_A (0x130|SSL_ST_CONNECT) +#define SSL3_ST_CR_CERT_B (0x131|SSL_ST_CONNECT) +#define SSL3_ST_CR_KEY_EXCH_A (0x140|SSL_ST_CONNECT) +#define SSL3_ST_CR_KEY_EXCH_B (0x141|SSL_ST_CONNECT) +#define SSL3_ST_CR_CERT_REQ_A (0x150|SSL_ST_CONNECT) +#define SSL3_ST_CR_CERT_REQ_B (0x151|SSL_ST_CONNECT) +#define SSL3_ST_CR_SRVR_DONE_A (0x160|SSL_ST_CONNECT) +#define SSL3_ST_CR_SRVR_DONE_B (0x161|SSL_ST_CONNECT) +/* write to server */ +#define SSL3_ST_CW_CERT_A (0x170|SSL_ST_CONNECT) +#define SSL3_ST_CW_CERT_B (0x171|SSL_ST_CONNECT) +#define SSL3_ST_CW_CERT_C (0x172|SSL_ST_CONNECT) +#define SSL3_ST_CW_CERT_D (0x173|SSL_ST_CONNECT) +#define SSL3_ST_CW_KEY_EXCH_A (0x180|SSL_ST_CONNECT) +#define SSL3_ST_CW_KEY_EXCH_B (0x181|SSL_ST_CONNECT) +#define SSL3_ST_CW_CERT_VRFY_A (0x190|SSL_ST_CONNECT) +#define SSL3_ST_CW_CERT_VRFY_B (0x191|SSL_ST_CONNECT) +#define SSL3_ST_CW_CHANGE_A (0x1A0|SSL_ST_CONNECT) +#define SSL3_ST_CW_CHANGE_B (0x1A1|SSL_ST_CONNECT) +#define SSL3_ST_CW_FINISHED_A (0x1B0|SSL_ST_CONNECT) +#define SSL3_ST_CW_FINISHED_B (0x1B1|SSL_ST_CONNECT) +/* read from server */ +#define SSL3_ST_CR_CHANGE_A (0x1C0|SSL_ST_CONNECT) +#define SSL3_ST_CR_CHANGE_B (0x1C1|SSL_ST_CONNECT) +#define SSL3_ST_CR_FINISHED_A (0x1D0|SSL_ST_CONNECT) +#define SSL3_ST_CR_FINISHED_B (0x1D1|SSL_ST_CONNECT) + +/* server */ +/* extra state */ +#define SSL3_ST_SW_FLUSH (0x100|SSL_ST_ACCEPT) +/* read from client */ +/* Do not change the number values, they do matter */ +#define SSL3_ST_SR_CLNT_HELLO_A (0x110|SSL_ST_ACCEPT) +#define SSL3_ST_SR_CLNT_HELLO_B (0x111|SSL_ST_ACCEPT) +#define SSL3_ST_SR_CLNT_HELLO_C (0x112|SSL_ST_ACCEPT) +/* write to client */ +#define DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A (0x113|SSL_ST_ACCEPT) +#define DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B (0x114|SSL_ST_ACCEPT) +#define SSL3_ST_SW_HELLO_REQ_A (0x120|SSL_ST_ACCEPT) +#define SSL3_ST_SW_HELLO_REQ_B (0x121|SSL_ST_ACCEPT) +#define SSL3_ST_SW_HELLO_REQ_C (0x122|SSL_ST_ACCEPT) +#define SSL3_ST_SW_SRVR_HELLO_A (0x130|SSL_ST_ACCEPT) +#define SSL3_ST_SW_SRVR_HELLO_B (0x131|SSL_ST_ACCEPT) +#define SSL3_ST_SW_CERT_A (0x140|SSL_ST_ACCEPT) +#define SSL3_ST_SW_CERT_B (0x141|SSL_ST_ACCEPT) +#define SSL3_ST_SW_KEY_EXCH_A (0x150|SSL_ST_ACCEPT) +#define SSL3_ST_SW_KEY_EXCH_B (0x151|SSL_ST_ACCEPT) +#define SSL3_ST_SW_CERT_REQ_A (0x160|SSL_ST_ACCEPT) +#define SSL3_ST_SW_CERT_REQ_B (0x161|SSL_ST_ACCEPT) +#define SSL3_ST_SW_SRVR_DONE_A (0x170|SSL_ST_ACCEPT) +#define SSL3_ST_SW_SRVR_DONE_B (0x171|SSL_ST_ACCEPT) +/* read from client */ +#define SSL3_ST_SR_CERT_A (0x180|SSL_ST_ACCEPT) +#define SSL3_ST_SR_CERT_B (0x181|SSL_ST_ACCEPT) +#define SSL3_ST_SR_KEY_EXCH_A (0x190|SSL_ST_ACCEPT) +#define SSL3_ST_SR_KEY_EXCH_B (0x191|SSL_ST_ACCEPT) +#define SSL3_ST_SR_CERT_VRFY_A (0x1A0|SSL_ST_ACCEPT) +#define SSL3_ST_SR_CERT_VRFY_B (0x1A1|SSL_ST_ACCEPT) +#define SSL3_ST_SR_CHANGE_A (0x1B0|SSL_ST_ACCEPT) +#define SSL3_ST_SR_CHANGE_B (0x1B1|SSL_ST_ACCEPT) +#define SSL3_ST_SR_FINISHED_A (0x1C0|SSL_ST_ACCEPT) +#define SSL3_ST_SR_FINISHED_B (0x1C1|SSL_ST_ACCEPT) +/* write to client */ +#define SSL3_ST_SW_CHANGE_A (0x1D0|SSL_ST_ACCEPT) +#define SSL3_ST_SW_CHANGE_B (0x1D1|SSL_ST_ACCEPT) +#define SSL3_ST_SW_FINISHED_A (0x1E0|SSL_ST_ACCEPT) +#define SSL3_ST_SW_FINISHED_B (0x1E1|SSL_ST_ACCEPT) + +#define SSL3_MT_HELLO_REQUEST 0 +#define SSL3_MT_CLIENT_HELLO 1 +#define SSL3_MT_SERVER_HELLO 2 +#define SSL3_MT_CERTIFICATE 11 +#define SSL3_MT_SERVER_KEY_EXCHANGE 12 +#define SSL3_MT_CERTIFICATE_REQUEST 13 +#define SSL3_MT_SERVER_DONE 14 +#define SSL3_MT_CERTIFICATE_VERIFY 15 +#define SSL3_MT_CLIENT_KEY_EXCHANGE 16 +#define SSL3_MT_FINISHED 20 +#define DTLS1_MT_HELLO_VERIFY_REQUEST 3 + + +#define SSL3_MT_CCS 1 + +/* These are used when changing over to a new cipher */ +#define SSL3_CC_READ 0x01 +#define SSL3_CC_WRITE 0x02 +#define SSL3_CC_CLIENT 0x10 +#define SSL3_CC_SERVER 0x20 +#define SSL3_CHANGE_CIPHER_CLIENT_WRITE (SSL3_CC_CLIENT|SSL3_CC_WRITE) +#define SSL3_CHANGE_CIPHER_SERVER_READ (SSL3_CC_SERVER|SSL3_CC_READ) +#define SSL3_CHANGE_CIPHER_CLIENT_READ (SSL3_CC_CLIENT|SSL3_CC_READ) +#define SSL3_CHANGE_CIPHER_SERVER_WRITE (SSL3_CC_SERVER|SSL3_CC_WRITE) + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/production/3rdparty/openssl/include/openssl/stack.h b/production/3rdparty/openssl/include/openssl/stack.h new file mode 100644 index 00000000..5cbb116a --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/stack.h @@ -0,0 +1,109 @@ +/* crypto/stack/stack.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_STACK_H +#define HEADER_STACK_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct stack_st + { + int num; + char **data; + int sorted; + + int num_alloc; + int (*comp)(const char * const *, const char * const *); + } STACK; + +#define M_sk_num(sk) ((sk) ? (sk)->num:-1) +#define M_sk_value(sk,n) ((sk) ? (sk)->data[n] : NULL) + +int sk_num(const STACK *); +char *sk_value(const STACK *, int); + +char *sk_set(STACK *, int, char *); + +STACK *sk_new(int (*cmp)(const char * const *, const char * const *)); +STACK *sk_new_null(void); +void sk_free(STACK *); +void sk_pop_free(STACK *st, void (*func)(void *)); +int sk_insert(STACK *sk,char *data,int where); +char *sk_delete(STACK *st,int loc); +char *sk_delete_ptr(STACK *st, char *p); +int sk_find(STACK *st,char *data); +int sk_find_ex(STACK *st,char *data); +int sk_push(STACK *st,char *data); +int sk_unshift(STACK *st,char *data); +char *sk_shift(STACK *st); +char *sk_pop(STACK *st); +void sk_zero(STACK *st); +int (*sk_set_cmp_func(STACK *sk, int (*c)(const char * const *, + const char * const *))) + (const char * const *, const char * const *); +STACK *sk_dup(STACK *st); +void sk_sort(STACK *st); +int sk_is_sorted(const STACK *st); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/store.h b/production/3rdparty/openssl/include/openssl/store.h new file mode 100644 index 00000000..64583377 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/store.h @@ -0,0 +1,554 @@ +/* crypto/store/store.h -*- mode:C; c-file-style: "eay" -*- */ +/* Written by Richard Levitte (richard@levitte.org) for the OpenSSL + * project 2003. + */ +/* ==================================================================== + * Copyright (c) 2003 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#ifndef HEADER_STORE_H +#define HEADER_STORE_H + +#include +#ifndef OPENSSL_NO_DEPRECATED +#include +#include +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Already defined in ossl_typ.h */ +/* typedef struct store_st STORE; */ +/* typedef struct store_method_st STORE_METHOD; */ + + +/* All the following functions return 0, a negative number or NULL on error. + When everything is fine, they return a positive value or a non-NULL + pointer, all depending on their purpose. */ + +/* Creators and destructor. */ +STORE *STORE_new_method(const STORE_METHOD *method); +STORE *STORE_new_engine(ENGINE *engine); +void STORE_free(STORE *ui); + + +/* Give a user interface parametrised control commands. This can be used to + send down an integer, a data pointer or a function pointer, as well as + be used to get information from a STORE. */ +int STORE_ctrl(STORE *store, int cmd, long i, void *p, void (*f)(void)); + +/* A control to set the directory with keys and certificates. Used by the + built-in directory level method. */ +#define STORE_CTRL_SET_DIRECTORY 0x0001 +/* A control to set a file to load. Used by the built-in file level method. */ +#define STORE_CTRL_SET_FILE 0x0002 +/* A control to set a configuration file to load. Can be used by any method + that wishes to load a configuration file. */ +#define STORE_CTRL_SET_CONF_FILE 0x0003 +/* A control to set a the section of the loaded configuration file. Can be + used by any method that wishes to load a configuration file. */ +#define STORE_CTRL_SET_CONF_SECTION 0x0004 + + +/* Some methods may use extra data */ +#define STORE_set_app_data(s,arg) STORE_set_ex_data(s,0,arg) +#define STORE_get_app_data(s) STORE_get_ex_data(s,0) +int STORE_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, + CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); +int STORE_set_ex_data(STORE *r,int idx,void *arg); +void *STORE_get_ex_data(STORE *r, int idx); + +/* Use specific methods instead of the built-in one */ +const STORE_METHOD *STORE_get_method(STORE *store); +const STORE_METHOD *STORE_set_method(STORE *store, const STORE_METHOD *meth); + +/* The standard OpenSSL methods. */ +/* This is the in-memory method. It does everything except revoking and updating, + and is of course volatile. It's used by other methods that have an in-memory + cache. */ +const STORE_METHOD *STORE_Memory(void); +#if 0 /* Not yet implemented */ +/* This is the directory store. It does everything except revoking and updating, + and uses STORE_Memory() to cache things in memory. */ +const STORE_METHOD *STORE_Directory(void); +/* This is the file store. It does everything except revoking and updating, + and uses STORE_Memory() to cache things in memory. Certificates are added + to it with the store operation, and it will only get cached certificates. */ +const STORE_METHOD *STORE_File(void); +#endif + +/* Store functions take a type code for the type of data they should store + or fetch */ +typedef enum STORE_object_types + { + STORE_OBJECT_TYPE_X509_CERTIFICATE= 0x01, /* X509 * */ + STORE_OBJECT_TYPE_X509_CRL= 0x02, /* X509_CRL * */ + STORE_OBJECT_TYPE_PRIVATE_KEY= 0x03, /* EVP_PKEY * */ + STORE_OBJECT_TYPE_PUBLIC_KEY= 0x04, /* EVP_PKEY * */ + STORE_OBJECT_TYPE_NUMBER= 0x05, /* BIGNUM * */ + STORE_OBJECT_TYPE_ARBITRARY= 0x06, /* BUF_MEM * */ + STORE_OBJECT_TYPE_NUM= 0x06 /* The amount of known + object types */ + } STORE_OBJECT_TYPES; +/* List of text strings corresponding to the object types. */ +extern const char * const STORE_object_type_string[STORE_OBJECT_TYPE_NUM+1]; + +/* Some store functions take a parameter list. Those parameters come with + one of the following codes. The comments following the codes below indicate + what type the value should be a pointer to. */ +typedef enum STORE_params + { + STORE_PARAM_EVP_TYPE= 0x01, /* int */ + STORE_PARAM_BITS= 0x02, /* size_t */ + STORE_PARAM_KEY_PARAMETERS= 0x03, /* ??? */ + STORE_PARAM_KEY_NO_PARAMETERS= 0x04, /* N/A */ + STORE_PARAM_AUTH_PASSPHRASE= 0x05, /* char * */ + STORE_PARAM_AUTH_KRB5_TICKET= 0x06, /* void * */ + STORE_PARAM_TYPE_NUM= 0x06 /* The amount of known + parameter types */ + } STORE_PARAM_TYPES; +/* Parameter value sizes. -1 means unknown, anything else is the required size. */ +extern const int STORE_param_sizes[STORE_PARAM_TYPE_NUM+1]; + +/* Store functions take attribute lists. Those attributes come with codes. + The comments following the codes below indicate what type the value should + be a pointer to. */ +typedef enum STORE_attribs + { + STORE_ATTR_END= 0x00, + STORE_ATTR_FRIENDLYNAME= 0x01, /* C string */ + STORE_ATTR_KEYID= 0x02, /* 160 bit string (SHA1) */ + STORE_ATTR_ISSUERKEYID= 0x03, /* 160 bit string (SHA1) */ + STORE_ATTR_SUBJECTKEYID= 0x04, /* 160 bit string (SHA1) */ + STORE_ATTR_ISSUERSERIALHASH= 0x05, /* 160 bit string (SHA1) */ + STORE_ATTR_ISSUER= 0x06, /* X509_NAME * */ + STORE_ATTR_SERIAL= 0x07, /* BIGNUM * */ + STORE_ATTR_SUBJECT= 0x08, /* X509_NAME * */ + STORE_ATTR_CERTHASH= 0x09, /* 160 bit string (SHA1) */ + STORE_ATTR_EMAIL= 0x0a, /* C string */ + STORE_ATTR_FILENAME= 0x0b, /* C string */ + STORE_ATTR_TYPE_NUM= 0x0b, /* The amount of known + attribute types */ + STORE_ATTR_OR= 0xff /* This is a special + separator, which + expresses the OR + operation. */ + } STORE_ATTR_TYPES; +/* Attribute value sizes. -1 means unknown, anything else is the required size. */ +extern const int STORE_attr_sizes[STORE_ATTR_TYPE_NUM+1]; + +typedef enum STORE_certificate_status + { + STORE_X509_VALID= 0x00, + STORE_X509_EXPIRED= 0x01, + STORE_X509_SUSPENDED= 0x02, + STORE_X509_REVOKED= 0x03 + } STORE_CERTIFICATE_STATUS; + +/* Engine store functions will return a structure that contains all the necessary + * information, including revokation status for certificates. This is really not + * needed for application authors, as the ENGINE framework functions will extract + * the OpenSSL-specific information when at all possible. However, for engine + * authors, it's crucial to know this structure. */ +typedef struct STORE_OBJECT_st + { + STORE_OBJECT_TYPES type; + union + { + struct + { + STORE_CERTIFICATE_STATUS status; + X509 *certificate; + } x509; + X509_CRL *crl; + EVP_PKEY *key; + BIGNUM *number; + BUF_MEM *arbitrary; + } data; + } STORE_OBJECT; +DECLARE_STACK_OF(STORE_OBJECT) +STORE_OBJECT *STORE_OBJECT_new(void); +void STORE_OBJECT_free(STORE_OBJECT *data); + + + +/* The following functions handle the storage. They return 0, a negative number + or NULL on error, anything else on success. */ +X509 *STORE_get_certificate(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +int STORE_store_certificate(STORE *e, X509 *data, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +int STORE_modify_certificate(STORE *e, OPENSSL_ITEM search_attributes[], + OPENSSL_ITEM add_attributes[], OPENSSL_ITEM modify_attributes[], + OPENSSL_ITEM delete_attributes[], OPENSSL_ITEM parameters[]); +int STORE_revoke_certificate(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +int STORE_delete_certificate(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +void *STORE_list_certificate_start(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +X509 *STORE_list_certificate_next(STORE *e, void *handle); +int STORE_list_certificate_end(STORE *e, void *handle); +int STORE_list_certificate_endp(STORE *e, void *handle); +EVP_PKEY *STORE_generate_key(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +EVP_PKEY *STORE_get_private_key(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +int STORE_store_private_key(STORE *e, EVP_PKEY *data, + OPENSSL_ITEM attributes[], OPENSSL_ITEM parameters[]); +int STORE_modify_private_key(STORE *e, OPENSSL_ITEM search_attributes[], + OPENSSL_ITEM add_sttributes[], OPENSSL_ITEM modify_attributes[], + OPENSSL_ITEM delete_attributes[], OPENSSL_ITEM parameters[]); +int STORE_revoke_private_key(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +int STORE_delete_private_key(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +void *STORE_list_private_key_start(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +EVP_PKEY *STORE_list_private_key_next(STORE *e, void *handle); +int STORE_list_private_key_end(STORE *e, void *handle); +int STORE_list_private_key_endp(STORE *e, void *handle); +EVP_PKEY *STORE_get_public_key(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +int STORE_store_public_key(STORE *e, EVP_PKEY *data, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +int STORE_modify_public_key(STORE *e, OPENSSL_ITEM search_attributes[], + OPENSSL_ITEM add_sttributes[], OPENSSL_ITEM modify_attributes[], + OPENSSL_ITEM delete_attributes[], OPENSSL_ITEM parameters[]); +int STORE_revoke_public_key(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +int STORE_delete_public_key(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +void *STORE_list_public_key_start(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +EVP_PKEY *STORE_list_public_key_next(STORE *e, void *handle); +int STORE_list_public_key_end(STORE *e, void *handle); +int STORE_list_public_key_endp(STORE *e, void *handle); +X509_CRL *STORE_generate_crl(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +X509_CRL *STORE_get_crl(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +int STORE_store_crl(STORE *e, X509_CRL *data, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +int STORE_modify_crl(STORE *e, OPENSSL_ITEM search_attributes[], + OPENSSL_ITEM add_sttributes[], OPENSSL_ITEM modify_attributes[], + OPENSSL_ITEM delete_attributes[], OPENSSL_ITEM parameters[]); +int STORE_delete_crl(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +void *STORE_list_crl_start(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +X509_CRL *STORE_list_crl_next(STORE *e, void *handle); +int STORE_list_crl_end(STORE *e, void *handle); +int STORE_list_crl_endp(STORE *e, void *handle); +int STORE_store_number(STORE *e, BIGNUM *data, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +int STORE_modify_number(STORE *e, OPENSSL_ITEM search_attributes[], + OPENSSL_ITEM add_sttributes[], OPENSSL_ITEM modify_attributes[], + OPENSSL_ITEM delete_attributes[], OPENSSL_ITEM parameters[]); +BIGNUM *STORE_get_number(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +int STORE_delete_number(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +int STORE_store_arbitrary(STORE *e, BUF_MEM *data, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +int STORE_modify_arbitrary(STORE *e, OPENSSL_ITEM search_attributes[], + OPENSSL_ITEM add_sttributes[], OPENSSL_ITEM modify_attributes[], + OPENSSL_ITEM delete_attributes[], OPENSSL_ITEM parameters[]); +BUF_MEM *STORE_get_arbitrary(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); +int STORE_delete_arbitrary(STORE *e, OPENSSL_ITEM attributes[], + OPENSSL_ITEM parameters[]); + + +/* Create and manipulate methods */ +STORE_METHOD *STORE_create_method(char *name); +void STORE_destroy_method(STORE_METHOD *store_method); + +/* These callback types are use for store handlers */ +typedef int (*STORE_INITIALISE_FUNC_PTR)(STORE *); +typedef void (*STORE_CLEANUP_FUNC_PTR)(STORE *); +typedef STORE_OBJECT *(*STORE_GENERATE_OBJECT_FUNC_PTR)(STORE *, STORE_OBJECT_TYPES type, OPENSSL_ITEM attributes[], OPENSSL_ITEM parameters[]); +typedef STORE_OBJECT *(*STORE_GET_OBJECT_FUNC_PTR)(STORE *, STORE_OBJECT_TYPES type, OPENSSL_ITEM attributes[], OPENSSL_ITEM parameters[]); +typedef void *(*STORE_START_OBJECT_FUNC_PTR)(STORE *, STORE_OBJECT_TYPES type, OPENSSL_ITEM attributes[], OPENSSL_ITEM parameters[]); +typedef STORE_OBJECT *(*STORE_NEXT_OBJECT_FUNC_PTR)(STORE *, void *handle); +typedef int (*STORE_END_OBJECT_FUNC_PTR)(STORE *, void *handle); +typedef int (*STORE_HANDLE_OBJECT_FUNC_PTR)(STORE *, STORE_OBJECT_TYPES type, OPENSSL_ITEM attributes[], OPENSSL_ITEM parameters[]); +typedef int (*STORE_STORE_OBJECT_FUNC_PTR)(STORE *, STORE_OBJECT_TYPES type, STORE_OBJECT *data, OPENSSL_ITEM attributes[], OPENSSL_ITEM parameters[]); +typedef int (*STORE_MODIFY_OBJECT_FUNC_PTR)(STORE *, STORE_OBJECT_TYPES type, OPENSSL_ITEM search_attributes[], OPENSSL_ITEM add_attributes[], OPENSSL_ITEM modify_attributes[], OPENSSL_ITEM delete_attributes[], OPENSSL_ITEM parameters[]); +typedef int (*STORE_GENERIC_FUNC_PTR)(STORE *, OPENSSL_ITEM attributes[], OPENSSL_ITEM parameters[]); +typedef int (*STORE_CTRL_FUNC_PTR)(STORE *, int cmd, long l, void *p, void (*f)(void)); + +int STORE_method_set_initialise_function(STORE_METHOD *sm, STORE_INITIALISE_FUNC_PTR init_f); +int STORE_method_set_cleanup_function(STORE_METHOD *sm, STORE_CLEANUP_FUNC_PTR clean_f); +int STORE_method_set_generate_function(STORE_METHOD *sm, STORE_GENERATE_OBJECT_FUNC_PTR generate_f); +int STORE_method_set_get_function(STORE_METHOD *sm, STORE_GET_OBJECT_FUNC_PTR get_f); +int STORE_method_set_store_function(STORE_METHOD *sm, STORE_STORE_OBJECT_FUNC_PTR store_f); +int STORE_method_set_modify_function(STORE_METHOD *sm, STORE_MODIFY_OBJECT_FUNC_PTR store_f); +int STORE_method_set_revoke_function(STORE_METHOD *sm, STORE_HANDLE_OBJECT_FUNC_PTR revoke_f); +int STORE_method_set_delete_function(STORE_METHOD *sm, STORE_HANDLE_OBJECT_FUNC_PTR delete_f); +int STORE_method_set_list_start_function(STORE_METHOD *sm, STORE_START_OBJECT_FUNC_PTR list_start_f); +int STORE_method_set_list_next_function(STORE_METHOD *sm, STORE_NEXT_OBJECT_FUNC_PTR list_next_f); +int STORE_method_set_list_end_function(STORE_METHOD *sm, STORE_END_OBJECT_FUNC_PTR list_end_f); +int STORE_method_set_update_store_function(STORE_METHOD *sm, STORE_GENERIC_FUNC_PTR); +int STORE_method_set_lock_store_function(STORE_METHOD *sm, STORE_GENERIC_FUNC_PTR); +int STORE_method_set_unlock_store_function(STORE_METHOD *sm, STORE_GENERIC_FUNC_PTR); +int STORE_method_set_ctrl_function(STORE_METHOD *sm, STORE_CTRL_FUNC_PTR ctrl_f); + +STORE_INITIALISE_FUNC_PTR STORE_method_get_initialise_function(STORE_METHOD *sm); +STORE_CLEANUP_FUNC_PTR STORE_method_get_cleanup_function(STORE_METHOD *sm); +STORE_GENERATE_OBJECT_FUNC_PTR STORE_method_get_generate_function(STORE_METHOD *sm); +STORE_GET_OBJECT_FUNC_PTR STORE_method_get_get_function(STORE_METHOD *sm); +STORE_STORE_OBJECT_FUNC_PTR STORE_method_get_store_function(STORE_METHOD *sm); +STORE_MODIFY_OBJECT_FUNC_PTR STORE_method_get_modify_function(STORE_METHOD *sm); +STORE_HANDLE_OBJECT_FUNC_PTR STORE_method_get_revoke_function(STORE_METHOD *sm); +STORE_HANDLE_OBJECT_FUNC_PTR STORE_method_get_delete_function(STORE_METHOD *sm); +STORE_START_OBJECT_FUNC_PTR STORE_method_get_list_start_function(STORE_METHOD *sm); +STORE_NEXT_OBJECT_FUNC_PTR STORE_method_get_list_next_function(STORE_METHOD *sm); +STORE_END_OBJECT_FUNC_PTR STORE_method_get_list_end_function(STORE_METHOD *sm); +STORE_GENERIC_FUNC_PTR STORE_method_get_update_store_function(STORE_METHOD *sm); +STORE_GENERIC_FUNC_PTR STORE_method_get_lock_store_function(STORE_METHOD *sm); +STORE_GENERIC_FUNC_PTR STORE_method_get_unlock_store_function(STORE_METHOD *sm); +STORE_CTRL_FUNC_PTR STORE_method_get_ctrl_function(STORE_METHOD *sm); + +/* Method helper structures and functions. */ + +/* This structure is the result of parsing through the information in a list + of OPENSSL_ITEMs. It stores all the necessary information in a structured + way.*/ +typedef struct STORE_attr_info_st STORE_ATTR_INFO; + +/* Parse a list of OPENSSL_ITEMs and return a pointer to a STORE_ATTR_INFO. + Note that we do this in the list form, since the list of OPENSSL_ITEMs can + come in blocks separated with STORE_ATTR_OR. Note that the value returned + by STORE_parse_attrs_next() must be freed with STORE_ATTR_INFO_free(). */ +void *STORE_parse_attrs_start(OPENSSL_ITEM *attributes); +STORE_ATTR_INFO *STORE_parse_attrs_next(void *handle); +int STORE_parse_attrs_end(void *handle); +int STORE_parse_attrs_endp(void *handle); + +/* Creator and destructor */ +STORE_ATTR_INFO *STORE_ATTR_INFO_new(void); +int STORE_ATTR_INFO_free(STORE_ATTR_INFO *attrs); + +/* Manipulators */ +char *STORE_ATTR_INFO_get0_cstr(STORE_ATTR_INFO *attrs, STORE_ATTR_TYPES code); +unsigned char *STORE_ATTR_INFO_get0_sha1str(STORE_ATTR_INFO *attrs, + STORE_ATTR_TYPES code); +X509_NAME *STORE_ATTR_INFO_get0_dn(STORE_ATTR_INFO *attrs, STORE_ATTR_TYPES code); +BIGNUM *STORE_ATTR_INFO_get0_number(STORE_ATTR_INFO *attrs, STORE_ATTR_TYPES code); +int STORE_ATTR_INFO_set_cstr(STORE_ATTR_INFO *attrs, STORE_ATTR_TYPES code, + char *cstr, size_t cstr_size); +int STORE_ATTR_INFO_set_sha1str(STORE_ATTR_INFO *attrs, STORE_ATTR_TYPES code, + unsigned char *sha1str, size_t sha1str_size); +int STORE_ATTR_INFO_set_dn(STORE_ATTR_INFO *attrs, STORE_ATTR_TYPES code, + X509_NAME *dn); +int STORE_ATTR_INFO_set_number(STORE_ATTR_INFO *attrs, STORE_ATTR_TYPES code, + BIGNUM *number); +int STORE_ATTR_INFO_modify_cstr(STORE_ATTR_INFO *attrs, STORE_ATTR_TYPES code, + char *cstr, size_t cstr_size); +int STORE_ATTR_INFO_modify_sha1str(STORE_ATTR_INFO *attrs, STORE_ATTR_TYPES code, + unsigned char *sha1str, size_t sha1str_size); +int STORE_ATTR_INFO_modify_dn(STORE_ATTR_INFO *attrs, STORE_ATTR_TYPES code, + X509_NAME *dn); +int STORE_ATTR_INFO_modify_number(STORE_ATTR_INFO *attrs, STORE_ATTR_TYPES code, + BIGNUM *number); + +/* Compare on basis of a bit pattern formed by the STORE_ATTR_TYPES values + in each contained attribute. */ +int STORE_ATTR_INFO_compare(STORE_ATTR_INFO *a, STORE_ATTR_INFO *b); +/* Check if the set of attributes in a is within the range of attributes + set in b. */ +int STORE_ATTR_INFO_in_range(STORE_ATTR_INFO *a, STORE_ATTR_INFO *b); +/* Check if the set of attributes in a are also set in b. */ +int STORE_ATTR_INFO_in(STORE_ATTR_INFO *a, STORE_ATTR_INFO *b); +/* Same as STORE_ATTR_INFO_in(), but also checks the attribute values. */ +int STORE_ATTR_INFO_in_ex(STORE_ATTR_INFO *a, STORE_ATTR_INFO *b); + + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_STORE_strings(void); + +/* Error codes for the STORE functions. */ + +/* Function codes. */ +#define STORE_F_MEM_DELETE 134 +#define STORE_F_MEM_GENERATE 135 +#define STORE_F_MEM_LIST_END 168 +#define STORE_F_MEM_LIST_NEXT 136 +#define STORE_F_MEM_LIST_START 137 +#define STORE_F_MEM_MODIFY 169 +#define STORE_F_MEM_STORE 138 +#define STORE_F_STORE_ATTR_INFO_GET0_CSTR 139 +#define STORE_F_STORE_ATTR_INFO_GET0_DN 140 +#define STORE_F_STORE_ATTR_INFO_GET0_NUMBER 141 +#define STORE_F_STORE_ATTR_INFO_GET0_SHA1STR 142 +#define STORE_F_STORE_ATTR_INFO_MODIFY_CSTR 143 +#define STORE_F_STORE_ATTR_INFO_MODIFY_DN 144 +#define STORE_F_STORE_ATTR_INFO_MODIFY_NUMBER 145 +#define STORE_F_STORE_ATTR_INFO_MODIFY_SHA1STR 146 +#define STORE_F_STORE_ATTR_INFO_SET_CSTR 147 +#define STORE_F_STORE_ATTR_INFO_SET_DN 148 +#define STORE_F_STORE_ATTR_INFO_SET_NUMBER 149 +#define STORE_F_STORE_ATTR_INFO_SET_SHA1STR 150 +#define STORE_F_STORE_CERTIFICATE 170 +#define STORE_F_STORE_CTRL 161 +#define STORE_F_STORE_DELETE_ARBITRARY 158 +#define STORE_F_STORE_DELETE_CERTIFICATE 102 +#define STORE_F_STORE_DELETE_CRL 103 +#define STORE_F_STORE_DELETE_NUMBER 104 +#define STORE_F_STORE_DELETE_PRIVATE_KEY 105 +#define STORE_F_STORE_DELETE_PUBLIC_KEY 106 +#define STORE_F_STORE_GENERATE_CRL 107 +#define STORE_F_STORE_GENERATE_KEY 108 +#define STORE_F_STORE_GET_ARBITRARY 159 +#define STORE_F_STORE_GET_CERTIFICATE 109 +#define STORE_F_STORE_GET_CRL 110 +#define STORE_F_STORE_GET_NUMBER 111 +#define STORE_F_STORE_GET_PRIVATE_KEY 112 +#define STORE_F_STORE_GET_PUBLIC_KEY 113 +#define STORE_F_STORE_LIST_CERTIFICATE_END 114 +#define STORE_F_STORE_LIST_CERTIFICATE_ENDP 153 +#define STORE_F_STORE_LIST_CERTIFICATE_NEXT 115 +#define STORE_F_STORE_LIST_CERTIFICATE_START 116 +#define STORE_F_STORE_LIST_CRL_END 117 +#define STORE_F_STORE_LIST_CRL_ENDP 154 +#define STORE_F_STORE_LIST_CRL_NEXT 118 +#define STORE_F_STORE_LIST_CRL_START 119 +#define STORE_F_STORE_LIST_PRIVATE_KEY_END 120 +#define STORE_F_STORE_LIST_PRIVATE_KEY_ENDP 155 +#define STORE_F_STORE_LIST_PRIVATE_KEY_NEXT 121 +#define STORE_F_STORE_LIST_PRIVATE_KEY_START 122 +#define STORE_F_STORE_LIST_PUBLIC_KEY_END 123 +#define STORE_F_STORE_LIST_PUBLIC_KEY_ENDP 156 +#define STORE_F_STORE_LIST_PUBLIC_KEY_NEXT 124 +#define STORE_F_STORE_LIST_PUBLIC_KEY_START 125 +#define STORE_F_STORE_MODIFY_ARBITRARY 162 +#define STORE_F_STORE_MODIFY_CERTIFICATE 163 +#define STORE_F_STORE_MODIFY_CRL 164 +#define STORE_F_STORE_MODIFY_NUMBER 165 +#define STORE_F_STORE_MODIFY_PRIVATE_KEY 166 +#define STORE_F_STORE_MODIFY_PUBLIC_KEY 167 +#define STORE_F_STORE_NEW_ENGINE 133 +#define STORE_F_STORE_NEW_METHOD 132 +#define STORE_F_STORE_PARSE_ATTRS_END 151 +#define STORE_F_STORE_PARSE_ATTRS_ENDP 172 +#define STORE_F_STORE_PARSE_ATTRS_NEXT 152 +#define STORE_F_STORE_PARSE_ATTRS_START 171 +#define STORE_F_STORE_REVOKE_CERTIFICATE 129 +#define STORE_F_STORE_REVOKE_PRIVATE_KEY 130 +#define STORE_F_STORE_REVOKE_PUBLIC_KEY 131 +#define STORE_F_STORE_STORE_ARBITRARY 157 +#define STORE_F_STORE_STORE_CERTIFICATE 100 +#define STORE_F_STORE_STORE_CRL 101 +#define STORE_F_STORE_STORE_NUMBER 126 +#define STORE_F_STORE_STORE_PRIVATE_KEY 127 +#define STORE_F_STORE_STORE_PUBLIC_KEY 128 + +/* Reason codes. */ +#define STORE_R_ALREADY_HAS_A_VALUE 127 +#define STORE_R_FAILED_DELETING_ARBITRARY 132 +#define STORE_R_FAILED_DELETING_CERTIFICATE 100 +#define STORE_R_FAILED_DELETING_KEY 101 +#define STORE_R_FAILED_DELETING_NUMBER 102 +#define STORE_R_FAILED_GENERATING_CRL 103 +#define STORE_R_FAILED_GENERATING_KEY 104 +#define STORE_R_FAILED_GETTING_ARBITRARY 133 +#define STORE_R_FAILED_GETTING_CERTIFICATE 105 +#define STORE_R_FAILED_GETTING_KEY 106 +#define STORE_R_FAILED_GETTING_NUMBER 107 +#define STORE_R_FAILED_LISTING_CERTIFICATES 108 +#define STORE_R_FAILED_LISTING_KEYS 109 +#define STORE_R_FAILED_MODIFYING_ARBITRARY 138 +#define STORE_R_FAILED_MODIFYING_CERTIFICATE 139 +#define STORE_R_FAILED_MODIFYING_CRL 140 +#define STORE_R_FAILED_MODIFYING_NUMBER 141 +#define STORE_R_FAILED_MODIFYING_PRIVATE_KEY 142 +#define STORE_R_FAILED_MODIFYING_PUBLIC_KEY 143 +#define STORE_R_FAILED_REVOKING_CERTIFICATE 110 +#define STORE_R_FAILED_REVOKING_KEY 111 +#define STORE_R_FAILED_STORING_ARBITRARY 134 +#define STORE_R_FAILED_STORING_CERTIFICATE 112 +#define STORE_R_FAILED_STORING_KEY 113 +#define STORE_R_FAILED_STORING_NUMBER 114 +#define STORE_R_NOT_IMPLEMENTED 128 +#define STORE_R_NO_CONTROL_FUNCTION 144 +#define STORE_R_NO_DELETE_ARBITRARY_FUNCTION 135 +#define STORE_R_NO_DELETE_NUMBER_FUNCTION 115 +#define STORE_R_NO_DELETE_OBJECT_FUNCTION 116 +#define STORE_R_NO_GENERATE_CRL_FUNCTION 117 +#define STORE_R_NO_GENERATE_OBJECT_FUNCTION 118 +#define STORE_R_NO_GET_OBJECT_ARBITRARY_FUNCTION 136 +#define STORE_R_NO_GET_OBJECT_FUNCTION 119 +#define STORE_R_NO_GET_OBJECT_NUMBER_FUNCTION 120 +#define STORE_R_NO_LIST_OBJECT_ENDP_FUNCTION 131 +#define STORE_R_NO_LIST_OBJECT_END_FUNCTION 121 +#define STORE_R_NO_LIST_OBJECT_NEXT_FUNCTION 122 +#define STORE_R_NO_LIST_OBJECT_START_FUNCTION 123 +#define STORE_R_NO_MODIFY_OBJECT_FUNCTION 145 +#define STORE_R_NO_REVOKE_OBJECT_FUNCTION 124 +#define STORE_R_NO_STORE 129 +#define STORE_R_NO_STORE_OBJECT_ARBITRARY_FUNCTION 137 +#define STORE_R_NO_STORE_OBJECT_FUNCTION 125 +#define STORE_R_NO_STORE_OBJECT_NUMBER_FUNCTION 126 +#define STORE_R_NO_VALUE 130 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/symhacks.h b/production/3rdparty/openssl/include/openssl/symhacks.h new file mode 100644 index 00000000..7e3602d2 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/symhacks.h @@ -0,0 +1,383 @@ +/* ==================================================================== + * Copyright (c) 1999 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#ifndef HEADER_SYMHACKS_H +#define HEADER_SYMHACKS_H + +#include + +/* Hacks to solve the problem with linkers incapable of handling very long + symbol names. In the case of VMS, the limit is 31 characters on VMS for + VAX. */ +#ifdef OPENSSL_SYS_VMS + +/* Hack a long name in crypto/ex_data.c */ +#undef CRYPTO_get_ex_data_implementation +#define CRYPTO_get_ex_data_implementation CRYPTO_get_ex_data_impl +#undef CRYPTO_set_ex_data_implementation +#define CRYPTO_set_ex_data_implementation CRYPTO_set_ex_data_impl + +/* Hack a long name in crypto/asn1/a_mbstr.c */ +#undef ASN1_STRING_set_default_mask_asc +#define ASN1_STRING_set_default_mask_asc ASN1_STRING_set_def_mask_asc + +#if 0 /* No longer needed, since safestack macro magic does the job */ +/* Hack the names created with DECLARE_ASN1_SET_OF(PKCS7_SIGNER_INFO) */ +#undef i2d_ASN1_SET_OF_PKCS7_SIGNER_INFO +#define i2d_ASN1_SET_OF_PKCS7_SIGNER_INFO i2d_ASN1_SET_OF_PKCS7_SIGINF +#undef d2i_ASN1_SET_OF_PKCS7_SIGNER_INFO +#define d2i_ASN1_SET_OF_PKCS7_SIGNER_INFO d2i_ASN1_SET_OF_PKCS7_SIGINF +#endif + +#if 0 /* No longer needed, since safestack macro magic does the job */ +/* Hack the names created with DECLARE_ASN1_SET_OF(PKCS7_RECIP_INFO) */ +#undef i2d_ASN1_SET_OF_PKCS7_RECIP_INFO +#define i2d_ASN1_SET_OF_PKCS7_RECIP_INFO i2d_ASN1_SET_OF_PKCS7_RECINF +#undef d2i_ASN1_SET_OF_PKCS7_RECIP_INFO +#define d2i_ASN1_SET_OF_PKCS7_RECIP_INFO d2i_ASN1_SET_OF_PKCS7_RECINF +#endif + +#if 0 /* No longer needed, since safestack macro magic does the job */ +/* Hack the names created with DECLARE_ASN1_SET_OF(ACCESS_DESCRIPTION) */ +#undef i2d_ASN1_SET_OF_ACCESS_DESCRIPTION +#define i2d_ASN1_SET_OF_ACCESS_DESCRIPTION i2d_ASN1_SET_OF_ACC_DESC +#undef d2i_ASN1_SET_OF_ACCESS_DESCRIPTION +#define d2i_ASN1_SET_OF_ACCESS_DESCRIPTION d2i_ASN1_SET_OF_ACC_DESC +#endif + +/* Hack the names created with DECLARE_PEM_rw(NETSCAPE_CERT_SEQUENCE) */ +#undef PEM_read_NETSCAPE_CERT_SEQUENCE +#define PEM_read_NETSCAPE_CERT_SEQUENCE PEM_read_NS_CERT_SEQ +#undef PEM_write_NETSCAPE_CERT_SEQUENCE +#define PEM_write_NETSCAPE_CERT_SEQUENCE PEM_write_NS_CERT_SEQ +#undef PEM_read_bio_NETSCAPE_CERT_SEQUENCE +#define PEM_read_bio_NETSCAPE_CERT_SEQUENCE PEM_read_bio_NS_CERT_SEQ +#undef PEM_write_bio_NETSCAPE_CERT_SEQUENCE +#define PEM_write_bio_NETSCAPE_CERT_SEQUENCE PEM_write_bio_NS_CERT_SEQ +#undef PEM_write_cb_bio_NETSCAPE_CERT_SEQUENCE +#define PEM_write_cb_bio_NETSCAPE_CERT_SEQUENCE PEM_write_cb_bio_NS_CERT_SEQ + +/* Hack the names created with DECLARE_PEM_rw(PKCS8_PRIV_KEY_INFO) */ +#undef PEM_read_PKCS8_PRIV_KEY_INFO +#define PEM_read_PKCS8_PRIV_KEY_INFO PEM_read_P8_PRIV_KEY_INFO +#undef PEM_write_PKCS8_PRIV_KEY_INFO +#define PEM_write_PKCS8_PRIV_KEY_INFO PEM_write_P8_PRIV_KEY_INFO +#undef PEM_read_bio_PKCS8_PRIV_KEY_INFO +#define PEM_read_bio_PKCS8_PRIV_KEY_INFO PEM_read_bio_P8_PRIV_KEY_INFO +#undef PEM_write_bio_PKCS8_PRIV_KEY_INFO +#define PEM_write_bio_PKCS8_PRIV_KEY_INFO PEM_write_bio_P8_PRIV_KEY_INFO +#undef PEM_write_cb_bio_PKCS8_PRIV_KEY_INFO +#define PEM_write_cb_bio_PKCS8_PRIV_KEY_INFO PEM_wrt_cb_bio_P8_PRIV_KEY_INFO + +/* Hack other PEM names */ +#undef PEM_write_bio_PKCS8PrivateKey_nid +#define PEM_write_bio_PKCS8PrivateKey_nid PEM_write_bio_PKCS8PrivKey_nid + +/* Hack some long X509 names */ +#undef X509_REVOKED_get_ext_by_critical +#define X509_REVOKED_get_ext_by_critical X509_REVOKED_get_ext_by_critic +#undef X509_policy_tree_get0_user_policies +#define X509_policy_tree_get0_user_policies X509_pcy_tree_get0_usr_policies +#undef X509_policy_node_get0_qualifiers +#define X509_policy_node_get0_qualifiers X509_pcy_node_get0_qualifiers +#undef X509_STORE_CTX_get_explicit_policy +#define X509_STORE_CTX_get_explicit_policy X509_STORE_CTX_get_expl_policy + +/* Hack some long CRYPTO names */ +#undef CRYPTO_set_dynlock_destroy_callback +#define CRYPTO_set_dynlock_destroy_callback CRYPTO_set_dynlock_destroy_cb +#undef CRYPTO_set_dynlock_create_callback +#define CRYPTO_set_dynlock_create_callback CRYPTO_set_dynlock_create_cb +#undef CRYPTO_set_dynlock_lock_callback +#define CRYPTO_set_dynlock_lock_callback CRYPTO_set_dynlock_lock_cb +#undef CRYPTO_get_dynlock_lock_callback +#define CRYPTO_get_dynlock_lock_callback CRYPTO_get_dynlock_lock_cb +#undef CRYPTO_get_dynlock_destroy_callback +#define CRYPTO_get_dynlock_destroy_callback CRYPTO_get_dynlock_destroy_cb +#undef CRYPTO_get_dynlock_create_callback +#define CRYPTO_get_dynlock_create_callback CRYPTO_get_dynlock_create_cb +#undef CRYPTO_set_locked_mem_ex_functions +#define CRYPTO_set_locked_mem_ex_functions CRYPTO_set_locked_mem_ex_funcs +#undef CRYPTO_get_locked_mem_ex_functions +#define CRYPTO_get_locked_mem_ex_functions CRYPTO_get_locked_mem_ex_funcs + +/* Hack some long SSL names */ +#undef SSL_CTX_set_default_verify_paths +#define SSL_CTX_set_default_verify_paths SSL_CTX_set_def_verify_paths +#undef SSL_get_ex_data_X509_STORE_CTX_idx +#define SSL_get_ex_data_X509_STORE_CTX_idx SSL_get_ex_d_X509_STORE_CTX_idx +#undef SSL_add_file_cert_subjects_to_stack +#define SSL_add_file_cert_subjects_to_stack SSL_add_file_cert_subjs_to_stk +#undef SSL_add_dir_cert_subjects_to_stack +#define SSL_add_dir_cert_subjects_to_stack SSL_add_dir_cert_subjs_to_stk +#undef SSL_CTX_use_certificate_chain_file +#define SSL_CTX_use_certificate_chain_file SSL_CTX_use_cert_chain_file +#undef SSL_CTX_set_cert_verify_callback +#define SSL_CTX_set_cert_verify_callback SSL_CTX_set_cert_verify_cb +#undef SSL_CTX_set_default_passwd_cb_userdata +#define SSL_CTX_set_default_passwd_cb_userdata SSL_CTX_set_def_passwd_cb_ud +#undef SSL_COMP_get_compression_methods +#define SSL_COMP_get_compression_methods SSL_COMP_get_compress_methods + +/* Hack some long ENGINE names */ +#undef ENGINE_get_default_BN_mod_exp_crt +#define ENGINE_get_default_BN_mod_exp_crt ENGINE_get_def_BN_mod_exp_crt +#undef ENGINE_set_default_BN_mod_exp_crt +#define ENGINE_set_default_BN_mod_exp_crt ENGINE_set_def_BN_mod_exp_crt +#undef ENGINE_set_load_privkey_function +#define ENGINE_set_load_privkey_function ENGINE_set_load_privkey_fn +#undef ENGINE_get_load_privkey_function +#define ENGINE_get_load_privkey_function ENGINE_get_load_privkey_fn + +/* Hack some long OCSP names */ +#undef OCSP_REQUEST_get_ext_by_critical +#define OCSP_REQUEST_get_ext_by_critical OCSP_REQUEST_get_ext_by_crit +#undef OCSP_BASICRESP_get_ext_by_critical +#define OCSP_BASICRESP_get_ext_by_critical OCSP_BASICRESP_get_ext_by_crit +#undef OCSP_SINGLERESP_get_ext_by_critical +#define OCSP_SINGLERESP_get_ext_by_critical OCSP_SINGLERESP_get_ext_by_crit + +/* Hack some long DES names */ +#undef _ossl_old_des_ede3_cfb64_encrypt +#define _ossl_old_des_ede3_cfb64_encrypt _ossl_odes_ede3_cfb64_encrypt +#undef _ossl_old_des_ede3_ofb64_encrypt +#define _ossl_old_des_ede3_ofb64_encrypt _ossl_odes_ede3_ofb64_encrypt + +/* Hack some long EVP names */ +#undef OPENSSL_add_all_algorithms_noconf +#define OPENSSL_add_all_algorithms_noconf OPENSSL_add_all_algo_noconf +#undef OPENSSL_add_all_algorithms_conf +#define OPENSSL_add_all_algorithms_conf OPENSSL_add_all_algo_conf + +/* Hack some long EC names */ +#undef EC_GROUP_set_point_conversion_form +#define EC_GROUP_set_point_conversion_form EC_GROUP_set_point_conv_form +#undef EC_GROUP_get_point_conversion_form +#define EC_GROUP_get_point_conversion_form EC_GROUP_get_point_conv_form +#undef EC_GROUP_clear_free_all_extra_data +#define EC_GROUP_clear_free_all_extra_data EC_GROUP_clr_free_all_xtra_data +#undef EC_POINT_set_Jprojective_coordinates_GFp +#define EC_POINT_set_Jprojective_coordinates_GFp \ + EC_POINT_set_Jproj_coords_GFp +#undef EC_POINT_get_Jprojective_coordinates_GFp +#define EC_POINT_get_Jprojective_coordinates_GFp \ + EC_POINT_get_Jproj_coords_GFp +#undef EC_POINT_set_affine_coordinates_GFp +#define EC_POINT_set_affine_coordinates_GFp EC_POINT_set_affine_coords_GFp +#undef EC_POINT_get_affine_coordinates_GFp +#define EC_POINT_get_affine_coordinates_GFp EC_POINT_get_affine_coords_GFp +#undef EC_POINT_set_compressed_coordinates_GFp +#define EC_POINT_set_compressed_coordinates_GFp EC_POINT_set_compr_coords_GFp +#undef EC_POINT_set_affine_coordinates_GF2m +#define EC_POINT_set_affine_coordinates_GF2m EC_POINT_set_affine_coords_GF2m +#undef EC_POINT_get_affine_coordinates_GF2m +#define EC_POINT_get_affine_coordinates_GF2m EC_POINT_get_affine_coords_GF2m +#undef EC_POINT_set_compressed_coordinates_GF2m +#define EC_POINT_set_compressed_coordinates_GF2m \ + EC_POINT_set_compr_coords_GF2m +#undef ec_GF2m_simple_group_clear_finish +#define ec_GF2m_simple_group_clear_finish ec_GF2m_simple_grp_clr_finish +#undef ec_GF2m_simple_group_check_discriminant +#define ec_GF2m_simple_group_check_discriminant ec_GF2m_simple_grp_chk_discrim +#undef ec_GF2m_simple_point_clear_finish +#define ec_GF2m_simple_point_clear_finish ec_GF2m_simple_pt_clr_finish +#undef ec_GF2m_simple_point_set_to_infinity +#define ec_GF2m_simple_point_set_to_infinity ec_GF2m_simple_pt_set_to_inf +#undef ec_GF2m_simple_points_make_affine +#define ec_GF2m_simple_points_make_affine ec_GF2m_simple_pts_make_affine +#undef ec_GF2m_simple_point_set_affine_coordinates +#define ec_GF2m_simple_point_set_affine_coordinates \ + ec_GF2m_smp_pt_set_af_coords +#undef ec_GF2m_simple_point_get_affine_coordinates +#define ec_GF2m_simple_point_get_affine_coordinates \ + ec_GF2m_smp_pt_get_af_coords +#undef ec_GF2m_simple_set_compressed_coordinates +#define ec_GF2m_simple_set_compressed_coordinates \ + ec_GF2m_smp_set_compr_coords +#undef ec_GFp_simple_group_set_curve_GFp +#define ec_GFp_simple_group_set_curve_GFp ec_GFp_simple_grp_set_curve_GFp +#undef ec_GFp_simple_group_get_curve_GFp +#define ec_GFp_simple_group_get_curve_GFp ec_GFp_simple_grp_get_curve_GFp +#undef ec_GFp_simple_group_clear_finish +#define ec_GFp_simple_group_clear_finish ec_GFp_simple_grp_clear_finish +#undef ec_GFp_simple_group_set_generator +#define ec_GFp_simple_group_set_generator ec_GFp_simple_grp_set_generator +#undef ec_GFp_simple_group_get0_generator +#define ec_GFp_simple_group_get0_generator ec_GFp_simple_grp_gt0_generator +#undef ec_GFp_simple_group_get_cofactor +#define ec_GFp_simple_group_get_cofactor ec_GFp_simple_grp_get_cofactor +#undef ec_GFp_simple_point_clear_finish +#define ec_GFp_simple_point_clear_finish ec_GFp_simple_pt_clear_finish +#undef ec_GFp_simple_point_set_to_infinity +#define ec_GFp_simple_point_set_to_infinity ec_GFp_simple_pt_set_to_inf +#undef ec_GFp_simple_points_make_affine +#define ec_GFp_simple_points_make_affine ec_GFp_simple_pts_make_affine +#undef ec_GFp_simple_group_get_curve_GFp +#define ec_GFp_simple_group_get_curve_GFp ec_GFp_simple_grp_get_curve_GFp +#undef ec_GFp_simple_set_Jprojective_coordinates_GFp +#define ec_GFp_simple_set_Jprojective_coordinates_GFp \ + ec_GFp_smp_set_Jproj_coords_GFp +#undef ec_GFp_simple_get_Jprojective_coordinates_GFp +#define ec_GFp_simple_get_Jprojective_coordinates_GFp \ + ec_GFp_smp_get_Jproj_coords_GFp +#undef ec_GFp_simple_point_set_affine_coordinates_GFp +#define ec_GFp_simple_point_set_affine_coordinates_GFp \ + ec_GFp_smp_pt_set_af_coords_GFp +#undef ec_GFp_simple_point_get_affine_coordinates_GFp +#define ec_GFp_simple_point_get_affine_coordinates_GFp \ + ec_GFp_smp_pt_get_af_coords_GFp +#undef ec_GFp_simple_set_compressed_coordinates_GFp +#define ec_GFp_simple_set_compressed_coordinates_GFp \ + ec_GFp_smp_set_compr_coords_GFp +#undef ec_GFp_simple_point_set_affine_coordinates +#define ec_GFp_simple_point_set_affine_coordinates \ + ec_GFp_smp_pt_set_af_coords +#undef ec_GFp_simple_point_get_affine_coordinates +#define ec_GFp_simple_point_get_affine_coordinates \ + ec_GFp_smp_pt_get_af_coords +#undef ec_GFp_simple_set_compressed_coordinates +#define ec_GFp_simple_set_compressed_coordinates \ + ec_GFp_smp_set_compr_coords +#undef ec_GFp_simple_group_check_discriminant +#define ec_GFp_simple_group_check_discriminant ec_GFp_simple_grp_chk_discrim + +/* Hack som long STORE names */ +#undef STORE_method_set_initialise_function +#define STORE_method_set_initialise_function STORE_meth_set_initialise_fn +#undef STORE_method_set_cleanup_function +#define STORE_method_set_cleanup_function STORE_meth_set_cleanup_fn +#undef STORE_method_set_generate_function +#define STORE_method_set_generate_function STORE_meth_set_generate_fn +#undef STORE_method_set_modify_function +#define STORE_method_set_modify_function STORE_meth_set_modify_fn +#undef STORE_method_set_revoke_function +#define STORE_method_set_revoke_function STORE_meth_set_revoke_fn +#undef STORE_method_set_delete_function +#define STORE_method_set_delete_function STORE_meth_set_delete_fn +#undef STORE_method_set_list_start_function +#define STORE_method_set_list_start_function STORE_meth_set_list_start_fn +#undef STORE_method_set_list_next_function +#define STORE_method_set_list_next_function STORE_meth_set_list_next_fn +#undef STORE_method_set_list_end_function +#define STORE_method_set_list_end_function STORE_meth_set_list_end_fn +#undef STORE_method_set_update_store_function +#define STORE_method_set_update_store_function STORE_meth_set_update_store_fn +#undef STORE_method_set_lock_store_function +#define STORE_method_set_lock_store_function STORE_meth_set_lock_store_fn +#undef STORE_method_set_unlock_store_function +#define STORE_method_set_unlock_store_function STORE_meth_set_unlock_store_fn +#undef STORE_method_get_initialise_function +#define STORE_method_get_initialise_function STORE_meth_get_initialise_fn +#undef STORE_method_get_cleanup_function +#define STORE_method_get_cleanup_function STORE_meth_get_cleanup_fn +#undef STORE_method_get_generate_function +#define STORE_method_get_generate_function STORE_meth_get_generate_fn +#undef STORE_method_get_modify_function +#define STORE_method_get_modify_function STORE_meth_get_modify_fn +#undef STORE_method_get_revoke_function +#define STORE_method_get_revoke_function STORE_meth_get_revoke_fn +#undef STORE_method_get_delete_function +#define STORE_method_get_delete_function STORE_meth_get_delete_fn +#undef STORE_method_get_list_start_function +#define STORE_method_get_list_start_function STORE_meth_get_list_start_fn +#undef STORE_method_get_list_next_function +#define STORE_method_get_list_next_function STORE_meth_get_list_next_fn +#undef STORE_method_get_list_end_function +#define STORE_method_get_list_end_function STORE_meth_get_list_end_fn +#undef STORE_method_get_update_store_function +#define STORE_method_get_update_store_function STORE_meth_get_update_store_fn +#undef STORE_method_get_lock_store_function +#define STORE_method_get_lock_store_function STORE_meth_get_lock_store_fn +#undef STORE_method_get_unlock_store_function +#define STORE_method_get_unlock_store_function STORE_meth_get_unlock_store_fn + +#endif /* defined OPENSSL_SYS_VMS */ + + +/* Case insensiteve linking causes problems.... */ +#if defined(OPENSSL_SYS_WIN16) || defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_OS2) +#undef ERR_load_CRYPTO_strings +#define ERR_load_CRYPTO_strings ERR_load_CRYPTOlib_strings +#undef OCSP_crlID_new +#define OCSP_crlID_new OCSP_crlID2_new + +#undef d2i_ECPARAMETERS +#define d2i_ECPARAMETERS d2i_UC_ECPARAMETERS +#undef i2d_ECPARAMETERS +#define i2d_ECPARAMETERS i2d_UC_ECPARAMETERS +#undef d2i_ECPKPARAMETERS +#define d2i_ECPKPARAMETERS d2i_UC_ECPKPARAMETERS +#undef i2d_ECPKPARAMETERS +#define i2d_ECPKPARAMETERS i2d_UC_ECPKPARAMETERS + +/* These functions do not seem to exist! However, I'm paranoid... + Original command in x509v3.h: + These functions are being redefined in another directory, + and clash when the linker is case-insensitive, so let's + hide them a little, by giving them an extra 'o' at the + beginning of the name... */ +#undef X509v3_cleanup_extensions +#define X509v3_cleanup_extensions oX509v3_cleanup_extensions +#undef X509v3_add_extension +#define X509v3_add_extension oX509v3_add_extension +#undef X509v3_add_netscape_extensions +#define X509v3_add_netscape_extensions oX509v3_add_netscape_extensions +#undef X509v3_add_standard_extensions +#define X509v3_add_standard_extensions oX509v3_add_standard_extensions + + +#endif + + +#endif /* ! defined HEADER_VMS_IDHACKS_H */ diff --git a/production/3rdparty/openssl/include/openssl/tls1.h b/production/3rdparty/openssl/include/openssl/tls1.h new file mode 100644 index 00000000..f8a215e6 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/tls1.h @@ -0,0 +1,274 @@ +/* ssl/tls1.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * + * Portions of the attached software ("Contribution") are developed by + * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project. + * + * The Contribution is licensed pursuant to the OpenSSL open source + * license provided above. + * + * ECC cipher suite support in OpenSSL originally written by + * Vipul Gupta and Sumit Gupta of Sun Microsystems Laboratories. + * + */ + +#ifndef HEADER_TLS1_H +#define HEADER_TLS1_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TLS1_ALLOW_EXPERIMENTAL_CIPHERSUITES 1 + +#define TLS1_VERSION 0x0301 +#define TLS1_VERSION_MAJOR 0x03 +#define TLS1_VERSION_MINOR 0x01 + +#define TLS1_AD_DECRYPTION_FAILED 21 +#define TLS1_AD_RECORD_OVERFLOW 22 +#define TLS1_AD_UNKNOWN_CA 48 /* fatal */ +#define TLS1_AD_ACCESS_DENIED 49 /* fatal */ +#define TLS1_AD_DECODE_ERROR 50 /* fatal */ +#define TLS1_AD_DECRYPT_ERROR 51 +#define TLS1_AD_EXPORT_RESTRICTION 60 /* fatal */ +#define TLS1_AD_PROTOCOL_VERSION 70 /* fatal */ +#define TLS1_AD_INSUFFICIENT_SECURITY 71 /* fatal */ +#define TLS1_AD_INTERNAL_ERROR 80 /* fatal */ +#define TLS1_AD_USER_CANCELLED 90 +#define TLS1_AD_NO_RENEGOTIATION 100 + +/* Additional TLS ciphersuites from draft-ietf-tls-56-bit-ciphersuites-00.txt + * (available if TLS1_ALLOW_EXPERIMENTAL_CIPHERSUITES is defined, see + * s3_lib.c). We actually treat them like SSL 3.0 ciphers, which we probably + * shouldn't. */ +#define TLS1_CK_RSA_EXPORT1024_WITH_RC4_56_MD5 0x03000060 +#define TLS1_CK_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5 0x03000061 +#define TLS1_CK_RSA_EXPORT1024_WITH_DES_CBC_SHA 0x03000062 +#define TLS1_CK_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA 0x03000063 +#define TLS1_CK_RSA_EXPORT1024_WITH_RC4_56_SHA 0x03000064 +#define TLS1_CK_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA 0x03000065 +#define TLS1_CK_DHE_DSS_WITH_RC4_128_SHA 0x03000066 + +/* AES ciphersuites from RFC3268 */ + +#define TLS1_CK_RSA_WITH_AES_128_SHA 0x0300002F +#define TLS1_CK_DH_DSS_WITH_AES_128_SHA 0x03000030 +#define TLS1_CK_DH_RSA_WITH_AES_128_SHA 0x03000031 +#define TLS1_CK_DHE_DSS_WITH_AES_128_SHA 0x03000032 +#define TLS1_CK_DHE_RSA_WITH_AES_128_SHA 0x03000033 +#define TLS1_CK_ADH_WITH_AES_128_SHA 0x03000034 + +#define TLS1_CK_RSA_WITH_AES_256_SHA 0x03000035 +#define TLS1_CK_DH_DSS_WITH_AES_256_SHA 0x03000036 +#define TLS1_CK_DH_RSA_WITH_AES_256_SHA 0x03000037 +#define TLS1_CK_DHE_DSS_WITH_AES_256_SHA 0x03000038 +#define TLS1_CK_DHE_RSA_WITH_AES_256_SHA 0x03000039 +#define TLS1_CK_ADH_WITH_AES_256_SHA 0x0300003A + +/* ECC ciphersuites from draft-ietf-tls-ecc-12.txt with changes soon to be in draft 13 */ +#define TLS1_CK_ECDH_ECDSA_WITH_NULL_SHA 0x0300C001 +#define TLS1_CK_ECDH_ECDSA_WITH_RC4_128_SHA 0x0300C002 +#define TLS1_CK_ECDH_ECDSA_WITH_DES_192_CBC3_SHA 0x0300C003 +#define TLS1_CK_ECDH_ECDSA_WITH_AES_128_CBC_SHA 0x0300C004 +#define TLS1_CK_ECDH_ECDSA_WITH_AES_256_CBC_SHA 0x0300C005 + +#define TLS1_CK_ECDHE_ECDSA_WITH_NULL_SHA 0x0300C006 +#define TLS1_CK_ECDHE_ECDSA_WITH_RC4_128_SHA 0x0300C007 +#define TLS1_CK_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA 0x0300C008 +#define TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA 0x0300C009 +#define TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA 0x0300C00A + +#define TLS1_CK_ECDH_RSA_WITH_NULL_SHA 0x0300C00B +#define TLS1_CK_ECDH_RSA_WITH_RC4_128_SHA 0x0300C00C +#define TLS1_CK_ECDH_RSA_WITH_DES_192_CBC3_SHA 0x0300C00D +#define TLS1_CK_ECDH_RSA_WITH_AES_128_CBC_SHA 0x0300C00E +#define TLS1_CK_ECDH_RSA_WITH_AES_256_CBC_SHA 0x0300C00F + +#define TLS1_CK_ECDHE_RSA_WITH_NULL_SHA 0x0300C010 +#define TLS1_CK_ECDHE_RSA_WITH_RC4_128_SHA 0x0300C011 +#define TLS1_CK_ECDHE_RSA_WITH_DES_192_CBC3_SHA 0x0300C012 +#define TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA 0x0300C013 +#define TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA 0x0300C014 + +#define TLS1_CK_ECDH_anon_WITH_NULL_SHA 0x0300C015 +#define TLS1_CK_ECDH_anon_WITH_RC4_128_SHA 0x0300C016 +#define TLS1_CK_ECDH_anon_WITH_DES_192_CBC3_SHA 0x0300C017 +#define TLS1_CK_ECDH_anon_WITH_AES_128_CBC_SHA 0x0300C018 +#define TLS1_CK_ECDH_anon_WITH_AES_256_CBC_SHA 0x0300C019 + +/* XXX + * Inconsistency alert: + * The OpenSSL names of ciphers with ephemeral DH here include the string + * "DHE", while elsewhere it has always been "EDH". + * (The alias for the list of all such ciphers also is "EDH".) + * The specifications speak of "EDH"; maybe we should allow both forms + * for everything. */ +#define TLS1_TXT_RSA_EXPORT1024_WITH_RC4_56_MD5 "EXP1024-RC4-MD5" +#define TLS1_TXT_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5 "EXP1024-RC2-CBC-MD5" +#define TLS1_TXT_RSA_EXPORT1024_WITH_DES_CBC_SHA "EXP1024-DES-CBC-SHA" +#define TLS1_TXT_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA "EXP1024-DHE-DSS-DES-CBC-SHA" +#define TLS1_TXT_RSA_EXPORT1024_WITH_RC4_56_SHA "EXP1024-RC4-SHA" +#define TLS1_TXT_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA "EXP1024-DHE-DSS-RC4-SHA" +#define TLS1_TXT_DHE_DSS_WITH_RC4_128_SHA "DHE-DSS-RC4-SHA" + +/* AES ciphersuites from RFC3268 */ +#define TLS1_TXT_RSA_WITH_AES_128_SHA "AES128-SHA" +#define TLS1_TXT_DH_DSS_WITH_AES_128_SHA "DH-DSS-AES128-SHA" +#define TLS1_TXT_DH_RSA_WITH_AES_128_SHA "DH-RSA-AES128-SHA" +#define TLS1_TXT_DHE_DSS_WITH_AES_128_SHA "DHE-DSS-AES128-SHA" +#define TLS1_TXT_DHE_RSA_WITH_AES_128_SHA "DHE-RSA-AES128-SHA" +#define TLS1_TXT_ADH_WITH_AES_128_SHA "ADH-AES128-SHA" + +#define TLS1_TXT_RSA_WITH_AES_256_SHA "AES256-SHA" +#define TLS1_TXT_DH_DSS_WITH_AES_256_SHA "DH-DSS-AES256-SHA" +#define TLS1_TXT_DH_RSA_WITH_AES_256_SHA "DH-RSA-AES256-SHA" +#define TLS1_TXT_DHE_DSS_WITH_AES_256_SHA "DHE-DSS-AES256-SHA" +#define TLS1_TXT_DHE_RSA_WITH_AES_256_SHA "DHE-RSA-AES256-SHA" +#define TLS1_TXT_ADH_WITH_AES_256_SHA "ADH-AES256-SHA" + +/* ECC ciphersuites from draft-ietf-tls-ecc-01.txt (Mar 15, 2001) */ +#define TLS1_TXT_ECDH_ECDSA_WITH_NULL_SHA "ECDH-ECDSA-NULL-SHA" +#define TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA "ECDH-ECDSA-RC4-SHA" +#define TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA "ECDH-ECDSA-DES-CBC3-SHA" +#define TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA "ECDH-ECDSA-AES128-SHA" +#define TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA "ECDH-ECDSA-AES256-SHA" + +#define TLS1_TXT_ECDHE_ECDSA_WITH_NULL_SHA "ECDHE-ECDSA-NULL-SHA" +#define TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA "ECDHE-ECDSA-RC4-SHA" +#define TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA "ECDHE-ECDSA-DES-CBC3-SHA" +#define TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA "ECDHE-ECDSA-AES128-SHA" +#define TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA "ECDHE-ECDSA-AES256-SHA" + +#define TLS1_TXT_ECDH_RSA_WITH_NULL_SHA "ECDH-RSA-NULL-SHA" +#define TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA "ECDH-RSA-RC4-SHA" +#define TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA "ECDH-RSA-DES-CBC3-SHA" +#define TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA "ECDH-RSA-AES128-SHA" +#define TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA "ECDH-RSA-AES256-SHA" + +#define TLS1_TXT_ECDHE_RSA_WITH_NULL_SHA "ECDHE-RSA-NULL-SHA" +#define TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA "ECDHE-RSA-RC4-SHA" +#define TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA "ECDHE-RSA-DES-CBC3-SHA" +#define TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA "ECDHE-RSA-AES128-SHA" +#define TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA "ECDHE-RSA-AES256-SHA" + +#define TLS1_TXT_ECDH_anon_WITH_NULL_SHA "AECDH-NULL-SHA" +#define TLS1_TXT_ECDH_anon_WITH_RC4_128_SHA "AECDH-RC4-SHA" +#define TLS1_TXT_ECDH_anon_WITH_DES_192_CBC3_SHA "AECDH-DES-CBC3-SHA" +#define TLS1_TXT_ECDH_anon_WITH_AES_128_CBC_SHA "AECDH-AES128-SHA" +#define TLS1_TXT_ECDH_anon_WITH_AES_256_CBC_SHA "AECDH-AES256-SHA" + +#define TLS_CT_RSA_SIGN 1 +#define TLS_CT_DSS_SIGN 2 +#define TLS_CT_RSA_FIXED_DH 3 +#define TLS_CT_DSS_FIXED_DH 4 +#define TLS_CT_ECDSA_SIGN 64 +#define TLS_CT_RSA_FIXED_ECDH 65 +#define TLS_CT_ECDSA_FIXED_ECDH 66 +#define TLS_CT_NUMBER 7 + +#define TLS1_FINISH_MAC_LENGTH 12 + +#define TLS_MD_MAX_CONST_SIZE 20 +#define TLS_MD_CLIENT_FINISH_CONST "client finished" +#define TLS_MD_CLIENT_FINISH_CONST_SIZE 15 +#define TLS_MD_SERVER_FINISH_CONST "server finished" +#define TLS_MD_SERVER_FINISH_CONST_SIZE 15 +#define TLS_MD_SERVER_WRITE_KEY_CONST "server write key" +#define TLS_MD_SERVER_WRITE_KEY_CONST_SIZE 16 +#define TLS_MD_KEY_EXPANSION_CONST "key expansion" +#define TLS_MD_KEY_EXPANSION_CONST_SIZE 13 +#define TLS_MD_CLIENT_WRITE_KEY_CONST "client write key" +#define TLS_MD_CLIENT_WRITE_KEY_CONST_SIZE 16 +#define TLS_MD_SERVER_WRITE_KEY_CONST "server write key" +#define TLS_MD_SERVER_WRITE_KEY_CONST_SIZE 16 +#define TLS_MD_IV_BLOCK_CONST "IV block" +#define TLS_MD_IV_BLOCK_CONST_SIZE 8 +#define TLS_MD_MASTER_SECRET_CONST "master secret" +#define TLS_MD_MASTER_SECRET_CONST_SIZE 13 + +#ifdef CHARSET_EBCDIC +#undef TLS_MD_CLIENT_FINISH_CONST +#define TLS_MD_CLIENT_FINISH_CONST "\x63\x6c\x69\x65\x6e\x74\x20\x66\x69\x6e\x69\x73\x68\x65\x64" /*client finished*/ +#undef TLS_MD_SERVER_FINISH_CONST +#define TLS_MD_SERVER_FINISH_CONST "\x73\x65\x72\x76\x65\x72\x20\x66\x69\x6e\x69\x73\x68\x65\x64" /*server finished*/ +#undef TLS_MD_SERVER_WRITE_KEY_CONST +#define TLS_MD_SERVER_WRITE_KEY_CONST "\x73\x65\x72\x76\x65\x72\x20\x77\x72\x69\x74\x65\x20\x6b\x65\x79" /*server write key*/ +#undef TLS_MD_KEY_EXPANSION_CONST +#define TLS_MD_KEY_EXPANSION_CONST "\x6b\x65\x79\x20\x65\x78\x70\x61\x6e\x73\x69\x6f\x6e" /*key expansion*/ +#undef TLS_MD_CLIENT_WRITE_KEY_CONST +#define TLS_MD_CLIENT_WRITE_KEY_CONST "\x63\x6c\x69\x65\x6e\x74\x20\x77\x72\x69\x74\x65\x20\x6b\x65\x79" /*client write key*/ +#undef TLS_MD_SERVER_WRITE_KEY_CONST +#define TLS_MD_SERVER_WRITE_KEY_CONST "\x73\x65\x72\x76\x65\x72\x20\x77\x72\x69\x74\x65\x20\x6b\x65\x79" /*server write key*/ +#undef TLS_MD_IV_BLOCK_CONST +#define TLS_MD_IV_BLOCK_CONST "\x49\x56\x20\x62\x6c\x6f\x63\x6b" /*IV block*/ +#undef TLS_MD_MASTER_SECRET_CONST +#define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" /*master secret*/ +#endif + +#ifdef __cplusplus +} +#endif +#endif + + + diff --git a/production/3rdparty/openssl/include/openssl/tmdiff.h b/production/3rdparty/openssl/include/openssl/tmdiff.h new file mode 100644 index 00000000..af5c41c6 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/tmdiff.h @@ -0,0 +1,93 @@ +/* crypto/tmdiff.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +/* Header for dynamic hash table routines + * Author - Eric Young + */ +/* ... erm yeah, "dynamic hash tables" you say? + * + * And what would dynamic hash tables have to do with any of this code *now*? + * AFAICS, this code is only referenced by crypto/bn/exp.c which is an unused + * file that I doubt compiles any more. speed.c is the only thing that could + * use this (and it has nothing to do with hash tables), yet it instead has its + * own duplication of all this stuff and looks, if anything, more complete. See + * the corresponding note in apps/speed.c. + * The Bemused - Geoff + */ + +#ifndef HEADER_TMDIFF_H +#define HEADER_TMDIFF_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ms_tm MS_TM; + +MS_TM *ms_time_new(void ); +void ms_time_free(MS_TM *a); +void ms_time_get(MS_TM *a); +double ms_time_diff(MS_TM *start, MS_TM *end); +int ms_time_cmp(const MS_TM *ap, const MS_TM *bp); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/production/3rdparty/openssl/include/openssl/txt_db.h b/production/3rdparty/openssl/include/openssl/txt_db.h new file mode 100644 index 00000000..307e1ba2 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/txt_db.h @@ -0,0 +1,109 @@ +/* crypto/txt_db/txt_db.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_TXT_DB_H +#define HEADER_TXT_DB_H + +#include +#ifndef OPENSSL_NO_BIO +#include +#endif +#include +#include + +#define DB_ERROR_OK 0 +#define DB_ERROR_MALLOC 1 +#define DB_ERROR_INDEX_CLASH 2 +#define DB_ERROR_INDEX_OUT_OF_RANGE 3 +#define DB_ERROR_NO_INDEX 4 +#define DB_ERROR_INSERT_INDEX_CLASH 5 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct txt_db_st + { + int num_fields; + STACK /* char ** */ *data; + LHASH **index; + int (**qual)(char **); + long error; + long arg1; + long arg2; + char **arg_row; + } TXT_DB; + +#ifndef OPENSSL_NO_BIO +TXT_DB *TXT_DB_read(BIO *in, int num); +long TXT_DB_write(BIO *out, TXT_DB *db); +#else +TXT_DB *TXT_DB_read(char *in, int num); +long TXT_DB_write(char *out, TXT_DB *db); +#endif +int TXT_DB_create_index(TXT_DB *db,int field,int (*qual)(char **), + LHASH_HASH_FN_TYPE hash, LHASH_COMP_FN_TYPE cmp); +void TXT_DB_free(TXT_DB *db); +char **TXT_DB_get_by_index(TXT_DB *db, int idx, char **value); +int TXT_DB_insert(TXT_DB *db,char **value); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/production/3rdparty/openssl/include/openssl/ui.h b/production/3rdparty/openssl/include/openssl/ui.h new file mode 100644 index 00000000..01829641 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/ui.h @@ -0,0 +1,381 @@ +/* crypto/ui/ui.h -*- mode:C; c-file-style: "eay" -*- */ +/* Written by Richard Levitte (richard@levitte.org) for the OpenSSL + * project 2001. + */ +/* ==================================================================== + * Copyright (c) 2001 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#ifndef HEADER_UI_H +#define HEADER_UI_H + +#ifndef OPENSSL_NO_DEPRECATED +#include +#endif +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Declared already in ossl_typ.h */ +/* typedef struct ui_st UI; */ +/* typedef struct ui_method_st UI_METHOD; */ + + +/* All the following functions return -1 or NULL on error and in some cases + (UI_process()) -2 if interrupted or in some other way cancelled. + When everything is fine, they return 0, a positive value or a non-NULL + pointer, all depending on their purpose. */ + +/* Creators and destructor. */ +UI *UI_new(void); +UI *UI_new_method(const UI_METHOD *method); +void UI_free(UI *ui); + +/* The following functions are used to add strings to be printed and prompt + strings to prompt for data. The names are UI_{add,dup}__string + and UI_{add,dup}_input_boolean. + + UI_{add,dup}__string have the following meanings: + add add a text or prompt string. The pointers given to these + functions are used verbatim, no copying is done. + dup make a copy of the text or prompt string, then add the copy + to the collection of strings in the user interface. + + The function is a name for the functionality that the given + string shall be used for. It can be one of: + input use the string as data prompt. + verify use the string as verification prompt. This + is used to verify a previous input. + info use the string for informational output. + error use the string for error output. + Honestly, there's currently no difference between info and error for the + moment. + + UI_{add,dup}_input_boolean have the same semantics for "add" and "dup", + and are typically used when one wants to prompt for a yes/no response. + + + All of the functions in this group take a UI and a prompt string. + The string input and verify addition functions also take a flag argument, + a buffer for the result to end up with, a minimum input size and a maximum + input size (the result buffer MUST be large enough to be able to contain + the maximum number of characters). Additionally, the verify addition + functions takes another buffer to compare the result against. + The boolean input functions take an action description string (which should + be safe to ignore if the expected user action is obvious, for example with + a dialog box with an OK button and a Cancel button), a string of acceptable + characters to mean OK and to mean Cancel. The two last strings are checked + to make sure they don't have common characters. Additionally, the same + flag argument as for the string input is taken, as well as a result buffer. + The result buffer is required to be at least one byte long. Depending on + the answer, the first character from the OK or the Cancel character strings + will be stored in the first byte of the result buffer. No NUL will be + added, so the result is *not* a string. + + On success, the all return an index of the added information. That index + is usefull when retrieving results with UI_get0_result(). */ +int UI_add_input_string(UI *ui, const char *prompt, int flags, + char *result_buf, int minsize, int maxsize); +int UI_dup_input_string(UI *ui, const char *prompt, int flags, + char *result_buf, int minsize, int maxsize); +int UI_add_verify_string(UI *ui, const char *prompt, int flags, + char *result_buf, int minsize, int maxsize, const char *test_buf); +int UI_dup_verify_string(UI *ui, const char *prompt, int flags, + char *result_buf, int minsize, int maxsize, const char *test_buf); +int UI_add_input_boolean(UI *ui, const char *prompt, const char *action_desc, + const char *ok_chars, const char *cancel_chars, + int flags, char *result_buf); +int UI_dup_input_boolean(UI *ui, const char *prompt, const char *action_desc, + const char *ok_chars, const char *cancel_chars, + int flags, char *result_buf); +int UI_add_info_string(UI *ui, const char *text); +int UI_dup_info_string(UI *ui, const char *text); +int UI_add_error_string(UI *ui, const char *text); +int UI_dup_error_string(UI *ui, const char *text); + +/* These are the possible flags. They can be or'ed together. */ +/* Use to have echoing of input */ +#define UI_INPUT_FLAG_ECHO 0x01 +/* Use a default password. Where that password is found is completely + up to the application, it might for example be in the user data set + with UI_add_user_data(). It is not recommended to have more than + one input in each UI being marked with this flag, or the application + might get confused. */ +#define UI_INPUT_FLAG_DEFAULT_PWD 0x02 + +/* The user of these routines may want to define flags of their own. The core + UI won't look at those, but will pass them on to the method routines. They + must use higher bits so they don't get confused with the UI bits above. + UI_INPUT_FLAG_USER_BASE tells which is the lowest bit to use. A good + example of use is this: + + #define MY_UI_FLAG1 (0x01 << UI_INPUT_FLAG_USER_BASE) + +*/ +#define UI_INPUT_FLAG_USER_BASE 16 + + +/* The following function helps construct a prompt. object_desc is a + textual short description of the object, for example "pass phrase", + and object_name is the name of the object (might be a card name or + a file name. + The returned string shall always be allocated on the heap with + OPENSSL_malloc(), and need to be free'd with OPENSSL_free(). + + If the ui_method doesn't contain a pointer to a user-defined prompt + constructor, a default string is built, looking like this: + + "Enter {object_desc} for {object_name}:" + + So, if object_desc has the value "pass phrase" and object_name has + the value "foo.key", the resulting string is: + + "Enter pass phrase for foo.key:" +*/ +char *UI_construct_prompt(UI *ui_method, + const char *object_desc, const char *object_name); + + +/* The following function is used to store a pointer to user-specific data. + Any previous such pointer will be returned and replaced. + + For callback purposes, this function makes a lot more sense than using + ex_data, since the latter requires that different parts of OpenSSL or + applications share the same ex_data index. + + Note that the UI_OpenSSL() method completely ignores the user data. + Other methods may not, however. */ +void *UI_add_user_data(UI *ui, void *user_data); +/* We need a user data retrieving function as well. */ +void *UI_get0_user_data(UI *ui); + +/* Return the result associated with a prompt given with the index i. */ +const char *UI_get0_result(UI *ui, int i); + +/* When all strings have been added, process the whole thing. */ +int UI_process(UI *ui); + +/* Give a user interface parametrised control commands. This can be used to + send down an integer, a data pointer or a function pointer, as well as + be used to get information from a UI. */ +int UI_ctrl(UI *ui, int cmd, long i, void *p, void (*f)(void)); + +/* The commands */ +/* Use UI_CONTROL_PRINT_ERRORS with the value 1 to have UI_process print the + OpenSSL error stack before printing any info or added error messages and + before any prompting. */ +#define UI_CTRL_PRINT_ERRORS 1 +/* Check if a UI_process() is possible to do again with the same instance of + a user interface. This makes UI_ctrl() return 1 if it is redoable, and 0 + if not. */ +#define UI_CTRL_IS_REDOABLE 2 + + +/* Some methods may use extra data */ +#define UI_set_app_data(s,arg) UI_set_ex_data(s,0,arg) +#define UI_get_app_data(s) UI_get_ex_data(s,0) +int UI_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, + CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); +int UI_set_ex_data(UI *r,int idx,void *arg); +void *UI_get_ex_data(UI *r, int idx); + +/* Use specific methods instead of the built-in one */ +void UI_set_default_method(const UI_METHOD *meth); +const UI_METHOD *UI_get_default_method(void); +const UI_METHOD *UI_get_method(UI *ui); +const UI_METHOD *UI_set_method(UI *ui, const UI_METHOD *meth); + +/* The method with all the built-in thingies */ +UI_METHOD *UI_OpenSSL(void); + + +/* ---------- For method writers ---------- */ +/* A method contains a number of functions that implement the low level + of the User Interface. The functions are: + + an opener This function starts a session, maybe by opening + a channel to a tty, or by opening a window. + a writer This function is called to write a given string, + maybe to the tty, maybe as a field label in a + window. + a flusher This function is called to flush everything that + has been output so far. It can be used to actually + display a dialog box after it has been built. + a reader This function is called to read a given prompt, + maybe from the tty, maybe from a field in a + window. Note that it's called wth all string + structures, not only the prompt ones, so it must + check such things itself. + a closer This function closes the session, maybe by closing + the channel to the tty, or closing the window. + + All these functions are expected to return: + + 0 on error. + 1 on success. + -1 on out-of-band events, for example if some prompting has + been canceled (by pressing Ctrl-C, for example). This is + only checked when returned by the flusher or the reader. + + The way this is used, the opener is first called, then the writer for all + strings, then the flusher, then the reader for all strings and finally the + closer. Note that if you want to prompt from a terminal or other command + line interface, the best is to have the reader also write the prompts + instead of having the writer do it. If you want to prompt from a dialog + box, the writer can be used to build up the contents of the box, and the + flusher to actually display the box and run the event loop until all data + has been given, after which the reader only grabs the given data and puts + them back into the UI strings. + + All method functions take a UI as argument. Additionally, the writer and + the reader take a UI_STRING. +*/ + +/* The UI_STRING type is the data structure that contains all the needed info + about a string or a prompt, including test data for a verification prompt. +*/ +DECLARE_STACK_OF(UI_STRING) +typedef struct ui_string_st UI_STRING; + +/* The different types of strings that are currently supported. + This is only needed by method authors. */ +enum UI_string_types + { + UIT_NONE=0, + UIT_PROMPT, /* Prompt for a string */ + UIT_VERIFY, /* Prompt for a string and verify */ + UIT_BOOLEAN, /* Prompt for a yes/no response */ + UIT_INFO, /* Send info to the user */ + UIT_ERROR /* Send an error message to the user */ + }; + +/* Create and manipulate methods */ +UI_METHOD *UI_create_method(char *name); +void UI_destroy_method(UI_METHOD *ui_method); +int UI_method_set_opener(UI_METHOD *method, int (*opener)(UI *ui)); +int UI_method_set_writer(UI_METHOD *method, int (*writer)(UI *ui, UI_STRING *uis)); +int UI_method_set_flusher(UI_METHOD *method, int (*flusher)(UI *ui)); +int UI_method_set_reader(UI_METHOD *method, int (*reader)(UI *ui, UI_STRING *uis)); +int UI_method_set_closer(UI_METHOD *method, int (*closer)(UI *ui)); +int (*UI_method_get_opener(UI_METHOD *method))(UI*); +int (*UI_method_get_writer(UI_METHOD *method))(UI*,UI_STRING*); +int (*UI_method_get_flusher(UI_METHOD *method))(UI*); +int (*UI_method_get_reader(UI_METHOD *method))(UI*,UI_STRING*); +int (*UI_method_get_closer(UI_METHOD *method))(UI*); + +/* The following functions are helpers for method writers to access relevant + data from a UI_STRING. */ + +/* Return type of the UI_STRING */ +enum UI_string_types UI_get_string_type(UI_STRING *uis); +/* Return input flags of the UI_STRING */ +int UI_get_input_flags(UI_STRING *uis); +/* Return the actual string to output (the prompt, info or error) */ +const char *UI_get0_output_string(UI_STRING *uis); +/* Return the optional action string to output (the boolean promtp instruction) */ +const char *UI_get0_action_string(UI_STRING *uis); +/* Return the result of a prompt */ +const char *UI_get0_result_string(UI_STRING *uis); +/* Return the string to test the result against. Only useful with verifies. */ +const char *UI_get0_test_string(UI_STRING *uis); +/* Return the required minimum size of the result */ +int UI_get_result_minsize(UI_STRING *uis); +/* Return the required maximum size of the result */ +int UI_get_result_maxsize(UI_STRING *uis); +/* Set the result of a UI_STRING. */ +int UI_set_result(UI *ui, UI_STRING *uis, const char *result); + + +/* A couple of popular utility functions */ +int UI_UTIL_read_pw_string(char *buf,int length,const char *prompt,int verify); +int UI_UTIL_read_pw(char *buf,char *buff,int size,const char *prompt,int verify); + + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_UI_strings(void); + +/* Error codes for the UI functions. */ + +/* Function codes. */ +#define UI_F_GENERAL_ALLOCATE_BOOLEAN 108 +#define UI_F_GENERAL_ALLOCATE_PROMPT 109 +#define UI_F_GENERAL_ALLOCATE_STRING 100 +#define UI_F_UI_CTRL 111 +#define UI_F_UI_DUP_ERROR_STRING 101 +#define UI_F_UI_DUP_INFO_STRING 102 +#define UI_F_UI_DUP_INPUT_BOOLEAN 110 +#define UI_F_UI_DUP_INPUT_STRING 103 +#define UI_F_UI_DUP_VERIFY_STRING 106 +#define UI_F_UI_GET0_RESULT 107 +#define UI_F_UI_NEW_METHOD 104 +#define UI_F_UI_SET_RESULT 105 + +/* Reason codes. */ +#define UI_R_COMMON_OK_AND_CANCEL_CHARACTERS 104 +#define UI_R_INDEX_TOO_LARGE 102 +#define UI_R_INDEX_TOO_SMALL 103 +#define UI_R_NO_RESULT_BUFFER 105 +#define UI_R_RESULT_TOO_LARGE 100 +#define UI_R_RESULT_TOO_SMALL 101 +#define UI_R_UNKNOWN_CONTROL_COMMAND 106 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/ui_compat.h b/production/3rdparty/openssl/include/openssl/ui_compat.h new file mode 100644 index 00000000..b35c9bb7 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/ui_compat.h @@ -0,0 +1,83 @@ +/* crypto/ui/ui.h -*- mode:C; c-file-style: "eay" -*- */ +/* Written by Richard Levitte (richard@levitte.org) for the OpenSSL + * project 2001. + */ +/* ==================================================================== + * Copyright (c) 2001 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#ifndef HEADER_UI_COMPAT_H +#define HEADER_UI_COMPAT_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* The following functions were previously part of the DES section, + and are provided here for backward compatibility reasons. */ + +#define des_read_pw_string(b,l,p,v) \ + _ossl_old_des_read_pw_string((b),(l),(p),(v)) +#define des_read_pw(b,bf,s,p,v) \ + _ossl_old_des_read_pw((b),(bf),(s),(p),(v)) + +int _ossl_old_des_read_pw_string(char *buf,int length,const char *prompt,int verify); +int _ossl_old_des_read_pw(char *buf,char *buff,int size,const char *prompt,int verify); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/x509.h b/production/3rdparty/openssl/include/openssl/x509.h new file mode 100644 index 00000000..66990ae5 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/x509.h @@ -0,0 +1,1340 @@ +/* crypto/x509/x509.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * ECDH support in OpenSSL originally developed by + * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. + */ + +#ifndef HEADER_X509_H +#define HEADER_X509_H + +#include +#include +#ifndef OPENSSL_NO_BUFFER +#include +#endif +#ifndef OPENSSL_NO_EVP +#include +#endif +#ifndef OPENSSL_NO_BIO +#include +#endif +#include +#include +#include + +#ifndef OPENSSL_NO_EC +#include +#endif + +#ifndef OPENSSL_NO_ECDSA +#include +#endif + +#ifndef OPENSSL_NO_ECDH +#include +#endif + +#ifndef OPENSSL_NO_DEPRECATED +#ifndef OPENSSL_NO_RSA +#include +#endif +#ifndef OPENSSL_NO_DSA +#include +#endif +#ifndef OPENSSL_NO_DH +#include +#endif +#endif + +#ifndef OPENSSL_NO_SHA +#include +#endif +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef OPENSSL_SYS_WIN32 +/* Under Win32 these are defined in wincrypt.h */ +#undef X509_NAME +#undef X509_CERT_PAIR +#endif + +#define X509_FILETYPE_PEM 1 +#define X509_FILETYPE_ASN1 2 +#define X509_FILETYPE_DEFAULT 3 + +#define X509v3_KU_DIGITAL_SIGNATURE 0x0080 +#define X509v3_KU_NON_REPUDIATION 0x0040 +#define X509v3_KU_KEY_ENCIPHERMENT 0x0020 +#define X509v3_KU_DATA_ENCIPHERMENT 0x0010 +#define X509v3_KU_KEY_AGREEMENT 0x0008 +#define X509v3_KU_KEY_CERT_SIGN 0x0004 +#define X509v3_KU_CRL_SIGN 0x0002 +#define X509v3_KU_ENCIPHER_ONLY 0x0001 +#define X509v3_KU_DECIPHER_ONLY 0x8000 +#define X509v3_KU_UNDEF 0xffff + +typedef struct X509_objects_st + { + int nid; + int (*a2i)(void); + int (*i2a)(void); + } X509_OBJECTS; + +struct X509_algor_st + { + ASN1_OBJECT *algorithm; + ASN1_TYPE *parameter; + } /* X509_ALGOR */; + +DECLARE_STACK_OF(X509_ALGOR) +DECLARE_ASN1_SET_OF(X509_ALGOR) + +typedef struct X509_val_st + { + ASN1_TIME *notBefore; + ASN1_TIME *notAfter; + } X509_VAL; + +typedef struct X509_pubkey_st + { + X509_ALGOR *algor; + ASN1_BIT_STRING *public_key; + EVP_PKEY *pkey; + } X509_PUBKEY; + +typedef struct X509_sig_st + { + X509_ALGOR *algor; + ASN1_OCTET_STRING *digest; + } X509_SIG; + +typedef struct X509_name_entry_st + { + ASN1_OBJECT *object; + ASN1_STRING *value; + int set; + int size; /* temp variable */ + } X509_NAME_ENTRY; + +DECLARE_STACK_OF(X509_NAME_ENTRY) +DECLARE_ASN1_SET_OF(X509_NAME_ENTRY) + +/* we always keep X509_NAMEs in 2 forms. */ +struct X509_name_st + { + STACK_OF(X509_NAME_ENTRY) *entries; + int modified; /* true if 'bytes' needs to be built */ +#ifndef OPENSSL_NO_BUFFER + BUF_MEM *bytes; +#else + char *bytes; +#endif + unsigned long hash; /* Keep the hash around for lookups */ + } /* X509_NAME */; + +DECLARE_STACK_OF(X509_NAME) + +#define X509_EX_V_NETSCAPE_HACK 0x8000 +#define X509_EX_V_INIT 0x0001 +typedef struct X509_extension_st + { + ASN1_OBJECT *object; + ASN1_BOOLEAN critical; + ASN1_OCTET_STRING *value; + } X509_EXTENSION; + +DECLARE_STACK_OF(X509_EXTENSION) +DECLARE_ASN1_SET_OF(X509_EXTENSION) + +/* a sequence of these are used */ +typedef struct x509_attributes_st + { + ASN1_OBJECT *object; + int single; /* 0 for a set, 1 for a single item (which is wrong) */ + union { + char *ptr; +/* 0 */ STACK_OF(ASN1_TYPE) *set; +/* 1 */ ASN1_TYPE *single; + } value; + } X509_ATTRIBUTE; + +DECLARE_STACK_OF(X509_ATTRIBUTE) +DECLARE_ASN1_SET_OF(X509_ATTRIBUTE) + + +typedef struct X509_req_info_st + { + ASN1_ENCODING enc; + ASN1_INTEGER *version; + X509_NAME *subject; + X509_PUBKEY *pubkey; + /* d=2 hl=2 l= 0 cons: cont: 00 */ + STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */ + } X509_REQ_INFO; + +typedef struct X509_req_st + { + X509_REQ_INFO *req_info; + X509_ALGOR *sig_alg; + ASN1_BIT_STRING *signature; + int references; + } X509_REQ; + +typedef struct x509_cinf_st + { + ASN1_INTEGER *version; /* [ 0 ] default of v1 */ + ASN1_INTEGER *serialNumber; + X509_ALGOR *signature; + X509_NAME *issuer; + X509_VAL *validity; + X509_NAME *subject; + X509_PUBKEY *key; + ASN1_BIT_STRING *issuerUID; /* [ 1 ] optional in v2 */ + ASN1_BIT_STRING *subjectUID; /* [ 2 ] optional in v2 */ + STACK_OF(X509_EXTENSION) *extensions; /* [ 3 ] optional in v3 */ + } X509_CINF; + +/* This stuff is certificate "auxiliary info" + * it contains details which are useful in certificate + * stores and databases. When used this is tagged onto + * the end of the certificate itself + */ + +typedef struct x509_cert_aux_st + { + STACK_OF(ASN1_OBJECT) *trust; /* trusted uses */ + STACK_OF(ASN1_OBJECT) *reject; /* rejected uses */ + ASN1_UTF8STRING *alias; /* "friendly name" */ + ASN1_OCTET_STRING *keyid; /* key id of private key */ + STACK_OF(X509_ALGOR) *other; /* other unspecified info */ + } X509_CERT_AUX; + +struct x509_st + { + X509_CINF *cert_info; + X509_ALGOR *sig_alg; + ASN1_BIT_STRING *signature; + int valid; + int references; + char *name; + CRYPTO_EX_DATA ex_data; + /* These contain copies of various extension values */ + long ex_pathlen; + long ex_pcpathlen; + unsigned long ex_flags; + unsigned long ex_kusage; + unsigned long ex_xkusage; + unsigned long ex_nscert; + ASN1_OCTET_STRING *skid; + struct AUTHORITY_KEYID_st *akid; + X509_POLICY_CACHE *policy_cache; +#ifndef OPENSSL_NO_SHA + unsigned char sha1_hash[SHA_DIGEST_LENGTH]; +#endif + X509_CERT_AUX *aux; + } /* X509 */; + +DECLARE_STACK_OF(X509) +DECLARE_ASN1_SET_OF(X509) + +/* This is used for a table of trust checking functions */ + +typedef struct x509_trust_st { + int trust; + int flags; + int (*check_trust)(struct x509_trust_st *, X509 *, int); + char *name; + int arg1; + void *arg2; +} X509_TRUST; + +DECLARE_STACK_OF(X509_TRUST) + +typedef struct x509_cert_pair_st { + X509 *forward; + X509 *reverse; +} X509_CERT_PAIR; + +/* standard trust ids */ + +#define X509_TRUST_DEFAULT -1 /* Only valid in purpose settings */ + +#define X509_TRUST_COMPAT 1 +#define X509_TRUST_SSL_CLIENT 2 +#define X509_TRUST_SSL_SERVER 3 +#define X509_TRUST_EMAIL 4 +#define X509_TRUST_OBJECT_SIGN 5 +#define X509_TRUST_OCSP_SIGN 6 +#define X509_TRUST_OCSP_REQUEST 7 + +/* Keep these up to date! */ +#define X509_TRUST_MIN 1 +#define X509_TRUST_MAX 7 + + +/* trust_flags values */ +#define X509_TRUST_DYNAMIC 1 +#define X509_TRUST_DYNAMIC_NAME 2 + +/* check_trust return codes */ + +#define X509_TRUST_TRUSTED 1 +#define X509_TRUST_REJECTED 2 +#define X509_TRUST_UNTRUSTED 3 + +/* Flags for X509_print_ex() */ + +#define X509_FLAG_COMPAT 0 +#define X509_FLAG_NO_HEADER 1L +#define X509_FLAG_NO_VERSION (1L << 1) +#define X509_FLAG_NO_SERIAL (1L << 2) +#define X509_FLAG_NO_SIGNAME (1L << 3) +#define X509_FLAG_NO_ISSUER (1L << 4) +#define X509_FLAG_NO_VALIDITY (1L << 5) +#define X509_FLAG_NO_SUBJECT (1L << 6) +#define X509_FLAG_NO_PUBKEY (1L << 7) +#define X509_FLAG_NO_EXTENSIONS (1L << 8) +#define X509_FLAG_NO_SIGDUMP (1L << 9) +#define X509_FLAG_NO_AUX (1L << 10) +#define X509_FLAG_NO_ATTRIBUTES (1L << 11) + +/* Flags specific to X509_NAME_print_ex() */ + +/* The field separator information */ + +#define XN_FLAG_SEP_MASK (0xf << 16) + +#define XN_FLAG_COMPAT 0 /* Traditional SSLeay: use old X509_NAME_print */ +#define XN_FLAG_SEP_COMMA_PLUS (1 << 16) /* RFC2253 ,+ */ +#define XN_FLAG_SEP_CPLUS_SPC (2 << 16) /* ,+ spaced: more readable */ +#define XN_FLAG_SEP_SPLUS_SPC (3 << 16) /* ;+ spaced */ +#define XN_FLAG_SEP_MULTILINE (4 << 16) /* One line per field */ + +#define XN_FLAG_DN_REV (1 << 20) /* Reverse DN order */ + +/* How the field name is shown */ + +#define XN_FLAG_FN_MASK (0x3 << 21) + +#define XN_FLAG_FN_SN 0 /* Object short name */ +#define XN_FLAG_FN_LN (1 << 21) /* Object long name */ +#define XN_FLAG_FN_OID (2 << 21) /* Always use OIDs */ +#define XN_FLAG_FN_NONE (3 << 21) /* No field names */ + +#define XN_FLAG_SPC_EQ (1 << 23) /* Put spaces round '=' */ + +/* This determines if we dump fields we don't recognise: + * RFC2253 requires this. + */ + +#define XN_FLAG_DUMP_UNKNOWN_FIELDS (1 << 24) + +#define XN_FLAG_FN_ALIGN (1 << 25) /* Align field names to 20 characters */ + +/* Complete set of RFC2253 flags */ + +#define XN_FLAG_RFC2253 (ASN1_STRFLGS_RFC2253 | \ + XN_FLAG_SEP_COMMA_PLUS | \ + XN_FLAG_DN_REV | \ + XN_FLAG_FN_SN | \ + XN_FLAG_DUMP_UNKNOWN_FIELDS) + +/* readable oneline form */ + +#define XN_FLAG_ONELINE (ASN1_STRFLGS_RFC2253 | \ + ASN1_STRFLGS_ESC_QUOTE | \ + XN_FLAG_SEP_CPLUS_SPC | \ + XN_FLAG_SPC_EQ | \ + XN_FLAG_FN_SN) + +/* readable multiline form */ + +#define XN_FLAG_MULTILINE (ASN1_STRFLGS_ESC_CTRL | \ + ASN1_STRFLGS_ESC_MSB | \ + XN_FLAG_SEP_MULTILINE | \ + XN_FLAG_SPC_EQ | \ + XN_FLAG_FN_LN | \ + XN_FLAG_FN_ALIGN) + +typedef struct X509_revoked_st + { + ASN1_INTEGER *serialNumber; + ASN1_TIME *revocationDate; + STACK_OF(X509_EXTENSION) /* optional */ *extensions; + int sequence; /* load sequence */ + } X509_REVOKED; + +DECLARE_STACK_OF(X509_REVOKED) +DECLARE_ASN1_SET_OF(X509_REVOKED) + +typedef struct X509_crl_info_st + { + ASN1_INTEGER *version; + X509_ALGOR *sig_alg; + X509_NAME *issuer; + ASN1_TIME *lastUpdate; + ASN1_TIME *nextUpdate; + STACK_OF(X509_REVOKED) *revoked; + STACK_OF(X509_EXTENSION) /* [0] */ *extensions; + ASN1_ENCODING enc; + } X509_CRL_INFO; + +struct X509_crl_st + { + /* actual signature */ + X509_CRL_INFO *crl; + X509_ALGOR *sig_alg; + ASN1_BIT_STRING *signature; + int references; + } /* X509_CRL */; + +DECLARE_STACK_OF(X509_CRL) +DECLARE_ASN1_SET_OF(X509_CRL) + +typedef struct private_key_st + { + int version; + /* The PKCS#8 data types */ + X509_ALGOR *enc_algor; + ASN1_OCTET_STRING *enc_pkey; /* encrypted pub key */ + + /* When decrypted, the following will not be NULL */ + EVP_PKEY *dec_pkey; + + /* used to encrypt and decrypt */ + int key_length; + char *key_data; + int key_free; /* true if we should auto free key_data */ + + /* expanded version of 'enc_algor' */ + EVP_CIPHER_INFO cipher; + + int references; + } X509_PKEY; + +#ifndef OPENSSL_NO_EVP +typedef struct X509_info_st + { + X509 *x509; + X509_CRL *crl; + X509_PKEY *x_pkey; + + EVP_CIPHER_INFO enc_cipher; + int enc_len; + char *enc_data; + + int references; + } X509_INFO; + +DECLARE_STACK_OF(X509_INFO) +#endif + +/* The next 2 structures and their 8 routines were sent to me by + * Pat Richard and are used to manipulate + * Netscapes spki structures - useful if you are writing a CA web page + */ +typedef struct Netscape_spkac_st + { + X509_PUBKEY *pubkey; + ASN1_IA5STRING *challenge; /* challenge sent in atlas >= PR2 */ + } NETSCAPE_SPKAC; + +typedef struct Netscape_spki_st + { + NETSCAPE_SPKAC *spkac; /* signed public key and challenge */ + X509_ALGOR *sig_algor; + ASN1_BIT_STRING *signature; + } NETSCAPE_SPKI; + +/* Netscape certificate sequence structure */ +typedef struct Netscape_certificate_sequence + { + ASN1_OBJECT *type; + STACK_OF(X509) *certs; + } NETSCAPE_CERT_SEQUENCE; + +/* Unused (and iv length is wrong) +typedef struct CBCParameter_st + { + unsigned char iv[8]; + } CBC_PARAM; +*/ + +/* Password based encryption structure */ + +typedef struct PBEPARAM_st { +ASN1_OCTET_STRING *salt; +ASN1_INTEGER *iter; +} PBEPARAM; + +/* Password based encryption V2 structures */ + +typedef struct PBE2PARAM_st { +X509_ALGOR *keyfunc; +X509_ALGOR *encryption; +} PBE2PARAM; + +typedef struct PBKDF2PARAM_st { +ASN1_TYPE *salt; /* Usually OCTET STRING but could be anything */ +ASN1_INTEGER *iter; +ASN1_INTEGER *keylength; +X509_ALGOR *prf; +} PBKDF2PARAM; + + +/* PKCS#8 private key info structure */ + +typedef struct pkcs8_priv_key_info_st + { + int broken; /* Flag for various broken formats */ +#define PKCS8_OK 0 +#define PKCS8_NO_OCTET 1 +#define PKCS8_EMBEDDED_PARAM 2 +#define PKCS8_NS_DB 3 + ASN1_INTEGER *version; + X509_ALGOR *pkeyalg; + ASN1_TYPE *pkey; /* Should be OCTET STRING but some are broken */ + STACK_OF(X509_ATTRIBUTE) *attributes; + } PKCS8_PRIV_KEY_INFO; + +#ifdef __cplusplus +} +#endif + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef SSLEAY_MACROS +#define X509_verify(a,r) ASN1_verify((int (*)())i2d_X509_CINF,a->sig_alg,\ + a->signature,(char *)a->cert_info,r) +#define X509_REQ_verify(a,r) ASN1_verify((int (*)())i2d_X509_REQ_INFO, \ + a->sig_alg,a->signature,(char *)a->req_info,r) +#define X509_CRL_verify(a,r) ASN1_verify((int (*)())i2d_X509_CRL_INFO, \ + a->sig_alg, a->signature,(char *)a->crl,r) + +#define X509_sign(x,pkey,md) \ + ASN1_sign((int (*)())i2d_X509_CINF, x->cert_info->signature, \ + x->sig_alg, x->signature, (char *)x->cert_info,pkey,md) +#define X509_REQ_sign(x,pkey,md) \ + ASN1_sign((int (*)())i2d_X509_REQ_INFO,x->sig_alg, NULL, \ + x->signature, (char *)x->req_info,pkey,md) +#define X509_CRL_sign(x,pkey,md) \ + ASN1_sign((int (*)())i2d_X509_CRL_INFO,x->crl->sig_alg,x->sig_alg, \ + x->signature, (char *)x->crl,pkey,md) +#define NETSCAPE_SPKI_sign(x,pkey,md) \ + ASN1_sign((int (*)())i2d_NETSCAPE_SPKAC, x->sig_algor,NULL, \ + x->signature, (char *)x->spkac,pkey,md) + +#define X509_dup(x509) (X509 *)ASN1_dup((int (*)())i2d_X509, \ + (char *(*)())d2i_X509,(char *)x509) +#define X509_ATTRIBUTE_dup(xa) (X509_ATTRIBUTE *)ASN1_dup(\ + (int (*)())i2d_X509_ATTRIBUTE, \ + (char *(*)())d2i_X509_ATTRIBUTE,(char *)xa) +#define X509_EXTENSION_dup(ex) (X509_EXTENSION *)ASN1_dup( \ + (int (*)())i2d_X509_EXTENSION, \ + (char *(*)())d2i_X509_EXTENSION,(char *)ex) +#define d2i_X509_fp(fp,x509) (X509 *)ASN1_d2i_fp((char *(*)())X509_new, \ + (char *(*)())d2i_X509, (fp),(unsigned char **)(x509)) +#define i2d_X509_fp(fp,x509) ASN1_i2d_fp(i2d_X509,fp,(unsigned char *)x509) +#define d2i_X509_bio(bp,x509) (X509 *)ASN1_d2i_bio((char *(*)())X509_new, \ + (char *(*)())d2i_X509, (bp),(unsigned char **)(x509)) +#define i2d_X509_bio(bp,x509) ASN1_i2d_bio(i2d_X509,bp,(unsigned char *)x509) + +#define X509_CRL_dup(crl) (X509_CRL *)ASN1_dup((int (*)())i2d_X509_CRL, \ + (char *(*)())d2i_X509_CRL,(char *)crl) +#define d2i_X509_CRL_fp(fp,crl) (X509_CRL *)ASN1_d2i_fp((char *(*)()) \ + X509_CRL_new,(char *(*)())d2i_X509_CRL, (fp),\ + (unsigned char **)(crl)) +#define i2d_X509_CRL_fp(fp,crl) ASN1_i2d_fp(i2d_X509_CRL,fp,\ + (unsigned char *)crl) +#define d2i_X509_CRL_bio(bp,crl) (X509_CRL *)ASN1_d2i_bio((char *(*)()) \ + X509_CRL_new,(char *(*)())d2i_X509_CRL, (bp),\ + (unsigned char **)(crl)) +#define i2d_X509_CRL_bio(bp,crl) ASN1_i2d_bio(i2d_X509_CRL,bp,\ + (unsigned char *)crl) + +#define PKCS7_dup(p7) (PKCS7 *)ASN1_dup((int (*)())i2d_PKCS7, \ + (char *(*)())d2i_PKCS7,(char *)p7) +#define d2i_PKCS7_fp(fp,p7) (PKCS7 *)ASN1_d2i_fp((char *(*)()) \ + PKCS7_new,(char *(*)())d2i_PKCS7, (fp),\ + (unsigned char **)(p7)) +#define i2d_PKCS7_fp(fp,p7) ASN1_i2d_fp(i2d_PKCS7,fp,\ + (unsigned char *)p7) +#define d2i_PKCS7_bio(bp,p7) (PKCS7 *)ASN1_d2i_bio((char *(*)()) \ + PKCS7_new,(char *(*)())d2i_PKCS7, (bp),\ + (unsigned char **)(p7)) +#define i2d_PKCS7_bio(bp,p7) ASN1_i2d_bio(i2d_PKCS7,bp,\ + (unsigned char *)p7) + +#define X509_REQ_dup(req) (X509_REQ *)ASN1_dup((int (*)())i2d_X509_REQ, \ + (char *(*)())d2i_X509_REQ,(char *)req) +#define d2i_X509_REQ_fp(fp,req) (X509_REQ *)ASN1_d2i_fp((char *(*)())\ + X509_REQ_new, (char *(*)())d2i_X509_REQ, (fp),\ + (unsigned char **)(req)) +#define i2d_X509_REQ_fp(fp,req) ASN1_i2d_fp(i2d_X509_REQ,fp,\ + (unsigned char *)req) +#define d2i_X509_REQ_bio(bp,req) (X509_REQ *)ASN1_d2i_bio((char *(*)())\ + X509_REQ_new, (char *(*)())d2i_X509_REQ, (bp),\ + (unsigned char **)(req)) +#define i2d_X509_REQ_bio(bp,req) ASN1_i2d_bio(i2d_X509_REQ,bp,\ + (unsigned char *)req) + +#define RSAPublicKey_dup(rsa) (RSA *)ASN1_dup((int (*)())i2d_RSAPublicKey, \ + (char *(*)())d2i_RSAPublicKey,(char *)rsa) +#define RSAPrivateKey_dup(rsa) (RSA *)ASN1_dup((int (*)())i2d_RSAPrivateKey, \ + (char *(*)())d2i_RSAPrivateKey,(char *)rsa) + +#define d2i_RSAPrivateKey_fp(fp,rsa) (RSA *)ASN1_d2i_fp((char *(*)())\ + RSA_new,(char *(*)())d2i_RSAPrivateKey, (fp), \ + (unsigned char **)(rsa)) +#define i2d_RSAPrivateKey_fp(fp,rsa) ASN1_i2d_fp(i2d_RSAPrivateKey,fp, \ + (unsigned char *)rsa) +#define d2i_RSAPrivateKey_bio(bp,rsa) (RSA *)ASN1_d2i_bio((char *(*)())\ + RSA_new,(char *(*)())d2i_RSAPrivateKey, (bp), \ + (unsigned char **)(rsa)) +#define i2d_RSAPrivateKey_bio(bp,rsa) ASN1_i2d_bio(i2d_RSAPrivateKey,bp, \ + (unsigned char *)rsa) + +#define d2i_RSAPublicKey_fp(fp,rsa) (RSA *)ASN1_d2i_fp((char *(*)())\ + RSA_new,(char *(*)())d2i_RSAPublicKey, (fp), \ + (unsigned char **)(rsa)) +#define i2d_RSAPublicKey_fp(fp,rsa) ASN1_i2d_fp(i2d_RSAPublicKey,fp, \ + (unsigned char *)rsa) +#define d2i_RSAPublicKey_bio(bp,rsa) (RSA *)ASN1_d2i_bio((char *(*)())\ + RSA_new,(char *(*)())d2i_RSAPublicKey, (bp), \ + (unsigned char **)(rsa)) +#define i2d_RSAPublicKey_bio(bp,rsa) ASN1_i2d_bio(i2d_RSAPublicKey,bp, \ + (unsigned char *)rsa) + +#define d2i_DSAPrivateKey_fp(fp,dsa) (DSA *)ASN1_d2i_fp((char *(*)())\ + DSA_new,(char *(*)())d2i_DSAPrivateKey, (fp), \ + (unsigned char **)(dsa)) +#define i2d_DSAPrivateKey_fp(fp,dsa) ASN1_i2d_fp(i2d_DSAPrivateKey,fp, \ + (unsigned char *)dsa) +#define d2i_DSAPrivateKey_bio(bp,dsa) (DSA *)ASN1_d2i_bio((char *(*)())\ + DSA_new,(char *(*)())d2i_DSAPrivateKey, (bp), \ + (unsigned char **)(dsa)) +#define i2d_DSAPrivateKey_bio(bp,dsa) ASN1_i2d_bio(i2d_DSAPrivateKey,bp, \ + (unsigned char *)dsa) + +#define d2i_ECPrivateKey_fp(fp,ecdsa) (EC_KEY *)ASN1_d2i_fp((char *(*)())\ + EC_KEY_new,(char *(*)())d2i_ECPrivateKey, (fp), \ + (unsigned char **)(ecdsa)) +#define i2d_ECPrivateKey_fp(fp,ecdsa) ASN1_i2d_fp(i2d_ECPrivateKey,fp, \ + (unsigned char *)ecdsa) +#define d2i_ECPrivateKey_bio(bp,ecdsa) (EC_KEY *)ASN1_d2i_bio((char *(*)())\ + EC_KEY_new,(char *(*)())d2i_ECPrivateKey, (bp), \ + (unsigned char **)(ecdsa)) +#define i2d_ECPrivateKey_bio(bp,ecdsa) ASN1_i2d_bio(i2d_ECPrivateKey,bp, \ + (unsigned char *)ecdsa) + +#define X509_ALGOR_dup(xn) (X509_ALGOR *)ASN1_dup((int (*)())i2d_X509_ALGOR,\ + (char *(*)())d2i_X509_ALGOR,(char *)xn) + +#define X509_NAME_dup(xn) (X509_NAME *)ASN1_dup((int (*)())i2d_X509_NAME, \ + (char *(*)())d2i_X509_NAME,(char *)xn) +#define X509_NAME_ENTRY_dup(ne) (X509_NAME_ENTRY *)ASN1_dup( \ + (int (*)())i2d_X509_NAME_ENTRY, \ + (char *(*)())d2i_X509_NAME_ENTRY,\ + (char *)ne) + +#define X509_digest(data,type,md,len) \ + ASN1_digest((int (*)())i2d_X509,type,(char *)data,md,len) +#define X509_NAME_digest(data,type,md,len) \ + ASN1_digest((int (*)())i2d_X509_NAME,type,(char *)data,md,len) +#ifndef PKCS7_ISSUER_AND_SERIAL_digest +#define PKCS7_ISSUER_AND_SERIAL_digest(data,type,md,len) \ + ASN1_digest((int (*)())i2d_PKCS7_ISSUER_AND_SERIAL,type,\ + (char *)data,md,len) +#endif +#endif + +#define X509_EXT_PACK_UNKNOWN 1 +#define X509_EXT_PACK_STRING 2 + +#define X509_get_version(x) ASN1_INTEGER_get((x)->cert_info->version) +/* #define X509_get_serialNumber(x) ((x)->cert_info->serialNumber) */ +#define X509_get_notBefore(x) ((x)->cert_info->validity->notBefore) +#define X509_get_notAfter(x) ((x)->cert_info->validity->notAfter) +#define X509_extract_key(x) X509_get_pubkey(x) /*****/ +#define X509_REQ_get_version(x) ASN1_INTEGER_get((x)->req_info->version) +#define X509_REQ_get_subject_name(x) ((x)->req_info->subject) +#define X509_REQ_extract_key(a) X509_REQ_get_pubkey(a) +#define X509_name_cmp(a,b) X509_NAME_cmp((a),(b)) +#define X509_get_signature_type(x) EVP_PKEY_type(OBJ_obj2nid((x)->sig_alg->algorithm)) + +#define X509_CRL_get_version(x) ASN1_INTEGER_get((x)->crl->version) +#define X509_CRL_get_lastUpdate(x) ((x)->crl->lastUpdate) +#define X509_CRL_get_nextUpdate(x) ((x)->crl->nextUpdate) +#define X509_CRL_get_issuer(x) ((x)->crl->issuer) +#define X509_CRL_get_REVOKED(x) ((x)->crl->revoked) + +/* This one is only used so that a binary form can output, as in + * i2d_X509_NAME(X509_get_X509_PUBKEY(x),&buf) */ +#define X509_get_X509_PUBKEY(x) ((x)->cert_info->key) + + +const char *X509_verify_cert_error_string(long n); + +#ifndef SSLEAY_MACROS +#ifndef OPENSSL_NO_EVP +int X509_verify(X509 *a, EVP_PKEY *r); + +int X509_REQ_verify(X509_REQ *a, EVP_PKEY *r); +int X509_CRL_verify(X509_CRL *a, EVP_PKEY *r); +int NETSCAPE_SPKI_verify(NETSCAPE_SPKI *a, EVP_PKEY *r); + +NETSCAPE_SPKI * NETSCAPE_SPKI_b64_decode(const char *str, int len); +char * NETSCAPE_SPKI_b64_encode(NETSCAPE_SPKI *x); +EVP_PKEY *NETSCAPE_SPKI_get_pubkey(NETSCAPE_SPKI *x); +int NETSCAPE_SPKI_set_pubkey(NETSCAPE_SPKI *x, EVP_PKEY *pkey); + +int NETSCAPE_SPKI_print(BIO *out, NETSCAPE_SPKI *spki); + +int X509_signature_print(BIO *bp,X509_ALGOR *alg, ASN1_STRING *sig); + +int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md); +int X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md); +int X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md); +int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *x, EVP_PKEY *pkey, const EVP_MD *md); + +int X509_pubkey_digest(const X509 *data,const EVP_MD *type, + unsigned char *md, unsigned int *len); +int X509_digest(const X509 *data,const EVP_MD *type, + unsigned char *md, unsigned int *len); +int X509_CRL_digest(const X509_CRL *data,const EVP_MD *type, + unsigned char *md, unsigned int *len); +int X509_REQ_digest(const X509_REQ *data,const EVP_MD *type, + unsigned char *md, unsigned int *len); +int X509_NAME_digest(const X509_NAME *data,const EVP_MD *type, + unsigned char *md, unsigned int *len); +#endif + +#ifndef OPENSSL_NO_FP_API +X509 *d2i_X509_fp(FILE *fp, X509 **x509); +int i2d_X509_fp(FILE *fp,X509 *x509); +X509_CRL *d2i_X509_CRL_fp(FILE *fp,X509_CRL **crl); +int i2d_X509_CRL_fp(FILE *fp,X509_CRL *crl); +X509_REQ *d2i_X509_REQ_fp(FILE *fp,X509_REQ **req); +int i2d_X509_REQ_fp(FILE *fp,X509_REQ *req); +#ifndef OPENSSL_NO_RSA +RSA *d2i_RSAPrivateKey_fp(FILE *fp,RSA **rsa); +int i2d_RSAPrivateKey_fp(FILE *fp,RSA *rsa); +RSA *d2i_RSAPublicKey_fp(FILE *fp,RSA **rsa); +int i2d_RSAPublicKey_fp(FILE *fp,RSA *rsa); +RSA *d2i_RSA_PUBKEY_fp(FILE *fp,RSA **rsa); +int i2d_RSA_PUBKEY_fp(FILE *fp,RSA *rsa); +#endif +#ifndef OPENSSL_NO_DSA +DSA *d2i_DSA_PUBKEY_fp(FILE *fp, DSA **dsa); +int i2d_DSA_PUBKEY_fp(FILE *fp, DSA *dsa); +DSA *d2i_DSAPrivateKey_fp(FILE *fp, DSA **dsa); +int i2d_DSAPrivateKey_fp(FILE *fp, DSA *dsa); +#endif +#ifndef OPENSSL_NO_EC +EC_KEY *d2i_EC_PUBKEY_fp(FILE *fp, EC_KEY **eckey); +int i2d_EC_PUBKEY_fp(FILE *fp, EC_KEY *eckey); +EC_KEY *d2i_ECPrivateKey_fp(FILE *fp, EC_KEY **eckey); +int i2d_ECPrivateKey_fp(FILE *fp, EC_KEY *eckey); +#endif +X509_SIG *d2i_PKCS8_fp(FILE *fp,X509_SIG **p8); +int i2d_PKCS8_fp(FILE *fp,X509_SIG *p8); +PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_fp(FILE *fp, + PKCS8_PRIV_KEY_INFO **p8inf); +int i2d_PKCS8_PRIV_KEY_INFO_fp(FILE *fp,PKCS8_PRIV_KEY_INFO *p8inf); +int i2d_PKCS8PrivateKeyInfo_fp(FILE *fp, EVP_PKEY *key); +int i2d_PrivateKey_fp(FILE *fp, EVP_PKEY *pkey); +EVP_PKEY *d2i_PrivateKey_fp(FILE *fp, EVP_PKEY **a); +int i2d_PUBKEY_fp(FILE *fp, EVP_PKEY *pkey); +EVP_PKEY *d2i_PUBKEY_fp(FILE *fp, EVP_PKEY **a); +#endif + +#ifndef OPENSSL_NO_BIO +X509 *d2i_X509_bio(BIO *bp,X509 **x509); +int i2d_X509_bio(BIO *bp,X509 *x509); +X509_CRL *d2i_X509_CRL_bio(BIO *bp,X509_CRL **crl); +int i2d_X509_CRL_bio(BIO *bp,X509_CRL *crl); +X509_REQ *d2i_X509_REQ_bio(BIO *bp,X509_REQ **req); +int i2d_X509_REQ_bio(BIO *bp,X509_REQ *req); +#ifndef OPENSSL_NO_RSA +RSA *d2i_RSAPrivateKey_bio(BIO *bp,RSA **rsa); +int i2d_RSAPrivateKey_bio(BIO *bp,RSA *rsa); +RSA *d2i_RSAPublicKey_bio(BIO *bp,RSA **rsa); +int i2d_RSAPublicKey_bio(BIO *bp,RSA *rsa); +RSA *d2i_RSA_PUBKEY_bio(BIO *bp,RSA **rsa); +int i2d_RSA_PUBKEY_bio(BIO *bp,RSA *rsa); +#endif +#ifndef OPENSSL_NO_DSA +DSA *d2i_DSA_PUBKEY_bio(BIO *bp, DSA **dsa); +int i2d_DSA_PUBKEY_bio(BIO *bp, DSA *dsa); +DSA *d2i_DSAPrivateKey_bio(BIO *bp, DSA **dsa); +int i2d_DSAPrivateKey_bio(BIO *bp, DSA *dsa); +#endif +#ifndef OPENSSL_NO_EC +EC_KEY *d2i_EC_PUBKEY_bio(BIO *bp, EC_KEY **eckey); +int i2d_EC_PUBKEY_bio(BIO *bp, EC_KEY *eckey); +EC_KEY *d2i_ECPrivateKey_bio(BIO *bp, EC_KEY **eckey); +int i2d_ECPrivateKey_bio(BIO *bp, EC_KEY *eckey); +#endif +X509_SIG *d2i_PKCS8_bio(BIO *bp,X509_SIG **p8); +int i2d_PKCS8_bio(BIO *bp,X509_SIG *p8); +PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_bio(BIO *bp, + PKCS8_PRIV_KEY_INFO **p8inf); +int i2d_PKCS8_PRIV_KEY_INFO_bio(BIO *bp,PKCS8_PRIV_KEY_INFO *p8inf); +int i2d_PKCS8PrivateKeyInfo_bio(BIO *bp, EVP_PKEY *key); +int i2d_PrivateKey_bio(BIO *bp, EVP_PKEY *pkey); +EVP_PKEY *d2i_PrivateKey_bio(BIO *bp, EVP_PKEY **a); +int i2d_PUBKEY_bio(BIO *bp, EVP_PKEY *pkey); +EVP_PKEY *d2i_PUBKEY_bio(BIO *bp, EVP_PKEY **a); +#endif + +X509 *X509_dup(X509 *x509); +X509_ATTRIBUTE *X509_ATTRIBUTE_dup(X509_ATTRIBUTE *xa); +X509_EXTENSION *X509_EXTENSION_dup(X509_EXTENSION *ex); +X509_CRL *X509_CRL_dup(X509_CRL *crl); +X509_REQ *X509_REQ_dup(X509_REQ *req); +X509_ALGOR *X509_ALGOR_dup(X509_ALGOR *xn); +X509_NAME *X509_NAME_dup(X509_NAME *xn); +X509_NAME_ENTRY *X509_NAME_ENTRY_dup(X509_NAME_ENTRY *ne); + +#endif /* !SSLEAY_MACROS */ + +int X509_cmp_time(ASN1_TIME *s, time_t *t); +int X509_cmp_current_time(ASN1_TIME *s); +ASN1_TIME * X509_time_adj(ASN1_TIME *s, long adj, time_t *t); +ASN1_TIME * X509_gmtime_adj(ASN1_TIME *s, long adj); + +const char * X509_get_default_cert_area(void ); +const char * X509_get_default_cert_dir(void ); +const char * X509_get_default_cert_file(void ); +const char * X509_get_default_cert_dir_env(void ); +const char * X509_get_default_cert_file_env(void ); +const char * X509_get_default_private_dir(void ); + +X509_REQ * X509_to_X509_REQ(X509 *x, EVP_PKEY *pkey, const EVP_MD *md); +X509 * X509_REQ_to_X509(X509_REQ *r, int days,EVP_PKEY *pkey); + +DECLARE_ASN1_FUNCTIONS(X509_ALGOR) +DECLARE_ASN1_FUNCTIONS(X509_VAL) + +DECLARE_ASN1_FUNCTIONS(X509_PUBKEY) + +int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey); +EVP_PKEY * X509_PUBKEY_get(X509_PUBKEY *key); +int X509_get_pubkey_parameters(EVP_PKEY *pkey, + STACK_OF(X509) *chain); +int i2d_PUBKEY(EVP_PKEY *a,unsigned char **pp); +EVP_PKEY * d2i_PUBKEY(EVP_PKEY **a,const unsigned char **pp, + long length); +#ifndef OPENSSL_NO_RSA +int i2d_RSA_PUBKEY(RSA *a,unsigned char **pp); +RSA * d2i_RSA_PUBKEY(RSA **a,const unsigned char **pp, + long length); +#endif +#ifndef OPENSSL_NO_DSA +int i2d_DSA_PUBKEY(DSA *a,unsigned char **pp); +DSA * d2i_DSA_PUBKEY(DSA **a,const unsigned char **pp, + long length); +#endif +#ifndef OPENSSL_NO_EC +int i2d_EC_PUBKEY(EC_KEY *a, unsigned char **pp); +EC_KEY *d2i_EC_PUBKEY(EC_KEY **a, const unsigned char **pp, + long length); +#endif + +DECLARE_ASN1_FUNCTIONS(X509_SIG) +DECLARE_ASN1_FUNCTIONS(X509_REQ_INFO) +DECLARE_ASN1_FUNCTIONS(X509_REQ) + +DECLARE_ASN1_FUNCTIONS(X509_ATTRIBUTE) +X509_ATTRIBUTE *X509_ATTRIBUTE_create(int nid, int atrtype, void *value); + +DECLARE_ASN1_FUNCTIONS(X509_EXTENSION) + +DECLARE_ASN1_FUNCTIONS(X509_NAME_ENTRY) + +DECLARE_ASN1_FUNCTIONS(X509_NAME) + +int X509_NAME_set(X509_NAME **xn, X509_NAME *name); + +DECLARE_ASN1_FUNCTIONS(X509_CINF) + +DECLARE_ASN1_FUNCTIONS(X509) +DECLARE_ASN1_FUNCTIONS(X509_CERT_AUX) + +DECLARE_ASN1_FUNCTIONS(X509_CERT_PAIR) + +int X509_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, + CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); +int X509_set_ex_data(X509 *r, int idx, void *arg); +void *X509_get_ex_data(X509 *r, int idx); +int i2d_X509_AUX(X509 *a,unsigned char **pp); +X509 * d2i_X509_AUX(X509 **a,const unsigned char **pp,long length); + +int X509_alias_set1(X509 *x, unsigned char *name, int len); +int X509_keyid_set1(X509 *x, unsigned char *id, int len); +unsigned char * X509_alias_get0(X509 *x, int *len); +unsigned char * X509_keyid_get0(X509 *x, int *len); +int (*X509_TRUST_set_default(int (*trust)(int , X509 *, int)))(int, X509 *, int); +int X509_TRUST_set(int *t, int trust); +int X509_add1_trust_object(X509 *x, ASN1_OBJECT *obj); +int X509_add1_reject_object(X509 *x, ASN1_OBJECT *obj); +void X509_trust_clear(X509 *x); +void X509_reject_clear(X509 *x); + +DECLARE_ASN1_FUNCTIONS(X509_REVOKED) +DECLARE_ASN1_FUNCTIONS(X509_CRL_INFO) +DECLARE_ASN1_FUNCTIONS(X509_CRL) + +int X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev); + +X509_PKEY * X509_PKEY_new(void ); +void X509_PKEY_free(X509_PKEY *a); +int i2d_X509_PKEY(X509_PKEY *a,unsigned char **pp); +X509_PKEY * d2i_X509_PKEY(X509_PKEY **a,const unsigned char **pp,long length); + +DECLARE_ASN1_FUNCTIONS(NETSCAPE_SPKI) +DECLARE_ASN1_FUNCTIONS(NETSCAPE_SPKAC) +DECLARE_ASN1_FUNCTIONS(NETSCAPE_CERT_SEQUENCE) + +#ifndef OPENSSL_NO_EVP +X509_INFO * X509_INFO_new(void); +void X509_INFO_free(X509_INFO *a); +char * X509_NAME_oneline(X509_NAME *a,char *buf,int size); + +int ASN1_verify(i2d_of_void *i2d, X509_ALGOR *algor1, + ASN1_BIT_STRING *signature,char *data,EVP_PKEY *pkey); + +int ASN1_digest(i2d_of_void *i2d,const EVP_MD *type,char *data, + unsigned char *md,unsigned int *len); + +int ASN1_sign(i2d_of_void *i2d, X509_ALGOR *algor1, + X509_ALGOR *algor2, ASN1_BIT_STRING *signature, + char *data,EVP_PKEY *pkey, const EVP_MD *type); + +int ASN1_item_digest(const ASN1_ITEM *it,const EVP_MD *type,void *data, + unsigned char *md,unsigned int *len); + +int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *algor1, + ASN1_BIT_STRING *signature,void *data,EVP_PKEY *pkey); + +int ASN1_item_sign(const ASN1_ITEM *it, X509_ALGOR *algor1, X509_ALGOR *algor2, + ASN1_BIT_STRING *signature, + void *data, EVP_PKEY *pkey, const EVP_MD *type); +#endif + +int X509_set_version(X509 *x,long version); +int X509_set_serialNumber(X509 *x, ASN1_INTEGER *serial); +ASN1_INTEGER * X509_get_serialNumber(X509 *x); +int X509_set_issuer_name(X509 *x, X509_NAME *name); +X509_NAME * X509_get_issuer_name(X509 *a); +int X509_set_subject_name(X509 *x, X509_NAME *name); +X509_NAME * X509_get_subject_name(X509 *a); +int X509_set_notBefore(X509 *x, ASN1_TIME *tm); +int X509_set_notAfter(X509 *x, ASN1_TIME *tm); +int X509_set_pubkey(X509 *x, EVP_PKEY *pkey); +EVP_PKEY * X509_get_pubkey(X509 *x); +ASN1_BIT_STRING * X509_get0_pubkey_bitstr(const X509 *x); +int X509_certificate_type(X509 *x,EVP_PKEY *pubkey /* optional */); + +int X509_REQ_set_version(X509_REQ *x,long version); +int X509_REQ_set_subject_name(X509_REQ *req,X509_NAME *name); +int X509_REQ_set_pubkey(X509_REQ *x, EVP_PKEY *pkey); +EVP_PKEY * X509_REQ_get_pubkey(X509_REQ *req); +int X509_REQ_extension_nid(int nid); +int * X509_REQ_get_extension_nids(void); +void X509_REQ_set_extension_nids(int *nids); +STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions(X509_REQ *req); +int X509_REQ_add_extensions_nid(X509_REQ *req, STACK_OF(X509_EXTENSION) *exts, + int nid); +int X509_REQ_add_extensions(X509_REQ *req, STACK_OF(X509_EXTENSION) *exts); +int X509_REQ_get_attr_count(const X509_REQ *req); +int X509_REQ_get_attr_by_NID(const X509_REQ *req, int nid, + int lastpos); +int X509_REQ_get_attr_by_OBJ(const X509_REQ *req, ASN1_OBJECT *obj, + int lastpos); +X509_ATTRIBUTE *X509_REQ_get_attr(const X509_REQ *req, int loc); +X509_ATTRIBUTE *X509_REQ_delete_attr(X509_REQ *req, int loc); +int X509_REQ_add1_attr(X509_REQ *req, X509_ATTRIBUTE *attr); +int X509_REQ_add1_attr_by_OBJ(X509_REQ *req, + const ASN1_OBJECT *obj, int type, + const unsigned char *bytes, int len); +int X509_REQ_add1_attr_by_NID(X509_REQ *req, + int nid, int type, + const unsigned char *bytes, int len); +int X509_REQ_add1_attr_by_txt(X509_REQ *req, + const char *attrname, int type, + const unsigned char *bytes, int len); + +int X509_CRL_set_version(X509_CRL *x, long version); +int X509_CRL_set_issuer_name(X509_CRL *x, X509_NAME *name); +int X509_CRL_set_lastUpdate(X509_CRL *x, ASN1_TIME *tm); +int X509_CRL_set_nextUpdate(X509_CRL *x, ASN1_TIME *tm); +int X509_CRL_sort(X509_CRL *crl); + +int X509_REVOKED_set_serialNumber(X509_REVOKED *x, ASN1_INTEGER *serial); +int X509_REVOKED_set_revocationDate(X509_REVOKED *r, ASN1_TIME *tm); + +int X509_REQ_check_private_key(X509_REQ *x509,EVP_PKEY *pkey); + +int X509_check_private_key(X509 *x509,EVP_PKEY *pkey); + +int X509_issuer_and_serial_cmp(const X509 *a, const X509 *b); +unsigned long X509_issuer_and_serial_hash(X509 *a); + +int X509_issuer_name_cmp(const X509 *a, const X509 *b); +unsigned long X509_issuer_name_hash(X509 *a); + +int X509_subject_name_cmp(const X509 *a, const X509 *b); +unsigned long X509_subject_name_hash(X509 *x); + +int X509_cmp(const X509 *a, const X509 *b); +int X509_NAME_cmp(const X509_NAME *a, const X509_NAME *b); +unsigned long X509_NAME_hash(X509_NAME *x); + +int X509_CRL_cmp(const X509_CRL *a, const X509_CRL *b); +#ifndef OPENSSL_NO_FP_API +int X509_print_ex_fp(FILE *bp,X509 *x, unsigned long nmflag, unsigned long cflag); +int X509_print_fp(FILE *bp,X509 *x); +int X509_CRL_print_fp(FILE *bp,X509_CRL *x); +int X509_REQ_print_fp(FILE *bp,X509_REQ *req); +int X509_NAME_print_ex_fp(FILE *fp, X509_NAME *nm, int indent, unsigned long flags); +#endif + +#ifndef OPENSSL_NO_BIO +int X509_NAME_print(BIO *bp, X509_NAME *name, int obase); +int X509_NAME_print_ex(BIO *out, X509_NAME *nm, int indent, unsigned long flags); +int X509_print_ex(BIO *bp,X509 *x, unsigned long nmflag, unsigned long cflag); +int X509_print(BIO *bp,X509 *x); +int X509_ocspid_print(BIO *bp,X509 *x); +int X509_CERT_AUX_print(BIO *bp,X509_CERT_AUX *x, int indent); +int X509_CRL_print(BIO *bp,X509_CRL *x); +int X509_REQ_print_ex(BIO *bp, X509_REQ *x, unsigned long nmflag, unsigned long cflag); +int X509_REQ_print(BIO *bp,X509_REQ *req); +#endif + +int X509_NAME_entry_count(X509_NAME *name); +int X509_NAME_get_text_by_NID(X509_NAME *name, int nid, + char *buf,int len); +int X509_NAME_get_text_by_OBJ(X509_NAME *name, ASN1_OBJECT *obj, + char *buf,int len); + +/* NOTE: you should be passsing -1, not 0 as lastpos. The functions that use + * lastpos, search after that position on. */ +int X509_NAME_get_index_by_NID(X509_NAME *name,int nid,int lastpos); +int X509_NAME_get_index_by_OBJ(X509_NAME *name,ASN1_OBJECT *obj, + int lastpos); +X509_NAME_ENTRY *X509_NAME_get_entry(X509_NAME *name, int loc); +X509_NAME_ENTRY *X509_NAME_delete_entry(X509_NAME *name, int loc); +int X509_NAME_add_entry(X509_NAME *name,X509_NAME_ENTRY *ne, + int loc, int set); +int X509_NAME_add_entry_by_OBJ(X509_NAME *name, ASN1_OBJECT *obj, int type, + unsigned char *bytes, int len, int loc, int set); +int X509_NAME_add_entry_by_NID(X509_NAME *name, int nid, int type, + unsigned char *bytes, int len, int loc, int set); +X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_txt(X509_NAME_ENTRY **ne, + const char *field, int type, const unsigned char *bytes, int len); +X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_NID(X509_NAME_ENTRY **ne, int nid, + int type,unsigned char *bytes, int len); +int X509_NAME_add_entry_by_txt(X509_NAME *name, const char *field, int type, + const unsigned char *bytes, int len, int loc, int set); +X509_NAME_ENTRY *X509_NAME_ENTRY_create_by_OBJ(X509_NAME_ENTRY **ne, + ASN1_OBJECT *obj, int type,const unsigned char *bytes, + int len); +int X509_NAME_ENTRY_set_object(X509_NAME_ENTRY *ne, + ASN1_OBJECT *obj); +int X509_NAME_ENTRY_set_data(X509_NAME_ENTRY *ne, int type, + const unsigned char *bytes, int len); +ASN1_OBJECT * X509_NAME_ENTRY_get_object(X509_NAME_ENTRY *ne); +ASN1_STRING * X509_NAME_ENTRY_get_data(X509_NAME_ENTRY *ne); + +int X509v3_get_ext_count(const STACK_OF(X509_EXTENSION) *x); +int X509v3_get_ext_by_NID(const STACK_OF(X509_EXTENSION) *x, + int nid, int lastpos); +int X509v3_get_ext_by_OBJ(const STACK_OF(X509_EXTENSION) *x, + ASN1_OBJECT *obj,int lastpos); +int X509v3_get_ext_by_critical(const STACK_OF(X509_EXTENSION) *x, + int crit, int lastpos); +X509_EXTENSION *X509v3_get_ext(const STACK_OF(X509_EXTENSION) *x, int loc); +X509_EXTENSION *X509v3_delete_ext(STACK_OF(X509_EXTENSION) *x, int loc); +STACK_OF(X509_EXTENSION) *X509v3_add_ext(STACK_OF(X509_EXTENSION) **x, + X509_EXTENSION *ex, int loc); + +int X509_get_ext_count(X509 *x); +int X509_get_ext_by_NID(X509 *x, int nid, int lastpos); +int X509_get_ext_by_OBJ(X509 *x,ASN1_OBJECT *obj,int lastpos); +int X509_get_ext_by_critical(X509 *x, int crit, int lastpos); +X509_EXTENSION *X509_get_ext(X509 *x, int loc); +X509_EXTENSION *X509_delete_ext(X509 *x, int loc); +int X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc); +void * X509_get_ext_d2i(X509 *x, int nid, int *crit, int *idx); +int X509_add1_ext_i2d(X509 *x, int nid, void *value, int crit, + unsigned long flags); + +int X509_CRL_get_ext_count(X509_CRL *x); +int X509_CRL_get_ext_by_NID(X509_CRL *x, int nid, int lastpos); +int X509_CRL_get_ext_by_OBJ(X509_CRL *x,ASN1_OBJECT *obj,int lastpos); +int X509_CRL_get_ext_by_critical(X509_CRL *x, int crit, int lastpos); +X509_EXTENSION *X509_CRL_get_ext(X509_CRL *x, int loc); +X509_EXTENSION *X509_CRL_delete_ext(X509_CRL *x, int loc); +int X509_CRL_add_ext(X509_CRL *x, X509_EXTENSION *ex, int loc); +void * X509_CRL_get_ext_d2i(X509_CRL *x, int nid, int *crit, int *idx); +int X509_CRL_add1_ext_i2d(X509_CRL *x, int nid, void *value, int crit, + unsigned long flags); + +int X509_REVOKED_get_ext_count(X509_REVOKED *x); +int X509_REVOKED_get_ext_by_NID(X509_REVOKED *x, int nid, int lastpos); +int X509_REVOKED_get_ext_by_OBJ(X509_REVOKED *x,ASN1_OBJECT *obj,int lastpos); +int X509_REVOKED_get_ext_by_critical(X509_REVOKED *x, int crit, int lastpos); +X509_EXTENSION *X509_REVOKED_get_ext(X509_REVOKED *x, int loc); +X509_EXTENSION *X509_REVOKED_delete_ext(X509_REVOKED *x, int loc); +int X509_REVOKED_add_ext(X509_REVOKED *x, X509_EXTENSION *ex, int loc); +void * X509_REVOKED_get_ext_d2i(X509_REVOKED *x, int nid, int *crit, int *idx); +int X509_REVOKED_add1_ext_i2d(X509_REVOKED *x, int nid, void *value, int crit, + unsigned long flags); + +X509_EXTENSION *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex, + int nid, int crit, ASN1_OCTET_STRING *data); +X509_EXTENSION *X509_EXTENSION_create_by_OBJ(X509_EXTENSION **ex, + ASN1_OBJECT *obj,int crit,ASN1_OCTET_STRING *data); +int X509_EXTENSION_set_object(X509_EXTENSION *ex,ASN1_OBJECT *obj); +int X509_EXTENSION_set_critical(X509_EXTENSION *ex, int crit); +int X509_EXTENSION_set_data(X509_EXTENSION *ex, + ASN1_OCTET_STRING *data); +ASN1_OBJECT * X509_EXTENSION_get_object(X509_EXTENSION *ex); +ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ne); +int X509_EXTENSION_get_critical(X509_EXTENSION *ex); + +int X509at_get_attr_count(const STACK_OF(X509_ATTRIBUTE) *x); +int X509at_get_attr_by_NID(const STACK_OF(X509_ATTRIBUTE) *x, int nid, + int lastpos); +int X509at_get_attr_by_OBJ(const STACK_OF(X509_ATTRIBUTE) *sk, ASN1_OBJECT *obj, + int lastpos); +X509_ATTRIBUTE *X509at_get_attr(const STACK_OF(X509_ATTRIBUTE) *x, int loc); +X509_ATTRIBUTE *X509at_delete_attr(STACK_OF(X509_ATTRIBUTE) *x, int loc); +STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr(STACK_OF(X509_ATTRIBUTE) **x, + X509_ATTRIBUTE *attr); +STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_OBJ(STACK_OF(X509_ATTRIBUTE) **x, + const ASN1_OBJECT *obj, int type, + const unsigned char *bytes, int len); +STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_NID(STACK_OF(X509_ATTRIBUTE) **x, + int nid, int type, + const unsigned char *bytes, int len); +STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_txt(STACK_OF(X509_ATTRIBUTE) **x, + const char *attrname, int type, + const unsigned char *bytes, int len); +X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_NID(X509_ATTRIBUTE **attr, int nid, + int atrtype, const void *data, int len); +X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_OBJ(X509_ATTRIBUTE **attr, + const ASN1_OBJECT *obj, int atrtype, const void *data, int len); +X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_txt(X509_ATTRIBUTE **attr, + const char *atrname, int type, const unsigned char *bytes, int len); +int X509_ATTRIBUTE_set1_object(X509_ATTRIBUTE *attr, const ASN1_OBJECT *obj); +int X509_ATTRIBUTE_set1_data(X509_ATTRIBUTE *attr, int attrtype, const void *data, int len); +void *X509_ATTRIBUTE_get0_data(X509_ATTRIBUTE *attr, int idx, + int atrtype, void *data); +int X509_ATTRIBUTE_count(X509_ATTRIBUTE *attr); +ASN1_OBJECT *X509_ATTRIBUTE_get0_object(X509_ATTRIBUTE *attr); +ASN1_TYPE *X509_ATTRIBUTE_get0_type(X509_ATTRIBUTE *attr, int idx); + +int EVP_PKEY_get_attr_count(const EVP_PKEY *key); +int EVP_PKEY_get_attr_by_NID(const EVP_PKEY *key, int nid, + int lastpos); +int EVP_PKEY_get_attr_by_OBJ(const EVP_PKEY *key, ASN1_OBJECT *obj, + int lastpos); +X509_ATTRIBUTE *EVP_PKEY_get_attr(const EVP_PKEY *key, int loc); +X509_ATTRIBUTE *EVP_PKEY_delete_attr(EVP_PKEY *key, int loc); +int EVP_PKEY_add1_attr(EVP_PKEY *key, X509_ATTRIBUTE *attr); +int EVP_PKEY_add1_attr_by_OBJ(EVP_PKEY *key, + const ASN1_OBJECT *obj, int type, + const unsigned char *bytes, int len); +int EVP_PKEY_add1_attr_by_NID(EVP_PKEY *key, + int nid, int type, + const unsigned char *bytes, int len); +int EVP_PKEY_add1_attr_by_txt(EVP_PKEY *key, + const char *attrname, int type, + const unsigned char *bytes, int len); + +int X509_verify_cert(X509_STORE_CTX *ctx); + +/* lookup a cert from a X509 STACK */ +X509 *X509_find_by_issuer_and_serial(STACK_OF(X509) *sk,X509_NAME *name, + ASN1_INTEGER *serial); +X509 *X509_find_by_subject(STACK_OF(X509) *sk,X509_NAME *name); + +DECLARE_ASN1_FUNCTIONS(PBEPARAM) +DECLARE_ASN1_FUNCTIONS(PBE2PARAM) +DECLARE_ASN1_FUNCTIONS(PBKDF2PARAM) + +X509_ALGOR *PKCS5_pbe_set(int alg, int iter, unsigned char *salt, int saltlen); +X509_ALGOR *PKCS5_pbe2_set(const EVP_CIPHER *cipher, int iter, + unsigned char *salt, int saltlen); + +/* PKCS#8 utilities */ + +DECLARE_ASN1_FUNCTIONS(PKCS8_PRIV_KEY_INFO) + +EVP_PKEY *EVP_PKCS82PKEY(PKCS8_PRIV_KEY_INFO *p8); +PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8(EVP_PKEY *pkey); +PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8_broken(EVP_PKEY *pkey, int broken); +PKCS8_PRIV_KEY_INFO *PKCS8_set_broken(PKCS8_PRIV_KEY_INFO *p8, int broken); + +int X509_check_trust(X509 *x, int id, int flags); +int X509_TRUST_get_count(void); +X509_TRUST * X509_TRUST_get0(int idx); +int X509_TRUST_get_by_id(int id); +int X509_TRUST_add(int id, int flags, int (*ck)(X509_TRUST *, X509 *, int), + char *name, int arg1, void *arg2); +void X509_TRUST_cleanup(void); +int X509_TRUST_get_flags(X509_TRUST *xp); +char *X509_TRUST_get0_name(X509_TRUST *xp); +int X509_TRUST_get_trust(X509_TRUST *xp); + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_X509_strings(void); + +/* Error codes for the X509 functions. */ + +/* Function codes. */ +#define X509_F_ADD_CERT_DIR 100 +#define X509_F_BY_FILE_CTRL 101 +#define X509_F_CHECK_POLICY 145 +#define X509_F_DIR_CTRL 102 +#define X509_F_GET_CERT_BY_SUBJECT 103 +#define X509_F_NETSCAPE_SPKI_B64_DECODE 129 +#define X509_F_NETSCAPE_SPKI_B64_ENCODE 130 +#define X509_F_X509AT_ADD1_ATTR 135 +#define X509_F_X509V3_ADD_EXT 104 +#define X509_F_X509_ATTRIBUTE_CREATE_BY_NID 136 +#define X509_F_X509_ATTRIBUTE_CREATE_BY_OBJ 137 +#define X509_F_X509_ATTRIBUTE_CREATE_BY_TXT 140 +#define X509_F_X509_ATTRIBUTE_GET0_DATA 139 +#define X509_F_X509_ATTRIBUTE_SET1_DATA 138 +#define X509_F_X509_CHECK_PRIVATE_KEY 128 +#define X509_F_X509_CRL_PRINT_FP 147 +#define X509_F_X509_EXTENSION_CREATE_BY_NID 108 +#define X509_F_X509_EXTENSION_CREATE_BY_OBJ 109 +#define X509_F_X509_GET_PUBKEY_PARAMETERS 110 +#define X509_F_X509_LOAD_CERT_CRL_FILE 132 +#define X509_F_X509_LOAD_CERT_FILE 111 +#define X509_F_X509_LOAD_CRL_FILE 112 +#define X509_F_X509_NAME_ADD_ENTRY 113 +#define X509_F_X509_NAME_ENTRY_CREATE_BY_NID 114 +#define X509_F_X509_NAME_ENTRY_CREATE_BY_TXT 131 +#define X509_F_X509_NAME_ENTRY_SET_OBJECT 115 +#define X509_F_X509_NAME_ONELINE 116 +#define X509_F_X509_NAME_PRINT 117 +#define X509_F_X509_PRINT_EX_FP 118 +#define X509_F_X509_PUBKEY_GET 119 +#define X509_F_X509_PUBKEY_SET 120 +#define X509_F_X509_REQ_CHECK_PRIVATE_KEY 144 +#define X509_F_X509_REQ_PRINT_EX 121 +#define X509_F_X509_REQ_PRINT_FP 122 +#define X509_F_X509_REQ_TO_X509 123 +#define X509_F_X509_STORE_ADD_CERT 124 +#define X509_F_X509_STORE_ADD_CRL 125 +#define X509_F_X509_STORE_CTX_GET1_ISSUER 146 +#define X509_F_X509_STORE_CTX_INIT 143 +#define X509_F_X509_STORE_CTX_NEW 142 +#define X509_F_X509_STORE_CTX_PURPOSE_INHERIT 134 +#define X509_F_X509_TO_X509_REQ 126 +#define X509_F_X509_TRUST_ADD 133 +#define X509_F_X509_TRUST_SET 141 +#define X509_F_X509_VERIFY_CERT 127 + +/* Reason codes. */ +#define X509_R_BAD_X509_FILETYPE 100 +#define X509_R_BASE64_DECODE_ERROR 118 +#define X509_R_CANT_CHECK_DH_KEY 114 +#define X509_R_CERT_ALREADY_IN_HASH_TABLE 101 +#define X509_R_ERR_ASN1_LIB 102 +#define X509_R_INVALID_DIRECTORY 113 +#define X509_R_INVALID_FIELD_NAME 119 +#define X509_R_INVALID_TRUST 123 +#define X509_R_KEY_TYPE_MISMATCH 115 +#define X509_R_KEY_VALUES_MISMATCH 116 +#define X509_R_LOADING_CERT_DIR 103 +#define X509_R_LOADING_DEFAULTS 104 +#define X509_R_NO_CERT_SET_FOR_US_TO_VERIFY 105 +#define X509_R_SHOULD_RETRY 106 +#define X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN 107 +#define X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY 108 +#define X509_R_UNKNOWN_KEY_TYPE 117 +#define X509_R_UNKNOWN_NID 109 +#define X509_R_UNKNOWN_PURPOSE_ID 121 +#define X509_R_UNKNOWN_TRUST_ID 120 +#define X509_R_UNSUPPORTED_ALGORITHM 111 +#define X509_R_WRONG_LOOKUP_TYPE 112 +#define X509_R_WRONG_TYPE 122 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/include/openssl/x509_vfy.h b/production/3rdparty/openssl/include/openssl/x509_vfy.h new file mode 100644 index 00000000..3f163304 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/x509_vfy.h @@ -0,0 +1,530 @@ +/* crypto/x509/x509_vfy.h */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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 cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#ifndef HEADER_X509_H +#include +/* openssl/x509.h ends up #include-ing this file at about the only + * appropriate moment. */ +#endif + +#ifndef HEADER_X509_VFY_H +#define HEADER_X509_VFY_H + +#include +#ifndef OPENSSL_NO_LHASH +#include +#endif +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Outer object */ +typedef struct x509_hash_dir_st + { + int num_dirs; + char **dirs; + int *dirs_type; + int num_dirs_alloced; + } X509_HASH_DIR_CTX; + +typedef struct x509_file_st + { + int num_paths; /* number of paths to files or directories */ + int num_alloced; + char **paths; /* the list of paths or directories */ + int *path_type; + } X509_CERT_FILE_CTX; + +/*******************************/ +/* +SSL_CTX -> X509_STORE + -> X509_LOOKUP + ->X509_LOOKUP_METHOD + -> X509_LOOKUP + ->X509_LOOKUP_METHOD + +SSL -> X509_STORE_CTX + ->X509_STORE + +The X509_STORE holds the tables etc for verification stuff. +A X509_STORE_CTX is used while validating a single certificate. +The X509_STORE has X509_LOOKUPs for looking up certs. +The X509_STORE then calls a function to actually verify the +certificate chain. +*/ + +#define X509_LU_RETRY -1 +#define X509_LU_FAIL 0 +#define X509_LU_X509 1 +#define X509_LU_CRL 2 +#define X509_LU_PKEY 3 + +typedef struct x509_object_st + { + /* one of the above types */ + int type; + union { + char *ptr; + X509 *x509; + X509_CRL *crl; + EVP_PKEY *pkey; + } data; + } X509_OBJECT; + +typedef struct x509_lookup_st X509_LOOKUP; + +DECLARE_STACK_OF(X509_LOOKUP) +DECLARE_STACK_OF(X509_OBJECT) + +/* This is a static that defines the function interface */ +typedef struct x509_lookup_method_st + { + const char *name; + int (*new_item)(X509_LOOKUP *ctx); + void (*free)(X509_LOOKUP *ctx); + int (*init)(X509_LOOKUP *ctx); + int (*shutdown)(X509_LOOKUP *ctx); + int (*ctrl)(X509_LOOKUP *ctx,int cmd,const char *argc,long argl, + char **ret); + int (*get_by_subject)(X509_LOOKUP *ctx,int type,X509_NAME *name, + X509_OBJECT *ret); + int (*get_by_issuer_serial)(X509_LOOKUP *ctx,int type,X509_NAME *name, + ASN1_INTEGER *serial,X509_OBJECT *ret); + int (*get_by_fingerprint)(X509_LOOKUP *ctx,int type, + unsigned char *bytes,int len, + X509_OBJECT *ret); + int (*get_by_alias)(X509_LOOKUP *ctx,int type,char *str,int len, + X509_OBJECT *ret); + } X509_LOOKUP_METHOD; + +/* This structure hold all parameters associated with a verify operation + * by including an X509_VERIFY_PARAM structure in related structures the + * parameters used can be customized + */ + +typedef struct X509_VERIFY_PARAM_st + { + char *name; + time_t check_time; /* Time to use */ + unsigned long inh_flags; /* Inheritance flags */ + unsigned long flags; /* Various verify flags */ + int purpose; /* purpose to check untrusted certificates */ + int trust; /* trust setting to check */ + int depth; /* Verify depth */ + STACK_OF(ASN1_OBJECT) *policies; /* Permissible policies */ + } X509_VERIFY_PARAM; + +DECLARE_STACK_OF(X509_VERIFY_PARAM) + +/* This is used to hold everything. It is used for all certificate + * validation. Once we have a certificate chain, the 'verify' + * function is then called to actually check the cert chain. */ +struct x509_store_st + { + /* The following is a cache of trusted certs */ + int cache; /* if true, stash any hits */ + STACK_OF(X509_OBJECT) *objs; /* Cache of all objects */ + + /* These are external lookup methods */ + STACK_OF(X509_LOOKUP) *get_cert_methods; + + X509_VERIFY_PARAM *param; + + /* Callbacks for various operations */ + int (*verify)(X509_STORE_CTX *ctx); /* called to verify a certificate */ + int (*verify_cb)(int ok,X509_STORE_CTX *ctx); /* error callback */ + int (*get_issuer)(X509 **issuer, X509_STORE_CTX *ctx, X509 *x); /* get issuers cert from ctx */ + int (*check_issued)(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); /* check issued */ + int (*check_revocation)(X509_STORE_CTX *ctx); /* Check revocation status of chain */ + int (*get_crl)(X509_STORE_CTX *ctx, X509_CRL **crl, X509 *x); /* retrieve CRL */ + int (*check_crl)(X509_STORE_CTX *ctx, X509_CRL *crl); /* Check CRL validity */ + int (*cert_crl)(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x); /* Check certificate against CRL */ + int (*cleanup)(X509_STORE_CTX *ctx); + + CRYPTO_EX_DATA ex_data; + int references; + } /* X509_STORE */; + +int X509_STORE_set_depth(X509_STORE *store, int depth); + +#define X509_STORE_set_verify_cb_func(ctx,func) ((ctx)->verify_cb=(func)) +#define X509_STORE_set_verify_func(ctx,func) ((ctx)->verify=(func)) + +/* This is the functions plus an instance of the local variables. */ +struct x509_lookup_st + { + int init; /* have we been started */ + int skip; /* don't use us. */ + X509_LOOKUP_METHOD *method; /* the functions */ + char *method_data; /* method data */ + + X509_STORE *store_ctx; /* who owns us */ + } /* X509_LOOKUP */; + +/* This is a used when verifying cert chains. Since the + * gathering of the cert chain can take some time (and have to be + * 'retried', this needs to be kept and passed around. */ +struct x509_store_ctx_st /* X509_STORE_CTX */ + { + X509_STORE *ctx; + int current_method; /* used when looking up certs */ + + /* The following are set by the caller */ + X509 *cert; /* The cert to check */ + STACK_OF(X509) *untrusted; /* chain of X509s - untrusted - passed in */ + STACK_OF(X509_CRL) *crls; /* set of CRLs passed in */ + + X509_VERIFY_PARAM *param; + void *other_ctx; /* Other info for use with get_issuer() */ + + /* Callbacks for various operations */ + int (*verify)(X509_STORE_CTX *ctx); /* called to verify a certificate */ + int (*verify_cb)(int ok,X509_STORE_CTX *ctx); /* error callback */ + int (*get_issuer)(X509 **issuer, X509_STORE_CTX *ctx, X509 *x); /* get issuers cert from ctx */ + int (*check_issued)(X509_STORE_CTX *ctx, X509 *x, X509 *issuer); /* check issued */ + int (*check_revocation)(X509_STORE_CTX *ctx); /* Check revocation status of chain */ + int (*get_crl)(X509_STORE_CTX *ctx, X509_CRL **crl, X509 *x); /* retrieve CRL */ + int (*check_crl)(X509_STORE_CTX *ctx, X509_CRL *crl); /* Check CRL validity */ + int (*cert_crl)(X509_STORE_CTX *ctx, X509_CRL *crl, X509 *x); /* Check certificate against CRL */ + int (*check_policy)(X509_STORE_CTX *ctx); + int (*cleanup)(X509_STORE_CTX *ctx); + + /* The following is built up */ + int valid; /* if 0, rebuild chain */ + int last_untrusted; /* index of last untrusted cert */ + STACK_OF(X509) *chain; /* chain of X509s - built up and trusted */ + X509_POLICY_TREE *tree; /* Valid policy tree */ + + int explicit_policy; /* Require explicit policy value */ + + /* When something goes wrong, this is why */ + int error_depth; + int error; + X509 *current_cert; + X509 *current_issuer; /* cert currently being tested as valid issuer */ + X509_CRL *current_crl; /* current CRL */ + + CRYPTO_EX_DATA ex_data; + } /* X509_STORE_CTX */; + +void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth); + +#define X509_STORE_CTX_set_app_data(ctx,data) \ + X509_STORE_CTX_set_ex_data(ctx,0,data) +#define X509_STORE_CTX_get_app_data(ctx) \ + X509_STORE_CTX_get_ex_data(ctx,0) + +#define X509_L_FILE_LOAD 1 +#define X509_L_ADD_DIR 2 + +#define X509_LOOKUP_load_file(x,name,type) \ + X509_LOOKUP_ctrl((x),X509_L_FILE_LOAD,(name),(long)(type),NULL) + +#define X509_LOOKUP_add_dir(x,name,type) \ + X509_LOOKUP_ctrl((x),X509_L_ADD_DIR,(name),(long)(type),NULL) + +#define X509_V_OK 0 +/* illegal error (for uninitialized values, to avoid X509_V_OK): 1 */ + +#define X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT 2 +#define X509_V_ERR_UNABLE_TO_GET_CRL 3 +#define X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE 4 +#define X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE 5 +#define X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY 6 +#define X509_V_ERR_CERT_SIGNATURE_FAILURE 7 +#define X509_V_ERR_CRL_SIGNATURE_FAILURE 8 +#define X509_V_ERR_CERT_NOT_YET_VALID 9 +#define X509_V_ERR_CERT_HAS_EXPIRED 10 +#define X509_V_ERR_CRL_NOT_YET_VALID 11 +#define X509_V_ERR_CRL_HAS_EXPIRED 12 +#define X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD 13 +#define X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD 14 +#define X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD 15 +#define X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD 16 +#define X509_V_ERR_OUT_OF_MEM 17 +#define X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT 18 +#define X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN 19 +#define X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY 20 +#define X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE 21 +#define X509_V_ERR_CERT_CHAIN_TOO_LONG 22 +#define X509_V_ERR_CERT_REVOKED 23 +#define X509_V_ERR_INVALID_CA 24 +#define X509_V_ERR_PATH_LENGTH_EXCEEDED 25 +#define X509_V_ERR_INVALID_PURPOSE 26 +#define X509_V_ERR_CERT_UNTRUSTED 27 +#define X509_V_ERR_CERT_REJECTED 28 +/* These are 'informational' when looking for issuer cert */ +#define X509_V_ERR_SUBJECT_ISSUER_MISMATCH 29 +#define X509_V_ERR_AKID_SKID_MISMATCH 30 +#define X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH 31 +#define X509_V_ERR_KEYUSAGE_NO_CERTSIGN 32 + +#define X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER 33 +#define X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION 34 +#define X509_V_ERR_KEYUSAGE_NO_CRL_SIGN 35 +#define X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION 36 +#define X509_V_ERR_INVALID_NON_CA 37 +#define X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED 38 +#define X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE 39 +#define X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED 40 + +#define X509_V_ERR_INVALID_EXTENSION 41 +#define X509_V_ERR_INVALID_POLICY_EXTENSION 42 +#define X509_V_ERR_NO_EXPLICIT_POLICY 43 + + +/* The application is not happy */ +#define X509_V_ERR_APPLICATION_VERIFICATION 50 + +/* Certificate verify flags */ + +/* Send issuer+subject checks to verify_cb */ +#define X509_V_FLAG_CB_ISSUER_CHECK 0x1 +/* Use check time instead of current time */ +#define X509_V_FLAG_USE_CHECK_TIME 0x2 +/* Lookup CRLs */ +#define X509_V_FLAG_CRL_CHECK 0x4 +/* Lookup CRLs for whole chain */ +#define X509_V_FLAG_CRL_CHECK_ALL 0x8 +/* Ignore unhandled critical extensions */ +#define X509_V_FLAG_IGNORE_CRITICAL 0x10 +/* Disable workarounds for broken certificates */ +#define X509_V_FLAG_X509_STRICT 0x20 +/* Enable proxy certificate validation */ +#define X509_V_FLAG_ALLOW_PROXY_CERTS 0x40 +/* Enable policy checking */ +#define X509_V_FLAG_POLICY_CHECK 0x80 +/* Policy variable require-explicit-policy */ +#define X509_V_FLAG_EXPLICIT_POLICY 0x100 +/* Policy variable inhibit-any-policy */ +#define X509_V_FLAG_INHIBIT_ANY 0x200 +/* Policy variable inhibit-policy-mapping */ +#define X509_V_FLAG_INHIBIT_MAP 0x400 +/* Notify callback that policy is OK */ +#define X509_V_FLAG_NOTIFY_POLICY 0x800 + +#define X509_VP_FLAG_DEFAULT 0x1 +#define X509_VP_FLAG_OVERWRITE 0x2 +#define X509_VP_FLAG_RESET_FLAGS 0x4 +#define X509_VP_FLAG_LOCKED 0x8 +#define X509_VP_FLAG_ONCE 0x10 + +/* Internal use: mask of policy related options */ +#define X509_V_FLAG_POLICY_MASK (X509_V_FLAG_POLICY_CHECK \ + | X509_V_FLAG_EXPLICIT_POLICY \ + | X509_V_FLAG_INHIBIT_ANY \ + | X509_V_FLAG_INHIBIT_MAP) + +int X509_OBJECT_idx_by_subject(STACK_OF(X509_OBJECT) *h, int type, + X509_NAME *name); +X509_OBJECT *X509_OBJECT_retrieve_by_subject(STACK_OF(X509_OBJECT) *h,int type,X509_NAME *name); +X509_OBJECT *X509_OBJECT_retrieve_match(STACK_OF(X509_OBJECT) *h, X509_OBJECT *x); +void X509_OBJECT_up_ref_count(X509_OBJECT *a); +void X509_OBJECT_free_contents(X509_OBJECT *a); +X509_STORE *X509_STORE_new(void ); +void X509_STORE_free(X509_STORE *v); + +int X509_STORE_set_flags(X509_STORE *ctx, unsigned long flags); +int X509_STORE_set_purpose(X509_STORE *ctx, int purpose); +int X509_STORE_set_trust(X509_STORE *ctx, int trust); +int X509_STORE_set1_param(X509_STORE *ctx, X509_VERIFY_PARAM *pm); + +X509_STORE_CTX *X509_STORE_CTX_new(void); + +int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x); + +void X509_STORE_CTX_free(X509_STORE_CTX *ctx); +int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, + X509 *x509, STACK_OF(X509) *chain); +void X509_STORE_CTX_trusted_stack(X509_STORE_CTX *ctx, STACK_OF(X509) *sk); +void X509_STORE_CTX_cleanup(X509_STORE_CTX *ctx); + +X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v, X509_LOOKUP_METHOD *m); + +X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void); +X509_LOOKUP_METHOD *X509_LOOKUP_file(void); + +int X509_STORE_add_cert(X509_STORE *ctx, X509 *x); +int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x); + +int X509_STORE_get_by_subject(X509_STORE_CTX *vs,int type,X509_NAME *name, + X509_OBJECT *ret); + +int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc, + long argl, char **ret); + +#ifndef OPENSSL_NO_STDIO +int X509_load_cert_file(X509_LOOKUP *ctx, const char *file, int type); +int X509_load_crl_file(X509_LOOKUP *ctx, const char *file, int type); +int X509_load_cert_crl_file(X509_LOOKUP *ctx, const char *file, int type); +#endif + + +X509_LOOKUP *X509_LOOKUP_new(X509_LOOKUP_METHOD *method); +void X509_LOOKUP_free(X509_LOOKUP *ctx); +int X509_LOOKUP_init(X509_LOOKUP *ctx); +int X509_LOOKUP_by_subject(X509_LOOKUP *ctx, int type, X509_NAME *name, + X509_OBJECT *ret); +int X509_LOOKUP_by_issuer_serial(X509_LOOKUP *ctx, int type, X509_NAME *name, + ASN1_INTEGER *serial, X509_OBJECT *ret); +int X509_LOOKUP_by_fingerprint(X509_LOOKUP *ctx, int type, + unsigned char *bytes, int len, X509_OBJECT *ret); +int X509_LOOKUP_by_alias(X509_LOOKUP *ctx, int type, char *str, + int len, X509_OBJECT *ret); +int X509_LOOKUP_shutdown(X509_LOOKUP *ctx); + +#ifndef OPENSSL_NO_STDIO +int X509_STORE_load_locations (X509_STORE *ctx, + const char *file, const char *dir); +int X509_STORE_set_default_paths(X509_STORE *ctx); +#endif + +int X509_STORE_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, + CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func); +int X509_STORE_CTX_set_ex_data(X509_STORE_CTX *ctx,int idx,void *data); +void * X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx,int idx); +int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx); +void X509_STORE_CTX_set_error(X509_STORE_CTX *ctx,int s); +int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx); +X509 * X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx); +STACK_OF(X509) *X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx); +STACK_OF(X509) *X509_STORE_CTX_get1_chain(X509_STORE_CTX *ctx); +void X509_STORE_CTX_set_cert(X509_STORE_CTX *c,X509 *x); +void X509_STORE_CTX_set_chain(X509_STORE_CTX *c,STACK_OF(X509) *sk); +void X509_STORE_CTX_set0_crls(X509_STORE_CTX *c,STACK_OF(X509_CRL) *sk); +int X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose); +int X509_STORE_CTX_set_trust(X509_STORE_CTX *ctx, int trust); +int X509_STORE_CTX_purpose_inherit(X509_STORE_CTX *ctx, int def_purpose, + int purpose, int trust); +void X509_STORE_CTX_set_flags(X509_STORE_CTX *ctx, unsigned long flags); +void X509_STORE_CTX_set_time(X509_STORE_CTX *ctx, unsigned long flags, + time_t t); +void X509_STORE_CTX_set_verify_cb(X509_STORE_CTX *ctx, + int (*verify_cb)(int, X509_STORE_CTX *)); + +X509_POLICY_TREE *X509_STORE_CTX_get0_policy_tree(X509_STORE_CTX *ctx); +int X509_STORE_CTX_get_explicit_policy(X509_STORE_CTX *ctx); + +X509_VERIFY_PARAM *X509_STORE_CTX_get0_param(X509_STORE_CTX *ctx); +void X509_STORE_CTX_set0_param(X509_STORE_CTX *ctx, X509_VERIFY_PARAM *param); +int X509_STORE_CTX_set_default(X509_STORE_CTX *ctx, const char *name); + +/* X509_VERIFY_PARAM functions */ + +X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void); +void X509_VERIFY_PARAM_free(X509_VERIFY_PARAM *param); +int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *to, + const X509_VERIFY_PARAM *from); +int X509_VERIFY_PARAM_set1(X509_VERIFY_PARAM *to, + const X509_VERIFY_PARAM *from); +int X509_VERIFY_PARAM_set1_name(X509_VERIFY_PARAM *param, const char *name); +int X509_VERIFY_PARAM_set_flags(X509_VERIFY_PARAM *param, unsigned long flags); +int X509_VERIFY_PARAM_clear_flags(X509_VERIFY_PARAM *param, + unsigned long flags); +unsigned long X509_VERIFY_PARAM_get_flags(X509_VERIFY_PARAM *param); +int X509_VERIFY_PARAM_set_purpose(X509_VERIFY_PARAM *param, int purpose); +int X509_VERIFY_PARAM_set_trust(X509_VERIFY_PARAM *param, int trust); +void X509_VERIFY_PARAM_set_depth(X509_VERIFY_PARAM *param, int depth); +void X509_VERIFY_PARAM_set_time(X509_VERIFY_PARAM *param, time_t t); +int X509_VERIFY_PARAM_add0_policy(X509_VERIFY_PARAM *param, + ASN1_OBJECT *policy); +int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *param, + STACK_OF(ASN1_OBJECT) *policies); +int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param); + +int X509_VERIFY_PARAM_add0_table(X509_VERIFY_PARAM *param); +const X509_VERIFY_PARAM *X509_VERIFY_PARAM_lookup(const char *name); +void X509_VERIFY_PARAM_table_cleanup(void); + +int X509_policy_check(X509_POLICY_TREE **ptree, int *pexplicit_policy, + STACK_OF(X509) *certs, + STACK_OF(ASN1_OBJECT) *policy_oids, + unsigned int flags); + +void X509_policy_tree_free(X509_POLICY_TREE *tree); + +int X509_policy_tree_level_count(const X509_POLICY_TREE *tree); +X509_POLICY_LEVEL * + X509_policy_tree_get0_level(const X509_POLICY_TREE *tree, int i); + +STACK_OF(X509_POLICY_NODE) * + X509_policy_tree_get0_policies(const X509_POLICY_TREE *tree); + +STACK_OF(X509_POLICY_NODE) * + X509_policy_tree_get0_user_policies(const X509_POLICY_TREE *tree); + +int X509_policy_level_node_count(X509_POLICY_LEVEL *level); + +X509_POLICY_NODE *X509_policy_level_get0_node(X509_POLICY_LEVEL *level, int i); + +const ASN1_OBJECT *X509_policy_node_get0_policy(const X509_POLICY_NODE *node); + +STACK_OF(POLICYQUALINFO) * + X509_policy_node_get0_qualifiers(const X509_POLICY_NODE *node); +const X509_POLICY_NODE * + X509_policy_node_get0_parent(const X509_POLICY_NODE *node); + +#ifdef __cplusplus +} +#endif +#endif + diff --git a/production/3rdparty/openssl/include/openssl/x509v3.h b/production/3rdparty/openssl/include/openssl/x509v3.h new file mode 100644 index 00000000..34429828 --- /dev/null +++ b/production/3rdparty/openssl/include/openssl/x509v3.h @@ -0,0 +1,759 @@ +/* x509v3.h */ +/* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL + * project 1999. + */ +/* ==================================================================== + * Copyright (c) 1999-2004 The OpenSSL Project. 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 acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * licensing@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (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 product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +#ifndef HEADER_X509V3_H +#define HEADER_X509V3_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward reference */ +struct v3_ext_method; +struct v3_ext_ctx; + +/* Useful typedefs */ + +typedef void * (*X509V3_EXT_NEW)(void); +typedef void (*X509V3_EXT_FREE)(void *); +typedef void * (*X509V3_EXT_D2I)(void *, const unsigned char ** , long); +typedef int (*X509V3_EXT_I2D)(void *, unsigned char **); +typedef STACK_OF(CONF_VALUE) * (*X509V3_EXT_I2V)(struct v3_ext_method *method, void *ext, STACK_OF(CONF_VALUE) *extlist); +typedef void * (*X509V3_EXT_V2I)(struct v3_ext_method *method, struct v3_ext_ctx *ctx, STACK_OF(CONF_VALUE) *values); +typedef char * (*X509V3_EXT_I2S)(struct v3_ext_method *method, void *ext); +typedef void * (*X509V3_EXT_S2I)(struct v3_ext_method *method, struct v3_ext_ctx *ctx, const char *str); +typedef int (*X509V3_EXT_I2R)(struct v3_ext_method *method, void *ext, BIO *out, int indent); +typedef void * (*X509V3_EXT_R2I)(struct v3_ext_method *method, struct v3_ext_ctx *ctx, const char *str); + +/* V3 extension structure */ + +struct v3_ext_method { +int ext_nid; +int ext_flags; +/* If this is set the following four fields are ignored */ +ASN1_ITEM_EXP *it; +/* Old style ASN1 calls */ +X509V3_EXT_NEW ext_new; +X509V3_EXT_FREE ext_free; +X509V3_EXT_D2I d2i; +X509V3_EXT_I2D i2d; + +/* The following pair is used for string extensions */ +X509V3_EXT_I2S i2s; +X509V3_EXT_S2I s2i; + +/* The following pair is used for multi-valued extensions */ +X509V3_EXT_I2V i2v; +X509V3_EXT_V2I v2i; + +/* The following are used for raw extensions */ +X509V3_EXT_I2R i2r; +X509V3_EXT_R2I r2i; + +void *usr_data; /* Any extension specific data */ +}; + +typedef struct X509V3_CONF_METHOD_st { +char * (*get_string)(void *db, char *section, char *value); +STACK_OF(CONF_VALUE) * (*get_section)(void *db, char *section); +void (*free_string)(void *db, char * string); +void (*free_section)(void *db, STACK_OF(CONF_VALUE) *section); +} X509V3_CONF_METHOD; + +/* Context specific info */ +struct v3_ext_ctx { +#define CTX_TEST 0x1 +int flags; +X509 *issuer_cert; +X509 *subject_cert; +X509_REQ *subject_req; +X509_CRL *crl; +X509V3_CONF_METHOD *db_meth; +void *db; +/* Maybe more here */ +}; + +typedef struct v3_ext_method X509V3_EXT_METHOD; + +DECLARE_STACK_OF(X509V3_EXT_METHOD) + +/* ext_flags values */ +#define X509V3_EXT_DYNAMIC 0x1 +#define X509V3_EXT_CTX_DEP 0x2 +#define X509V3_EXT_MULTILINE 0x4 + +typedef BIT_STRING_BITNAME ENUMERATED_NAMES; + +typedef struct BASIC_CONSTRAINTS_st { +int ca; +ASN1_INTEGER *pathlen; +} BASIC_CONSTRAINTS; + + +typedef struct PKEY_USAGE_PERIOD_st { +ASN1_GENERALIZEDTIME *notBefore; +ASN1_GENERALIZEDTIME *notAfter; +} PKEY_USAGE_PERIOD; + +typedef struct otherName_st { +ASN1_OBJECT *type_id; +ASN1_TYPE *value; +} OTHERNAME; + +typedef struct EDIPartyName_st { + ASN1_STRING *nameAssigner; + ASN1_STRING *partyName; +} EDIPARTYNAME; + +typedef struct GENERAL_NAME_st { + +#define GEN_OTHERNAME 0 +#define GEN_EMAIL 1 +#define GEN_DNS 2 +#define GEN_X400 3 +#define GEN_DIRNAME 4 +#define GEN_EDIPARTY 5 +#define GEN_URI 6 +#define GEN_IPADD 7 +#define GEN_RID 8 + +int type; +union { + char *ptr; + OTHERNAME *otherName; /* otherName */ + ASN1_IA5STRING *rfc822Name; + ASN1_IA5STRING *dNSName; + ASN1_TYPE *x400Address; + X509_NAME *directoryName; + EDIPARTYNAME *ediPartyName; + ASN1_IA5STRING *uniformResourceIdentifier; + ASN1_OCTET_STRING *iPAddress; + ASN1_OBJECT *registeredID; + + /* Old names */ + ASN1_OCTET_STRING *ip; /* iPAddress */ + X509_NAME *dirn; /* dirn */ + ASN1_IA5STRING *ia5;/* rfc822Name, dNSName, uniformResourceIdentifier */ + ASN1_OBJECT *rid; /* registeredID */ + ASN1_TYPE *other; /* x400Address */ +} d; +} GENERAL_NAME; + +typedef STACK_OF(GENERAL_NAME) GENERAL_NAMES; + +typedef struct ACCESS_DESCRIPTION_st { + ASN1_OBJECT *method; + GENERAL_NAME *location; +} ACCESS_DESCRIPTION; + +typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS; + +typedef STACK_OF(ASN1_OBJECT) EXTENDED_KEY_USAGE; + +DECLARE_STACK_OF(GENERAL_NAME) +DECLARE_ASN1_SET_OF(GENERAL_NAME) + +DECLARE_STACK_OF(ACCESS_DESCRIPTION) +DECLARE_ASN1_SET_OF(ACCESS_DESCRIPTION) + +typedef struct DIST_POINT_NAME_st { +int type; +union { + GENERAL_NAMES *fullname; + STACK_OF(X509_NAME_ENTRY) *relativename; +} name; +} DIST_POINT_NAME; + +typedef struct DIST_POINT_st { +DIST_POINT_NAME *distpoint; +ASN1_BIT_STRING *reasons; +GENERAL_NAMES *CRLissuer; +} DIST_POINT; + +typedef STACK_OF(DIST_POINT) CRL_DIST_POINTS; + +DECLARE_STACK_OF(DIST_POINT) +DECLARE_ASN1_SET_OF(DIST_POINT) + +typedef struct AUTHORITY_KEYID_st { +ASN1_OCTET_STRING *keyid; +GENERAL_NAMES *issuer; +ASN1_INTEGER *serial; +} AUTHORITY_KEYID; + +/* Strong extranet structures */ + +typedef struct SXNET_ID_st { + ASN1_INTEGER *zone; + ASN1_OCTET_STRING *user; +} SXNETID; + +DECLARE_STACK_OF(SXNETID) +DECLARE_ASN1_SET_OF(SXNETID) + +typedef struct SXNET_st { + ASN1_INTEGER *version; + STACK_OF(SXNETID) *ids; +} SXNET; + +typedef struct NOTICEREF_st { + ASN1_STRING *organization; + STACK_OF(ASN1_INTEGER) *noticenos; +} NOTICEREF; + +typedef struct USERNOTICE_st { + NOTICEREF *noticeref; + ASN1_STRING *exptext; +} USERNOTICE; + +typedef struct POLICYQUALINFO_st { + ASN1_OBJECT *pqualid; + union { + ASN1_IA5STRING *cpsuri; + USERNOTICE *usernotice; + ASN1_TYPE *other; + } d; +} POLICYQUALINFO; + +DECLARE_STACK_OF(POLICYQUALINFO) +DECLARE_ASN1_SET_OF(POLICYQUALINFO) + +typedef struct POLICYINFO_st { + ASN1_OBJECT *policyid; + STACK_OF(POLICYQUALINFO) *qualifiers; +} POLICYINFO; + +typedef STACK_OF(POLICYINFO) CERTIFICATEPOLICIES; + +DECLARE_STACK_OF(POLICYINFO) +DECLARE_ASN1_SET_OF(POLICYINFO) + +typedef struct POLICY_MAPPING_st { + ASN1_OBJECT *issuerDomainPolicy; + ASN1_OBJECT *subjectDomainPolicy; +} POLICY_MAPPING; + +DECLARE_STACK_OF(POLICY_MAPPING) + +typedef STACK_OF(POLICY_MAPPING) POLICY_MAPPINGS; + +typedef struct GENERAL_SUBTREE_st { + GENERAL_NAME *base; + ASN1_INTEGER *minimum; + ASN1_INTEGER *maximum; +} GENERAL_SUBTREE; + +DECLARE_STACK_OF(GENERAL_SUBTREE) + +typedef struct NAME_CONSTRAINTS_st { + STACK_OF(GENERAL_SUBTREE) *permittedSubtrees; + STACK_OF(GENERAL_SUBTREE) *excludedSubtrees; +} NAME_CONSTRAINTS; + +typedef struct POLICY_CONSTRAINTS_st { + ASN1_INTEGER *requireExplicitPolicy; + ASN1_INTEGER *inhibitPolicyMapping; +} POLICY_CONSTRAINTS; + +/* Proxy certificate structures, see RFC 3820 */ +typedef struct PROXY_POLICY_st + { + ASN1_OBJECT *policyLanguage; + ASN1_OCTET_STRING *policy; + } PROXY_POLICY; + +typedef struct PROXY_CERT_INFO_EXTENSION_st + { + ASN1_INTEGER *pcPathLengthConstraint; + PROXY_POLICY *proxyPolicy; + } PROXY_CERT_INFO_EXTENSION; + +DECLARE_ASN1_FUNCTIONS(PROXY_POLICY) +DECLARE_ASN1_FUNCTIONS(PROXY_CERT_INFO_EXTENSION) + + +#define X509V3_conf_err(val) ERR_add_error_data(6, "section:", val->section, \ +",name:", val->name, ",value:", val->value); + +#define X509V3_set_ctx_test(ctx) \ + X509V3_set_ctx(ctx, NULL, NULL, NULL, NULL, CTX_TEST) +#define X509V3_set_ctx_nodb(ctx) (ctx)->db = NULL; + +#define EXT_BITSTRING(nid, table) { nid, 0, ASN1_ITEM_ref(ASN1_BIT_STRING), \ + 0,0,0,0, \ + 0,0, \ + (X509V3_EXT_I2V)i2v_ASN1_BIT_STRING, \ + (X509V3_EXT_V2I)v2i_ASN1_BIT_STRING, \ + NULL, NULL, \ + table} + +#define EXT_IA5STRING(nid) { nid, 0, ASN1_ITEM_ref(ASN1_IA5STRING), \ + 0,0,0,0, \ + (X509V3_EXT_I2S)i2s_ASN1_IA5STRING, \ + (X509V3_EXT_S2I)s2i_ASN1_IA5STRING, \ + 0,0,0,0, \ + NULL} + +#define EXT_END { -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + + +/* X509_PURPOSE stuff */ + +#define EXFLAG_BCONS 0x1 +#define EXFLAG_KUSAGE 0x2 +#define EXFLAG_XKUSAGE 0x4 +#define EXFLAG_NSCERT 0x8 + +#define EXFLAG_CA 0x10 +#define EXFLAG_SS 0x20 +#define EXFLAG_V1 0x40 +#define EXFLAG_INVALID 0x80 +#define EXFLAG_SET 0x100 +#define EXFLAG_CRITICAL 0x200 +#define EXFLAG_PROXY 0x400 + +#define EXFLAG_INVALID_POLICY 0x400 + +#define KU_DIGITAL_SIGNATURE 0x0080 +#define KU_NON_REPUDIATION 0x0040 +#define KU_KEY_ENCIPHERMENT 0x0020 +#define KU_DATA_ENCIPHERMENT 0x0010 +#define KU_KEY_AGREEMENT 0x0008 +#define KU_KEY_CERT_SIGN 0x0004 +#define KU_CRL_SIGN 0x0002 +#define KU_ENCIPHER_ONLY 0x0001 +#define KU_DECIPHER_ONLY 0x8000 + +#define NS_SSL_CLIENT 0x80 +#define NS_SSL_SERVER 0x40 +#define NS_SMIME 0x20 +#define NS_OBJSIGN 0x10 +#define NS_SSL_CA 0x04 +#define NS_SMIME_CA 0x02 +#define NS_OBJSIGN_CA 0x01 +#define NS_ANY_CA (NS_SSL_CA|NS_SMIME_CA|NS_OBJSIGN_CA) + +#define XKU_SSL_SERVER 0x1 +#define XKU_SSL_CLIENT 0x2 +#define XKU_SMIME 0x4 +#define XKU_CODE_SIGN 0x8 +#define XKU_SGC 0x10 +#define XKU_OCSP_SIGN 0x20 +#define XKU_TIMESTAMP 0x40 +#define XKU_DVCS 0x80 + +#define X509_PURPOSE_DYNAMIC 0x1 +#define X509_PURPOSE_DYNAMIC_NAME 0x2 + +typedef struct x509_purpose_st { + int purpose; + int trust; /* Default trust ID */ + int flags; + int (*check_purpose)(const struct x509_purpose_st *, + const X509 *, int); + char *name; + char *sname; + void *usr_data; +} X509_PURPOSE; + +#define X509_PURPOSE_SSL_CLIENT 1 +#define X509_PURPOSE_SSL_SERVER 2 +#define X509_PURPOSE_NS_SSL_SERVER 3 +#define X509_PURPOSE_SMIME_SIGN 4 +#define X509_PURPOSE_SMIME_ENCRYPT 5 +#define X509_PURPOSE_CRL_SIGN 6 +#define X509_PURPOSE_ANY 7 +#define X509_PURPOSE_OCSP_HELPER 8 + +#define X509_PURPOSE_MIN 1 +#define X509_PURPOSE_MAX 8 + +/* Flags for X509V3_EXT_print() */ + +#define X509V3_EXT_UNKNOWN_MASK (0xfL << 16) +/* Return error for unknown extensions */ +#define X509V3_EXT_DEFAULT 0 +/* Print error for unknown extensions */ +#define X509V3_EXT_ERROR_UNKNOWN (1L << 16) +/* ASN1 parse unknown extensions */ +#define X509V3_EXT_PARSE_UNKNOWN (2L << 16) +/* BIO_dump unknown extensions */ +#define X509V3_EXT_DUMP_UNKNOWN (3L << 16) + +/* Flags for X509V3_add1_i2d */ + +#define X509V3_ADD_OP_MASK 0xfL +#define X509V3_ADD_DEFAULT 0L +#define X509V3_ADD_APPEND 1L +#define X509V3_ADD_REPLACE 2L +#define X509V3_ADD_REPLACE_EXISTING 3L +#define X509V3_ADD_KEEP_EXISTING 4L +#define X509V3_ADD_DELETE 5L +#define X509V3_ADD_SILENT 0x10 + +DECLARE_STACK_OF(X509_PURPOSE) + +DECLARE_ASN1_FUNCTIONS(BASIC_CONSTRAINTS) + +DECLARE_ASN1_FUNCTIONS(SXNET) +DECLARE_ASN1_FUNCTIONS(SXNETID) + +int SXNET_add_id_asc(SXNET **psx, char *zone, char *user, int userlen); +int SXNET_add_id_ulong(SXNET **psx, unsigned long lzone, char *user, int userlen); +int SXNET_add_id_INTEGER(SXNET **psx, ASN1_INTEGER *izone, char *user, int userlen); + +ASN1_OCTET_STRING *SXNET_get_id_asc(SXNET *sx, char *zone); +ASN1_OCTET_STRING *SXNET_get_id_ulong(SXNET *sx, unsigned long lzone); +ASN1_OCTET_STRING *SXNET_get_id_INTEGER(SXNET *sx, ASN1_INTEGER *zone); + +DECLARE_ASN1_FUNCTIONS(AUTHORITY_KEYID) + +DECLARE_ASN1_FUNCTIONS(PKEY_USAGE_PERIOD) + +DECLARE_ASN1_FUNCTIONS(GENERAL_NAME) + + +ASN1_BIT_STRING *v2i_ASN1_BIT_STRING(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval); +STACK_OF(CONF_VALUE) *i2v_ASN1_BIT_STRING(X509V3_EXT_METHOD *method, + ASN1_BIT_STRING *bits, + STACK_OF(CONF_VALUE) *extlist); + +STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME(X509V3_EXT_METHOD *method, GENERAL_NAME *gen, STACK_OF(CONF_VALUE) *ret); +int GENERAL_NAME_print(BIO *out, GENERAL_NAME *gen); + +DECLARE_ASN1_FUNCTIONS(GENERAL_NAMES) + +STACK_OF(CONF_VALUE) *i2v_GENERAL_NAMES(X509V3_EXT_METHOD *method, + GENERAL_NAMES *gen, STACK_OF(CONF_VALUE) *extlist); +GENERAL_NAMES *v2i_GENERAL_NAMES(X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval); + +DECLARE_ASN1_FUNCTIONS(OTHERNAME) +DECLARE_ASN1_FUNCTIONS(EDIPARTYNAME) + +char *i2s_ASN1_OCTET_STRING(X509V3_EXT_METHOD *method, ASN1_OCTET_STRING *ia5); +ASN1_OCTET_STRING *s2i_ASN1_OCTET_STRING(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, char *str); + +DECLARE_ASN1_FUNCTIONS(EXTENDED_KEY_USAGE) +int i2a_ACCESS_DESCRIPTION(BIO *bp, ACCESS_DESCRIPTION* a); + +DECLARE_ASN1_FUNCTIONS(CERTIFICATEPOLICIES) +DECLARE_ASN1_FUNCTIONS(POLICYINFO) +DECLARE_ASN1_FUNCTIONS(POLICYQUALINFO) +DECLARE_ASN1_FUNCTIONS(USERNOTICE) +DECLARE_ASN1_FUNCTIONS(NOTICEREF) + +DECLARE_ASN1_FUNCTIONS(CRL_DIST_POINTS) +DECLARE_ASN1_FUNCTIONS(DIST_POINT) +DECLARE_ASN1_FUNCTIONS(DIST_POINT_NAME) + +DECLARE_ASN1_FUNCTIONS(ACCESS_DESCRIPTION) +DECLARE_ASN1_FUNCTIONS(AUTHORITY_INFO_ACCESS) + +DECLARE_ASN1_ITEM(POLICY_MAPPING) +DECLARE_ASN1_ALLOC_FUNCTIONS(POLICY_MAPPING) +DECLARE_ASN1_ITEM(POLICY_MAPPINGS) + +DECLARE_ASN1_ITEM(GENERAL_SUBTREE) +DECLARE_ASN1_ALLOC_FUNCTIONS(GENERAL_SUBTREE) + +DECLARE_ASN1_ITEM(NAME_CONSTRAINTS) +DECLARE_ASN1_ALLOC_FUNCTIONS(NAME_CONSTRAINTS) + +DECLARE_ASN1_ALLOC_FUNCTIONS(POLICY_CONSTRAINTS) +DECLARE_ASN1_ITEM(POLICY_CONSTRAINTS) + +#ifdef HEADER_CONF_H +GENERAL_NAME *v2i_GENERAL_NAME(X509V3_EXT_METHOD *method, X509V3_CTX *ctx, + CONF_VALUE *cnf); +GENERAL_NAME *v2i_GENERAL_NAME_ex(GENERAL_NAME *out, X509V3_EXT_METHOD *method, + X509V3_CTX *ctx, CONF_VALUE *cnf, int is_nc); +void X509V3_conf_free(CONF_VALUE *val); + +X509_EXTENSION *X509V3_EXT_nconf_nid(CONF *conf, X509V3_CTX *ctx, int ext_nid, char *value); +X509_EXTENSION *X509V3_EXT_nconf(CONF *conf, X509V3_CTX *ctx, char *name, char *value); +int X509V3_EXT_add_nconf_sk(CONF *conf, X509V3_CTX *ctx, char *section, STACK_OF(X509_EXTENSION) **sk); +int X509V3_EXT_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section, X509 *cert); +int X509V3_EXT_REQ_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section, X509_REQ *req); +int X509V3_EXT_CRL_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section, X509_CRL *crl); + +X509_EXTENSION *X509V3_EXT_conf_nid(LHASH *conf, X509V3_CTX *ctx, int ext_nid, char *value); +X509_EXTENSION *X509V3_EXT_conf(LHASH *conf, X509V3_CTX *ctx, char *name, char *value); +int X509V3_EXT_add_conf(LHASH *conf, X509V3_CTX *ctx, char *section, X509 *cert); +int X509V3_EXT_REQ_add_conf(LHASH *conf, X509V3_CTX *ctx, char *section, X509_REQ *req); +int X509V3_EXT_CRL_add_conf(LHASH *conf, X509V3_CTX *ctx, char *section, X509_CRL *crl); + +int X509V3_add_value_bool_nf(char *name, int asn1_bool, + STACK_OF(CONF_VALUE) **extlist); +int X509V3_get_value_bool(CONF_VALUE *value, int *asn1_bool); +int X509V3_get_value_int(CONF_VALUE *value, ASN1_INTEGER **aint); +void X509V3_set_nconf(X509V3_CTX *ctx, CONF *conf); +void X509V3_set_conf_lhash(X509V3_CTX *ctx, LHASH *lhash); +#endif + +char * X509V3_get_string(X509V3_CTX *ctx, char *name, char *section); +STACK_OF(CONF_VALUE) * X509V3_get_section(X509V3_CTX *ctx, char *section); +void X509V3_string_free(X509V3_CTX *ctx, char *str); +void X509V3_section_free( X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *section); +void X509V3_set_ctx(X509V3_CTX *ctx, X509 *issuer, X509 *subject, + X509_REQ *req, X509_CRL *crl, int flags); + +int X509V3_add_value(const char *name, const char *value, + STACK_OF(CONF_VALUE) **extlist); +int X509V3_add_value_uchar(const char *name, const unsigned char *value, + STACK_OF(CONF_VALUE) **extlist); +int X509V3_add_value_bool(const char *name, int asn1_bool, + STACK_OF(CONF_VALUE) **extlist); +int X509V3_add_value_int(const char *name, ASN1_INTEGER *aint, + STACK_OF(CONF_VALUE) **extlist); +char * i2s_ASN1_INTEGER(X509V3_EXT_METHOD *meth, ASN1_INTEGER *aint); +ASN1_INTEGER * s2i_ASN1_INTEGER(X509V3_EXT_METHOD *meth, char *value); +char * i2s_ASN1_ENUMERATED(X509V3_EXT_METHOD *meth, ASN1_ENUMERATED *aint); +char * i2s_ASN1_ENUMERATED_TABLE(X509V3_EXT_METHOD *meth, ASN1_ENUMERATED *aint); +int X509V3_EXT_add(X509V3_EXT_METHOD *ext); +int X509V3_EXT_add_list(X509V3_EXT_METHOD *extlist); +int X509V3_EXT_add_alias(int nid_to, int nid_from); +void X509V3_EXT_cleanup(void); + +X509V3_EXT_METHOD *X509V3_EXT_get(X509_EXTENSION *ext); +X509V3_EXT_METHOD *X509V3_EXT_get_nid(int nid); +int X509V3_add_standard_extensions(void); +STACK_OF(CONF_VALUE) *X509V3_parse_list(const char *line); +void *X509V3_EXT_d2i(X509_EXTENSION *ext); +void *X509V3_get_d2i(STACK_OF(X509_EXTENSION) *x, int nid, int *crit, int *idx); + + +X509_EXTENSION *X509V3_EXT_i2d(int ext_nid, int crit, void *ext_struc); +int X509V3_add1_i2d(STACK_OF(X509_EXTENSION) **x, int nid, void *value, int crit, unsigned long flags); + +char *hex_to_string(unsigned char *buffer, long len); +unsigned char *string_to_hex(char *str, long *len); +int name_cmp(const char *name, const char *cmp); + +void X509V3_EXT_val_prn(BIO *out, STACK_OF(CONF_VALUE) *val, int indent, + int ml); +int X509V3_EXT_print(BIO *out, X509_EXTENSION *ext, unsigned long flag, int indent); +int X509V3_EXT_print_fp(FILE *out, X509_EXTENSION *ext, int flag, int indent); + +int X509V3_extensions_print(BIO *out, char *title, STACK_OF(X509_EXTENSION) *exts, unsigned long flag, int indent); + +int X509_check_ca(X509 *x); +int X509_check_purpose(X509 *x, int id, int ca); +int X509_supported_extension(X509_EXTENSION *ex); +int X509_PURPOSE_set(int *p, int purpose); +int X509_check_issued(X509 *issuer, X509 *subject); +int X509_PURPOSE_get_count(void); +X509_PURPOSE * X509_PURPOSE_get0(int idx); +int X509_PURPOSE_get_by_sname(char *sname); +int X509_PURPOSE_get_by_id(int id); +int X509_PURPOSE_add(int id, int trust, int flags, + int (*ck)(const X509_PURPOSE *, const X509 *, int), + char *name, char *sname, void *arg); +char *X509_PURPOSE_get0_name(X509_PURPOSE *xp); +char *X509_PURPOSE_get0_sname(X509_PURPOSE *xp); +int X509_PURPOSE_get_trust(X509_PURPOSE *xp); +void X509_PURPOSE_cleanup(void); +int X509_PURPOSE_get_id(X509_PURPOSE *); + +STACK *X509_get1_email(X509 *x); +STACK *X509_REQ_get1_email(X509_REQ *x); +void X509_email_free(STACK *sk); + +ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc); +ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc); +int X509V3_NAME_from_section(X509_NAME *nm, STACK_OF(CONF_VALUE)*dn_sk, + unsigned long chtype); + +void X509_POLICY_NODE_print(BIO *out, X509_POLICY_NODE *node, int indent); + +/* BEGIN ERROR CODES */ +/* The following lines are auto generated by the script mkerr.pl. Any changes + * made after this point may be overwritten when the script is next run. + */ +void ERR_load_X509V3_strings(void); + +/* Error codes for the X509V3 functions. */ + +/* Function codes. */ +#define X509V3_F_COPY_EMAIL 122 +#define X509V3_F_COPY_ISSUER 123 +#define X509V3_F_DO_DIRNAME 144 +#define X509V3_F_DO_EXT_CONF 124 +#define X509V3_F_DO_EXT_I2D 135 +#define X509V3_F_DO_EXT_NCONF 151 +#define X509V3_F_DO_I2V_NAME_CONSTRAINTS 148 +#define X509V3_F_HEX_TO_STRING 111 +#define X509V3_F_I2S_ASN1_ENUMERATED 121 +#define X509V3_F_I2S_ASN1_IA5STRING 149 +#define X509V3_F_I2S_ASN1_INTEGER 120 +#define X509V3_F_I2V_AUTHORITY_INFO_ACCESS 138 +#define X509V3_F_NOTICE_SECTION 132 +#define X509V3_F_NREF_NOS 133 +#define X509V3_F_POLICY_SECTION 131 +#define X509V3_F_PROCESS_PCI_VALUE 150 +#define X509V3_F_R2I_CERTPOL 130 +#define X509V3_F_R2I_PCI 155 +#define X509V3_F_S2I_ASN1_IA5STRING 100 +#define X509V3_F_S2I_ASN1_INTEGER 108 +#define X509V3_F_S2I_ASN1_OCTET_STRING 112 +#define X509V3_F_S2I_ASN1_SKEY_ID 114 +#define X509V3_F_S2I_SKEY_ID 115 +#define X509V3_F_STRING_TO_HEX 113 +#define X509V3_F_SXNET_ADD_ID_ASC 125 +#define X509V3_F_SXNET_ADD_ID_INTEGER 126 +#define X509V3_F_SXNET_ADD_ID_ULONG 127 +#define X509V3_F_SXNET_GET_ID_ASC 128 +#define X509V3_F_SXNET_GET_ID_ULONG 129 +#define X509V3_F_V2I_ASN1_BIT_STRING 101 +#define X509V3_F_V2I_AUTHORITY_INFO_ACCESS 139 +#define X509V3_F_V2I_AUTHORITY_KEYID 119 +#define X509V3_F_V2I_BASIC_CONSTRAINTS 102 +#define X509V3_F_V2I_CRLD 134 +#define X509V3_F_V2I_EXTENDED_KEY_USAGE 103 +#define X509V3_F_V2I_GENERAL_NAMES 118 +#define X509V3_F_V2I_GENERAL_NAME_EX 117 +#define X509V3_F_V2I_ISSUER_ALT 153 +#define X509V3_F_V2I_NAME_CONSTRAINTS 147 +#define X509V3_F_V2I_POLICY_CONSTRAINTS 146 +#define X509V3_F_V2I_POLICY_MAPPINGS 145 +#define X509V3_F_V2I_SUBJECT_ALT 154 +#define X509V3_F_V3_GENERIC_EXTENSION 116 +#define X509V3_F_X509V3_ADD1_I2D 140 +#define X509V3_F_X509V3_ADD_VALUE 105 +#define X509V3_F_X509V3_EXT_ADD 104 +#define X509V3_F_X509V3_EXT_ADD_ALIAS 106 +#define X509V3_F_X509V3_EXT_CONF 107 +#define X509V3_F_X509V3_EXT_I2D 136 +#define X509V3_F_X509V3_EXT_NCONF 152 +#define X509V3_F_X509V3_GET_SECTION 142 +#define X509V3_F_X509V3_GET_STRING 143 +#define X509V3_F_X509V3_GET_VALUE_BOOL 110 +#define X509V3_F_X509V3_PARSE_LIST 109 +#define X509V3_F_X509_PURPOSE_ADD 137 +#define X509V3_F_X509_PURPOSE_SET 141 + +/* Reason codes. */ +#define X509V3_R_BAD_IP_ADDRESS 118 +#define X509V3_R_BAD_OBJECT 119 +#define X509V3_R_BN_DEC2BN_ERROR 100 +#define X509V3_R_BN_TO_ASN1_INTEGER_ERROR 101 +#define X509V3_R_DIRNAME_ERROR 149 +#define X509V3_R_DUPLICATE_ZONE_ID 133 +#define X509V3_R_ERROR_CONVERTING_ZONE 131 +#define X509V3_R_ERROR_CREATING_EXTENSION 144 +#define X509V3_R_ERROR_IN_EXTENSION 128 +#define X509V3_R_EXPECTED_A_SECTION_NAME 137 +#define X509V3_R_EXTENSION_EXISTS 145 +#define X509V3_R_EXTENSION_NAME_ERROR 115 +#define X509V3_R_EXTENSION_NOT_FOUND 102 +#define X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED 103 +#define X509V3_R_EXTENSION_VALUE_ERROR 116 +#define X509V3_R_ILLEGAL_EMPTY_EXTENSION 151 +#define X509V3_R_ILLEGAL_HEX_DIGIT 113 +#define X509V3_R_INCORRECT_POLICY_SYNTAX_TAG 152 +#define X509V3_R_INVALID_BOOLEAN_STRING 104 +#define X509V3_R_INVALID_EXTENSION_STRING 105 +#define X509V3_R_INVALID_NAME 106 +#define X509V3_R_INVALID_NULL_ARGUMENT 107 +#define X509V3_R_INVALID_NULL_NAME 108 +#define X509V3_R_INVALID_NULL_VALUE 109 +#define X509V3_R_INVALID_NUMBER 140 +#define X509V3_R_INVALID_NUMBERS 141 +#define X509V3_R_INVALID_OBJECT_IDENTIFIER 110 +#define X509V3_R_INVALID_OPTION 138 +#define X509V3_R_INVALID_POLICY_IDENTIFIER 134 +#define X509V3_R_INVALID_PROXY_POLICY_SETTING 153 +#define X509V3_R_INVALID_PURPOSE 146 +#define X509V3_R_INVALID_SECTION 135 +#define X509V3_R_INVALID_SYNTAX 143 +#define X509V3_R_ISSUER_DECODE_ERROR 126 +#define X509V3_R_MISSING_VALUE 124 +#define X509V3_R_NEED_ORGANIZATION_AND_NUMBERS 142 +#define X509V3_R_NO_CONFIG_DATABASE 136 +#define X509V3_R_NO_ISSUER_CERTIFICATE 121 +#define X509V3_R_NO_ISSUER_DETAILS 127 +#define X509V3_R_NO_POLICY_IDENTIFIER 139 +#define X509V3_R_NO_PROXY_CERT_POLICY_LANGUAGE_DEFINED 154 +#define X509V3_R_NO_PUBLIC_KEY 114 +#define X509V3_R_NO_SUBJECT_DETAILS 125 +#define X509V3_R_ODD_NUMBER_OF_DIGITS 112 +#define X509V3_R_OPERATION_NOT_DEFINED 148 +#define X509V3_R_OTHERNAME_ERROR 147 +#define X509V3_R_POLICY_LANGUAGE_ALREADTY_DEFINED 155 +#define X509V3_R_POLICY_PATH_LENGTH 156 +#define X509V3_R_POLICY_PATH_LENGTH_ALREADTY_DEFINED 157 +#define X509V3_R_POLICY_SYNTAX_NOT_CURRENTLY_SUPPORTED 158 +#define X509V3_R_POLICY_WHEN_PROXY_LANGUAGE_REQUIRES_NO_POLICY 159 +#define X509V3_R_SECTION_NOT_FOUND 150 +#define X509V3_R_UNABLE_TO_GET_ISSUER_DETAILS 122 +#define X509V3_R_UNABLE_TO_GET_ISSUER_KEYID 123 +#define X509V3_R_UNKNOWN_BIT_STRING_ARGUMENT 111 +#define X509V3_R_UNKNOWN_EXTENSION 129 +#define X509V3_R_UNKNOWN_EXTENSION_NAME 130 +#define X509V3_R_UNKNOWN_OPTION 120 +#define X509V3_R_UNSUPPORTED_OPTION 117 +#define X509V3_R_USER_TOO_LONG 132 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/production/3rdparty/openssl/lib/libeay32.dll b/production/3rdparty/openssl/lib/libeay32.dll new file mode 100644 index 0000000000000000000000000000000000000000..59b005df27e553c4680f68f5a48e9bf41d38d3e1 GIT binary patch literal 1036288 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

      + +
      By Chris Koeritz (koeritz@gruntose.com)
      +

      Ax79qIitn#Efab~;p?tZxuvbH$ZQ$)^1 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 LNsrLk|Hl6hcs7zB literal 0 HcmV?d00001 diff --git a/database/pictures/clams_tran.gif b/database/pictures/clams_tran.gif new file mode 100644 index 0000000000000000000000000000000000000000..d804a06d611c5a1f2d7f200dd978a8289753678f GIT binary patch literal 9479 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�$T4AEZIFBCE!4 z^4SYgoGHKOj=O+>r*H)$`TvFVoRQq73>R9!^koI9$|d;J?EbZ}y!I~_YAu^$_m*Tl zCqFosu}`IWvX^%;lA<;ko{cMDn^FTwTPg1ZB1}Ot7T}K|;ryCJ&|IIFq4;#S*a}Eu zS6o92RjNS!bd*{DYjP4ax6@A=kQ5eWF1N5OnEyQB()W1W6eS9|tQfe={>3MuGT=Is zTgmTC-SQpGAt@r~z{=_oeg>@_fYZ)t`=wHhJt%*ywx)<9%fJ*x6`LTpkigBHS>!S6 zv1s;Z+m*K0x0&Sk+GR;9g-bt$n23N5VNHuK8?k)jIdmZO$u^^@IlinPvCA)uO?aew zCFOiFn=%q$c@EwIpYav6DycZO+MD6XUG5+lC$;|xR8nmlguvIL(Ss1R4+#7A{IFR6 zeKzJ9w)TwF?IZPlQxM`@E_lj?RTnx;cLg&~`-&8N$L0oc5wUkbOGOIPbiGhLH6uw- zXtOY+l>c9A7@tMnyJ{g3=Tn=ep*fOZctvoF*FKVtc`N_1oM;XB;rB+Y3C2Yej^J~4 z*KRluP}R1wb1n9@L{9G=Ku?~ySSqNY26V<#6JWTRt&HM&2Rs^7k3I-RL_FLqDAW;< zCKDz8)V2}=CWz14+zk1tDZXN6X>_Vx)ME^Ih~ECu$9=uaNw)i~Y(OEtlOrRrNMXDY ztN3jXJHP#=wu{LxU%y@a=3_~oZSWM{Pt&Oz2``m7@!zYJ15`~XgA25V6am6+A2*nx zB$OOda=qWD$V($g7LUt-b;-1$6mP2*Q$f#TL=C&gAn7%IbRb<%1p!kJW$4p!VoNhG zXPtMEV|Q=w!2#H(kX4Q?V71Ib{?81LNQ6?wdCDn}a9@k|GS567Qn19Z?Yb{GY4ov8 zaEu49+dAWzE5(x<2ub``4E?#8{_L-|un0bLTYDhg=_8C*-wpivbUad_M7sNDjveeK zWRT7`1yP2fyd4|QJlKA=bFV_b*p^`PAxURNkDqja2&R`?h-Lr8;i+<`= zy%H#(5%+?+@C%|mr+h>YY&|Gq{-V2x4{8`$ulNxB{9v0<;_dO$+hJ>8ES8uRrkgH8 zYL+*A%!?7NKwXa$IR%Sqj2}}E|1ajByEG(>5|(G#>IrWb*7+mkA!r?Wh_WX_CX!iz zDVut6Mi{E+DvW>fy-3c2rr3~LOGrGTr*^-9c4H4AnGh_#+12-G)4n;&|7jsPHrCA5 zE+CUwKwWvq5=%cfTyB_LSbAkDttv~<34~GUQf*;r+k9M=2r zzan&%zHE9kYt8dSzadvsR=4H$Yj3^X^#P_xQHFLIOuV075ri!Ck0W!)A5SLlVg?e& z*0|P8CSMGxRKu3)Vyn?c;^#j&ILPN&mm-rRl_xO{TzhT4_=*kC*p)daD|-jjl+(=0 zXgHaJD1g%O7q^S4Hq|976)nLrsZ5_t*Gy@JvOB?ifMYu219cEZzygweU6O+K)5VTD{#-Q82gDTsst?l22+0LzO%)=8!0$F(W zyjHr}k77F&HE5~>{QLg%I^Eq%!sONmO9Edo$kBPpgx#ZE5d^dqXEkyd20g4Y6-s(0 zboeAv&dFZtL@hNMWT~sEf~oz)vSTq>@`2j{nXXDKs*ZY5s9tL3(>c2B^J9LW5~`nyDipIY2J3nymTE)to@;nn|WExcXQhdxVZ@Hm?nn zGfoFD%_eDKmU}AD98#n3U{~jh@*{LO(yJ-0`|b%fB+j*G6gPm*#xNB%`=i2tLL808 ziq2!m(DJ8a9zxw_^f5xd18QoVnoS{LBxv`X{NJzA~2F4V2GPouNo;4)ND`~ z(}X%x_y*LWJ{s2BLvdpW(!N5Dl}3*Oy=EJy>Z9+0EhF0UL{qDhO5)Xho%>BI2m8s2 zcd-Usp6YlK|^V3u$CaP^d3y5j@ zwScd%L6*xZ^D@|1iAHpgUgN$qT!yna)uX-@q7Q9DqfDm3+t;{CLLRW%dEgomG5Fya_gfg>Pide=kg=GscpowuKX9jH z#(vhIlS=(-J?o~0$Y{3f!Np`_i(1*mxe6l*K9k_^QU@zHCw)yFWB%0mD@aH6;s;bq`{`4HzySZPF#1fRWF40*e2IR z6jItynCsHcl$rx|071P*{B?HiQ4K`FYRVdQ3=LS~;}_n^r6xZ*z&7d|gNgL?FDI$O z?|-V<7vt{XXlDx|`pyvXR+vjYePh31*(ZZ7>RPM#bhz5k_ch5@xe{#5W-cmZ^nHU zOfKXsnvN=4^ur72JZ-Rfm4=d>f_g~~* zd(A8x3|^{`qa-TRXbu1TNkjio9*)?PrR=w5x-XF@7wBe}U%pV1qaS4mDMf-# z*jSsG^RT1tR?T@TZXlwTCh=G-E>KAD#L%m(+{2?qYC{0r0TvMN6mEol1j}(<%sq|KX+~0q`_5hhA$SOl)n*x|P7st||O&NO8Hg>uXY1 zBxN%>TLg4W;YM(Q6d@|H-TQ%j*sp1bIhSz(yl>_dMe)TfdMG5X@h}pDB>WxgD#e(%44Fbe@R_MlOpVlY%x$eg~y~HHt*v zyBQL&qG!M;*qoN8`}#}sU`c-@QMqVfdKpW#31rdDvMxv{@dYpGQe};|POGd5OWEMB z`4Q53XV|a*mWwsD3kD;=PNvZ7TJW!1(Z=R>NsQ=SxicxLXj$d1w>$#|bNkl7Zp})3 z@dV_wi|d*CeE7Z$B1tn;jDYL0tk}ifTGD1pu=&3LLO6+pHr_ zWjgF-ZRWP$YTnPU$p#)y-rBmG%sI{aN#MnEAx~rVg9vr_?Ik9yY8g|mMT0z{BGNbSIe6fTx=W*_LvmA0u_Yn=z_F@y zv(AQbbNiPSDKXfd!a>_3OntV)>+}87s11;yV^gPfex0}Cddx7SxBeHE{^gZ7_Evty zIY*!7z^D1Jy%a^38V)TGGI7&X6zVdokQXV*v+O%Z z$lXx+JK%~}KZDz1%N(+!=T%!8SC({HbYURrvDVx z9hAeC@9BNq@vpwXyG_lpwKHJj3!d3bZ4hr?m1B&Yt@sfqcD_G#^6yuU{NUb14R}g5 z$NS*(to$S{Fa-ksH(63rF()i?&fMQbOTsqxegDa%k2ePmRCz@Lb^c-RMPxJ^=6Z3& zk;4Fn>!T%Um8L;mqLLUoiEL8{MC?BqnhvFy9}B^CKkR%x7COmQJB9>VIB{j1z=5Xn z-T&zAZDY;j+8&Mrweg*`_L~0~tUfsz;{Wklq9O~q?BD)95pQ%ja9y{2qgTvjwVwAC z*{P9eM-8pN8xPrvx!q+5+po4=_|+j#ofqEI#Cgs?RANyzy+VijcN5czKXm8f;?cG< zcgA?c)^#o4roLAkz%J~Lk~t^~hVa@mmf1mmQG-oS^1|D~F-pQ`E5^0M@sL;9ZpB*; zJzhubmdYq9_Mu?Y$P6_?lT?!00EvLNlK?)q4Dr+J1tY>_xid&qO>Qgq!E0*|@Z zJgF*XghR92?}IK?{~pE(Xo7e5>Y2?%(~F~usx(G81zknOpYn1uNxs^175@70v07B) z9C^wB9}V@IzjuuP5`byc!>q-!pd2RNHqtI>ZytFWxwiU``ECYl6)vfTP`I-lhp6#g zY$C?La@#RHxy+tTR>yF9xc`zm+MwcJ=(Lza937$hmO3!?U8fmB4HwUD zs=fmXjk&ld(oBD(pE?|xU8WvNULO=77j7gH+*ii79SjhDA-bjxw8vK>feSSF^WS7D z7fzjUZ>D1Zgwv?#@q!a%mn+kIR?IAj=EudTucXq&oP4(C`U&cy+&zs^$0n?TXB1bO zbI_d10rzyc4C`Xr@l#83U)G#naChh;wQr%qYm&?_;_MwQ(p!$rdbA>GO7uLpx7;)9 zlJL&WPffP?lNG(boS2ug;ABTZx33PW2&3Yn&B2d;GL4q9s-6Rik=q8)nS}!CL|uB~ zH8ma>a}AUjN)n^;%w~X4pzSU7rplQ-ARwA3FcNskCI`En@GA0 z3q4;W<@m@q+b`M7CtJ#R^NC#k@y2`wRgzcD5*!StV$}m4w)b8?&UMsXFG2qZjKD^A zF~S0)gj3BbJU?b*S*8O;h(9|vTQFR!;F*`z?KMq|Xl?p7$(DPJYOJL4o5FSty%}6& zhmT(;g}d($8BhJY>bZ&{hxx;=2`9ON*bEmwTIlLtd@N`&%yk3PuiB=5UUhw-RK8#A zH~M<(>3CeKEl-7csudr`7U^$$=a4DGJfdi}t;5o39Lu*>TQ!x6yl19Ic82tV_Ur4=i!`T(yN_zT?j!{N z07)Y_&~LczTV=Q^&vHoV6NBpGfG&H+t=`4jHx>71Yio`e+*TplekvMJ0L)+zkA0A13$=YWiW4)rh4GizH_;vHQ3mPj_~PdhtYwWne^EDYKBj zg|pYoRlv$4LbJT4L4wcJ5(1xoG@j|Xu}KYmArz~{QvU!3=JPdXPA=7BX5nuEjdy2` z8{WLq+!2TYXwbP=WgZ#Pb@%PcHMFtHwoqcVd=Fs@WXp8>GjfUTZ--&ihGkkK1M}2&&=o-IM#!1eN=19 zd(>ki?psdnPW9>%#~gA3%vUJrdW`hxuI(|>-0jZ|d_c0X5oe;F;qLDDYte0%cRjz= z;jr^8BRz<2YCJ&j_i06H_G;Uh_QD$w;iHY09C-Y+o@HQ2uws&Il6Q`6&wixQ)tBrZ z6NgeVNcj+b`sU*s?h&ld6N<6-kZ{M}`VDC0-}aWP2H|p{n{ZU&?7+y+iPlGZ-**kd zc@{|8CQ*=tV}y7C?$xq5P(f?B>+KmE%EG;Lo~O8On_|7{wTdA8*0mpd8Fk}JF2$pv1*DOa0D4iCI&UELsp$%@-6$h^N6bLXS~0Qh?4A4j0w z>xmNb%f;;E9t<4*np?M^ziMsD{{T40{-*NOx6&1c=YNGsV#?VKa}L|<#?Xo70xHYQ z3}7GB{B_O7ym7_as1nw0eKH5jnD}a~!rXFzlNF8-J(TaqyZh_AeYoRO9A#vHk8Ly1 zc;ma_%T&~!=T82*O_rYIuVsoW7JDj8sUsW=7SF?;uHECgvfqw70=%=gHIF&r&w2CE zmsIVSBELZ5HUtCwt)?@2Ic~?Jhjy4q*PcSoUnGU)jZEH>sy82{OnEZ05WGfSrj&N! zy9ptd`S!lYn<3IWm$yC3v%hYy5;w5Ys^CA79Rc}hjhf#bR@>dX8zZL|FxrdDx@|RV zg&C`1NR}hbLGEGTbzG-XhF&`S{xZ2Swvmx{mhI~MbS)r_%~?7&2?~5O`W;G?n}z8f zoPTcfHUI-0E9cV#LTr2e{l0aLB58?SykKN7#(I0TtNuT=$3}bGeNz?bSA(-7gahI< z*xWD_)C5NMoQvgAe!9%=AJ~kf}oZ>e)Meg@%h%Y5SvW9CQbIDAP z?VqNc-reNfHf7tcH*Cs#Naa0P0C^Mp>8A~?Xr0ID8uHkq2Hr#!1r~zqP1=Cc#tCLc zK0bQw>T7cLC`V=rYabAIh^Z~$boSY;x_UhqX+dc>tc2l=V}sesPlue($|cQM~maUEmogTwQ5$x%!Dz-Aso_HUgm)y19w$z&YJofPG6gV)mLcFc{>VV?KD*dxrCB?&8!ZuJx6MaO0dZW1k7{ zQ{Z)_+%N&g=kGCMV7W@;9#hh1cMBG!sFO;d$peCup~|E8+3gCV+`?8E(ob__EzWV~{=oYdH*8=U-{CGc~PtoTn^u zUG;e*;|Ut$JrrOPeq{62?$oT8^=W?9KJt^#;hw*~nxfo&g&$e237TWs5IXZYA3!ws z(e1Nu+bP?9<`!2dJ*MPNM|PbRkC*H8=>|-v7~_$0d+QtjGySVmKrLlb(El{f!@Z*R3jT zHQ}<-!x`gw8A`~$N5jua*eWv1U2|kOA<4iBJBasay;znkGALkVqhSqHw_?hpxZ{kV zIrtwDqjx&iC-icC(!_>oX0)gsj(Ai9%jf!Pu(8>yAPXAT1jvlsnIAno*H=`zEl4Gf zaMrfwY*-4IP!2G|xDav6z-Sc}M~eIRiJ2?|+6%Z?cqvt(4jtrBO9PB&-Jq*%mxS%b zC`Jp!q#TyhiHv)6EUb}Cj5wg1JfvRXp5`8Ul3$8&lSW1y@P;R$<^ASr$Qm)zQZ)wcEwm922o8?$t1cw! zi_;kI^3_ljSs|8Xb_Pr+_h%%Z4B&hYx{_kR3 z_Mbm_pCgu%(_~up?nh;ka^!GzDo1!dMm4J0B$A{zXWS}WiRDowmw^q!t5%|#gE>{EcuM*|go{{eJ zZX23NvMRJ>6bGXC4Ljrt7=w2q?F+9UW$EgrGtBTh0GMIO0qmTPy!?Cl`00#y}86FuUUfhz+`Cjdq;q2r^Vq@ilj1!OFT`2*PnN|2_ToqOs6OZrt>L*e+ z!c+nXW*f^ZKH((qS8RsES9U(Y8OOMtXzKbi4%u0^Th>6F46Ig@gahY-bEM$RUEzk~ z*N!$MNUx3zW53^#@b~Ms?{={QW|CmP{{Sk{ED!oY>U6_M-S@B3OD-pK?`=D4d)_Zs zlC6t})nq=`0)*mG+y`*;)M-gTTZXOa8Yo6F9BYz12fz33sw4o#ZxXa6foE~d_0QKF zygoW_S9IE|(T*#7Lp;$gJ#HUsC;7aOehz}}ZI9~#UkPbPJ)qn9+9{INX=Zo(f3&u;O$+^^aV_ zB-|yTW_jtzk;d`%V}S-p@+V8H$o3tSMhQHzjw6%E44(e}UK*!`q;*2&<{-3y4iO0O z@;%ydw~0W9$unqo`H+`v5l073Q89*=I*ZTp)_W)AMew|H+5bY$1ogpx^FIO`VA4tc20 z=uSRbs%2IRNve|ebqSEOb(Zg-K>?N|3Q*@D3}->okZu+u^`(jxmjDz$b3C|+?&puL ztG53D#sl^fMu-MmjNqMLwbQO6Gu@Rb)=UYTu5dai?H6hjV6P8DDz5)xY2DXA(;myQz0icS&{y620(%Tcx1M6_d(mLVnttQ5X7J^G8p(kL8JtkCpn zRe_a9MbA<3^3w3eZDyt;tUF|ptJ_MT$}8TD;#P7_a)ba0>&y)XD_wo8MN&jXR&FeE zV}f;CJIftgm8NG(?lTh{7*B`0!M+E(UYgaTb*7Fm5vA=o0mn1Q=i`Hoa{gm}2{?8u#N0=k=)Jnb$xhbaIS5fnRzHg#{TBO#a4XE@5RJlEbteb=Yt`L1 zD&bf~3@9@^!|<BSQbiQj0kRzmYe2$3ZUu`i#H`kiuBnEHk_EMm&j*?>WI zD1(4O;Bl3Fbol=I>$|4r{*Z0~?${7lUa<`1d1}kABiM&zj?8aDtf4(-IJigz{w#GF z*EfjiQMk|x5X#Z=EXkjd!TRg0)pQ)lsZ4|u$eh}4R-+n4{OVgi)i0!1n21)jF`sCTV0h|_HKc^kTh=>hfn^e@$ zc-K*&dUTxHGLoZ_8eQxyNV~jGZ7fY0GKmPnk1P(Iz-cinBdicHTP1)C!mn-3@RTI2%R;acW<1jI3 zqm8+jY~ZoaMfYfmlv>mhS-1WlZbr<-8zJ$GDDlZ7pMlU2t4{*cnz~IYjtJH;+(3K0 zPK@2Aw_DNq_PcSIWrotos>2*gLFw`Q#(^TN@CV*YGmrq2n8mn6%{0-)fruf;D3`kf z`HzoXO=!glNMZnRo+Vs$>Cfq)@7HToD_@HXBuWMpXF6Y7cic?u3I}#x5HW&1+OLX; z{$b4288Mn79Th2swMnHS2A8wqVcguo&xaO$+0>F5*)P+c6Eqdv@i91Pk8=aud`Cm? z=dV1slWH${l|W_TLOJyZM(^+#Ev?6iid%^#WK+z01BmnU8b(zD$E&#vp>+lC09hJg z!u^xa7|I2VuWWg-otd~s-W!J zNTZmrQ700d^zJ$M;QUU93?&t4gVt9}D_5;XI^0qT4jh~oW84Ryp0#@&cb+SdZW7*_ zSl58lX5mLw@ICrVQ4+_a*Hg2GW`=NZ#0Pe6ocLs>(@_V~{7FJ=y%mh}mX}v^|uwpexH9T#ufW0c@UQwAq10fOZqfN-efX z#EsfjE$t5V8Rz-rd%W~Fq`O4he&rfQs7#~;bBr)O4?PzxNiS}Foi}Nun*ag#SEoOA z2VXru^q17**{f~$tfaT%Igf+89mh`i!v+;VNP9!+$Q63}xt`qKdJ{=bdoet5s-`07 zPBKT&^3tGl+7TF%#DFL)c|tq6_a0gD)ssYIWh&!~9FBO1Cz<3s`?>gOjC*{vEWriH z(m2{a(DAs*9$;!DGq=K25=d4sa3&7(!&U4f5WEVx?G7+`d7gjrbvsl{P>jJPVBKok zQmgPMq0n(hd2GpNYL<0@80EphC&(VU(NgrHE6_gGxYy#8j0vqj5YL_f#+Yg0_WViZ z%IJOD9=M2Na63AgmN@J{i-BduG*UnoDE8BQrtE+eX17`;7&#X@pY#D)~cw~U);64VeLnvHr+7`BuWGMGNosEF^F+DzNxiI} z%?jBlZ+|rNIC~paEk3HuqQSD+NLnO+-{{R-KEI;!R)a!&yT_&2F z*N!#-#>tKhXS?5n@b_uOze%CnD=ybYuoHoXmaFi9e6kL8oU)sQlclZBYYko4dt~Px z@H9@5vTxtCII_E+cWDME?D(SzVlF|{@|b)4PMPg*E$azZJ829~teHd?ihSRZj4~MmBkG z?ox?(F$p3`1~8y?<~4g$hjNwh-JM|G$^*=d@*hn6bpHU+xhPGvSla5@O7~eyw4ND4?n`)dHIfb)s+H;+QH^a*`HQIt<%-O_avKaM|wWQc;R8gk*RFk zk6?HT3owv7Sf*Tw2Tq<@7&^CkvtHC)lvWUy<;}SbQBbDeJWR8jD;eU8BbG-$mU;X% zjtI%+J4@zKWDUgBZ?y_XRvIGgQ?l?iXyh#vO5e@gGpwz?RqWAt5ECc0A}}QwcXh$| z9X6u7zwn*T-HTiXM;vju4coJHUlH@wH#jT$JxV6*BQj1&M8;xu=YmdtT5O(Se0r&m z2q+4)7HJ}pDoMhUn*7~lOoqoHkPe_-;T@bs5_cR@fe<1n&xdgT0DXGFj7u^_o*vVQ ziEgK$`seAZYP!Y&`&2Cpb3zx`UgAekbiA>fO4&W+B!Kmrv1?z1r!UUDR@ClxN ze2zaoLcLQjVFTFgE3+yYTf05O;i7k~u5U-KhlLW&K3U`m#=4q`>DTYI?H zmW`znVGx4Og+_6O13qMEyD{@$!{j>4W5waF#{U4shp!lwnUK0ffjCDj7Cg_$YW>n` z4YomJWr|wRtfMIHoVSj0PBmW16n0y1h0iAB3GVPZ8*ZmOk;z^;T02$T<}=ZUyT3{c z74LTmLe$?mqTBW(xZf8{#wII<5u7{?j!r+Be|~zM&F6E2x7xRjzP*`a_G6w!I6T15 z$mc*&zqrFZZ5$>WwZ=w1q+rn)K;esY4DeVep;4`iW=cem2$?OxzxDp{g8R|4$_$=*@C3}?}ASXDH zOGZzMxaIrls-kgf4^plmiYX7Zi8zV!^7(5nmHze&-nC-haq)R6SE54^$n~C2CnLYzTdO^1C z5#5QbT(Koo`_tNFUVqZPbw^AKXQW;+U%W1F-{sx*GcJlj8BnuICr zDtfCx@vcg+=1PEb<(*hA2qBWi31vvp_+7-DCA`Ktoe@ur*s&~unIn(yu*Vihcb7dT z>}%Jpro{xcZBUqY3-Ya(`zq?#v0ah9l2aoD4upfFDb2QSTLjVAx#}_-21f60OM*c2 z#*xvQ8(W8Ky4PlbQ-iS%!-$TDmoceq_9ENG=*?)DSOrv&3#y*rPMKH(w+8)uCRfLi zi2&?P!?vxU?%OH5M}_R6F|%M%z@LUb$;FaKWyBr zJOoc1E_*!wBhY*~XWTV18JGfS2&5a*+|3v84X!#cQQR#UugbiApgbTS1FWX`uwUEf zX&P3TmBL6)LkwfDyG7lPaJ?d2m+j=4vPn4MUoMy$j^NL>!m*hg)sw2^kf+BVsMYcy zU~Om!>+IPSYGZ!7N|G(pX{%V1nzj@er5w~A^*#qu-fD5xUBOqevO4hd4Wa4L-1iXx3#v@<}AIjP{Xp-j3en~5u^3L3vOlOBZ!_QQ2QpIA= zZFN;7W0T3V@#U!t(ix89^r{@S;0u^e%BOhmPtmt2AK<~}->;bJy~!4VPGUPR{r{-@!pX-|KrZ(SzJPkI5k zINU8mw$SGo{{Yn9dS(9r#5XAVJwDN3$d{I0A8R1+U}N>r8$-bbku616k$I23ocU<2 z>RX%LxXrG@DN~ZjMi02^d+98vbGO2#Jh+=7zGrqyfWZ5Ni`!WIDtQGO@sD)f;;|ZF zSAkikl=4yFqr~Z~cVn@)9ih?)uYJ(ba(6$jk=)z&O|epqt0fSg(83rTdAD&tmaC9y zP^-sZpT<`suu+U`XSPj>{^2}r9E}qw!EkZHJaesdTL#kAXWQx9Gj0l;LPByg;5D4v zWPQRmg(Y!TKHx|Uec!3qYC(Ra(prl!JSQl?I1|SB)j$r$C+S{>dD^L=${pCjQ^ZkuD z-Zqi5?4r%qQF1~MM5JXC_6D!n_KQ1cwYvJ&3ZH4z;ko$u>pfyScPxqbDV}C0D80mZ z`D&UDG_t#xDk5XZ zqylyZbai0sHd`c`Lc}g z;CGJ=4lZ%mj?L$HiQK9%erP;-kIPDYroDRJYG6ZGNOs2s_^&eTO3%PMMoBt4@&!+Z zbs4s8&uXOYX?8#y@zyl|r%bugS>`AuXHzJs`g!U3f%gS-iMn8mk zYev>6dO5d;a!XgZDp8z|Dp0U?0?sCr|XuE5+04~k#RU9W~vY}2K_#bEJbzb92WLWF8tu@Oo zGNYg#?(Gx4#~pXu;X}!JWOw=NG1M1*s;01oZN5^g5_;uUp&uzA9j`9_f7t2IqTAIY zTHG6?Pb{T6byye4ofHCnR~K)bcRi~%ns`5mR}C>;x%-1;(F#;WK=*v=bY!^ zrNa3XS8non+0;~Ry6zK4Byv}>6_n(};0*hZf6qizw;h%iV3p|H@x~RuzLr5<5kgDD zEM3beEAl5Aakn?9uzD7zt^Evm(Cb2nX4DSHq@!zVpa?w-DLo|IXt{#rDNlDDZ@S+C z2>NM-Z%D*riVdQEGuQ0VGx8bLiN4x(#hO*EkLFW@tmgG@(Iu%tdbsaGqD3C#*G^dD z-pTq*BiYu^h=hEjoyXEG>PSlw9^VM%#J=2z$Y)u*yOdUIyePLF-|tB2#Le+1mZ7xE zGtb)3a$?;KZxK1uOQ!3m1xOMlJ!X>w8d6^>7P=p-+m8Y&+6w!azAf$PKz7eh|+Uafk*%4_n#jv7AvK$tW=Ixtb_3Yk}DOaYIVNW zNf)qR93ErkopWp4EG4Km&C1JHjsizD&tLe|>06qt{7ONzK*JNvA0PMf*Ct78S@oNU zvPmc6vF!}|#&ND1wFXoCO0uzF%4iR?X}s+RbKGDXUBY<1lw<7s&a?ZiPvM@qS|Uk} z)e95P9Qpk|y7#Qu+xHl^NJ9?I_+2yibmx5BcCFa@^(8UcafwzzgpmHg`fHpnLd0x+ zaU$2^{W*idyG2?UVwWgFjO2HC=vgkV>nl@NUd)0^H!htuFIAgxNL^twEnssP97GR1 zbH~qJ+V-&|ZzQZ1y;U+vI8ZnThnde$4C{;JVi%;)*^O>?5Z^?VYwmSkdjd47rQ~9J zx$^q_^aYteih50Q%|Kl+cu3WoO)0lQBi14$NdwAdUtlL(u|3{eZ@=yl?Y2T}F`#*2 zJ-S6)2-suq4-Fa`0Xzwfm<)k{zuIS`w>pSDY|+`8_68R z@S`UX`DtaXx>zdLo@;SDlBk&(4oc)?XZPWwcME%l^#o0tb;O0u_ynD3~CfQSRty^)Aw2!QVI0MT|>)5SEVGK2$ zpFHI}{#e&++O2kD4E3LgpJqYJ=bcYd>EvJAyX%cJ+brMM;*eIpvn+~n032BJJ{k_5-8IOpNfo$KWd>Jw zILv?ypK zaAcK%$XJT~;PKlVL#0ycP@rJT&#sM=cZR}A3`D;WIKbzObT!b4ZKfs%5d z=N(k|5PJCh^~I|;-m*?%Fs+2)2v;0l?DN6+d-N6ZDbhHa*Ey|@&`qwt@dc{{r2A5o z2R!l1{{VKhJ66iuUh62_t`M>NEW@5hanSo6?Wb&zUB0E}i~OkK8B{)cQ!TmoP0n4? zP~0WBQFHga2Dt(uQg=VM#%CAe)pUgK7bSbL?Ym{OJaz~il#NO_C!TesaQ1H)>>J#? z{{8jy)erLZ@y=r?ij?urxV+FI4eg6RMo?7F2i?{vDDllTqY#+Ri z#kWzvzDrhBM5PHKoSYNqomgwp8-?06os>e}z;?>G{STgt^iIy(9Sz35;IAFSk~w|v zaq-eB2-YlLau{(V1tfFMIewZTu9dD${{YmMB2Gj2hkDaT*drW;V!Zpq5BS$5-z{5= zZQG4>Skf*S3?N8s>v>UWN)5wN91(UDC=)qaxNu5Ez96iX<*#=s_g!(szTybhhi%_?$l+FwM7s}!_QqfQCdI}xuT zEMyapF{N9%m)1L2Y+5oNK^8m#s||S2H$P}{VMbeVe`7Wi`b%)H+HH125B{4geqfy} zZTETHCJ|PnBxj+P#zO#Czk%RRWrdv$Ku{+Doz@$dTUMJD}xFANaWroLRxm@&z| z=4ko#5j&d@bFZR&8P5Bw*mU~6nj(ardJ?BR51ue0X{PS9G z25tl&mX%n6WgG|sNBoeXApC}rgND>j!=&80)~Gc4cbV6It3{x=348XVV-KoR5Rx%23iKmee2Avdl zawjMA(#sRkGr5kW)}+ojiR4eeOrdf4fwL%}Czu;Crr>DUtf9aWOaYE%@%;{hs$-iQ zcH|Vg^M+p$;tq(80FNyR(PNx)mb3C!{-x5Z+hv&l0PxQ8A|4?Vee^zFKMhkLCY?q1 zg_}?gqQua(S)qwp0hfcjzZwbaK|E&7Bb6uHh;>Hs8eI>h6U7?CG7#y=lh@svZJotbQO5C->{TTx44QkesMS(3s(fUhS=icx38Th~Yjy@5@G7j;(Mch*%MyyCiSK zKDq)O)+saF#v+rymQ?7dnlQ!Tnd0K>z;dVIu0V$LQT)KAYiTHD?T#R|8<2kNicp`A zJ$lUZ*Y<3zu>;Y#vZvg7eRUwy2Ff*K{Y@V~rZo#mV&Dm4ZqWxsjc`6+@24fe^4^gR=NYl8s`(&fV7YU7cRos9Oc@O!} zlWhfw0#PIR90931IP+HWZH>7c#VvkIHq;bia7oH+{Z=d$O6TFddt*$;s*T&?cPq&LiAo zUVN0kPs>d1HN#3O*Y7oqFm_UTAn^B{Oe*QEG;6%XxN;7^IbB`QZSdGxHt}RKU^@(| zzAy>+jXHwX&AIJ0E6D|>lAqy-D*IjM2N}{hE?%=c0ZHYaFbsvk%JhYYU>~<0O=vjrPj48u$v%UicC&QuA<7x(5 z5?-|^k+~rQiI|S0`1oVXN5l)qUtMCHASM;Y<&{o_!O#SD)c zeVkx`**VD^hZk`^Up*;vK!Yvd+R-~gF6~qvN0P0EiZ0Ny$nE$>Z~)Z17(`#;WtMj6 zL?Z(}BwpgWiLPt<$;@4%4?NS!Q#=c#z4y)X&(@RoXqj!QiCLvZYzz%VpMwNQf z*%REo#*xYa?Rq{s<4IfBUzn&Y*BkuAW+8~g4Y?(R_X&A}?m5&d?No#ImMpZfmkeDS zqW6M4HSNx7_V3AJj{{PWg%B{x#6SQXc$^dQIw2iLzr&oxjnm(paQDanWL*bi-c(TVOWmfF@vIRJ<2WWmb*E2#GNHng}Aa$RpRah zl1`H=$0Vy85hQ#>3>*$Fgs*TvO<4A)O0v>|QuT{}l+p0;mG3?LuzzP<9PUYy@M&lr zrj{+*Dw5fu2HulAc5_ZR&l;{rGoOA=ms+W}%jpYPxxBlm0zoMpTy2s#6@0mAGTMP- zuJr86XGZq&I7*Yyj}CmkI%5rumIQ?Qjhhb=bK-6m5*~a&_;Sz3P2vXUr>>GC2t%n% zV)k}Tvu(Po1+e!)QY8v_k8$A{#*$0Adh`_{UA$Kj@Q0NK6`UMr>!SS}onzgik*W}u z6rWkN-Kj+D$N&H*7|?dD-Kh%gkJ+_Wk=i~HmKkj1{PY=#7B(KgF_8-7ECH{KhRL+< z`;9N~5*VOTdq)QcKk=qd=*1`6B)Ge-Zbi&-H&ftq^g0rYTUztMM(b8C++YLAyjS0) zhMkK9i8m<8&o$z~`DhhgAZfE*?ZdBA1-1obhr>e6 zx~gDSkF=Axk%e7N^Z+%`{KaCRZ~Vqf1Qk(iTs-skQ1vW)PK{cVO%Z8+&yT8(;peQ> z7Zw>!t1@=hPeOVxyH-oS+MOB*5SDY*Moy27j<@|G8Bh)3cHTA4-M4U{>omRt#Qy+o z9YR`F>YmLo6Zlw8mohPHUPW-Ji1wPS*542C2?$bttwwf)zx-7POM*J`6tz`e~N`03GQ&DEBpJF0MExQ-rVwcNcNSm2wm39Q}{a<2?TWXUK6q zUfrjxQLkY`NRi5?p?<#x8dX1|@MkGBupjDPA@dsam*uB0iV-s-askLZ`@sZfKOGR( z?I_|9&j*-ZB|ih4eDuwe3;WAGB5}VWFB=Zv4q}oC*T6nMJrn#A%0{oi@wptroZ$R5 zdTvfg?JCDFi@^9J;D5e~i*>9pJ!(h~^3It0=q@j3JQ{&h4|$uK#I@vT#jj~e1QqCh zTJm4I#{%M@@=>i!satSRWQw(+{L0+^V@qfBexvh^Y{Y%xhLrm@QMwx+IIV{#Dr`to zFvvl-xKA+tQrH@EJbQd_fXpLYbSIv(a%@te`#YE)){MX3P;H{ypKjbO?<{{!FgX-g zhrBAzqO`)@_STTC8V1zA%)=mlnw4qqF5UPpNXTUw z;l@WX;CcLpufMp>Pj9JFqY_B)<;l{*yMsnsH>%@{eBag@$f9my=rpiIIBY%5>+q*abv&Tpx#vf01?E? z6T)!70EC}`=c_0fpb_7%6sa1z@J8qF5ziyzu2c&=QAuh_5hR&!wL)0yw{CrYAuPo2 z#YuM37{oG9Fi^t|r#SM*U#_N;EO0X=9^AyG+CTx}%X2?bsU>dGF@aqf+>DpwPGgro z?qgL(!e-!XM>``iaClN|9`AVP@X_-Qu(J`t-dJX{Gr;9zMwN3IQ=ZIo2R`f*sv(Ug zkJ)b7PLV_sKiDaQS*MzR)-02pq3vV3}d zbUN8FLw~!Tvy(#v%(V^1?RYr581ShH$9eC@he@YF9b;5mX-FI_I=%?wBL{1oGvCp9Rb=Q%Kw*#`9 zb!KGXDgX+&AoJypMEvwVB4{nqdFeRQi6B>!hhk++tb_r@h99Wdm4$E57t|qGAK6%w-%X;ML6}E~l%t!$l$T;r!>9@a$_0};a@4V1g{u?Yf zMp+jfGtWtG`l6E6ZOsAjN9CbL%VAHmX+ZKuAEu`sz|4Nq)P#RbNA=ZDio&!;W_A^( z1ov&Bz`R_4P}g9ZOERox%a&pQ0e`lg!LU<|W#FDu{caR|hK@-p(L=y$RUPCJlcgTb zlb32AIID&EV?rz&6zv-@UMI&cGk|n6Y!?a4_N&Cc0F(M@w40@N`?A=v#)D);lH{Tpo|r9NFfaj{I;gnnO+qBP^>NN#EJ>9~SC# z0GjDuKUbb&Iq^uiUzZ{I=xDD`a$_9pCuY2eSH@3(;l_&O#gE-ugf=wbL!0=8`wK&B zfumPAGH~#Zf1aAyv$uL7Ow_32eb!O-lJzT~whH%BIiuRZY1=)mr$1)^?*WffjVzYV z-%8OHBsVSR+qmn*0q{ld_vu?99&^e2e5aZ189Z0+2T0Rzqp|O`j7H)`c@7cC#~8;f zgWsbTrtz~?Y8Ief!)!deC*}bDmhS!IS?RXeEDqY6g55!k;E#rh+x;Ql_LJDVi*?5% z9D|-dIw8O|!|Ff8e7GG=)sI|AU4pxo^)@b_{{YKi;#B_tN&G;3_39g|#=V2HIxjAK zq)&c&>73iX!@NcV!nLe-$1f#5Tci>!Bv6xxvq|GRNXEAU_e$P>*r78Q~?+ zj|mF>4wbU+=Mtwdzc8r9cEK`(U$B$E4x9(|*P7GNJT_{&dGWUo%oF)&blh&Z_9U=> z&=BguO6}GR^=Fuq{{U7+U(JBm7GM29+zp!=i&I7(+qx_rsECB=+HsP(_;dPev@Th( zaHFb-fCLpe3=V*~R6-=DayE&gC`b}301EMb9$g2YT^q72D;$O|xC-!RuL(K)@;(~2 zJdVx*GT5@5gm1J-y@L0vC-X)J{-dotu*DwOsz~8L0r%@S=^7N8tewM&K284mELt)_ zN}xQZlm_0K$#o?_%*F(a7qYAlKE{bo`jNcG)e*xzw(+a8-GhyeZ$wJ*NKvyE>*#qTQ{wLLt~F{293wAqDo=;+ znE3Z&Lab29U^6hn1U$~mk+Ro@?vePbmO!xb>0}Xw;Nadq3 zB4m;#Ao!`qjNIZgQ<+k_yn#73dVKYore-iZki}tTbMEMWJskc=s+G(DI%+zC86k!{ zG0rjSr~o%>2MuyRTBsV=>iYAwR;Y;It1KD zwB|AFPQT6X>z^^IZX}Y(WIOc8rR^2ILT_!kqR00BsgSpe#V( z3J@P0=RZOR&q0TnhLU9pYEcU!O$~ZrzD%SXhn&_I}U2l6m9v)s`1-{vs(z9ILJaag*(6s)8)SF*SKj zLQc_Yj^MjWGKI9+NgJGR?jU_Ne%-R%{xfc|oIQADSZptO4h|dLod>n8t+REj1*;^| zM>AlY@P+RmJvG|*7n9Q48;n8LwSk9?#tuC6$fCeIZ^zPRe;;)15^ofst6omYI3t2oT>GIZA*5x!dE?1Wx z&jQ8=cQO9@b_t2eVYj0;V9KqGIqUfmb@yrP7#Nl{*`oybXO@lF;VUbEF(PvwW4~3$ z3nj}l5DW3-adhH8^YqdCbc`;=Gsv>BJS^$a!RUTt>8UDP`MHG`&1aa-v{6a7xC8}! zkB+kYN|^T>5&r-zY(BbT*1I3#6*GlNGK0we#(?yF43@1)-ND20Bxtb==m#;KgED~~ z?iQ_Aw2K})CIphi;cz^)72VX-#%ZKsCqW)F=egUkbd-5Q2hgbnx*gEjC)ec;f9q*>zzb0jq(F>E1ww_ z+n7#RzXUXAn1PNd7=V5Q16IQ`!TxlRKy&_FDhnT%PmYwe%Tpc{SCpRO?I+Wwu8xl9 zD9f*7AN8<|57$LmUpp zO6@eQ>ZRIos_oYY*v?^d_+;ijdZo6zkb)RA(D!z12p)Jp?WQwq(!@^-!u75ieEBrrdAhezB!W;qT zjN?X&GqU>u4<7L0U5xAE!}S^-U*YagZAXQ>%x(H;07tsj>+3s717qv&EvaK>oC9Sk zME?NkVftyF>9=jw_MvX0BbQz!Wd{If9sd9wXD`#K9U*F|ejY9o8@o&@*%Rz9SQEz` zQ}5)*RmYIoRs)GYFnulY=sQaH0@rWZMJ!CxtbZj@`2(wIgF;IcPvB+8yND700Nivo z*1`7(-^Ok(f9TGL-PlY$VNdyyG8}lX{OBn1oV{)~6$^|oj zB!cK;Z!*0{+__lvvjAoHjVts~-j3^Toa6qdZka!Qd4u#POryghx|2NbXeUeKt_{Sg zBsPJucQQpBN$mKO`#(KZDSCEeXk3)$py!!a9JB5?`Dm*&-)yyJoru|>MksO2n90g8 z@#XsHON|ua%}`n>V_Y#9&k4!=#F28MGsD_R53t1XWB_tMJRc*jLPE!6z}=)!u6WNb zA&@^dA1z8Q(%ERElZMLsQ72w8-Uo^6%fH|?8=|A<&Q6*J^FzO^U|$xX<}< z#uwTyd&%$mYU>-MYv|$IEXO0t(}^W~=Zq8We0AE}q%E-7+iS%tGude&U~$>5eVqB~ zeobFbyvupL+W!Ek1XEVrw(n8mTJpr8DQ;QDi;6>AYmGf&49BX-bMzeZtHs@#&2Bx< zZ4&MY?6k0uipTnfarD(j-KBzQs8FF!D8LLr%i#DE@y<0xYTTXcq|}+)s_(=Cqe(eN zm=|E#8Bhw3e_xr^)*H5ka)m~7fI5zN$4>1+PSURe%Pwrnc%5;AihO(t@bK%`P?dvM zs`DNQWCU@6lB0>U^1%7(ced~b3_-ZZZDBM*NL`#`3R?h=u9EseVqv((yeO=I5XsMo z>A;FDx+hjp$t-_kVSem`yC=DaPlko7Mx+nhZWhuy!^AMimOW0mC)ue1c~LJ_|)c?vT5vdCOEJ^)W`mH z!dazOZlb#9-;@3H2nF?ZzqIs-7ky%l)ynQuu;}PB`}k>xqY&bo%11dT3-AL!u9sSk z>{y?HOE6FX11DN)kgXbZqJR1^86V%&>ORi8vli$sCKbGepcnIXqN=}+S5xld{{Z#X zG=oswD#pcuiqfFRfzST!Pib(3S#$neKUO@yT~)g<8;5EC0QVytf21GNN=KAs>v1yx z*vlScou-!CV{MkzwM8+8a>hO^fY|Z#8dqxm(Fi`Mm0kY;{{RE|>#b;4*j6G>6t$Zl z7Y9rB>a|{z+O)39tP}-3baG@Sm1~HW<+Q2jJ9d_~<$c!+JXMhN_Cn{}I{h>@8pC^Y z*|OdUr-$p0%SRUlw9AQ) zxUjB2_N)Q78&VF+jd2Qif%@v0=uJF?%`lP1Netit`JbkV++eJnQ9(P*!|yq4Ao+DV z8kBO#ro9x%ClX5EclYJ_>SPkyTX=@`Q*ShuE9DAIqDYT<8Pch?;~Jj9vl;vha=Ptp z;%cNvaSsnpA+OrDwZoFyyl5+#x*~r(p zm9Sx+daa&^ET`f$&S)3@q-B4pdT4f}2>Uih2gi}nI5H1(*ns0qf{j?71dcL2jU`sm7}HI5&{JY)}+N9(01#=Ny9=+Mg1 zpKv+<0P^~4Ir5cm-oJS^phJ4~yq?zPJAgMLjK4#J^cp%m@Y#kz#9^2mc^+S=(3hg@ zH)(J^i4QL@!z1gaal&VFGUqm7D%d=OkM|m;Aa_VGgGGBnHfb?dzE3z^p>Nal((gz( z65DVvFB=9Uyl0-T+MJiM9~OH=S(JDY$o`sN=^-n*$0+-=M5O&jsg1(bzkW|UK-BqHG2)-?$5f{?B%fuNMvmLLp=sgJUVC%w%pL}lE{*+Yq0ak9>l5r zLq>W>dhX(46p9vzi3%JRf&5j!l=y$PsLmNf0l53jFNZ3R4Na-z;Q)hd*(+UoyQO>3 z%MrsoP71f5OpkWExLJnN`lVRiHs~g_$2nG&rIA9jxyT9+Fg&^sj;@M3vPh2wbVxWE zN=$MK72zyAJF)K2tKdf{f3#i5$Hf$ssc<~e7Vzg=qeUP)wXv$01eGhR&O z=cxzo>X{-DGQlK)ABbRc;Vef^T|T2#a75FnlriBXvZM7R0n{A#4H9c&B@bzNS?0Ga z^F<;;yct<>ob%_)$Q^WvqqhyJ(L2D-_*lTsK=blHp1A^9O43&0cjJ?ckO@470Lk(v z=dVt-o}jD1(c(_f5)MJ{<~+Y4p=X$$ett53tynThDp(Ro{{U!gNx}ih!2o^jc3jOe z_AoNYS>2|8xEIWM5%tiFku2@mh6I6ycFqXz$vM@r#Ii`xEP11tdVhcav{gF;C!+%^3bZ}RG3r!!mEg78&Oo4!6Z!E zZIZ;+Cx`{{Q`8VU`3*+i-?2|=EA4gfPb|j>WN;C4H z{*%u`ES2FE>(r1tsN)i+j(Hy=qCl!OUw@=m!VdG$_xI~F==JNgCz%%?k7#A&^D*pf|?i#S(H=51D z*%CO0KlHDDgxvP&C7xRn$XxJ+jZgCt%;*}^4$rZTzRndY4~aeh05hcw!zH%Eq~E7Q zji>>%J{tbFQQeLarYy%j03Y+JjGRd{*x^|lE99VHYKUIgjp@icI5^plivoWwb9a}r ztg!+1l8gc1x=FkX)XpC(X@&lUi@nbz)^T*8TU1+^$b%75xQUibG89JU&XeWPP=1VX%7reW4 z0A7Nr`;b65hlm`=`fA9~rqfzHdr~oceqUV$VnbrZ>Blo82-o6&ux&N66()`^!wg^_ zF8=`2QyZ3VUbclmB$-@DixRPmwU$RJiq1gf2hW~$?A$}c+SI9@N4RH1#Q-1W#(&iMu)ySbjR@l#9%86=?l298$_|J*E}KJK@C864h3 zEkec0H95^Pyo0+xrj=OTtlVQAR3T3w$Ci$~Saw~^H;*2}nM>GtffA&4Idf(9A0B<$ zUoCi9vmBk1{{Sqbg#7wzNS3Nf1Y4CvRvjea_D{#V->6!nR3{p=s~H*M2ed=`_v<0T zTv#_CaTXR0%<@{@X`H0;@Qz&15dPXXYusg?Zq7iwSjZq_IOpBVrgh78ma2)NAc8o- zOn|^1V2|Hj2ThGV<`*27v;!x={{XSnk>3-5PSQ%ui~%!8eL)K*G8ulOLMOD4&It`# zTpu+jApUx3W-nq$@%K*>`?Ri`tTu{BeTViR$Q*E=pD&)b=NfVb=w`fvm=hy2n)b-< zicF7oaz2Ma?;t~bsUKr050TxGgZg~5$9;&!GPVihjC{v0)M!hJE^ScF)#5x2?(Wts ztGg#@42yb}5xLZMuUX_i&>f4g>Cc)syQZw!t_b7_KeDcI&+s_qjWgLIP2P#WC^~W>kvH zLo<|cZ~^ht>x&#V3#)d0spfmL206k5yD zjUza<82dIqc4Y1u@zNdH9t&)Z9|InIx*svCcCF6+WGNJVybIM>a$j-o)Kf5Ao9)s? zas_?iSFcAUnB=h(m8~JfgNKt2@$niq9lLLBvR&%EiPjqO1a;!}j&Y26WQ|ug?$%=o zHrd2GiRX`T)Hd!Inbj63kb@<{Txe)gGYJH7ag-0w*f1s6pS3UN}}t*Tfm)1G?uWwmp(3lzclS0wY`QQf0`O2yc0+#`xm1-E zB-wx-ZG>62!feYb3+q|2#=L8mU@!n4cmr2%Rqa)hDJHo_VLBcN-dvHB-`oi4&rJ6^ z3ANFdJ;FufiI~xuSCf_)V8_RnqfVB|El8HW;TVC*4;`dDo1x)vhMPzB-5;Dq&#Q1~ zN^CuR}7;)51tA7X}zjD zBo(Gh8-!l19zM>Z5?Q;rn_SwdN4Zc~cKHz4k&$l9%aui3@bI4=ni@a0?EK|UeLLNV zkR#Wdv8Q@l0oRy46F-^%0G&tyBoYAa#2X?vUIM&_7Bkd%jW&J0#W)3)JQizS-~_)D z$T94NUo)WhS)sQ@Ww}mz*F>cypO1(Rc^pgOr7A!-9diOayNI}kMQaf=izgf;HWUu< zd$df&5+G{EC9}#&Tnqwva=_>Z_wvc&q?M~nKJ6qh%4P_O!|cesJe%F;pp@-X&PNo8 z@iq?$%H$ml2Dy|_xhScglW(H?XYtDxCTOIWfG-!c4?LfCbOk%G#ckw*IV{dk3@R7y zuW->Dmkit}!YN}@#JmKdKJYR%C^u79GLY?@Jh_qI@z)ZeyHqMPx0wB*7T(%ZkW0?w zigS;<3U~AI(n=2mamgH^iaO(w!Rx7RyQTXPgm}9M81zy1z30C}ZD`ZlNY*|x;oLmW zmXY&@eYfcYp$W$5J`cdd*|C)`iuU1%ySb66a0+IC0b zoh9_9Xx>$dC*pE_pfzl?G1=+I&RZgZj_17?h?uNMbD3C1J>5==+<#*989nQRd$*E!`FF%i2PCBaT3UhmaV+@yi`^u8aYC&MTu35RK~U zGZ^9ak~5aa<9^}$@fxdXsUoe37o320C{Io!&Cj@gLsP3$f(N)$?4UiIIX~u1e?AU_nYIPMXxJNt(#L9JvJXd;$^m_6K(<_&LnO!-ZM~^RRqydS7W=H0Rv6E-AF!v#`H$)F(;Cqp&11(mIX>-e z!ZljspcPmGVY2@KNvdzM}vvXr}dz-y@_<48^ zU#5zuUOPNfNr0diEWoxGI0G5h2WQKWfOeS}v$+7k;jaQ5mNP$uV<3Cj^3w&eEMCMy zI{dkC`*M8$0I<_K^(q7sziQEe_vLN{`fIXXg%riM)<8StD}^85RTv5jCZ$F#BXd#N z+x8vV8cGxTyB=q;!!O1&)BgZ2v)1a_*{!T{Ix;ee6{07CRL&10-~Gmpy;3%dw}Y`Z z41Kgxz(@`O{{U?>+`?|&jl+I5%R%M$H09KKy@X-tp7vttB@S(zCY8aO$VCJ}cew{@PbnMAAG&0o$h%j_x(? z`F>iCa2$d<&AC8g2AD)zMu?X2`#$!Iq{{W=->RT0qR(Gc%_H&ib zhMHP|PZTaQ6o7&KwXJ`zhe_sN{Y-xtlGWbU8t}~DX%he+#ZN5$`^KTP5GJv=%gJOO z&*`cjs99Q-gTlyj9EZJokGuEduI?f0+>Uko=s{u+aT)ycuQ-+ssk%(I*QoT3{TI9Z zC(;#;ZdL6L2M4BlY0c)Zvv1Q?zM$=wmx5gM0Q7%{`|CTqL@ZZJkbUFrf;))orn`;k zH}6MkiB*#HON4XxUmS|($6C;p1xL(&^EP}%T-OKH&P%fPn~QCd&B1mO8Yj*LgdH>I zp`vMN?i1}7kj6_M%%2S8XlN$0cWi|NH?L9!tH?UK4yb&`<*fuVRrJSfNaSG-?*Pni z(~lZlZD!M-$nEoskJ=v>qa**W9T5ehH9I^W8gp?z^ z1?T`-WQ}CU^<$^RYGr&yP`ro=>jO_nEcI;38ET?59Birzf^Z2LEP3~85m>8Gb~jI~ zNO^sZPCp@{Vt&@7wH?Yk4`sX$@AdH-VX=BWUJbDB(%pa*5Gui05Kscekriyd>%UEX&~ zo{>!{3yO8rh2_W}EkcInYK=)Gg^EigIV?JkWPNd+A*BLPs->^N$@Q+b%s_#?I+tcA8`@*eh}c&*E=zKakbCB^b}6wl}M?%OruZ z5*EsaCmF_*27#lVk&LvZ11CSkdHpl_>DkE&-=s`x934i_t)m~~Ru&sI>_2T^IhHA+ z1$c?>=a75(XkNtwq*5!#TZlNpKW2Pzdi^zAI}^&Bb4u+eA%o$sT^1tTDa9;>@HksN z>EEA*r{@CPO+FTpWK?JA6%EpYMZBiw_aT%FxID4kF|K+$OLg5IUEBDR(}oO{kdu!+ zb{+bqU4!^li2G9{gtkv4UjG1@{dMNwsm-uUv{#Hp!y>X^@^#NofzxVDS9^0`8IGS# zUf1hr3&MtZB9D$(9m_js{KMXGKi^mX02!OrpsiWE3dM*>pon0wBd+qMJueCILEuLbiQ&e1li$g76sHlcewX#)!bp3p|k9y;d&A| z2VD1&pUY3HJzq#*+$NPC31dzPCoA@E%aJ3;SmD^DRp^+=n1l<)at~cm9PZy!GvrZ>4JMDRplNPlG*R!>r?pf<(N!*`M_;|;-Kwis z?d(jIE3rtyX*g4hfOuoP>UOs~SE$0%F_Gegva#lTKAnCV?$5d0^mb?`3&mD&1f05@ z_vr;*{CA8L;!9CxQ!Sd7e!`*kc}F5=$odXilr?Qf8G9lrI7kEq9?&Nom~-#bt?OZ} zcWZ6pJF)hR*&KrWJoK=+f{ayF%g8yic%SzE+IC7~tL?%1OJc~mEcS_SGMS=;mT+Gr zcd*VE()q2<+ewtU64;au zUL4M*7sCfvCQuCk>gz8SJysRlw(=yLkF=M#c%LDlcFKLN*04#EIGYO1_}}|?>Dp0- z4Y!3zg;-`;SmfjNI;OOv2^AQNNW+G-T}l44stj9(bFk?PC=^`mC#<}-EK8W)tr=23flBXIb~LYM$RbuFd>H z^6^U{>l{b8d7TW6M&F;n{{S=3$?Y$Yn&YH<0k67mb#}eDfAZ=hg-HBo@j3YC-=%(+ zT^pYKD?!e+LBgPPCq!(wE9^TRxfNOONS@OUk%Pzre0;R4Xp6Ir?%3>Adp{ViN+#17u=VQWs z!zc0|Jw#UfQBu2c%PGr9JBNAqe*86%7A>h}tw!|-e%BD1LonqM`zhu-&Ya6Atxj7l z$ng6$)sY)ZrpKBf(^iT9K(C7D>zthL@iE5*W<*<3k{>p!; zzuQ_3va+>F3C9CCT%YAp{{T8)WflVrSQv?WM_=ka2fqXBrgk5(+pAEIoYJqigU}wo zJmXm~0E|iLA&EkgPf1levhO>ANZnBaw1wmagmHoZ!13qPS}#`gduIyvpVlQD$8JIA z83(_+b7+nn|*W;`o!ImPSLdTOCpvkxgFmfSr&$) O$=+f&ncCWvPyg9jJA|YF literal 0 HcmV?d00001 diff --git a/database/pictures/grywarky2.jpg b/database/pictures/grywarky2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..01c0b89be1172d7f7020c67f068a626054787c76 GIT binary patch literal 32566 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 XuJl9r+{)#2&zrIPirZGPfBXL*gkwge literal 0 HcmV?d00001 diff --git a/database/pictures/home_front_b_5.jpg b/database/pictures/home_front_b_5.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ff96be50bbad4d0ee69cd2265ba707024fdb2416 GIT binary patch literal 19125 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 literal 0 HcmV?d00001 diff --git a/database/pictures/no_matches.html b/database/pictures/no_matches.html new file mode 100644 index 00000000..683fcb5c --- /dev/null +++ b/database/pictures/no_matches.html @@ -0,0 +1,33 @@ + + + + + no matches for your search... + + +

    sN0rF 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 z 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

    '; + if (node._hc) { + + str += '
    '; + str += this.addNode(node); + str += '
    '; + + } + this.aIndent.pop(); + return str; +}; + +// Adds the empty and line icons +dTree.prototype.indent = function(node, nodeId) { + var str = ''; + if (this.root.id != node.pid) { + for (var n=0; n'; + (node._ls) ? this.aIndent.push(0) : this.aIndent.push(1); + if (node._hc) { + str += ''; + } else str += ''; + } + return str; +}; + +// Checks if a node has any children and if it is the last sibling +dTree.prototype.setCS = function(node) { + var lastId; + for (var n=0; n + + diff --git a/scripts/web/javascript/gruntose/launchers.js b/scripts/web/javascript/gruntose/launchers.js new file mode 100644 index 00000000..08723dd7 --- /dev/null +++ b/scripts/web/javascript/gruntose/launchers.js @@ -0,0 +1,21 @@ + + + diff --git a/scripts/web/javascript/gruntose/norse_date.js b/scripts/web/javascript/gruntose/norse_date.js new file mode 100644 index 00000000..cdb1a6a3 --- /dev/null +++ b/scripts/web/javascript/gruntose/norse_date.js @@ -0,0 +1,59 @@ + + diff --git a/scripts/web/javascript/gruntose/show_file.js b/scripts/web/javascript/gruntose/show_file.js new file mode 100644 index 00000000..12b07d7a --- /dev/null +++ b/scripts/web/javascript/gruntose/show_file.js @@ -0,0 +1,30 @@ + + diff --git a/scripts/web/javascript/jquery/jquery-1.5.1.js b/scripts/web/javascript/jquery/jquery-1.5.1.js new file mode 100644 index 00000000..78fcfa46 --- /dev/null +++ b/scripts/web/javascript/jquery/jquery-1.5.1.js @@ -0,0 +1,8316 @@ +/*! + * jQuery JavaScript Library v1.5.1 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Wed Feb 23 13:55:29 2011 -0500 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // Check for digits + rdigit = /\d/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + + // Useragent RegExp + rwebkit = /(webkit)[ \/]([\w.]+)/, + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + rmsie = /(msie) ([\w.]+)/, + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // Has the ready events already been bound? + readyBound = false, + + // The deferred used on DOM ready + readyList, + + // Promise methods + promiseMethods = "then done fail isResolved isRejected promise".split( " " ), + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + trim = String.prototype.trim, + indexOf = Array.prototype.indexOf, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context && document.body ) { + this.context = document; + this[0] = document.body; + this.selector = "body"; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + match = quickExpr.exec( selector ); + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = (context ? context.ownerDocument || context : document); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return (context || rootjQuery).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if (selector.selector !== undefined) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.5.1", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = this.constructor(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + (this.selector ? " " : "") + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.done( fn ); + + return this; + }, + + eq: function( i ) { + return i === -1 ? + this.slice( i ) : + this.slice( i, +i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + window.$ = _$; + + if ( deep ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + // A third-party is pushing the ready event forwards + if ( wait === true ) { + jQuery.readyWait--; + } + + // Make sure that the DOM is not already loaded + if ( !jQuery.readyWait || (wait !== true && !jQuery.isReady) ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).unbind( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyBound ) { + return; + } + + readyBound = true; + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + return setTimeout( jQuery.ready, 1 ); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent("onreadystatechange", DOMContentLoaded); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + // A crude way of determining if an object is a window + isWindow: function( obj ) { + return obj && typeof obj === "object" && "setInterval" in obj; + }, + + isNaN: function( obj ) { + return obj == null || !rdigit.test( obj ) || isNaN( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw msg; + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test(data.replace(rvalidescape, "@") + .replace(rvalidtokens, "]") + .replace(rvalidbraces, "")) ) { + + // Try to use the native JSON parser first + return window.JSON && window.JSON.parse ? + window.JSON.parse( data ) : + (new Function("return " + data))(); + + } else { + jQuery.error( "Invalid JSON: " + data ); + } + }, + + // Cross-browser xml parsing + // (xml & tmp used internally) + parseXML: function( data , xml , tmp ) { + + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + + tmp = xml.documentElement; + + if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) { + jQuery.error( "Invalid XML: " + data ); + } + + return xml; + }, + + noop: function() {}, + + // Evalulates a script in a global context + globalEval: function( data ) { + if ( data && rnotwhite.test(data) ) { + // Inspired by code by Andrea Giammarchi + // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html + var head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement, + script = document.createElement( "script" ); + + if ( jQuery.support.scriptEval() ) { + script.appendChild( document.createTextNode( data ) ); + } else { + script.text = data; + } + + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709). + head.insertBefore( script, head.firstChild ); + head.removeChild( script ); + } + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction(object); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( var value = object[0]; + i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {} + } + } + + return object; + }, + + // Use native String.trim function wherever possible + trim: trim ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // The extra typeof function check is to prevent crashes + // in Safari 2 (See: #3039) + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + var type = jQuery.type(array); + + if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array ) { + if ( array.indexOf ) { + return array.indexOf( elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, + j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = [], retVal; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var ret = [], value; + + // Go through the array, translating each of the items to their + // new value (or values). + for ( var i = 0, length = elems.length; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + proxy: function( fn, proxy, thisObject ) { + if ( arguments.length === 2 ) { + if ( typeof proxy === "string" ) { + thisObject = fn; + fn = thisObject[ proxy ]; + proxy = undefined; + + } else if ( proxy && !jQuery.isFunction( proxy ) ) { + thisObject = proxy; + proxy = undefined; + } + } + + if ( !proxy && fn ) { + proxy = function() { + return fn.apply( thisObject || this, arguments ); + }; + } + + // Set the guid of unique handler to the same of original handler, so it can be removed + if ( fn ) { + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + } + + // So proxy can be declared as an argument + return proxy; + }, + + // Mutifunctional method to get and set values to a collection + // The value/s can be optionally by executed if its a function + access: function( elems, key, value, exec, fn, pass ) { + var length = elems.length; + + // Setting many attributes + if ( typeof key === "object" ) { + for ( var k in key ) { + jQuery.access( elems, k, key[k], exec, fn, value ); + } + return elems; + } + + // Setting one attribute + if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = !pass && exec && jQuery.isFunction(value); + + for ( var i = 0; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + + return elems; + } + + // Getting an attribute + return length ? fn( elems[0], key ) : undefined; + }, + + now: function() { + return (new Date()).getTime(); + }, + + // Create a simple deferred (one callbacks list) + _Deferred: function() { + var // callbacks list + callbacks = [], + // stored [ context , args ] + fired, + // to avoid firing when already doing so + firing, + // flag to know if the deferred has been cancelled + cancelled, + // the deferred itself + deferred = { + + // done( f1, f2, ...) + done: function() { + if ( !cancelled ) { + var args = arguments, + i, + length, + elem, + type, + _fired; + if ( fired ) { + _fired = fired; + fired = 0; + } + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + deferred.done.apply( deferred, elem ); + } else if ( type === "function" ) { + callbacks.push( elem ); + } + } + if ( _fired ) { + deferred.resolveWith( _fired[ 0 ], _fired[ 1 ] ); + } + } + return this; + }, + + // resolve with given context and args + resolveWith: function( context, args ) { + if ( !cancelled && !fired && !firing ) { + firing = 1; + try { + while( callbacks[ 0 ] ) { + callbacks.shift().apply( context, args ); + } + } + // We have to add a catch block for + // IE prior to 8 or else the finally + // block will never get executed + catch (e) { + throw e; + } + finally { + fired = [ context, args ]; + firing = 0; + } + } + return this; + }, + + // resolve with this as context and given arguments + resolve: function() { + deferred.resolveWith( jQuery.isFunction( this.promise ) ? this.promise() : this, arguments ); + return this; + }, + + // Has this deferred been resolved? + isResolved: function() { + return !!( firing || fired ); + }, + + // Cancel + cancel: function() { + cancelled = 1; + callbacks = []; + return this; + } + }; + + return deferred; + }, + + // Full fledged deferred (two callbacks list) + Deferred: function( func ) { + var deferred = jQuery._Deferred(), + failDeferred = jQuery._Deferred(), + promise; + // Add errorDeferred methods, then and promise + jQuery.extend( deferred, { + then: function( doneCallbacks, failCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ); + return this; + }, + fail: failDeferred.done, + rejectWith: failDeferred.resolveWith, + reject: failDeferred.resolve, + isRejected: failDeferred.isResolved, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + if ( promise ) { + return promise; + } + promise = obj = {}; + } + var i = promiseMethods.length; + while( i-- ) { + obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ]; + } + return obj; + } + } ); + // Make sure only one callback list will be used + deferred.done( failDeferred.cancel ).fail( deferred.cancel ); + // Unexpose cancel + delete deferred.cancel; + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + return deferred; + }, + + // Deferred helper + when: function( object ) { + var lastIndex = arguments.length, + deferred = lastIndex <= 1 && object && jQuery.isFunction( object.promise ) ? + object : + jQuery.Deferred(), + promise = deferred.promise(); + + if ( lastIndex > 1 ) { + var array = slice.call( arguments, 0 ), + count = lastIndex, + iCallback = function( index ) { + return function( value ) { + array[ index ] = arguments.length > 1 ? slice.call( arguments, 0 ) : value; + if ( !( --count ) ) { + deferred.resolveWith( promise, array ); + } + }; + }; + while( ( lastIndex-- ) ) { + object = array[ lastIndex ]; + if ( object && jQuery.isFunction( object.promise ) ) { + object.promise().then( iCallback(lastIndex), deferred.reject ); + } else { + --count; + } + } + if ( !count ) { + deferred.resolveWith( promise, array ); + } + } else if ( deferred !== object ) { + deferred.resolve( object ); + } + return promise; + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = rwebkit.exec( ua ) || + ropera.exec( ua ) || + rmsie.exec( ua ) || + ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + sub: function() { + function jQuerySubclass( selector, context ) { + return new jQuerySubclass.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySubclass, this ); + jQuerySubclass.superclass = this; + jQuerySubclass.fn = jQuerySubclass.prototype = this(); + jQuerySubclass.fn.constructor = jQuerySubclass; + jQuerySubclass.subclass = this.subclass; + jQuerySubclass.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySubclass) ) { + context = jQuerySubclass(context); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySubclass ); + }; + jQuerySubclass.fn.init.prototype = jQuerySubclass.fn; + var rootjQuerySubclass = jQuerySubclass(document); + return jQuerySubclass; + }, + + browser: {} +}); + +// Create readyList deferred +readyList = jQuery._Deferred(); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +if ( indexOf ) { + jQuery.inArray = function( elem, array ) { + return indexOf.call( array, elem ); + }; +} + +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +// Expose jQuery to the global object +return jQuery; + +})(); + + +(function() { + + jQuery.support = {}; + + var div = document.createElement("div"); + + div.style.display = "none"; + div.innerHTML = "
    a"; + + var all = div.getElementsByTagName("*"), + a = div.getElementsByTagName("a")[0], + select = document.createElement("select"), + opt = select.appendChild( document.createElement("option") ), + input = div.getElementsByTagName("input")[0]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return; + } + + jQuery.support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: div.firstChild.nodeType === 3, + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText insted) + style: /red/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: a.getAttribute("href") === "/a", + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55$/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: input.value === "on", + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Will be defined later + deleteExpando: true, + optDisabled: false, + checkClone: false, + noCloneEvent: true, + noCloneChecked: true, + boxModel: null, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableHiddenOffsets: true + }; + + input.checked = true; + jQuery.support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as diabled) + select.disabled = true; + jQuery.support.optDisabled = !opt.disabled; + + var _scriptEval = null; + jQuery.support.scriptEval = function() { + if ( _scriptEval === null ) { + var root = document.documentElement, + script = document.createElement("script"), + id = "script" + jQuery.now(); + + try { + script.appendChild( document.createTextNode( "window." + id + "=1;" ) ); + } catch(e) {} + + root.insertBefore( script, root.firstChild ); + + // Make sure that the execution of code works by injecting a script + // tag with appendChild/createTextNode + // (IE doesn't support this, fails, and uses .text instead) + if ( window[ id ] ) { + _scriptEval = true; + delete window[ id ]; + } else { + _scriptEval = false; + } + + root.removeChild( script ); + // release memory in IE + root = script = id = null; + } + + return _scriptEval; + }; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + + } catch(e) { + jQuery.support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent("onclick", function click() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + jQuery.support.noCloneEvent = false; + div.detachEvent("onclick", click); + }); + div.cloneNode(true).fireEvent("onclick"); + } + + div = document.createElement("div"); + div.innerHTML = ""; + + var fragment = document.createDocumentFragment(); + fragment.appendChild( div.firstChild ); + + // WebKit doesn't clone checked state correctly in fragments + jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked; + + // Figure out if the W3C box model works as expected + // document.body must exist before we can do this + jQuery(function() { + var div = document.createElement("div"), + body = document.getElementsByTagName("body")[0]; + + // Frameset documents with no body should not run this code + if ( !body ) { + return; + } + + div.style.width = div.style.paddingLeft = "1px"; + body.appendChild( div ); + jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2; + + if ( "zoom" in div.style ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.style.display = "inline"; + div.style.zoom = 1; + jQuery.support.inlineBlockNeedsLayout = div.offsetWidth === 2; + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = ""; + div.innerHTML = "
    "; + jQuery.support.shrinkWrapBlocks = div.offsetWidth !== 2; + } + + div.innerHTML = "
    t
    "; + var tds = div.getElementsByTagName("td"); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + jQuery.support.reliableHiddenOffsets = tds[0].offsetHeight === 0; + + tds[0].style.display = ""; + tds[1].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE < 8 fail this test) + jQuery.support.reliableHiddenOffsets = jQuery.support.reliableHiddenOffsets && tds[0].offsetHeight === 0; + div.innerHTML = ""; + + body.removeChild( div ).style.display = "none"; + div = tds = null; + }); + + // Technique from Juriy Zaytsev + // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ + var eventSupported = function( eventName ) { + var el = document.createElement("div"); + eventName = "on" + eventName; + + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( !el.attachEvent ) { + return true; + } + + var isSupported = (eventName in el); + if ( !isSupported ) { + el.setAttribute(eventName, "return;"); + isSupported = typeof el[eventName] === "function"; + } + el = null; + + return isSupported; + }; + + jQuery.support.submitBubbles = eventSupported("submit"); + jQuery.support.changeBubbles = eventSupported("change"); + + // release memory in IE + div = all = a = null; +})(); + + + +var rbrace = /^(?:\{.*\}|\[.*\])$/; + +jQuery.extend({ + cache: {}, + + // Please use with caution + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ jQuery.expando ] = id = ++jQuery.uuid; + } else { + id = jQuery.expando; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery + // metadata on plain JS objects when the object is serialized using + // JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name); + } else { + cache[ id ] = jQuery.extend(cache[ id ], name); + } + } + + thisCache = cache[ id ]; + + // Internal jQuery data is stored in a separate object inside the object's data + // cache in order to avoid key collisions between internal data and user-defined + // data + if ( pvt ) { + if ( !thisCache[ internalKey ] ) { + thisCache[ internalKey ] = {}; + } + + thisCache = thisCache[ internalKey ]; + } + + if ( data !== undefined ) { + thisCache[ name ] = data; + } + + // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should + // not attempt to inspect the internal events object using jQuery.data, as this + // internal data object is undocumented and subject to change. + if ( name === "events" && !thisCache[name] ) { + return thisCache[ internalKey ] && thisCache[ internalKey ].events; + } + + return getByName ? thisCache[ name ] : thisCache; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var internalKey = jQuery.expando, isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + + // See jQuery.data for more information + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ]; + + if ( thisCache ) { + delete thisCache[ name ]; + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !isEmptyDataObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( pvt ) { + delete cache[ id ][ internalKey ]; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject(cache[ id ]) ) { + return; + } + } + + var internalCache = cache[ id ][ internalKey ]; + + // Browsers that fail expando deletion also refuse to delete expandos on + // the window, but it will allow it on all other JS objects; other browsers + // don't care + if ( jQuery.support.deleteExpando || cache != window ) { + delete cache[ id ]; + } else { + cache[ id ] = null; + } + + // We destroyed the entire user cache at once because it's faster than + // iterating through each key, but we need to continue to persist internal + // data if it existed + if ( internalCache ) { + cache[ id ] = {}; + // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery + // metadata on plain JS objects when the object is serialized using + // JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + + cache[ id ][ internalKey ] = internalCache; + + // Otherwise, we need to eliminate the expando on the node to avoid + // false lookups in the cache for entries that no longer exist + } else if ( isNode ) { + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( jQuery.support.deleteExpando ) { + delete elem[ jQuery.expando ]; + } else if ( elem.removeAttribute ) { + elem.removeAttribute( jQuery.expando ); + } else { + elem[ jQuery.expando ] = null; + } + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + if ( elem.nodeName ) { + var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; + + if ( match ) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + + return true; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var data = null; + + if ( typeof key === "undefined" ) { + if ( this.length ) { + data = jQuery.data( this[0] ); + + if ( this[0].nodeType === 1 ) { + var attr = this[0].attributes, name; + for ( var i = 0, l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = name.substr( 5 ); + dataAttr( this[0], name, data[ name ] ); + } + } + } + } + + return data; + + } else if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + var parts = key.split("."); + parts[1] = parts[1] ? "." + parts[1] : ""; + + if ( value === undefined ) { + data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + + // Try to fetch any internally stored data first + if ( data === undefined && this.length ) { + data = jQuery.data( this[0], key ); + data = dataAttr( this[0], key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + + } else { + return this.each(function() { + var $this = jQuery( this ), + args = [ parts[0], value ]; + + $this.triggerHandler( "setData" + parts[1] + "!", args ); + jQuery.data( this, key, value ); + $this.triggerHandler( "changeData" + parts[1] + "!", args ); + }); + } + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + data = elem.getAttribute( "data-" + key ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + !jQuery.isNaN( data ) ? parseFloat( data ) : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON +// property to be considered empty objects; this property always exists in +// order to make sure JSON.stringify does not expose internal metadata +function isEmptyDataObject( obj ) { + for ( var name in obj ) { + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + + + + +jQuery.extend({ + queue: function( elem, type, data ) { + if ( !elem ) { + return; + } + + type = (type || "fx") + "queue"; + var q = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( !data ) { + return q || []; + } + + if ( !q || jQuery.isArray(data) ) { + q = jQuery._data( elem, type, jQuery.makeArray(data) ); + + } else { + q.push( data ); + } + + return q; + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + fn = queue.shift(); + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift("inprogress"); + } + + fn.call(elem, function() { + jQuery.dequeue(elem, type); + }); + } + + if ( !queue.length ) { + jQuery.removeData( elem, type + "queue", true ); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + } + + if ( data === undefined ) { + return jQuery.queue( this[0], type ); + } + return this.each(function( i ) { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; + type = type || "fx"; + + return this.queue( type, function() { + var elem = this; + setTimeout(function() { + jQuery.dequeue( elem, type ); + }, time ); + }); + }, + + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + } +}); + + + + +var rclass = /[\n\t\r]/g, + rspaces = /\s+/, + rreturn = /\r/g, + rspecialurl = /^(?:href|src|style)$/, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea)?$/i, + rradiocheck = /^(?:radio|checkbox)$/i; + +jQuery.props = { + "for": "htmlFor", + "class": "className", + readonly: "readOnly", + maxlength: "maxLength", + cellspacing: "cellSpacing", + rowspan: "rowSpan", + colspan: "colSpan", + tabindex: "tabIndex", + usemap: "useMap", + frameborder: "frameBorder" +}; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, name, value, true, jQuery.attr ); + }, + + removeAttr: function( name, fn ) { + return this.each(function(){ + jQuery.attr( this, name, "" ); + if ( this.nodeType === 1 ) { + this.removeAttribute( name ); + } + }); + }, + + addClass: function( value ) { + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + self.addClass( value.call(this, i, self.attr("class")) ); + }); + } + + if ( value && typeof value === "string" ) { + var classNames = (value || "").split( rspaces ); + + for ( var i = 0, l = this.length; i < l; i++ ) { + var elem = this[i]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className ) { + elem.className = value; + + } else { + var className = " " + elem.className + " ", + setClass = elem.className; + + for ( var c = 0, cl = classNames.length; c < cl; c++ ) { + if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) { + setClass += " " + classNames[c]; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + self.removeClass( value.call(this, i, self.attr("class")) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + var classNames = (value || "").split( rspaces ); + + for ( var i = 0, l = this.length; i < l; i++ ) { + var elem = this[i]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + var className = (" " + elem.className + " ").replace(rclass, " "); + for ( var c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[c] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function(i) { + var self = jQuery(this); + self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( rspaces ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " "; + for ( var i = 0, l = this.length; i < l; i++ ) { + if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + if ( !arguments.length ) { + var elem = this[0]; + + if ( elem ) { + if ( jQuery.nodeName( elem, "option" ) ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + + // We need to handle select boxes special + if ( jQuery.nodeName( elem, "select" ) ) { + var index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery(option).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + } + + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) { + return elem.getAttribute("value") === null ? "on" : elem.value; + } + + // Everything else, we just grab the value + return (elem.value || "").replace(rreturn, ""); + + } + + return undefined; + } + + var isFunction = jQuery.isFunction(value); + + return this.each(function(i) { + var self = jQuery(this), val = value; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call(this, i, self.val()); + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray(val) ) { + val = jQuery.map(val, function (value) { + return value == null ? "" : value + ""; + }); + } + + if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) { + this.checked = jQuery.inArray( self.val(), val ) >= 0; + + } else if ( jQuery.nodeName( this, "select" ) ) { + var values = jQuery.makeArray(val); + + jQuery( "option", this ).each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + this.selectedIndex = -1; + } + + } else { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attr: function( elem, name, value, pass ) { + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || elem.nodeType === 2 ) { + return undefined; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery(elem)[name](value); + } + + var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ), + // Whether we are setting (or getting) + set = value !== undefined; + + // Try to normalize/fix the name + name = notxml && jQuery.props[ name ] || name; + + // Only do all the following if this is a node (faster for style) + if ( elem.nodeType === 1 ) { + // These attributes require special treatment + var special = rspecialurl.test( name ); + + // Safari mis-reports the default selected property of an option + // Accessing the parent's selectedIndex property fixes it + if ( name === "selected" && !jQuery.support.optSelected ) { + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + + // If applicable, access the attribute via the DOM 0 way + // 'in' checks fail in Blackberry 4.7 #6931 + if ( (name in elem || elem[ name ] !== undefined) && notxml && !special ) { + if ( set ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } + + if ( value === null ) { + if ( elem.nodeType === 1 ) { + elem.removeAttribute( name ); + } + + } else { + elem[ name ] = value; + } + } + + // browsers index elements by id/name on forms, give priority to attributes. + if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) { + return elem.getAttributeNode( name ).nodeValue; + } + + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + if ( name === "tabIndex" ) { + var attributeNode = elem.getAttributeNode( "tabIndex" ); + + return attributeNode && attributeNode.specified ? + attributeNode.value : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + + return elem[ name ]; + } + + if ( !jQuery.support.style && notxml && name === "style" ) { + if ( set ) { + elem.style.cssText = "" + value; + } + + return elem.style.cssText; + } + + if ( set ) { + // convert the value to a string (all browsers do this but IE) see #1070 + elem.setAttribute( name, "" + value ); + } + + // Ensure that missing attributes return undefined + // Blackberry 4.7 returns "" from getAttribute #6938 + if ( !elem.attributes[ name ] && (elem.hasAttribute && !elem.hasAttribute( name )) ) { + return undefined; + } + + var attr = !jQuery.support.hrefNormalized && notxml && special ? + // Some attributes require a special call on IE + elem.getAttribute( name, 2 ) : + elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return attr === null ? undefined : attr; + } + // Handle everything which isn't a DOM element node + if ( set ) { + elem[ name ] = value; + } + return elem[ name ]; + } +}); + + + + +var rnamespaces = /\.(.*)$/, + rformElems = /^(?:textarea|input|select)$/i, + rperiod = /\./g, + rspace = / /g, + rescape = /[^\w\s.|`]/g, + fcleanup = function( nm ) { + return nm.replace(rescape, "\\$&"); + }; + +/* + * A number of helper functions used for managing events. + * Many of the ideas behind this code originated from + * Dean Edwards' addEvent library. + */ +jQuery.event = { + + // Bind an event to an element + // Original by Dean Edwards + add: function( elem, types, handler, data ) { + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // TODO :: Use a try/catch until it's safe to pull this out (likely 1.6) + // Minor release fix for bug #8018 + try { + // For whatever reason, IE has trouble passing the window object + // around, causing it to be cloned in the process + if ( jQuery.isWindow( elem ) && ( elem !== window && !elem.frameElement ) ) { + elem = window; + } + } + catch ( e ) {} + + if ( handler === false ) { + handler = returnFalse; + } else if ( !handler ) { + // Fixes bug #7229. Fix recommended by jdalton + return; + } + + var handleObjIn, handleObj; + + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + } + + // Make sure that the function being executed has a unique ID + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure + var elemData = jQuery._data( elem ); + + // If no elemData is found then we must be trying to bind to one of the + // banned noData elements + if ( !elemData ) { + return; + } + + var events = elemData.events, + eventHandle = elemData.handle; + + if ( !events ) { + elemData.events = events = {}; + } + + if ( !eventHandle ) { + elemData.handle = eventHandle = function() { + // Handle the second event of a trigger and when + // an event is called after a page has unloaded + return typeof jQuery !== "undefined" && !jQuery.event.triggered ? + jQuery.event.handle.apply( eventHandle.elem, arguments ) : + undefined; + }; + } + + // Add elem as a property of the handle function + // This is to prevent a memory leak with non-native events in IE. + eventHandle.elem = elem; + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = types.split(" "); + + var type, i = 0, namespaces; + + while ( (type = types[ i++ ]) ) { + handleObj = handleObjIn ? + jQuery.extend({}, handleObjIn) : + { handler: handler, data: data }; + + // Namespaced event handlers + if ( type.indexOf(".") > -1 ) { + namespaces = type.split("."); + type = namespaces.shift(); + handleObj.namespace = namespaces.slice(0).sort().join("."); + + } else { + namespaces = []; + handleObj.namespace = ""; + } + + handleObj.type = type; + if ( !handleObj.guid ) { + handleObj.guid = handler.guid; + } + + // Get the current list of functions bound to this event + var handlers = events[ type ], + special = jQuery.event.special[ type ] || {}; + + // Init the event handler queue + if ( !handlers ) { + handlers = events[ type ] = []; + + // Check for a special event handler + // Only use addEventListener/attachEvent if the special + // events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add the function to the element's handler list + handlers.push( handleObj ); + + // Keep track of which events have been used, for global triggering + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, pos ) { + // don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + if ( handler === false ) { + handler = returnFalse; + } + + var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + events = elemData && elemData.events; + + if ( !elemData || !events ) { + return; + } + + // types is actually an event object here + if ( types && types.type ) { + handler = types.handler; + types = types.type; + } + + // Unbind all events for the element + if ( !types || typeof types === "string" && types.charAt(0) === "." ) { + types = types || ""; + + for ( type in events ) { + jQuery.event.remove( elem, type + types ); + } + + return; + } + + // Handle multiple events separated by a space + // jQuery(...).unbind("mouseover mouseout", fn); + types = types.split(" "); + + while ( (type = types[ i++ ]) ) { + origType = type; + handleObj = null; + all = type.indexOf(".") < 0; + namespaces = []; + + if ( !all ) { + // Namespaced event handlers + namespaces = type.split("."); + type = namespaces.shift(); + + namespace = new RegExp("(^|\\.)" + + jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + eventType = events[ type ]; + + if ( !eventType ) { + continue; + } + + if ( !handler ) { + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( all || namespace.test( handleObj.namespace ) ) { + jQuery.event.remove( elem, origType, handleObj.handler, j ); + eventType.splice( j--, 1 ); + } + } + + continue; + } + + special = jQuery.event.special[ type ] || {}; + + for ( j = pos || 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( handler.guid === handleObj.guid ) { + // remove the given handler for the given type + if ( all || namespace.test( handleObj.namespace ) ) { + if ( pos == null ) { + eventType.splice( j--, 1 ); + } + + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + + if ( pos != null ) { + break; + } + } + } + + // remove generic event handler if no more handlers exist + if ( eventType.length === 0 || pos != null && eventType.length === 1 ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + ret = null; + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + var handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + delete elemData.events; + delete elemData.handle; + + if ( jQuery.isEmptyObject( elemData ) ) { + jQuery.removeData( elem, undefined, true ); + } + } + }, + + // bubbling is internal + trigger: function( event, data, elem /*, bubbling */ ) { + // Event object or event type + var type = event.type || event, + bubbling = arguments[3]; + + if ( !bubbling ) { + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + jQuery.extend( jQuery.Event(type), event ) : + // Just the event type (string) + jQuery.Event(type); + + if ( type.indexOf("!") >= 0 ) { + event.type = type = type.slice(0, -1); + event.exclusive = true; + } + + // Handle a global trigger + if ( !elem ) { + // Don't bubble custom events when global (to avoid too much overhead) + event.stopPropagation(); + + // Only trigger if we've ever bound an event for it + if ( jQuery.event.global[ type ] ) { + // XXX This code smells terrible. event.js should not be directly + // inspecting the data cache + jQuery.each( jQuery.cache, function() { + // internalKey variable is just used to make it easier to find + // and potentially change this stuff later; currently it just + // points to jQuery.expando + var internalKey = jQuery.expando, + internalCache = this[ internalKey ]; + if ( internalCache && internalCache.events && internalCache.events[ type ] ) { + jQuery.event.trigger( event, data, internalCache.handle.elem ); + } + }); + } + } + + // Handle triggering a single element + + // don't do events on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) { + return undefined; + } + + // Clean up in case it is reused + event.result = undefined; + event.target = elem; + + // Clone the incoming data, if any + data = jQuery.makeArray( data ); + data.unshift( event ); + } + + event.currentTarget = elem; + + // Trigger the event, it is assumed that "handle" is a function + var handle = jQuery._data( elem, "handle" ); + + if ( handle ) { + handle.apply( elem, data ); + } + + var parent = elem.parentNode || elem.ownerDocument; + + // Trigger an inline bound script + try { + if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) { + if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) { + event.result = false; + event.preventDefault(); + } + } + + // prevent IE from throwing an error for some elements with some event types, see #3533 + } catch (inlineError) {} + + if ( !event.isPropagationStopped() && parent ) { + jQuery.event.trigger( event, data, parent, true ); + + } else if ( !event.isDefaultPrevented() ) { + var old, + target = event.target, + targetType = type.replace( rnamespaces, "" ), + isClick = jQuery.nodeName( target, "a" ) && targetType === "click", + special = jQuery.event.special[ targetType ] || {}; + + if ( (!special._default || special._default.call( elem, event ) === false) && + !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) { + + try { + if ( target[ targetType ] ) { + // Make sure that we don't accidentally re-trigger the onFOO events + old = target[ "on" + targetType ]; + + if ( old ) { + target[ "on" + targetType ] = null; + } + + jQuery.event.triggered = true; + target[ targetType ](); + } + + // prevent IE from throwing an error for some elements with some event types, see #3533 + } catch (triggerError) {} + + if ( old ) { + target[ "on" + targetType ] = old; + } + + jQuery.event.triggered = false; + } + } + }, + + handle: function( event ) { + var all, handlers, namespaces, namespace_re, events, + namespace_sort = [], + args = jQuery.makeArray( arguments ); + + event = args[0] = jQuery.event.fix( event || window.event ); + event.currentTarget = this; + + // Namespaced event handlers + all = event.type.indexOf(".") < 0 && !event.exclusive; + + if ( !all ) { + namespaces = event.type.split("."); + event.type = namespaces.shift(); + namespace_sort = namespaces.slice(0).sort(); + namespace_re = new RegExp("(^|\\.)" + namespace_sort.join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + event.namespace = event.namespace || namespace_sort.join("."); + + events = jQuery._data(this, "events"); + + handlers = (events || {})[ event.type ]; + + if ( events && handlers ) { + // Clone the handlers to prevent manipulation + handlers = handlers.slice(0); + + for ( var j = 0, l = handlers.length; j < l; j++ ) { + var handleObj = handlers[ j ]; + + // Filter the functions by class + if ( all || namespace_re.test( handleObj.namespace ) ) { + // Pass in a reference to the handler function itself + // So that we can later remove it + event.handler = handleObj.handler; + event.data = handleObj.data; + event.handleObj = handleObj; + + var ret = handleObj.handler.apply( this, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + } + + return event.result; + }, + + props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // store a copy of the original event object + // and "clone" to set read-only properties + var originalEvent = event; + event = jQuery.Event( originalEvent ); + + for ( var i = this.props.length, prop; i; ) { + prop = this.props[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary + if ( !event.target ) { + // Fixes #1925 where srcElement might not be defined either + event.target = event.srcElement || document; + } + + // check if target is a textnode (safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && event.fromElement ) { + event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; + } + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && event.clientX != null ) { + var doc = document.documentElement, + body = document.body; + + event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); + event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); + } + + // Add which for key events + if ( event.which == null && (event.charCode != null || event.keyCode != null) ) { + event.which = event.charCode != null ? event.charCode : event.keyCode; + } + + // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) + if ( !event.metaKey && event.ctrlKey ) { + event.metaKey = event.ctrlKey; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && event.button !== undefined ) { + event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); + } + + return event; + }, + + // Deprecated, use jQuery.guid instead + guid: 1E8, + + // Deprecated, use jQuery.proxy instead + proxy: jQuery.proxy, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady, + teardown: jQuery.noop + }, + + live: { + add: function( handleObj ) { + jQuery.event.add( this, + liveConvert( handleObj.origType, handleObj.selector ), + jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); + }, + + remove: function( handleObj ) { + jQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj ); + } + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + if ( elem.detachEvent ) { + elem.detachEvent( "on" + type, handle ); + } + }; + +jQuery.Event = function( src ) { + // Allow instantiation without the 'new' keyword + if ( !this.preventDefault ) { + return new jQuery.Event( src ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // timeStamp is buggy for some events on Firefox(#3843) + // So we won't rely on the native value + this.timeStamp = jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Checks if an event happened on an element within another element +// Used in jQuery.event.special.mouseenter and mouseleave handlers +var withinElement = function( event ) { + // Check if mouse(over|out) are still within the same parent element + var parent = event.relatedTarget; + + // Firefox sometimes assigns relatedTarget a XUL element + // which we cannot access the parentNode property of + try { + + // Chrome does something similar, the parentNode property + // can be accessed but is null. + if ( parent !== document && !parent.parentNode ) { + return; + } + // Traverse up the tree + while ( parent && parent !== this ) { + parent = parent.parentNode; + } + + if ( parent !== this ) { + // set the correct event type + event.type = event.data; + + // handle event if we actually just moused on to a non sub-element + jQuery.event.handle.apply( this, arguments ); + } + + // assuming we've left the element since we most likely mousedover a xul element + } catch(e) { } +}, + +// In case of event delegation, we only need to rename the event.type, +// liveHandler will take care of the rest. +delegate = function( event ) { + event.type = event.data; + jQuery.event.handle.apply( this, arguments ); +}; + +// Create mouseenter and mouseleave events +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + setup: function( data ) { + jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig ); + }, + teardown: function( data ) { + jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement ); + } + }; +}); + +// submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function( data, namespaces ) { + if ( this.nodeName && this.nodeName.toLowerCase() !== "form" ) { + jQuery.event.add(this, "click.specialSubmit", function( e ) { + var elem = e.target, + type = elem.type; + + if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) { + trigger( "submit", this, arguments ); + } + }); + + jQuery.event.add(this, "keypress.specialSubmit", function( e ) { + var elem = e.target, + type = elem.type; + + if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) { + trigger( "submit", this, arguments ); + } + }); + + } else { + return false; + } + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialSubmit" ); + } + }; + +} + +// change delegation, happens here so we have bind. +if ( !jQuery.support.changeBubbles ) { + + var changeFilters, + + getVal = function( elem ) { + var type = elem.type, val = elem.value; + + if ( type === "radio" || type === "checkbox" ) { + val = elem.checked; + + } else if ( type === "select-multiple" ) { + val = elem.selectedIndex > -1 ? + jQuery.map( elem.options, function( elem ) { + return elem.selected; + }).join("-") : + ""; + + } else if ( elem.nodeName.toLowerCase() === "select" ) { + val = elem.selectedIndex; + } + + return val; + }, + + testChange = function testChange( e ) { + var elem = e.target, data, val; + + if ( !rformElems.test( elem.nodeName ) || elem.readOnly ) { + return; + } + + data = jQuery._data( elem, "_change_data" ); + val = getVal(elem); + + // the current data will be also retrieved by beforeactivate + if ( e.type !== "focusout" || elem.type !== "radio" ) { + jQuery._data( elem, "_change_data", val ); + } + + if ( data === undefined || val === data ) { + return; + } + + if ( data != null || val ) { + e.type = "change"; + e.liveFired = undefined; + jQuery.event.trigger( e, arguments[1], elem ); + } + }; + + jQuery.event.special.change = { + filters: { + focusout: testChange, + + beforedeactivate: testChange, + + click: function( e ) { + var elem = e.target, type = elem.type; + + if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) { + testChange.call( this, e ); + } + }, + + // Change has to be called before submit + // Keydown will be called before keypress, which is used in submit-event delegation + keydown: function( e ) { + var elem = e.target, type = elem.type; + + if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") || + (e.keyCode === 32 && (type === "checkbox" || type === "radio")) || + type === "select-multiple" ) { + testChange.call( this, e ); + } + }, + + // Beforeactivate happens also before the previous element is blurred + // with this event you can't trigger a change event, but you can store + // information + beforeactivate: function( e ) { + var elem = e.target; + jQuery._data( elem, "_change_data", getVal(elem) ); + } + }, + + setup: function( data, namespaces ) { + if ( this.type === "file" ) { + return false; + } + + for ( var type in changeFilters ) { + jQuery.event.add( this, type + ".specialChange", changeFilters[type] ); + } + + return rformElems.test( this.nodeName ); + }, + + teardown: function( namespaces ) { + jQuery.event.remove( this, ".specialChange" ); + + return rformElems.test( this.nodeName ); + } + }; + + changeFilters = jQuery.event.special.change.filters; + + // Handle when the input is .focus()'d + changeFilters.focus = changeFilters.beforeactivate; +} + +function trigger( type, elem, args ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + // Don't pass args or remember liveFired; they apply to the donor event. + var event = jQuery.extend( {}, args[ 0 ] ); + event.type = type; + event.originalEvent = {}; + event.liveFired = undefined; + jQuery.event.handle.call( elem, event ); + if ( event.isDefaultPrevented() ) { + args[ 0 ].preventDefault(); + } +} + +// Create "bubbling" focus and blur events +if ( document.addEventListener ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + jQuery.event.special[ fix ] = { + setup: function() { + this.addEventListener( orig, handler, true ); + }, + teardown: function() { + this.removeEventListener( orig, handler, true ); + } + }; + + function handler( e ) { + e = jQuery.event.fix( e ); + e.type = fix; + return jQuery.event.handle.call( this, e ); + } + }); +} + +jQuery.each(["bind", "one"], function( i, name ) { + jQuery.fn[ name ] = function( type, data, fn ) { + // Handle object literals + if ( typeof type === "object" ) { + for ( var key in type ) { + this[ name ](key, data, type[key], fn); + } + return this; + } + + if ( jQuery.isFunction( data ) || data === false ) { + fn = data; + data = undefined; + } + + var handler = name === "one" ? jQuery.proxy( fn, function( event ) { + jQuery( this ).unbind( event, handler ); + return fn.apply( this, arguments ); + }) : fn; + + if ( type === "unload" && name !== "one" ) { + this.one( type, data, fn ); + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.add( this[i], type, handler, data ); + } + } + + return this; + }; +}); + +jQuery.fn.extend({ + unbind: function( type, fn ) { + // Handle object literals + if ( typeof type === "object" && !type.preventDefault ) { + for ( var key in type ) { + this.unbind(key, type[key]); + } + + } else { + for ( var i = 0, l = this.length; i < l; i++ ) { + jQuery.event.remove( this[i], type, fn ); + } + } + + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.live( types, data, fn, selector ); + }, + + undelegate: function( selector, types, fn ) { + if ( arguments.length === 0 ) { + return this.unbind( "live" ); + + } else { + return this.die( types, null, fn, selector ); + } + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + + triggerHandler: function( type, data ) { + if ( this[0] ) { + var event = jQuery.Event( type ); + event.preventDefault(); + event.stopPropagation(); + jQuery.event.trigger( event, data, this[0] ); + return event.result; + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + i = 1; + + // link all the functions, so any of them can unbind this click handler + while ( i < args.length ) { + jQuery.proxy( fn, args[ i++ ] ); + } + + return this.click( jQuery.proxy( fn, function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + })); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +var liveMap = { + focus: "focusin", + blur: "focusout", + mouseenter: "mouseover", + mouseleave: "mouseout" +}; + +jQuery.each(["live", "die"], function( i, name ) { + jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) { + var type, i = 0, match, namespaces, preType, + selector = origSelector || this.selector, + context = origSelector ? this : jQuery( this.context ); + + if ( typeof types === "object" && !types.preventDefault ) { + for ( var key in types ) { + context[ name ]( key, data, types[key], selector ); + } + + return this; + } + + if ( jQuery.isFunction( data ) ) { + fn = data; + data = undefined; + } + + types = (types || "").split(" "); + + while ( (type = types[ i++ ]) != null ) { + match = rnamespaces.exec( type ); + namespaces = ""; + + if ( match ) { + namespaces = match[0]; + type = type.replace( rnamespaces, "" ); + } + + if ( type === "hover" ) { + types.push( "mouseenter" + namespaces, "mouseleave" + namespaces ); + continue; + } + + preType = type; + + if ( type === "focus" || type === "blur" ) { + types.push( liveMap[ type ] + namespaces ); + type = type + namespaces; + + } else { + type = (liveMap[ type ] || type) + namespaces; + } + + if ( name === "live" ) { + // bind live handler + for ( var j = 0, l = context.length; j < l; j++ ) { + jQuery.event.add( context[j], "live." + liveConvert( type, selector ), + { data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } ); + } + + } else { + // unbind live handler + context.unbind( "live." + liveConvert( type, selector ), fn ); + } + } + + return this; + }; +}); + +function liveHandler( event ) { + var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret, + elems = [], + selectors = [], + events = jQuery._data( this, "events" ); + + // Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911) + if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) { + return; + } + + if ( event.namespace ) { + namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)"); + } + + event.liveFired = this; + + var live = events.live.slice(0); + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) { + selectors.push( handleObj.selector ); + + } else { + live.splice( j--, 1 ); + } + } + + match = jQuery( event.target ).closest( selectors, event.currentTarget ); + + for ( i = 0, l = match.length; i < l; i++ ) { + close = match[i]; + + for ( j = 0; j < live.length; j++ ) { + handleObj = live[j]; + + if ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) { + elem = close.elem; + related = null; + + // Those two events require additional checking + if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) { + event.type = handleObj.preType; + related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0]; + } + + if ( !related || related !== elem ) { + elems.push({ elem: elem, handleObj: handleObj, level: close.level }); + } + } + } + } + + for ( i = 0, l = elems.length; i < l; i++ ) { + match = elems[i]; + + if ( maxLevel && match.level > maxLevel ) { + break; + } + + event.currentTarget = match.elem; + event.data = match.handleObj.data; + event.handleObj = match.handleObj; + + ret = match.handleObj.origHandler.apply( match.elem, arguments ); + + if ( ret === false || event.isPropagationStopped() ) { + maxLevel = match.level; + + if ( ret === false ) { + stop = false; + } + if ( event.isImmediatePropagationStopped() ) { + break; + } + } + } + + return stop; +} + +function liveConvert( type, selector ) { + return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspace, "&"); +} + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.bind( name, data, fn ) : + this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } +}); + + +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rNonWord = /\W/; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set ); + } + } + + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var match, + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + var found, item, + filter = Expr.filter[ type ], + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw "Syntax error, unrecognized expression: " + msg; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + // Handle if an un-quoted value was used + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return "text" === elem.getAttribute( 'type' ); + }, + radio: function( elem ) { + return "radio" === elem.type; + }, + + checkbox: function( elem ) { + return "checkbox" === elem.type; + }, + + file: function( elem ) { + return "file" === elem.type; + }, + password: function( elem ) { + return "password" === elem.type; + }, + + submit: function( elem ) { + return "submit" === elem.type; + }, + + image: function( elem ) { + return "image" === elem.type; + }, + + reset: function( elem ) { + return "reset" === elem.type; + }, + + button: function( elem ) { + return "button" === elem.type || elem.nodeName.toLowerCase() === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + var first = match[2], + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // If the nodes are siblings (or identical) we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +// Utility function for retreiving the text value of an array of DOM nodes +Sizzle.getText = function( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + // Get the text from text nodes and CDATA nodes + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + // Traverse everything else, except comment nodes + } else if ( elem.nodeType !== 8 ) { + ret += Sizzle.getText( elem.childNodes ); + } + } + + return ret; +}; + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = ""; + + // Inject it into the root element, check its status, and remove it quickly + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + // release memory in IE + root = form = null; +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = ""; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + // release memory in IE + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "

    "; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && !Sizzle.isXML(context) ) { + // See if we find a selector to speed up + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + // Speed-up: Sizzle("TAG") + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + // Speed-up: Sizzle(".CLASS") + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + // Speed-up: Sizzle("body") + // The body element only exists once, optimize finding it + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + // Speed-up: Sizzle("#ID") + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + // release memory in IE + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector, + pseudoWorks = false; + + try { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + if ( matches ) { + Sizzle.matchesSelector = function( node, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + return matches.call( node, expr ); + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "
    "; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + // release memory in IE + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})(); + + +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + isSimple = /^.[^:#\[\.,]*$/, + slice = Array.prototype.slice, + POS = jQuery.expr.match.POS, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var ret = this.pushStack( "", "find", selector ), + length = 0; + + for ( var i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( var n = length; n < ret.length; n++ ) { + for ( var r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && jQuery.filter( selector, this ).length > 0; + }, + + closest: function( selectors, context ) { + var ret = [], i, l, cur = this[0]; + + if ( jQuery.isArray( selectors ) ) { + var match, selector, + matches = {}, + level = 1; + + if ( cur && selectors.length ) { + for ( i = 0, l = selectors.length; i < l; i++ ) { + selector = selectors[i]; + + if ( !matches[selector] ) { + matches[selector] = jQuery.expr.match.POS.test( selector ) ? + jQuery( selector, context || this.context ) : + selector; + } + } + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( selector in matches ) { + match = matches[selector]; + + if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) { + ret.push({ selector: selector, elem: cur, level: level }); + } + } + + cur = cur.parentNode; + level++; + } + } + + return ret; + } + + var pos = POS.test( selectors ) ? + jQuery( selectors, context || this.context ) : null; + + for ( i = 0, l = this.length; i < l; i++ ) { + cur = this[i]; + + while ( cur ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + + } else { + cur = cur.parentNode; + if ( !cur || !cur.ownerDocument || cur === context ) { + break; + } + } + } + } + + ret = ret.length > 1 ? jQuery.unique(ret) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + if ( !elem || typeof elem === "string" ) { + return jQuery.inArray( this[0], + // If it receives a string, the selector is used + // If it receives nothing, the siblings are used + elem ? jQuery( elem ) : this.parent().children() ); + } + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( elem.parentNode.firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ), + // The variable 'args' was introduced in + // https://github.com/jquery/jquery/commit/52a0238 + // to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed. + // http://code.google.com/p/v8/issues/detail?id=1050 + args = slice.call(arguments); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, args.join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return (elem === qualifier) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return (jQuery.inArray( elem, qualifier ) >= 0) === keep; + }); +} + + + + +var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rtagName = /<([\w:]+)/, + rtbody = /", "" ], + legend: [ 1, "
    ", "
    " ], + thead: [ 1, "", "
    " ], + tr: [ 2, "", "
    " ], + td: [ 3, "", "
    " ], + col: [ 2, "", "
    " ], + area: [ 1, "", "" ], + _default: [ 0, "", "" ] + }; + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize and