IOWA 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CONTRIBUTORS +14 -0
- data/README.md +23 -0
- data/RELEASE_NOTES +77 -0
- data/ToDo +21 -0
- data/components/CRUDList/CRUDList.html +13 -0
- data/components/CRUDList/CRUDList.iwa +136 -0
- data/components/Content/Content.html +0 -0
- data/components/Content/Content.iwa +143 -0
- data/components/Content/example/README +9 -0
- data/components/Content/example/cgi/iowa.cgi +10 -0
- data/components/Content/example/demo.rb +30 -0
- data/components/Content/example/doc/index.html +7 -0
- data/components/Content/example/iowa/Content.html +1 -0
- data/components/Content/example/iowa/Content.iwa +1 -0
- data/components/Content/example/iowa/Main.html +6 -0
- data/components/Content/example/iowa/Main.iwa +3 -0
- data/components/Content/example/iowa/_content/body +7 -0
- data/components/Content/example/iowa/_content/body2 +3 -0
- data/components/Content/example/iowa/_content/title +2 -0
- data/components/Content/example/iowa/app.cnf +8 -0
- data/components/Content/example/iowa/app.rb +8 -0
- data/components/Content/example/webrick.rb +38 -0
- data/components/Include/Include.html +1 -0
- data/components/Include/Include.iwa +30 -0
- data/components/Include/readme +5 -0
- data/components/JumpTo/JumpTo.html +8 -0
- data/components/JumpTo/JumpTo.iwa +8 -0
- data/components/JumpTo/readme +6 -0
- data/doc/Architecture.txt +6 -0
- data/doc/History.txt +33 -0
- data/doc/StandardDispatcher.txt +19 -0
- data/examples/blog/README +1 -0
- data/examples/hello_world/README +17 -0
- data/examples/hello_world/htdocs/hello_world_html.html +24 -0
- data/examples/hello_world/htdocs/hello_world_ruby.html +41 -0
- data/examples/hello_world/iowa/HelloWorld.html +14 -0
- data/examples/hello_world/iowa/HelloWorld.iwa +31 -0
- data/examples/hello_world/iowa/Index.html +16 -0
- data/examples/hello_world/iowa/Index.iwa +5 -0
- data/examples/hello_world/iowa/iowa_hello_world.cnf +19 -0
- data/examples/hello_world/iowa/iowa_hello_world.rb +5 -0
- data/examples/hello_world/iowa/mapfile.cnf +6 -0
- data/examples/hw1/iowa/Main.html +1 -0
- data/examples/hw1/iowa/README +9 -0
- data/examples/hw1/iowa/hw.rb +4 -0
- data/examples/hw2/iowa/Main.html +10 -0
- data/examples/hw2/iowa/Main.iwa +7 -0
- data/examples/hw2/iowa/README +16 -0
- data/examples/hw2/iowa/hw.rb +4 -0
- data/examples/hw3/iowa/Main.html +11 -0
- data/examples/hw3/iowa/Main.iwa +7 -0
- data/examples/hw3/iowa/README +22 -0
- data/examples/hw3/iowa/hw.rb +4 -0
- data/examples/hw4/iowa/Greetings.html +10 -0
- data/examples/hw4/iowa/Greetings.iwa +9 -0
- data/examples/hw4/iowa/Main.html +12 -0
- data/examples/hw4/iowa/Main.iwa +10 -0
- data/examples/hw4/iowa/README +28 -0
- data/examples/hw4/iowa/hw.rb +4 -0
- data/examples/hw5/iowa/Footer.html +2 -0
- data/examples/hw5/iowa/Greetings.html +4 -0
- data/examples/hw5/iowa/Greetings.iwa +12 -0
- data/examples/hw5/iowa/Header.html +6 -0
- data/examples/hw5/iowa/Main.html +12 -0
- data/examples/hw5/iowa/Main.iwa +13 -0
- data/examples/hw5/iowa/README +16 -0
- data/examples/hw5/iowa/hw.rb +4 -0
- data/examples/template_server/iowa/Content.iwa +121 -0
- data/examples/template_server/iowa/Content.view +0 -0
- data/examples/template_server/iowa/DBContentDispatcher.rb +112 -0
- data/examples/template_server/iowa/DBContentPage.html +3 -0
- data/examples/template_server/iowa/DBContentPage.iwa +85 -0
- data/examples/template_server/iowa/app.cnf +13 -0
- data/examples/template_server/iowa/app.rb +5 -0
- data/ext/Classifier/classifier.c +256 -0
- data/ext/Classifier/ext_help.h +14 -0
- data/ext/Classifier/extconf.rb +5 -0
- data/ext/Classifier/tst.h +40 -0
- data/ext/Classifier/tst_cleanup.c +24 -0
- data/ext/Classifier/tst_delete.c +146 -0
- data/ext/Classifier/tst_grow_node_free_list.c +38 -0
- data/ext/Classifier/tst_init.c +41 -0
- data/ext/Classifier/tst_insert.c +192 -0
- data/ext/Classifier/tst_search.c +68 -0
- data/ext/http11/README +11 -0
- data/ext/http11/ext_help.h +15 -0
- data/ext/http11/extconf.rb +5 -0
- data/ext/http11/http11.c +534 -0
- data/ext/http11/http11.c.dif +422 -0
- data/ext/http11/http11_parser.c +1243 -0
- data/ext/http11/http11_parser.c.dif +193 -0
- data/ext/http11/http11_parser.h +49 -0
- data/ext/http11/http11_parser.h.dif +20 -0
- data/ext/http11/http11_parser.rl +192 -0
- data/ext/httpmachine/Makefile +149 -0
- data/ext/httpmachine/extconf.rb +33 -0
- data/ext/httpmachine/http.cpp +430 -0
- data/ext/httpmachine/http.h +156 -0
- data/ext/httpmachine/rubyhttp.cpp +165 -0
- data/external/mime-types/LICENCE +18 -0
- data/external/mime-types/README +46 -0
- data/external/mime-types/doc/classes/MIME.html +120 -0
- data/external/mime-types/doc/classes/MIME/InvalidContentType.html +119 -0
- data/external/mime-types/doc/classes/MIME/Type.html +866 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000010.html +22 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000011.html +18 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000012.html +19 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000013.html +18 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000014.html +26 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000015.html +33 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000016.html +27 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000017.html +35 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000018.html +34 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000019.html +24 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000020.html +38 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000021.html +22 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000022.html +18 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000023.html +18 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000024.html +18 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000025.html +18 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000026.html +18 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000027.html +18 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000028.html +18 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000029.html +18 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000030.html +19 -0
- data/external/mime-types/doc/classes/MIME/Type.src/M000031.html +26 -0
- data/external/mime-types/doc/classes/MIME/Types.html +459 -0
- data/external/mime-types/doc/classes/MIME/Types.src/M000001.html +19 -0
- data/external/mime-types/doc/classes/MIME/Types.src/M000002.html +32 -0
- data/external/mime-types/doc/classes/MIME/Types.src/M000003.html +21 -0
- data/external/mime-types/doc/classes/MIME/Types.src/M000004.html +18 -0
- data/external/mime-types/doc/classes/MIME/Types.src/M000005.html +26 -0
- data/external/mime-types/doc/classes/MIME/Types.src/M000006.html +18 -0
- data/external/mime-types/doc/classes/MIME/Types.src/M000007.html +18 -0
- data/external/mime-types/doc/classes/MIME/Types.src/M000008.html +18 -0
- data/external/mime-types/doc/classes/MIME/Types.src/M000009.html +18 -0
- data/external/mime-types/doc/created.rid +1 -0
- data/external/mime-types/doc/files/ChangeLog.html +320 -0
- data/external/mime-types/doc/files/README.html +145 -0
- data/external/mime-types/doc/files/lib/mime/types_rb.html +101 -0
- data/external/mime-types/doc/fr_class_index.html +30 -0
- data/external/mime-types/doc/fr_file_index.html +29 -0
- data/external/mime-types/doc/fr_method_index.html +57 -0
- data/external/mime-types/doc/index.html +24 -0
- data/external/mime-types/doc/rdoc-style.css +208 -0
- data/external/mime-types/lib/mime/types.rb +1558 -0
- data/external/package.rb +672 -0
- data/external/test_support.rb +95 -0
- data/external/tmail/README +7 -0
- data/external/tmail/tmail.rb +4 -0
- data/external/tmail/tmail/address.rb +222 -0
- data/external/tmail/tmail/base64.rb +52 -0
- data/external/tmail/tmail/compat.rb +39 -0
- data/external/tmail/tmail/config.rb +50 -0
- data/external/tmail/tmail/encode.rb +447 -0
- data/external/tmail/tmail/header.rb +895 -0
- data/external/tmail/tmail/info.rb +14 -0
- data/external/tmail/tmail/loader.rb +1 -0
- data/external/tmail/tmail/mail.rb +869 -0
- data/external/tmail/tmail/mailbox.rb +386 -0
- data/external/tmail/tmail/mbox.rb +1 -0
- data/external/tmail/tmail/net.rb +260 -0
- data/external/tmail/tmail/obsolete.rb +123 -0
- data/external/tmail/tmail/parser.rb +1475 -0
- data/external/tmail/tmail/parser.y +372 -0
- data/external/tmail/tmail/port.rb +356 -0
- data/external/tmail/tmail/scanner.rb +17 -0
- data/external/tmail/tmail/scanner_r.rb +243 -0
- data/external/tmail/tmail/stringio.rb +256 -0
- data/external/tmail/tmail/textutils.rb +197 -0
- data/external/tmail/tmail/tmail.rb +1 -0
- data/external/tmail/tmail/utils.rb +23 -0
- data/external/win32-process/README +133 -0
- data/external/win32-process/lib/win32/process.rb +561 -0
- data/external/windows-pr/README +145 -0
- data/external/windows-pr/doc/conversion_guide.txt +25 -0
- data/external/windows-pr/lib/windows/clipboard.rb +72 -0
- data/external/windows-pr/lib/windows/console.rb +323 -0
- data/external/windows-pr/lib/windows/device_io.rb +88 -0
- data/external/windows-pr/lib/windows/directory.rb +80 -0
- data/external/windows-pr/lib/windows/error.rb +313 -0
- data/external/windows-pr/lib/windows/eventlog.rb +120 -0
- data/external/windows-pr/lib/windows/file.rb +349 -0
- data/external/windows-pr/lib/windows/filesystem.rb +16 -0
- data/external/windows-pr/lib/windows/handle.rb +31 -0
- data/external/windows-pr/lib/windows/library.rb +76 -0
- data/external/windows-pr/lib/windows/limits.rb +13 -0
- data/external/windows-pr/lib/windows/memory.rb +117 -0
- data/external/windows-pr/lib/windows/msvcrt/buffer.rb +48 -0
- data/external/windows-pr/lib/windows/msvcrt/file.rb +18 -0
- data/external/windows-pr/lib/windows/msvcrt/string.rb +46 -0
- data/external/windows-pr/lib/windows/national.rb +557 -0
- data/external/windows-pr/lib/windows/path.rb +296 -0
- data/external/windows-pr/lib/windows/pipe.rb +77 -0
- data/external/windows-pr/lib/windows/process.rb +171 -0
- data/external/windows-pr/lib/windows/registry.rb +238 -0
- data/external/windows-pr/lib/windows/security.rb +89 -0
- data/external/windows-pr/lib/windows/service.rb +183 -0
- data/external/windows-pr/lib/windows/shell.rb +88 -0
- data/external/windows-pr/lib/windows/sound.rb +52 -0
- data/external/windows-pr/lib/windows/synchronize.rb +161 -0
- data/external/windows-pr/lib/windows/system_info.rb +70 -0
- data/external/windows-pr/lib/windows/unicode.rb +138 -0
- data/external/windows-pr/lib/windows/window.rb +22 -0
- data/iowa.gemspec +45 -0
- data/microprojects/DiskCache/LICENSE +28 -0
- data/microprojects/DiskCache/README +17 -0
- data/microprojects/DiskCache/external/package.rb +608 -0
- data/microprojects/DiskCache/external/test_support.rb +8 -0
- data/microprojects/DiskCache/setup.rb +22 -0
- data/microprojects/DiskCache/src/iowa/Association.rb +67 -0
- data/microprojects/DiskCache/src/iowa/Constants.rb +159 -0
- data/microprojects/DiskCache/src/iowa/DiskStore.rb +377 -0
- data/microprojects/DiskCache/src/iowa/Hash.rb +63 -0
- data/microprojects/DiskCache/src/iowa/Lockfile.rb +575 -0
- data/microprojects/DiskCache/src/iowa/Mutex.rb +142 -0
- data/microprojects/DiskCache/src/iowa/caches/DiskCache.rb +605 -0
- data/microprojects/DiskCache/src/iowa/caches/LRUCache.rb +287 -0
- data/microprojects/DiskCache/test/TC_DiskCache.rb +218 -0
- data/microprojects/LRUCache/LICENSE +28 -0
- data/microprojects/LRUCache/README +13 -0
- data/microprojects/LRUCache/external/package.rb +608 -0
- data/microprojects/LRUCache/external/test_support.rb +8 -0
- data/microprojects/LRUCache/setup.rb +22 -0
- data/microprojects/LRUCache/src/iowa/Association.rb +57 -0
- data/microprojects/LRUCache/src/iowa/Constants.rb +159 -0
- data/microprojects/LRUCache/src/iowa/Hash.rb +63 -0
- data/microprojects/LRUCache/src/iowa/Mutex.rb +129 -0
- data/microprojects/LRUCache/src/iowa/caches/LRUCache.rb +287 -0
- data/microprojects/LRUCache/test/TC_LRUCache.rb +65 -0
- data/microprojects/LinkedList/LICENSE +28 -0
- data/microprojects/LinkedList/README +13 -0
- data/microprojects/LinkedList/external/package.rb +608 -0
- data/microprojects/LinkedList/external/test_support.rb +8 -0
- data/microprojects/LinkedList/setup.rb +22 -0
- data/microprojects/LinkedList/src/iowa/LinkedList.rb +165 -0
- data/microprojects/LinkedList/test/TC_LinkedList.rb +42 -0
- data/microprojects/README +8 -0
- data/setup.rb +116 -0
- data/share/iowa/app_skeleton/Main.html +8 -0
- data/share/iowa/app_skeleton/Main.iwa +15 -0
- data/share/iowa/app_skeleton/app.cnf +48 -0
- data/share/iowa/app_skeleton/app.rb +58 -0
- data/share/iowa/app_skeleton/models/model.rb +49 -0
- data/src/ihc.rb +223 -0
- data/src/iowa.cgi +29 -0
- data/src/iowa.rb +637 -0
- data/src/iowa/AbstractCache.rb +96 -0
- data/src/iowa/AcceptLanguage.rb +76 -0
- data/src/iowa/Application.rb +928 -0
- data/src/iowa/ApplicationStats.rb +72 -0
- data/src/iowa/Association.rb +67 -0
- data/src/iowa/BindingsParser.rb +62 -0
- data/src/iowa/Breakpoint.rb +273 -0
- data/src/iowa/CSS.rb +564 -0
- data/src/iowa/Client.rb +192 -0
- data/src/iowa/Component.rb +405 -0
- data/src/iowa/ComponentProxy.rb +26 -0
- data/src/iowa/Config.rb +21 -0
- data/src/iowa/Constants.rb +226 -0
- data/src/iowa/Context.rb +218 -0
- data/src/iowa/ContextLogger.rb +16 -0
- data/src/iowa/DbPool.rb +222 -0
- data/src/iowa/DetachedComponent.rb +18 -0
- data/src/iowa/Dispatcher.rb +27 -0
- data/src/iowa/DynamicElements.rb +471 -0
- data/src/iowa/Element.rb +100 -0
- data/src/iowa/Email.rb +287 -0
- data/src/iowa/Extensions/AllExtensions.rb +4 -0
- data/src/iowa/Extensions/Class.rb +94 -0
- data/src/iowa/Extensions/Date.rb +88 -0
- data/src/iowa/Extensions/DateTime.rb +88 -0
- data/src/iowa/Extensions/Hash.rb +22 -0
- data/src/iowa/Extensions/Kernel.rb +6 -0
- data/src/iowa/Extensions/Numeric.rb +47 -0
- data/src/iowa/Extensions/Object.rb +11 -0
- data/src/iowa/Extensions/String.rb +60 -0
- data/src/iowa/Extensions/Time.rb +89 -0
- data/src/iowa/Extensions/TimeExtensions.rb +6 -0
- data/src/iowa/Form.rb +368 -0
- data/src/iowa/Hash.rb +85 -0
- data/src/iowa/ISAAC.rb +175 -0
- data/src/iowa/ImageSize.rb +279 -0
- data/src/iowa/IowaComponentMixins.rb +7 -0
- data/src/iowa/JSON-lexer.rb +296 -0
- data/src/iowa/JSON-objects.rb +201 -0
- data/src/iowa/KeyValueCoding.rb +91 -0
- data/src/iowa/LinkedList.rb +175 -0
- data/src/iowa/Loader.rb +22 -0
- data/src/iowa/Lockfile.rb +575 -0
- data/src/iowa/Logger.rb +74 -0
- data/src/iowa/Monkey.rb +20 -0
- data/src/iowa/Mutex.rb +142 -0
- data/src/iowa/Policy.rb +70 -0
- data/src/iowa/Pool.rb +243 -0
- data/src/iowa/PrettyException.rb +1091 -0
- data/src/iowa/Request.rb +244 -0
- data/src/iowa/Response.rb +133 -0
- data/src/iowa/Session.rb +354 -0
- data/src/iowa/SessionStats.rb +78 -0
- data/src/iowa/String.rb +65 -0
- data/src/iowa/Tag.rb +101 -0
- data/src/iowa/TemplateParser.rb +236 -0
- data/src/iowa/Util.rb +314 -0
- data/src/iowa/Webcache.rb +122 -0
- data/src/iowa/caches/BiLevelCache.rb +65 -0
- data/src/iowa/caches/ClassLimitedCache.rb +67 -0
- data/src/iowa/caches/DiskCache.rb +609 -0
- data/src/iowa/caches/DiskStore.rb +380 -0
- data/src/iowa/caches/LRUCache-alternative.rb +155 -0
- data/src/iowa/caches/LRUCache.rb +290 -0
- data/src/iowa/caches/SimpleLRUCache.rb +112 -0
- data/src/iowa/dispatchers/StandardDispatcher.rb +396 -0
- data/src/iowa/dispatchers/StandardDispatcherWithClassifier.rb +93 -0
- data/src/iowa/js/iowa_jsonrpc.js +381 -0
- data/src/iowa/js/jsonrpc.js +187 -0
- data/src/iowa/js/jsonrpc_async.js +261 -0
- data/src/iowa/loaders/DiskLoader.rb +50 -0
- data/src/iowa/loggers/Analogger.rb +54 -0
- data/src/iowa/loggers/AsyncLogger.rb +54 -0
- data/src/iowa/loggers/BitBucket.rb +38 -0
- data/src/iowa/loggers/Log4R.rb +13 -0
- data/src/iowa/loggers/Log4rLogger.rb +48 -0
- data/src/iowa/loggers/Logger.rb +29 -0
- data/src/iowa/loggers/RubyLogger.rb +9 -0
- data/src/iowa/pools/DBConnectionPool.rb +53 -0
- data/src/iowa/request/Apache.rb +90 -0
- data/src/iowa/request/EMHybrid.rb +59 -0
- data/src/iowa/request/ENV.rb +80 -0
- data/src/iowa/request/FCGI.rb +68 -0
- data/src/iowa/request/HTTPMachine.rb +75 -0
- data/src/iowa/request/Mongrel.rb +68 -0
- data/src/iowa/request/WEBrick.rb +48 -0
- data/src/iowa/version.rb +3 -0
- data/src/iowa/webrick/HTTPServer.rb +43 -0
- data/src/iowa/webrick/WEBrickServlet.rb +28 -0
- data/src/iowa_fcgi_handler.rb +101 -0
- data/src/iowa_httpmachine.rb +141 -0
- data/src/iowa_hybrid.rb +193 -0
- data/src/iowa_hybrid_cluster.rb +231 -0
- data/src/iowa_mongrel.rb +136 -0
- data/src/iowa_webrick.rb +194 -0
- data/src/iowa_webrick_legacy.rb +104 -0
- data/src/mod_iowa.rb +104 -0
- data/tcss.rb +61 -0
- data/test/README.windows +12 -0
- data/test/TC_AcceptLanguage.rb +61 -0
- data/test/TC_AppConfig.rb +43 -0
- data/test/TC_AppConfig/cgi/iowa.cgi +7 -0
- data/test/TC_AppConfig/doc/index.html +1 -0
- data/test/TC_AppConfig/iowa/Main.html +4 -0
- data/test/TC_AppConfig/iowa/Main.iwa +17 -0
- data/test/TC_AppConfig/iowa/README +1 -0
- data/test/TC_AppConfig/iowa/app.cnf +19 -0
- data/test/TC_AppConfig/iowa/app.rb +8 -0
- data/test/TC_AppConfig/webrick.rb +38 -0
- data/test/TC_Association.rb +29 -0
- data/test/TC_BiLevelCache.rb +71 -0
- data/test/TC_CGI_Adaptor.rb +55 -0
- data/test/TC_CGI_Adaptor/cgi/iowa.cgi +7 -0
- data/test/TC_CGI_Adaptor/doc/index.html +1 -0
- data/test/TC_CGI_Adaptor/iowa/Main.html +8 -0
- data/test/TC_CGI_Adaptor/iowa/Main.iwa +14 -0
- data/test/TC_CGI_Adaptor/iowa/README +1 -0
- data/test/TC_CGI_Adaptor/iowa/app.cnf +8 -0
- data/test/TC_CGI_Adaptor/iowa/app.rb +8 -0
- data/test/TC_CGI_Adaptor/webrick.rb +38 -0
- data/test/TC_CSS.rb +660 -0
- data/test/TC_ClassLimitedCache.rb +89 -0
- data/test/TC_Classifier.rb +80 -0
- data/test/TC_DbPool.rb +127 -0
- data/test/TC_DiskCache.rb +218 -0
- data/test/TC_Hybrid.rb +58 -0
- data/test/TC_Hybrid/doc/thing.txt +1 -0
- data/test/TC_Hybrid/iowa/Main.html +3 -0
- data/test/TC_Hybrid/iowa/Main.iwa +7 -0
- data/test/TC_Hybrid/iowa/NewPage.html +1 -0
- data/test/TC_Hybrid/iowa/NewPage.iwa +5 -0
- data/test/TC_Hybrid/iowa/app.cnf +10 -0
- data/test/TC_Hybrid/iowa/app.rb +5 -0
- data/test/TC_IOWAFunctions/doc/Ajax1.html +1 -0
- data/test/TC_IOWAFunctions/doc/Ajax1.iwa +1 -0
- data/test/TC_IOWAFunctions/doc/index.html +1 -0
- data/test/TC_IOWAFunctions/doc/js/dojo.js +9686 -0
- data/test/TC_IOWAFunctions/iowa/Ajax1.html +40 -0
- data/test/TC_IOWAFunctions/iowa/Ajax1.iwa +23 -0
- data/test/TC_IOWAFunctions/iowa/AjaxWidget.iwa +19 -0
- data/test/TC_IOWAFunctions/iowa/AjaxWidget.view +22 -0
- data/test/TC_IOWAFunctions/iowa/DanielTest.html +9 -0
- data/test/TC_IOWAFunctions/iowa/DanielTest.iwa +10 -0
- data/test/TC_IOWAFunctions/iowa/IntervalWidget.html +1 -0
- data/test/TC_IOWAFunctions/iowa/IntervalWidget.iwa +14 -0
- data/test/TC_IOWAFunctions/iowa/Main.html +1 -0
- data/test/TC_IOWAFunctions/iowa/NestedRepeat1.html +12 -0
- data/test/TC_IOWAFunctions/iowa/NestedRepeat1.iwa +24 -0
- data/test/TC_IOWAFunctions/iowa/RPCResponse.iwa +7 -0
- data/test/TC_IOWAFunctions/iowa/RPCResponse.view +1 -0
- data/test/TC_IOWAFunctions/iowa/Repeat1.iwa +31 -0
- data/test/TC_IOWAFunctions/iowa/Repeat1.view +9 -0
- data/test/TC_IOWAFunctions/iowa/Repeat2.iwa +32 -0
- data/test/TC_IOWAFunctions/iowa/Repeat2.view +11 -0
- data/test/TC_IOWAFunctions/iowa/Repeat3.iwa +44 -0
- data/test/TC_IOWAFunctions/iowa/Repeat3.view +18 -0
- data/test/TC_IOWAFunctions/iowa/TesCon.html +1 -0
- data/test/TC_IOWAFunctions/iowa/TesCon.iwa +5 -0
- data/test/TC_IOWAFunctions/iowa/app.cnf +19 -0
- data/test/TC_IOWAFunctions/iowa/app.rb +9 -0
- data/test/TC_IOWAFunctions/iowa/mapfile.cnf +8 -0
- data/test/TC_ISAAC.rb +52 -0
- data/test/TC_ImageSize.rb +84 -0
- data/test/TC_ImageSize/img.bmp +0 -0
- data/test/TC_ImageSize/img.gif +0 -0
- data/test/TC_ImageSize/img.jpg +0 -0
- data/test/TC_ImageSize/img.pcx +0 -0
- data/test/TC_ImageSize/img.pgm +7144 -0
- data/test/TC_ImageSize/img.png +0 -0
- data/test/TC_ImageSize/img.ppm +0 -0
- data/test/TC_ImageSize/img.psd +0 -0
- data/test/TC_ImageSize/img.tiff +0 -0
- data/test/TC_ImageSize/img.xbm +22 -0
- data/test/TC_KeyValueCoding.rb +35 -0
- data/test/TC_LRUCache.rb +296 -0
- data/test/TC_LinkedList.rb +46 -0
- data/test/TC_Lockfile.rb +106 -0
- data/test/TC_Minimal.rb +45 -0
- data/test/TC_Minimal/app.cnf +8 -0
- data/test/TC_Minimal/cgi-bin/iowa.cgi +11 -0
- data/test/TC_Minimal/doc/index.html +1 -0
- data/test/TC_Minimal/iowa/Main.html +1 -0
- data/test/TC_Minimal/iowa/README +1 -0
- data/test/TC_Minimal/iowa/app.rb +3 -0
- data/test/TC_Minimal/webrick.rb +38 -0
- data/test/TC_Mongrel.rb +58 -0
- data/test/TC_Mongrel/doc/thing.txt +1 -0
- data/test/TC_Mongrel/iowa/Main.html +3 -0
- data/test/TC_Mongrel/iowa/Main.iwa +7 -0
- data/test/TC_Mongrel/iowa/NewPage.html +1 -0
- data/test/TC_Mongrel/iowa/NewPage.iwa +5 -0
- data/test/TC_Mongrel/iowa/app.cnf +10 -0
- data/test/TC_Mongrel/iowa/app.rb +5 -0
- data/test/TC_NoSubclass.rb +56 -0
- data/test/TC_NoSubclass/cgi/iowa.cgi +7 -0
- data/test/TC_NoSubclass/doc/index.html +1 -0
- data/test/TC_NoSubclass/iowa/Main.html +8 -0
- data/test/TC_NoSubclass/iowa/Main.iwa +14 -0
- data/test/TC_NoSubclass/iowa/README +1 -0
- data/test/TC_NoSubclass/iowa/app.cnf +8 -0
- data/test/TC_NoSubclass/iowa/app.rb +5 -0
- data/test/TC_NoSubclass/webrick.rb +38 -0
- data/test/TC_Pool.rb +139 -0
- data/test/TC_RenderedCache/doc/thing.txt +1 -0
- data/test/TC_RenderedCache/iowa/BigPage.html +1 -0
- data/test/TC_RenderedCache/iowa/BigPage.iwa +12 -0
- data/test/TC_RenderedCache/iowa/Main.html +3 -0
- data/test/TC_RenderedCache/iowa/Main.iwa +7 -0
- data/test/TC_RenderedCache/iowa/NewPage.html +1 -0
- data/test/TC_RenderedCache/iowa/NewPage.iwa +11 -0
- data/test/TC_RenderedCache/iowa/app.cnf +13 -0
- data/test/TC_RenderedCache/iowa/app.rb +5 -0
- data/test/TC_RenderedCache/iowa/mapfile.map +4 -0
- data/test/TC_ResourceURL.rb +73 -0
- data/test/TC_ResourceURL/iowa/Main.html +2 -0
- data/test/TC_ResourceURL/iowa/Main.iwa +10 -0
- data/test/TC_ResourceURL/iowa/app.cnf +10 -0
- data/test/TC_ResourceURL/iowa/app.rb +5 -0
- data/test/TC_SimpleDetached.rb +41 -0
- data/test/TC_StandardDispatcher.rb +362 -0
- data/test/TC_StandardDispatcherWithClassifier.rb +358 -0
- data/test/TC_String.rb +24 -0
- data/test/TC_Tag.rb +41 -0
- data/test/TC_Webrick.rb +56 -0
- data/test/TC_Webrick/doc/thing.txt +1 -0
- data/test/TC_Webrick/iowa/Main.html +3 -0
- data/test/TC_Webrick/iowa/Main.iwa +7 -0
- data/test/TC_Webrick/iowa/NewPage.html +1 -0
- data/test/TC_Webrick/iowa/NewPage.iwa +5 -0
- data/test/TC_Webrick/iowa/app.cnf +10 -0
- data/test/TC_Webrick/iowa/app.rb +5 -0
- data/test/tc_template.rb +15 -0
- data/test/tests.conf +19 -0
- data/utils/CVS/Entries +3 -0
- data/utils/CVS/Repository +1 -0
- data/utils/CVS/Root +1 -0
- data/utils/QuickCert-1.0.2.tar.gz +0 -0
- data/utils/QuickCert-1.0.2/InstalledFiles +7 -0
- data/utils/QuickCert-1.0.2/MANIFEST +10 -0
- data/utils/QuickCert-1.0.2/Makefile +15 -0
- data/utils/QuickCert-1.0.2/README +56 -0
- data/utils/QuickCert-1.0.2/bin/QuickCert +355 -0
- data/utils/QuickCert-1.0.2/config.save +12 -0
- data/utils/QuickCert-1.0.2/data/examples/ruby/QuickCert/README +60 -0
- data/utils/QuickCert-1.0.2/data/examples/ruby/QuickCert/drbssl_c.rb +26 -0
- data/utils/QuickCert-1.0.2/data/examples/ruby/QuickCert/drbssl_s.rb +35 -0
- data/utils/QuickCert-1.0.2/data/examples/ruby/QuickCert/qc_config +21 -0
- data/utils/QuickCert-1.0.2/lib/QuickCert/defaults.rb +28 -0
- data/utils/QuickCert-1.0.2/setup.rb +1312 -0
- data/utils/iowa_apps +422 -0
- data/utils/smtp_sink.rb +9 -0
- data/utils/startup_template.cnf +27 -0
- data/utils/startup_template.rb +73 -0
- metadata +551 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#
|
|
2
|
+
# textutils.rb
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 1998-2004 Minero Aoki
|
|
5
|
+
#
|
|
6
|
+
# This program is free software.
|
|
7
|
+
# You can distribute/modify this program under the terms of
|
|
8
|
+
# the GNU Lesser General Public License version 2.1.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
module TMail
|
|
12
|
+
|
|
13
|
+
class SyntaxError < StandardError; end
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
module TextUtils
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def new_boundary
|
|
21
|
+
'mimepart_' + random_tag()
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
@@uniq = 0
|
|
25
|
+
|
|
26
|
+
def random_tag
|
|
27
|
+
@@uniq += 1
|
|
28
|
+
t = Time.now
|
|
29
|
+
sprintf('%x%x_%x%x%d%x',
|
|
30
|
+
t.to_i, t.tv_usec,
|
|
31
|
+
$$, Thread.current.id, @@uniq, rand(255))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
aspecial = '()<>[]:;.@\\,"'
|
|
35
|
+
tspecial = '()<>[];:@\\,"/?='
|
|
36
|
+
lwsp = " \t\r\n"
|
|
37
|
+
control = '\x00-\x1f\x7f-\xff'
|
|
38
|
+
|
|
39
|
+
ATOM_UNSAFE = /[#{Regexp.quote aspecial}#{control}#{lwsp}]/n
|
|
40
|
+
PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n
|
|
41
|
+
TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{lwsp}]/n
|
|
42
|
+
CONTROL_CHAR = /[#{control}]/n
|
|
43
|
+
RFC2231_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{lwsp}\*\'\%]/n
|
|
44
|
+
|
|
45
|
+
def atom_safe?(str)
|
|
46
|
+
ATOM_UNSAFE !~ str
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def quote_atom(str)
|
|
50
|
+
(ATOM_UNSAFE =~ str) ? dquote(str) : str
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def quote_phrase(str)
|
|
54
|
+
(PHRASE_UNSAFE =~ str) ? dquote(str) : str
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def token_safe?(str)
|
|
58
|
+
TOKEN_UNSAFE !~ str
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def quote_token(str)
|
|
62
|
+
(TOKEN_UNSAFE =~ str) ? dquote(str) : str
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def dquote(str)
|
|
66
|
+
'"' + str.gsub(/["\\]/n) {|s| '\\' + s } + '"'
|
|
67
|
+
end
|
|
68
|
+
private :dquote
|
|
69
|
+
|
|
70
|
+
def join_domain(arr)
|
|
71
|
+
arr.map {|i| (/\A\[.*\]\z/ =~ i) ? i : quote_atom(i) }.join('.')
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
ZONESTR_TABLE = {
|
|
75
|
+
'jst' => 9 * 60,
|
|
76
|
+
'eet' => 2 * 60,
|
|
77
|
+
'bst' => 1 * 60,
|
|
78
|
+
'met' => 1 * 60,
|
|
79
|
+
'gmt' => 0,
|
|
80
|
+
'utc' => 0,
|
|
81
|
+
'ut' => 0,
|
|
82
|
+
'nst' => -(3 * 60 + 30),
|
|
83
|
+
'ast' => -4 * 60,
|
|
84
|
+
'edt' => -4 * 60,
|
|
85
|
+
'est' => -5 * 60,
|
|
86
|
+
'cdt' => -5 * 60,
|
|
87
|
+
'cst' => -6 * 60,
|
|
88
|
+
'mdt' => -6 * 60,
|
|
89
|
+
'mst' => -7 * 60,
|
|
90
|
+
'pdt' => -7 * 60,
|
|
91
|
+
'pst' => -8 * 60,
|
|
92
|
+
'a' => -1 * 60,
|
|
93
|
+
'b' => -2 * 60,
|
|
94
|
+
'c' => -3 * 60,
|
|
95
|
+
'd' => -4 * 60,
|
|
96
|
+
'e' => -5 * 60,
|
|
97
|
+
'f' => -6 * 60,
|
|
98
|
+
'g' => -7 * 60,
|
|
99
|
+
'h' => -8 * 60,
|
|
100
|
+
'i' => -9 * 60,
|
|
101
|
+
# j not use
|
|
102
|
+
'k' => -10 * 60,
|
|
103
|
+
'l' => -11 * 60,
|
|
104
|
+
'm' => -12 * 60,
|
|
105
|
+
'n' => 1 * 60,
|
|
106
|
+
'o' => 2 * 60,
|
|
107
|
+
'p' => 3 * 60,
|
|
108
|
+
'q' => 4 * 60,
|
|
109
|
+
'r' => 5 * 60,
|
|
110
|
+
's' => 6 * 60,
|
|
111
|
+
't' => 7 * 60,
|
|
112
|
+
'u' => 8 * 60,
|
|
113
|
+
'v' => 9 * 60,
|
|
114
|
+
'w' => 10 * 60,
|
|
115
|
+
'x' => 11 * 60,
|
|
116
|
+
'y' => 12 * 60,
|
|
117
|
+
'z' => 0 * 60
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
def timezone_string_to_unixtime(str)
|
|
121
|
+
if m = /([\+\-])(\d\d?)(\d\d)/.match(str)
|
|
122
|
+
sec = (m[2].to_i * 60 + m[3].to_i) * 60
|
|
123
|
+
(m[1] == '-') ? -sec : sec
|
|
124
|
+
else
|
|
125
|
+
min = ZONESTR_TABLE[str.downcase] or
|
|
126
|
+
raise SyntaxError, "wrong timezone format '#{str}'"
|
|
127
|
+
min * 60
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
WDAY = %w( Sun Mon Tue Wed Thu Fri Sat TMailBUG )
|
|
132
|
+
MONTH = %w( TMailBUG Jan Feb Mar Apr May Jun
|
|
133
|
+
Jul Aug Sep Oct Nov Dec TMailBUG )
|
|
134
|
+
|
|
135
|
+
def time2str(tm)
|
|
136
|
+
# [ruby-list:7928]
|
|
137
|
+
gmt = Time.at(tm.to_i)
|
|
138
|
+
gmt.gmtime
|
|
139
|
+
offset = tm.to_i - Time.local(*gmt.to_a[0,6].reverse).to_i
|
|
140
|
+
|
|
141
|
+
# DO NOT USE strftime: setlocale() breaks it
|
|
142
|
+
sprintf '%s, %s %s %d %02d:%02d:%02d %+.2d%.2d',
|
|
143
|
+
WDAY[tm.wday], tm.mday, MONTH[tm.month],
|
|
144
|
+
tm.year, tm.hour, tm.min, tm.sec,
|
|
145
|
+
*(offset / 60).divmod(60)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
MESSAGE_ID = /<[^\@>]+\@[^>\@]+>/
|
|
149
|
+
|
|
150
|
+
def message_id?(str)
|
|
151
|
+
MESSAGE_ID =~ str
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def mime_encoded?(str)
|
|
155
|
+
/=\?[^\s?=]+\?[QB]\?[^\s?=]+\?=/i =~ str
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def decode_params(hash)
|
|
159
|
+
new = Hash.new
|
|
160
|
+
encoded = nil
|
|
161
|
+
hash.each do |key, value|
|
|
162
|
+
if m = /\*(?:(\d+)\*)?\z/.match(key)
|
|
163
|
+
((encoded ||= {})[m.pre_match] ||= [])[(m[1] || 0).to_i] = value
|
|
164
|
+
else
|
|
165
|
+
new[key] = to_kcode(value)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
if encoded
|
|
169
|
+
encoded.each do |key, strings|
|
|
170
|
+
new[key] = decode_RFC2231(strings.join(''))
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
new
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
NKF_FLAGS = {
|
|
178
|
+
'EUC' => '-e -m',
|
|
179
|
+
'SJIS' => '-s -m'
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
def to_kcode(str)
|
|
183
|
+
flag = NKF_FLAGS[$KCODE] or return str
|
|
184
|
+
NKF.nkf(flag, str)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
RFC2231_ENCODED = /\A(?:iso-2022-jp|euc-jp|shift_jis|us-ascii)?'[a-z]*'/in
|
|
188
|
+
|
|
189
|
+
def decode_RFC2231(str)
|
|
190
|
+
m = RFC2231_ENCODED.match(str) or return str
|
|
191
|
+
NKF.nkf(NKF_FLAGS[$KCODE],
|
|
192
|
+
m.post_match.gsub(/%[\da-f]{2}/in) {|s| s[1,2].hex.chr })
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'tmail'
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#
|
|
2
|
+
# utils.rb
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 1998-2004 Minero Aoki
|
|
5
|
+
#
|
|
6
|
+
# This program is free software.
|
|
7
|
+
# You can distribute/modify this program under the terms of
|
|
8
|
+
# the GNU Lesser General Public License version 2.1.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
require 'tmail/textutils'
|
|
12
|
+
require 'socket'
|
|
13
|
+
|
|
14
|
+
module TMail
|
|
15
|
+
|
|
16
|
+
extend TextUtils
|
|
17
|
+
|
|
18
|
+
def TMail.new_message_id(fqdn = nil)
|
|
19
|
+
fqdn ||= ::Socket.gethostname
|
|
20
|
+
"<#{random_tag()}@#{fqdn}.tmail>"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
This is a stripped down package of Daniel Berger's win32-process library.
|
|
2
|
+
This is version 0.5.0.
|
|
3
|
+
|
|
4
|
+
The original can be found at Rubyforge at:
|
|
5
|
+
|
|
6
|
+
http://rubyforge.org/projects/win32utils
|
|
7
|
+
|
|
8
|
+
This library will be installed only if it is not already present on the
|
|
9
|
+
appropriate platform. The main setup.rb for IOWA takes care of it, including
|
|
10
|
+
generation of ri documentation.
|
|
11
|
+
|
|
12
|
+
Below is the original README
|
|
13
|
+
|
|
14
|
+
-----
|
|
15
|
+
|
|
16
|
+
= Description
|
|
17
|
+
This package provides the fork, wait, wait2, waitpid, and waitpid2 methods
|
|
18
|
+
for MS Windows. In addition, it provides a different implementation of the
|
|
19
|
+
kill method.
|
|
20
|
+
|
|
21
|
+
= Prerequisites
|
|
22
|
+
Ruby 1.8.2 or later.
|
|
23
|
+
The 'windows-pr' package 0.5.2 or later.
|
|
24
|
+
The 'sys-proctable' package 0.7.0 or later (test suite only).
|
|
25
|
+
|
|
26
|
+
= Supported Platforms
|
|
27
|
+
This code is supported on Windows NT, Windows 2000 or Windows XP Pro only.
|
|
28
|
+
It may work on Windows XP Home, but it is not officially supported for that
|
|
29
|
+
platform.
|
|
30
|
+
|
|
31
|
+
= Installation Instructions
|
|
32
|
+
== Manual Installation
|
|
33
|
+
ruby test/tc_process.rb (optional)
|
|
34
|
+
ruby install.rb
|
|
35
|
+
== Gem Installation
|
|
36
|
+
ruby win32-process.gemspec
|
|
37
|
+
win32-process-X.Y.Z-mswin32.gem
|
|
38
|
+
|
|
39
|
+
= Synopsis
|
|
40
|
+
require 'win32/process'
|
|
41
|
+
|
|
42
|
+
Process.fork{
|
|
43
|
+
3.times{
|
|
44
|
+
puts 'In the child'
|
|
45
|
+
sleep 1
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Process.wait
|
|
50
|
+
puts 'Done'
|
|
51
|
+
|
|
52
|
+
= Developer's Notes
|
|
53
|
+
== The fork and wait methods
|
|
54
|
+
The fork method is emulated on Win32 by spawning a another Ruby process
|
|
55
|
+
against $PROGRAM_NAME via the CreateProcess() Win32 API function. It will
|
|
56
|
+
use its parent's environment and starting directory.
|
|
57
|
+
|
|
58
|
+
The various wait methods are a wrapper for the WaitForSingleObject() or
|
|
59
|
+
WaitForMultipleObjects() Win32 API functions, for the wait* and waitpid*
|
|
60
|
+
methods, respectively. In the case of wait2 and waitpid2, the exit value
|
|
61
|
+
is returned via the GetExitCodeProcess() Win32API function.
|
|
62
|
+
|
|
63
|
+
For now the waitpid and waitpid2 calls do not accept a second argument.
|
|
64
|
+
That's because we haven't yet determined if there's a signal we should
|
|
65
|
+
allow to be sent.
|
|
66
|
+
|
|
67
|
+
IMPORTANT!
|
|
68
|
+
Note that because fork is calling CreateProcess($PROGRAM_NAME), it will
|
|
69
|
+
start again from the top of the script instead of from the point of the
|
|
70
|
+
call. We will try to address this in a future release, if possible.
|
|
71
|
+
|
|
72
|
+
== The kill method
|
|
73
|
+
Initially, the kill method will try to get a HANDLE on the PID using the
|
|
74
|
+
OpenProcess() function. If that succeeds, we know the process is running.
|
|
75
|
+
|
|
76
|
+
In the event of signal 2 or signal 3, the GenerateConsoleCtrlEvent()
|
|
77
|
+
function is used to send a signal to that process. These will not kill
|
|
78
|
+
GUI processes. It will not (currently) send a signal to remote
|
|
79
|
+
processes.
|
|
80
|
+
|
|
81
|
+
In the event of signal 1 or 4-8, the CreateRemoteThread() function is used
|
|
82
|
+
after the HANDLE's process has been identified to create a thread
|
|
83
|
+
within that process. The ExitProcess() function is then sent to that
|
|
84
|
+
thread.
|
|
85
|
+
|
|
86
|
+
In the event of signal 9, the TerminateProcess() function is called. This
|
|
87
|
+
will almost certainly kill the process, but doesn't give the process a
|
|
88
|
+
chance to necessarily do any cleanup it might otherwise do.
|
|
89
|
+
|
|
90
|
+
== Differences between Ruby's kill and the Win32 Utils kill
|
|
91
|
+
Ruby does not currently use the CreateRemoteThread() + ExitProcess()
|
|
92
|
+
approach which is, according to everything I've read, a cleaner approach.
|
|
93
|
+
This includes not only online research but also Jeffrey Richter's
|
|
94
|
+
"Advanced Windows Programming, 3rd Edition."
|
|
95
|
+
|
|
96
|
+
Also, the way kill handles multiple pids works slightly differently (and
|
|
97
|
+
better IMHO) in the Win32 Utils version than the way Ruby currently
|
|
98
|
+
provides.
|
|
99
|
+
|
|
100
|
+
The reason Process.kill was originally added, in case anyone cares for
|
|
101
|
+
historical trivia, is that Ruby 1.6.x did not support Process.kill on
|
|
102
|
+
Windows at all.
|
|
103
|
+
|
|
104
|
+
== Notes
|
|
105
|
+
It is unlikely you will be able to kill system processes with this module.
|
|
106
|
+
It's probably better that you don't.
|
|
107
|
+
|
|
108
|
+
== Known Bugs
|
|
109
|
+
None known (though please see the +Details+ section for quirks). Any
|
|
110
|
+
bugs should be reported on the project page at
|
|
111
|
+
http://rubyforge.org/projects/win32utils.
|
|
112
|
+
|
|
113
|
+
== Future Plans
|
|
114
|
+
Train Process.fork to execute from the point of the call rather than the
|
|
115
|
+
top of the script (if possible).
|
|
116
|
+
|
|
117
|
+
Other suggestions welcome.
|
|
118
|
+
|
|
119
|
+
== License
|
|
120
|
+
Ruby's
|
|
121
|
+
|
|
122
|
+
== Copyright
|
|
123
|
+
(C) 2003-2006 Daniel J. Berger
|
|
124
|
+
All Rights Reserved
|
|
125
|
+
|
|
126
|
+
== Warranty
|
|
127
|
+
This package is provided "as is" and without any express or
|
|
128
|
+
implied warranties, including, without limitation, the implied
|
|
129
|
+
warranties of merchantability and fitness for a particular purpose.
|
|
130
|
+
|
|
131
|
+
== Author(s)
|
|
132
|
+
Park Heesob
|
|
133
|
+
Daniel J. Berger
|
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
require 'windows/error'
|
|
2
|
+
require 'windows/process'
|
|
3
|
+
require 'windows/synchronize'
|
|
4
|
+
require 'windows/handle'
|
|
5
|
+
require 'windows/library'
|
|
6
|
+
require 'windows/console'
|
|
7
|
+
require 'windows/window'
|
|
8
|
+
|
|
9
|
+
class ProcessError < RuntimeError; end
|
|
10
|
+
|
|
11
|
+
module Process
|
|
12
|
+
WIN32_PROCESS_VERSION = '0.5.0'
|
|
13
|
+
|
|
14
|
+
include Windows::Process
|
|
15
|
+
include Windows::Error
|
|
16
|
+
include Windows::Library
|
|
17
|
+
include Windows::Console
|
|
18
|
+
include Windows::Handle
|
|
19
|
+
include Windows::Synchronize
|
|
20
|
+
include Windows::Window
|
|
21
|
+
extend Windows::Error
|
|
22
|
+
extend Windows::Process
|
|
23
|
+
extend Windows::Synchronize
|
|
24
|
+
extend Windows::Handle
|
|
25
|
+
extend Windows::Library
|
|
26
|
+
extend Windows::Console
|
|
27
|
+
|
|
28
|
+
# Used by Process.create
|
|
29
|
+
ProcessInfo = Struct.new("ProcessInfo",
|
|
30
|
+
:process_handle,
|
|
31
|
+
:thread_handle,
|
|
32
|
+
:process_id,
|
|
33
|
+
:thread_id
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
@child_pids = [] # Static variable used for Process.fork
|
|
37
|
+
@i = -1 # Static variable used for Process.fork
|
|
38
|
+
|
|
39
|
+
# Waits for the given child process to exit and returns that pid.
|
|
40
|
+
#
|
|
41
|
+
# Note that the $? (Process::Status) global variable is NOT set. This
|
|
42
|
+
# may be addressed in a future release.
|
|
43
|
+
#
|
|
44
|
+
def waitpid(pid)
|
|
45
|
+
exit_code = [0].pack('L')
|
|
46
|
+
handle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid)
|
|
47
|
+
|
|
48
|
+
if handle == INVALID_HANDLE_VALUE
|
|
49
|
+
raise ProcessError, get_last_error
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# TODO: update the $? global variable (if/when possible)
|
|
53
|
+
status = WaitForSingleObject(handle, INFINITE)
|
|
54
|
+
|
|
55
|
+
unless GetExitCodeProcess(handle, exit_code)
|
|
56
|
+
raise ProcessError, get_last_error
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
CloseHandle(handle)
|
|
60
|
+
@child_pids.delete(pid)
|
|
61
|
+
|
|
62
|
+
# TODO: update the $? global variable (if/when possible)
|
|
63
|
+
exit_code = exit_code.unpack('L').first
|
|
64
|
+
|
|
65
|
+
pid
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Waits for the given child process to exit and returns an array containing
|
|
69
|
+
# the process id and the exit status.
|
|
70
|
+
#
|
|
71
|
+
# Note that the $? (Process::Status) global variable is NOT set. This
|
|
72
|
+
# may be addressed in a future release.
|
|
73
|
+
#
|
|
74
|
+
def waitpid2(pid)
|
|
75
|
+
exit_code = [0].pack('L')
|
|
76
|
+
handle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid)
|
|
77
|
+
|
|
78
|
+
if handle == INVALID_HANDLE_VALUE
|
|
79
|
+
raise ProcessError, get_last_error
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# TODO: update the $? global variable (if/when possible)
|
|
83
|
+
status = WaitForSingleObject(handle, INFINITE)
|
|
84
|
+
|
|
85
|
+
unless GetExitCodeProcess(handle, exit_code)
|
|
86
|
+
raise ProcessError, get_last_error
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
CloseHandle(handle)
|
|
90
|
+
@child_pids.delete(pid)
|
|
91
|
+
|
|
92
|
+
# TODO: update the $? global variable (if/when possible)
|
|
93
|
+
exit_code = exit_code.unpack('L').first
|
|
94
|
+
|
|
95
|
+
[pid, status]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Sends the given +signal+ to an array of process id's. The +signal+ may
|
|
99
|
+
# be any value from 0 to 9, or the special strings 'SIGINT' (or 'INT'),
|
|
100
|
+
# 'SIGBRK' (or 'BRK') and 'SIGKILL' (or 'KILL'). An array of successfully
|
|
101
|
+
# killed pids is returned.
|
|
102
|
+
#
|
|
103
|
+
# Signal 0 merely tests if the process is running without killing it.
|
|
104
|
+
# Signal 2 sends a CTRL_C_EVENT to the process.
|
|
105
|
+
# Signal 3 sends a CTRL_BRK_EVENT to the process.
|
|
106
|
+
# Signal 9 kills the process in a harsh manner.
|
|
107
|
+
# Signals 1 and 4-8 kill the process in a nice manner.
|
|
108
|
+
#
|
|
109
|
+
# SIGINT/INT corresponds to signal 2
|
|
110
|
+
# SIGBRK/BRK corresponds to signal 3
|
|
111
|
+
# SIGKILL/KILL corresponds to signal 9
|
|
112
|
+
#
|
|
113
|
+
# Signals 2 and 3 only affect console processes, and then only if the
|
|
114
|
+
# process was created with the CREATE_NEW_PROCESS_GROUP flag.
|
|
115
|
+
#
|
|
116
|
+
def kill(signal, *pids)
|
|
117
|
+
case signal
|
|
118
|
+
when 'SIGINT', 'INT'
|
|
119
|
+
signal = 2
|
|
120
|
+
when 'SIGBRK', 'BRK'
|
|
121
|
+
signal = 3
|
|
122
|
+
when 'SIGKILL', 'KILL'
|
|
123
|
+
signal = 9
|
|
124
|
+
when 0..9
|
|
125
|
+
# Do nothing
|
|
126
|
+
else
|
|
127
|
+
raise ProcessError, "Invalid signal '#{signal}'"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
killed_pids = []
|
|
131
|
+
|
|
132
|
+
pids.each{ |pid|
|
|
133
|
+
# Send the signal to the current process if the pid is zero
|
|
134
|
+
if pid == 0
|
|
135
|
+
pid = Process.pid
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# No need for full access if the signal is zero
|
|
139
|
+
if signal == 0
|
|
140
|
+
access = PROCESS_QUERY_INFORMATION|PROCESS_VM_READ
|
|
141
|
+
handle = OpenProcess(access, 0 , pid)
|
|
142
|
+
else
|
|
143
|
+
handle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
case signal
|
|
147
|
+
when 0
|
|
148
|
+
if handle != 0
|
|
149
|
+
killed_pids.push(pid)
|
|
150
|
+
CloseHandle(handle)
|
|
151
|
+
else
|
|
152
|
+
# If ERROR_ACCESS_DENIED is returned, we know it's running
|
|
153
|
+
if GetLastError() == ERROR_ACCESS_DENIED
|
|
154
|
+
killed_pids.push(pid)
|
|
155
|
+
else
|
|
156
|
+
raise ProcessError, get_last_error
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
when 2
|
|
160
|
+
if GenerateConsoleCtrlEvent(CTRL_C_EVENT, pid)
|
|
161
|
+
killed_pids.push(pid)
|
|
162
|
+
end
|
|
163
|
+
when 3
|
|
164
|
+
if GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid)
|
|
165
|
+
killed_pids.push(pid)
|
|
166
|
+
end
|
|
167
|
+
when 9
|
|
168
|
+
if TerminateProcess(handle, pid)
|
|
169
|
+
CloseHandle(handle)
|
|
170
|
+
killed_pids.push(pid)
|
|
171
|
+
@child_pids.delete(pid)
|
|
172
|
+
else
|
|
173
|
+
raise ProcessError, get_last_error
|
|
174
|
+
end
|
|
175
|
+
else
|
|
176
|
+
if handle != 0
|
|
177
|
+
thread_id = [0].pack('L')
|
|
178
|
+
dll = 'kernel32'
|
|
179
|
+
proc = 'ExitProcess'
|
|
180
|
+
|
|
181
|
+
thread = CreateRemoteThread(
|
|
182
|
+
handle,
|
|
183
|
+
0,
|
|
184
|
+
0,
|
|
185
|
+
GetProcAddress(GetModuleHandle(dll), proc),
|
|
186
|
+
0,
|
|
187
|
+
0,
|
|
188
|
+
thread_id
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
if thread
|
|
192
|
+
WaitForSingleObject(thread, 5)
|
|
193
|
+
CloseHandle(handle)
|
|
194
|
+
killed_pids.push(pid)
|
|
195
|
+
@child_pids.delete(pid)
|
|
196
|
+
else
|
|
197
|
+
CloseHandle(handle)
|
|
198
|
+
raise ProcessError, get_last_error
|
|
199
|
+
end
|
|
200
|
+
else
|
|
201
|
+
raise ProcessError, get_last_error
|
|
202
|
+
end
|
|
203
|
+
@child_pids.delete(pid)
|
|
204
|
+
end
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
killed_pids
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Process.create(key => value, ...) => ProcessInfo
|
|
211
|
+
#
|
|
212
|
+
# This is a wrapper for the CreateProcess() function. It executes a process,
|
|
213
|
+
# returning a ProcessInfo struct. It accepts a hash as an argument.
|
|
214
|
+
# There are six primary keys:
|
|
215
|
+
#
|
|
216
|
+
# * app_name (mandatory)
|
|
217
|
+
# * inherit (default: false)
|
|
218
|
+
# * process_inherit (default: false)
|
|
219
|
+
# * thread_inherit (default: false)
|
|
220
|
+
# * creation_flags (default: 0)
|
|
221
|
+
# * cwd (default: Dir.pwd)
|
|
222
|
+
# * startup_info (default: nil)
|
|
223
|
+
# * environment (default: nil)
|
|
224
|
+
#
|
|
225
|
+
# Of these, the 'app_name' must be specified or an error is raised.
|
|
226
|
+
#
|
|
227
|
+
# The startup_info key takes a hash. Its keys are attributes that are
|
|
228
|
+
# part of the StartupInfo struct, and are generally only meaningful for
|
|
229
|
+
# GUI or console processes. See the documentation on CreateProcess()
|
|
230
|
+
# and the StartupInfo struct on MSDN for more information.
|
|
231
|
+
#
|
|
232
|
+
# * desktop
|
|
233
|
+
# * title
|
|
234
|
+
# * x
|
|
235
|
+
# * y
|
|
236
|
+
# * x_size
|
|
237
|
+
# * y_size
|
|
238
|
+
# * x_count_chars
|
|
239
|
+
# * y_count_chars
|
|
240
|
+
# * fill_attribute
|
|
241
|
+
# * sw_flags
|
|
242
|
+
# * startf_flags
|
|
243
|
+
#
|
|
244
|
+
# The relevant constants for 'creation_flags', 'sw_flags' and 'startf_flags'
|
|
245
|
+
# are included in the Windows::Process, Windows::Console and Windows::Window
|
|
246
|
+
# modules. These come with the windows-pr package, a prerequisite of this
|
|
247
|
+
# package.
|
|
248
|
+
#
|
|
249
|
+
# The ProcessInfo struct contains the following members:
|
|
250
|
+
#
|
|
251
|
+
# * process_handle - The handle to the newly created process
|
|
252
|
+
# * thread_handle - The handle to the primary thread of the newly created
|
|
253
|
+
# process.
|
|
254
|
+
# * process_id - Process ID.
|
|
255
|
+
# * thread_id - Thread ID.
|
|
256
|
+
#
|
|
257
|
+
def create(args)
|
|
258
|
+
raise TypeError unless args.kind_of?(Hash)
|
|
259
|
+
|
|
260
|
+
valid_keys = %w/
|
|
261
|
+
app_name inherit creation_flags cwd environment startup_info
|
|
262
|
+
thread_inherit process_inherit
|
|
263
|
+
/
|
|
264
|
+
|
|
265
|
+
valid_si_keys = %/
|
|
266
|
+
startf_flags desktop title x y x_size y_size x_count_chars
|
|
267
|
+
y_count_chars fill_attribute sw_flags stdin stdout stderr
|
|
268
|
+
/
|
|
269
|
+
|
|
270
|
+
# Set some default values
|
|
271
|
+
hash = {
|
|
272
|
+
'inherit' => 0,
|
|
273
|
+
'process_inherit' => 0,
|
|
274
|
+
'thread_inherit' => 0,
|
|
275
|
+
'creation_flags' => 0,
|
|
276
|
+
'cwd' => 0
|
|
277
|
+
}
|
|
278
|
+
env = 0
|
|
279
|
+
|
|
280
|
+
# Validate the keys, and convert symbols and case to lowercase strings.
|
|
281
|
+
args.each{ |key, val|
|
|
282
|
+
key = key.to_s.downcase
|
|
283
|
+
unless valid_keys.include?(key)
|
|
284
|
+
raise ProcessError, "invalid key '#{key}'"
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Convert true to 1 and nil/false to 0.
|
|
288
|
+
case val
|
|
289
|
+
when true, false
|
|
290
|
+
hash[key] = val == false ? 0 : 1
|
|
291
|
+
when nil
|
|
292
|
+
hash[key] = 0 # Win32API no likey nil
|
|
293
|
+
else
|
|
294
|
+
hash[key] = val
|
|
295
|
+
end
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
si_hash = {}
|
|
299
|
+
|
|
300
|
+
# If the startup_info key is present, validate its subkeys
|
|
301
|
+
if hash['startup_info']
|
|
302
|
+
hash['startup_info'].each{ |key, val|
|
|
303
|
+
key = key.to_s.downcase
|
|
304
|
+
unless valid_si_keys.include?(key)
|
|
305
|
+
raise ProcessError, "invalid startup_info key '#{key}'"
|
|
306
|
+
end
|
|
307
|
+
si_hash[key] = val
|
|
308
|
+
}
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# The +app_name+ key is mandatory
|
|
312
|
+
unless hash['app_name']
|
|
313
|
+
raise ProcessError, 'app_name must be specified'
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
# The environment string should be passed as a string of ';' separated
|
|
317
|
+
# paths.
|
|
318
|
+
if hash['environment']
|
|
319
|
+
env = hash['environment'].split(File::PATH_SEPARATOR) << 0.chr
|
|
320
|
+
env = [env.join("\0")].pack('p*').unpack('L').first
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
startinfo = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
|
|
324
|
+
startinfo = startinfo.pack('LLLLLLLLLLLLSSLLLL')
|
|
325
|
+
procinfo = [0,0,0,0].pack('LLLL')
|
|
326
|
+
|
|
327
|
+
# Process SECURITY_ATTRIBUTE structure
|
|
328
|
+
process_security = 0
|
|
329
|
+
if hash['process_inherit?']
|
|
330
|
+
process_security = [0,0,0].pack('LLL')
|
|
331
|
+
process_security[0,4] = [12].pack('L') # sizeof(SECURITY_ATTRIBUTE)
|
|
332
|
+
process_security[8,4] = [hash['process_inherit?']].pack('L')
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Thread SECURITY_ATTRIBUTE structure
|
|
336
|
+
thread_security = 0
|
|
337
|
+
if hash['thread_security?']
|
|
338
|
+
thread_security = [0,0,0].pack('LLL')
|
|
339
|
+
thread_security[0,4] = [12].pack('L') # sizeof(SECURITY_ATTRIBUTE)
|
|
340
|
+
thread_security[8,4] = [hash['thread_inherit?']].pack('L')
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# The bytes not covered here are reserved (null)
|
|
344
|
+
unless si_hash.empty?
|
|
345
|
+
startinfo[0,4] = [startinfo.size].pack('L')
|
|
346
|
+
startinfo[8,4] = [si_hash['desktop']].pack('p*') if si_hash['desktop']
|
|
347
|
+
startinfo[12,4] = [si_hash['title']].pack('p*') if si_hash['title']
|
|
348
|
+
startinfo[16,4] = [si_hash['x']].pack('L') if si_hash['x']
|
|
349
|
+
startinfo[20,4] = [si_hash['y']].pack('L') if si_hash['y']
|
|
350
|
+
startinfo[24,4] = [si_hash['x_size']].pack('L') if si_hash['x_size']
|
|
351
|
+
startinfo[28,4] = [si_hash['y_size']].pack('L') if si_hash['y_size']
|
|
352
|
+
startinfo[32,4] = [si_hash['x_count_chars']].pack('L') if si_hash['x_count_chars']
|
|
353
|
+
startinfo[36,4] = [si_hash['y_count_chars']].pack('L') if si_hash['y_count_chars']
|
|
354
|
+
startinfo[40,4] = [si_hash['fill_attribute']].pack('L') if si_hash['fill_attribute']
|
|
355
|
+
startinfo[44,4] = [si_hash['startf_flags']].pack('L') if si_hash['startf_flags']
|
|
356
|
+
startinfo[48,2] = [si_hash['sw_flags']].pack('S') if si_hash['sw_flags']
|
|
357
|
+
startinfo[56,4] = [si_hash['stdin']].pack('L') if si_hash['stdin']
|
|
358
|
+
startinfo[60,4] = [si_hash['stdout']].pack('L') if si_hash['stdout']
|
|
359
|
+
startinfo[64,4] = [si_hash['stderr']].pack('L') if si_hash['stderr']
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
rv = CreateProcess(
|
|
363
|
+
0, # App name
|
|
364
|
+
hash['app_name'], # Command line
|
|
365
|
+
process_security, # Process attributes
|
|
366
|
+
thread_security, # Thread attributes
|
|
367
|
+
hash['inherit'], # Inherit handles?
|
|
368
|
+
hash['creation_flags'], # Creation flags
|
|
369
|
+
env, # Environment
|
|
370
|
+
hash['cwd'], # Working directory
|
|
371
|
+
startinfo, # Startup Info
|
|
372
|
+
procinfo # Process Info
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
if rv == 0
|
|
376
|
+
raise ProcessError, "CreateProcess() failed: %", get_last_error
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
ProcessInfo.new(
|
|
380
|
+
procinfo[0,4].unpack('L').first, # hProcess
|
|
381
|
+
procinfo[4,4].unpack('L').first, # hThread
|
|
382
|
+
procinfo[8,4].unpack('L').first, # hProcessId
|
|
383
|
+
procinfo[12,4].unpack('L').first # hThreadId
|
|
384
|
+
)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# Waits for any child process to exit and returns the process id of that
|
|
388
|
+
# child.
|
|
389
|
+
#
|
|
390
|
+
# Note that the $? (Process::Status) global variable is NOT set. This
|
|
391
|
+
# may be addressed in a future release.
|
|
392
|
+
#--
|
|
393
|
+
# The GetProcessId() function is not defined in Windows 2000 or earlier
|
|
394
|
+
# so we have to do some extra work for those platforms.
|
|
395
|
+
#
|
|
396
|
+
def wait
|
|
397
|
+
handles = []
|
|
398
|
+
|
|
399
|
+
# Windows 2000 or earlier
|
|
400
|
+
unless defined? GetProcessId
|
|
401
|
+
pids = []
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
@child_pids.each_with_index{ |pid, i|
|
|
405
|
+
handles[i] = OpenProcess(PROCESS_ALL_ACCESS, 0, pid)
|
|
406
|
+
|
|
407
|
+
if handles[i] == INVALID_HANDLE_VALUE
|
|
408
|
+
err = "unable to get HANDLE on process associated with pid #{pid}"
|
|
409
|
+
raise ProcessError, err
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
unless defined? GetProcessId
|
|
413
|
+
pids[i] = pid
|
|
414
|
+
end
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
wait = WaitForMultipleObjects(
|
|
418
|
+
handles.size,
|
|
419
|
+
handles.pack('L*'),
|
|
420
|
+
0,
|
|
421
|
+
INFINITE
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
if wait >= WAIT_OBJECT_0 && wait <= WAIT_OBJECT_0 + @child_pids.size - 1
|
|
425
|
+
index = wait - WAIT_OBJECT_0
|
|
426
|
+
handle = handles[index]
|
|
427
|
+
|
|
428
|
+
if defined? GetProcessId
|
|
429
|
+
pid = GetProcessId(handle)
|
|
430
|
+
else
|
|
431
|
+
pid = pids[index]
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
@child_pids.delete(pid)
|
|
435
|
+
handles.each{ |handle| CloseHandle(handle) }
|
|
436
|
+
return pid
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
nil
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
# Waits for any child process to exit and returns an array containing the
|
|
443
|
+
# process id and the exit status of that child.
|
|
444
|
+
#
|
|
445
|
+
# Note that the $? (Process::Status) global variable is NOT set. This
|
|
446
|
+
# may be addressed in a future release.
|
|
447
|
+
#--
|
|
448
|
+
# The GetProcessId() function is not defined in Windows 2000 or earlier
|
|
449
|
+
# so we have to do some extra work for those platforms.
|
|
450
|
+
#
|
|
451
|
+
def wait2
|
|
452
|
+
handles = []
|
|
453
|
+
|
|
454
|
+
# Windows 2000 or earlier
|
|
455
|
+
unless defined? GetProcessId
|
|
456
|
+
pids = []
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
@child_pids.each_with_index{ |pid, i|
|
|
460
|
+
handles[i] = OpenProcess(PROCESS_ALL_ACCESS, 0, pid)
|
|
461
|
+
|
|
462
|
+
if handles[i] == INVALID_HANDLE_VALUE
|
|
463
|
+
err = "unable to get HANDLE on process associated with pid #{pid}"
|
|
464
|
+
raise ProcessError, err
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
unless defined? GetProcessId
|
|
468
|
+
pids[i] = pid
|
|
469
|
+
end
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
wait = WaitForMultipleObjects(
|
|
473
|
+
handles.size,
|
|
474
|
+
handles.pack('L*'),
|
|
475
|
+
0,
|
|
476
|
+
INFINITE
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
if wait >= WAIT_OBJECT_0 && wait <= WAIT_OBJECT_0 + @child_pids.size - 1
|
|
480
|
+
index = wait - WAIT_OBJECT_0
|
|
481
|
+
handle = handles[index]
|
|
482
|
+
|
|
483
|
+
if defined? GetProcessId
|
|
484
|
+
pid = GetProcessId(handle)
|
|
485
|
+
else
|
|
486
|
+
pid = pids[index]
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
exit_code = [0].pack('l')
|
|
490
|
+
unless GetExitCodeProcess(handle, exit_code)
|
|
491
|
+
raise get_last_error
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
@child_pids.delete(pid)
|
|
495
|
+
|
|
496
|
+
handles.each{ |handle| CloseHandle(handle) }
|
|
497
|
+
return [pid, exit_code.unpack('l').first]
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
nil
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
# Creates the equivalent of a subshell via the CreateProcess() function.
|
|
504
|
+
# This behaves in a manner that is similar, but not identical to, the
|
|
505
|
+
# Kernel.fork method for Unix.
|
|
506
|
+
#
|
|
507
|
+
def fork
|
|
508
|
+
last_arg = ARGV.last
|
|
509
|
+
|
|
510
|
+
# Look for the 'child#xxx' tag
|
|
511
|
+
if last_arg =~ /child#\d+/
|
|
512
|
+
@i += 1
|
|
513
|
+
num = last_arg.split('#').last.to_i
|
|
514
|
+
if num == @i
|
|
515
|
+
if block_given?
|
|
516
|
+
status = 0
|
|
517
|
+
begin
|
|
518
|
+
yield
|
|
519
|
+
rescue Exception
|
|
520
|
+
status = -1 # Any non-zero result is failure
|
|
521
|
+
ensure
|
|
522
|
+
return status
|
|
523
|
+
end
|
|
524
|
+
end
|
|
525
|
+
return nil
|
|
526
|
+
else
|
|
527
|
+
return false
|
|
528
|
+
end
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
# Tag the command with the word 'child#xxx' to distinguish it
|
|
532
|
+
# from the calling process.
|
|
533
|
+
cmd = 'ruby -I "' + $LOAD_PATH.join(File::PATH_SEPARATOR) << '" "'
|
|
534
|
+
cmd << File.expand_path($PROGRAM_NAME) << '" ' << ARGV.join(' ')
|
|
535
|
+
cmd << ' child#' << @child_pids.length.to_s
|
|
536
|
+
|
|
537
|
+
startinfo = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
|
|
538
|
+
startinfo = startinfo.pack('LLLLLLLLLLLLSSLLLL')
|
|
539
|
+
procinfo = [0,0,0,0].pack('LLLL')
|
|
540
|
+
|
|
541
|
+
rv = CreateProcess(0, cmd, 0, 0, 1, 0, 0, 0, startinfo, procinfo)
|
|
542
|
+
|
|
543
|
+
if rv == 0
|
|
544
|
+
raise ProcessError, get_last_error
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
pid = procinfo[8,4].unpack('L').first
|
|
548
|
+
@child_pids.push(pid)
|
|
549
|
+
|
|
550
|
+
pid
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
module_function :kill, :wait, :wait2, :waitpid, :waitpid2, :create, :fork
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
# Create a global fork method
|
|
557
|
+
module Kernel
|
|
558
|
+
def fork(&block)
|
|
559
|
+
Process.fork(&block)
|
|
560
|
+
end
|
|
561
|
+
end
|