From 9138e22d81214661a2aa3a9db783e5331f2f3013 Mon Sep 17 00:00:00 2001
From: "te@myria.de"
Date: Tue, 14 Sep 2021 06:21:53 +0200
Subject: [PATCH] spyce
---
spyce-2.1/CHANGES | 747 +++
spyce-2.1/LICENCE | 40 +
spyce-2.1/README | 15 +
spyce-2.1/RELEASE | 21 +
spyce-2.1/THANKS | 60 +
spyce-2.1/_eginfo.txt | 77 +
spyce-2.1/contrib/jedit-spyce.xml | 168 +
spyce-2.1/contrib/kate-spyce.xml | 383 ++
spyce-2.1/contrib/modules/automaton.py | 135 +
spyce-2.1/contrib/modules/automaton.spy | 88 +
spyce-2.1/contrib/modules/etag.py | 147 +
spyce-2.1/contrib/modules/mail.py | 202 +
spyce-2.1/contrib/modules/mail.txt | 133 +
spyce-2.1/contrib/modules/session1.py | 371 ++
spyce-2.1/contrib/modules/spydurus.py | 110 +
spyce-2.1/contrib/modules/template.py | 83 +
spyce-2.1/contrib/modules/toc.py | 381 ++
spyce-2.1/contrib/modules/toc.pyc | Bin 0 -> 17962 bytes
spyce-2.1/contrib/pyweboff/config.py | 31 +
spyce-2.1/contrib/pyweboff/lib/actions.py | 19 +
spyce-2.1/contrib/pyweboff/lib/pyweboff.sql | 38 +
spyce-2.1/contrib/pyweboff/www/index.spy | 73 +
spyce-2.1/contrib/pyweboff/www/parent.spi | 46 +
spyce-2.1/contrib/pyweboff/www/user-new.spy | 12 +
.../pyweboff/www/util/form_calendar.gif | Bin 0 -> 152 bytes
.../pyweboff/www/util/form_calendar.js | 1464 +++++
.../contrib/pyweboff/www/util/spyce2.png | Bin 0 -> 812 bytes
spyce-2.1/contrib/readme.txt | 32 +
spyce-2.1/contrib/spyce.el | 26 +
spyce-2.1/contrib/spyce.vim | 110 +
spyce-2.1/contrib/tags/flow.py | 117 +
spyce-2.1/contrib/xitami/lrwplib.py | 273 +
spyce-2.1/contrib/xitami/spyceLRWP.py | 213 +
spyce-2.1/debugspyce.py | 35 +
spyce-2.1/fcgi.py | 268 +
spyce-2.1/installHelper.py | 181 +
spyce-2.1/misc/addfirstline.sh | 11 +
spyce-2.1/misc/benchmark/hello.c | 24 +
spyce-2.1/misc/benchmark/hello.html | 4 +
spyce-2.1/misc/benchmark/hello.jsp | 6 +
spyce-2.1/misc/benchmark/hello.php | 6 +
spyce-2.1/misc/benchmark/hello.py | 9 +
spyce-2.1/misc/benchmark/hellopy.cgi | 17 +
spyce-2.1/misc/one-check.bmp | Bin 0 -> 360 bytes
spyce-2.1/misc/one-nocheck.bmp | Bin 0 -> 360 bytes
spyce-2.1/misc/pics/pilpel1-edit.jpg | Bin 0 -> 6447 bytes
spyce-2.1/misc/pics/pilpel2.jpg | Bin 0 -> 4507 bytes
spyce-2.1/misc/pics/pypow.gif | Bin 0 -> 361 bytes
spyce-2.1/misc/pics/spyce-border.ico | Bin 0 -> 766 bytes
spyce-2.1/misc/pics/spyce.ico | Bin 0 -> 766 bytes
spyce-2.1/misc/pics/spyce1.bmp | Bin 0 -> 67854 bytes
spyce-2.1/misc/pics/spyce2.bmp | Bin 0 -> 67851 bytes
spyce-2.1/misc/pics/spyce3.bmp | Bin 0 -> 67854 bytes
spyce-2.1/misc/pics/spyce3.gif | Bin 0 -> 4705 bytes
spyce-2.1/misc/pics/spyce3.jpg | Bin 0 -> 2684 bytes
spyce-2.1/misc/runsfcgi.py | 18 +
spyce-2.1/misc/spyceconf-sf.py | 11 +
spyce-2.1/modules/compress.py | 84 +
spyce-2.1/modules/cookie.py | 54 +
spyce-2.1/modules/cookie.pyc | Bin 0 -> 3014 bytes
spyce-2.1/modules/error.py | 175 +
spyce-2.1/modules/error.pyc | Bin 0 -> 7904 bytes
spyce-2.1/modules/include.py | 136 +
spyce-2.1/modules/include.pyc | Bin 0 -> 5994 bytes
spyce-2.1/modules/pool.py | 92 +
spyce-2.1/modules/pool.pyc | Bin 0 -> 4953 bytes
spyce-2.1/modules/redirect.py | 58 +
spyce-2.1/modules/redirect.pyc | Bin 0 -> 4067 bytes
spyce-2.1/modules/request.py | 239 +
spyce-2.1/modules/request.pyc | Bin 0 -> 11342 bytes
spyce-2.1/modules/response.py | 205 +
spyce-2.1/modules/response.pyc | Bin 0 -> 11866 bytes
spyce-2.1/modules/session.py | 301 +
spyce-2.1/modules/session.pyc | Bin 0 -> 13641 bytes
spyce-2.1/modules/spylambda.py | 56 +
spyce-2.1/modules/spylambda.pyc | Bin 0 -> 2721 bytes
spyce-2.1/modules/stdout.py | 107 +
spyce-2.1/modules/stdout.pyc | Bin 0 -> 6052 bytes
spyce-2.1/modules/taglib.py | 103 +
spyce-2.1/modules/transform.py | 159 +
spyce-2.1/modules/transform.pyc | Bin 0 -> 11036 bytes
spyce-2.1/pcqueue.py | 75 +
spyce-2.1/pcqueue.pyc | Bin 0 -> 3746 bytes
spyce-2.1/run_spyceCGI.py | 21 +
spyce-2.1/run_spyceCmd.py | 22 +
spyce-2.1/run_spyceModpy.py | 49 +
spyce-2.1/scheduler.py | 195 +
spyce-2.1/scheduler.pyc | Bin 0 -> 7632 bytes
spyce-2.1/spyce.mime | 495 ++
spyce-2.1/spyce.nsi | 208 +
spyce-2.1/spyce.nsi.in | 208 +
spyce-2.1/spyce.py | 861 +++
spyce-2.1/spyce.pyc | Bin 0 -> 34637 bytes
spyce-2.1/spyce.spec.in | 73 +
spyce-2.1/spyceApache.conf | 83 +
spyce-2.1/spyceCGI.py | 58 +
spyce-2.1/spyceCache.py | 165 +
spyce-2.1/spyceCache.pyc | Bin 0 -> 8042 bytes
spyce-2.1/spyceCmd.py | 307 +
spyce-2.1/spyceCmd.pyc | Bin 0 -> 13171 bytes
spyce-2.1/spyceCompile.py | 1638 ++++++
spyce-2.1/spyceCompile.pyc | Bin 0 -> 56026 bytes
spyce-2.1/spyceConsole.py | 75 +
spyce-2.1/spyceException.py | 152 +
spyce-2.1/spyceException.pyc | Bin 0 -> 8016 bytes
spyce-2.1/spyceLock.py | 110 +
spyce-2.1/spyceLock.pyc | Bin 0 -> 4915 bytes
spyce-2.1/spyceModpy.py | 139 +
spyce-2.1/spyceModule.py | 98 +
spyce-2.1/spyceModule.pyc | Bin 0 -> 5011 bytes
spyce-2.1/spycePreload.py | 27 +
spyce-2.1/spycePreload.pyc | Bin 0 -> 1510 bytes
spyce-2.1/spyceProject.py | 136 +
spyce-2.1/spyceTag.py | 334 ++
spyce-2.1/spyceTag.pyc | Bin 0 -> 15032 bytes
spyce-2.1/spyceUtil.py | 240 +
spyce-2.1/spyceUtil.pyc | Bin 0 -> 11138 bytes
spyce-2.1/spyceWWW.py | 397 ++
spyce-2.1/spyceWWW.pyc | Bin 0 -> 15788 bytes
spyce-2.1/spyceconf.py | 333 ++
spyce-2.1/spyceconf.pyc | Bin 0 -> 2763 bytes
spyce-2.1/sqlalchemy/__init__.py | 17 +
spyce-2.1/sqlalchemy/__init__.pyc | Bin 0 -> 623 bytes
spyce-2.1/sqlalchemy/ansisql.py | 903 +++
spyce-2.1/sqlalchemy/ansisql.pyc | Bin 0 -> 42820 bytes
spyce-2.1/sqlalchemy/databases/__init__.py | 8 +
spyce-2.1/sqlalchemy/databases/__init__.pyc | Bin 0 -> 245 bytes
spyce-2.1/sqlalchemy/databases/firebird.py | 423 ++
.../databases/information_schema.py | 200 +
spyce-2.1/sqlalchemy/databases/mssql.py | 600 ++
spyce-2.1/sqlalchemy/databases/mysql.py | 476 ++
spyce-2.1/sqlalchemy/databases/oracle.py | 460 ++
spyce-2.1/sqlalchemy/databases/postgres.py | 558 ++
spyce-2.1/sqlalchemy/databases/sqlite.py | 325 ++
spyce-2.1/sqlalchemy/databases/sqlite.pyc | Bin 0 -> 17629 bytes
spyce-2.1/sqlalchemy/engine/__init__.py | 91 +
spyce-2.1/sqlalchemy/engine/__init__.pyc | Bin 0 -> 5049 bytes
spyce-2.1/sqlalchemy/engine/base.py | 816 +++
spyce-2.1/sqlalchemy/engine/base.pyc | Bin 0 -> 50141 bytes
spyce-2.1/sqlalchemy/engine/default.py | 222 +
spyce-2.1/sqlalchemy/engine/default.pyc | Bin 0 -> 11471 bytes
spyce-2.1/sqlalchemy/engine/strategies.py | 120 +
spyce-2.1/sqlalchemy/engine/strategies.pyc | Bin 0 -> 6760 bytes
spyce-2.1/sqlalchemy/engine/threadlocal.py | 122 +
spyce-2.1/sqlalchemy/engine/threadlocal.pyc | Bin 0 -> 9035 bytes
spyce-2.1/sqlalchemy/engine/url.py | 127 +
spyce-2.1/sqlalchemy/engine/url.pyc | Bin 0 -> 5153 bytes
spyce-2.1/sqlalchemy/exceptions.py | 62 +
spyce-2.1/sqlalchemy/exceptions.pyc | Bin 0 -> 4343 bytes
spyce-2.1/sqlalchemy/ext/__init__.py | 1 +
spyce-2.1/sqlalchemy/ext/__init__.pyc | Bin 0 -> 151 bytes
spyce-2.1/sqlalchemy/ext/activemapper.py | 308 +
spyce-2.1/sqlalchemy/ext/assignmapper.py | 36 +
spyce-2.1/sqlalchemy/ext/assignmapper.pyc | Bin 0 -> 2468 bytes
spyce-2.1/sqlalchemy/ext/associationproxy.py | 96 +
spyce-2.1/sqlalchemy/ext/proxy.py | 99 +
spyce-2.1/sqlalchemy/ext/selectresults.py | 166 +
spyce-2.1/sqlalchemy/ext/sessioncontext.py | 55 +
spyce-2.1/sqlalchemy/ext/sessioncontext.pyc | Bin 0 -> 3475 bytes
spyce-2.1/sqlalchemy/ext/sqlsoup.py | 429 ++
spyce-2.1/sqlalchemy/ext/sqlsoup.pyc | Bin 0 -> 19921 bytes
spyce-2.1/sqlalchemy/logging.py | 76 +
spyce-2.1/sqlalchemy/logging.pyc | Bin 0 -> 3614 bytes
spyce-2.1/sqlalchemy/mods/__init__.py | 0
spyce-2.1/sqlalchemy/mods/legacy_session.py | 140 +
spyce-2.1/sqlalchemy/mods/selectresults.py | 7 +
spyce-2.1/sqlalchemy/mods/threadlocal.py | 47 +
spyce-2.1/sqlalchemy/orm/__init__.py | 179 +
spyce-2.1/sqlalchemy/orm/__init__.pyc | Bin 0 -> 8953 bytes
spyce-2.1/sqlalchemy/orm/attributes.py | 757 +++
spyce-2.1/sqlalchemy/orm/attributes.pyc | Bin 0 -> 39480 bytes
spyce-2.1/sqlalchemy/orm/dependency.py | 348 ++
spyce-2.1/sqlalchemy/orm/dependency.pyc | Bin 0 -> 15574 bytes
spyce-2.1/sqlalchemy/orm/interfaces.py | 174 +
spyce-2.1/sqlalchemy/orm/interfaces.pyc | Bin 0 -> 13204 bytes
spyce-2.1/sqlalchemy/orm/mapper.py | 1419 +++++
spyce-2.1/sqlalchemy/orm/mapper.pyc | Bin 0 -> 59051 bytes
spyce-2.1/sqlalchemy/orm/properties.py | 317 +
spyce-2.1/sqlalchemy/orm/properties.pyc | Bin 0 -> 16521 bytes
spyce-2.1/sqlalchemy/orm/query.py | 530 ++
spyce-2.1/sqlalchemy/orm/query.pyc | Bin 0 -> 24166 bytes
spyce-2.1/sqlalchemy/orm/session.py | 454 ++
spyce-2.1/sqlalchemy/orm/session.pyc | Bin 0 -> 25789 bytes
spyce-2.1/sqlalchemy/orm/strategies.py | 544 ++
spyce-2.1/sqlalchemy/orm/strategies.pyc | Bin 0 -> 26185 bytes
spyce-2.1/sqlalchemy/orm/sync.py | 151 +
spyce-2.1/sqlalchemy/orm/sync.pyc | Bin 0 -> 6861 bytes
spyce-2.1/sqlalchemy/orm/unitofwork.py | 856 +++
spyce-2.1/sqlalchemy/orm/unitofwork.pyc | Bin 0 -> 37886 bytes
spyce-2.1/sqlalchemy/orm/uowdumper.py | 198 +
spyce-2.1/sqlalchemy/orm/util.py | 99 +
spyce-2.1/sqlalchemy/orm/util.pyc | Bin 0 -> 5913 bytes
spyce-2.1/sqlalchemy/pool.py | 486 ++
spyce-2.1/sqlalchemy/pool.pyc | Bin 0 -> 25613 bytes
spyce-2.1/sqlalchemy/queue.py | 182 +
spyce-2.1/sqlalchemy/queue.pyc | Bin 0 -> 7389 bytes
spyce-2.1/sqlalchemy/schema.py | 974 ++++
spyce-2.1/sqlalchemy/schema.pyc | Bin 0 -> 54797 bytes
spyce-2.1/sqlalchemy/sql.py | 1668 ++++++
spyce-2.1/sqlalchemy/sql.pyc | Bin 0 -> 97912 bytes
spyce-2.1/sqlalchemy/sql_util.py | 109 +
spyce-2.1/sqlalchemy/sql_util.pyc | Bin 0 -> 7293 bytes
spyce-2.1/sqlalchemy/topological.py | 269 +
spyce-2.1/sqlalchemy/topological.pyc | Bin 0 -> 11850 bytes
spyce-2.1/sqlalchemy/types.py | 305 +
spyce-2.1/sqlalchemy/types.pyc | Bin 0 -> 21020 bytes
spyce-2.1/sqlalchemy/util.py | 301 +
spyce-2.1/sqlalchemy/util.pyc | Bin 0 -> 16213 bytes
spyce-2.1/tags/_coreutil.py | 83 +
spyce-2.1/tags/_coreutil.pyc | Bin 0 -> 3924 bytes
spyce-2.1/tags/_formhelper.py | 47 +
spyce-2.1/tags/_formhelper.pyc | Bin 0 -> 2353 bytes
spyce-2.1/tags/core.py | 143 +
spyce-2.1/tags/core.pyc | Bin 0 -> 7282 bytes
spyce-2.1/tags/form.py | 227 +
spyce-2.1/tags/form.pyc | Bin 0 -> 11790 bytes
spyce-2.1/tags/render.spi | 54 +
spyce-2.1/tags/render.spic | Bin 0 -> 4520 bytes
spyce-2.1/tree.py | 70 +
spyce-2.1/tree.pyc | Bin 0 -> 2766 bytes
spyce-2.1/util/form_calendar.gif | Bin 0 -> 152 bytes
spyce-2.1/util/form_calendar.js | 1464 +++++
spyce-2.1/verchk.py | 36 +
spyce-2.1/verchk.pyc | Bin 0 -> 950 bytes
.../www/demo-site/_util/form_calendar.gif | Bin 0 -> 152 bytes
.../www/demo-site/_util/form_calendar.js | 1464 +++++
.../www/demo-site/demos/chat/chatbox.spi | 38 +
spyce-2.1/www/demo-site/demos/chat/index.spy | 48 +
.../www/demo-site/demos/to-do-durus/index.spy | 37 +
.../demo-site/demos/to-do-durus/list-view.spy | 48 +
.../www/demo-site/demos/to-do-durus/model.py | 61 +
.../demo-site/demos/to-do-durus/parent.spi | 14 +
.../demo-site/demos/to-do-durus/tododata.py | 25 +
.../www/demo-site/demos/to-do/actions.py | 34 +
spyce-2.1/www/demo-site/demos/to-do/index.spy | 34 +
.../www/demo-site/demos/to-do/list-view.spy | 47 +
.../www/demo-site/demos/to-do/parent.spi | 14 +
spyce-2.1/www/demo-site/demos/to-do/todo.db | Bin 0 -> 4096 bytes
spyce-2.1/www/demo-site/demos/to-do/todo.sql | 11 +
.../www/demo-site/docs/compress.src.html | 210 +
spyce-2.1/www/demo-site/docs/config.src.html | 207 +
spyce-2.1/www/demo-site/docs/cookie.src.html | 258 +
spyce-2.1/www/demo-site/docs/db.src.html | 224 +
spyce-2.1/www/demo-site/docs/doc-add.html | 213 +
.../www/demo-site/docs/doc-add_history.html | 246 +
.../www/demo-site/docs/doc-add_perf.html | 311 +
spyce-2.1/www/demo-site/docs/doc-conf.html | 231 +
.../www/demo-site/docs/doc-conf_apache.html | 223 +
.../demo-site/docs/doc-conf_modpython.html | 227 +
.../www/demo-site/docs/doc-conf_next.html | 247 +
.../www/demo-site/docs/doc-conf_overview.html | 257 +
.../www/demo-site/docs/doc-conf_proxy.html | 232 +
.../www/demo-site/docs/doc-conf_rpm.html | 229 +
.../www/demo-site/docs/doc-conf_source.html | 257 +
.../www/demo-site/docs/doc-conf_windows.html | 260 +
spyce-2.1/www/demo-site/docs/doc-intro.html | 245 +
.../www/demo-site/docs/doc-intro_design.html | 254 +
.../demo-site/docs/doc-intro_rationale.html | 322 ++
spyce-2.1/www/demo-site/docs/doc-lang.html | 265 +
.../www/demo-site/docs/doc-lang_asp.html | 227 +
.../www/demo-site/docs/doc-lang_chunk.html | 248 +
.../www/demo-site/docs/doc-lang_chunkc.html | 257 +
.../www/demo-site/docs/doc-lang_comment.html | 217 +
.../demo-site/docs/doc-lang_directive.html | 311 +
.../www/demo-site/docs/doc-lang_expr.html | 224 +
.../www/demo-site/docs/doc-lang_lambda.html | 286 +
.../www/demo-site/docs/doc-lang_stmt.html | 276 +
.../www/demo-site/docs/doc-lang_string.html | 230 +
spyce-2.1/www/demo-site/docs/doc-mod.html | 248 +
.../www/demo-site/docs/doc-mod_compress.html | 266 +
.../www/demo-site/docs/doc-mod_cookie.html | 309 +
spyce-2.1/www/demo-site/docs/doc-mod_db.html | 428 ++
.../www/demo-site/docs/doc-mod_error.html | 337 ++
.../www/demo-site/docs/doc-mod_include.html | 381 ++
.../www/demo-site/docs/doc-mod_internal.html | 224 +
.../www/demo-site/docs/doc-mod_lambda.html | 238 +
spyce-2.1/www/demo-site/docs/doc-mod_new.html | 373 ++
.../www/demo-site/docs/doc-mod_pool.html | 259 +
.../www/demo-site/docs/doc-mod_redirect.html | 294 +
.../www/demo-site/docs/doc-mod_request.html | 494 ++
.../www/demo-site/docs/doc-mod_response.html | 315 +
.../www/demo-site/docs/doc-mod_session.html | 287 +
.../www/demo-site/docs/doc-mod_stdout.html | 260 +
.../www/demo-site/docs/doc-mod_taglib.html | 253 +
.../www/demo-site/docs/doc-mod_transform.html | 410 ++
spyce-2.1/www/demo-site/docs/doc-runtime.html | 239 +
.../demo-site/docs/doc-runtime_cmdline.html | 241 +
.../demo-site/docs/doc-runtime_common.html | 609 ++
.../demo-site/docs/doc-runtime_except.html | 262 +
.../www/demo-site/docs/doc-runtime_prog.html | 235 +
.../docs/doc-runtime_prog_basicUsage.html | 231 +
.../docs/doc-runtime_prog_example.html | 311 +
.../doc-runtime_prog_passingParameters.html | 226 +
.../doc-runtime_prog_requestAndResponse.html | 213 +
.../demo-site/docs/doc-runtime_static.html | 236 +
.../demo-site/docs/doc-runtime_transform.html | 239 +
.../www/demo-site/docs/doc-runtime_util.html | 221 +
.../docs/doc-runtime_util_scheduler.html | 354 ++
.../demo-site/docs/doc-runtime_util_su.html | 225 +
.../www/demo-site/docs/doc-runtime_web.html | 222 +
spyce-2.1/www/demo-site/docs/doc-single.html | 5118 +++++++++++++++++
spyce-2.1/www/demo-site/docs/doc-single.spy | 3 +
spyce-2.1/www/demo-site/docs/doc-tag.html | 305 +
.../www/demo-site/docs/doc-tag_core.html | 392 ++
.../www/demo-site/docs/doc-tag_form.html | 469 ++
.../www/demo-site/docs/doc-tag_handlers.html | 367 ++
spyce-2.1/www/demo-site/docs/doc-tag_new.html | 444 ++
.../www/demo-site/docs/doc-tag_new2.html | 291 +
spyce-2.1/www/demo-site/docs/doc.html | 528 ++
spyce-2.1/www/demo-site/docs/doc.spy | 3727 ++++++++++++
spyce-2.1/www/demo-site/docs/eg.html | 335 ++
spyce-2.1/www/demo-site/docs/eg.spy | 84 +
spyce-2.1/www/demo-site/docs/error.src.html | 227 +
spyce-2.1/www/demo-site/docs/examples/asp.spy | 6 +
.../www/demo-site/docs/examples/compress.spy | 9 +
.../www/demo-site/docs/examples/config.spy | 6 +
.../www/demo-site/docs/examples/cookie.spy | 57 +
spyce-2.1/www/demo-site/docs/examples/db.spy | 23 +
.../www/demo-site/docs/examples/delay.spy | 9 +
.../www/demo-site/docs/examples/error.spi | 26 +
.../www/demo-site/docs/examples/error.spy | 3 +
.../demo-site/docs/examples/errorSyntax.spy | 1 +
.../www/demo-site/docs/examples/ex2hello.spy | 31 +
.../demo-site/docs/examples/fileupload.spy | 28 +
.../www/demo-site/docs/examples/filter.py | 13 +
.../www/demo-site/docs/examples/formintro.spy | 17 +
.../www/demo-site/docs/examples/formtag.spy | 50 +
spyce-2.1/www/demo-site/docs/examples/gif.spy | 10 +
.../demo-site/docs/examples/handlerintro.spy | 16 +
.../docs/examples/handlervalidate.spy | 21 +
.../docs/examples/handlervalidate2.spy | 14 +
.../docs/examples/hello-templated.spy | 17 +
.../www/demo-site/docs/examples/hello.spy | 5 +
.../www/demo-site/docs/examples/hello2.spy | 21 +
.../www/demo-site/docs/examples/include.spi | 8 +
.../www/demo-site/docs/examples/include.spy | 13 +
.../demo-site/docs/examples/includestatic.spi | 4 +
.../demo-site/docs/examples/includestatic.spy | 10 +
.../www/demo-site/docs/examples/info.spy | 99 +
.../docs/examples/login-optional.spy | 16 +
.../docs/examples/login-required.spy | 8 +
.../www/demo-site/docs/examples/myModule.py | 6 +
.../www/demo-site/docs/examples/myTaglib.py | 18 +
.../www/demo-site/docs/examples/pool.spy | 15 +
.../docs/examples/programmaticUsage.py | 70 +
.../www/demo-site/docs/examples/recurse.spy | 10 +
.../www/demo-site/docs/examples/redirect.spy | 39 +
.../www/demo-site/docs/examples/request.spy | 35 +
.../www/demo-site/docs/examples/scheduling.py | 9 +
.../www/demo-site/docs/examples/session2.spy | 8 +
.../www/demo-site/docs/examples/spyce.gif | Bin 0 -> 4705 bytes
.../www/demo-site/docs/examples/spylambda.spy | 33 +
.../www/demo-site/docs/examples/stdout.spy | 11 +
spyce-2.1/www/demo-site/docs/examples/tag.spy | 6 +
.../www/demo-site/docs/examples/tagbold.spi | 5 +
.../www/demo-site/docs/examples/transform.spy | 47 +
.../www/demo-site/docs/examples/whatsnew.spy | 22 +
.../www/demo-site/docs/examples/whatsnew2.spy | 18 +
.../www/demo-site/docs/fileupload.src.html | 229 +
spyce-2.1/www/demo-site/docs/filter.src.html | 233 +
.../www/demo-site/docs/formintro.src.html | 217 +
spyce-2.1/www/demo-site/docs/formtag.src.html | 251 +
spyce-2.1/www/demo-site/docs/get.html | 220 +
spyce-2.1/www/demo-site/docs/get.spy | 47 +
spyce-2.1/www/demo-site/docs/gif.src.html | 211 +
.../www/demo-site/docs/handlerintro.src.html | 217 +
.../demo-site/docs/handlervalidate.src.html | 222 +
.../demo-site/docs/handlervalidate2.src.html | 215 +
.../demo-site/docs/hello-templated.src.html | 218 +
spyce-2.1/www/demo-site/docs/include.src.html | 209 +
.../www/demo-site/docs/includestatic.src.html | 205 +
spyce-2.1/www/demo-site/docs/license.html | 236 +
spyce-2.1/www/demo-site/docs/license.spy | 15 +
spyce-2.1/www/demo-site/docs/links.html | 256 +
spyce-2.1/www/demo-site/docs/links.spy | 90 +
.../demo-site/docs/login-optional.src.html | 216 +
.../demo-site/docs/login-required.src.html | 209 +
spyce-2.1/www/demo-site/docs/mail.html | 279 +
spyce-2.1/www/demo-site/docs/mail.spy | 118 +
.../www/demo-site/docs/myModule.src.html | 226 +
.../www/demo-site/docs/myTaglib.src.html | 238 +
spyce-2.1/www/demo-site/docs/pool.src.html | 216 +
.../demo-site/docs/programmaticUsage.src.html | 290 +
.../www/demo-site/docs/redirect.src.html | 240 +
spyce-2.1/www/demo-site/docs/request.src.html | 236 +
.../www/demo-site/docs/scheduling.src.html | 229 +
.../www/demo-site/docs/session2.src.html | 209 +
.../www/demo-site/docs/spylambda.src.html | 234 +
spyce-2.1/www/demo-site/docs/stdout.src.html | 212 +
spyce-2.1/www/demo-site/docs/tag.src.html | 207 +
spyce-2.1/www/demo-site/docs/tagbold.src.html | 205 +
.../www/demo-site/docs/transform.src.html | 248 +
spyce-2.1/www/demo-site/docs/tutorial.html | 262 +
spyce-2.1/www/demo-site/docs/tutorial.spy | 77 +
.../www/demo-site/docs/whats-new-2-1.html | 364 ++
.../www/demo-site/docs/whats-new-2-1.spy | 115 +
spyce-2.1/www/demo-site/dump.spy | 17 +
spyce-2.1/www/demo-site/inc/head.spi | 23 +
spyce-2.1/www/demo-site/inc/head1.spi | 166 +
spyce-2.1/www/demo-site/inc/static.spi | 53 +
spyce-2.1/www/demo-site/inc/tail.spi | 26 +
spyce-2.1/www/demo-site/index.html | 213 +
spyce-2.1/www/demo-site/index.spy | 101 +
spyce-2.1/www/demo-site/parent.spi | 121 +
spyce-2.1/www/demo-site/pp.py | 69 +
spyce-2.1/www/demo-site/pp.pyc | Bin 0 -> 2117 bytes
spyce-2.1/www/demo-site/spyce.gif | Bin 0 -> 4705 bytes
spyce-2.1/www/demo-site/spyce12.png | 9 +
spyce-2.1/www/demo-site/spyce16.png | 9 +
spyce-2.1/www/demo-site/spyce2.png | Bin 0 -> 812 bytes
spyce-2.1/www/demo-site/spyce32.png | 9 +
spyce-2.1/www/demo-site/spycefav.ico | Bin 0 -> 3637 bytes
spyce-2.1/www/demo-site/spycepow.gif | Bin 0 -> 2657 bytes
spyce-2.1/www/demo-site/spycepow2.gif | Bin 0 -> 1553 bytes
spyce-2.1/www/demo-site/spycepow3.gif | Bin 0 -> 22150 bytes
spyce-2.1/www/demo-site/trans1x1.gif | Bin 0 -> 43 bytes
spyce-2.1/www/index.spy | 292 +
spyce-2.1/www/search.spy | 268 +
spyce-2.1/www/site.css | 62 +
419 files changed, 74958 insertions(+)
create mode 100755 spyce-2.1/CHANGES
create mode 100755 spyce-2.1/LICENCE
create mode 100755 spyce-2.1/README
create mode 100755 spyce-2.1/RELEASE
create mode 100755 spyce-2.1/THANKS
create mode 100755 spyce-2.1/_eginfo.txt
create mode 100755 spyce-2.1/contrib/jedit-spyce.xml
create mode 100755 spyce-2.1/contrib/kate-spyce.xml
create mode 100755 spyce-2.1/contrib/modules/automaton.py
create mode 100755 spyce-2.1/contrib/modules/automaton.spy
create mode 100755 spyce-2.1/contrib/modules/etag.py
create mode 100755 spyce-2.1/contrib/modules/mail.py
create mode 100755 spyce-2.1/contrib/modules/mail.txt
create mode 100755 spyce-2.1/contrib/modules/session1.py
create mode 100755 spyce-2.1/contrib/modules/spydurus.py
create mode 100755 spyce-2.1/contrib/modules/template.py
create mode 100755 spyce-2.1/contrib/modules/toc.py
create mode 100644 spyce-2.1/contrib/modules/toc.pyc
create mode 100755 spyce-2.1/contrib/pyweboff/config.py
create mode 100755 spyce-2.1/contrib/pyweboff/lib/actions.py
create mode 100755 spyce-2.1/contrib/pyweboff/lib/pyweboff.sql
create mode 100755 spyce-2.1/contrib/pyweboff/www/index.spy
create mode 100755 spyce-2.1/contrib/pyweboff/www/parent.spi
create mode 100755 spyce-2.1/contrib/pyweboff/www/user-new.spy
create mode 100755 spyce-2.1/contrib/pyweboff/www/util/form_calendar.gif
create mode 100755 spyce-2.1/contrib/pyweboff/www/util/form_calendar.js
create mode 100755 spyce-2.1/contrib/pyweboff/www/util/spyce2.png
create mode 100755 spyce-2.1/contrib/readme.txt
create mode 100755 spyce-2.1/contrib/spyce.el
create mode 100755 spyce-2.1/contrib/spyce.vim
create mode 100755 spyce-2.1/contrib/tags/flow.py
create mode 100755 spyce-2.1/contrib/xitami/lrwplib.py
create mode 100755 spyce-2.1/contrib/xitami/spyceLRWP.py
create mode 100755 spyce-2.1/debugspyce.py
create mode 100755 spyce-2.1/fcgi.py
create mode 100755 spyce-2.1/installHelper.py
create mode 100755 spyce-2.1/misc/addfirstline.sh
create mode 100755 spyce-2.1/misc/benchmark/hello.c
create mode 100755 spyce-2.1/misc/benchmark/hello.html
create mode 100755 spyce-2.1/misc/benchmark/hello.jsp
create mode 100755 spyce-2.1/misc/benchmark/hello.php
create mode 100755 spyce-2.1/misc/benchmark/hello.py
create mode 100755 spyce-2.1/misc/benchmark/hellopy.cgi
create mode 100755 spyce-2.1/misc/one-check.bmp
create mode 100755 spyce-2.1/misc/one-nocheck.bmp
create mode 100755 spyce-2.1/misc/pics/pilpel1-edit.jpg
create mode 100755 spyce-2.1/misc/pics/pilpel2.jpg
create mode 100755 spyce-2.1/misc/pics/pypow.gif
create mode 100755 spyce-2.1/misc/pics/spyce-border.ico
create mode 100755 spyce-2.1/misc/pics/spyce.ico
create mode 100755 spyce-2.1/misc/pics/spyce1.bmp
create mode 100755 spyce-2.1/misc/pics/spyce2.bmp
create mode 100755 spyce-2.1/misc/pics/spyce3.bmp
create mode 100755 spyce-2.1/misc/pics/spyce3.gif
create mode 100755 spyce-2.1/misc/pics/spyce3.jpg
create mode 100755 spyce-2.1/misc/runsfcgi.py
create mode 100755 spyce-2.1/misc/spyceconf-sf.py
create mode 100755 spyce-2.1/modules/compress.py
create mode 100755 spyce-2.1/modules/cookie.py
create mode 100644 spyce-2.1/modules/cookie.pyc
create mode 100755 spyce-2.1/modules/error.py
create mode 100644 spyce-2.1/modules/error.pyc
create mode 100755 spyce-2.1/modules/include.py
create mode 100644 spyce-2.1/modules/include.pyc
create mode 100755 spyce-2.1/modules/pool.py
create mode 100644 spyce-2.1/modules/pool.pyc
create mode 100755 spyce-2.1/modules/redirect.py
create mode 100755 spyce-2.1/modules/redirect.pyc
create mode 100755 spyce-2.1/modules/request.py
create mode 100644 spyce-2.1/modules/request.pyc
create mode 100755 spyce-2.1/modules/response.py
create mode 100644 spyce-2.1/modules/response.pyc
create mode 100755 spyce-2.1/modules/session.py
create mode 100644 spyce-2.1/modules/session.pyc
create mode 100755 spyce-2.1/modules/spylambda.py
create mode 100644 spyce-2.1/modules/spylambda.pyc
create mode 100755 spyce-2.1/modules/stdout.py
create mode 100644 spyce-2.1/modules/stdout.pyc
create mode 100755 spyce-2.1/modules/taglib.py
create mode 100755 spyce-2.1/modules/transform.py
create mode 100644 spyce-2.1/modules/transform.pyc
create mode 100755 spyce-2.1/pcqueue.py
create mode 100644 spyce-2.1/pcqueue.pyc
create mode 100755 spyce-2.1/run_spyceCGI.py
create mode 100755 spyce-2.1/run_spyceCmd.py
create mode 100755 spyce-2.1/run_spyceModpy.py
create mode 100755 spyce-2.1/scheduler.py
create mode 100644 spyce-2.1/scheduler.pyc
create mode 100755 spyce-2.1/spyce.mime
create mode 100755 spyce-2.1/spyce.nsi
create mode 100755 spyce-2.1/spyce.nsi.in
create mode 100755 spyce-2.1/spyce.py
create mode 100644 spyce-2.1/spyce.pyc
create mode 100755 spyce-2.1/spyce.spec.in
create mode 100755 spyce-2.1/spyceApache.conf
create mode 100755 spyce-2.1/spyceCGI.py
create mode 100755 spyce-2.1/spyceCache.py
create mode 100644 spyce-2.1/spyceCache.pyc
create mode 100755 spyce-2.1/spyceCmd.py
create mode 100644 spyce-2.1/spyceCmd.pyc
create mode 100755 spyce-2.1/spyceCompile.py
create mode 100644 spyce-2.1/spyceCompile.pyc
create mode 100755 spyce-2.1/spyceConsole.py
create mode 100755 spyce-2.1/spyceException.py
create mode 100644 spyce-2.1/spyceException.pyc
create mode 100755 spyce-2.1/spyceLock.py
create mode 100644 spyce-2.1/spyceLock.pyc
create mode 100755 spyce-2.1/spyceModpy.py
create mode 100755 spyce-2.1/spyceModule.py
create mode 100644 spyce-2.1/spyceModule.pyc
create mode 100755 spyce-2.1/spycePreload.py
create mode 100644 spyce-2.1/spycePreload.pyc
create mode 100755 spyce-2.1/spyceProject.py
create mode 100755 spyce-2.1/spyceTag.py
create mode 100644 spyce-2.1/spyceTag.pyc
create mode 100755 spyce-2.1/spyceUtil.py
create mode 100644 spyce-2.1/spyceUtil.pyc
create mode 100755 spyce-2.1/spyceWWW.py
create mode 100644 spyce-2.1/spyceWWW.pyc
create mode 100755 spyce-2.1/spyceconf.py
create mode 100644 spyce-2.1/spyceconf.pyc
create mode 100755 spyce-2.1/sqlalchemy/__init__.py
create mode 100644 spyce-2.1/sqlalchemy/__init__.pyc
create mode 100755 spyce-2.1/sqlalchemy/ansisql.py
create mode 100644 spyce-2.1/sqlalchemy/ansisql.pyc
create mode 100755 spyce-2.1/sqlalchemy/databases/__init__.py
create mode 100644 spyce-2.1/sqlalchemy/databases/__init__.pyc
create mode 100755 spyce-2.1/sqlalchemy/databases/firebird.py
create mode 100755 spyce-2.1/sqlalchemy/databases/information_schema.py
create mode 100755 spyce-2.1/sqlalchemy/databases/mssql.py
create mode 100755 spyce-2.1/sqlalchemy/databases/mysql.py
create mode 100755 spyce-2.1/sqlalchemy/databases/oracle.py
create mode 100755 spyce-2.1/sqlalchemy/databases/postgres.py
create mode 100755 spyce-2.1/sqlalchemy/databases/sqlite.py
create mode 100644 spyce-2.1/sqlalchemy/databases/sqlite.pyc
create mode 100755 spyce-2.1/sqlalchemy/engine/__init__.py
create mode 100644 spyce-2.1/sqlalchemy/engine/__init__.pyc
create mode 100755 spyce-2.1/sqlalchemy/engine/base.py
create mode 100644 spyce-2.1/sqlalchemy/engine/base.pyc
create mode 100755 spyce-2.1/sqlalchemy/engine/default.py
create mode 100644 spyce-2.1/sqlalchemy/engine/default.pyc
create mode 100755 spyce-2.1/sqlalchemy/engine/strategies.py
create mode 100644 spyce-2.1/sqlalchemy/engine/strategies.pyc
create mode 100755 spyce-2.1/sqlalchemy/engine/threadlocal.py
create mode 100644 spyce-2.1/sqlalchemy/engine/threadlocal.pyc
create mode 100755 spyce-2.1/sqlalchemy/engine/url.py
create mode 100644 spyce-2.1/sqlalchemy/engine/url.pyc
create mode 100755 spyce-2.1/sqlalchemy/exceptions.py
create mode 100644 spyce-2.1/sqlalchemy/exceptions.pyc
create mode 100755 spyce-2.1/sqlalchemy/ext/__init__.py
create mode 100644 spyce-2.1/sqlalchemy/ext/__init__.pyc
create mode 100755 spyce-2.1/sqlalchemy/ext/activemapper.py
create mode 100755 spyce-2.1/sqlalchemy/ext/assignmapper.py
create mode 100644 spyce-2.1/sqlalchemy/ext/assignmapper.pyc
create mode 100755 spyce-2.1/sqlalchemy/ext/associationproxy.py
create mode 100755 spyce-2.1/sqlalchemy/ext/proxy.py
create mode 100755 spyce-2.1/sqlalchemy/ext/selectresults.py
create mode 100755 spyce-2.1/sqlalchemy/ext/sessioncontext.py
create mode 100644 spyce-2.1/sqlalchemy/ext/sessioncontext.pyc
create mode 100755 spyce-2.1/sqlalchemy/ext/sqlsoup.py
create mode 100644 spyce-2.1/sqlalchemy/ext/sqlsoup.pyc
create mode 100755 spyce-2.1/sqlalchemy/logging.py
create mode 100644 spyce-2.1/sqlalchemy/logging.pyc
create mode 100755 spyce-2.1/sqlalchemy/mods/__init__.py
create mode 100755 spyce-2.1/sqlalchemy/mods/legacy_session.py
create mode 100755 spyce-2.1/sqlalchemy/mods/selectresults.py
create mode 100755 spyce-2.1/sqlalchemy/mods/threadlocal.py
create mode 100755 spyce-2.1/sqlalchemy/orm/__init__.py
create mode 100644 spyce-2.1/sqlalchemy/orm/__init__.pyc
create mode 100755 spyce-2.1/sqlalchemy/orm/attributes.py
create mode 100644 spyce-2.1/sqlalchemy/orm/attributes.pyc
create mode 100755 spyce-2.1/sqlalchemy/orm/dependency.py
create mode 100644 spyce-2.1/sqlalchemy/orm/dependency.pyc
create mode 100755 spyce-2.1/sqlalchemy/orm/interfaces.py
create mode 100644 spyce-2.1/sqlalchemy/orm/interfaces.pyc
create mode 100755 spyce-2.1/sqlalchemy/orm/mapper.py
create mode 100644 spyce-2.1/sqlalchemy/orm/mapper.pyc
create mode 100755 spyce-2.1/sqlalchemy/orm/properties.py
create mode 100644 spyce-2.1/sqlalchemy/orm/properties.pyc
create mode 100755 spyce-2.1/sqlalchemy/orm/query.py
create mode 100644 spyce-2.1/sqlalchemy/orm/query.pyc
create mode 100755 spyce-2.1/sqlalchemy/orm/session.py
create mode 100644 spyce-2.1/sqlalchemy/orm/session.pyc
create mode 100755 spyce-2.1/sqlalchemy/orm/strategies.py
create mode 100644 spyce-2.1/sqlalchemy/orm/strategies.pyc
create mode 100755 spyce-2.1/sqlalchemy/orm/sync.py
create mode 100644 spyce-2.1/sqlalchemy/orm/sync.pyc
create mode 100755 spyce-2.1/sqlalchemy/orm/unitofwork.py
create mode 100644 spyce-2.1/sqlalchemy/orm/unitofwork.pyc
create mode 100755 spyce-2.1/sqlalchemy/orm/uowdumper.py
create mode 100755 spyce-2.1/sqlalchemy/orm/util.py
create mode 100644 spyce-2.1/sqlalchemy/orm/util.pyc
create mode 100755 spyce-2.1/sqlalchemy/pool.py
create mode 100644 spyce-2.1/sqlalchemy/pool.pyc
create mode 100755 spyce-2.1/sqlalchemy/queue.py
create mode 100644 spyce-2.1/sqlalchemy/queue.pyc
create mode 100755 spyce-2.1/sqlalchemy/schema.py
create mode 100644 spyce-2.1/sqlalchemy/schema.pyc
create mode 100755 spyce-2.1/sqlalchemy/sql.py
create mode 100644 spyce-2.1/sqlalchemy/sql.pyc
create mode 100755 spyce-2.1/sqlalchemy/sql_util.py
create mode 100644 spyce-2.1/sqlalchemy/sql_util.pyc
create mode 100755 spyce-2.1/sqlalchemy/topological.py
create mode 100644 spyce-2.1/sqlalchemy/topological.pyc
create mode 100755 spyce-2.1/sqlalchemy/types.py
create mode 100644 spyce-2.1/sqlalchemy/types.pyc
create mode 100755 spyce-2.1/sqlalchemy/util.py
create mode 100644 spyce-2.1/sqlalchemy/util.pyc
create mode 100755 spyce-2.1/tags/_coreutil.py
create mode 100644 spyce-2.1/tags/_coreutil.pyc
create mode 100755 spyce-2.1/tags/_formhelper.py
create mode 100644 spyce-2.1/tags/_formhelper.pyc
create mode 100755 spyce-2.1/tags/core.py
create mode 100644 spyce-2.1/tags/core.pyc
create mode 100755 spyce-2.1/tags/form.py
create mode 100644 spyce-2.1/tags/form.pyc
create mode 100755 spyce-2.1/tags/render.spi
create mode 100644 spyce-2.1/tags/render.spic
create mode 100755 spyce-2.1/tree.py
create mode 100644 spyce-2.1/tree.pyc
create mode 100755 spyce-2.1/util/form_calendar.gif
create mode 100755 spyce-2.1/util/form_calendar.js
create mode 100755 spyce-2.1/verchk.py
create mode 100644 spyce-2.1/verchk.pyc
create mode 100755 spyce-2.1/www/demo-site/_util/form_calendar.gif
create mode 100755 spyce-2.1/www/demo-site/_util/form_calendar.js
create mode 100755 spyce-2.1/www/demo-site/demos/chat/chatbox.spi
create mode 100755 spyce-2.1/www/demo-site/demos/chat/index.spy
create mode 100755 spyce-2.1/www/demo-site/demos/to-do-durus/index.spy
create mode 100755 spyce-2.1/www/demo-site/demos/to-do-durus/list-view.spy
create mode 100755 spyce-2.1/www/demo-site/demos/to-do-durus/model.py
create mode 100755 spyce-2.1/www/demo-site/demos/to-do-durus/parent.spi
create mode 100755 spyce-2.1/www/demo-site/demos/to-do-durus/tododata.py
create mode 100755 spyce-2.1/www/demo-site/demos/to-do/actions.py
create mode 100755 spyce-2.1/www/demo-site/demos/to-do/index.spy
create mode 100755 spyce-2.1/www/demo-site/demos/to-do/list-view.spy
create mode 100755 spyce-2.1/www/demo-site/demos/to-do/parent.spi
create mode 100755 spyce-2.1/www/demo-site/demos/to-do/todo.db
create mode 100755 spyce-2.1/www/demo-site/demos/to-do/todo.sql
create mode 100755 spyce-2.1/www/demo-site/docs/compress.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/config.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/cookie.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/db.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-add.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-add_history.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-add_perf.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-conf.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-conf_apache.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-conf_modpython.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-conf_next.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-conf_overview.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-conf_proxy.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-conf_rpm.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-conf_source.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-conf_windows.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-intro.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-intro_design.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-intro_rationale.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-lang.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-lang_asp.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-lang_chunk.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-lang_chunkc.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-lang_comment.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-lang_directive.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-lang_expr.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-lang_lambda.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-lang_stmt.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-lang_string.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_compress.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_cookie.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_db.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_error.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_include.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_internal.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_lambda.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_new.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_pool.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_redirect.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_request.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_response.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_session.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_stdout.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_taglib.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-mod_transform.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-runtime.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-runtime_cmdline.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-runtime_common.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-runtime_except.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-runtime_prog.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-runtime_prog_basicUsage.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-runtime_prog_example.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-runtime_prog_passingParameters.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-runtime_prog_requestAndResponse.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-runtime_static.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-runtime_transform.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-runtime_util.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-runtime_util_scheduler.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-runtime_util_su.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-runtime_web.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-single.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-single.spy
create mode 100755 spyce-2.1/www/demo-site/docs/doc-tag.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-tag_core.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-tag_form.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-tag_handlers.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-tag_new.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc-tag_new2.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc.html
create mode 100755 spyce-2.1/www/demo-site/docs/doc.spy
create mode 100755 spyce-2.1/www/demo-site/docs/eg.html
create mode 100755 spyce-2.1/www/demo-site/docs/eg.spy
create mode 100755 spyce-2.1/www/demo-site/docs/error.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/examples/asp.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/compress.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/config.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/cookie.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/db.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/delay.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/error.spi
create mode 100755 spyce-2.1/www/demo-site/docs/examples/error.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/errorSyntax.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/ex2hello.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/fileupload.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/filter.py
create mode 100755 spyce-2.1/www/demo-site/docs/examples/formintro.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/formtag.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/gif.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/handlerintro.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/handlervalidate.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/handlervalidate2.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/hello-templated.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/hello.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/hello2.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/include.spi
create mode 100755 spyce-2.1/www/demo-site/docs/examples/include.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/includestatic.spi
create mode 100755 spyce-2.1/www/demo-site/docs/examples/includestatic.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/info.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/login-optional.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/login-required.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/myModule.py
create mode 100755 spyce-2.1/www/demo-site/docs/examples/myTaglib.py
create mode 100755 spyce-2.1/www/demo-site/docs/examples/pool.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/programmaticUsage.py
create mode 100755 spyce-2.1/www/demo-site/docs/examples/recurse.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/redirect.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/request.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/scheduling.py
create mode 100755 spyce-2.1/www/demo-site/docs/examples/session2.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/spyce.gif
create mode 100755 spyce-2.1/www/demo-site/docs/examples/spylambda.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/stdout.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/tag.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/tagbold.spi
create mode 100755 spyce-2.1/www/demo-site/docs/examples/transform.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/whatsnew.spy
create mode 100755 spyce-2.1/www/demo-site/docs/examples/whatsnew2.spy
create mode 100755 spyce-2.1/www/demo-site/docs/fileupload.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/filter.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/formintro.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/formtag.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/get.html
create mode 100755 spyce-2.1/www/demo-site/docs/get.spy
create mode 100755 spyce-2.1/www/demo-site/docs/gif.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/handlerintro.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/handlervalidate.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/handlervalidate2.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/hello-templated.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/include.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/includestatic.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/license.html
create mode 100755 spyce-2.1/www/demo-site/docs/license.spy
create mode 100755 spyce-2.1/www/demo-site/docs/links.html
create mode 100755 spyce-2.1/www/demo-site/docs/links.spy
create mode 100755 spyce-2.1/www/demo-site/docs/login-optional.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/login-required.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/mail.html
create mode 100755 spyce-2.1/www/demo-site/docs/mail.spy
create mode 100755 spyce-2.1/www/demo-site/docs/myModule.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/myTaglib.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/pool.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/programmaticUsage.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/redirect.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/request.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/scheduling.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/session2.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/spylambda.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/stdout.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/tag.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/tagbold.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/transform.src.html
create mode 100755 spyce-2.1/www/demo-site/docs/tutorial.html
create mode 100755 spyce-2.1/www/demo-site/docs/tutorial.spy
create mode 100755 spyce-2.1/www/demo-site/docs/whats-new-2-1.html
create mode 100755 spyce-2.1/www/demo-site/docs/whats-new-2-1.spy
create mode 100755 spyce-2.1/www/demo-site/dump.spy
create mode 100755 spyce-2.1/www/demo-site/inc/head.spi
create mode 100755 spyce-2.1/www/demo-site/inc/head1.spi
create mode 100755 spyce-2.1/www/demo-site/inc/static.spi
create mode 100755 spyce-2.1/www/demo-site/inc/tail.spi
create mode 100755 spyce-2.1/www/demo-site/index.html
create mode 100755 spyce-2.1/www/demo-site/index.spy
create mode 100755 spyce-2.1/www/demo-site/parent.spi
create mode 100755 spyce-2.1/www/demo-site/pp.py
create mode 100755 spyce-2.1/www/demo-site/pp.pyc
create mode 100755 spyce-2.1/www/demo-site/spyce.gif
create mode 100755 spyce-2.1/www/demo-site/spyce12.png
create mode 100755 spyce-2.1/www/demo-site/spyce16.png
create mode 100755 spyce-2.1/www/demo-site/spyce2.png
create mode 100755 spyce-2.1/www/demo-site/spyce32.png
create mode 100755 spyce-2.1/www/demo-site/spycefav.ico
create mode 100755 spyce-2.1/www/demo-site/spycepow.gif
create mode 100755 spyce-2.1/www/demo-site/spycepow2.gif
create mode 100755 spyce-2.1/www/demo-site/spycepow3.gif
create mode 100755 spyce-2.1/www/demo-site/trans1x1.gif
create mode 100755 spyce-2.1/www/index.spy
create mode 100755 spyce-2.1/www/search.spy
create mode 100755 spyce-2.1/www/site.css
diff --git a/spyce-2.1/CHANGES b/spyce-2.1/CHANGES
new file mode 100755
index 0000000..726af01
--- /dev/null
+++ b/spyce-2.1/CHANGES
@@ -0,0 +1,747 @@
+Change Log
+* denotes potential backwards incompatibility
+
+v2.1.3
+ db module requires PK definition up-front (instead of erroring out later)
+ default login render tags give better feedback on unsuccessful login
+ raise NameError instead of string exception for invalid eval'd tag attributes
+ fixes for spyceProject
+ fix kwargs render-through on spy: list and table tags
+ fix auto-selecting of multiple defaults in compound controls
+ fix for class-based exceptions in old-style tag exception handlers
+
+v2.1.2
+ fix support for threadless python builds
+ fix using compiled tags within other tags
+ fix f:textarea
+ fix spyceUtil.extractValue for incomplete dict work-alikes
+ fix session1
+ fix tempdir on SF site
+
+v2.1
+ * Python 2.3 required now!
+
+ # New features #
+ SqlAlchemy integration
+ spy:login, spy:loginrequired tags
+ HandlerError integration with forms
+ :list, :bool, and :int hints to handler arg-marshalling
+ intelligent ValidationError
+ "data" option to f:select; added f:checkboxlist, f:radiolist
+ * removed "value" and "default" parameters to f:form
+ * changed semantics of "default" parameter to f:select
+ spy:ul, spy:ol, spy:dl, spy:table tags
+ new session module based on work by Conan Albrecht
+ pyweboff demo
+ * spy:parent searches for parent.spi in current directory before using config.defaultparent
+ * indexExtensions superceded by indexFiles in spyceconf
+ cached parent template lookup
+ improved ability to report certain types of Spyce syntax errors
+ Fixes for "special characters" in cookies
+ improved Programmatic Interface docs (Iwan Vosloo)
+ (f)cgi improvements by Pauli Virtanen
+
+ # New & updated examples (see docs/eg.html) #
+ db.spy
+ formtag.spy
+ formintro.spy
+ login-optional.spy
+ login-required.spy
+ handlerintro.spy
+ handlervalidate.spy
+ handlervalidate2.spy
+ session2.spy
+
+ # Deprecations / incompatibilities #
+ template module moved to contrib/
+ flow control from "core" taglib moved to contrib/flow.py
+ * these will probably be gone entirely in Spyce 2.2
+ * session [the old one] module renamed to contrib/session1.py
+ To make your old code happy, change [[.import name=session]] to [[.import name=session1 as=session]]
+ * Removed javascript generation done by f:submit; too "black magical"
+ * removed check_modules_no_restart option
+ * indexExtensions superceded by indexFiles in spyceconf
+
+ # Bug fixes #
+ module/tag cache obeys check_mtime
+ fix f:date javascript
+ KeyboardInterrupt fix for spyceWWW in restart-for-changes mode
+ spyceNotFound error can be customized again when using Spyce webserver
+ fix for multiline string constants in files with weird line endings
+
+
+v2.0.3
+ fix pool bug if server not in concurrency=threaded mode; reported by "Dude"
+ documentation improvements
+ avoid stomping on user python modules named 'config'; reported by Betty Li
+ (John Reese) fixed bad interaction of "redirect if directory path doesn't
+ end in /" and "look for default index.$[index extensions] files" code
+ in Spyce webserver
+ default concurrency mode for Spyce webserver is 'threading' instead of None
+ spyceProject script to automate new-project creation; see
+ http://spyce.sourceforge.net/docs/doc-conf_next.html
+
+v2.0.2
+ session_dir uses config.tmp by default if no directory is specified
+ fix for session_dir pickling on win32
+ fix for fileCache pickling bug on win32 reported by Jaros³aw Zabiello
+ fix for sessions + handlers problem reported by Jonathan Taylor
+ * all module init() methods are now run before handlers are called.
+
+v2.0.1
+ use tempfile.gettempdir() as default config.tmp
+ add originalsyspath to spyceconf
+ bugfixes for Active Tag compiler
+ portability & other bugfixes (John Reese)
+
+v2.0
+
+Important new features:
+ new-style Active Tags
+ - http://spyce.sourceforge.net/docs/doc-tag_new2.html
+ OpenACS-like (Tiles-ish, for you JSP people) parent/child layout system
+ - roughly the same speed as include.spyce, so using one parent template
+ has about 1/2 the overhead as the old standard
+ two-includes-for-header-and-footer.
+ - http://spyce.sourceforge.net/docs/doc-tag_core.html#parent
+ Active Handlers
+ reusable components without the leaky abstraction of ASP.NET et al.
+ - http://spyce.sourceforge.net/docs/doc-lang_handlers.html
+
+Important incompatibilities with version 1.3:
+ * replaced spyce.conf with spyceconf.py; functionality retained
+ * request.get and request.post now return [] instead of None by default
+ if name is specified but nothing was submitted corresponding to it
+ - "if request.get('foo'):" will work the same since [] is false for boolean purposes
+ - motivation: now you can simply write "for item in request.get('foo')"
+ without worrying about TypeError
+ many absolute paths are relative to spyce docroot now
+ - redirect.internal, includes, error.setFileHandler
+ * this will break your code if you were using absolute filesystem paths;
+ relative paths will be unaffected
+ * active tag attributes are evaluated in spyceProcess context
+ - no more explicit tagcontext
+
+Changes since 2.0-beta:
+ - mod_python fix
+ - fix sometimes ignoring config.debug option
+ - updated concurrency protection for more general case of all spyce_cache accesses
+ - added indexExtensions option to spyceconf
+ - included code by John Reese
+ - added contrib/spyce.el (Emacs Spyce mode)
+ - added form:date tag
+
+Other changes:
+ new config options for spyceWWW mode (-l):
+ check_modules_and_restart (default), check_modules_no_restart
+ - detects changed python modules and restarts/reloads to apply the changes
+ add "class chunk" language construct: [[! or <%!
+ - enables inlining handler callbacks
+ - http://spyce.sourceforge.net/docs/doc-lang_chunkc.html
+ add globaltags configuration option
+ - no longer have to specify [[.taglib]] on each page
+ spyceCompile.py now optimizes away taglib loads if the tag isn't used on the page
+ - so no penalty for putting a lot of stuff into globaltags
+ add form:date tag
+ add scheduler [python] module
+ spyceTag export method allows sending variables back to spyceProcess context
+ * core:let only supports singleton form; core:unlet removed
+ pool module now works with non-spyceWWW deployments, via pickle
+ * writeExpr emits empty string for None by default
+ - so you can write [[= request['foo'] ]] instead of
+ [[= request.getpost1('foo', '') ]]
+ param|file request filters
+ evaluate imports in spyceConfig as the very last part of spyceServer __init__
+ - so imported module can proceed to "import spyce; spyce.getServer()..."
+ added request.stack method
+ - infinite-loop checking for includes, parent templates
+ * removed include.fromFile field
+ added ipaddr WWW config option, for machines w/ multiple IPs
+ * spyceTagPlus.syntaxExists removed (equivalent checking now done automatically)
+ - only custom tag authors are affected by this
+ * spyce Modules are no longer allowed to redirect.internal during finish()
+ - it potentially prevented other modules' finish methods from being called
+ an active tag's directory is pushed onto sys.path for the duration of its use
+ - means tag can import from .py files in its directory no matter where installed
+ fix: apply --conf option to CGI mode
+ fix: follow symlinks to find correct mtime of .spy files to compare against cached
+ - also call normpath in FileHandler
+ - patch by Francisco Javier Cabello
+ fix: potential race condition in commonHandler/newWrapper call
+ - (only could cause problems when running spyceWWW/threaded concurrency)
+ * fix: pool module now reflects server globals, as indicated by documentation
+ fix: changed spyce modules are reloaded
+ - wasn't a huge deal to not have this before since not many people
+ wrote active tags or modified existing Spyce modules; more important now
+ that (I expect) more people will be using the 2.0-style active tags
+
+
+v1.3.14
+ fix: mod_python environment dictionary does not support iteration, causing
+ problems with cgi.py library in Python >2.3. Fix is to convert to
+ regular Python dictionary. Thanks to Jan Kujawa
+
+v1.3.13
+ improved performance (approx. 3x) of single-threaded print
+ exceptions in non-default modules reported like errors in script code
+ fix: mysession.spy: session depends on pool, so switched import order
+ fix: faulty session handlers thrown out on error
+ - takes care of annoying NameError: 'pool' not found
+ fix: sys.path always returned to original state, even on error
+ globals accessible via python modules
+ - applied patch contributed by: Niko Matsakis
+ iterable objects allowed within 'for' tag
+ - modified patch submitted by: Stefan Behnel
+ spyce lambdas can return values
+ updated spyceLock.py to eliminate Python FutureWarning for large constants
+ added include.spyceStr(), idea from Santtu Pajukanta
+ fix: error module performs response.clearFilters
+ - thanks to Colin Gillespie for reporting this
+ turning on spyce.DEBUG_ERROR will also display when modules start/finish
+
+v1.3.12
+ added fix to prevent reparsing of POST stream on
+ internal redirect (modified Conan Albrecht's contribution)
+ documentation updates
+ fix: parsing totally empty files threw exception
+ user request: iterate over request object
+ modified semantics of active tags
+ - singleton tags now behave like paired tags with empty bodies
+ - defined a number of flags in spyceTag to reduce level of indentation
+ in generated python code where unnecessary:
+ conditional, loops, catches, mustend
+ - see tag documentation
+ updated core tag library accordingly
+ fix: dump_handler with binary files truncating on Windows
+ fix: files with DOS linebreaks
+ fix: both <%. and <%@ can now begin a spyce directive
+ directory listings of Spyce webserver look nicer, Apache-like
+ added form active tag library
+ added PATH_INFO functionality to spyceWWW
+
+v1.3.11
+ user request: daemon webserver mode
+ fix: mod_python flush problem
+ performance: rewrote tokenizer/parser
+ - no longer using clusmy parser generator package
+ - still pure python, compilation times between 2-6x faster
+ user request: expose functionality to define spyceProcess function
+ with arbitrary parameters, and pass in parameters... helps
+ Coil framework with Spyce integration
+ fix: spyce webserver not performing path manipulations correctly
+ on Windows
+ fix: spyce.mime file not copied for .rpm and Windows installers
+
+v1.3.10
+ Default development configuration changed to:
+ Apache 2.0.40 and Python 2.2.x
+ Release testing will be performed:
+ both on Linux and Windows
+ under CGI, FastCGI and mod_python
+ Other versions of Apache and Python should continue to work, but
+ will not be tested. I am depending on user feedback to catch any
+ errant bugs under these older configurations.
+ fix: spyceWWW properly deals with directory URLs that don't end in '/'
+ fix: request.getpost1/postget1() now accept default values
+ fix: memory cache checks file permission as well as modification time
+ fix: makefile was including .pyc/.pyo files in tarball
+ fix: spyce.vim syntax highlighting for spyce lambdas
+ fix: error module should be loaded last to avoid stdout module being
+ unloaded on error, thereby causing print statements to no longer go
+ to the browser during error handling
+ fix: error module setHandler used incorrect variable name, causing
+ setHandler to fail
+ updated spyce.vim syntax file for JSP/ASP like delimeters
+ spyce.vim now included in vim distribution
+ rpm generates spyceParserTable.py
+ (allowing for different versions of python)
+ added 'no-store' and 'must-revalidate' to response.uncacheable()
+ added pageerror configuration option to modify default page-level handler
+ rpm now requires http >2.0 and python >2.2 installed
+
+v1.3.9
+ spyceWWW web server improved
+ - configuration options integrated into spyce.conf
+ - handler mechanism created
+ - defined spyce and dump handlers
+ - reads Apache format mime-type definition files
+ - .spy files ==> spyce handler; rest ==> dump handler
+ - can display directory listings
+ - configuration options added accordingly
+ - corresponding documentation changed
+ documentation restructured to explain common configuration file
+ options in the runtime section
+ fix: docs/examples/*.gif added to rpm and windows installer
+ expanded section on how to get Spyce running under IIS via CGI
+
+v1.3.8
+ user request:
+ modified request.get/post/get1/post1/env() to accept default values
+ (note: will break code that provided caseInsensitive parameter by position)
+ added request.getpost/getpost1/postget/postget1/default() methods
+ bug fixes: python 1.5 backwards compatibility issues in the following
+ online examples: gif.spy, myPortal.spy, mysession.spy
+
+v1.3.7
+ support for ASP-style delimeters -- <% %>
+ use of Bastion eliminated, due to Python deprecation
+
+v1.3.6
+ info.spy example updated to deal with implicitly loaded taglib module
+ minor documentation fix for doc-mod_include
+ quotes for the PythonPath in httpd.conf
+
+v1.3.5
+ taglib and spylambda modules loaded implicitly only when needed
+ (i.e. when tags or spyce lambdas are actually used in a given file)
+ make install made more portable; removed install -D switch
+ EOFError now handled for file-based spyce caching (strange Windows bug?)
+ improvements to automaton module
+
+v1.3.4
+ doc updates - session module
+ minor mod_python bug - filename attribute used over environment
+ fix - windows installer unable to find python executable in some cases
+
+v1.3.3
+ examples/info.spy added
+ keep track of spyce entry point, added to spyce header
+ fix - CGI fails (only on Apache2.0!) with GET info due v1.3.2 changes
+ fix - typo in core:if tag
+
+v1.3.2
+ mod_python 3.0.1 compatibility
+ - switched to sre module, despite stack limits, because
+ pre module conflicts with pcre shared object that apache uses
+ (actually, just fails on some complicate reg.exps!)
+ This implies that very, very long spyce files might fail, until
+ sre module implements a state-machine-based reg.exp engine.
+ - apacheRequest.connection.child_num mysteriously removed,
+ therefore using os.getpid() in spyceModpyRequest.getServerID()
+ spyceApache.conf tweaked (should be more compatible)
+ installHelper.py converts backslash to forward slash
+ for httpd.conf on Windows
+ switched from pre to sre module in spyceCompile.py
+ - reason: Apache 2.0.x uses different pcre library from Python
+ causing failure under mod_python
+ - pre was used over the default (sre) because sre implementation is
+ stack-based and encountered overruns... Oh, well! Don't write
+ Spyce files that blow the stack until sre is fixed.
+
+v1.3.1
+ fix - wrapped thread-unsafe yacc-like package with lock
+ renamed util module to spyceUtil.py
+ - conflict with python1.5 site-package
+ renamed cache.py, lock.py (just in case)
+ make website update script faster
+
+v1.3.0
+ active tags introduced
+ - see: http://spyce.sourceforge.net/doc-tag.html
+ - [[.taglib]] directive added
+ - taglib spyce module added
+ - compiler changes to deal with active tags
+ - tagging infrastructure (spyceTag, spyceTagPlus, spyceTagLibrary)
+ - see: spyceTags.py
+ - user-defined active tag libraries possible
+ - see: http://spyce.sourceforge.net/doc-tag_new.html
+ - core active tag library
+ see: tags/core.py
+ see: http://spyce.sourceforge.net/doc-tag_core.html
+ - tag libraries loaded from same path as modules
+ - compiler syntax checking improved
+ - check for unbalanced parens
+ - check for unbalanced active tags
+ - extensible syntax checking for active tags
+
+v1.2.10
+ bugfix - typo in spyceWWW caused threading mode startup failure
+
+v1.2.9
+ stdout.push() can now accept no file argument
+ stdout.pop() now returns captured output
+ stdout.capture() added
+ see: examples/stdout.spy and stdout module docs
+ session_user session handler added in session module
+ see: examples/mysession.spy and session module docs
+ spylambda.define() can now memoize
+ see: http://spyce.sourceforge.net/doc-mod_lambda.html
+ memoized spyce lambda syntax: [[spy! ...: ...]]
+ see: http://spyce.sourceforge.net/doc-lang_lambda.html
+ slight modification to spyce.vim syntax file
+ response.addHeader() now support replacement
+ response.timestamp(), expire(), expireRel(), lastModified()
+ and uncacheable() methods added
+ see: http://spyce.sourceforge.net/doc-mod_response.html
+ performance!
+
+v1.2.8
+ links page added
+ spyce VIM syntax file updated; deals with spyce lambdas
+ include module improvements
+ - 'vars' field added
+ - included file can return value
+ - documentation updated, specifically regarding use of 'context'
+
+v1.2.7
+ internal restructuring continues
+ - separated spyce exceptions
+ - separated spyce configuration
+ - expanded spyce API and spyceServer
+ - spyce.spyceDone now imported as spyceDone
+ simplified spyceModule
+ - renamed wrapper field, to _api
+ - old spyceModule available as spyceModulePlus
+ - all standard modules updated
+ fixed - syntax errors were not reported properly
+ file-based spyce caching, with config option
+ performance improvements
+
+v1.2.6
+ single and multi-page documentation
+ minor fixes:
+ - NoCloseOut.flush() added
+ - BufferedOutput.flush() flushes sub-stream
+ - template module pointed at new location of cache code
+
+v1.2.5
+ spyceAPI defined: module access to spyceWrapper object restricted
+ - see: http://spyce.sourceforge.net/doc-mod_new.html
+ - (in general, will be moving towards restricted execution space)
+ toc module improved; add level(), l1()...l9() methods
+ server-level debug option added to config file
+ - see: http://spyce.sourceforge.net/doc-conf_common_debug.html
+ - debug Spyce module deprecated
+ engine now supports recursive requests (include spyce from itself)
+ sys.stdout (and therefore print statements) made thread-safe
+ spyce engine supports concurrent requests
+ server-level concurrency option added to config file
+ - see: http://spyce.sourceforge.net/doc-conf_common_concurrency.html
+ - spyce webserver operates in single, forking and threading modes
+ server-level Spyce module caching
+ - replaces Spyce-level module caching
+ - caching-related code separated from wrapper
+ code compilation seperated from wrapper (spyce.spyceCode)
+ autodetect when PYTHONOPTIMIZE causes lexer/parser failure
+ minor fixes and performance tweaks
+
+v1.2.4
+ fix - new PLY parser uses reflection at runtime to read
+ documentation strings containing grammar, thus you
+ should not run Python in optimize mode, thus
+ mod_python option in spyceApache.conf changed.
+ fix - python 1.5 compatible .spy files for docs
+
+v1.2.3
+ fix - code for new tokenizer/parser made python 1.5.2 compatible
+
+v1.2.2
+ fix - PATH_INFO via CGI
+ fix - magic (#!) on first line treated as comment
+
+v1.2.1
+ complete rewrite of spyce tokenizer and parser
+ - using PLY, table-driven
+ added spyce lambdas to language
+
+v1.2.0
+ contrib section added
+ support for SPYCE_PATH environment variable
+ lots of documentation fixes
+ decided spyce was mature enough for 1.2.0
+
+v1.1.46
+ feature request: improved examples page on website
+
+v1.1.45
+ site and documentation revamp
+ refactored the spyceModule class (see spyceModule.py)
+ altered all standard modules to conform to new internal design
+ new table-of-contents (toc) module (see docs)
+ improved stdout module (see docs)
+ added push() and pop() methods
+ now loaded implicitly
+ exception tracebacks in chunks identify specific error lines
+ file globbing added to -O command-line option
+
+v1.1.44
+ module directive deprecated
+ replaced with import tag
+ import tag accepts args attribute
+ calls module init() method at location of directive
+ init() methods added to modules: session, compress
+ see: http://spyce.sourceforge.net/doc_lang_directive.html
+ http://spyce.sourceforge.net/doc_mod.html
+ http://spyce.sourceforge.net/doc_mod_compress.html
+ http://spyce.sourceforge.net/doc_mod_session.html
+ http://spyce.sourceforge.net/doc_mod_new.html
+ bugfix - modules finalized on redirect
+
+v1.1.43
+ bugfix - included files not inheriting modules properly
+ bugfix - transform module inside included file
+
+v1.1.42
+ renamed spyce.conf to spyceApache.conf
+ renamed spyceApache to spyceModpy
+ renamed run_spyceApache to run_spyceModpy (affect spyceApache.conf)
+ added server-level configuration file functionality
+ server module search path
+ modules to load at startup
+ server-level error handler
+ global server variables
+ see: docs/doc_conf_common.html
+ added response.isCancelled() function
+ see: docs/doc_mod_response.html
+ bugfix - early client disconnect caused problems under mod_python
+
+v1.1.41
+ extended HTTP response constants to conform to spec
+ extended HTML entity encoded characters to conform to spec
+ modified internal buffering semantics to allow eliminiation of special
+ case code for specific HTTP return codes (redirects) in the common path
+ performance improvements
+ convenience functions transform.html_encode() and url_encode() added
+ error module added: handles errors that occur during spyce processing
+ bugfix - HTTP return codes propagated correctly under mod_python
+
+v1.1.40
+ bugfix - spyce syntax error propagated properly
+ response headers cleared on an internal redirect
+ case insensitive request.get,post,get1,post1,file
+
+v1.1.39
+ modified how filter module injects itself into output stream
+ added response.addFilter() to allow piped functionality
+ on the output stream, modules can insert write, writeStatic,
+ writeExpr, flush and clear handlers
+ added compress module for dynamic compression functionality
+ compress module documentation
+ renamed filter module to transform (name conflict with Python builtin)
+ sys.path forced to be absolute before changing directory in CGI mode
+ bugfix - spyce path trimmed to just filename when directory changed for
+ CGI processing
+ bugfix - spyce web server closes sockets
+
+v1.1.38
+ spyce can now run as a (proxy) web server
+ spyce -l [-p port]
+
+v1.1.37
+ spyceDone exception to stop spyce processing
+ raise spyceDone, see gif.spy, fileupload.spy examples
+ response.close() deprecated
+ not needed with spyceDone functionality
+ cPickle used in session module
+ improved session serialization performance
+
+v1.1.36
+ redirect.externalRefresh now has url= in string
+ internal redirect fixed
+ bug fix - consecutive compact line removal now possible
+ examples added: hello2.spy, form.spy
+ handle ISINDEX CGI queries that have extra command-line parameters
+ Status CGI header used for spyce redirect return codes
+
+v1.1.35
+ bug - fixed cgi chdir in case of local directory
+ request - invoke spyce engine programmatically with spyce string
+ source tarball does not contain extra CVS junk
+
+v1.1.34
+ fixed apache config bug in windows installer
+
+v1.1.33
+ appended current Spyce file's directory to sys.path
+
+v1.1.32
+ minor documentation tweaks
+ names attribute added to [[.module ]] tag
+ request.__getitem__() added
+ chdir in cgi mode
+
+v1.1.31
+ windows installer improved: apache configuration and restart
+ fixed - handling of initial spaces in multi-line strings in python chunks
+
+v1.1.30
+ red page marker in docs
+ created undefined windows lock variables
+
+v1.1.29
+ documentation split up
+ rpm is now noarch
+
+v1.1.28
+ include.dump() now has binary option
+ stdout changed to binary mode on windows for cgi purposes
+ fixed session_dir handler bug on windows
+
+v1.1.27
+ fcgi implemented on windows too
+ windows installer
+
+v1.1.26
+ fixed - nasty bug with the new module behaviour
+ small improvements to documentation and examples
+ improved request.uri() function
+
+v1.1.25
+ fixed - fcgi module broke on windows
+
+v1.1.24
+ line compaction improved
+ module behaviour on include.spyce() defined
+
+v1.1.23
+ lots of changes so that: it works on Python 1.5.2 now too!
+ file-based session handler now uses pid, and file locks
+ live examples on sourceforge
+
+v1.1.22
+ fixed Python v2.1.1-related bugs.
+ improved installation process and documentation
+ rpm more likely to succeed - uses fcgi or drops back to cgi
+ no longer mod_python based by default
+
+v1.1.21 (faulty release)
+ stochastic session clean up; no more threading dependency
+ documentation: better installation notes
+ peep-hole optimizer
+
+v1.1.20 (faulty release)
+ created explicit (swappable) cache infrastructure
+ BUG ** Spyce also works on Python v2.1
+ request - request.post(),post1() works in includes
+ documentation: cheetah install, ...
+
+v1.1.19
+ filter module
+
+v1.1.18
+ fcgi support added
+ X-Spyce header added
+ documentation: how to write new modules
+
+v1.1.17
+ feature request - compaction algorithm improved
+
+v1.1.16
+ generalised session.setHandler (session handler selection mechanism)
+ gdbm, bsd db session handlers added
+
+v1.1.15
+ minor makefile and rpm script changes
+ handling of multi-line strings in python code
+ response.flush() added
+
+v1.1.14
+ wrappers to check python version
+
+v1.1.13
+ added new language construct: "Python chunks"
+
+v1.1.12
+ stdout module redirects stdout to response object
+ added writeln() to response module
+
+v1.1.11
+ fixed lots of CGI bugs:
+ reported bug - headers not sent
+ session module thread prevented script death
+ added spyce.ONE_SHOT variable
+ cookie module fixed
+ gif.spy example fixed
+ external redirect fixed
+
+v1.1.10
+ performance:
+ implemented semantic cache for spyce compilation
+ templating module performs caching
+ lots of commenting
+
+v1.1.9
+ templating module (cheetah integration)
+ documentation
+
+v1.1.8
+ automaton module
+ documentation
+
+v1.1.7
+ associative array access to session and cookie information
+ added pool module
+ documentation
+ comments emitted as tokens
+ syntax highlighting function: include.spycecode
+ documentation
+
+v1.1.6
+ dynamically loading modules
+
+v1.1.5
+ redirect module added
+
+v1.1.4
+ response.unbuffer()
+
+v1.1.3
+ support for file upload
+ request.get1(),post1()
+
+v1.1.2
+ more reliable exception location reporting
+
+v1.1.1
+ static includes
+ module search path
+
+v1.1.0
+ Implemented modules -- major rewrite.
+ Changed includes, sessions, cookies, ... everything into modules
+ Changed the generated "stub", though this is mostly under-the-covers
+ Rewrote most of the documentation
+
+v1.0.5
+ CGI support
+ Expanded install docs
+
+v1.0.4
+ Many doc updates
+ Autosession support
+ changed directives tags to use html-like attributes
+
+v1.0.3
+ Automatic session cleanup
+ Updated pilpel image
+
+v1.0.2
+ Handle 403 - Forbidden
+ Handle 404 - Not Found
+
+v1.0.1
+ Tracking original spyce code locations in generated code
+ Reporting runtime exceptions in original spyce code
+ Reporting syntax (compile) exceptions in original spyce code
+
+v1.0 - Initial release
+ Documentation
+ Added [[.nocompact]] and [[.compact]]
+ Allowed escaped \[[ and \]] in HTML
+ Added session support, with on-disk implementation
+ Realised and implemented command-line
+ Added cookies
+ Added http header calls
+ Added get and post support
+ Created request and response objects
+ Added [[.include]]
+ Added [[.funcion]] and [[./function]]
+ Create in-memory spyce cache
+ Wrote a token-based Brace Converter
+ Added [[ ]] and [[= ]]
+ Created Spyce compiler shell
+ Wrote initial mod_python "hello world" handler
+ Read up on mod_python
+ Looked at PyServ
+ Attempted to engineer a WebWare-based solution
+
diff --git a/spyce-2.1/LICENCE b/spyce-2.1/LICENCE
new file mode 100755
index 0000000..a38ce43
--- /dev/null
+++ b/spyce-2.1/LICENCE
@@ -0,0 +1,40 @@
+Copyright (c) 2002-05 Rimon Barr.
+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 and
+this LICENSE in its entirety including the disclaimer. The LICENSE of this
+product may only be modified by the Copyright holder.
+
+2. Redistributions in binary form must reproduce the above copyright notice
+and this LICENSE in its entirety including the disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+3. The end-user documentation included with the redistribution, if any, must
+include the following acknowledgment: "This product uses Spyce, Copyright
+Rimon Barr." Alternately, this acknowledgment may appear in the software
+itself, wherever such third-party acknowledgments normally appear. The
+documentation must also provide a instructions on how to receive an original
+Spyce distribution, preferably a link to the website
+http://spyce.sourceforge.net.
+
+4. The names "Spyce", or "Rimon Barr" must not be used to endorse or promote
+products derived from this software without prior written permission. For
+written permission, please contact rimon-AT-acm.org.
+
+5. Products derived from this software may not be called "Spyce", nor may
+"Spyce" appear in their names, without prior written permission of the
+Copyright holder.
+
+THIS SOFTWARE IS PROVIDED ``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 RIMON BARR
+OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/spyce-2.1/README b/spyce-2.1/README
new file mode 100755
index 0000000..e4a2c07
--- /dev/null
+++ b/spyce-2.1/README
@@ -0,0 +1,15 @@
+SPYCE - Python Server Pages
+Copyright 2002-03. Rimon Barr
+---------------------------------------------------------------------
+
+Description: Python-based dynamic HTML scripting engine.
+
+Name: SPYCE - Python Server Pages
+Author: Rimon Barr
+
+Please refer to the LICENCE file
+
+For system documentation:
+ Run python spyceCmd.py -l
+ Browse to localhost:8000/index.spy
+
diff --git a/spyce-2.1/RELEASE b/spyce-2.1/RELEASE
new file mode 100755
index 0000000..6c47bc6
--- /dev/null
+++ b/spyce-2.1/RELEASE
@@ -0,0 +1,21 @@
+To release a new version of spyce:
+
+- Run: make clean
+- Edit spyce.py: change version (possibly release)
+- Edit CHANGES
+- Run: cvs update; cvs commit
+- Perform release testing
+- Run: make upload (and do the sourceforge file release)
+- Run: make sf; make sfcontrib
+- Run: make clean
+- post to freshmeat
+ spyce-users@sourceforge.net
+ python-announce@python.org
+ python-list@python.org
+ python-web-modules@yahoogroups.com
+ ? cheetahtemplate-discuss@lists.sourceforge.net
+ ? mod_python@modpython.org
+ ? webware-discuss@lists.sourceforge.net
+ ? sixthdev@yahoogroups.com
+ ? quixote-users@mems-exchange.org
+ ? php, jsp, apache, cherrypy
diff --git a/spyce-2.1/THANKS b/spyce-2.1/THANKS
new file mode 100755
index 0000000..b3fd1b8
--- /dev/null
+++ b/spyce-2.1/THANKS
@@ -0,0 +1,60 @@
+This file contains mention of people that deserve thanks.
+If you feel that you should be added, please email me
+at
+
+Thanks (in reverse-chronological order) to:
+
+John Reese
+ Several bug fixes to the 2.0 release.
+
+Brandon Beck
+ For the daemon mode suggestion
+
+Conan C. Albrecht
+ Much help with the tags, the built-in webserver, suggestions,
+ bug reports, and fixes ...
+
+Fred Moscicki
+ Bug reports.
+
+Adrien Plisson
+ Lots of bug reports, help with module development,
+ and input of many ideas.
+
+The NullSoft crew:
+ For NSIS SuperPiMP installer
+ http://www.nullsoft.com/free/nsis/
+
+Tino Lange
+ Inspiring my work to get the Spyce engine and modules down from
+ Python 2.2 to Python 1.5.2.
+
+John J Smith
+ Finding bugs. Email discussions that led to improvements
+ in the Spyce line compacting mode, and the way modules behave
+ in included files.
+
+Piers Lauder
+ Email discussions that led to Python chunks (ala Poor Man's Zope),
+ filters (ala Cheetah), and some other ideas.
+
+The Cheetah team
+ http://www.cheetahtemplate.org/
+
+Natalya Katsnelson :
+ For the Spyce mascot, "Pilpel".
+
+Dave Wallace :
+ Provided initial idea in Webware's PSP implementation to add braces
+ to Python code, solving the indentation problem.
+
+Gregory Trubetskoy :
+ For the mod_python project, upon which this work depends
+ http://www.modpython.org/
+
+The SourceForge team
+ http://www.sourceforge.net
+
+The Apache Team
+ http://www.apache.org/
+
diff --git a/spyce-2.1/_eginfo.txt b/spyce-2.1/_eginfo.txt
new file mode 100755
index 0000000..3a9df35
--- /dev/null
+++ b/spyce-2.1/_eginfo.txt
@@ -0,0 +1,77 @@
+Language / Python Class Chunks:lang_chunkc:examples/handlerintro.spy:1
+Language / Spyce Lambdas:lang_lambda:examples/spylambda.spy:1
+Runtime / Exceptions:runtime_except:examples/gif.spy:1
+Runtime / Configuration:runtime_common:examples/config.spy:1
+Runtime / Server utilities / The Spyce scheduler:runtime_util_scheduler:examples/scheduling.py:0
+Runtime / Modules / DB (implicit):mod_db:examples/db.spy:1
+Runtime / Modules / Request (implicit):mod_request:examples/filter.py:0
+Runtime / Modules / Request (implicit):mod_request:examples/request.spy:1
+Runtime / Modules / Request (implicit):mod_request:examples/fileupload.spy:1
+Runtime / Modules / Response (implicit):mod_response:examples/gif.spy:1
+Runtime / Modules / Redirect:mod_redirect:examples/redirect.spy:1
+Runtime / Modules / Cookie:mod_cookie:examples/cookie.spy:1
+Runtime / Modules / Session:mod_session:examples/session2.spy:1
+Runtime / Modules / Pool:mod_pool:examples/pool.spy:1
+Runtime / Modules / Transform:mod_transform:examples/transform.spy:1
+Runtime / Modules / Compress:mod_compress:examples/compress.spy:1
+Runtime / Modules / Include:mod_include:examples/include.spy:1
+Runtime / Modules / Include:mod_include:examples/include.spi:0
+Runtime / Modules / Include:mod_include:examples/includestatic.spy:1
+Runtime / Modules / Include:mod_include:examples/includestatic.spi:0
+Runtime / Modules / Internal modules / Error:mod_error:examples/error.spy:1
+Runtime / Modules / Internal modules / Error:mod_error:examples/error.spi:0
+Runtime / Modules / Internal modules / Stdout:mod_stdout:examples/stdout.spy:1
+Runtime / Modules / Writing Modules:mod_new:examples/myModule.py:0
+Runtime / Tags / Core:tag_core:examples/hello-templated.spy:1
+Runtime / Tags / Core:tag_core:examples/login-optional.spy:1
+Runtime / Tags / Core:tag_core:examples/login-required.spy:1
+Runtime / Tags / Form:tag_form:examples/formintro.spy:1
+Runtime / Tags / Form:tag_form:examples/formtag.spy:1
+Runtime / Tags / Active Handlers:tag_handlers:examples/handlerintro.spy:1
+Runtime / Tags / Active Handlers:tag_handlers:examples/handlervalidate.spy:1
+Runtime / Tags / Active Handlers:tag_handlers:examples/handlervalidate2.spy:1
+Runtime / Tags / Active Handlers:tag_handlers:examples/db.spy:1
+Runtime / Tags / Writing Tag Libraries:tag_new2:examples/tagbold.spi:0
+Runtime / Tags / Writing Tag Libraries the hard way:tag_new:examples/myTaglib.py:0
+Runtime / Tags / Writing Tag Libraries the hard way:tag_new:examples/tag.spy:1
+Runtime / Programmatic Interface / Example:runtime_prog_example:examples/programmaticUsage.py:0
+Addenda / Performance:add_perf:examples/hello.spy:1
+:root:examples/handlerintro.spy:1
+:root:examples/spylambda.spy:1
+:root:examples/gif.spy:1
+:root:examples/config.spy:1
+:root:examples/scheduling.py:1
+:root:examples/db.spy:1
+:root:examples/filter.py:1
+:root:examples/request.spy:1
+:root:examples/fileupload.spy:1
+:root:examples/gif.spy:1
+:root:examples/redirect.spy:1
+:root:examples/cookie.spy:1
+:root:examples/session2.spy:1
+:root:examples/pool.spy:1
+:root:examples/transform.spy:1
+:root:examples/compress.spy:1
+:root:examples/include.spy:1
+:root:examples/include.spi:1
+:root:examples/includestatic.spy:1
+:root:examples/includestatic.spi:1
+:root:examples/error.spy:1
+:root:examples/error.spi:1
+:root:examples/stdout.spy:1
+:root:examples/myModule.py:1
+:root:examples/hello-templated.spy:1
+:root:examples/login-optional.spy:1
+:root:examples/login-required.spy:1
+:root:examples/formintro.spy:1
+:root:examples/formtag.spy:1
+:root:examples/handlerintro.spy:1
+:root:examples/handlervalidate.spy:1
+:root:examples/handlervalidate2.spy:1
+:root:examples/db.spy:1
+:root:examples/tagbold.spi:1
+:root:examples/myTaglib.py:1
+:root:examples/tag.spy:1
+:root:examples/programmaticUsage.py:1
+:root:examples/whatsnew.spy:1
+:root:examples/whatsnew2.spy:1
diff --git a/spyce-2.1/contrib/jedit-spyce.xml b/spyce-2.1/contrib/jedit-spyce.xml
new file mode 100755
index 0000000..0de1e87
--- /dev/null
+++ b/spyce-2.1/contrib/jedit-spyce.xml
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [[--
+ --]]
+
+
+
+
+ [[.
+ ]]
+
+
+
+
+ [[=
+ ]]
+
+
+
+
+ [[\
+ ]]
+
+
+
+
+ [[
+ ]]
+
+
+
+
+ <!--
+ -->
+
+
+
+
+ <SCRIPT
+ </SCRIPT>
+
+
+
+
+ <STYLE
+ </STYLE>
+
+
+
+
+ <!
+ >
+
+
+
+ <
+ >
+
+
+
+
+ &
+ ;
+
+
+
+
+
+ [[--
+ --]]
+
+
+
+
+ [[=
+ ]]
+
+
+
+
+ [[
+ ]]
+
+
+
+
+ "
+ "
+
+
+
+ '
+ '
+
+
+
+ include
+ compact
+ import
+ taglib
+
+ file
+ mode
+ off
+ space
+ line
+ full
+ name
+ from
+ as
+ args
+ names
+
+
+
+
+
+
+ [[--
+ --]]
+
+
+
+
+ [[=
+ ]]
+
+
+
+ "
+ "
+
+
+
+ '
+ '
+
+
+ /
+ :
+ :
+
+
+
+
+ [[=
+ ]]
+
+
+
diff --git a/spyce-2.1/contrib/kate-spyce.xml b/spyce-2.1/contrib/kate-spyce.xml
new file mode 100755
index 0000000..642d7d3
--- /dev/null
+++ b/spyce-2.1/contrib/kate-spyce.xml
@@ -0,0 +1,383 @@
+
+
+
+]>
+
+
+
+
+
+
+ - and
+ - assert
+ - break
+ - class
+ - continue
+ - def
+ - del
+ - elif
+ - else
+ - except
+ - exec
+ - finally
+ - for
+ - global
+ - if
+ - in
+ - is
+ - lambda
+ - not
+ - or
+ - pass
+ - print
+ - raise
+ - return
+ - try
+ - while
+ - yield
+
+
+ - None
+ - self
+ - True
+ - False
+ - NotImplemented
+ - Ellipsis
+
+
+ - spy
+ - import
+ - session
+ - cookie
+ - pool
+ - include
+ - automaton
+ - taglib
+ - from
+ - as
+ - core
+ - request
+ - response
+ - stdout
+ - transform
+ - redirect
+ - error
+ - spylambda
+
+
+
+
+ - abs
+ - apply
+ - buffer
+ - callable
+ - chr
+ - cmp
+ - coerce
+ - compile
+ - complex
+ - copyright
+ - credits
+ - delattr
+ - dir
+ - divmod
+ - eval
+ - execfile
+ - exit
+ - filter
+ - float
+ - getattr
+ - globals
+ - hasattr
+ - hash
+ - hex
+ - id
+ - input
+ - int
+ - intern
+ - isinstance
+ - issubclass
+ - iter
+ - len
+ - license
+ - list
+ - locals
+ - long
+ - map
+ - max
+ - min
+ - oct
+ - open
+ - ord
+ - pow
+ - quit
+ - range
+ - raw_input
+ - reduce
+ - reload
+ - repr
+ - round
+ - setattr
+ - slice
+ - str
+ - tuple
+ - type
+ - unichr
+ - unicode
+ - vars
+ - xrange
+ - zip
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spyce-2.1/contrib/modules/automaton.py b/spyce-2.1/contrib/modules/automaton.py
new file mode 100755
index 0000000..3b78ded
--- /dev/null
+++ b/spyce-2.1/contrib/modules/automaton.py
@@ -0,0 +1,135 @@
+##################################################
+# SPYCE - Python-based HTML Scripting
+# Copyright (c) 2002 Rimon Barr.
+#
+# Refer to spyce.py
+# CVS: $Id: automaton.py 860 2006-04-30 01:18:25Z ellisj $
+##################################################
+
+from spyceModule import spyceModule
+
+__doc__ = '''
+[[toc.n('Automaton', 'mod_automaton')]]
+
+The automaton module provides support for state machine-based
+application design, which is often useful when designing websites with
+application flows. The state machine is a directed, labelled graph. It has
+states (nodes with names), and transitions (directed edges with names). One of
+the states is defined to be a "begin" state for the machine. Every state
+has a "send" function, a "receive" function and a set of outgoing
+edges.
+
+The basic idea behind the operation of the automaton module is as follows: The
+application is at some state when a request comes in. The receive function for
+that state is invoked to process the input from the browser. Based on this
+input the receive function returns some edge label, which takes the
+application from the current state to its new state. The send function of this
+new state is invoked to emit the appropriate application page. The data that
+returns from this page will be processed by the corresponding receive
+function, and so on. All you need to remember between requests is which state
+the application is in, which can be done via get or post, or via cookies using
+the cookie module. Better yet (to keep application states private and on the
+server for security reasons), one can store the state label in the session
+using the session module.
+
+A state machine can be defined programmatically using the following functions:
+
+ state ( name, send, recv )
+ Add a new state labeled "name" with associated "send" and "recv"
+ functions.
+
+ transition ( state1, name, state2 )
+ Add a new edge labelled "name" from "state1" to "state2". There is
+ always a self-referencing edge with the label None, but this can
+ be overidden.
+
+ begin ( state )
+ Define a given state to be the begin state.
+
+ define ( sm, begin )
+ Define an entire automaton "sm" all at once, where sm is a
+ hashtable. The keys are the states and the values are triplets
+ with a send function, a receive function and an edge hashtable.
+ The edge hashtable has names of the edges as keys and the target
+ states as values. The "begin" state is given
+
+To step through the state machine transitions, you call:
+
+ step ( [state] )
+ If "state" is specified, then call the receive function of that
+ state. The receive function returns an edge label, which points to
+ the new state. If no state is specified, just set the new state to
+ the begin state of the automaton. Then, call the send function of
+ the new state. Note that the send function is responsible for
+ encoding its own state label, for use on the subsequent client
+ request.
+
+
+Future releases of this module may add support for different types of send and
+receive handlers. For example, it is probably useful to be able to internally
+redirect to various Spyce pages for send processing, rather than inline
+functions. It may also be possible to pass information among the different
+functions, which could be useful, for example, in handling error messages
+during form processing. It may also be useful to define a sequence of states,
+where previous and next are implicit edges.
+
+See automaton.spy for example usage.
+'''
+
+SEND = 0
+RECV = 1
+EDGES = 2
+
+class automaton(spyceModule):
+ def start(self):
+ "Initialise an empty automaton"
+ self.clear()
+ def clear(self):
+ self._nodes = {}
+ self._edges = {}
+ # defining the automaton
+ def state(self, name, send, recv):
+ "Add a new automaton state"
+ self._nodes[name] = send, recv
+ self.transition(name, None, name)
+ def transition(self, state1, edge, state2):
+ "Add a new automaton transition"
+ if not self._nodes.has_key(state1):
+ raise 'state %s does not exist' % state1
+ if not self._nodes.has_key(state2):
+ raise 'state %s does not exist' % state2
+ self._edges[(state1, edge)] = state2
+ node=state
+ edge=transition
+ def begin(self, name):
+ if not self._nodes.has_key(name):
+ raise 'state %s does not exist' % name
+ self._begin = name
+ def define(self, sm, start):
+ self.clear()
+ for s1 in sm.keys():
+ self.node(s1, sm[s1][SEND], sm[s1][RECV])
+ for s1 in sm.keys():
+ for e in sm[s1][EDGES].keys():
+ self.edge(s1, e, sm[s1][EDGES][e])
+ self.begin(start)
+
+ # running the automaton
+ def step(self, state=None):
+ """Run the automaton one step: recv (old state), transition,
+ send (new state)"""
+ if state==None:
+ state = self._begin
+ else:
+ try: _, recv = self._nodes[state]
+ except: raise 'invalid state: %s' % state
+ edge = recv()
+ try: state = self._edges[(state, edge)]
+ except: raise 'invalid transition: %s,%s' % (state, edge)
+ try: send, _ = self._nodes[state]
+ except: raise 'invalid state: %s' % state
+ send()
+
+
+# rimtodo: cached state-machines
+
diff --git a/spyce-2.1/contrib/modules/automaton.spy b/spyce-2.1/contrib/modules/automaton.spy
new file mode 100755
index 0000000..38482e7
--- /dev/null
+++ b/spyce-2.1/contrib/modules/automaton.spy
@@ -0,0 +1,88 @@
+[[-- sample use of automaton Spyce module; see automaton.py --]]
+
+[[.import name=automaton]]
+[[.import name=session1 args="'session_dir', '/tmp', auto=10"]]
+[[\
+if not session.auto: session.auto = {
+ 'name': '',
+}
+
+step1send = [[spy:
+
+
+ ]]
+def step1recv():
+ if request.post1('dir') == 'next': return 'next'
+
+step2send = [[spy:
+
+
+ ]]
+def step2recv():
+ if request.post1('dir') == 'prev': return 'prev'
+ if request.post1('dir') == 'next': return 'next'
+
+step3send = [[spy:
+
+
+ ]]
+def step3recv():
+ if request.post1('dir') == 'prev': return 'prev'
+ if request.post1('dir') == 'next': return 'next'
+
+step4send = [[spy:
+
+ Thanks.
+ ]]
+def step4recv():
+ pass
+
+automaton.define({
+ 'step1': ( step1send, step1recv, {
+ 'next': 'step2',
+ }),
+ 'step2': ( step2send, step2recv, {
+ 'next': 'step3',
+ 'prev': 'step1',
+ }),
+ 'step3': ( step3send, step3recv, {
+ 'next': 'step4',
+ 'prev': 'step2',
+ }),
+ 'step4': ( step4send, step4recv, {
+ }),
+}, 'step1')
+
+state = request.post1('state')
+automaton.step(state)
+]]
+
+[[--
+spyce file
+spyce inline
+function or method reference
+inline code
+--]]
diff --git a/spyce-2.1/contrib/modules/etag.py b/spyce-2.1/contrib/modules/etag.py
new file mode 100755
index 0000000..83e00ab
--- /dev/null
+++ b/spyce-2.1/contrib/modules/etag.py
@@ -0,0 +1,147 @@
+# I have tried to implement the same functionality as cgi_buffer.
+# http://www.mnot.net/cgi_buffer/
+# Author: Francisco Javier Cabello
+
+# spyce file:
+# -----------------------o-----------------------
+# [[.import name=etag]]
+#
+# Just a test
+#
+# -----------------------o-----------------------
+
+# When you import etag module a http header (ETag header)is added to the output
+# of your spyce script. The next time your browser ask for the same page to the
+# server, it will send the header 'If-None-Match'. The server will check the
+# etag given and the one it has computed and when both values will be the same,
+# it will send a 'Not Modified' http response.
+
+# Example:
+
+# First time:
+# -----------------------
+# GET /cgi-bin/main/main.spy HTTP/1.1
+# Host: 192.67.79.171
+# User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.6) Gecko/20040227
+# Firefox/0.8
+# Accept:
+# text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
+# Accept-Language: en-us,en;q=0.5
+# Accept-Encoding: gzip,deflate
+# Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
+# Keep-Alive: 300
+# Connection: keep-alive
+# Cookie: current_page=main/300_Mstate; user=administrator;
+# session=7ef88d81a0ce8d6d7cf8a8d7c01d7498; language=en
+
+# HTTP/1.1 200 Ok
+# Date: Wed, 14 Jul 2004 13:53:14 GMT
+# Server: Apache/1.3.27 (Unix) Debian GNU/Linux mod_gzip/1.3.26.1a
+# mod_python/2.7.8 Python/2.2.2
+# X-Spyce: Spyce/modpy_1.3.12 Python/2.2
+# ETag: "CTjpbS6wVaFs7SuUOMx8uQ=="
+# Keep-Alive: timeout=15, max=91
+# Connection: Keep-Alive
+# Content-Type: text/html; charset=iso-8859-1
+# ... page ...
+
+# -----------------------
+
+# Second time:
+# -----------------------
+# GET /cgi-bin/main/main.spy HTTP/1.1
+# Host: 192.67.79.171
+# User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.6) Gecko/20040227
+# Firefox/0.8
+# Accept:
+# text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
+# Accept-Language: en-us,en;q=0.5
+# Accept-Encoding: gzip,deflate
+# Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
+# Keep-Alive: 300
+# Connection: keep-alive
+# Cookie: current_page=main/300_Mstate; user=administrator;
+# session=7ef88d81a0ce8d6d7cf8a8d7c01d7498; language=en
+# If-None-Match: "CTjpbS6wVaFs7SuUOMx8uQ=="
+# Cache-Control: max-age=0
+
+# HTTP/1.1 304 Not Modified
+# Date: Wed, 14 Jul 2004 13:53:14 GMT
+# Server: Apache/1.3.27 (Unix) Debian GNU/Linux mod_gzip/1.3.26.1a
+# mod_python/2.7.8 Python/2.2.2
+# X-Spyce: Spyce/modpy_1.3.12 Python/2.2
+# ETag: "CTjpbS6wVaFs7SuUOMx8uQ=="
+# Keep-Alive: timeout=15, max=91
+# Connection: Keep-Alive
+# Content-Type: text/html; charset=iso-8859-1
+# -----------------------
+
+from spyceModule import spyceModule
+from cStringIO import StringIO
+import base64, md5
+
+OUTPUT_POSITION = 94
+
+__doc__ = '''ETag module. provides etag header generation and verification.'''
+
+class etag(spyceModule):
+ def start(self):
+ # install compress filter into response module
+ self._filter = FilterEtag(self)
+ self._api.getModule('response').addFilter(OUTPUT_POSITION, self._filter)
+
+ def init(self):
+ pass
+
+ def finish(self, theError=None):
+ if not theError:
+ self._filter.close()
+
+class FilterEtag(Filter):
+ def __init__(self, module):
+ self._module = module
+ self._buf = StringIO()
+ self._flushed = 0
+
+ def writeStatic(self, s):
+ self.write(s)
+
+ def writeExpr(self, s, **kwargs):
+ self.write(str(s))
+
+ def write(self, s, *args, **kwargs):
+ self._buf.write(s)
+
+ def flushImpl(self, final=0):
+ self._flushed = 1
+ body = self._buf.getvalue()
+ self._buf = StringIO()
+
+ etag = ""
+
+ if final:
+ # etag = base64(md5(body))
+ etag = base64.encodestring(md5.new(str(body)).digest())[:-1]
+ self._module._api.getModule('response').addHeader('ETag', '"%s"' % str(etag) )
+
+ if_none_match = self._module._api.getModule('request').getHeader('If-None-Match')
+
+ if not if_none_match or string.find(if_none_match, etag)<0:
+ # if If-None-Match header hasn't been found or etag sent is different from
+ # etag computed
+ self.next.write(body)
+ else:
+ # etag sent is the same
+ self._module._api.getModule('response').setReturnCode( 304 )
+ else:
+ self.next.write(body)
+
+ etag_sent = self._module._api.getModule('request').getHeader('If-None-Match')
+
+ def clearImpl(self):
+ self._buf = StringIO()
+
+ def close(self):
+ self.flushImpl(1)
+
+
diff --git a/spyce-2.1/contrib/modules/mail.py b/spyce-2.1/contrib/modules/mail.py
new file mode 100755
index 0000000..d766f92
--- /dev/null
+++ b/spyce-2.1/contrib/modules/mail.py
@@ -0,0 +1,202 @@
+##################################################
+# Spyce mail module
+# Copyright (c) 2002 Adrien Plisson.
+#
+# SPYCE is Copyright (c) 2002 Rimon Barr.
+# Refer to spyce.py
+##################################################
+
+from spyceModule import spyceModule
+
+import smtplib
+import email
+
+import types
+import string
+import quopri
+
+class mail(spyceModule):
+ def start(self):
+ pass
+
+ def init(self, *args, **kwargs):
+ if args != () or kwargs != {}:
+ self.setServer(*args, **kwargs)
+
+ def finish(self, error=None):
+ pass
+
+ def setServer(self, host, port=25, user=None, password=None):
+ self.host = host
+ self.port= port
+ self.user = user
+ self.password = password
+
+ def send(self, from_field, to_field, subject_field, content, cc_field=None, bcc_field=None):
+ #parse arguments
+ from_field = _parse_addrs(from_field)
+ to_field = _parse_addrs(to_field)
+ if cc_field != None:
+ cc_field = _parse_addrs(cc_field)
+ if bcc_field != None:
+ bcc_field = _parse_addrs(bcc_field)
+
+ #compose the mail
+ msg = _build_main_message(from_field, to_field, subject_field, content, cc_field).as_string()
+
+ #build the sender and recipients lists
+ sender = from_field[0]
+ if cc_field == None:
+ recipients = to_field
+ else:
+ recipients = to_field + cc_field
+ sender= _unparse_addr(sender)
+ for i in range(len(recipients)):
+ recipients[i] = _unparse_addr(recipients[i])
+
+ #establish the connection
+ con = smtplib.SMTP(self.host, self.port)
+
+ if self.user != None and self.password != None:
+ con.login(self.user, self.password)
+
+ #send the mail first to the recipients in the to and cc fields
+ con.sendmail(sender, recipients, msg)
+ #then we send it for each recipient in the bcc field
+ if bcc_field != None:
+ for recipient in bcc_field:
+ con.sendmail(sender, recipient, msg)
+
+ #close the connection
+ con.quit()
+
+def _parse_addrs(addrs):
+ if type(addrs) == types.ListType:
+ return map(_parse_addr, addrs)
+ else:
+ return [_parse_addr(addrs)]
+
+def _parse_addr(addr):
+ if type(addr) in types.StringTypes:
+ return email.Utils.parseaddr(addr)
+ elif type(addr) == types.TupleType:
+ return addr
+
+def _unparse_addrs(addrs):
+ return string.join(map(_unparse_addr, addrs), ', ')
+
+def _unparse_addr(addr):
+ return email.Utils.dump_address_pair(addr)
+
+def _build_main_message(from_field, to_field, subject_field, content, cc_field=None):
+ msg = email.Message.Message()
+ msg['From'] = _unparse_addrs(from_field)
+ msg['To'] = _unparse_addrs(to_field)
+ if cc_field != None:
+ msg['Cc'] = _unparse_addrs(cc_field)
+
+ msg['Subject'] = _encode_field(subject_field)
+
+ msg['MIME-Version'] = '1.0'
+ #if content is a dictionnary (with content, type, encoding and disposition)
+ if type(content) == types.DictType:
+ msg['Content-Type'] = content['type']
+
+ #if we have subparts
+ if type(content['content']) == types.ListType:
+ for submsg in content['content']:
+ msg.attach(_build_message(submsg))
+ #if we don't have any subpart
+ else:
+ #if the content is a string
+ if type(content['content']) in types.StringTypes:
+ msg.attach(content['content'])
+ #or if it's an open file
+ else:
+ msg.attach(content['content'].read())
+
+ #if we have an encoding, then we encode
+ if content.has_key('encoding'):
+ msg = _encode_message(msg, content['encoding'])
+
+ #we allow to specify some additional fields
+ if content.has_key('additional_fields'):
+ for field in content['additional_fields']:
+ msg[field] = content['additional_fields'][field]
+
+ #if the content is a list then we have subparts
+ elif type(content) == types.ListType:
+ msg['Content-Type'] = 'multipart/mixed'
+ for submsg in content:
+ msg.attach(_build_message(submsg))
+
+ #if content is a string, then we dump it as is
+ elif type(content) in types.StringTypes:
+ msg.attach(content)
+
+ #in other cases, we consider it's an open file
+ else:
+ msg.attach(content.read())
+
+ return msg
+
+def _build_message(content):
+ msg = email.Message.Message()
+
+ #if content is a dictionnary (with content, type, encoding and disposition)
+ if type(content) == types.DictType:
+ msg['Content-Type'] = content['type']
+
+ #if we have subparts
+ if type(content['content']) == types.ListType:
+ for submsg in content['content']:
+ msg.attach(_build_message(submsg))
+ #if we don't have any subpart
+ else:
+ #if the content is a string
+ if type(content['content']) in types.StringTypes:
+ msg.attach(content['content'])
+ #or if it's an open file
+ else:
+ msg.attach(content['content'].read())
+
+ #if we have an encoding, then we encode
+ if content.has_key('encoding'):
+ msg = _encode_message(msg, content['encoding'])
+
+ #we allow to specify some additional fields
+ if content.has_key('additional_fields'):
+ for field in content['additional_fields']:
+ msg[field] = content['additional_fields'][field]
+
+ #if the content is a list then we have subparts
+ elif type(content) == types.ListType:
+ msg['Content-Type'] = 'multipart/mixed'
+ for submsg in content:
+ msg.attach(_build_message(submsg))
+
+ #if content is a string, then we dump it as is
+ elif type(content) in types.StringTypes:
+ msg.attach(content)
+
+ #in other cases, we consider it's an open file
+ else:
+ msg.attach(content.read())
+
+ return msg
+
+def _encode_field(field):
+ if quopri.encodestring(field) == field:
+ return field
+ else:
+ return email.Utils.encode(field)
+
+def _encode_message(msg, encoding):
+ if encoding == 'quoted-printable':
+ email.Encoders.encode_quopri(msg)
+ elif encoding == 'base64':
+ email.Encoders.encode_base64(msg)
+ elif encoding == '7bit' or encoding == '8bit':
+ email.Encoders.encode_7or8bit(msg)
+
+ return msg
\ No newline at end of file
diff --git a/spyce-2.1/contrib/modules/mail.txt b/spyce-2.1/contrib/modules/mail.txt
new file mode 100755
index 0000000..cf12bc8
--- /dev/null
+++ b/spyce-2.1/contrib/modules/mail.txt
@@ -0,0 +1,133 @@
+___________
+Spyce mail module
+
+1. Foreword
+
+ Sorry for you people that do not use Python 2.2, but this module is not for
+ you !
+
+ The mail module make extensive use of the 'email' module, which appeared
+ only with the 2.2 release of Python. If you don't have it, then the mail
+ module won't work at all.
+
+2. Content
+
+ 1. Foreword
+ 2. Content
+ 3. Introduction
+ 4. Module content
+ 4.1 setServer
+ 4.2 mail
+ 5. More informations
+
+3. Introduction
+
+ The mail module is a spyce module which allows to send a mail from a spyce
+ script. It's usage is simple due to it's restricted number of functions, but
+ it's capabilities are very impressive:
+
+ - Named recipients;
+ - Cc and Bcc;
+ - Multipart MIME message for attachment;
+ - Quoted-printable and base64 encoding;
+ - Sending of text or files.
+
+ All this is performed with two function.
+
+4. Module content
+
+ The mail module provides the following functions:
+
+ 4.1 setServer
+
+ Declaration:
+ setServer(host, [port=25], [user], [password])
+
+ Parameters:
+ host: The smtp server to which you will connect to send your mail.
+
+ port: The tcp/ip port to use for this connection.
+
+ user: For smtp server with authentication, those parameters allows you
+ password: to identify yourself to the server.
+
+ 4.2 send
+
+ Declaration:
+ send(from_field, to_field, subject_field, content, [cc_field=None], [bcc_field=None])
+
+ Parameters:
+ from_field: The people who this mail comes from (see syntax below).
+ to_field: The people who should receive this mail (see syntax below).
+ subject_field: The subject, a simple string.
+ content: The content (see syntax below).
+ cc_field: The people who should receive a copy of this mail.
+ bcc_field: The people who should receive a copy of this mail but without
+ the others knowing it (see syntax below).
+
+ Syntax:
+ The from_field, to_field, cc_field and bcc_field all have the same syntax.
+ They can be:
+ - a simple string containing the address.
+ eg: 'rien@yeepa.org'
+
+ - a string containing a RFC822 mail address, that is containing the name
+ and the mailbox.
+ eg: '"Adrien Plisson" '
+
+ - a tuple containing the name and the mailbox.
+ eg: ('Adrien Plisson', 'rien@yeepa.org')
+
+ - a list where each element is of one of the three syntax given before.
+ eg: [('Adrien Plisson', 'rien@yeepa.org'), '"Rimon Barr" ',
+ 'grisha@modpython.org']
+
+ you don't have to worry about the encoding of the subject field, the mail
+ function will encode it automatically in quoted-printable if necessary.
+
+ The content can be expressed in these ways:
+ - a string (what more simple ?).
+ eg: "this is the content of the mail"
+
+ - an open file which is simply dumped onto the mail.
+ eg: data = open('/usr/bin/spyce/spyce.py', 'r')
+ mail.send('rien@yeepa.org', 'barr@cs.cornell.edu', 'spyce', data)
+
+ - a dictionnary containing the folowing keys:
+ - content,
+ - type,
+ - encoding (optional),
+ - additional_fields (optional)
+ Content can be aany of the possible form for content. Type is the MIME
+ type, you can precise here some arguments, i.e. the name of a file.
+ Encoding is one of the encoding specified in the MIME definition, that
+ is 'quoted-printable', 'base64', '7bit' or '8bit'. Additional_fields
+ allows you to add some more fields in the header of the current
+ message part.
+ eg: { 'content': 'this is the content',
+ 'type': 'text/plain',
+ 'encoding': 'quoted-printable',
+ 'additional_fields': {'X-Mailer': 'Spyce mail module (the power of mail in one function)' } }
+
+ - a list where each element is of one of the possible form for content (string, file, dict or list)
+ eg: ["this is the content of spyce",
+ {'content': data,
+ 'type': 'application/octet-stream',
+ 'encoding': 'base64',
+ 'additional_fields': {'Content-Disposition': 'attachment; filename="spyce.py"'} }
+ ]
+
+ Please note that the content syntax definition is redundant: each content
+ can contain content. This way you can build multipart MIME mails the way
+ you want. You can also provide some extra headers.
+
+ When you specify an encoding, the mail function will encode the content
+ according to the specified algorithm. Don't bother encoding it yourself...
+
+5. More informations
+
+ RFC 2821: Simple Mail Transfert Protocol
+ RFC 2822: Internet Message Format
+ RFC 2045: MIME Part One: Format of Internet Message Bodies
+ RFC 2046: MIME Part Two: Media Types
+ RFC 2047: MIME Part Three: Message Header Extensions for Non-ASCII Text
diff --git a/spyce-2.1/contrib/modules/session1.py b/spyce-2.1/contrib/modules/session1.py
new file mode 100755
index 0000000..d2dca9e
--- /dev/null
+++ b/spyce-2.1/contrib/modules/session1.py
@@ -0,0 +1,371 @@
+##################################################
+# SPYCE - Python-based HTML Scripting
+# Copyright (c) 2002 Rimon Barr.
+##################################################
+
+# This is the old session module. It is provided for backwards compatibility,
+# but not recommended for new development.
+
+from spyceModule import spyceModule
+import re, time, string, random
+import spyceLock
+
+try:
+ import cPickle
+ pickle = cPickle
+except:
+ import pickle
+
+__doc__ = '''Session module provides support for session management - the
+storage of variables on the server between requests under some short
+identifier.
+
+A user must call setHandler() to determine how the sessions are stored, before
+using the other session methods. The get(), set() and delete() methods provide
+access to the session information.
+
+The autoSession() method will turn on the automatic session management
+(loading and saving the session). When automatic session management is turned
+on the session information, identifier, parameter name and browser method are
+stored in the variables called auto, autoID, autoName and autoMethod,
+respectively.'''
+
+class session1(spyceModule):
+ def start(self):
+ "Initialise the session module variables."
+ self._serverobject = self._api.getServerObject()
+ if 'session' not in dir(self._serverobject):
+ self._serverobject.session = sessionHandlerRegistry()
+ self._handler = None
+ self._clearAutoSession()
+ def finish(self, theError=None):
+ "Save the session, if automatic session management is turned on."
+ if self.autoID:
+ self.set(self.auto, self.autoExpire, self.autoID)
+ if self.autoMethod=='cookie':
+ self._api.getModule('cookie').set(self.autoName, self.autoID)
+ sessionCleanup(self._serverobject.session)
+ def init(self, handler=None, *args, **kwargs):
+ if handler:
+ session = apply(self.setHandler, (handler,)+args)
+ if kwargs.has_key('auto') and kwargs['auto']:
+ auto = kwargs['auto']
+ if type(auto) != type(()):
+ auto = (auto,)
+ apply(session.autoSession, auto)
+ def setHandler(self, file_name, *params):
+ "Select a session handler."
+ file_name = string.split(file_name, ':')
+ if len(file_name)==1: file, name = None, file_name[0]
+ else: file, name = file_name[:2]
+ if file: handler = self._api.loadModule(name, file, self._api.getFilename())
+ else: handler = eval(name)
+ self._handler = apply(handler, (self,)+params)
+ self._serverobject.session.add(self._handler)
+ return self
+ def get(self, id): # method deletes session, if stale
+ "Retrieve session information."
+ if not self._handler: raise 'call setHandler to initialise'
+ return self._handler.get(id)
+ def delete(self, id=None):
+ "Delete session information."
+ if not self._handler: raise 'call setHandler to initialise'
+ if not id:
+ id = self.autoID
+ self._clearAutoSession()
+ return self._handler.delete(id)
+ def set(self, state, expire, id=None):
+ "Set session information."
+ if not self._handler: raise 'call setHandler to initialise'
+ return self._handler.set(state, expire, id)
+ def clear(self):
+ "Clear all session information in current handler."
+ if not self._handler: raise 'call setHandler to initialise'
+ return self._handler.clear()
+ def autoSession(self, expire, method='cookie', name='spyceSession'):
+ "Turn on automatic session management."
+ if not self._handler: raise 'call setHandler to initialise'
+ method = string.lower(method)
+ if method=='cookie': self.autoID = self._api.getModule('cookie').get(name)
+ elif method=='post': self.autoID = self._api.getModule('request').post1(name)
+ elif method=='get': self.autoID = self._api.getModule('request').get1(name)
+ else: raise 'runtime error: invalid autosession method'
+ self.autoMethod = method
+ self.autoName = name
+ self.autoExpire = expire
+ self.auto = None
+ if self.autoID:
+ self.auto = self.get(self.autoID)
+ if not self.auto: self.autoID = None
+ if not self.autoID: # generate a sessionid
+ self.autoID = self.set(None, self.autoExpire)
+ def _clearAutoSession(self):
+ self.auto = None
+ self.autoID = None
+ self.autoMethod = None
+ self.autoName = None
+ self.autoExpire = None
+
+##################################################
+# Cleanup
+#
+
+# expire sessions every n requests in expectation
+SESSION_EXPIRE_CHECK = 50
+
+class sessionHandlerRegistry:
+ "Registry of all used session handlers."
+ def __init__(self):
+ self.handlers = {}
+ def add(self, handler):
+ self.handlers[handler.getHandlerID()] = handler
+ def list(self):
+ return self.handlers.values()
+ def remove(self, handler):
+ del self.handlers[handler.getHandlerID()]
+
+def sessionCleanup(registry):
+ """Iterates through all session handlers and sessions to perform session
+ cleanup"""
+ if random.randrange(SESSION_EXPIRE_CHECK): return
+ for handler in registry.list():
+ try:
+ sessions = handler.keys()
+ for s in sessions:
+ handler.get(s) # will delete stale sessions
+ except:
+ registry.remove(handler)
+
+
+##################################################
+# Session handlers
+#
+
+class sessionHandler:
+ '''All session handlers should subclass this, and implement the methods
+ marked: 'not implemented'.'''
+ def __init__(self, sessionModule):
+ self.childnum = sessionModule._api.getServerID()
+ def getHandlerID(self):
+ raise 'not implemented'
+ def get(self, id): # method should delete, if session is stale
+ raise 'not implemented'
+ def delete(self, id):
+ raise 'not implemented'
+ def clear(self):
+ raise 'not implemented'
+ def set(self, state, expire, id=None):
+ raise 'not implemented'
+ def keys(self):
+ raise 'not implemented'
+ def __getitem__(self, key):
+ return self.get(key)
+ def __delitem__(self, key):
+ return self.delete(key)
+
+##################################################
+# File-based session handler
+#
+
+class session_dir(sessionHandler):
+ def __init__(self, sessionModule, dir=None):
+ sessionHandler.__init__(self, sessionModule)
+ if not dir:
+ import spyce
+ dir = spyce.getServer().config.tmp
+ if not os.path.exists(dir):
+ raise "session directory '%s' does not exist" % dir
+ self.dir = dir
+ self.prefix = 'spy'
+ def getHandlerID(self):
+ return 'session_dir', self.childnum, self.dir
+ def get(self, id):
+ if not id: return None
+ filename = os.path.join(self.dir, self.prefix+id)
+ f=None
+ sessionInfo = None
+ try:
+ f=open(filename, 'rb')
+ sessionInfo = pickle.load(f)
+ f.close()
+ except:
+ try:
+ if f: f.close()
+ os.unlink(filename)
+ except: pass
+ if sessionInfo:
+ if time.time() > sessionInfo['expire']:
+ self.delete(id)
+ return None
+ else: return sessionInfo['state']
+ else: return None
+ def delete(self, id):
+ try:
+ filename = os.path.join(self.dir, self.prefix+id)
+ os.remove(filename)
+ except: pass
+ def clear(self):
+ for id in self.keys():
+ self.delete(id)
+ def set(self, state, expire, id=None):
+ f=None
+ try:
+ if id:
+ filename = os.path.join(self.dir, self.prefix+id)
+ f=open(filename, 'wb')
+ else:
+ filename, f, id = openUniqueFile(self.dir, self.prefix, ('%d_' % self.childnum))
+ sessionInfo = {}
+ sessionInfo['expire'] = int(time.time())+expire
+ sessionInfo['state'] = state
+ pickle.dump(sessionInfo, f, -1)
+ f.close()
+ except:
+ try:
+ if f: f.close()
+ except: pass
+ raise
+ return id
+ def keys(self):
+ sessions = os.listdir(self.dir)
+ sessions = filter(lambda s, p=self.prefix: s[:len(p)]==p, sessions)
+ sessions = map(lambda s, self=self: s[len(self.prefix):], sessions)
+ return sessions
+
+# requires unique (dir, prefix)
+def openUniqueFile(dir, prefix, unique, mode='w', max=1000000):
+ filelock = spyceLock.fileLock(os.path.join(dir, prefix))
+ filelock.acquire()
+ try:
+ id = "%06d"%random.randrange(max)
+ filename = os.path.join(dir, prefix+unique+id)
+ while os.path.exists(filename):
+ id = str(random.randrange(max))
+ filename = os.path.join(dir, prefix+unique+id)
+ f = None
+ f = open(filename, mode)
+ return filename, f, unique+id
+ finally:
+ filelock.release()
+
+##################################################
+# Hash file session handlers
+#
+
+class sessionHandlerDBM(sessionHandler):
+ def __init__(self, sessionModule, filename):
+ sessionHandler.__init__(self, sessionModule)
+ self.filename = filename
+ self.dbm = None
+ self.BINARY_MODE = 1
+ self.dbm_type = None # redefine in subclass
+ def getHandlerID(self):
+ return 'session_'+self.dbm_type, self.childnum, self.filename
+ def _open(self):
+ raise 'need to implement'
+ def _close(self):
+ if self.dbm:
+ self.dbm.close()
+ self.dbm = None
+ def get(self, id):
+ if not id: return None
+ self._open()
+ try:
+ expire, state = None, None
+ if self.dbm.has_key(id):
+ expire, state = pickle.loads(self.dbm[id])
+ if expire!=None and time.time() > expire:
+ self.delete(id)
+ state = None
+ return state
+ finally:
+ self._close()
+ def delete(self, id):
+ self._open()
+ try:
+ if self.dbm.has_key(id):
+ del self.dbm[id]
+ finally:
+ self._close()
+ def clear(self):
+ if os.path.exists(self.filename):
+ os.unlink(self.filename)
+ def set(self, state, expire, id=None):
+ self._open()
+ try:
+ if not id:
+ id = generateKey(self.dbm, self.childnum)
+ value = pickle.dumps( (int(time.time())+expire, state), self.BINARY_MODE)
+ self.dbm[id] = value
+ return id
+ finally:
+ self._close()
+ def keys(self):
+ self._open()
+ try:
+ return self.dbm.keys()
+ finally:
+ self._close()
+
+def opendb(dbm_session_handler, module, filename, flags):
+ mod = __import__(module)
+ if not dbm_session_handler.dbm:
+ dbm_session_handler.dbm = mod.open(filename, flags)
+
+class session_gdbm(sessionHandlerDBM):
+ def __init__(self, sessionModule, filename):
+ sessionHandlerDBM.__init__(self, sessionModule, filename)
+ self.dbm_type = 'gdbm'
+ def _open(self):
+ opendb(self, self.dbm_type, self.filename, 'cu')
+
+class session_bsddb(sessionHandlerDBM):
+ def __init__(self, sessionModule, filename):
+ sessionHandlerDBM.__init__(self, sessionModule, filename)
+ self.dbm_type = 'bsddb'
+ def _open(self):
+ opendb(self, 'dbhash', self.filename, 'c')
+
+def generateKey(hash, prefix, max = 1000000):
+ prefix = str(prefix)+'_'
+ key = random.randrange(max)
+ while hash.has_key(prefix+str(key)):
+ key = random.randrange(max)
+ key = prefix+str(key)
+ hash[key] = pickle.dumps(None, 1)
+ return key
+
+
+##################################################
+# User callback session handlers
+#
+
+class session_user(sessionHandler):
+ '''User-callback session handler'''
+ def __init__(self, sessionModule, getf, setf, delf, idsf, info=None):
+ self.serverID = sessionModule._api.getServerID()
+ self.info = info
+ self.getf = getf
+ self.setf = setf
+ self.delf = delf
+ self.idsf = idsf
+ def getHandlerID(self):
+ return 'session_user', self.serverID, self.info
+ def get(self, id): # method should delete, if session is stale
+ return self.getf(self.info, id)
+ def set(self, state, expire, id):
+ return self.setf(self.info, state, expire, self.serverID, id)
+ def delete(self, id):
+ return self.delf(self.info, id)
+ def keys(self):
+ return self.idsf(self.info)
+ def clear(self):
+ for id in self.keys():
+ self.delete(id)
+
+##################################################
+# database-based session handlers
+#
+
+# rimtodo: database-based session handler
+
diff --git a/spyce-2.1/contrib/modules/spydurus.py b/spyce-2.1/contrib/modules/spydurus.py
new file mode 100755
index 0000000..1058ef1
--- /dev/null
+++ b/spyce-2.1/contrib/modules/spydurus.py
@@ -0,0 +1,110 @@
+# spydurus provides a threadsafe, pooled Spyce interface to a durus database.
+# Author: Jonathan Ellis
+
+# If you're just trying to get a feel for Spyce, the main thing to
+# understand is that this provides (via a subclass) the get method you
+# see used (e.g., "data.get(id)") in .spy pages. More than that you don't
+# really need to know unless you need to write your own connection
+# pool module.
+
+CONNECTIONS = 3 # max connections to put in the pool
+
+import sys, os, os.path, threading, time
+import spyce, spyceUtil
+
+# try to start a server:
+# in a production environment, you'd keep it running with inittab or something,
+# so this step wouldn't be necessary.
+
+# The best approach is to fork and then use StorageServer.serve to start durus;
+# that way we don't have to worry about durus being on the path. However,
+# since win32 doesn't support fork, AND that's where we're most likely to have
+# PATH issues (durus on win32 doesn't modify the path on install),
+# in the interest of making this run out of the box for windows people we'll
+# take the StorageServer approach, but in a thread, not a new process.
+
+# Please use the inittab approach in production; Durus will be more performant
+# in its own process.
+from durus.storage_server import StorageServer
+from durus.file_storage import FileStorage, TempFileStorage
+_runningDurus = False
+def maybeStartDurus(db_path):
+ if spyce.getServer().threaded():
+ _runningDurus = True
+ def _maybeStartDurus():
+ st = db_path and FileStorage(db_path) or TempFileStorage()
+ try:
+ StorageServer(st).serve()
+ except:
+ # already running
+ _runningDurus = False
+ _t = threading.Thread(target=_maybeStartDurus)
+ _t.start()
+ def _cleanup():
+ if _runningDurus:
+ from durus.run_durus import stop_durus, DEFAULT_HOST, DEFAULT_PORT
+ stop_durus(DEFAULT_HOST, DEFAULT_PORT)
+ _t.join()
+ import atexit
+ atexit.register(_cleanup)
+
+import Queue
+q = Queue.Queue()
+
+def initPool(connections=3, db_path=None):
+ "if another process takes care of durus, you never need to pass db_path"
+ from durus.connection import Connection
+ if spyce.getServer().threaded():
+ from durus.client_storage import ClientStorage
+ for i in range(connections * 3):
+ if q.qsize() >= connections:
+ break
+ try:
+ q.put(Connection(ClientStorage()))
+ except:
+ time.sleep(0.5) # sometimes takes a while for server to start
+ else:
+ # not a long-running process -- spyce running under [Fast]CGI or mod_python
+ st = spyceUtil.tryForAwhile(lambda: FileStorage(db_path))
+ if not st:
+ raise 'timeout while getting durus connection'
+ q.put(Connection(st))
+ if not q.qsize():
+ raise 'no durus connections created'
+
+# the actual spyceModule is refreshingly short now that starting Durus is out of the way
+from spyceModule import spyceModule
+
+class spydurus(spyceModule):
+ def start(self):
+ self.conn = None
+ try:
+ self.conn = q.get(timeout=10)
+ except Queue.Empty:
+ raise 'timeout while getting durus connection'
+ else:
+ self.conn.abort() # syncs connection
+
+ # spyce automatically calls finish methods at the end of each request.
+ # this is an excellent way to make sure that our connection always
+ # returns to the queue.
+ def finish(self, err):
+ q.put(self.conn)
+
+ def root(self):
+ return self.conn.get_root()
+
+ def get(self, oid):
+ """oid should be a formatted oid, not a raw _p_oid"""
+ return self.conn.get(int(oid))
+
+ def commit(self):
+ self.conn.commit()
+
+# you may wish to inherit from this instead of Persistent directly
+from durus.persistent import Persistent
+class PersistentWithId(Persistent):
+ # _p_format_oid is rather cumbersome to type
+ def id(self):
+ return self._p_format_oid()
+
diff --git a/spyce-2.1/contrib/modules/template.py b/spyce-2.1/contrib/modules/template.py
new file mode 100755
index 0000000..b47a266
--- /dev/null
+++ b/spyce-2.1/contrib/modules/template.py
@@ -0,0 +1,83 @@
+##################################################
+# SPYCE - Python-based HTML Scripting
+# Copyright (c) 2002 Rimon Barr.
+#
+# CVS: $Id: template.py 859 2006-04-30 00:55:03Z ellisj $
+##################################################
+
+from spyceModule import spyceModule
+import spyceException, spyceCache
+import os
+
+__doc__ = """
+Template module provides templating functionality: the ability to separate
+form from function, or HTML page design from programming code. This module
+currently provides support for the Cheetah template engine.
+
+The Cheetah engine is invoked as follows:
+ cheetah ( file, [lookup] )
+
+Calling this function will invoke the Cheetah engine to compile (and
+cache) the template file> provided. The engine then "runs" the
+template and fills in the appropriate data from the lookup dictionary,
+or list of dictionaries. If the lookup is omitted, the convenient
+default is to use the local and global variables from the current
+context. The template is filled and the resulting string is returned.
+
+In general, that the Python path must simply include the Cheetah
+installation directory and Spyce will find it.
+
+Support for other templating engines could be added in a similar fashion.
+"""
+
+class template(spyceModule):
+ def cheetah(self, filename, lookup=None):
+ "Hook into the Cheetah template engine."
+ # check whether cheetah installed
+ from Cheetah.Compiler import Compiler
+ # define template cache
+ if not self._api.getModule('pool').has_key('cheetahCache'):
+ self._api.getModule('pool')['cheetahCache'] = spyceCache.semanticCache(spyceCache.memoryCache(), cheetahValid, cheetahGenerate)
+ cheetahCache = self._api.getModule('pool')['cheetahCache']
+ # absolute filename, relative to script filename
+ filename = os.path.abspath(os.path.join(
+ os.path.dirname(self._api.getFilename()), filename))
+ # set lookup variables
+ if lookup == None:
+ import inspect
+ lookup = [inspect.currentframe().f_back.f_locals, inspect.currentframe().f_back.f_globals]
+ elif type(lookup)!=type([]):
+ lookup = [lookup]
+ # compile (or get cached) and run template
+ return cheetahCache[filename](searchList=lookup)
+
+##################################################
+# Cheetah semantic cache helper functions
+#
+
+def cheetahValid(filename, validity):
+ try:
+ return os.path.getmtime(filename) == validity
+ except: return 0
+
+def cheetahGenerate(filename):
+ # check permissions
+ if not os.path.exists(filename):
+ raise spyceException.spyceNotFound(filename)
+ if not os.access(filename, os.R_OK):
+ raise spyceException.spyceForbidden(filename)
+ # read the template
+ f = None
+ try:
+ f = open(filename, 'r')
+ buf = f.read()
+ finally:
+ if f: f.close()
+ # compile template, get timestamp
+ mtime = os.path.getmtime(filename)
+ from Cheetah.Compiler import Compiler
+ code = Compiler(source=buf).__str__()
+ dict = {}
+ exec code in dict
+ return mtime, dict['GenTemplate']
+
diff --git a/spyce-2.1/contrib/modules/toc.py b/spyce-2.1/contrib/modules/toc.py
new file mode 100755
index 0000000..8cbb549
--- /dev/null
+++ b/spyce-2.1/contrib/modules/toc.py
@@ -0,0 +1,381 @@
+##################################################
+# SPYCE - Python-based HTML Scripting
+# Copyright (c) 2002 Rimon Barr.
+#
+# Refer to spyce.py
+# CVS: $Id: toc.py 944 2006-07-22 17:25:58Z ellisj $
+##################################################
+
+from spyceModule import spyceModule
+import tree
+
+__doc__ = '''
+The TOC module provides support for constructing a table contents for a
+lengthy document, such as this user documentation. The primary task of the TOC
+module is to maintain a document tree, and initiate callbacks at the
+appropriate points in the document. Note that this module may automatically
+force a secondary processing of the Spyce file to resolve forward references.
+
+
+The module provides the following methods to segment the document:
+
+
+
+ - begin( data, [tag] ):
Increase the nesting level and add a
+ new section. The data is stored in the document tree, and used for
+ callbacks (see later). An optional tag may be associated with the
+ node, otherwise one will automatically be generated. The function b()
+ is equivalent.
+
+
- next( data, [tag] ):
Add a new section at the same nesting
+ level. The data is stored in the document tree, and used for
+ callbacks (see later). An optional tag may be associated with the
+ node, or one will be automatically generated. The function n() is
+ equivalent.
+
+
- end():
Decrease the nesting level. The function e()
+ is equivalent.
+
+
- anchor( data, [tag] ):
Set data and optionally the
+ tag associated with the root of the document tree. If the tag is
+ omitted, it defaults to the string 'root'.
+
+
- level( depth, data, [tag] ):
Start a new section at given
+ depth with given data and optional tag. The necessary
+ begin(), next() and end() calls are automatically made, based on the current
+ document depth, so both types of calls can be inter-mixed.
+
+
- l1( data, [tag] ):
Start a level 1 section. This
+ function merely calls level(1, data, tag).
+ The functions, l2()...l9() are similarly defined.
+
+
+
+The following methods provide access to document information:
+
+
+
+ - getTag():
Return the tag of the current document section.
+
+
+
- getNumbering( [tag] )
Return the numbering of some section
+ of the document identified by the given tag. If the tag is omitted,
+ the current document section is assumed. The numbering is an array of
+ numbers. This function may return 'None' on the first pass through a
+ document.
+
+
- getData( [tag] )
Return the data associated with some
+ section of the document identified by the given tag. If the tag is
+ omitted, the current document section is assumed. This function may return
+ 'None' on the first pass through a document.
+
+
- getDepth( [tag] )
Return the depth of some section of the
+ document identified by the given tag. If the tag is omitted, the
+ current document section is assumed. This function may return 'None' on the
+ first pass through a document.
+
+
- getNextTag( [tag] )
Return the tag of the section following
+ some section of the document identified by the given tag. If the tag
+ is omitted, the current document section is assumed. If this is the last
+ section of the document, then this function will return 'None'. This
+ function may return 'None' on the first pass through a document.
+
+
- getPrevTag( [tag] )
Return the tag of the section before
+ some section of the document identified by the given tag. If the tag
+ is omitted, the current document section is assumed. If this is the first
+ section of the document, then this function will return 'None'. This
+ function may return 'None' on the first pass through a document.
+
+
- getParentTag( [tag] )
Return the tag of the section above
+ (or containing) some section of the document identified by the given
+ tag. If the tag is omitted, the current document section is assumed.
+ If this is the top-most section of the document, then this function will
+ return 'None'. This function may return 'None' on the first pass through a
+ document.
+
+
- getChildrenTags( [tag] )
Return a list (possibly empty) of
+ tags of the sections directly contained within some section of the document
+ identified by the given tag. If the tag is omitted, the current
+ document section is assumed. This function may return a shorter list than
+ anticipated or 'None', on the first pass through a document.
+
+
+
+The TOC modules can make callbacks to handlers that format the document
+correctly. The handlers should be defined and registered before the first
+section break in the document. The following functions register handlers:
+
+
+
+ - setDOC_PUSH( f ):
Register a function f to be called
+ when the nesting depth of the document increases.
+
+
- setDOC_POP( f ):
Register a function f to be called
+ when the nesting depth of the document decreases.
+
+
- setDOC_START( f ):
Register a funtion f to be called
+ at the beginning of a section.
+
+
- setDOC_END( f ):
Register a function f to be called
+ at the end of a section.
+
+
- setTOC_PUSH( f ):
Register a function f to be called
+ when the nesting depth of the table of contents increases.
+
+
- setTOC_POP( f ):
Register a function f to be called
+ when the nesting depth of the table of contents decreases.
+
+
- setTOC_ENTRY( f ):
Register a function f to be called
+ for each table of contents entry.
+
+
+
+Each callback function should be of the form:
f(depth,
+tag, numbering, data), where: depth is the nesting depth,
+tag is the associated tag, numbering is the position array, and
+data is the associated data of the section for which the callback was
+made.
+
+The DOC callbacks are made as the sections are encountered. The
+TOC callbacks are made while printing the table of contents. If the
+modules detects that forward references exist in the document, the document
+will be processed twice, and only the second output will be sent. Note that
+buffering MUST be turned on for this to function correctly.
+
+To display a table of contents, define the appropriate TOC callback functions
+and call:
+
+
+
+ - showTOC(): Display the table of contents.
+
+
+'''
+
+ROOT_NAME = 'root'
+
+class toc(spyceModule):
+
+ def start(self):
+ if not self._api.getModule('pool').has_key('toc'):
+ self._api.getModule('pool')['toc'] = {}
+ try:
+ self.oldtree, self.oldtags = self._api.getModule('pool')['toc'][self._api.getFilename()]
+ except (KeyError, TypeError):
+ self.oldtree = tree.tree( (ROOT_NAME, [], None) )
+ self.oldtags = {ROOT_NAME: self.oldtree}
+ # tree data: (tag, numbering, data)
+ self.tree = tree.tree((ROOT_NAME, [], None))
+ self.tags = {ROOT_NAME: self.tree}
+ self.node = self.tree
+ self.numbering = []
+ self.autotag = 0
+ self.tocShown = 0
+ self.fDOC_PUSH = None
+ self.fDOC_POP = None
+ self.fDOC_START = None
+ self.fDOC_END = None
+ self.fTOC_PUSH = None
+ self.fTOC_POP = None
+ self.fTOC_ENTRY = None
+
+ def finish(self, theError):
+ if self.oldtree is not None:
+ self.oldtree.delete()
+ self.oldtree = None
+ self.oldtags = None
+
+ def generate(self):
+ self.tree.computePreChain()
+ regenerate = not (self.oldtree == self.tree)
+ file = self._api.getFilename()
+ self._api.getModule('pool')['toc'][file] = self.tree, self.tags
+ if self.tocShown and regenerate:
+ self._api.getModule('redirect').internal(filename=file)
+
+ # set callbacks
+ def setDOC_PUSH(self, f):
+ self.fDOC_PUSH = f
+ def setDOC_POP(self, f):
+ self.fDOC_POP = f
+ def setDOC_START(self, f):
+ self.fDOC_START = f
+ def setDOC_END(self, f):
+ self.fDOC_END = f
+ def setTOC_PUSH(self, f):
+ self.fTOC_PUSH = f
+ def setTOC_POP(self, f):
+ self.fTOC_POP = f
+ def setTOC_ENTRY(self, f):
+ self.fTOC_ENTRY = f
+
+ # sectioning
+ def begin(self, data, tag=None, number=1):
+ self._emit(self.node, self.fDOC_PUSH)
+ self.numbering = _in(self.numbering)
+ if number:
+ self.numbering = _inc(self.numbering)
+ self.node = self.node.append( (tag, self.numbering, data) )
+ else:
+ self.node = self.node.append( (tag, None, data) )
+ if not tag: tag = self._genTag()
+ self.tags[tag] = self.node
+ self._emit(self.node, self.fDOC_START)
+ def end(self):
+ self._emit(self.node, self.fDOC_END)
+ self.numbering = _out(self.numbering)
+ self.node = self.node.parent
+ self._emit(self.node, self.fDOC_POP)
+ def next(self, data, tag=None, number=1):
+ self._emit(self.node, self.fDOC_END)
+ self.node = self.node.parent
+ if number:
+ self.numbering = _inc(self.numbering)
+ self.node = self.node.append( (tag, self.numbering, data) )
+ else:
+ self.node = self.node.append( (tag, None, data) )
+ if not tag: tag = self._genTag()
+ self.tags[tag] = self.node
+ self._emit(self.node, self.fDOC_START)
+ def anchor(self, data, tag=ROOT_NAME):
+ self.tree.data = tag, [], data
+ self.tags[tag] = self.tree
+
+ # shortcuts
+ b=begin
+ e=end
+ n=next
+
+ # sectioning by depth
+ def level(self, depth, data, tag=None):
+ curdepth = self.getDepth()
+ if curdepth > depth: # indent
+ while curdepth > depth:
+ self.end()
+ curdepth = self.getDepth()
+ self.next(data, tag)
+ elif curdepth < depth: # outdent
+ while curdepth < depth - 1:
+ self.begin(None)
+ curdepth = self.getDepth()
+ self.begin(data, tag)
+ else: # next
+ self.next(data, tag)
+ def l1(self, data, tag=None):
+ self.level(1, data, tag)
+ def l2(self, data, tag=None):
+ self.level(2, data, tag)
+ def l3(self, data, tag=None):
+ self.level(3, data, tag)
+ def l4(self, data, tag=None):
+ self.level(4, data, tag)
+ def l5(self, data, tag=None):
+ self.level(5, data, tag)
+ def l6(self, data, tag=None):
+ self.level(6, data, tag)
+ def l7(self, data, tag=None):
+ self.level(7, data, tag)
+ def l8(self, data, tag=None):
+ self.level(8, data, tag)
+ def l9(self, data, tag=None):
+ self.level(9, data, tag)
+
+ # show toc
+ def showTOC(self):
+ self.tocShown = 1
+ self._tocHelper(self.oldtree)
+ def _tocHelper(self, node):
+ self._emit(node, self.fTOC_ENTRY)
+ if node.children:
+ self._emit(node, self.fTOC_PUSH)
+ for c in node.children:
+ self._tocHelper(c)
+ self._emit(node, self.fTOC_POP)
+
+ # current state
+ def getTag(self, node=None):
+ self.tocShown = 1
+ if not node: node = self.node
+ tag, numbering, data = node.data
+ return tag
+ def getNumbering(self, tag=None):
+ self.tocShown = 1
+ try:
+ node = self.node
+ if tag: node = self.oldtags[tag]
+ tag, numbering, data = node.data
+ return numbering
+ except KeyError:
+ return None
+ def getData(self, tag=None):
+ self.tocShown = 1
+ try:
+ node = self.node
+ if tag: node = self.oldtags[tag]
+ tag, numbering, data = node.data
+ return data
+ except KeyError:
+ return None
+ def getDepth(self, tag=None):
+ self.tocShown = 1
+ try:
+ node = self.node
+ if tag: node = self.tags[tag]
+ return node.depth
+ except KeyError:
+ return None
+ def getNextTag(self, tag=None):
+ self.tocShown = 1
+ try:
+ if not tag: tag = self.getTag()
+ tag = self.oldtags[tag].next
+ if tag==None: return None
+ return self.getTag(tag)
+ except KeyError:
+ return None
+ def getPrevTag(self, tag=None):
+ self.tocShown = 1
+ try:
+ if not tag: tag = self.getTag()
+ node = self.oldtags[tag].prev
+ if node==None: return None
+ return self.getTag(node)
+ except KeyError:
+ return None
+ def getParentTag(self, tag=None):
+ self.tocShown = 1
+ try:
+ if not tag: tag = self.getTag()
+ node = self.oldtags[tag].parent
+ if node==None: return None
+ return self.getTag(node)
+ except KeyError:
+ return None
+ def getChildrenTags(self, tag=None):
+ self.tocShown = 1
+ try:
+ if not tag: tag = self.getTag()
+ nodes = self.oldtags[tag].children
+ return map(self.getTag, nodes)
+ except KeyError:
+ return None
+
+ # internal helpers
+ def _genTag(self):
+ tag = 'auto_'+str(self.autotag)
+ self.autotag = self.autotag + 1
+ return tag
+ def _emit(self, node, f):
+ tag, numbering, data = node.data
+ if f: s = f(node.depth, tag, numbering, data)
+
+# hierarchical counting
+def _inc(numbering, inc=1):
+ return numbering[:-1]+[numbering[-1]+inc]
+def _in(numbering, start=0):
+ return numbering+[start]
+def _out(numbering):
+ return numbering[:-1]
+
+def defaultOutput(tag, numbering, data):
+ return reduce(lambda s, i: '%s%d.' % (s, i), numbering, '') + ' ' + str(data)
diff --git a/spyce-2.1/contrib/modules/toc.pyc b/spyce-2.1/contrib/modules/toc.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..3449815f0a208a222c43fb437498e9c132d35b19
GIT binary patch
literal 17962
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
bE_aD5Vp&FdS{tvo~vFiT=(i&?}
literal 0
HcmV?d00001
diff --git a/spyce-2.1/contrib/pyweboff/config.py b/spyce-2.1/contrib/pyweboff/config.py
new file mode 100755
index 0000000..290584a
--- /dev/null
+++ b/spyce-2.1/contrib/pyweboff/config.py
@@ -0,0 +1,31 @@
+from spyceconf import *
+
+# The root option defines the path from which Spyce requests are processed.
+# I.e., when a request for http://yourserver/path/foo.spy arrives,
+# Spyce looks for foo.spy in /path/.
+PYWEBOFF_HOME = os.path.abspath(os.path.split(__file__)[0])
+root = os.path.join(PYWEBOFF_HOME, 'www')
+# This allows you to import .py modules from the lib directory.
+sys.path.append(os.path.join(PYWEBOFF_HOME, 'lib'))
+
+# Some commonly overridden options -- see spyceconf.py from the Spyce
+# distribution for details and the full set of options.
+port = 8001 # webserver port
+indexExtensions = ['spy'] # list of extensions to check if directory requested
+
+#### pyweboff config ####
+db = SqlSoup('sqlite:///%s' % os.path.join(PYWEBOFF_HOME, 'pyweboff.db'))
+
+# admin login is "Bhargan Basepair", "basepair"
+def validator(login, password):
+ L = db.users.select_by(name=login, password=password)
+ if L:
+ return login
+ return None
+def admin_validator(login, password):
+ L = db.users.select_by(name=login, password=password, admin=True)
+ if L:
+ return login
+ return None
+login_defaultvalidator = validator
+login_storage = FileStorage(os.path.join(PYWEBOFF_HOME, 'login-tokens'))
diff --git a/spyce-2.1/contrib/pyweboff/lib/actions.py b/spyce-2.1/contrib/pyweboff/lib/actions.py
new file mode 100755
index 0000000..9a949a3
--- /dev/null
+++ b/spyce-2.1/contrib/pyweboff/lib/actions.py
@@ -0,0 +1,19 @@
+from spyceConfig import db
+
+def book_loan(api, user_name, books):
+ for book_id in books:
+ db.loans.insert(user_name=user_name, book_id=book_id)
+ db.flush()
+
+def book_return(api, books):
+ for book_id in books:
+ loan = db.loans.selectone_by(book_id=book_id)
+ db.delete(loan)
+ db.flush()
+
+def user_new(api, name, email, password, classname, admin):
+ u = db.users.insert(name=name, email=email, password=password, classname=classname, admin=admin)
+ u.admin = admin
+ db.flush()
+ api.redirect.external('/')
+ api.response.end()
diff --git a/spyce-2.1/contrib/pyweboff/lib/pyweboff.sql b/spyce-2.1/contrib/pyweboff/lib/pyweboff.sql
new file mode 100755
index 0000000..dde7381
--- /dev/null
+++ b/spyce-2.1/contrib/pyweboff/lib/pyweboff.sql
@@ -0,0 +1,38 @@
+CREATE TABLE books (
+ id integer PRIMARY KEY, -- auto-SERIAL in sqlite
+ title text NOT NULL,
+ published_year char(4) NOT NULL,
+ authors text NOT NULL
+);
+
+CREATE TABLE users (
+ name varchar(32) PRIMARY KEY,
+ email varchar(128) NOT NULL,
+ password varchar(128) NOT NULL,
+ classname text,
+ admin int NOT NULL -- 0 = false
+);
+
+CREATE TABLE loans (
+ book_id int PRIMARY KEY REFERENCES books(id),
+ user_name varchar(32) references users(name)
+ ON DELETE SET NULL ON UPDATE CASCADE,
+ loan_date date DEFAULT current_timestamp
+);
+
+insert into users(name, email, password, admin)
+values('Bhargan Basepair', 'basepair@example.edu', 'basepair', 1);
+insert into users(name, email, password, admin)
+values('Joe Student', 'student@example.edu', 'student', 0);
+
+insert into books(title, published_year, authors)
+values('Mustards I Have Known', '1989', 'Jones');
+insert into books(title, published_year, authors)
+values('Regional Variation in Moss', '1971', 'Flim and Flam');
+
+insert into loans(book_id, user_name, loan_date)
+values (
+ (select min(id) from books),
+ (select name from users where name like 'Joe%'),
+ '2006-07-12 0:0:0')
+;
diff --git a/spyce-2.1/contrib/pyweboff/www/index.spy b/spyce-2.1/contrib/pyweboff/www/index.spy
new file mode 100755
index 0000000..ff3a1a6
--- /dev/null
+++ b/spyce-2.1/contrib/pyweboff/www/index.spy
@@ -0,0 +1,73 @@
+[[\
+from spyceConfig import db
+
+books = db.books.select()
+users = db.users.select_by(admin=False)
+]]
+
+
+
+[[!
+def new_user(self, api):
+ api.redirect.external('user-new.spy')
+ api.response.end()
+]]
+
+
+
+[[ current_user = db.users.selectone_by(name=request.login_id()) ]]
+Welcome, [[= current_user.name ]]
+
+
+
+
+
+ |
+ Title |
+ Published |
+ Author(s) |
+ On loan to |
+ Borrower's email |
+ Loaned |
+
+
+[[\
+# sqlite3 has a bug that prevents using the simpler join
+# of (books, (users, loans))
+book_loans = db.join(db.join(db.books, db.loans, isouter=True),
+ db.users, isouter=True)
+]]
+[[ for book_loan in book_loans.select():{ ]]
+
+ |
+ [[= book_loan.title ]] |
+ [[= book_loan.published_year ]] |
+ [[= book_loan.authors ]] |
+ [[ if book_loan.user_name:{ # loaned out ]]
+ [[= book_loan.user_name ]] |
+ [[ if current_user.admin or current_user.classname == book_loan.classname:{ ]]
+ [[= book_loan.email ]] |
+ [[ } ]]
+ [[= book_loan.loan_date ]] |
+ [[ }else:{ ]]
+ |
+ |
+ |
+ [[ } ]]
+
+[[ } ]]
+
+
+
+[[ if current_user.admin:{ ]]
+
+[[ } ]]
+
+
+
+
diff --git a/spyce-2.1/contrib/pyweboff/www/parent.spi b/spyce-2.1/contrib/pyweboff/www/parent.spi
new file mode 100755
index 0000000..d4efb6a
--- /dev/null
+++ b/spyce-2.1/contrib/pyweboff/www/parent.spi
@@ -0,0 +1,46 @@
+
+
+
+ [[= child.title ]]
+
+
+
+
+ [[= child.title ]]
+
+
+ [[= child._body ]]
+
+
+
+
diff --git a/spyce-2.1/contrib/pyweboff/www/user-new.spy b/spyce-2.1/contrib/pyweboff/www/user-new.spy
new file mode 100755
index 0000000..727f009
--- /dev/null
+++ b/spyce-2.1/contrib/pyweboff/www/user-new.spy
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spyce-2.1/contrib/pyweboff/www/util/form_calendar.gif b/spyce-2.1/contrib/pyweboff/www/util/form_calendar.gif
new file mode 100755
index 0000000000000000000000000000000000000000..2ae947fb21f30fd9a571f46d727b51ab2c572258
GIT binary patch
literal 152
zcmZ?wbhEHb6k_0K*v!GOb?equt5#K3R!*KgdB=_&X=!N;3=Chte*ORdKahb56o0ZX
zGB9v3=zyd^W-ze03Y_%RHnn)2yM$3efJa4hj#GQh!3aK$RZ_0G_rKT39J%*--&{VA
nKY!C&mK5!jk!p5P|5E8-wB9pQaMe;d+c^fuoVK|zFjxZsH8V0T
literal 0
HcmV?d00001
diff --git a/spyce-2.1/contrib/pyweboff/www/util/form_calendar.js b/spyce-2.1/contrib/pyweboff/www/util/form_calendar.js
new file mode 100755
index 0000000..394556a
--- /dev/null
+++ b/spyce-2.1/contrib/pyweboff/www/util/form_calendar.js
@@ -0,0 +1,1464 @@
+// ===================================================================
+// Author: Matt Kruse
+// WWW: http://www.mattkruse.com/
+//
+// NOTICE: You may use this code for any purpose, commercial or
+// private, without any further permission from the author. You may
+// remove this notice from your final code if you wish, however it is
+// appreciated by the author if at least my web site address is kept.
+//
+// You may *NOT* re-distribute this code in any way except through its
+// use. That means, you can include it in your product, or your web
+// site, or any other form where the code is actually being used. You
+// may not put the plain javascript up on your site for download or
+// include it in your javascript libraries for download.
+// If you wish to share this code with others, please just point them
+// to the URL instead.
+// Please DO NOT link directly to my .js files from your site. Copy
+// the files to your server and use them there. Thank you.
+//
+// (Redistribution in Spyce is with permission.)
+// ===================================================================
+
+
+/* SOURCE FILE: AnchorPosition.js */
+
+/*
+AnchorPosition.js
+Author: Matt Kruse
+Last modified: 5/24/04
+
+DESCRIPTION: These functions find the position of an tag in a document,
+so other elements can be positioned relative to it.
+
+COMPATABILITY: Netscape 4.x,6.x,Mozilla, IE 5.x,6.x on Windows. Some small
+positioning errors - usually with Window positioning - occur on the
+Macintosh platform.
+
+FUNCTIONS:
+getAnchorPosition(anchorname)
+ Returns an Object() having .x and .y properties of the pixel coordinates
+ of the upper-left corner of the anchor. Position is relative to the PAGE.
+
+getAnchorWindowPosition(anchorname)
+ Returns an Object() having .x and .y properties of the pixel coordinates
+ of the upper-left corner of the anchor, relative to the WHOLE SCREEN.
+
+NOTES:
+
+1) For popping up separate browser windows, use getAnchorWindowPosition.
+ Otherwise, use getAnchorPosition
+
+2) Your anchor tag MUST contain both NAME and ID attributes which are the
+ same. For example:
+
+
+3) There must be at least a space between for IE5.5 to see the
+ anchor tag correctly. Do not do with no space.
+*/
+
+// getAnchorPosition(anchorname)
+// This function returns an object having .x and .y properties which are the coordinates
+// of the named anchor, relative to the page.
+function getAnchorPosition(anchorname) {
+ // This function will return an Object with x and y properties
+ var useWindow=false;
+ var coordinates=new Object();
+ var x=0,y=0;
+ // Browser capability sniffing
+ var use_gebi=false, use_css=false, use_layers=false;
+ if (document.getElementById) { use_gebi=true; }
+ else if (document.all) { use_css=true; }
+ else if (document.layers) { use_layers=true; }
+ // Logic to find position
+ if (use_gebi && document.all) {
+ x=AnchorPosition_getPageOffsetLeft(document.all[anchorname]);
+ y=AnchorPosition_getPageOffsetTop(document.all[anchorname]);
+ }
+ else if (use_gebi) {
+ var o=document.getElementById(anchorname);
+ x=AnchorPosition_getPageOffsetLeft(o);
+ y=AnchorPosition_getPageOffsetTop(o);
+ }
+ else if (use_css) {
+ x=AnchorPosition_getPageOffsetLeft(document.all[anchorname]);
+ y=AnchorPosition_getPageOffsetTop(document.all[anchorname]);
+ }
+ else if (use_layers) {
+ var found=0;
+ for (var i=0; i9?"":"0")+x}
+
+// ------------------------------------------------------------------
+// isDate ( date_string, format_string )
+// Returns true if date string matches format of format string and
+// is a valid date. Else returns false.
+// It is recommended that you trim whitespace around the value before
+// passing it to this function, as whitespace is NOT ignored!
+// ------------------------------------------------------------------
+function isDate(val,format) {
+ var date=getDateFromFormat(val,format);
+ if (date==0) { return false; }
+ return true;
+ }
+
+// -------------------------------------------------------------------
+// compareDates(date1,date1format,date2,date2format)
+// Compare two date strings to see which is greater.
+// Returns:
+// 1 if date1 is greater than date2
+// 0 if date2 is greater than date1 of if they are the same
+// -1 if either of the dates is in an invalid format
+// -------------------------------------------------------------------
+function compareDates(date1,dateformat1,date2,dateformat2) {
+ var d1=getDateFromFormat(date1,dateformat1);
+ var d2=getDateFromFormat(date2,dateformat2);
+ if (d1==0 || d2==0) {
+ return -1;
+ }
+ else if (d1 > d2) {
+ return 1;
+ }
+ return 0;
+ }
+
+// ------------------------------------------------------------------
+// formatDate (date_object, format)
+// Returns a date in the output format specified.
+// The format string uses the same abbreviations as in getDateFromFormat()
+// ------------------------------------------------------------------
+function formatDate(date,format) {
+ format=format+"";
+ var result="";
+ var i_format=0;
+ var c="";
+ var token="";
+ var y=date.getYear()+"";
+ var M=date.getMonth()+1;
+ var d=date.getDate();
+ var E=date.getDay();
+ var H=date.getHours();
+ var m=date.getMinutes();
+ var s=date.getSeconds();
+ var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k;
+ // Convert real date parts into formatted versions
+ var value=new Object();
+ if (y.length < 4) {y=""+(y-0+1900);}
+ value["y"]=""+y;
+ value["yyyy"]=y;
+ value["yy"]=y.substring(2,4);
+ value["M"]=M;
+ value["MM"]=LZ(M);
+ value["MMM"]=MONTH_NAMES[M-1];
+ value["NNN"]=MONTH_NAMES[M+11];
+ value["d"]=d;
+ value["dd"]=LZ(d);
+ value["E"]=DAY_NAMES[E+7];
+ value["EE"]=DAY_NAMES[E];
+ value["H"]=H;
+ value["HH"]=LZ(H);
+ if (H==0){value["h"]=12;}
+ else if (H>12){value["h"]=H-12;}
+ else {value["h"]=H;}
+ value["hh"]=LZ(value["h"]);
+ if (H>11){value["K"]=H-12;} else {value["K"]=H;}
+ value["k"]=H+1;
+ value["KK"]=LZ(value["K"]);
+ value["kk"]=LZ(value["k"]);
+ if (H > 11) { value["a"]="PM"; }
+ else { value["a"]="AM"; }
+ value["m"]=m;
+ value["mm"]=LZ(m);
+ value["s"]=s;
+ value["ss"]=LZ(s);
+ while (i_format < format.length) {
+ c=format.charAt(i_format);
+ token="";
+ while ((format.charAt(i_format)==c) && (i_format < format.length)) {
+ token += format.charAt(i_format++);
+ }
+ if (value[token] != null) { result=result + value[token]; }
+ else { result=result + token; }
+ }
+ return result;
+ }
+
+// ------------------------------------------------------------------
+// Utility functions for parsing in getDateFromFormat()
+// ------------------------------------------------------------------
+function _isInteger(val) {
+ var digits="1234567890";
+ for (var i=0; i < val.length; i++) {
+ if (digits.indexOf(val.charAt(i))==-1) { return false; }
+ }
+ return true;
+ }
+function _getInt(str,i,minlength,maxlength) {
+ for (var x=maxlength; x>=minlength; x--) {
+ var token=str.substring(i,i+x);
+ if (token.length < minlength) { return null; }
+ if (_isInteger(token)) { return token; }
+ }
+ return null;
+ }
+
+// ------------------------------------------------------------------
+// getDateFromFormat( date_string , format_string )
+//
+// This function takes a date string and a format string. It matches
+// If the date string matches the format string, it returns the
+// getTime() of the date. If it does not match, it returns 0.
+// ------------------------------------------------------------------
+function getDateFromFormat(val,format) {
+ val=val+"";
+ format=format+"";
+ var i_val=0;
+ var i_format=0;
+ var c="";
+ var token="";
+ var token2="";
+ var x,y;
+ var now=new Date();
+ var year=now.getYear();
+ var month=now.getMonth()+1;
+ var date=1;
+ var hh=now.getHours();
+ var mm=now.getMinutes();
+ var ss=now.getSeconds();
+ var ampm="";
+
+ while (i_format < format.length) {
+ // Get next token from format string
+ c=format.charAt(i_format);
+ token="";
+ while ((format.charAt(i_format)==c) && (i_format < format.length)) {
+ token += format.charAt(i_format++);
+ }
+ // Extract contents of value based on format token
+ if (token=="yyyy" || token=="yy" || token=="y") {
+ if (token=="yyyy") { x=4;y=4; }
+ if (token=="yy") { x=2;y=2; }
+ if (token=="y") { x=2;y=4; }
+ year=_getInt(val,i_val,x,y);
+ if (year==null) { return 0; }
+ i_val += year.length;
+ if (year.length==2) {
+ if (year > 70) { year=1900+(year-0); }
+ else { year=2000+(year-0); }
+ }
+ }
+ else if (token=="MMM"||token=="NNN"){
+ month=0;
+ for (var i=0; i11)) {
+ month=i+1;
+ if (month>12) { month -= 12; }
+ i_val += month_name.length;
+ break;
+ }
+ }
+ }
+ if ((month < 1)||(month>12)){return 0;}
+ }
+ else if (token=="EE"||token=="E"){
+ for (var i=0; i12)){return 0;}
+ i_val+=month.length;}
+ else if (token=="dd"||token=="d") {
+ date=_getInt(val,i_val,token.length,2);
+ if(date==null||(date<1)||(date>31)){return 0;}
+ i_val+=date.length;}
+ else if (token=="hh"||token=="h") {
+ hh=_getInt(val,i_val,token.length,2);
+ if(hh==null||(hh<1)||(hh>12)){return 0;}
+ i_val+=hh.length;}
+ else if (token=="HH"||token=="H") {
+ hh=_getInt(val,i_val,token.length,2);
+ if(hh==null||(hh<0)||(hh>23)){return 0;}
+ i_val+=hh.length;}
+ else if (token=="KK"||token=="K") {
+ hh=_getInt(val,i_val,token.length,2);
+ if(hh==null||(hh<0)||(hh>11)){return 0;}
+ i_val+=hh.length;}
+ else if (token=="kk"||token=="k") {
+ hh=_getInt(val,i_val,token.length,2);
+ if(hh==null||(hh<1)||(hh>24)){return 0;}
+ i_val+=hh.length;hh--;}
+ else if (token=="mm"||token=="m") {
+ mm=_getInt(val,i_val,token.length,2);
+ if(mm==null||(mm<0)||(mm>59)){return 0;}
+ i_val+=mm.length;}
+ else if (token=="ss"||token=="s") {
+ ss=_getInt(val,i_val,token.length,2);
+ if(ss==null||(ss<0)||(ss>59)){return 0;}
+ i_val+=ss.length;}
+ else if (token=="a") {
+ if (val.substring(i_val,i_val+2).toLowerCase()=="am") {ampm="AM";}
+ else if (val.substring(i_val,i_val+2).toLowerCase()=="pm") {ampm="PM";}
+ else {return 0;}
+ i_val+=2;}
+ else {
+ if (val.substring(i_val,i_val+token.length)!=token) {return 0;}
+ else {i_val+=token.length;}
+ }
+ }
+ // If there are any trailing characters left in the value, it doesn't match
+ if (i_val != val.length) { return 0; }
+ // Is date valid for month?
+ if (month==2) {
+ // Check for leap year
+ if ( ( (year%4==0)&&(year%100 != 0) ) || (year%400==0) ) { // leap year
+ if (date > 29){ return 0; }
+ }
+ else { if (date > 28) { return 0; } }
+ }
+ if ((month==4)||(month==6)||(month==9)||(month==11)) {
+ if (date > 30) { return 0; }
+ }
+ // Correct hours value
+ if (hh<12 && ampm=="PM") { hh=hh-0+12; }
+ else if (hh>11 && ampm=="AM") { hh-=12; }
+ var newdate=new Date(year,month-1,date,hh,mm,ss);
+ return newdate.getTime();
+ }
+
+// ------------------------------------------------------------------
+// parseDate( date_string [, prefer_euro_format] )
+//
+// This function takes a date string and tries to match it to a
+// number of possible date formats to get the value. It will try to
+// match against the following international formats, in this order:
+// y-M-d MMM d, y MMM d,y y-MMM-d d-MMM-y MMM d
+// M/d/y M-d-y M.d.y MMM-d M/d M-d
+// d/M/y d-M-y d.M.y d-MMM d/M d-M
+// A second argument may be passed to instruct the method to search
+// for formats like d/M/y (european format) before M/d/y (American).
+// Returns a Date object or null if no patterns match.
+// ------------------------------------------------------------------
+function parseDate(val) {
+ var preferEuro=(arguments.length==2)?arguments[1]:false;
+ generalFormats=new Array('y-M-d','MMM d, y','MMM d,y','y-MMM-d','d-MMM-y','MMM d');
+ monthFirst=new Array('M/d/y','M-d-y','M.d.y','MMM-d','M/d','M-d');
+ dateFirst =new Array('d/M/y','d-M-y','d.M.y','d-MMM','d/M','d-M');
+ var checkList=new Array('generalFormats',preferEuro?'dateFirst':'monthFirst',preferEuro?'monthFirst':'dateFirst');
+ var d=null;
+ for (var i=0; i tags may cause errors.
+
+USAGE:
+// Create an object for a WINDOW popup
+var win = new PopupWindow();
+
+// Create an object for a DIV window using the DIV named 'mydiv'
+var win = new PopupWindow('mydiv');
+
+// Set the window to automatically hide itself when the user clicks
+// anywhere else on the page except the popup
+win.autoHide();
+
+// Show the window relative to the anchor name passed in
+win.showPopup(anchorname);
+
+// Hide the popup
+win.hidePopup();
+
+// Set the size of the popup window (only applies to WINDOW popups
+win.setSize(width,height);
+
+// Populate the contents of the popup window that will be shown. If you
+// change the contents while it is displayed, you will need to refresh()
+win.populate(string);
+
+// set the URL of the window, rather than populating its contents
+// manually
+win.setUrl("http://www.site.com/");
+
+// Refresh the contents of the popup
+win.refresh();
+
+// Specify how many pixels to the right of the anchor the popup will appear
+win.offsetX = 50;
+
+// Specify how many pixels below the anchor the popup will appear
+win.offsetY = 100;
+
+NOTES:
+1) Requires the functions in AnchorPosition.js
+
+2) Your anchor tag MUST contain both NAME and ID attributes which are the
+ same. For example:
+
+
+3) There must be at least a space between for IE5.5 to see the
+ anchor tag correctly. Do not do with no space.
+
+4) When a PopupWindow object is created, a handler for 'onmouseup' is
+ attached to any event handler you may have already defined. Do NOT define
+ an event handler for 'onmouseup' after you define a PopupWindow object or
+ the autoHide() will not work correctly.
+*/
+
+// Set the position of the popup window based on the anchor
+function PopupWindow_getXYPosition(anchorname) {
+ var coordinates;
+ if (this.type == "WINDOW") {
+ coordinates = getAnchorWindowPosition(anchorname);
+ }
+ else {
+ coordinates = getAnchorPosition(anchorname);
+ }
+ this.x = coordinates.x;
+ this.y = coordinates.y;
+ }
+// Set width/height of DIV/popup window
+function PopupWindow_setSize(width,height) {
+ this.width = width;
+ this.height = height;
+ }
+// Fill the window with contents
+function PopupWindow_populate(contents) {
+ this.contents = contents;
+ this.populated = false;
+ }
+// Set the URL to go to
+function PopupWindow_setUrl(url) {
+ this.url = url;
+ }
+// Set the window popup properties
+function PopupWindow_setWindowProperties(props) {
+ this.windowProperties = props;
+ }
+// Refresh the displayed contents of the popup
+function PopupWindow_refresh() {
+ if (this.divName != null) {
+ // refresh the DIV object
+ if (this.use_gebi) {
+ document.getElementById(this.divName).innerHTML = this.contents;
+ }
+ else if (this.use_css) {
+ document.all[this.divName].innerHTML = this.contents;
+ }
+ else if (this.use_layers) {
+ var d = document.layers[this.divName];
+ d.document.open();
+ d.document.writeln(this.contents);
+ d.document.close();
+ }
+ }
+ else {
+ if (this.popupWindow != null && !this.popupWindow.closed) {
+ if (this.url!="") {
+ this.popupWindow.location.href=this.url;
+ }
+ else {
+ this.popupWindow.document.open();
+ this.popupWindow.document.writeln(this.contents);
+ this.popupWindow.document.close();
+ }
+ this.popupWindow.focus();
+ }
+ }
+ }
+// Position and show the popup, relative to an anchor object
+function PopupWindow_showPopup(anchorname) {
+ this.getXYPosition(anchorname);
+ this.x += this.offsetX;
+ this.y += this.offsetY;
+ if (!this.populated && (this.contents != "")) {
+ this.populated = true;
+ this.refresh();
+ }
+ if (this.divName != null) {
+ // Show the DIV object
+ if (this.use_gebi) {
+ document.getElementById(this.divName).style.left = this.x + "px";
+ document.getElementById(this.divName).style.top = this.y + "px";
+ document.getElementById(this.divName).style.visibility = "visible";
+ }
+ else if (this.use_css) {
+ document.all[this.divName].style.left = this.x;
+ document.all[this.divName].style.top = this.y;
+ document.all[this.divName].style.visibility = "visible";
+ }
+ else if (this.use_layers) {
+ document.layers[this.divName].left = this.x;
+ document.layers[this.divName].top = this.y;
+ document.layers[this.divName].visibility = "visible";
+ }
+ }
+ else {
+ if (this.popupWindow == null || this.popupWindow.closed) {
+ // If the popup window will go off-screen, move it so it doesn't
+ if (this.x<0) { this.x=0; }
+ if (this.y<0) { this.y=0; }
+ if (screen && screen.availHeight) {
+ if ((this.y + this.height) > screen.availHeight) {
+ this.y = screen.availHeight - this.height;
+ }
+ }
+ if (screen && screen.availWidth) {
+ if ((this.x + this.width) > screen.availWidth) {
+ this.x = screen.availWidth - this.width;
+ }
+ }
+ var avoidAboutBlank = window.opera || ( document.layers && !navigator.mimeTypes['*'] ) || navigator.vendor == 'KDE' || ( document.childNodes && !document.all && !navigator.taintEnabled );
+ this.popupWindow = window.open(avoidAboutBlank?"":"about:blank","window_"+anchorname,this.windowProperties+",width="+this.width+",height="+this.height+",screenX="+this.x+",left="+this.x+",screenY="+this.y+",top="+this.y+"");
+ }
+ this.refresh();
+ }
+ }
+// Hide the popup
+function PopupWindow_hidePopup() {
+ if (this.divName != null) {
+ if (this.use_gebi) {
+ document.getElementById(this.divName).style.visibility = "hidden";
+ }
+ else if (this.use_css) {
+ document.all[this.divName].style.visibility = "hidden";
+ }
+ else if (this.use_layers) {
+ document.layers[this.divName].visibility = "hidden";
+ }
+ }
+ else {
+ if (this.popupWindow && !this.popupWindow.closed) {
+ this.popupWindow.close();
+ this.popupWindow = null;
+ }
+ }
+ }
+// Pass an event and return whether or not it was the popup DIV that was clicked
+function PopupWindow_isClicked(e) {
+ if (this.divName != null) {
+ if (this.use_layers) {
+ var clickX = e.pageX;
+ var clickY = e.pageY;
+ var t = document.layers[this.divName];
+ if ((clickX > t.left) && (clickX < t.left+t.clip.width) && (clickY > t.top) && (clickY < t.top+t.clip.height)) {
+ return true;
+ }
+ else { return false; }
+ }
+ else if (document.all) { // Need to hard-code this to trap IE for error-handling
+ var t = window.event.srcElement;
+ while (t.parentElement != null) {
+ if (t.id==this.divName) {
+ return true;
+ }
+ t = t.parentElement;
+ }
+ return false;
+ }
+ else if (this.use_gebi && e) {
+ var t = e.originalTarget;
+ while (t.parentNode != null) {
+ if (t.id==this.divName) {
+ return true;
+ }
+ t = t.parentNode;
+ }
+ return false;
+ }
+ return false;
+ }
+ return false;
+ }
+
+// Check an onMouseDown event to see if we should hide
+function PopupWindow_hideIfNotClicked(e) {
+ if (this.autoHideEnabled && !this.isClicked(e)) {
+ this.hidePopup();
+ }
+ }
+// Call this to make the DIV disable automatically when mouse is clicked outside it
+function PopupWindow_autoHide() {
+ this.autoHideEnabled = true;
+ }
+// This global function checks all PopupWindow objects onmouseup to see if they should be hidden
+function PopupWindow_hidePopupWindows(e) {
+ for (var i=0; i0) {
+ this.type="DIV";
+ this.divName = arguments[0];
+ }
+ else {
+ this.type="WINDOW";
+ }
+ this.use_gebi = false;
+ this.use_css = false;
+ this.use_layers = false;
+ if (document.getElementById) { this.use_gebi = true; }
+ else if (document.all) { this.use_css = true; }
+ else if (document.layers) { this.use_layers = true; }
+ else { this.type = "WINDOW"; }
+ this.offsetX = 0;
+ this.offsetY = 0;
+ // Method mappings
+ this.getXYPosition = PopupWindow_getXYPosition;
+ this.populate = PopupWindow_populate;
+ this.setUrl = PopupWindow_setUrl;
+ this.setWindowProperties = PopupWindow_setWindowProperties;
+ this.refresh = PopupWindow_refresh;
+ this.showPopup = PopupWindow_showPopup;
+ this.hidePopup = PopupWindow_hidePopup;
+ this.setSize = PopupWindow_setSize;
+ this.isClicked = PopupWindow_isClicked;
+ this.autoHide = PopupWindow_autoHide;
+ this.hideIfNotClicked = PopupWindow_hideIfNotClicked;
+ }
+
+/* SOURCE FILE: CalendarPopup.js */
+
+// HISTORY
+// ------------------------------------------------------------------
+// Feb 7, 2005: Fixed a CSS styles to use px unit
+// March 29, 2004: Added check in select() method for the form field
+// being disabled. If it is, just return and don't do anything.
+// March 24, 2004: Fixed bug - when month name and abbreviations were
+// changed, date format still used original values.
+// January 26, 2004: Added support for drop-down month and year
+// navigation (Thanks to Chris Reid for the idea)
+// September 22, 2003: Fixed a minor problem in YEAR calendar with
+// CSS prefix.
+// August 19, 2003: Renamed the function to get styles, and made it
+// work correctly without an object reference
+// August 18, 2003: Changed showYearNavigation and
+// showYearNavigationInput to optionally take an argument of
+// true or false
+// July 31, 2003: Added text input option for year navigation.
+// Added a per-calendar CSS prefix option to optionally use
+// different styles for different calendars.
+// July 29, 2003: Fixed bug causing the Today link to be clickable
+// even though today falls in a disabled date range.
+// Changed formatting to use pure CSS, allowing greater control
+// over look-and-feel options.
+// June 11, 2003: Fixed bug causing the Today link to be unselectable
+// under certain cases when some days of week are disabled
+// March 14, 2003: Added ability to disable individual dates or date
+// ranges, display as light gray and strike-through
+// March 14, 2003: Removed dependency on graypixel.gif and instead
+/// use table border coloring
+// March 12, 2003: Modified showCalendar() function to allow optional
+// start-date parameter
+// March 11, 2003: Modified select() function to allow optional
+// start-date parameter
+/*
+DESCRIPTION: This object implements a popup calendar to allow the user to
+select a date, month, quarter, or year.
+
+COMPATABILITY: Works with Netscape 4.x, 6.x, IE 5.x on Windows. Some small
+positioning errors - usually with Window positioning - occur on the
+Macintosh platform.
+The calendar can be modified to work for any location in the world by
+changing which weekday is displayed as the first column, changing the month
+names, and changing the column headers for each day.
+
+USAGE:
+// Create a new CalendarPopup object of type WINDOW
+var cal = new CalendarPopup();
+
+// Create a new CalendarPopup object of type DIV using the DIV named 'mydiv'
+var cal = new CalendarPopup('mydiv');
+
+// Easy method to link the popup calendar with an input box.
+cal.select(inputObject, anchorname, dateFormat);
+// Same method, but passing a default date other than the field's current value
+cal.select(inputObject, anchorname, dateFormat, '01/02/2000');
+// This is an example call to the popup calendar from a link to populate an
+// input box. Note that to use this, date.js must also be included!!
+Select
+
+// Set the type of date select to be used. By default it is 'date'.
+cal.setDisplayType(type);
+
+// When a date, month, quarter, or year is clicked, a function is called and
+// passed the details. You must write this function, and tell the calendar
+// popup what the function name is.
+// Function to be called for 'date' select receives y, m, d
+cal.setReturnFunction(functionname);
+// Function to be called for 'month' select receives y, m
+cal.setReturnMonthFunction(functionname);
+// Function to be called for 'quarter' select receives y, q
+cal.setReturnQuarterFunction(functionname);
+// Function to be called for 'year' select receives y
+cal.setReturnYearFunction(functionname);
+
+// Show the calendar relative to a given anchor
+cal.showCalendar(anchorname);
+
+// Hide the calendar. The calendar is set to autoHide automatically
+cal.hideCalendar();
+
+// Set the month names to be used. Default are English month names
+cal.setMonthNames("January","February","March",...);
+
+// Set the month abbreviations to be used. Default are English month abbreviations
+cal.setMonthAbbreviations("Jan","Feb","Mar",...);
+
+// Show navigation for changing by the year, not just one month at a time
+cal.showYearNavigation();
+
+// Show month and year dropdowns, for quicker selection of month of dates
+cal.showNavigationDropdowns();
+
+// Set the text to be used above each day column. The days start with
+// sunday regardless of the value of WeekStartDay
+cal.setDayHeaders("S","M","T",...);
+
+// Set the day for the first column in the calendar grid. By default this
+// is Sunday (0) but it may be changed to fit the conventions of other
+// countries.
+cal.setWeekStartDay(1); // week is Monday - Sunday
+
+// Set the weekdays which should be disabled in the 'date' select popup. You can
+// then allow someone to only select week end dates, or Tuedays, for example
+cal.setDisabledWeekDays(0,1); // To disable selecting the 1st or 2nd days of the week
+
+// Selectively disable individual days or date ranges. Disabled days will not
+// be clickable, and show as strike-through text on current browsers.
+// Date format is any format recognized by parseDate() in date.js
+// Pass a single date to disable:
+cal.addDisabledDates("2003-01-01");
+// Pass null as the first parameter to mean "anything up to and including" the
+// passed date:
+cal.addDisabledDates(null, "01/02/03");
+// Pass null as the second parameter to mean "including the passed date and
+// anything after it:
+cal.addDisabledDates("Jan 01, 2003", null);
+// Pass two dates to disable all dates inbetween and including the two
+cal.addDisabledDates("January 01, 2003", "Dec 31, 2003");
+
+// When the 'year' select is displayed, set the number of years back from the
+// current year to start listing years. Default is 2.
+// This is also used for year drop-down, to decide how many years +/- to display
+cal.setYearSelectStartOffset(2);
+
+// Text for the word "Today" appearing on the calendar
+cal.setTodayText("Today");
+
+// The calendar uses CSS classes for formatting. If you want your calendar to
+// have unique styles, you can set the prefix that will be added to all the
+// classes in the output.
+// For example, normal output may have this:
+// Today
+// But if you set the prefix like this:
+cal.setCssPrefix("Test");
+// The output will then look like:
+// Today
+// And you can define that style somewhere in your page.
+
+// When using Year navigation, you can make the year be an input box, so
+// the user can manually change it and jump to any year
+cal.showYearNavigationInput();
+
+// Set the calendar offset to be different than the default. By default it
+// will appear just below and to the right of the anchorname. So if you have
+// a text box where the date will go and and anchor immediately after the
+// text box, the calendar will display immediately under the text box.
+cal.offsetX = 20;
+cal.offsetY = 20;
+
+NOTES:
+1) Requires the functions in AnchorPosition.js and PopupWindow.js
+
+2) Your anchor tag MUST contain both NAME and ID attributes which are the
+ same. For example:
+
+
+3) There must be at least a space between for IE5.5 to see the
+ anchor tag correctly. Do not do with no space.
+
+4) When a CalendarPopup object is created, a handler for 'onmouseup' is
+ attached to any event handler you may have already defined. Do NOT define
+ an event handler for 'onmouseup' after you define a CalendarPopup object
+ or the autoHide() will not work correctly.
+
+5) The calendar popup display uses style sheets to make it look nice.
+
+*/
+
+// CONSTRUCTOR for the CalendarPopup Object
+function CalendarPopup() {
+ var c;
+ if (arguments.length>0) {
+ c = new PopupWindow(arguments[0]);
+ }
+ else {
+ c = new PopupWindow();
+ c.setSize(150,175);
+ }
+ c.offsetX = -152;
+ c.offsetY = 25;
+ c.autoHide();
+ // Calendar-specific properties
+ c.monthNames = new Array("January","February","March","April","May","June","July","August","September","October","November","December");
+ c.monthAbbreviations = new Array("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
+ c.dayHeaders = new Array("S","M","T","W","T","F","S");
+ c.returnFunction = "CP_tmpReturnFunction";
+ c.returnMonthFunction = "CP_tmpReturnMonthFunction";
+ c.returnQuarterFunction = "CP_tmpReturnQuarterFunction";
+ c.returnYearFunction = "CP_tmpReturnYearFunction";
+ c.weekStartDay = 0;
+ c.isShowYearNavigation = false;
+ c.displayType = "date";
+ c.disabledWeekDays = new Object();
+ c.disabledDatesExpression = "";
+ c.yearSelectStartOffset = 2;
+ c.currentDate = null;
+ c.todayText="Today";
+ c.cssPrefix="";
+ c.isShowNavigationDropdowns=false;
+ c.isShowYearNavigationInput=false;
+ window.CP_calendarObject = null;
+ window.CP_targetInput = null;
+ window.CP_dateFormat = "MM/dd/yyyy";
+ // Method mappings
+ c.copyMonthNamesToWindow = CP_copyMonthNamesToWindow;
+ c.setReturnFunction = CP_setReturnFunction;
+ c.setReturnMonthFunction = CP_setReturnMonthFunction;
+ c.setReturnQuarterFunction = CP_setReturnQuarterFunction;
+ c.setReturnYearFunction = CP_setReturnYearFunction;
+ c.setMonthNames = CP_setMonthNames;
+ c.setMonthAbbreviations = CP_setMonthAbbreviations;
+ c.setDayHeaders = CP_setDayHeaders;
+ c.setWeekStartDay = CP_setWeekStartDay;
+ c.setDisplayType = CP_setDisplayType;
+ c.setDisabledWeekDays = CP_setDisabledWeekDays;
+ c.addDisabledDates = CP_addDisabledDates;
+ c.setYearSelectStartOffset = CP_setYearSelectStartOffset;
+ c.setTodayText = CP_setTodayText;
+ c.showYearNavigation = CP_showYearNavigation;
+ c.showCalendar = CP_showCalendar;
+ c.hideCalendar = CP_hideCalendar;
+ c.getStyles = getCalendarStyles;
+ c.refreshCalendar = CP_refreshCalendar;
+ c.getCalendar = CP_getCalendar;
+ c.select = CP_select;
+ c.setCssPrefix = CP_setCssPrefix;
+ c.showNavigationDropdowns = CP_showNavigationDropdowns;
+ c.showYearNavigationInput = CP_showYearNavigationInput;
+ c.copyMonthNamesToWindow();
+ // Return the object
+ return c;
+ }
+function CP_copyMonthNamesToWindow() {
+ // Copy these values over to the date.js
+ if (typeof(window.MONTH_NAMES)!="undefined" && window.MONTH_NAMES!=null) {
+ window.MONTH_NAMES = new Array();
+ for (var i=0; i\n";
+ result += "."+p+"cpYearNavigation,."+p+"cpMonthNavigation { background-color:#C0C0C0; text-align:center; vertical-align:center; text-decoration:none; color:#000000; font-weight:bold; }\n";
+ result += "."+p+"cpDayColumnHeader, ."+p+"cpYearNavigation,."+p+"cpMonthNavigation,."+p+"cpCurrentMonthDate,."+p+"cpCurrentMonthDateDisabled,."+p+"cpOtherMonthDate,."+p+"cpOtherMonthDateDisabled,."+p+"cpCurrentDate,."+p+"cpCurrentDateDisabled,."+p+"cpTodayText,."+p+"cpTodayTextDisabled,."+p+"cpText { font-family:arial; font-size:8pt; }\n";
+ result += "TD."+p+"cpDayColumnHeader { text-align:right; border:solid thin #C0C0C0;border-width:0px 0px 1px 0px; }\n";
+ result += "."+p+"cpCurrentMonthDate, ."+p+"cpOtherMonthDate, ."+p+"cpCurrentDate { text-align:right; text-decoration:none; }\n";
+ result += "."+p+"cpCurrentMonthDateDisabled, ."+p+"cpOtherMonthDateDisabled, ."+p+"cpCurrentDateDisabled { color:#D0D0D0; text-align:right; text-decoration:line-through; }\n";
+ result += "."+p+"cpCurrentMonthDate, .cpCurrentDate { color:#000000; }\n";
+ result += "."+p+"cpOtherMonthDate { color:#808080; }\n";
+ result += "TD."+p+"cpCurrentDate { color:white; background-color: #C0C0C0; border-width:1px; border:solid thin #800000; }\n";
+ result += "TD."+p+"cpCurrentDateDisabled { border-width:1px; border:solid thin #FFAAAA; }\n";
+ result += "TD."+p+"cpTodayText, TD."+p+"cpTodayTextDisabled { border:solid thin #C0C0C0; border-width:1px 0px 0px 0px;}\n";
+ result += "A."+p+"cpTodayText, SPAN."+p+"cpTodayTextDisabled { height:20px; }\n";
+ result += "A."+p+"cpTodayText { color:black; }\n";
+ result += "."+p+"cpTodayTextDisabled { color:#D0D0D0; }\n";
+ result += "."+p+"cpBorder { border:solid thin #808080; }\n";
+ result += "\n";
+ return result;
+ }
+
+// Return a string containing all the calendar code to be displayed
+function CP_getCalendar() {
+ var now = new Date();
+ // Reference to window
+ if (this.type == "WINDOW") { var windowref = "window.opener."; }
+ else { var windowref = ""; }
+ var result = "";
+ // If POPUP, write entire HTML document
+ if (this.type == "WINDOW") {
+ result += "Calendar"+this.getStyles()+"\n";
+ result += '\n';
+ }
+ else {
+ result += '\n';
+ result += '\n';
+ result += '\n';
+ }
+ // Code for DATE display (default)
+ // -------------------------------
+ if (this.displayType=="date" || this.displayType=="week-end") {
+ if (this.currentDate==null) { this.currentDate = now; }
+ if (arguments.length > 0) { var month = arguments[0]; }
+ else { var month = this.currentDate.getMonth()+1; }
+ if (arguments.length > 1 && arguments[1]>0 && arguments[1]-0==arguments[1]) { var year = arguments[1]; }
+ else { var year = this.currentDate.getFullYear(); }
+ var daysinmonth= new Array(0,31,28,31,30,31,30,31,31,30,31,30,31);
+ if ( ( (year%4 == 0)&&(year%100 != 0) ) || (year%400 == 0) ) {
+ daysinmonth[2] = 29;
+ }
+ var current_month = new Date(year,month-1,1);
+ var display_year = year;
+ var display_month = month;
+ var display_date = 1;
+ var weekday= current_month.getDay();
+ var offset = 0;
+
+ offset = (weekday >= this.weekStartDay) ? weekday-this.weekStartDay : 7-this.weekStartDay+weekday ;
+ if (offset > 0) {
+ display_month--;
+ if (display_month < 1) { display_month = 12; display_year--; }
+ display_date = daysinmonth[display_month]-offset+1;
+ }
+ var next_month = month+1;
+ var next_month_year = year;
+ if (next_month > 12) { next_month=1; next_month_year++; }
+ var last_month = month-1;
+ var last_month_year = year;
+ if (last_month < 1) { last_month=12; last_month_year--; }
+ var date_class;
+ if (this.type!="WINDOW") {
+ result += "\n';
+ result += '\n';
+ result += '\n';
+ for (var j=0; j<7; j++) {
+
+ result += ''+this.dayHeaders[(this.weekStartDay+j)%7]+' | \n';
+ }
+ result += ' \n';
+ for (var row=1; row<=6; row++) {
+ result += '\n';
+ for (var col=1; col<=7; col++) {
+ var disabled=false;
+ if (this.disabledDatesExpression!="") {
+ var ds=""+display_year+LZ(display_month)+LZ(display_date);
+ eval("disabled=("+this.disabledDatesExpression+")");
+ }
+ var dateClass = "";
+ if ((display_month == this.currentDate.getMonth()+1) && (display_date==this.currentDate.getDate()) && (display_year==this.currentDate.getFullYear())) {
+ dateClass = "cpCurrentDate";
+ }
+ else if (display_month == month) {
+ dateClass = "cpCurrentMonthDate";
+ }
+ else {
+ dateClass = "cpOtherMonthDate";
+ }
+ if (disabled || this.disabledWeekDays[col-1]) {
+ result += ' '+display_date+' | \n';
+ }
+ else {
+ var selected_date = display_date;
+ var selected_month = display_month;
+ var selected_year = display_year;
+ if (this.displayType=="week-end") {
+ var d = new Date(selected_year,selected_month-1,selected_date,0,0,0,0);
+ d.setDate(d.getDate() + (7-col));
+ selected_year = d.getYear();
+ if (selected_year < 1000) { selected_year += 1900; }
+ selected_month = d.getMonth()+1;
+ selected_date = d.getDate();
+ }
+ result += ' '+display_date+' | \n';
+ }
+ display_date++;
+ if (display_date > daysinmonth[display_month]) {
+ display_date=1;
+ display_month++;
+ }
+ if (display_month > 12) {
+ display_month=1;
+ display_year++;
+ }
+ }
+ result += ' ';
+ }
+ var current_weekday = now.getDay() - this.weekStartDay;
+ if (current_weekday < 0) {
+ current_weekday += 7;
+ }
+ result += '\n';
+ result += ' \n';
+ if (this.disabledDatesExpression!="") {
+ var ds=""+now.getFullYear()+LZ(now.getMonth()+1)+LZ(now.getDate());
+ eval("disabled=("+this.disabledDatesExpression+")");
+ }
+ if (disabled || this.disabledWeekDays[current_weekday+1]) {
+ result += ' '+this.todayText+'\n';
+ }
+ else {
+ result += ' '+this.todayText+'\n';
+ }
+ result += ' \n';
+ result += ' | |
\n';
+ }
+
+ // Code common for MONTH, QUARTER, YEAR
+ // ------------------------------------
+ if (this.displayType=="month" || this.displayType=="quarter" || this.displayType=="year") {
+ if (arguments.length > 0) { var year = arguments[0]; }
+ else {
+ if (this.displayType=="year") { var year = now.getFullYear()-this.yearSelectStartOffset; }
+ else { var year = now.getFullYear(); }
+ }
+ if (this.displayType!="year" && this.isShowYearNavigation) {
+ result += "";
+ result += '\n';
+ result += ' << | \n';
+ result += ' '+year+' | \n';
+ result += ' >> | \n';
+ result += '
\n';
+ }
+ }
+
+ // Code for MONTH display
+ // ----------------------
+ if (this.displayType=="month") {
+ // If POPUP, write entire HTML document
+ result += '\n';
+ for (var i=0; i<4; i++) {
+ result += '';
+ for (var j=0; j<3; j++) {
+ var monthindex = ((i*3)+j);
+ result += ''+this.monthAbbreviations[monthindex]+' | ';
+ }
+ result += '
';
+ }
+ result += '
\n';
+ }
+
+ // Code for QUARTER display
+ // ------------------------
+ if (this.displayType=="quarter") {
+ result += '
\n';
+ for (var i=0; i<2; i++) {
+ result += '';
+ for (var j=0; j<2; j++) {
+ var quarter = ((i*2)+j+1);
+ result += ' Q'+quarter+'
| ';
+ }
+ result += '
';
+ }
+ result += '
\n';
+ }
+
+ // Code for YEAR display
+ // ---------------------
+ if (this.displayType=="year") {
+ var yearColumnSize = 4;
+ result += "";
+ result += '\n';
+ result += ' << | \n';
+ result += ' >> | \n';
+ result += '
\n';
+ result += '\n';
+ for (var i=0; i'+currentyear+'';
+ }
+ result += '';
+ }
+ result += '
\n';
+ }
+ // Common
+ if (this.type == "WINDOW") {
+ result += "\n";
+ }
+ return result;
+ }
diff --git a/spyce-2.1/contrib/pyweboff/www/util/spyce2.png b/spyce-2.1/contrib/pyweboff/www/util/spyce2.png
new file mode 100755
index 0000000000000000000000000000000000000000..c3bd014e2bd24f9d6107b7c902834dc107b9ad48
GIT binary patch
literal 812
zcmV+{1JnG8P)Nkljkd6#lR}(2Yn8FnUC?2fQ`AGhk)NZ-~kQa)vA
zdlcWyi&JGm)9j#J)~n|rFH(~blSB#|GR*;be60Y0`?RWnklFwsUyfvY&fi&53_?tj
z>K;{}A9+hJpWmM(okY4576y({RT_Q{6#vc8Kxb9P>@R8V*2NZpc{URmRK#>FkKaa5
zJ^(1TC4dJZBTvh*z<}MD>};2ugt>)de2rV
z*4{b`T=T_ik|qz^7EwPLwshw+yNH#fm}fH=06a@kn{O-9toGh%F2D-Pk{79}1)6$-
zEU+^~aiQ1mj){%7owy0(NSq9tTUD+?8Gt>ZQqOgf7M;=Xo=o`I*xJfE-hu%&DW)h)M`*-yH@a
z{lSitU#KX6#Q=VBXw^m4GI)Khif(xyQ7xmf1}`)^(E-4x&R;0nEzxcd2vjYDkk^7<
zIzzL+Z|uQqI1~&1M3*GDE_l11;~OuKZMt|w%kEuC(kK4Qo;UiWf0`izA}@Sy?~fjV
qh_}b(JO6;_;k`{>q)jzBx%~l^>}ka#_xQsA0000")
+ (spyce2
+ :submode python-mode
+ :front "\\[\\["
+ :back "\\]\\]")
+ )
+)
+(mmm-add-mode-ext-class nil "\\.spi$" 'spyce1)
+(mmm-add-mode-ext-class nil "\\.spy$" 'spyce1)
+(mmm-add-mode-ext-class nil "\\.spi$" 'spyce2)
+(mmm-add-mode-ext-class nil "\\.spy$" 'spyce2)
+
+(mmm-add-mode-ext-class nil "\\.spi$" 'html-js)
+(mmm-add-mode-ext-class nil "\\.spy$" 'html-js)
diff --git a/spyce-2.1/contrib/spyce.vim b/spyce-2.1/contrib/spyce.vim
new file mode 100755
index 0000000..b14a749
--- /dev/null
+++ b/spyce-2.1/contrib/spyce.vim
@@ -0,0 +1,110 @@
+" Vim syntax file
+" Language: SPYCE
+" Maintainer: Rimon Barr
+" URL: http://spyce.sourceforge.net
+" Last Change: 2003 Jul 13
+
+" For version 5.x: Clear all syntax items
+" For version 6.x: Quit when a syntax file was already loaded
+if version < 600
+ syntax clear
+elseif exists("b:current_syntax")
+ finish
+endif
+
+" we define it here so that included files can test for it
+if !exists("main_syntax")
+ let main_syntax='spyce'
+endif
+
+" Read the HTML syntax to start with
+let b:did_indent = 1 " don't perform HTML indentation!
+let html_no_rendering = 1 " do not render ,, etc...
+if version < 600
+ so :p:h/html.vim
+else
+ runtime! syntax/html.vim
+ unlet b:current_syntax
+endif
+
+" include python
+syn include @Python :p:h/python.vim
+syn include @Html :p:h/html.vim
+
+" spyce definitions
+syn keyword spyceDirectiveKeyword include compact module import contained
+syn keyword spyceDirectiveArg name names file as contained
+syn region spyceDirectiveString start=+"+ end=+"+ contained
+syn match spyceDirectiveValue "=[\t ]*[^'", \t>][^, \t>]*"hs=s+1 contained
+
+syn match spyceBeginErrorS ,\[\[,
+syn match spyceBeginErrorA ,<%,
+syn cluster spyceBeginError contains=spyceBeginErrorS,spyceBeginErrorA
+syn match spyceEndErrorS ,\]\],
+syn match spyceEndErrorA ,%>,
+syn cluster spyceEndError contains=spyceEndErrorS,spyceEndErrorA
+
+syn match spyceEscBeginS ,\\\[\[,
+syn match spyceEscBeginA ,\\<%,
+syn cluster spyceEscBegin contains=spyceEscBeginS,spyceEscBeginA
+syn match spyceEscEndS ,\\\]\],
+syn match spyceEscEndA ,\\%>,
+syn cluster spyceEscEnd contains=spyceEscEndS,spyceEscEndA
+syn match spyceEscEndCommentS ,--\\\]\],
+syn match spyceEscEndCommentA ,--\\%>,
+syn cluster spyceEscEndComment contains=spyceEscEndCommentS,spyceEscEndCommentA
+
+syn region spyceStmtS matchgroup=spyceStmtDelim start=,\[\[, end=,\]\], contains=@Python,spyceLambdaS,spyceLambdaA,spyceBeginError keepend
+syn region spyceStmtA matchgroup=spyceStmtDelim start=,<%, end=,%>, contains=@Python,spyceLambdaS,spyceLambdaA,spyceBeginError keepend
+syn region spyceChunkS matchgroup=spyceChunkDelim start=,\[\[\\, end=,\]\], contains=@Python,spyceLambdaS,spyceLambdaA,spyceBeginError keepend
+syn region spyceChunkA matchgroup=spyceChunkDelim start=,<%\\, end=,%>, contains=@Python,spyceLambdaS,spyceLambdaA,spyceBeginError keepend
+syn region spyceEvalS matchgroup=spyceEvalDelim start=,\[\[=, end=,\]\], contains=@Python,spyceLambdaS,spyceLambdaA,spyceBeginError keepend
+syn region spyceEvalA matchgroup=spyceEvalDelim start=,<%=, end=,%>, contains=@Python,spyceLambdaS,spyceLambdaA,spyceBeginError keepend
+syn region spyceDirectiveS matchgroup=spyceDelim start=,\[\[\., end=,\]\], contains=spyceBeginError,spyceDirectiveKeyword,spyceDirectiveArg,spyceDirectiveValue,spyceDirectiveString keepend
+syn region spyceDirectiveA matchgroup=spyceDelim start=,<%@, end=,%>, contains=spyceBeginError,spyceDirectiveKeyword,spyceDirectiveArg,spyceDirectiveValue,spyceDirectiveString keepend
+syn region spyceCommentS matchgroup=spyceCommentDelim start=,\[\[--, end=,--\]\],
+syn region spyceCommentA matchgroup=spyceCommentDelim start=,<%--, end=,--%>,
+syn region spyceLambdaS matchgroup=spyceLambdaDelim start=,\[\[spy!\?, end=,\]\], contains=@Html,@spyce extend
+syn region spyceLambdaA matchgroup=spyceLambdaDelim start=,<%spy!\?, end=,%>, contains=@Html,@spyce extend
+
+syn cluster spyce contains=spyceStmtS,spyceStmtA,spyceChunkS,spyceChunkA,spyceEvalS,spyceEvalA,spyceCommentS,spyceCommentA,spyceDirectiveS,spyceDirectiveA
+
+syn cluster htmlPreproc contains=@spyce
+
+hi link spyceDirectiveKeyword Special
+hi link spyceDirectiveArg Type
+hi link spyceDirectiveString String
+hi link spyceDirectiveValue String
+
+hi link spyceDelim Special
+hi link spyceStmtDelim spyceDelim
+hi link spyceChunkDelim spyceDelim
+hi link spyceEvalDelim spyceDelim
+hi link spyceLambdaDelim spyceDelim
+hi link spyceCommentDelim Comment
+
+hi link spyceBeginErrorS Error
+hi link spyceBeginErrorA Error
+hi link spyceEndErrorS Error
+hi link spyceEndErrorA Error
+
+hi link spyceStmtS spyce
+hi link spyceStmtA spyce
+hi link spyceChunkS spyce
+hi link spyceChunkA spyce
+hi link spyceEvalS spyce
+hi link spyceEvalA spyce
+hi link spyceDirectiveS spyce
+hi link spyceDirectiveA spyce
+hi link spyceCommentS Comment
+hi link spyceCommentA Comment
+hi link spyceLambdaS Normal
+hi link spyceLambdaA Normal
+
+hi link spyce Statement
+
+let b:current_syntax = "spyce"
+if main_syntax == 'spyce'
+ unlet main_syntax
+endif
+
diff --git a/spyce-2.1/contrib/tags/flow.py b/spyce-2.1/contrib/tags/flow.py
new file mode 100755
index 0000000..05f5d88
--- /dev/null
+++ b/spyce-2.1/contrib/tags/flow.py
@@ -0,0 +1,117 @@
+from spyceTag import spyceTagLibrary, spyceTagPlus
+
+# use of these tags is deprecated; they will be removed in a future version of Spyce
+
+# These tags are modeled after some of the functionality that exists in Java's
+# JSTL. However, with Spyce,
+# it's simpler and cleaner to just embed python statements. The cumbersome
+# nature of using Java code directly inside JSP makes the JSTL make sense for
+# J2EE users; there's no real need for the same in Spyce, but these are
+# available for those who avoid Python in "view" code for religions reasons.
+
+# < print val=expression [encode=url|html] [default=expression] />
+# Outputs the value of the "val" expression. If there is an error
+# and a "default" is provided, the default will be evaluated instead. The
+# output may be encoded to be HTML- or URL-safe, depending on the
+# "encode" attribute
+
+# < let var=expression val=expression /> [exports *var]
+# Sets the variable "var" to the value "val".
+
+# < if test=expression>...
+# Evaluate "test" and conditionally process body of tag.
+
+# < for items=expression [var=expr (item)] [counter=expr]>...
+# [exports *var, *counter]
+# Iterate through "items" and process the body each time. The current
+# item can optionally be stored in variable named by "var", and the
+# current iteration number (starting at zero) can optionally be stored
+# in a variable named by "counter".
+
+class tag_print(spyceTagPlus):
+ name = 'print'
+ def syntax(self):
+ self.syntaxSingleOnly()
+ self.syntaxNonEmpty('val')
+ self.syntaxValidSet('encode', ['false', 'url', 'html'])
+ def begin(self, val, encode='html', default=None):
+ try: out = str(val)
+ except:
+ if default:
+ out = str(default)
+ else:
+ raise
+ if encode == 'html':
+ out = self.getModule('transform').html_encode(out)
+ elif encode == 'url':
+ out = self.getModule('transform').url_encode(out)
+ self.getOut().write(out)
+
+class tag_let(spyceTagPlus):
+ name = 'let'
+ exports = 1
+ def syntax(self):
+ self.syntaxSingleOnly()
+ self.syntaxNonEmpty('var')
+ def begin(self, var, val):
+ self.var = var
+ self.val = val
+ def export(self):
+ return {self.var: self.val}
+
+class tag_if(spyceTagPlus):
+ name = 'if'
+ conditional = 1
+ def syntax(self):
+ self.syntaxPairOnly()
+ def begin(self, test, var=None):
+ return test
+
+# rimtodo: add first, last boolean flag variables
+class tag_for(spyceTagPlus):
+ name = 'for'
+ conditional = 1
+ loops = 1
+ exports = 1
+ def syntax(self):
+ self.syntaxPairOnly()
+ self.syntaxNonEmpty('items', 'var', 'counter')
+ def body(self, _contents):
+ return self._iter()
+ def begin(self, items, var='item', counter=None):
+ self.remaining = items
+ try: self.remaining = list(self.remaining)
+ except TypeError:
+ raise 'items expression should result in sequence, %s is not iterable'%repr(self.remaining)
+ self.var = var
+ self.counter = counter
+ self.i = 0
+ return self._iter()
+ def export(self):
+ d = {}
+ if self.var:
+ try:
+ d[self.var] = self.element
+ except: pass
+ if self.counter:
+ d[self.counter] = self.i
+ return d
+
+ def _iter(self):
+ # get next
+ if not self.remaining: return 0
+ self.element, self.remaining = self.remaining[0], self.remaining[1:]
+ self.i = self.i + 1
+ return 1
+
+
+# rimtodo: catch
+# rimtodo: choose, when, otherwise
+
+class flow(spyceTagLibrary):
+ tags = [
+ tag_print,
+ tag_let,
+ tag_if,
+ tag_for,
+ ]
diff --git a/spyce-2.1/contrib/xitami/lrwplib.py b/spyce-2.1/contrib/xitami/lrwplib.py
new file mode 100755
index 0000000..0c36b12
--- /dev/null
+++ b/spyce-2.1/contrib/xitami/lrwplib.py
@@ -0,0 +1,273 @@
+#!python
+#------------------------------------------------------------------------
+# Copyright (c) 1997 by Total Control Software
+# All Rights Reserved
+#------------------------------------------------------------------------
+#
+# Module Name: lrwplib.py
+#
+# Description: Class LRWP handles the connection to the LRWP agent in
+# Xitami. This class can be used standalone or derived
+# from to override behavior.
+#
+# Creation Date: 11/11/97 8:36:21PM
+#
+# License: This is free software. You may use this software for any
+# purpose including modification/redistribution, so long as
+# this header remains intact and that you do not claim any
+# rights of ownership or authorship of this software. This
+# software has been tested, but no warranty is expressed or
+# implied.
+#
+#------------------------------------------------------------------------
+
+import sys, socket, string
+import os, cgi
+from cStringIO import StringIO
+
+
+__version__ = '1.0'
+
+LENGTHSIZE = 9
+LENGTHFMT = '%09d'
+
+#---------------------------------------------------------------------------
+# Exception objects
+
+ConnectError = 'lrwp.ConnectError'
+ConnectionClosed = 'lrwp.ConnectionClosed'
+SocketError = 'lrwp.SocketError'
+
+#---------------------------------------------------------------------------
+
+class Request:
+ '''
+ Encapsulates the request/response IO objects and CGI-Environment.
+ An instance of this class is returned
+ '''
+ def __init__(self, lrwp):
+ self.inp = lrwp.inp
+ self.out = lrwp.out
+ self.err = lrwp.out
+ self.env = lrwp.env
+ self.lrwp = lrwp
+
+ def finish(self):
+ self.lrwp.finish()
+
+ def getFieldStorage(self):
+ method = 'POST'
+ if self.env.has_key('REQUEST_METHOD'):
+ method = string.upper(self.env['REQUEST_METHOD'])
+ if method == 'GET':
+ return cgi.FieldStorage(environ=self.env, keep_blank_values=1)
+ else:
+ return cgi.FieldStorage(fp=self.inp, environ=self.env, keep_blank_values=1)
+
+
+#---------------------------------------------------------------------------
+
+class LRWP:
+ def __init__(self, name, host, port, vhost='', filter='', useStdio=0):
+ '''
+ Construct an LRWP object.
+ name The name or alias of this request handler. Requests
+ matching http://host/name will be directed to this
+ LRWP object.
+ host Hostname or IP address to connect to.
+ port Port number to connect on.
+ vhost If this handler is to only be available to a specific
+ virtual host, name it here.
+ filter A space separated list of file extenstions that should
+ be directed to this handler in filter mode. (Not yet
+ supported.)
+ '''
+ self.name = name
+ self.host = host
+ self.port = port
+ self.vhost = vhost
+ self.filter = filter
+ self.useStdio = useStdio
+ self.sock = None
+ self.env = None
+ self.inp = None
+ self.out = None
+
+ #----------------------------------------
+ def connect(self):
+ '''
+ Establishes the connection to the web server, using the parameters given
+ at construction.
+ '''
+ try:
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock.connect((self.host, self.port)) # Changed to two item tuple with Python 2.0
+ self.sock.send("%s\xFF%s\xFF%s" % (self.name, self.vhost, self.filter) )
+ buf = self.sock.recv(1024)
+ if buf != 'OK':
+ raise ConnectError, buf
+ except socket.error, val:
+ raise SocketError, val
+
+ #----------------------------------------
+ def acceptRequest(self):
+ '''
+ Wait for, and accept a new request from the Web Server. Reads the
+ name=value pairs that comprise the CGI environment, followed by the
+ post data, if any. Constructs and returns a Request object.
+ '''
+ if self.out:
+ self.finish()
+ try:
+ # get the length of the environment data
+ data = self.recvBlock(LENGTHSIZE)
+ if not data: # server closed down
+ raise ConnectionClosed
+ length = string.atoi(data)
+
+ # and then the environment data
+ data = self.recvBlock(length)
+ if not data: # server closed down
+ raise ConnectionClosed
+ data = string.split(data, '\000')
+ self.env = {}
+ for x in data:
+ x = string.split(x, '=')
+ if len(x) > 1:
+ self.env[x[0]] = string.join(x[1:], '=')
+
+ # now get the size of the POST data
+ data = self.recvBlock(LENGTHSIZE)
+ if not data: # server closed down
+ raise ConnectionClosed
+ length = string.atoi(data)
+
+ # and the POST data...
+ if length:
+ data = self.recvBlock(length)
+ if not data: # server closed down
+ raise ConnectionClosed
+ self.inp = StringIO(data)
+ else:
+ self.inp = StringIO()
+
+ self.out = StringIO()
+
+ # do the switcheroo on the sys IO objects, etc.
+ if self.useStdio:
+ self.saveStdio = sys.stdin, sys.stdout, sys.stderr, os.environ
+ sys.stdin, sys.stdout, sys.stderr, os.environ = \
+ self.inp, self.out, self.out, self.env
+
+ return Request(self)
+
+ except socket.error, val:
+ raise SocketError, val
+
+
+ #----------------------------------------
+ def recvBlock(self, size):
+ '''
+ Pull an exact number of bytes from the socket, taking into
+ account the possibility of multiple packets...
+ '''
+ numRead = 0
+ data = []
+ while numRead < size:
+ buf = self.sock.recv(size - numRead);
+ if not buf:
+ return ''
+ data.append(buf)
+ numRead = numRead + len(buf)
+
+ return string.join(data, '')
+
+ #----------------------------------------
+ def finish(self):
+ '''
+ Complete the request and send the output back to the webserver.
+ '''
+ doc = self.out.getvalue()
+ size = LENGTHFMT % (len(doc), )
+ try:
+ self.sock.send(size)
+ self.sock.send(doc)
+ except socket.error, val:
+ raise SocketError, val
+
+ if self.useStdio:
+ sys.stdin, sys.stdout, sys.stderr, os.environ = self.saveStdio
+
+ self.env = None
+ self.inp = None
+ self.out = None
+
+ #----------------------------------------
+ def close(self):
+ '''
+ Close the LRWP connection to the web server.
+ '''
+ self.sock.close()
+ self.sock = None
+ self.env = None
+ self.inp = None
+ self.out = None
+
+#---------------------------------------------------------------------------
+
+
+def _test():
+ import os, time
+
+ eol = '\r\n'
+ appname = 'testapp1'
+ vhost = ''
+ host = 'localhost'
+ port = 5081
+ if len(sys.argv) > 1:
+ appname = sys.argv[1]
+ if len(sys.argv) > 2:
+ host = sys.argv[2]
+ if len(sys.argv) > 3:
+ port = string.atoi(sys.argv[3])
+ if len(sys.argv) > 4:
+ vhost = sys.argv[4]
+
+ lrwp = LRWP(appname, host, port, vhost)
+ lrwp.connect()
+
+ count = 0
+ while count < 5: # exit after servicing 5 requests
+ req = lrwp.acceptRequest()
+
+ doc = ['LRWP TestApp ('+appname+')\n\n']
+ count = count + 1
+ doc.append('LRWP test app ('+appname+')
')
+ doc.append('request count = %d
' % (count, ))
+ if hasattr(os, 'getpid'):
+ doc.append('pid = %s
' % (os.getpid(), ))
+ doc.append('
post data: ' + req.inp.read() + '
')
+
+ doc.append('
')
+ keys = req.env.keys()
+ keys.sort()
+ for k in keys:
+ doc.append('%-20s : %s\n' % (k, req.env[k]))
+ doc.append('\n
\n')
+ doc.append('\n')
+
+
+ req.out.write('Content-type: text/html' + eol)
+ req.out.write(eol)
+ req.out.write(string.join(doc, ''))
+
+ req.finish()
+
+ lrwp.close()
+
+
+if __name__ == '__main__':
+ #import pdb
+ #pdb.run('_test()')
+ _test()
+
diff --git a/spyce-2.1/contrib/xitami/spyceLRWP.py b/spyce-2.1/contrib/xitami/spyceLRWP.py
new file mode 100755
index 0000000..0369fa3
--- /dev/null
+++ b/spyce-2.1/contrib/xitami/spyceLRWP.py
@@ -0,0 +1,213 @@
+##################################################
+# SPYCE - Python-based HTML Scripting
+# Copyright (c) 2002 Rimon Barr.
+#
+# Refer to spyce.py
+# CVS:
+##################################################
+
+# spyceLRWP.py by Jim Madsen
+# Adapted from the work of Rimon Barr & Robin Dunn
+# Some further modifications by:
+# Rimon Barr
+##################################################
+'''
+Instructions:
+
+Edit the 'Startup Parameters' section below for your configuration.
+Make sure Xitami is already running.
+Run spyceLRWP.py - status will display in console window.
+(You can rename spyceLRWP.py to the name of your app)
+
+The URL for your app will be: http://server/appname/filename
+Example: http://localhost/spyceLRWP/form.spy
+Base directory is the default you set for Xitami pages.
+
+To shutdown the connection cleanly, enter Ctrl-C...shutdown will
+occur on next request handled. (Send one yourself if necessary)
+
+If you are unfamiliar with LRWP, the Xitami Docs have a good section on it.
+
+Multiple instances of spyceLRWP can be run. Just start spyceLRWP.py multiple
+times. Xitami will pool the instances and use them if server load warrants it.
+'''
+##################################################
+
+# Startup Parameters
+
+LRWPappName = 'spyceLRWP'
+LRWPhost = 'localhost'
+LRWPport = 5081
+
+##################################################
+import os, sys, string
+import spyce, spyceUtil
+from lrwplib import LRWP
+
+__doc__ = '''Xitami LRWP-based Spyce entry point.'''
+
+##################################################
+
+# Request / Response handlers
+
+class spyceLRWPRequest(spyce.spyceRequest):
+
+ def __init__(self, input, env, filename):
+ spyce.spyceRequest.__init__(self)
+ self._in = input
+ self._env = env
+ if not self._env.has_key('QUERY_STRING'):
+ self._env['QUERY_STRING']=''
+ self._headers = {
+ 'Content-Length': self.env('CONTENT_LENGTH'),
+ 'Content-Type': self.env('CONTENT_TYPE'),
+ 'User-Agent': self.env('HTTP_USER_AGENT'),
+ 'Accept': self.env('HTTP_ACCEPT'),
+ 'Accept-Encoding': self.env('HTTP_ACCEPT_ENCODING'),
+ 'Accept-Language': self.env('HTTP_ACCEPT_LANGUAGE'),
+ 'Accept-Charset': self.env('HTTP_ACCEPT_CHARSET'),
+ 'Cookie': self.env('HTTP_COOKIE'),
+ 'Referer': self.env('HTTP_REFERER'),
+ 'Host': self.env('HTTP_HOST'),
+ 'Connection': self.env('HTTP_CONNECTION'),
+ 'Keep-Alive': self.env('HTTP_KEEP_ALIVE'),
+ }
+ def env(self, name=None):
+ return spyceUtil.extractValue(self._env, name)
+ def getHeader(self, type=None):
+ return spyceUtil.extractValue(self._headers, type)
+ def getServerID(self):
+ return os.getpid()
+
+class spyceLRWPResponse(spyce.spyceResponse):
+
+ def __init__(self, out, err):
+ spyce.spyceResponse.__init__(self)
+ self.origout = out
+ self.out = spyceUtil.BufferedOutput(out)
+ self.err = err
+ self.headers = []
+ self.headersSent = 0
+ self.CT = None
+ self.returncode = self.RETURN_OK
+ def write(self, s):
+ self.out.write(s)
+ def writeErr(self, s):
+ self.err.write(s)
+ def close(self):
+ self.flush()
+ self.out.close()
+ def clear(self):
+ self.out.clear()
+ def sendHeaders(self):
+ if not self.headersSent:
+ resultText = spyceUtil.extractValue(self.RETURN_CODE, self.returncode)
+ self.origout.write('Status: %3d "%s"\n' % (self.returncode, resultText))
+ if not self.CT:
+ self.setContentType('text/html')
+ self.origout.write('Content-Type: %s\n' % self.CT)
+ for h in self.headers:
+ self.origout.write('%s: %s\n'%h)
+ self.origout.write('\n')
+ self.headersSent = 1
+ def clearHeaders(self):
+ if self.headersSent:
+ raise 'headers already sent'
+ self.headers = []
+ def setContentType(self, content_type):
+ if self.headersSent:
+ raise 'headers already sent'
+ self.CT = content_type
+ def setReturnCode(self, code):
+ if self.headersSent:
+ raise 'headers already sent'
+ self.returncode = code
+ def addHeader(self, type, data, replace=0):
+ if self.headersSent:
+ raise 'headers already sent'
+ if type=='Content-Type':
+ self.setContentType(data)
+ else:
+ if replace:
+ self.headers = filter(lambda (type, _), t2=type: type!=t2, self.headers)
+ self.headers.append((type, data))
+ def flush(self, stopFlag=0):
+ if stopFlag: return
+ self.sendHeaders()
+ self.out.flush()
+ def unbuffer(self):
+ self.sendHeaders()
+ self.out.unbuffer()
+
+def spyceMain(cgiscript, inp, out, err, env):
+
+ dir = os.path.dirname(cgiscript)
+ script = os.path.basename(cgiscript)
+ os.chdir(dir)
+
+ output = out
+ request = spyceLRWPRequest(inp, env, script)
+ response = spyceLRWPResponse(output, err)
+ result = spyce.spyceFileHandler(request, response, script)
+ response.close()
+
+ if output:
+ output.close()
+
+def doSpyce(inp, out, err, env):
+ path = env['PATH_TRANSLATED']
+ if os.path.isfile(path):
+ print 'Processing - ' + path
+ result = spyceMain(path, inp, out, err, env)
+ else:
+ result = 'pageError'
+ print 'Invalid Page Requested'
+ return result
+
+def main():
+ try:
+ lrwp = LRWP(LRWPappName, LRWPhost, LRWPport)
+ lrwp.connect()
+ print ('\r\n Connected to Xitami -- Listening for ' + LRWPappName + '\r\n')
+
+ except:
+ sys.exit('\r\n Could not make proper connection to Xitami')
+
+ while 1:
+ try:
+ req = lrwp.acceptRequest()
+
+ # Fix environment variables due to the way Xitami reports them under LRWP
+ req.env['SCRIPT_NAME'] = ('/' + LRWPappName)
+ req.env['REQUEST_URI'] = ('/' + LRWPappName + req.env['PATH_INFO'])
+
+ result = doSpyce(req.inp, spyceUtil.NoCloseOut(req.out), req.out, req.env)
+
+ # Error page to keep LRWP from hanging on bad urls
+ if result == 'pageError':
+ req.out.write('Content-type: text/html\r\n\r\n')
+ req.out.write('Page Error')
+ req.out.write('\n')
+ req.out.write('')
+ req.out.write('404 Error
')
+ req.out.write('')
+ req.out.write('
Page Not Available On This Server
')
+ req.out.write('\n\n')
+
+ req.finish()
+
+ # Capture Ctrl-C...shutdown will occur on next request handled
+ except KeyboardInterrupt:
+ print '\r\n Closing connection to Xitami \r\n'
+ lrwp.close()
+ sys.exit(' Clean Exit')
+
+ except:
+ print 'Unknown Error handling requests'
+
+
+if __name__=='__main__':
+ if sys.platform == "win32":
+ import os, msvcrt
+ msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
+ main()
diff --git a/spyce-2.1/debugspyce.py b/spyce-2.1/debugspyce.py
new file mode 100755
index 0000000..a318a82
--- /dev/null
+++ b/spyce-2.1/debugspyce.py
@@ -0,0 +1,35 @@
+# quick-and-dirty spyce debugging harness
+
+import spyceconf
+import sys
+import spyce
+
+import taglib as t
+
+class response_stub:
+ def writeStatic(self, s):
+ print s
+ def writeExpr(self, s):
+ print s
+
+response = response_stub()
+
+class wrapper_stub:
+ def spyceTaglib(self, name, file=None, rel_file=None):
+ "Return Spyce taglib class"
+ return spyce.getServer(spyceconf).loadModule(name, file, rel_file)
+ def registerModuleCallback(self, callback):
+ pass
+ def getFilename(self):
+ return 'debugstub'
+ def getModules(self):
+ return {'response': response,
+ 'stdout': sys.stdout}
+
+taglib = t.taglib(wrapper_stub())
+taglib.start()
+
+# paste compiled Spyce here
+
+test = spyceImpl()
+test.spyceProcess()
diff --git a/spyce-2.1/fcgi.py b/spyce-2.1/fcgi.py
new file mode 100755
index 0000000..7aae895
--- /dev/null
+++ b/spyce-2.1/fcgi.py
@@ -0,0 +1,268 @@
+##################################################
+# SPYCE - Python-based HTML Scripting
+# Copyright (c) 2002 Rimon Barr.
+#
+# Refer to spyce.py
+# CVS: $Id: fcgi.py 915 2006-07-20 00:00:51Z ellisj $
+##################################################
+
+# Taken originally from: http://alldunn.com/python/fcgi.py
+# Edited a fair bit. -- RB
+
+__doc__ = 'Python Fast CGI implementation'
+
+import os, sys, string, socket, errno, cgi
+from cStringIO import StringIO
+import spyceUtil
+
+##################################################
+# Constants
+#
+
+# Protol constants: record types
+FCGI_BEGIN_REQUEST = 1
+FCGI_ABORT_REQUEST = 2
+FCGI_END_REQUEST = 3
+FCGI_PARAMS = 4
+FCGI_STDIN = 5
+FCGI_STDOUT = 6
+FCGI_STDERR = 7
+FCGI_DATA = 8
+FCGI_GET_VALUES = 9
+FCGI_GET_VALUES_RESULT = 10
+FCGI_UNKNOWN_TYPE = 11
+FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
+# Protocol constants: FCGI_BEGIN_REQUEST flag mask
+FCGI_KEEP_CONN = 1
+# Protocol constants: FCGI_BEGIN_REQUEST role
+FCGI_RESPONDER = 1
+FCGI_AUTHORIZER = 2
+FCGI_FILTER = 3
+# Protocol constants: FCGI_END_REQUEST protocolStatus
+FCGI_REQUEST_COMPLETE = 0 # ok
+FCGI_CANT_MPX_CONN = 1 # can not multiplex
+FCGI_OVERLOADED = 2 # too busy
+FCGI_UNKNOWN_ROLE = 3 # role unknown
+# Protocol constants: management record types
+FCGI_NULL_REQUEST_ID = 0
+
+# Protocol setting: maximum number of requests
+FCGI_MAX_REQS = 1
+FCGI_MAX_CONNS = 1
+# Protocol setting: can multiplex?
+FCGI_MPXS_CONNS = 0
+# Protocol setting: FastCGI protocol version
+FCGI_VERSION_1 = 1
+
+##################################################
+# Protocol
+#
+
+class record:
+ def __init__(self):
+ self.version = FCGI_VERSION_1
+ self.recType = FCGI_UNKNOWN_TYPE
+ self.reqId = FCGI_NULL_REQUEST_ID
+ self.content = ""
+ def readRecord(self, sock):
+ # read content
+ hdr = map(ord, self.readExact(sock, 8))
+ self.version = hdr[0]
+ self.recType = hdr[1]
+ self.reqId = (hdr[2]<<8)+hdr[3]
+ contentLength = (hdr[4]<<8)+hdr[5]
+ paddingLength = hdr[6]
+ self.content = self.readExact(sock, contentLength)
+ self.readExact(sock, paddingLength)
+ # parse
+ c = self.content
+ if self.recType == FCGI_BEGIN_REQUEST:
+ self.role = (ord(c[0])<<8) + ord(c[1])
+ self.flags = ord(c[2])
+ elif self.recType == FCGI_UNKNOWN_TYPE:
+ self.unknownType = ord(c[0])
+ elif self.recType == FCGI_GET_VALUES or self.recType == FCGI_PARAMS:
+ self.values={}
+ pos=0
+ while pos < len(c):
+ name, value, pos = self.decodePair(c, pos)
+ self.values[name] = value
+ elif self.recType == FCGI_END_REQUEST:
+ b = map(ord, c[0:5])
+ self.appStatus = (b[0]<<24) + (b[1]<<16) + (b[2]<<8) + b[3]
+ self.protocolStatus = b[4]
+ def writeRecord(self, sock):
+ content = self.content
+ if self.recType == FCGI_BEGIN_REQUEST:
+ content = chr(self.role>>8) + chr(self.role & 255) + chr(self.flags) + 5*'\000'
+ elif self.recType == FCGI_UNKNOWN_TYPE:
+ content = chr(self.unknownType) + 7*'\000'
+ elif self.recType==FCGI_GET_VALUES or self.recType==FCGI_PARAMS:
+ content = ""
+ for i in self.values.keys():
+ content = content + self.encodePair(i, self.values[i])
+ elif self.recType==FCGI_END_REQUEST:
+ v = self.appStatus
+ content = chr((v>>24)&255) + chr((v>>16)&255) + chr((v>>8)&255) + chr(v&255)
+ content = content + chr(self.protocolStatus) + 3*'\000'
+ cLen = len(content)
+ eLen = (cLen + 7) & (0xFFFF - 7) # align to an 8-byte boundary
+ padLen = eLen - cLen
+ hdr = [ self.version, self.recType, self.reqId >> 8,
+ self.reqId & 255, cLen >> 8, cLen & 255, padLen, 0]
+ hdr = string.joinfields(map(chr, hdr), '')
+ sock.send(hdr + content + padLen*'\000')
+ def readExact(self, sock, amount):
+ data = ''
+ while amount and len(data) < amount:
+ data = data + sock.recv(amount-len(data))
+ return data
+ def decodePair(self, s, pos):
+ nameLen=ord(s[pos]) ; pos=pos+1
+ if nameLen & 128:
+ b=map(ord, s[pos:pos+3]) ; pos=pos+3
+ nameLen=((nameLen&127)<<24) + (b[0]<<16) + (b[1]<<8) + b[2]
+ valueLen=ord(s[pos]) ; pos=pos+1
+ if valueLen & 128:
+ b=map(ord, s[pos:pos+3]) ; pos=pos+3
+ valueLen=((valueLen&127)<<24) + (b[0]<<16) + (b[1]<<8) + b[2]
+ name = s[pos:pos+nameLen] ; pos = pos + nameLen
+ value = s[pos:pos+valueLen] ; pos = pos + valueLen
+ return name, value, pos
+ def encodePair(self, name, value):
+ l=len(name)
+ if l<128: s=chr(l)
+ else: s=chr(128|(l>>24)&255)+chr((l>>16)&255)+chr((l>>8)&255)+chr(l&255)
+ l=len(value)
+ if l<128: s=s+chr(l)
+ else: s=s+chr(128|(l>>24)&255)+chr((l>>16)&255)+chr((l>>8)&255)+chr(l&255)
+ return s + name + value
+
+class FCGI:
+ def __init__(self, port=None):
+ # environment variables
+ try:
+ self.FCGI_PORT = int(os.environ['FCGI_PORT'])
+ except:
+ self.FCGI_PORT = None
+ if port: self.FCGI_PORT = port
+ self.FCGI_PORT = None
+ try:
+ self.FCGI_ALLOWED_ADDR = os.environ['FCGI_WEB_SERVER_ADDRS']
+ self.FCGI_ALLOWED_ADDR = map(string.strip, string.split(self.FCGI_ALLOWED_ADDR, ','))
+ except:
+ self.FCGI_ALLOWED_ADDR = None
+ self.firstCall = 1
+ self.clearState()
+ self.socket = None
+ self.createServerSocket()
+ def createServerSocket(self):
+ if self.FCGI_PORT:
+ s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.set_reuse_addr()
+ s.bind(('127.0.0.1', self.FCGI_PORT))
+ else:
+ try:
+ s=socket.fromfd(sys.stdin.fileno(), socket.AF_INET, socket.SOCK_STREAM)
+ s.getpeername()
+ except socket.error, (err, errmsg):
+ if err!=errno.ENOTCONN:
+ return
+ except:
+ return
+ self.socket = s
+ def accept(self):
+ if not self.socket: # plain CGI
+ if self.firstCall:
+ result = sys.stdin, spyceUtil.NoCloseOut(sys.stdout), sys.stderr, os.environ
+ else:
+ return 0
+ else: # FCGI
+ result = self.recv()
+ self.firstCall = 0
+ return result
+ def finish(self):
+ if self.firstCall or not self.socket: return
+ self.send()
+ def clearState(self):
+ self.reqID = 0
+ self.connection = None
+ self.environ = {}
+ self.stdin = StringIO()
+ self.stderr = StringIO()
+ self.stdout = StringIO()
+ self.data = StringIO()
+ def send(self):
+ self.stderr.seek(0,0)
+ self.stdout.seek(0,0)
+ self.sendStream(FCGI_STDERR, self.stderr.read())
+ self.sendStream(FCGI_STDOUT, self.stdout.read())
+ r=record()
+ r.recType=FCGI_END_REQUEST
+ r.reqId=self.reqID
+ r.appStatus=0
+ r.protocolStatus=FCGI_REQUEST_COMPLETE
+ r.writeRecord(self.connection)
+ self.connection.close()
+ self.clearState()
+ def sendStream(self, streamType, streamData):
+ if not streamData:
+ return
+ r=record()
+ r.recType = streamType
+ r.reqId = self.reqID
+ data = streamData
+ while data:
+ r.content, data = data[:8192], data[8192:]
+ r.writeRecord(self.connection)
+ r.content='' ; r.writeRecord(self.connection)
+ def recv(self):
+ self.connection, address = self.socket.accept()
+ # rimtodo: filter to serve only allowed addresses
+ # if good_addrs!=None and addr not in good_addrs:
+ # raise 'Connection from invalid server!'
+ remaining=1
+ while remaining:
+ r=record(); r.readRecord(self.connection)
+ if r.recType in [FCGI_GET_VALUES]: # management records
+ if r.recType == FCGI_GET_VALUES:
+ r.recType = FCGI_GET_VALUES_RESULT
+ v={}
+ vars={'FCGI_MAX_CONNS' : FCGI_MAX_CONNS,
+ 'FCGI_MAX_REQS' : FCGI_MAX_REQS,
+ 'FCGI_MPXS_CONNS': FCGI_MPXS_CONNS}
+ for i in r.values.keys():
+ if vars.has_key(i): v[i]=vars[i]
+ r.values=vars
+ r.writeRecord(self.connection)
+ elif r.reqId == 0: # management record of unknown type
+ r2 = record()
+ r2.recType = FCGI_UNKNOWN_TYPE ; r2.unknownType = r.recType
+ r2.writeRecord(self.connection)
+ continue
+ elif r.reqId != self.reqID and r.recType != FCGI_BEGIN_REQUEST:
+ continue # ignore inactive requests
+ elif r.recType == FCGI_BEGIN_REQUEST and self.reqID != 0:
+ continue # ignore BEGIN_REQUESTs in the middle of request
+ if r.recType == FCGI_BEGIN_REQUEST: # begin request
+ self.reqID = r.reqId
+ if r.role == FCGI_AUTHORIZER: remaining=1
+ elif r.role == FCGI_RESPONDER: remaining=2
+ elif r.role == FCGI_FILTER: remaining=3
+ elif r.recType == FCGI_PARAMS: # environment
+ if r.content == '': remaining=remaining-1
+ else:
+ for i in r.values.keys():
+ self.environ[i] = r.values[i]
+ elif r.recType == FCGI_STDIN: # stdin
+ if r.content == '': remaining=remaining-1
+ else: self.stdin.write(r.content)
+ elif r.recType == FCGI_DATA: # data
+ if r.content == '': remaining=remaining-1
+ else: self.data.write(r.content)
+ # end while
+ self.stdin.seek(0,0)
+ self.data.seek(0,0)
+ # return CGI environment
+ return self.stdin, spyceUtil.NoCloseOut(self.stdout), self.stderr, self.environ
+
diff --git a/spyce-2.1/installHelper.py b/spyce-2.1/installHelper.py
new file mode 100755
index 0000000..adaac5c
--- /dev/null
+++ b/spyce-2.1/installHelper.py
@@ -0,0 +1,181 @@
+##################################################
+# SPYCE - Python-based HTML Scripting
+# Copyright (c) 2002 Rimon Barr.
+#
+# Refer to spyce.py
+# CVS: $Id: installHelper.py 312 2002-12-02 00:56:49Z batripler $
+##################################################
+
+__doc__ = 'Spyce install helper script'
+
+import os, imp, sys, getopt, string, re, time
+
+CONF_BEGIN_MARK = '### BEGIN SPYCE CONFIG MARKER'
+CONF_END_MARK = '### END SPYCE CONFIG MARKER'
+HTTPD_LOCATIONS = [
+ '/etc/httpd/conf',
+ r'C:\Program Files\Apache Group\Apache2\conf',
+ r'C:\Program Files\Apache Group\Apache\conf',
+ '/etc',
+ '/']
+APACHE_EXE_LOCATIONS = [
+ r'C:\Program Files\Apache Group\Apache2\bin',
+ r'C:\Program Files\Apache Group\Apache2',
+ r'C:\Program Files\Apache Group\Apache\bin',
+ r'C:\Program Files\Apache Group\Apache',
+]
+SYS_LOCATIONS = [
+ r'C:\winnt\system32',
+]
+
+def endsWith(s, suffix):
+ suffixLen = len(suffix)
+ return s[-suffixLen:] == suffix
+
+def listDirFilter(dir, extension):
+ files = os.listdir(dir)
+ files = filter(lambda file: endsWith(file, extension), files)
+ return files
+
+def compilePythonDir(dir):
+ print '** Compiling Python files in: %s' % dir
+ for file in listDirFilter(dir, '.py'):
+ print 'Compiling: %s' % file
+ try:
+ p = os.path.join(dir, file)
+ f = None
+ try:
+ f = open(p, 'r')
+ imp.load_source(os.path.split(file)[1][:-3], p, f)
+ finally:
+ if f: f.close()
+ except: pass
+
+def compileSpyceDir(dir):
+ import spyceCmd
+ print '** Processing Spyce files in: %s' % dir
+ for file in listDirFilter(dir, '.spy'):
+ print 'Processing: %s' % file
+ sys.argv = ['', '-o', os.path.join(dir, file[:-4]+'.html'), os.path.join(dir, file)]
+ spyceCmd.spyceMain()
+
+def findLine(array, line):
+ line = string.strip(line)
+ for i in range(len(array)):
+ if re.search(line, string.strip(array[i])):
+ return i
+ return None
+
+def unconfig(s):
+ lines = string.split(s, '\n')
+ begin = findLine(lines, CONF_BEGIN_MARK)
+ end = findLine(lines, CONF_END_MARK)
+ if begin!=None and end!=None and end>begin:
+ del lines[begin:end+1]
+ s = string.join(lines, '\n')
+ return s
+
+def config(s, root):
+ append = readFile('spyceApache.conf')
+ root = re.sub(r'\\', '/', root)
+ append = string.replace(append, 'XXX', root)
+ append = string.split(append, '\n')
+ if os.name=='nt':
+ row = findLine(append, 'ScriptInterpreterSource')
+ append[row] = string.strip(re.sub('#', '', append[row]))
+ lines = [s] + [CONF_BEGIN_MARK] + append + [CONF_END_MARK]
+ s = string.join(lines, '\n')
+ return s
+
+def readFile(filename):
+ f = None
+ try:
+ f = open(filename, 'r')
+ return f.read()
+ finally:
+ if f: f.close()
+
+def writeFileBackup(filename, new):
+ old = readFile(filename)
+ backupname = filename + '.save'
+ f = None
+ try:
+ f = open(backupname, 'w')
+ f.write(old)
+ finally:
+ if f: f.close()
+ f = None
+ try:
+ f = open(filename, 'w')
+ f.write(new)
+ finally:
+ if f: f.close()
+
+def locateFile(file, locations):
+ def visit(arg, dirname, names, file=file):
+ path = os.path.join(dirname, file)
+ if os.path.exists(path):
+ arg.append(path)
+ if arg:
+ del names[:]
+ found = []
+ for path in locations:
+ os.path.walk(path, visit, found)
+ if found:
+ return found[0]
+
+def configHTTPD(spyceroot):
+ print '** Searching for httpd.conf...'
+ file = locateFile('httpd.conf', HTTPD_LOCATIONS)
+ if file:
+ print '** Modifying httpd.conf'
+ s = readFile(file)
+ s = unconfig(s)
+ s = config(s, spyceroot)
+ writeFileBackup(file, s)
+
+def unconfigHTTPD():
+ print '** Searching for httpd.conf...'
+ file = locateFile('httpd.conf', HTTPD_LOCATIONS)
+ if file:
+ print '** Modifying httpd.conf'
+ s = readFile(file)
+ s = unconfig(s)
+ writeFileBackup(file, s)
+
+def restartApache():
+ print '** Searching for apache.exe...'
+ file = locateFile('apache.exe', APACHE_EXE_LOCATIONS)
+ cmd = locateFile('cmd.exe', SYS_LOCATIONS)
+ if file and cmd:
+ print '** Restarting Apache'
+ os.spawnl(os.P_WAIT, cmd, '/c "%s" -k restart'%file)
+ return
+ print 'Could not find apache.exe'
+
+
+
+def main():
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], '',
+ ['py=', 'spy=', 'apache=', 'apacheUN',
+ 'apacheRestart']);
+ except getopt.error:
+ print "Syntax error"
+ return -1
+ for o, a in opts:
+ if o == "--py":
+ compilePythonDir(a); return 0
+ if o == "--spy":
+ compileSpyceDir(a); return 0
+ if o == "--apache":
+ configHTTPD(a); return 0
+ if o == "--apacheUN":
+ unconfigHTTPD(); return 0
+ if o == "--apacheRestart":
+ restartApache(); return 0
+ print "Syntax error"
+ return -1
+
+if __name__=='__main__':
+ sys.exit(main())
diff --git a/spyce-2.1/misc/addfirstline.sh b/spyce-2.1/misc/addfirstline.sh
new file mode 100755
index 0000000..6300b7f
--- /dev/null
+++ b/spyce-2.1/misc/addfirstline.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+file=$1
+line=$2
+
+cat >> addfirstline.tmp << EOF
+$line
+`cat $file`
+EOF
+
+mv addfirstline.tmp $file
diff --git a/spyce-2.1/misc/benchmark/hello.c b/spyce-2.1/misc/benchmark/hello.c
new file mode 100755
index 0000000..ad6e080
--- /dev/null
+++ b/spyce-2.1/misc/benchmark/hello.c
@@ -0,0 +1,24 @@
+#include
+
+int main()
+{
+ int i;
+ printf("Date: Sun, 26 May 2002 18:22:24 GMT\n");
+ printf("Server: Apache/1.3.22 (Unix) (Red-Hat/Linux) mod_python/2.7.6 Python/2.2\n");
+ printf("Last-Modified: Sun, 26 May 2002 18:19:49 GMT\n");
+ printf("ETag: \"84244-47-3cf12745\"\n");
+ printf("Accept-Ranges: bytes\n");
+ printf("Content-Length: 71\n");
+ printf("Connection: close\n");
+ printf("Content-Type: text/html\n");
+ printf("\n");
+ printf("\n");
+ printf("Hello world!\n");
+ for(i=0; i<10; i++)
+ {
+ printf(" %d ", i);
+ }
+ printf("\n");
+ return 0;
+}
+
diff --git a/spyce-2.1/misc/benchmark/hello.html b/spyce-2.1/misc/benchmark/hello.html
new file mode 100755
index 0000000..bc1d51e
--- /dev/null
+++ b/spyce-2.1/misc/benchmark/hello.html
@@ -0,0 +1,4 @@
+
+Hello world!
+0 1 2 3 4 5 6 7 8 9
+
diff --git a/spyce-2.1/misc/benchmark/hello.jsp b/spyce-2.1/misc/benchmark/hello.jsp
new file mode 100755
index 0000000..6dec3bb
--- /dev/null
+++ b/spyce-2.1/misc/benchmark/hello.jsp
@@ -0,0 +1,6 @@
+
+Hello world!
+<% for(int i=0; i<10; i++) { %>
+ <%=i%>
+<% } %>
+
diff --git a/spyce-2.1/misc/benchmark/hello.php b/spyce-2.1/misc/benchmark/hello.php
new file mode 100755
index 0000000..9fade22
--- /dev/null
+++ b/spyce-2.1/misc/benchmark/hello.php
@@ -0,0 +1,6 @@
+
+ Hello world!
+
+
+ } ?>
+
diff --git a/spyce-2.1/misc/benchmark/hello.py b/spyce-2.1/misc/benchmark/hello.py
new file mode 100755
index 0000000..f5a926e
--- /dev/null
+++ b/spyce-2.1/misc/benchmark/hello.py
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+print "Content-Type: text/html"
+print
+print ""
+print "Hello world! "
+for i in range(10):
+ print i,
+print ""
diff --git a/spyce-2.1/misc/benchmark/hellopy.cgi b/spyce-2.1/misc/benchmark/hellopy.cgi
new file mode 100755
index 0000000..9ec0108
--- /dev/null
+++ b/spyce-2.1/misc/benchmark/hellopy.cgi
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+print "Date: Sun, 26 May 2002 18:22:24 GMT"
+print "Server: Apache/1.3.22 (Unix) (Red-Hat/Linux) mod_python/2.7.6 Python/2.2"
+print "Last-Modified: Sun, 26 May 2002 18:19:49 GMT"
+print "ETag: \"84244-47-3cf12745\""
+print "Accept-Ranges: bytes"
+print "Content-Length: 71"
+print "Connection: close"
+print "Content-Type: text/html"
+print ""
+print ""
+print "Hello world! "
+for i in range(10):
+ print i,
+print ""
+
diff --git a/spyce-2.1/misc/one-check.bmp b/spyce-2.1/misc/one-check.bmp
new file mode 100755
index 0000000000000000000000000000000000000000..02e09b2739dd667ae4293ef78adaeff85f9ed39a
GIT binary patch
literal 360
zcmYk0y$u5)426ACx^z*7NO?1)&mNf|nCj?~4Uj8VVXMQny*FJ-cN*Z#Ljr1E)&F&m#|X1mod=yDl?Iq2Dg3Bq@{OUCk6l#ldwe4YUL^)E|})@@G+e7G0#-Iy?i
zul1TMyAdJ-9OPUNHzi6byb3%9?ouS5Ed4vuplKcr%Iz2U(sipowYi;2ujlpHp?9;P
Mv#D6a@fW(l4XL2h9RL6T
literal 0
HcmV?d00001
diff --git a/spyce-2.1/misc/pics/pilpel1-edit.jpg b/spyce-2.1/misc/pics/pilpel1-edit.jpg
new file mode 100755
index 0000000000000000000000000000000000000000..9c124425d893296e3f98491d05f35bb971ad2ab3
GIT binary patch
literal 6447
zcmb7|byO5ux4?&nfdvlfFaro9-Q7sn03+QX-5t^?2nfR^rBy&;kO77oQ0b8F5|9ol
zN%11_yx)7@U7zoL@2&OD`Dd>azqQxi=j^@DxtqUR1>DnA)ldcC;NSq%e|5m!Er7(|
z*Tu&Df#BUDKnXxhNJvCTKuknLL_$IgB%=nBk&=>uDXA!^8R(f98R((V2M_t!AF%Mi
zpimBRP96b45fKq4c1c+YAsIel5uv{zI3y$_WTa&Gfx!Dh%ur^b|989V0Dy>b05||V
z92NjB2nP>@bJqia{u;o=`%Ck`8$J;p0U<69@vm9=djK3fTzq^yd?G@8qQ6%BwHhBp
zK*=mfsGw^@#o|L0o%RMMq^MWdc}z`X>pQVV{BZJwPFPGowixq^0Qp7!i~lzr0Kmh?
z!6hIh`ZdW10^s7};Q#7)xc{L4;^X3h@PE+>s91jS31LFfYt(u^X>}9Fcb@@dc)wsE
zJP_a!pnU=aP*%nP-NRRw2mP1GM3=GK@`Y^!NDfI>W8Hois(~a7dy)D81@%f2)C#N^
z(tW(G3S1!bWKKS3%GK4GVU$zfaZP1n-(}tIE-tMlm+P%XLL^M|!0
z+*M>=V)ge$8Qb{}u($W^?AHr;Q=R>^;e5o{<%rn!0V5PJ*uC0-`gB40gow~K2+vX`
z*?l@{u{U{z|BM`7ys9JUz(Ac7$S!zKSy+WTQPTV}T*~jd+()g!3LGG+Ljg}vFfq>hXQsSfRxwp;lj*?guE`eQ&tX|@>E0jdr^t0`}_#~4)X
zLYbr~Ezc6e*qDZZ-{(2EH7w~;$bNCNSEUiN>=0`L+;`yc|MZ3KzFVdDp4yC&&Zj3%
zv!SEW#y`dyT?0iYo}44Iu2ShExz)TUDlZq@IQwMt23=*d`8r*FS`S#b=#$)a(8L?*
zLUVPeWWD9ugx6PGjLkGPJx=}?0e;%BWXp=vt%
zK#6*jMw8K*K@-a(v_o4mDT#DTR;$Y{mif{m=#j$AYGGG)6p{+In7iuu)o;eaLN+91
zpbn3uh(|6@v(oP8S_;<7y|3%KCSXh>`bm7CHW|(1mTczg9NKir{FvsPKsF8z!6Z9S
z#$_B?!^;u(+}@g%2}dK#hmtm5u2l?dwEIuu4C3g=bO%hsk=@<4hoj-k{zkw*(C<
zTam+LF+m-NQ5Zu?+>vLX;kyFSgkJ90Hz)T+$&+Ik{#*{cZv|bF%J0SsNh}|In;Pdm
zN_Qj4Uw$}CCH>7Yb5gtaT~?HR413G=jjHY$*VkoV3p|zBsa36fvP=Z`c|~T^*s$CH
zG-^|Jzj^^~5ZFsO!%(MIs;o!(_htI+whPzAJSA|
z2%gBe?2R`Mn9{g`Yxdnird)E|>#bXs22YNy>NZ7V%T=<7#}!L(CkYs1#}ai&Ft
z2EbU#w5+^H(8S|yt&-u~DwXD5Y)B1pg*&AN{v_>k7yAn5Ht?|GpmDUc!rk+auIrSm
zQQXzg+3K~`A~RWKw+jo#m3klxvk417>#UgSv4*97-C@j-EPr{#OKTi#t3`m4C1zA`
zVE(66Uu573acQ0rrs$o$WJ*-fRokuAh5xB_ZZz+M7zO$mx?LSeuWg?Iy%cs!NTLtI
z$DuE~f)9eAx|Yd!sWrx6b28
zMw!?^UG44x9Gq1PTy(?vym*Cj^wu|2S7X`mo8+TQlv|>;U5Y$6BDJ>bD=u#{gQhkKGHf@EJ5dFETs3&WoMzT
z>|MLi4R14*pSMX>$0!4$Jk-{`fJ1~W0N^UcDq>nt&N9ABVhBfMUM;YRfH^A`5cOv@eP#L!{2
zEwO(pCf?&q7ueXkHVhtVj$PXQ!1oeiMLF~{JZ(eVDPU@1t>K{LNjAr(U%c%)&XRRN
zRZUskEu^x4a-K*WwP>T;2DB#Be(2Id_YwV8TQoLNEu|h&T~Odh;!u90MRU!oi-?7q
z6@KC6woW&?skmZXU2=WTWZ(F9+*X7yLsc@puPbCpA~n|NTl{XyQ<7P|w%Ljk7(v&n
zh1q`mcMp+<5B8kVU1;sj9vv*jG#L7*=o@X^bB`RVJ8JO^IykVtdOB^s8x*PR
z8q!i(K;|`dRH*n;UvJ4o3CtUBluqb{@>sYd4YOSr{d@M4_E*?dM?MbmZ
z>!gRnuEk;MM@q{^B;HJvE)FbR-x;#EW5lzG{_xXyf;-i{71wbV(8i5fLxC&}wV?d>
zS#GYb9~Ea;90hUgR~s5_Uh&9~XX-sUKS^!Lck@F1)QDO$ka#w8-k_^uD0=p3acL#g
zm+qkH0RVv8U3~SPga@{htugB9ewFZo?RSU#(>DNJ)Ed>4231`5y!oeFE_IVl=TPWW
z9W93=KjnaiZ5>u$eQ~ef?FTQ+Y=Q^j!(6(jhOVkvr)Sq6(}m>8^NO__e(Do$zIze?-7Z%=kIRjmJ$gL
z`!J7Sr5KEbP8D;2B?)R1W;?FdsqoB7{FULhMFoHEb_ZxNd_nFSRvrl98+%sw
z2p0qPquLrgmb`seJ~hDku3(JYpuF6L_A`HqpH*K=DQ%j!nm`-6WmWOFv;I%h{-?n=
zCE7`Z67SQdV;3Ca;gV%ncy_y(
zBJc}tJtX+)aUVBBI9+V0#=ZGM_1mFH2VL)No&z~FxKq+h$=-Q;Wp1?^`{m)2P<39G
z)!5<#UB@4<^jV(AEDQS^l>6*+chialu1!x3~nExl)CR4!bazu+`3Z
z=enw=uZ0C41p~?h26;Xu(b&Pn(DAKi#<(7BA}^gnxAP$)ulN-`isoT112b#Smz|;y
z7Y1mwV0tHC)qu5B7VzZqyX!!$IX&?r0_>I33DQuq#(LC9QV;J0IiX97^F|&zq)?tF^&8D#MI1(8
z8BIqjw*M|jwf1mYpLCjkv@qy5oSX7BuxvC3+aBN@F!k3AI!WO#GCddOC3GiHLeP~SV?UoXc^4P?(Y
zGTkuwpkbcUAGLVH&c>h1K+;9aq9$uT_(&C(AMA(rLxiOwXA6k3oZ2YM%-Hhn#e37#o>jsm5w@%m9ybiNCd&Tda6AVcI;6
z9-&;uA1e%&GL(8r9iprvDF0iw!y99*={=D+&ZC=_ZAq9&**j8j59W(}QE9CoOF3jE
z{@m>6>Gf>AhZ;w|HnGlbUpUQo@m_3-29GG%6Ok50C=zK{9?Mke7Pli#xS^+T$w9L)
zls8Ty{;;EP6?iV2-J4UWgoWK=ImUu|Abo)|eB1Xj@?zncw|Jq~C`M>2orEofY)(q)bR>2ivkxw7=X_
zI5H+WJr-+|gT(!O0IqN7@zsWw9oe;594KsgMl|WYFa1;NI9un_KzSc&QA}GtWib+!
z?l?pivo<1r8B)oWx{BtOk+k^16)htC{;R)uGV)1S!fnzaQdOIZHKlVn=vNKb!;n;O
zarrJoF?3d&0&I|^MJ;#rBOC8yQe{byTOkbjn(CY7N`u)xy{qb?yvAW@gmn_y>h!A;
zr18Q)RDa}o^KqufWZA8j5UIN27!~Q~Az%EXlF?(vO9lX!3hZ=T3LacQ?6r+Tk8Ykd
z3c(V>#$4th90xY_?>TXv3yRJ3m^x+O>w6x(MZ|ezgk@bS=>!cAt26MU28izfJOmfy
z+(U)uH#n|8NlqmK5E=Xu(&RW5pp6M430zfq-j6dML`l!K$T`_SrZfghqjQ#RZ!BcQ
zd(N*FRHwiLdXQjP-Q?`flx6d^FwjZLQbOUCa)0hn)M7~~3WEZ(rnJPy
zefjeAXLoDm{_fjuQ~~k0D+P-b+VC9P-g)%Fs!hBbyhXoTWGguzR_T|HC#&t_0Dx8!dW528pLh$?RPzfdF^9FK}H
za9h}TlT|$T;c@!uN-YUqv+Qm1-pm-h`lR&s{o5z?mecBjxg}Mz+9EWuK-FPg*)CX8263grIZn
zl!EqfzxaITrRr?#YagE@0|~sHmxxsPGQy?!`X{xy4aI5E8(*WPOsfv?1pYYnc)4&`
zG%^Rb)GUdg&RvRlf6nk+G_!>=8SFQH!KBzA9@fQ{s)}WOuLK!w2e#4*!-gfP~Sox{pr}zeh-hFP8rbW$0;Y>+b@fc_8&;$o
z%ZUa#+z|wMd8YU2&Avlu89L3%KA9cV5R|%9ELdU3C??krDFW6j@CLshg@?qMZ-hy*
zGAI>SCiQD!8l1nWjJ!v1_@+8-Tz`Dp?|uhJ8^NeDusZvN)6FS%kmjtK4r#wEUpwm#
z75EsNkJxHWT^CKQx9|%wmA?b9o7!6RzOpYqQGK%g+?QFIqA0BwIW8SoXtpIHQJ5OZ
zFM6^z2DW#!8IQ~2@%EK@8DDOGv}Yap-K{V)zgcIQy39cE5Eu%G{IXZq77wRWCSjm<
z?}140E+)TZFDNT~n%FF`6;^}sn?DOHR~iV9B^Q1qf)NwDl&&})He1VC{ums`25*B7
z2Rg3kQR^XBHbV-T@~8H)=ZD()iP1AbIfcTKV3P2ORVgCI)bu=Qy7TLV6``dGWm>u<`CE
z4O(z@-QbOGZA8+43Q3!4-W@XZ^oogZHwVfH!@|3A6kZqK6hBtRq!7tBSj})PFp_F(
z=XPn74+Gk&oy8AFsv4`%Wp5Isf=wX-wLa5}ueU3$Bzog{d-y{&1Ql0+MK3?zT(*C-
zuImHMrxv=2k}pWYn*xglIhBKC9kfTb@>#3j<`G?`cA6+Tyk6sxu(1$TA^aw|6U_3(
z$<6Mhxy5UGhOHQ5TRLros7NRk|2?|>N~9$-dJ3i_NyW74-2+|U^+050Q-c!k5V2mt
z98ix-l4~h66V=+_CkG#Ya+!1b)WE%c#;TrU6=~lgh?S%xo#DZhswIOZe{O5)*3l!m
zHt?H(g2ly*_%|WQD(3lyYr|giuJcA-Nj!zk{^ahB)*TcURIZ?jI1#+OHq>x&VnklZ
zc_Cac-$9k!)`M_PdOY3)n$v6t9e*xMo^|_{#X4e|`Tuq!-$^;xJriHXsoZv?bU|
z*SI)eTT#IFUZpWGd*;~)KGswJTxgpC%2PL>l%R*jP7RK)G|lEygzR%lxK^Ik3ci+F
z@6I*S3KPGV9F*~1T;?^P3|%<_rGTl%P-^p;s;VVw;!vtVZp`gY^&SQH!<6jZQ)uhz
z$nnN$LKqpltZ6lB_goTb`Lo$t`d8elWq*~jkct;DP9~lG6&bzOJHSs$IZd_F)wA2T
zTsp1&`aO9seA?wq}=*L
z@mY&gsj?CL_y6+WKac1VS&)ufX(^#05|*LLBpW682L9vHB98@;dxTX|EKi0!PPvdqSo3{(?yLnBcd6le3l?c
zl?qxxQChK;NYhABYwRQTrFgwF?>Td(=li~MzH`3k{&An>p5OgFzvteA&j()tCt*-4
zD1eWT5Af6B0vzlEgu_DoJnx)^9B=?v0mlRdg#-nT2?+@a3m+3Xep=+XsOWK7iBn>y
z737o@73AdQHnn&MNDu$jfV9)Y3VBK~GOlNyF%}A>jenCvZsoZxcAee^gND*uSf0Cjm$J
z_>Uar7dSNGKacPoR>cJbC4RVYMab6klydNc^wTO3^A=s?t^6vyYUC|7SXXJ$8RmAu0z^I;YKy!VM~2v4uekXT;m6-rzY$NwphFy%d2)~aoGPshhY
zy$SbmoZUv(XUj@C1rl#%vaF>rAJ>1;Q+U)&p=KMS0!fai!-2LpQc85RreYEpu(t)#-hq^Peu+l%tCB{Z5Er
zrFBJKpDge0c_MxML-(HM+UA-uHR{H5Sq7yA7@C|?2=Xgo>h_%(y+-|b@tli`meo+N
z2t^+hsKp)bi`DC%f6gO2;r$EYU8*O@rA|zLDCu2&nI%4`>jTSs%E^Dg4W5-JiZ+&?
z_yqr$(yM6OAHho6+zEI?WzID01drLz2bcB=>v*ngHiX9ROf`L(t+;gF=>^+yQ!1F;
zo2Acw!7b3CT^ri$g~^eOt+fp+rEO-0+Egp=nI?LAo|#XzX1nw)Tc4Vk*q=*t*;Ygg
zQ1dQ_L(#-@;g(n`&n&><9cDDfsB*JLj)NAX(A{CdzHuIfyU-D}+)UqYdcIQ(qj8(-6R)28qa#}9q(Mwy;0Q9`
z(0}8%%$vpJ!Ezb;xfiJyJBjPq{(_mXO!%I8>P2wdu_mLgF-aoMJ^lcYr4WSfjhv5B
zYZ1*~YLpI!-DL!MIa2hq>+uy@iIxG69j*HF@?<^BJ9aWvr-;Kbsn=`;o<~{8^R-)C
zOtWO^NX^?51uykUakRNpSarP>wf)RyE@Q9TXN3b$<+M8cgpP~6?g}dZSqbG=^GVK>
zzvEf-&w4E68~ZZHauNG{t-&0E*RhJ%7yDJCrD5zL?~Um!Ou1hzbLqBA2ZS)5Q?NsA
zXUX2_L!20%>2k2{nOUtjSs#t(xX)FToPpKIH`rDT*z{=NrQDCt(rgkO8f+JGl)G^v
z%+J~FRdUh^p=6P5(yls#FeU3ni&V?#S1+M{GaIOF9IVu`ru1-w$&IV4Md;pNKlOTzf1Ywlkg=6wV8faF!e{Kw$;_L?y0HIxzU%VioW!`*HRXl?KUzI-Jl#GNt~D@`60HHfIGxKoO{k(3J5o3Iv+u=12n-E%1WT98V;
zdm||c!2iSfe`xn-V2SZqo9m9X6-PxA(GldE4wCHH)}2ChkM
zK_9BF@Z1nPuhik<>0N!V*JZ<^Rp#Lk$c!y4*W+E=dgyEB{Wr+PEVU*mM)MqJm#L
zO^qoVU}OTMM~#Sw(eatrrusuwpN`<}5P|AzSOcKxP(jy1YThT$Dxg>IWD_v)
zQTqMptb0lia%g>}Z0`90qU6vP{@%RQyMRh<5`(N&dI0FG^&)18dJHF?39L?ZG2EA_
z>|VA+>9kb4SmPwy_2=S>N%gT-=RRqnyE>}rN?fo5yb2m
z?CFMQkXja|AM0sbvJUzJ4$d%PAf9H^Fo^cD@KJe>AS7_wFxgi(R^G+Edk1?@+tV$X
zscg*J*!C{Y?`^3zU}JgOA%qHzt}2vGN8*=tZ)YX%rs-?XQ|d7qlhDuLH(AdEK(Yqk
zl7E{4&kY$vKa+rOn1Xr0;Ly11_lx&$#{dJ1^M$uLb$cSq!xK?rc|6DcLYsa7-_M`E
znY!8}E11@}@mS!sp+;Cn>pNX!Sa3suj`aqJCE%PcV8b$L&
zM_tzosaAI@udnDkud5Cc$*gZ?_=PfiQ{G&&Vv&RDgv(Jv<2*3=V;lK|+%|+|;#Yaq
z(iCs3?Y`D+qLu^!lkc{#E;!dt!8}e)S;fTnY+a`WUZ2^QAK{IZ*%8gqnw4dfVT?zM
z*=+VG$0_C6SgzuUDNOL0a#VQX07ktp!L3KyWhw0p?&DZSZv$i*z?M7vcRzo5df4`H
zw*I*A8>SbMVts@aD00YEJZ&5$lp@pBUBs};FhS-8jA
z_O%u)^c;6&)8ZwO8;;a6P*^$g+1IG|MyeCvdWuJ;`Rm)?oSS_8o%bFtx?Mslj(-t6GDa|ORs;2
zV7Qb*uh#1Od7x9+Jj*R#2?DmrOMh5eW!Fe!5;eeGJUa{LU89q29=yt~ZG7Qvd)sTm#r&Y8OsklWn`AC6X3q#~
z4fdrMTK>pkg%n14xxur8Q<}swAmca3Vx4<=bW`sWIRN9UiQfyjTDnQwbtF!dY?y@SCA#=5Nf7?fxHHacbI(A0yO04i*~HLc
z+D?T}dA$(aa
z8I4SM^K;USbH<>pg+NjVv#8G?5mc4oL9Rf0ME4{>b3KuY2#Eguf^UT`@1(|FyJ4*12D-VV;(qI4+8{}0zl#|s
zwzd^E`=Fx2^ofaoYV!5Ege;$PIUg728vD%bN!A6MUr9WuLV$q;*J`<0aaj_N&`sIoa`CPahHEZFh~KUxJMy$NHo?aujKhxGLMNE{7nU-9J@$IW=>G*EBUwZQ-Svubcvpk$7RI(net$>qsv)UK-1
z*fXt&)UfnSgJ0ozx9-s(nCovj1!EuD?9qxdmAOXvX&>ohFWO7QuuW5I^?u&P7rG?p
zG~423@~&Am&Yr%(HRvJU>CheYlGW+3dx+`rW>Lm|MQ<8l^UWRBql0l^+M~XfsRO`O
zMR3wuI{a5FpieIiV4T7IUPS;vsDqG!P|s7(>fUthkC1#moDw%y0&dyGeV8RRBu6{h
zYd~vGlY2aiy=tkq80W^OZKQf{Z>(Di5?s9}R2gn|rs@oOq|avMB9Pz(d}&Ql8z^#W
z6qutmvs7U9RC0i+>W!K>MJkVmInZZgS%_PS^=WJ<@}G^p=ucn{d7q}bVfL@aSVr#aeUL?PMi
z_X_-grBO$yL;^i|uKO)tlhM!LJ;*;p{mg`qzYK1WQrFaH!Cpr9-CqWcaVBjZy_W=~
zbh^EwEQwjFGB+}p@<3)jnFhUMMPoarbWTYqO*;)~RmzReYzIRJFC$`_xbMyz#Tl@6^W
zPut~QB1UGv3FR%iq@pR6h=vXO|7
z3cC_$=P~%n3Jd=6%^t^#6a`v!6FKAAs4UpYxx<>Qe|DOZXw|WC;Xb)F9yP@HEDsNnfQ>5
RIe<{ne<$1jm=il-{|n3r0>J@000F4u*pd)bvWydt^eS}jU*SI
z&2y~Bqdv~du5a9`bp4{0a{xd<;n(^Vg1}=Da5yH3DkjW<${3qMDG@t_8iib8w50)Q
zuE+F4IgSp~+ALVQv2cFD!aA#OIY@Z$FR)=n3>eyXUtK#`@$9APGNrR>a
z)e2GrJs}}uM%JuiMJx%s;YygV8z?Mk5ZFs#42~`qHhKifL!PdatxASK$x@|DlPzC1
HhyVaP^9-1&
literal 0
HcmV?d00001
diff --git a/spyce-2.1/misc/pics/spyce-border.ico b/spyce-2.1/misc/pics/spyce-border.ico
new file mode 100755
index 0000000000000000000000000000000000000000..4cca56510dd26072254e5c2690f3fd47457f770a
GIT binary patch
literal 766
zcmd6kze~eV5XZ05p}1HPR1l#jx2UkHShjvit
zEDnwxi#iHUg2h@}T;dPNdGDo({t4dW^78rK05HOyV4q=^
zy0m?c1r~3;^zQb<)KUQ;N-#L~4Y59g_6dUIG{NZ^!FBZ-ZtHjO@Z>_%eTTQP>EdH^k;@9HVWkaco%LXDQccdWMH5n0gDM&t-U?OTIpR;pjN(jrudBvV#
zTZ^_WGNxXV`QB{a5*BA}riF9ktl+4sx9ph1Z}p-j(!+{#Hm-B4Tq-@J_+H07
zB9GaRyQbElm%J|0{`XW^rRS_J1gEL_d1Qmpn)K(BwJ;?zD~*7??26E~S#RZgvyQnY
zG2rBeUMchM1@$%@&B1|RzPAxOI!Bx=7K?enA
z>EPH=w4>l8v{-A!CDuaD%WIS1fT6m;zpz+4DJoX%+duwH^In63&KMLJyQgUS%T9uf?~A@5A{cQda>cd{)FcWL9Iq$
z+XO@^c=}_!U2b1D0#C_6#Kf*A*n$yJ_k?p71fq7+cI46`C8a5FUa@0X=AvcEtk9Lg
zNp6_ZWJea)T!S;YWny}g6&zO$%LNgCuV0!n6IYzKaGjHUDl@9Mtm8hnF}Lfs&>Hl*
zq03CWpDtFZlGCMhZS7$jjOMh5l*g3JuJ~JYaw}5X@vGz{3qq}}BRPpp{l3)Mo~vyZ
zv;xM0hO&j&8@!Ib`xw=#m8W?Nd_Dnx0CZ_a?B07olfU^wD01g9od67k4}9c0^jsbJ
YOosr+0de*f{)N=~T@H$!-{{|_ADteRCjbBd
literal 0
HcmV?d00001
diff --git a/spyce-2.1/misc/pics/spyce1.bmp b/spyce-2.1/misc/pics/spyce1.bmp
new file mode 100755
index 0000000000000000000000000000000000000000..97d582a9f02b07b114011fae4998cb69eeac575c
GIT binary patch
literal 67854
zcmeI537l0`8Ni3xn1vqsWiD7$=-2;y=gpZrcX{v54D;@NF!y)n{m#4h&b#-W|NXx6
zec$=cc~cK*U)&}~w{iRp;qRNtzcx8l)N*oOENYWeUYe8mOYGo9TDL53fmViD4v^b#
zW!TCv`&%IUzd@@Uwld8A7Rdf@&?<+m470xlvi}>j%3&+R>~DeW{|2pc*vc^bTOj+t
zL8~0LGR*!K$o_B8Du=BMv%dwh{~JUkhyVGHy#K!ZFGIgpNlpAi4?|xT)`crxQaryI~HJ*2WyOZV=2
zsG>rOi}i%mrBf$4>L|s&e7Sz|O*iRuUFoL~TFX`W3CQOjpTA4pz)~u1OTjd8okl+4RUU@~{
zdvEub(p|(5sY3vM|9fq`O`BxD{Zzp6^A#GUuy7YZ3j@}k)1`|{nj}k?%9U5j(@*c_
zQrSF)7$0;;@4hQ9ydYounjCzv^y#BgS5l&?(za;kD@ZBN4tIib9y3O}*7MGjJMWa|
zp4$z1HkBdtcJ9>n_{A^ej5GA1A3Rui`q7iLU*@5uy<*?K+7&NbCU@MSl8Mg$_rG@&
zo=s)=;fHExu3s
q#~h=%JzUuvZDJ)N*feUXC
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
Cdrj^D
literal 0
HcmV?d00001
diff --git a/spyce-2.1/misc/pics/spyce2.bmp b/spyce-2.1/misc/pics/spyce2.bmp
new file mode 100755
index 0000000000000000000000000000000000000000..8477865dfc28c8e9c1be1c9e0505d5dc1ba5af68
GIT binary patch
literal 67851
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
literal 0
HcmV?d00001
diff --git a/spyce-2.1/misc/pics/spyce3.bmp b/spyce-2.1/misc/pics/spyce3.bmp
new file mode 100755
index 0000000000000000000000000000000000000000..3a8385994e1b9133e6453632224552feb6cc5795
GIT binary patch
literal 67854
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
j8lBmvf*hKgt*OLpff|M}n6-ahW1
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