From dee382527fa76bfb8d6f4b2e17d5f941d817a0d8 Mon Sep 17 00:00:00 2001 From: Paulo Henrique Silva Date: Thu, 24 Aug 2006 17:15:21 +0000 Subject: [PATCH] - versao migrada do SVN que quebrou --- AUTHORS | 0 BUGS | 15 + COPYING | 340 +++ ChangeLog | 0 INSTALL | 0 Makefile.am | 45 + NEWS | 0 README | 7 + SConstruct | 22 + acinclude.m4 | 35 + autogen.sh | 21 + configure.ac | 97 + contrib/Makefile.am | 4 + contrib/bin/Makefile.am | 5 + contrib/bin/Spm | Bin 0 -> 22226 bytes contrib/bin/sbig_load_lpt.in | 32 + contrib/bin/sbig_load_usb.in | 37 + contrib/bin/sbig_load_usb_suse.in | 37 + contrib/bin/sbig_unload_lpt | 16 + contrib/bin/sbig_unload_usb | 17 + contrib/bin/spmtable | Bin 0 -> 28812 bytes contrib/fits2jpeg/FITS2jpeg.c | 390 +++ contrib/fits2jpeg/LICENSE | 343 +++ contrib/fits2jpeg/Makefile.am | 6 + contrib/fits2jpeg/jpegsubs.c | 216 ++ contrib/fits2jpeg/jpegsubs.h | 46 + contrib/include/Makefile.am | 3 + contrib/include/csbigcam.h | 113 + contrib/include/csbigimg.h | 157 ++ contrib/include/drvrsmem.h | 178 ++ contrib/include/fitsio.h | 1557 ++++++++++++ contrib/include/fitsio2.h | 1109 +++++++++ contrib/include/longnam.h | 535 ++++ contrib/include/rdcolor.h | 89 + contrib/include/sbigudrv.h | 668 +++++ contrib/include/setproto.h | 28 + contrib/include/sockets.h | 440 ++++ contrib/include/xstdlib.h | 18 + contrib/include/xtdio.h | 157 ++ contrib/lib/Makefile.am | 6 + contrib/lib/libcfitsio.a | Bin 0 -> 1221884 bytes contrib/lib/libcfitsio.so | Bin 0 -> 963919 bytes contrib/lib/libsbigudrv.a | Bin 0 -> 90056 bytes contrib/lib/libsbigudrv.so | Bin 0 -> 89865 bytes contrib/lib/libsimpleskts.a | Bin 0 -> 863048 bytes contrib/lib/libsimpleskts.so | Bin 0 -> 362768 bytes contrib/lib/libsimpleskts.so.2 | Bin 0 -> 362768 bytes contrib/lib/sbigusbmod_2_6.ko | Bin 0 -> 10935 bytes doc/SPEC | 109 + doc/Technical_Description | 34 + doc/daemons.txt | 54 + rules.make | 15 + src/Makefile.am | 1 + src/config/Makefile.am | 5 + src/config/initscripts/Makefile.am | 3 + src/config/initscripts/debian/Makefile.am | 7 + src/config/initscripts/debian/sbigcam | 68 + src/config/initscripts/debian/secd | 72 + src/config/initscripts/debian/spmd | 64 + src/config/initscripts/debian/uts.in | 9 + src/config/initscripts/redhat/Makefile.am | 6 + src/config/initscripts/redhat/sbigcam | 56 + src/config/initscripts/redhat/secd | 52 + src/config/initscripts/redhat/spmd | 57 + src/config/initscripts/redhat/uts.in | 9 + src/config/initscripts/suse/Makefile.am | 7 + src/config/initscripts/suse/sbigcam | 146 ++ src/config/initscripts/suse/secd | 208 ++ src/config/initscripts/suse/spmd | 195 ++ src/config/initscripts/suse/uts.in | 11 + src/config/rpc.conf | 0 src/config/site.xml | 29 + src/drivers/Makefile.am | 2 + src/drivers/camfits.fake/Makefile.am | 9 + src/drivers/camfits.fake/ccd.c | 465 ++++ src/drivers/camfits.fake/ccd.h | 120 + src/drivers/camfits.fake/errors.h | 19 + src/drivers/camfits.fake/fake.camera.xml | 44 + src/drivers/camfits.fake/main.c | 323 +++ src/drivers/camfits.fake/ser.c | 250 ++ src/drivers/camfits.fake/ser.h | 34 + src/drivers/camfits.st4/Makefile.am | 10 + src/drivers/camfits.st4/camera.c | 243 ++ src/drivers/camfits.st4/ccd.c | 470 ++++ src/drivers/camfits.st4/ccd.h | 120 + src/drivers/camfits.st4/errors.h | 19 + src/drivers/camfits.st4/fitsio.h | 1355 +++++++++++ src/drivers/camfits.st4/longnam.h | 476 ++++ src/drivers/camfits.st4/ser.c | 249 ++ src/drivers/camfits.st4/ser.h | 34 + src/drivers/camfits.st4/st4.camera.xml | 64 + src/drivers/camfits.st7/Makefile.am | 11 + src/drivers/camfits.st7/ccdcap-test.cpp | 81 + src/drivers/camfits.st7/ccdcap.h | 85 + src/drivers/camfits.st7/cfw8.filter.xml | 38 + src/drivers/camfits.st7/csbigcam.cpp | 981 ++++++++ src/drivers/camfits.st7/csbigcam.h | 113 + src/drivers/camfits.st7/csbigimg.cpp | 379 +++ src/drivers/camfits.st7/csbigimg.h | 157 ++ src/drivers/camfits.st7/getindex.c | 58 + src/drivers/camfits.st7/getindex.h | 14 + src/drivers/camfits.st7/main.cpp | 671 +++++ src/drivers/camfits.st7/sbigcamcap.cpp | 98 + src/drivers/camfits.st7/sbigcamcap.h | 53 + src/drivers/camfits.st7/st7.camera.xml | 92 + src/drivers/telgo.fake/Makefile.am | 9 + src/drivers/telgo.fake/errors.h | 35 + src/drivers/telgo.fake/fake.telescope.xml | 50 + src/drivers/telgo.fake/fields.c | 67 + src/drivers/telgo.fake/fields.h | 32 + src/drivers/telgo.fake/main.c | 237 ++ src/drivers/telgo.fake/ser.c | 267 ++ src/drivers/telgo.fake/ser.h | 49 + src/drivers/telgo.lx200/Makefile.am | 9 + src/drivers/telgo.lx200/errors.h | 35 + src/drivers/telgo.lx200/lx200.c | 348 +++ src/drivers/telgo.lx200/lx200.h | 77 + src/drivers/telgo.lx200/lx200.telescope.xml | 50 + src/drivers/telgo.lx200/main.c | 460 ++++ src/drivers/telgo.lx200/ser.c | 269 ++ src/drivers/telgo.lx200/ser.h | 56 + src/drivers/telgo.paramount/Makefile.am | 8 + .../telgo.paramount/paramount.telescope.xml | 50 + .../telgo.paramount/telgo.paramount.py | 97 + src/drivers/telgo.paramount/telgo.server.py | 130 + src/drivers/telgo.paramount/telgo.service.py | 33 + src/drivers/weather.wx200/Makefile.am | 4 + src/drivers/weather.wx200/wx200.weather.xml | 29 + src/python/Makefile.am | 5 + src/python/bin/uts | 26 + src/python/doc/doc.sh | 1 + src/python/doc/exemplo/exemplo.py | 68 + src/python/doc/exemplo/falso_chefao.py | 43 + src/python/setup.py | 13 + src/python/test/uts.pylint | 353 +++ src/python/uts/Makefile.am | 5 + src/python/uts/__init__.py | 1 + src/python/uts/controllers/__init__.py | 0 src/python/uts/core/Makefile.am | 1 + src/python/uts/core/__init__.py | 0 src/python/uts/core/async.py | 61 + src/python/uts/core/config.py | 123 + src/python/uts/core/controller.py | 13 + src/python/uts/core/driver.py | 192 ++ src/python/uts/core/event.py | 79 + src/python/uts/core/exceptions.py | 3 + src/python/uts/core/instrument.py | 88 + src/python/uts/core/location.py | 83 + src/python/uts/core/log.config | 44 + src/python/uts/core/manager.py | 145 ++ src/python/uts/core/proxy.py | 89 + src/python/uts/core/register.py | 129 + src/python/uts/core/sec.py | 363 +++ src/python/uts/core/site.py | 190 ++ src/python/uts/core/spm.py | 129 + src/python/uts/core/threads.py | 200 ++ src/python/uts/core/version.py | 2 + src/python/uts/instruments/Makefile.am | 0 src/python/uts/instruments/__init__.py | 0 src/python/uts/interfaces/Makefile | 291 +++ src/python/uts/interfaces/Makefile.am | 0 src/python/uts/interfaces/__init__.py | 0 src/python/uts/interfaces/camera.py | 39 + src/python/uts/interfaces/telescope.py | 43 + src/python/uts/util/__init__.py | 0 src/python/uts/util/catalog.py | 20 + src/python/uts/util/etree/ElementInclude.py | 141 ++ src/python/uts/util/etree/ElementPath.py | 196 ++ src/python/uts/util/etree/ElementTree.py | 1254 ++++++++++ src/python/uts/util/etree/HTMLTreeBuilder.py | 230 ++ .../uts/util/etree/SgmlopXMLTreeBuilder.py | 103 + .../uts/util/etree/SimpleXMLTreeBuilder.py | 144 ++ src/python/uts/util/etree/SimpleXMLWriter.py | 279 +++ .../uts/util/etree/TidyHTMLTreeBuilder.py | 6 + src/python/uts/util/etree/TidyTools.py | 128 + src/python/uts/util/etree/XMLTreeBuilder.py | 113 + src/python/uts/util/etree/__init__.py | 30 + src/python/uts/util/observation.py | 47 + src/python/uts/util/output.py | 196 ++ src/sec/Makefile.am | 13 + src/sec/SConscript | 24 + src/sec/fields.c | 61 + src/sec/fields.h | 68 + src/sec/main.c | 281 +++ src/sec/messages.c | 40 + src/sec/messages.h | 40 + src/sec/sdict-test.c | 56 + src/sec/sdict.c | 167 ++ src/sec/sdict.h | 45 + src/sec/sec.c | 468 ++++ src/sec/sec.h | 120 + src/sec/sec_config.c | 123 + src/sec/sec_config.h | 46 + src/sec/sec_server.c | 918 +++++++ src/sec/sec_server.h | 67 + src/sec/server.c | 267 ++ src/sec/server.h | 73 + src/sec/skterror.c | 50 + src/sec/skterror.h | 30 + src/sec/sktlist.c | 118 + src/sec/sktlist.h | 69 + src/tools/Makefile.am | 3 + src/tools/client/Makefile.am | 6 + src/tools/client/SConscript | 15 + src/tools/client/main.c | 50 + src/tools/client/sterm.c | 31 + src/tools/uts-gen-conf | 101 + src/www/LEIAME | 71 + src/www/LEIAME.html | 111 + src/www/LICENSE | 340 +++ src/www/admin/change-pass.php | 102 + src/www/admin/home.php | 117 + src/www/admin/index.php | 215 ++ src/www/admin/login.php | 70 + src/www/admin/logout.php | 40 + src/www/admin/root.php | 26 + src/www/admin/setup.php | 202 ++ src/www/admin/time-add.php | 98 + src/www/admin/time-del.php | 59 + src/www/admin/user-add.php | 130 + src/www/admin/user-del.php | 57 + src/www/admin/user.php | 320 +++ src/www/apontar-orch.php | 81 + src/www/apontar-uts.php | 161 ++ src/www/arquivo.php | 129 + src/www/bin/fits2jpeg-linux | Bin 0 -> 32325 bytes src/www/bin/fits2jpeg-win32 | Bin 0 -> 32325 bytes src/www/config/uts.inc.php | 47 + src/www/css/uts.css | 186 ++ src/www/db/db-schema.sql | 77 + src/www/db/db-schema.xml | 365 +++ src/www/db/db-user.sql | 7 + src/www/display.php | 58 + src/www/download.php | 53 + src/www/formApontar.php | 257 ++ src/www/formOpcoes.php | 104 + src/www/getStatus.php | 97 + src/www/home.php | 138 ++ src/www/imagens/back-ajuda.png | Bin 0 -> 914 bytes src/www/imagens/cnpqlogo.jpg | Bin 0 -> 12808 bytes src/www/imagens/default.png | Bin 0 -> 4338 bytes src/www/imagens/inpelogo.jpg | Bin 0 -> 11601 bytes src/www/imagens/ovlogo.jpg | Bin 0 -> 20525 bytes src/www/imagens/ufrgslogo.jpg | Bin 0 -> 11622 bytes src/www/imagens/ufrjlogo.jpg | Bin 0 -> 11942 bytes src/www/imagens/ufrnlogo.jpg | Bin 0 -> 11252 bytes src/www/imagens/ufsclogo.jpg | Bin 0 -> 12061 bytes src/www/imagens/usplogo.jpg | Bin 0 -> 10627 bytes src/www/imagens/vitaelogo.jpg | Bin 0 -> 11969 bytes src/www/imagens/warning.gif | Bin 0 -> 139 bytes src/www/index.php | 200 ++ src/www/instalacao-action.php | 218 ++ src/www/instalacao-action2.php | 108 + src/www/instalacao.php | 216 ++ src/www/js/jscountdown.js | 83 + src/www/js/jsval.js | 400 +++ src/www/js/nicetitle.js | 220 ++ src/www/js/uts.js | 82 + src/www/lib/Sec.php | 270 +++ src/www/lib/Socket.php | 92 + src/www/lib/astro.php | 20 + src/www/lib/db.php | 40 + src/www/lib/erro.php | 79 + src/www/lib/pear/DB.php | 1388 +++++++++++ src/www/lib/pear/DB/common.php | 2157 +++++++++++++++++ src/www/lib/pear/DB/dbase.php | 510 ++++ src/www/lib/pear/DB/fbsql.php | 770 ++++++ src/www/lib/pear/DB/ibase.php | 1071 ++++++++ src/www/lib/pear/DB/ifx.php | 681 ++++++ src/www/lib/pear/DB/msql.php | 810 +++++++ src/www/lib/pear/DB/mssql.php | 914 +++++++ src/www/lib/pear/DB/mysql.php | 1034 ++++++++ src/www/lib/pear/DB/mysqli.php | 1076 ++++++++ src/www/lib/pear/DB/oci8.php | 1117 +++++++++ src/www/lib/pear/DB/odbc.php | 883 +++++++ src/www/lib/pear/DB/pgsql.php | 1097 +++++++++ src/www/lib/pear/DB/sqlite.php | 942 +++++++ src/www/lib/pear/DB/storage.php | 504 ++++ src/www/lib/pear/DB/sybase.php | 907 +++++++ src/www/lib/pear/OS/Guess.php | 287 +++ src/www/lib/pear/PEAR.php | 1055 ++++++++ src/www/lib/pear/PEAR/Autoloader.php | 208 ++ src/www/lib/pear/PEAR/Builder.php | 426 ++++ src/www/lib/pear/PEAR/Command.php | 398 +++ src/www/lib/pear/PEAR/Command/Auth.php | 155 ++ src/www/lib/pear/PEAR/Command/Build.php | 89 + src/www/lib/pear/PEAR/Command/Common.php | 249 ++ src/www/lib/pear/PEAR/Command/Config.php | 225 ++ src/www/lib/pear/PEAR/Command/Install.php | 470 ++++ src/www/lib/pear/PEAR/Command/Mirror.php | 101 + src/www/lib/pear/PEAR/Command/Package.php | 819 +++++++ src/www/lib/pear/PEAR/Command/Registry.php | 351 +++ src/www/lib/pear/PEAR/Command/Remote.php | 435 ++++ src/www/lib/pear/PEAR/Common.php | 2094 ++++++++++++++++ src/www/lib/pear/PEAR/Config.php | 1169 +++++++++ src/www/lib/pear/PEAR/Dependency.php | 487 ++++ src/www/lib/pear/PEAR/Downloader.php | 680 ++++++ src/www/lib/pear/PEAR/ErrorStack.php | 981 ++++++++ src/www/lib/pear/PEAR/Exception.php | 359 +++ src/www/lib/pear/PEAR/Frontend/CLI.php | 509 ++++ src/www/lib/pear/PEAR/Installer.php | 1068 ++++++++ src/www/lib/pear/PEAR/Packager.php | 165 ++ src/www/lib/pear/PEAR/Registry.php | 538 ++++ src/www/lib/pear/PEAR/Remote.php | 394 +++ src/www/lib/pear/PEAR/RunTest.php | 363 +++ src/www/lib/pear/System.php | 540 +++++ src/www/lib/root.php | 27 + src/www/lib/spmtable.py | 78 + src/www/lib/utils.php | 328 +++ src/www/lib/utils.php.orig | 304 +++ src/www/login.php | 77 + src/www/logout.php | 66 + src/www/opcoes.php | 48 + src/www/root.php | 26 + src/www/sky.php | 61 + src/www/status.php | 222 ++ 316 files changed, 64062 insertions(+) create mode 100644 AUTHORS create mode 100644 BUGS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 SConstruct create mode 100644 acinclude.m4 create mode 100644 autogen.sh create mode 100644 configure.ac create mode 100644 contrib/Makefile.am create mode 100644 contrib/bin/Makefile.am create mode 100755 contrib/bin/Spm create mode 100644 contrib/bin/sbig_load_lpt.in create mode 100644 contrib/bin/sbig_load_usb.in create mode 100644 contrib/bin/sbig_load_usb_suse.in create mode 100644 contrib/bin/sbig_unload_lpt create mode 100644 contrib/bin/sbig_unload_usb create mode 100755 contrib/bin/spmtable create mode 100644 contrib/fits2jpeg/FITS2jpeg.c create mode 100644 contrib/fits2jpeg/LICENSE create mode 100644 contrib/fits2jpeg/Makefile.am create mode 100644 contrib/fits2jpeg/jpegsubs.c create mode 100644 contrib/fits2jpeg/jpegsubs.h create mode 100644 contrib/include/Makefile.am create mode 100644 contrib/include/csbigcam.h create mode 100644 contrib/include/csbigimg.h create mode 100644 contrib/include/drvrsmem.h create mode 100644 contrib/include/fitsio.h create mode 100644 contrib/include/fitsio2.h create mode 100644 contrib/include/longnam.h create mode 100644 contrib/include/rdcolor.h create mode 100644 contrib/include/sbigudrv.h create mode 100644 contrib/include/setproto.h create mode 100644 contrib/include/sockets.h create mode 100644 contrib/include/xstdlib.h create mode 100644 contrib/include/xtdio.h create mode 100644 contrib/lib/Makefile.am create mode 100644 contrib/lib/libcfitsio.a create mode 100755 contrib/lib/libcfitsio.so create mode 100644 contrib/lib/libsbigudrv.a create mode 100755 contrib/lib/libsbigudrv.so create mode 100644 contrib/lib/libsimpleskts.a create mode 100755 contrib/lib/libsimpleskts.so create mode 100755 contrib/lib/libsimpleskts.so.2 create mode 100644 contrib/lib/sbigusbmod_2_6.ko create mode 100644 doc/SPEC create mode 100644 doc/Technical_Description create mode 100644 doc/daemons.txt create mode 100644 rules.make create mode 100644 src/Makefile.am create mode 100644 src/config/Makefile.am create mode 100644 src/config/initscripts/Makefile.am create mode 100644 src/config/initscripts/debian/Makefile.am create mode 100644 src/config/initscripts/debian/sbigcam create mode 100644 src/config/initscripts/debian/secd create mode 100644 src/config/initscripts/debian/spmd create mode 100644 src/config/initscripts/debian/uts.in create mode 100644 src/config/initscripts/redhat/Makefile.am create mode 100644 src/config/initscripts/redhat/sbigcam create mode 100644 src/config/initscripts/redhat/secd create mode 100644 src/config/initscripts/redhat/spmd create mode 100644 src/config/initscripts/redhat/uts.in create mode 100644 src/config/initscripts/suse/Makefile.am create mode 100644 src/config/initscripts/suse/sbigcam create mode 100644 src/config/initscripts/suse/secd create mode 100644 src/config/initscripts/suse/spmd create mode 100644 src/config/initscripts/suse/uts.in create mode 100644 src/config/rpc.conf create mode 100644 src/config/site.xml create mode 100644 src/drivers/Makefile.am create mode 100644 src/drivers/camfits.fake/Makefile.am create mode 100644 src/drivers/camfits.fake/ccd.c create mode 100644 src/drivers/camfits.fake/ccd.h create mode 100644 src/drivers/camfits.fake/errors.h create mode 100644 src/drivers/camfits.fake/fake.camera.xml create mode 100644 src/drivers/camfits.fake/main.c create mode 100644 src/drivers/camfits.fake/ser.c create mode 100644 src/drivers/camfits.fake/ser.h create mode 100644 src/drivers/camfits.st4/Makefile.am create mode 100644 src/drivers/camfits.st4/camera.c create mode 100644 src/drivers/camfits.st4/ccd.c create mode 100644 src/drivers/camfits.st4/ccd.h create mode 100644 src/drivers/camfits.st4/errors.h create mode 100644 src/drivers/camfits.st4/fitsio.h create mode 100644 src/drivers/camfits.st4/longnam.h create mode 100644 src/drivers/camfits.st4/ser.c create mode 100644 src/drivers/camfits.st4/ser.h create mode 100644 src/drivers/camfits.st4/st4.camera.xml create mode 100644 src/drivers/camfits.st7/Makefile.am create mode 100644 src/drivers/camfits.st7/ccdcap-test.cpp create mode 100644 src/drivers/camfits.st7/ccdcap.h create mode 100644 src/drivers/camfits.st7/cfw8.filter.xml create mode 100644 src/drivers/camfits.st7/csbigcam.cpp create mode 100644 src/drivers/camfits.st7/csbigcam.h create mode 100644 src/drivers/camfits.st7/csbigimg.cpp create mode 100644 src/drivers/camfits.st7/csbigimg.h create mode 100644 src/drivers/camfits.st7/getindex.c create mode 100644 src/drivers/camfits.st7/getindex.h create mode 100644 src/drivers/camfits.st7/main.cpp create mode 100644 src/drivers/camfits.st7/sbigcamcap.cpp create mode 100644 src/drivers/camfits.st7/sbigcamcap.h create mode 100644 src/drivers/camfits.st7/st7.camera.xml create mode 100644 src/drivers/telgo.fake/Makefile.am create mode 100644 src/drivers/telgo.fake/errors.h create mode 100644 src/drivers/telgo.fake/fake.telescope.xml create mode 100644 src/drivers/telgo.fake/fields.c create mode 100644 src/drivers/telgo.fake/fields.h create mode 100644 src/drivers/telgo.fake/main.c create mode 100644 src/drivers/telgo.fake/ser.c create mode 100644 src/drivers/telgo.fake/ser.h create mode 100644 src/drivers/telgo.lx200/Makefile.am create mode 100644 src/drivers/telgo.lx200/errors.h create mode 100644 src/drivers/telgo.lx200/lx200.c create mode 100644 src/drivers/telgo.lx200/lx200.h create mode 100644 src/drivers/telgo.lx200/lx200.telescope.xml create mode 100644 src/drivers/telgo.lx200/main.c create mode 100644 src/drivers/telgo.lx200/ser.c create mode 100644 src/drivers/telgo.lx200/ser.h create mode 100644 src/drivers/telgo.paramount/Makefile.am create mode 100644 src/drivers/telgo.paramount/paramount.telescope.xml create mode 100644 src/drivers/telgo.paramount/telgo.paramount.py create mode 100644 src/drivers/telgo.paramount/telgo.server.py create mode 100644 src/drivers/telgo.paramount/telgo.service.py create mode 100644 src/drivers/weather.wx200/Makefile.am create mode 100644 src/drivers/weather.wx200/wx200.weather.xml create mode 100644 src/python/Makefile.am create mode 100644 src/python/bin/uts create mode 100644 src/python/doc/doc.sh create mode 100644 src/python/doc/exemplo/exemplo.py create mode 100644 src/python/doc/exemplo/falso_chefao.py create mode 100644 src/python/setup.py create mode 100644 src/python/test/uts.pylint create mode 100644 src/python/uts/Makefile.am create mode 100644 src/python/uts/__init__.py create mode 100644 src/python/uts/controllers/__init__.py create mode 100644 src/python/uts/core/Makefile.am create mode 100644 src/python/uts/core/__init__.py create mode 100644 src/python/uts/core/async.py create mode 100644 src/python/uts/core/config.py create mode 100644 src/python/uts/core/controller.py create mode 100644 src/python/uts/core/driver.py create mode 100644 src/python/uts/core/event.py create mode 100644 src/python/uts/core/exceptions.py create mode 100644 src/python/uts/core/instrument.py create mode 100644 src/python/uts/core/location.py create mode 100644 src/python/uts/core/log.config create mode 100644 src/python/uts/core/manager.py create mode 100644 src/python/uts/core/proxy.py create mode 100644 src/python/uts/core/register.py create mode 100644 src/python/uts/core/sec.py create mode 100644 src/python/uts/core/site.py create mode 100644 src/python/uts/core/spm.py create mode 100644 src/python/uts/core/threads.py create mode 100644 src/python/uts/core/version.py create mode 100644 src/python/uts/instruments/Makefile.am create mode 100644 src/python/uts/instruments/__init__.py create mode 100644 src/python/uts/interfaces/Makefile create mode 100644 src/python/uts/interfaces/Makefile.am create mode 100644 src/python/uts/interfaces/__init__.py create mode 100644 src/python/uts/interfaces/camera.py create mode 100644 src/python/uts/interfaces/telescope.py create mode 100644 src/python/uts/util/__init__.py create mode 100644 src/python/uts/util/catalog.py create mode 100644 src/python/uts/util/etree/ElementInclude.py create mode 100644 src/python/uts/util/etree/ElementPath.py create mode 100644 src/python/uts/util/etree/ElementTree.py create mode 100644 src/python/uts/util/etree/HTMLTreeBuilder.py create mode 100644 src/python/uts/util/etree/SgmlopXMLTreeBuilder.py create mode 100644 src/python/uts/util/etree/SimpleXMLTreeBuilder.py create mode 100644 src/python/uts/util/etree/SimpleXMLWriter.py create mode 100644 src/python/uts/util/etree/TidyHTMLTreeBuilder.py create mode 100644 src/python/uts/util/etree/TidyTools.py create mode 100644 src/python/uts/util/etree/XMLTreeBuilder.py create mode 100644 src/python/uts/util/etree/__init__.py create mode 100644 src/python/uts/util/observation.py create mode 100644 src/python/uts/util/output.py create mode 100644 src/sec/Makefile.am create mode 100644 src/sec/SConscript create mode 100644 src/sec/fields.c create mode 100644 src/sec/fields.h create mode 100644 src/sec/main.c create mode 100644 src/sec/messages.c create mode 100644 src/sec/messages.h create mode 100644 src/sec/sdict-test.c create mode 100644 src/sec/sdict.c create mode 100644 src/sec/sdict.h create mode 100644 src/sec/sec.c create mode 100644 src/sec/sec.h create mode 100644 src/sec/sec_config.c create mode 100644 src/sec/sec_config.h create mode 100644 src/sec/sec_server.c create mode 100644 src/sec/sec_server.h create mode 100644 src/sec/server.c create mode 100644 src/sec/server.h create mode 100644 src/sec/skterror.c create mode 100644 src/sec/skterror.h create mode 100644 src/sec/sktlist.c create mode 100644 src/sec/sktlist.h create mode 100644 src/tools/Makefile.am create mode 100644 src/tools/client/Makefile.am create mode 100644 src/tools/client/SConscript create mode 100644 src/tools/client/main.c create mode 100644 src/tools/client/sterm.c create mode 100644 src/tools/uts-gen-conf create mode 100644 src/www/LEIAME create mode 100644 src/www/LEIAME.html create mode 100644 src/www/LICENSE create mode 100644 src/www/admin/change-pass.php create mode 100644 src/www/admin/home.php create mode 100644 src/www/admin/index.php create mode 100644 src/www/admin/login.php create mode 100644 src/www/admin/logout.php create mode 100644 src/www/admin/root.php create mode 100644 src/www/admin/setup.php create mode 100644 src/www/admin/time-add.php create mode 100644 src/www/admin/time-del.php create mode 100644 src/www/admin/user-add.php create mode 100644 src/www/admin/user-del.php create mode 100644 src/www/admin/user.php create mode 100644 src/www/apontar-orch.php create mode 100644 src/www/apontar-uts.php create mode 100644 src/www/arquivo.php create mode 100755 src/www/bin/fits2jpeg-linux create mode 100755 src/www/bin/fits2jpeg-win32 create mode 100644 src/www/config/uts.inc.php create mode 100644 src/www/css/uts.css create mode 100644 src/www/db/db-schema.sql create mode 100644 src/www/db/db-schema.xml create mode 100644 src/www/db/db-user.sql create mode 100644 src/www/display.php create mode 100644 src/www/download.php create mode 100644 src/www/formApontar.php create mode 100644 src/www/formOpcoes.php create mode 100644 src/www/getStatus.php create mode 100644 src/www/home.php create mode 100644 src/www/imagens/back-ajuda.png create mode 100644 src/www/imagens/cnpqlogo.jpg create mode 100644 src/www/imagens/default.png create mode 100644 src/www/imagens/inpelogo.jpg create mode 100644 src/www/imagens/ovlogo.jpg create mode 100644 src/www/imagens/ufrgslogo.jpg create mode 100644 src/www/imagens/ufrjlogo.jpg create mode 100644 src/www/imagens/ufrnlogo.jpg create mode 100644 src/www/imagens/ufsclogo.jpg create mode 100644 src/www/imagens/usplogo.jpg create mode 100644 src/www/imagens/vitaelogo.jpg create mode 100644 src/www/imagens/warning.gif create mode 100644 src/www/index.php create mode 100644 src/www/instalacao-action.php create mode 100644 src/www/instalacao-action2.php create mode 100644 src/www/instalacao.php create mode 100644 src/www/js/jscountdown.js create mode 100644 src/www/js/jsval.js create mode 100644 src/www/js/nicetitle.js create mode 100644 src/www/js/uts.js create mode 100644 src/www/lib/Sec.php create mode 100644 src/www/lib/Socket.php create mode 100644 src/www/lib/astro.php create mode 100644 src/www/lib/db.php create mode 100644 src/www/lib/erro.php create mode 100644 src/www/lib/pear/DB.php create mode 100644 src/www/lib/pear/DB/common.php create mode 100644 src/www/lib/pear/DB/dbase.php create mode 100644 src/www/lib/pear/DB/fbsql.php create mode 100644 src/www/lib/pear/DB/ibase.php create mode 100644 src/www/lib/pear/DB/ifx.php create mode 100644 src/www/lib/pear/DB/msql.php create mode 100644 src/www/lib/pear/DB/mssql.php create mode 100644 src/www/lib/pear/DB/mysql.php create mode 100644 src/www/lib/pear/DB/mysqli.php create mode 100644 src/www/lib/pear/DB/oci8.php create mode 100644 src/www/lib/pear/DB/odbc.php create mode 100644 src/www/lib/pear/DB/pgsql.php create mode 100644 src/www/lib/pear/DB/sqlite.php create mode 100644 src/www/lib/pear/DB/storage.php create mode 100644 src/www/lib/pear/DB/sybase.php create mode 100644 src/www/lib/pear/OS/Guess.php create mode 100644 src/www/lib/pear/PEAR.php create mode 100644 src/www/lib/pear/PEAR/Autoloader.php create mode 100644 src/www/lib/pear/PEAR/Builder.php create mode 100644 src/www/lib/pear/PEAR/Command.php create mode 100644 src/www/lib/pear/PEAR/Command/Auth.php create mode 100644 src/www/lib/pear/PEAR/Command/Build.php create mode 100644 src/www/lib/pear/PEAR/Command/Common.php create mode 100644 src/www/lib/pear/PEAR/Command/Config.php create mode 100644 src/www/lib/pear/PEAR/Command/Install.php create mode 100644 src/www/lib/pear/PEAR/Command/Mirror.php create mode 100644 src/www/lib/pear/PEAR/Command/Package.php create mode 100644 src/www/lib/pear/PEAR/Command/Registry.php create mode 100644 src/www/lib/pear/PEAR/Command/Remote.php create mode 100644 src/www/lib/pear/PEAR/Common.php create mode 100644 src/www/lib/pear/PEAR/Config.php create mode 100644 src/www/lib/pear/PEAR/Dependency.php create mode 100644 src/www/lib/pear/PEAR/Downloader.php create mode 100644 src/www/lib/pear/PEAR/ErrorStack.php create mode 100644 src/www/lib/pear/PEAR/Exception.php create mode 100644 src/www/lib/pear/PEAR/Frontend/CLI.php create mode 100644 src/www/lib/pear/PEAR/Installer.php create mode 100644 src/www/lib/pear/PEAR/Packager.php create mode 100644 src/www/lib/pear/PEAR/Registry.php create mode 100644 src/www/lib/pear/PEAR/Remote.php create mode 100644 src/www/lib/pear/PEAR/RunTest.php create mode 100644 src/www/lib/pear/System.php create mode 100644 src/www/lib/root.php create mode 100644 src/www/lib/spmtable.py create mode 100644 src/www/lib/utils.php create mode 100644 src/www/lib/utils.php.orig create mode 100644 src/www/login.php create mode 100644 src/www/logout.php create mode 100644 src/www/opcoes.php create mode 100644 src/www/root.php create mode 100644 src/www/sky.php create mode 100644 src/www/status.php diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..e69de29b diff --git a/BUGS b/BUGS new file mode 100644 index 00000000..2c3ecae1 --- /dev/null +++ b/BUGS @@ -0,0 +1,15 @@ + +- versao 0.9 usa fits2jpeg proprio, mas o uts-www ainda inclui o antigo + +- filtro e foco: ao trocar o filtro, o controlador deve procurar qual a configuracao de foco adequado, focar, testar (configuravel), e se nao houver configuracao de foco, deve preparar uma e salvar no banco de configuracoes + +- configuracoes: necessário.. e muito... + + +- FITS headers + + +- controle de temperatura +- controle de eixos individuais +- NSEW control + diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..5b6e7c66 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..e69de29b diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..e69de29b diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..60e04734 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,45 @@ +include $(top_srcdir)/rules.make + +SUBDIRS = src contrib + +EXTRA_DIST = rules.make + +#install-exec-hook: + +# @echo "Creating UTS services..." + +# @if [ @UTS_DISTRO@ = "suse" ]; then \ +# /sbin/insserv spmd; \ +# /sbin/insserv secd; \ +# /sbin/insserv camd; \ +# /sbin/insserv teld; \ +# /sbin/insserv sbigcam; \ +# fi; + +# @if [ @UTS_DISTRO@ = "redhat" ]; then \ +# /sbin/chkconfig --del sbigcam; /sbin/chkconfig --add sbigcam; /sbin/chkconfig --level 345 sbigcam on; \ +# /sbin/chkconfig --del spmd; /sbin/chkconfig --add spmd; /sbin/chkconfig --level 345 spmd on; \ +# /sbin/chkconfig --del secd; /sbin/chkconfig --add secd; /sbin/chkconfig --level 345 secd on; \ +# /sbin/chkconfig --del camd; /sbin/chkconfig --add camd; /sbin/chkconfig --level 345 camd on; \ +# /sbin/chkconfig --del teld; /sbin/chkconfig --add teld; /sbin/chkconfig --level 345 teld on; \ +# fi; + +# @if [ @UTS_DISTRO@ = "debian" ]; then \ +# /usr/sbin/update-rc.d sbigcam defaults 90; \ +# /usr/sbin/update-rc.d spmd defaults 91; \ +# /usr/sbin/update-rc.d secd defaults 92; \ +# /usr/sbin/update-rc.d teld defaults 93; \ +# /usr/sbin/update-rc.d camd defaults 94; \ +# fi; + +# @if [ ! -d /var/log/uts ]; then \ +# @echo "Creating log directory... ( /var/log/uts )"; \ +# mkdir /var/log/uts; \ +# fi; + +# @if [ -x @PYTHON@ ]; then \ +# cd $(top_srcdir)/src/python; \ +# @PYTHON@ setup.py build; \ +# @PYTHON@ setup.py install; \ +# cd $(top_srcdir); \ +# fi; diff --git a/NEWS b/NEWS new file mode 100644 index 00000000..e69de29b diff --git a/README b/README new file mode 100644 index 00000000..d36447af --- /dev/null +++ b/README @@ -0,0 +1,7 @@ + +sh autogen.sh +./configure --prefix=/usr +make +make install + +(make install deve ser rodado como root) diff --git a/SConstruct b/SConstruct new file mode 100644 index 00000000..f8b0c11f --- /dev/null +++ b/SConstruct @@ -0,0 +1,22 @@ +#! /usr/bin/python + +# +# Main Sconscruct file for UTS +# +# Copyright(c) 2006 - P. Henrique Silva +# + +env = Environment() + +Export('env') + +SConscript('src/base/SConscript') +SConscript('src/sec/SConscript') + +SConscript('src/instruments/cam/SConscript') +SConscript('src/instruments/tel/SConscript') +SConscript('src/instruments/order/SConscript') + +SConscript('src/tools/client/SConscript') + + diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 00000000..0c7fea3b --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,35 @@ +# UTS_CHECK_DISTRO + +AC_DEFUN([UTS_CHECK_DISTRO], [ + + AC_MSG_CHECKING([which distro we are running on]) + + if test -a /etc/SuSE-release; then + + distro=suse + initconfig=/etc/sysconfig + + elif test -a /etc/redhat-release; then + + distro=redhat + initconfig=/etc/sysconfig + + + elif test -a /etc/debian_version; then + + distro=debian + initconfig=/etc/default + + else + distro=suse + fi + + AC_SUBST([UTS_DISTRO], $distro) + AC_SUBST([UTS_INITCONFIG_DIR], $initconfig) + + AC_MSG_RESULT([$distro]) + +]) + + + diff --git a/autogen.sh b/autogen.sh new file mode 100644 index 00000000..a3a4e8a1 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,21 @@ + +which aclocal > /dev/null || "You don't have aclocal in your PATH." + +echo -n "Running "; +aclocal --version | grep aclocal +aclocal + +echo -n "Running "; +autoheader --version | grep autoheader +autoheader + +echo -n "Running "; +autoconf --version | grep autoconf +autoconf + +echo -n "Running "; +automake --version | grep automake +automake -a + +echo "Now, run ./configure, then, make and make install." + diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..89011fdc --- /dev/null +++ b/configure.ac @@ -0,0 +1,97 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.59) +AC_INIT([uts], [1.0RC1], [henrique@astro.ufsc.br] ) +AC_CONFIG_SRCDIR([src/sec/main.c]) +AM_INIT_AUTOMAKE + +AC_DEFINE([PROGRAM_URL], ["http://www.astro.ufsc.br/"], [ ]) + +# Checks for programs. +AC_PROG_CXX +AC_PROG_CC +AC_PROG_CPP +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +AC_PROG_RANLIB + +# Python stuff +AM_PATH_PYTHON + +# which distro? +UTS_CHECK_DISTRO + +# Checks for header files. +AC_HEADER_DIRENT +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS([fcntl.h limits.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/socket.h sys/time.h termios.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_CONST +AC_TYPE_SIZE_T +AC_HEADER_TIME +AC_STRUCT_TM + +# Checks for library functions. +AC_FUNC_ERROR_AT_LINE +AC_REPLACE_FNMATCH +AC_FUNC_FORK +AC_FUNC_MALLOC +AC_FUNC_SELECT_ARGTYPES +AC_TYPE_SIGNAL +AC_CHECK_FUNCS([alarm floor memset pow select sqrt strcasecmp strchr strncasecmp strrchr]) + +# Checks for libraries. +AC_CHECK_LIB([m], [sin], [AC_DEFINE([HAVE_LM], [1], [Have C Math Library])]) +AC_CHECK_LIB([jpeg], [jpeg_set_quality], [AC_DEFINE([HAVE_JPEG], [1], [Have libjpeg])]) + +# outputs +AM_CONFIG_HEADER([config.h]) +AC_CONFIG_FILES([ + Makefile + src/Makefile + + src/drivers/Makefile + src/drivers/camfits.fake/Makefile + src/drivers/camfits.st4/Makefile + src/drivers/camfits.st7/Makefile + src/drivers/telgo.fake/Makefile + src/drivers/telgo.lx200/Makefile + src/drivers/telgo.paramount/Makefile + src/drivers/weather.wx200/Makefile + + src/sec/Makefile + + src/tools/Makefile + src/tools/client/Makefile + + src/config/Makefile + src/config/initscripts/Makefile + src/config/initscripts/redhat/Makefile + src/config/initscripts/redhat/uts + src/config/initscripts/suse/Makefile + src/config/initscripts/suse/uts + src/config/initscripts/debian/Makefile + src/config/initscripts/debian/uts + + src/python/Makefile + src/python/uts/Makefile + src/python/uts/core/Makefile + src/python/uts/instruments/Makefile + src/python/uts/interfaces/Makefile + + contrib/Makefile + contrib/bin/Makefile + contrib/bin/sbig_load_lpt + contrib/bin/sbig_load_usb + contrib/bin/sbig_load_usb_suse + contrib/fits2jpeg/Makefile + contrib/lib/Makefile]) + +AC_OUTPUT + + diff --git a/contrib/Makefile.am b/contrib/Makefile.am new file mode 100644 index 00000000..a2c4cede --- /dev/null +++ b/contrib/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = bin lib fits2jpeg + +EXTRA_DIST = include + diff --git a/contrib/bin/Makefile.am b/contrib/bin/Makefile.am new file mode 100644 index 00000000..5207ecd8 --- /dev/null +++ b/contrib/bin/Makefile.am @@ -0,0 +1,5 @@ +include $(top_srcdir)/rules.make + +dist_bin_SCRIPTS = Spm spmtable \ + sbig_load_usb sbig_load_usb_suse sbig_load_lpt \ + sbig_unload_lpt sbig_unload_usb diff --git a/contrib/bin/Spm b/contrib/bin/Spm new file mode 100755 index 0000000000000000000000000000000000000000..d1dffdfc11400298b9da145600b999314796d3aa GIT binary patch literal 22226 zcmcJ1eSB2Kx&Q2*lVpX31js5V>hcne8aBLIK&lBLKr}#1LO@WL&Awze>>K++5~Ugp zC^2|Tt+n?4XhmCFZSB2WZ|h4-YbsuWmfK71%e5EU(pK9|leQEsVztfverL|voN!Uw zKYr))nKSd9dFGjCo_S{GoSAdx?%Mk0Hk(bDmtAOrX!irUV?KOGjY^p>+@esFi|OKW z_(Cvkem7{~`VpopfYaeLxVPw#MmGszI;N3#zBHr>xS3@_&@sO#QR!8{RbST~bkEy` zSb{{B%XC1|a0T46x`XZ*Fdfrb2Au1! zu$Ho|SiKe@RgMhc264ny4e&BxH(FUJ#lJ8OA4X89R+vo%^+k$u+9PI@jWd76eD&!kN`VGKL{{`@S zz-%94wvn?>cg!{TPlrDh*yL9YTmj6sB|R0n1bC7`&$g@wW?h*6QMg-yP5#S(qri6< z>CMQu4Vd;o`ZaJ5K^}O(W5i;l@3y4VO7{Y9k?BH=L;BOe{YLq(!9M_O%6A*+jshPy z=-+`ap4T0;kqNNc9?X1pFRwEqu!RJ2>ZV-4TRO9EO_;%z6-0 z&I;h&2Ht~wOMriA;4b*}z#9zwSEQePPSw8z?e!w?tw?{(NWU653T(=^1M$s!O?{0M zzeD;qOZsNu-M|kU`ELW>3w#(p<@+rBr-6TEq~8z!0C0zCPe?xs{6};m(k}--0c_gC z8DOit4}xaCAwTWy4Wti&{+kB>3&3aFH%99BIMUxkI>#6Ce-hY$_^E_~+y z0i1cc5jYLE4e9KgOcxke(~(#*8cc7_q}9Nh&LrcV8PSx?X41mD?3UHltLkb*QzjA% zCbF3hUj%89IPM^9jcyj9R4~|-@NW)ggjdElB~y`jCM06M&1M2JCPJ}bOf>oY{$LUu zQbAv!wJQ@u5h3IfsenHbO{9c(Mg5wk)%D&r%a=FRHhY_^m)6&MnX1IOajUm6*cM4= zf~lIQFP%pIrjAr36BJEtLCWiGizVXTbjFv;c)hHipS8MHL=*A0_E-`^r~Jt-cmc>H zkS7@5Dnc@E%omL&{35OD;#VkSRHii&4~R61KxzUNh*E~Q2qjXRMLO;G#Y2)cO9*Gd zydzHL;Y2#q+7I;}DbZxGd6 zQD3*T#yi(DCm*7nsQJeRpCR5=48x37ac0U$I#X0Gbsx!ZlkqmpKICBLL_l6e`t(5|G9Nw1zh z2Kfg1m_VE9W3pUNAEWoJ^f6&>qK^qTKpzu+m_8mJqVyqAl0GKY41EaJNgtEzHu~sb z-SqJwwSzt;yIu4#@$RONNpcVUDVQ_pW0HP^J|@Av^zm@Ak3JqCzCa%n_mlM981v{) z6JkGoOv+!Qk3r{o`ehgc>Ej`!pFSQqUZ!6z#9{iFypPhygno=ZCYvA8$HUQU^ygrV zq(4`Pll1Y>^E!PDnmPKI42S4rvOGl}gVWmHH+qH~xd~8hF3Q_|P{NUeVtBzqPz;wX z!JX|iTvmlpn8AUQ!?? ziDwgcOMELaEn^@l@h0Lb;!P3{S4sRd@oU7hCEibblGrWr*NAh( zB@#bRe2Q2|e1P~Y@u|PE{{6)4^#dm*ewmn${sYG(K1^If+%NG_;>pDOB|b*%Cf+CU zkBG~O_elI2@oeI5iBAwOBu+|vlDLX^lfeE7S$CA{RHbVu%Ap@6sc>_OjXYM_S1#_b31K$~l}BL^i7`cL;q9T>Mn_NvEL;vK55Waq1yi+a!W^y{A|9)2|+ogI9ofTXq~ z2Yb(`zW3N{2nL>0*w$oT(lg`?GcxzRE$_bF^Kyw%&bpQ+q(J7r+Z=~#he)0K@q5F= zJNt`w{Q_MV{*F%~67mc$;5gboCifq#x&r0xw(!Zv!SylJuIFX95nX|3_vaCPKU-aq zUXHj;jN2+nC+FF19bxAIO*d&8CC$|EPcRyX`w^q4b5}z&Z@b7ngFLx!0py-%s0N{) zcWrmw-dF4FOX@FP{8`=c0w;s_-uz;=?y+v@t9zmNCjCqWbjZqZau}Z1b5PIy7%ciU z=ma#`U-#AM^p8U=gP(eAhsv5gI`~JKPj=RDKk|kS6>LJHC_*5_z&He287M>`z<{3n z9Rxe1&m?{B4;1p{lAZm%FBR`-g>-#YC4J(4$F6ZzFQ9#w5>(k9uO0#R$Cr)(?WV_T zfbJ^pTkGsw3K?e=?a9Ip{9HfFIP2_a9G&*0(4c6*o&g2`_(fEG{RU`Ws{45q z=2eX_i{-8zI)wIv=1XVhb|b*ukJlYmK+M5{op(J*Mr^0x^>W`|e2A-gPU>WGxAj>@o88D`B zm?%ZuLJ&*yKUDh`T2$m#BE9EjCtBRrZtp#qdtR}DeQ2F=$kMJqkbSGA-QI2+o@eHn zDf3KiFA48QT@Dp|4R_XgcpiEVqwE5KXAmg-N$x&LjBb;?yy1b`Qw=f?d7Ua@`JHBR z#)ZD)dXfh_yiQLPUKWQ5eB` zUmJW0^_An|kBZ2;nV-}_785(l}NdrienWxHuP$_o50h%UdBP9&w`|H zm6JAq$z33$dU(w zZ}q;m@pdm}i%Y|hY@iN8sd)*LK%WLp--tmF-n_mh&N}YC8fWiPC+?U?#`ZRp$Y~0? z?RBwcgQt-4d2$*&!7&meLX#AwWbj1;*BJOoIX_9=mY_#){Dgv0k(S&ZMH1>6b{6kA zfvUbz;HFtLa4c!bO*_NZffW?*`W+G*TGT*HrRIXj+9#_bhlk9i(9iHZsm0upKO6Zn zclK>`V(j>JlVW(xOeo&<889@4rk31U6tsTBoc>VXS`1F#bx46e)xWZg(lms6*J7x8 zwK4Zob{@);nbKg?q$PJ0+D0T4EY-^dcKX3L%uq}2DUb~Q+^AH&DMRNJvqCH-_mJWv zpBZIatFCw{L}NRtzG#Q$qy}4Z&61vd5Sn`Xt-Pl8o~xD~Oey~KOT`az4qW(BW(p@PT2520OLEPd)Xw9X zGD>4dbKQk9yztMQ##n|462lJ!U$07rabSwS#-wdT>@9t@4ovljK#tJJ{C=bY(1HTV?-@BVY&#>r@w?+^X~Wf$sB>#)p#n)e*id%nfY1-%FF`tCqALVNmT;vxNB06x<_P-APz zZAPxa!&GWNX83k{ZZUZFo-r*C;}V|I)Z4&J`3B`QM%-aM#<2V&2SL-+_?R1Sf<0%4 zzezvi>>1SY?x`Un`_}r6295cS`V%Zf<=T-~&ibt0sEY#+SM=xO0q*I6+NU_-6kVs`#`M5;v z#=2}gk#Wma+L35cmaZyGyBE950-~v5)$+Q=+I7|S^`b0L)`~qt_p+47T@&`Duqy7Z zMbH;Zwg#io3ipjEmir->JCb&1Anr(cEMG>cW%9J!sKohMaq$;R%kI{$4-~}hiwE4z zST^o*uktr0T7#)f+MNiwS64Sxd(O{U2~JhA-IA$9TgoT>t&sq#>T^#|h7<8%Ya+3E zI$5QIsjW~VTMDTt4%LdJK8QCJ*8$S)2DmN?D1Pg+m{MTiLtp6 zLvK?03;FzLXOxfD`G8U^vYU1-1(OY!4(3}l4YS9FN(bypZ86c}G6`(+P?YyCAPEDq zDrkCeD-3A5l#E6gN+n`a_z^)y^-Aa$wPOv_i5LQbK*Stk3|nLW^kvTBLFr^az?c;O zp!&yrU2Gn!h*Cl+pwzXS+dt>BFK<}oy?Je2^Hm>QdpuGl+*`6iGzyx*T8K{;&_T&n z$3&lGMYsAQQ5w9PCCL4Ot{?1c}rM=S}&CNM;84zicX0kOO+T>&Q6bh*Wek=l*jUykxi%F@@LkL9Q#$HIRk zKW57yH#`3h4%xW0R*09+?&E~Cd>!j$J|f7i0Dz_~{2Naz?0DXBz<7?F0AwKhWiBEr{F#f_q>7sAN)t*zF^=@c=RLw3LNR4 zHR6xLXZ&vr{Ac*&`xgW6L%x%6RiD!x`{2Z*x?>aE-;mx9-@Om(+i)l0+>ap)cM`4& zEA#u{UI!idUjQDIa{~NQIL347m=4>P^1|ac-wdQX3Ev6FZ47L0%5O8c3{{SAvarx9 z>hHx09sH;7(H%6D=~%hs>ns=M%x{PId^jJv<)UU7jIr&R|K6yg7qS4C!sqt~li{#E zD!-4y<7?veU09cZPkHdfornV?_zzutd}apIXW0CPh~bw|w^!kQ1$P?Gj_+Y6!cB*p z3%3-m5v~<31$PhJgK&?-Jp=a=+^cZEf;$ao$M+u-;ikjQgjozM5YZ}=PMeU0vA_&a1o$)HY_l&x$YEcty#wFAipUO$^m+uE;0hS9iNZ1n zq!$(8QA_9rjw~X-h~;}-tJNMxing4z%;@+W0wE~fpOUZcH?is(yKVTeA4=*fDM_AHgR+KASkq3mlE(zQy; z-e7Rmenk0hR%uR+pKR!js{9gR|E(-#gLW;pkn~&3QXDPR9-`cOKzoomf~Le@Wc@;l z(4pCx)}~5wY7@yMtWb$|4P}ZbRI1TL^>&qSvNl8sH!Cz%*zW`zhn~@z$u4C`p*=&C zR!A521AwLac5Nf`e$13-BlUTgB64Wsn0B{9PVIX{-G)5E{zLN9?^S$v8l?#5X+{^U z9=8f@QBY4bZZVWwu<9KIMPVxyU8G-t+R#bk)kNU|q}dAX3lQZfSfkmQRA=ziIN%km zW_k7*kbDCYorRZa*Fe_7sYGo1DQK6%OG_kz?ebeny}*7ia7p0|MLUfsPdi=FVt;c5 zD&{B{|0+`TqAQByfTH*)inA5}Gs>_RFG7q~`~dqIT62oLU{x=6N!$M3lt9(nbYMU}(ZJ@R>l;35OW^DQ`0L3x)=96Jzx>C>^AH z0o%(ye&9HGbDnv|XW@+5yN^bdN&iS|d@9LKPZ6x+%I> zq1j3=yOdt$qgpqjMC`g@ltnQ!i>kDVsAti~-#`pi{7a%e3hCNUi5^m{9NKY;_lQDH ztq=K&_9|4O@$1Z@&pjtaFO}w1^r$kK$=bcF$mdmKxFN!SqO6Ip;#TqDm5t`#rcL8xn+zW0K>?jR+gFpldG#w7aQz*I6*OPfIZI z2ax!<<`M)QMXoWVxne(KsC#^Z7ev@T=OxB` z9p&2!UH=4wu+Np{Eyb{HpZ5zU{+KxnT-9VbpAxvrS>**(ysLwW*Gef^3;RNr;7U`r zMJ(RM=C)r~4$e?JBlrP}{iB~`a4uP1zm>svaI@bagB;WCRofYRma40ku@919sSI*d zwAXODcU?d+mr1^-n5|X{wUz|(QNhK5!oET>yPZ<6lm+*ZwoWpePH|S3k=bP=kdvb8 zO=_Z%V}lD%H^SZ|OZ!g-n`LFY$ak%b-Al<@WbjL@?0Oj-M-CgMIIS$=b{Tw#!JrJ@ z&&r3TKE0GTDziPw;1(G?!6H(!Ze1)QD{C>p*sZb_capYC#_nP84jFqN2|gx+G&p4h2##>BA27@9<6$df^-|nRJh-Vrh*Qx7LURI}FR2*qItyk+zFHY^ z6YeFOa8ubBnm{6O-!OQUl$@p z;XEAYnSu5NPU`{a3^MYBD#J#!WcTeo6$iDbo3jto}W zK&&lL_m8o%O|X>|PQ+asf64f=@sl96eUi;KY3$g=W3L0y#tCgagLRV@qIq=ug*B;e z5=>&6sGC#?g2I^~7(4cQ1o1Vl=1@WWU1FQWc*W&{Vlfs3V80 zM#{o(J18nIMR7SYP6dY&7hQ_ry(_#}y9`Cz@~IK7WmdNFuHq`Q>6H8ztA;f+qpe}Y7qr? zepHHAJlkazmtD5*GW!KkG-CPct<6E?Vtb*A;XAQ*u|XCHR8-7bd>P)AY_=Oeq`-VX zF$(0{x;D**-1thLW%0FLn`~2<>pWanLE1KlQ`h+z1GV9edfr?w{{QguFA*6?b$B=P zmkF;ofED%)8VDbU}+lLh3QBecaLZbNRmaSBXp$`?=bpAkb5 z(5?Jug{p~p@fXeJQI(U@Woah)=vX!r?3D4N+plhQ!`fzWArHfVG6}ETEcRLo%z~F8 zRR)UpMSX^e1td?%BZcth8&2w%O7PPrlW8tkGZcv@WR5^K7V835(yU%Xqms#66Mnf1 zjj_S71#dE$5r7CH<_|uyNIRv`S=+u3 z;V35?3@Sk>=`dy&Zy4K1Q4TGBi@iV@Nn@-?j~c9C%Cbwy84kSoyi{W>5u0XKyQ-o2 z7O(1LraIHfNSwVz7H{;%CU1Rp6Z@eWGmNelN{@o59aU-akzG1JjG%`q}YTeUm7L&VWHKyJvkV)dHFS|wT2NaqjX%N|1|YQjxK zBYuwDvNyr!u&6Yp3R3-x6RtUpSjId#507qeMIRe^e1S=rCX;VNziH7*?ZyNjH79Rq ziO(7%x-=OmQ;?dJ-Nl&VAhKj3Jn61j#@7lzlTtp+O@*XsCxr)Jiv&H@OY3H3d~L!L z_NBwZ6X=Q~qw+H;;lbmqhnw*HJjx2ZNJ|Byq*sAtG$TA5u!M&Llkmu&aXqPo>7Vz=TlfU@h z!fOT`URm*Yju(FHcRG;ZM%cXYiYzbu9x2Z{ARYHb%R#pT&P=CI$Fl_RmL~aezw~Kj z*oQ>|`8s9L@OheN86aBHaetMs%+tx-xWHOoe9^_DcY;aW7meeEJA<%UF5~H%;kcK| zF!xirz%mb)#n8wR2_dbNlk41HZt{Ze*v^Oue5%@TK(y>jd@Zl@ZHE}Y+xZH;SDflK`J-*tl zzha~;l9=T_iZFFTe)9Yi&>cgV?ZJz56yr%alOMm*bYH3B5iKv1AAdVB>B>P@{yuci zTIl#6;B3&DJjl=tf73$8ef#;Kdzl2rMLN2EILgU-2z-3Z15dVD+Re>)T>k-QQ5QOi zvLTshN&CJf%(Nuv)-zySEQ{CA;H>-(gAPrSzs$7XpF{V1(8aL6VqPX4H;K(K(>5%? zxh$3lGsw+aGfcXj`0x2`ON1HZ*&$|_blVnU;e`R?GWF~>=-3x|?EzgqJC<>g2OYnU zkUBQvPgI{@KGIs$FV?Tbk`VY!Lpe5DyluYMHJaTFBgdHo!H`D4 zBP+%s0+vgYrvQyaXgDLvdTGLKg^*|3C_B+{Hj1p6K}Adra2bkGc6>!N!$_mYbPs$} ze>R*x!p}5J{n;=h(`HTm+hDmo`^MC#4LzzInEye;gDk}&4qyHRE%Alo4){N^l)neK z1pi^fJFh%@5kJ#tAIkF-u=!0G<>B~gmFHz(t31bu4SQmJ&(+6okZxYA{}2HG5@FAw z{C|YcUw%ydXJG!bYU1~R`LC6Ub#zqzbH|>={Cqy)-*_hG9L0YjOw0|(Zs5ZP{l&oj z7`MA6zEI2nuEHM_ivB`T0UUTxcbq~x>%%RWlSrqYY_QiFVE(Gfo zP7@*DKH$u!b;lnJ{3NjTY^iSmThEsI4zTrXsYAf#cVRs1hkHp?|ExQX!YBWq!?&I_ zbqd&e)|3qm!|{ajPQb}pY$zwb2Q$x}asf{To-gHttpis8vmbEuSO~lX*gSjcM&Q6B z_*PJ+J4F+4J<>UHlKvLpTY)hRNxTU-3QW6a`exv5z>5rgH?TQg@+_*Iz`HH!p9bCw zyv3k@6!>Z2ep!DZ{tfs5uqp3B;G@9D4f^i`^Ep7(|3di;XFZFGAGVwTy(y0aQ^b%3 zUk=RYI&gvfPH_eBdq_9Wruqo56W_-Ljq+{;o(jCiz)ipvz`G5+5qJskFAdC3BIv^UFlcFRuV^ zv!wqFcsKCF2EVs}_oF}DAoc4Me*@l&^uva}FM=FT1OLjPzY_QWuz8kMC9o6rHdCfM z#UkLNNH@>6x(WEyr_Al6YV$Cz~2Jq4j(+(AC4kmJ?rX6zz5KOdnCP6{0#W$!@A>32L26j1^Ag~ zVR8S`)PD)|Ux1EaJsayn;Cj%T?NQE-Vq9+cJb%!7HdYOA6!h;RjrFTLhkiZqsI#$* ztt@(cE5@#tA73n64ww>t{Bx7ZhC)d4dTX%f;H|G~YR0)!UhlF|sCK1yd1LjeTJO@@ z6?Lm6!HqR}>ZE73z&kTe;lfWdIDH9wCJAp_G|}pddgU9o*O%?YF-~}E_L}R|vb5UO z%SH+_fhx#EEXE9vaFws`k}E#EhV%&>VwJ$JL_)4y1%TxOxk!csSo?^IbTSs|KscNB zwFLz~^p?w-3~(Vt$bWgT?v;u0)tsx<@^~%$86!^t%YTC|j1SF4AQ-hoaxtE~pa=4W z3g}^N4Ij5c8R>+%5}L%KZA|_BAP~mFAdewS`NOHID)aQJ*@!vc31*|dEH@78vgVS% zZdfC*_2CV7j6V6yI1bE8#-Gcq7(X@OOfhQ=B*j_|7INS3+%)sZFl#2cJBHugur3+5 z#*dt?#?#8IDLkDl60pWtPGG|>Bv!GliHO19W20j7$D3JWQ6KZLGHbMPaF{$g%^GhU zv}TP$!J|)qlgFG{DXhoASxzOBN1FYgL(Z%jxjh@s z8P;VGsKeW=Bs`VPip_)TkTryj3K%3uiP0^CG=DV0B{6kWoHf6B7~6Tzz2n(>qlvKx zA&-O`iN$!KPKO(bntkp-FFJ zfByIR_~bL0`Ofb*zxmC~Z|0fjnR#Y!36z&P91dYFr*H|P=G)w!+3-`JSCrYpFLK2c zaiO>Xz7V9%ZUGNmH^Ou|aBesk+zC3+=*A#SM;c{ka|2Dl?U*J69n%Y+rWYZ+8+5q) zK=7X_WWJ<3LGnWZ{}0?Ax`V)U)Qfr0d60=`yW2zO2c|Q$84Y#SGa72AH`Fzyw-hGh zg|ie5q6KwZx^fNvt9+Oj^_mPf749N9#_>8^d4v9c1}Gnn{FG;|i3k+Hv2J)xfSUw2 z3T_GN{&NTt}=jiZw?Vk_-LO74cf2zl_nJdo(Y(DE}B-|Nrqv2Se<{D-K+Gm{% zheNZ7^WZLk8v%C~+*mj-+&DO1e))g!zYhV6cDOwopcM1l3ReQmdLw=WE(mp|zRLVkfU zpoV>au1Ka5HO*T^B2u$S)Wn;bA~h+oA(DzTZ4y!VNzoihB;pCtSk=%FuMx?3&BjPd zBqI%EOh%e&MUA3V*CCul3UI`mK}#i4@r}qS31K3ctf^{>%G?^8MKYF7)y6kBF{fBO znX2B}RMi+UNQRTmiMpm#R77hU;>idS)v4%ei%Hb=tMVJZ{doTy7hL}W`{N<`z$ z$R@meRT!*wP2qGhQVVt#6fsqaRJgIK4tk)tv4n^=r%|*`QIkm`B6FwiGWT$JLu0&2 zCk%_F<;xeBhG!KPXG1fyA=at$Px^C#%keQz-f;%fvxkvO-G|H=I;O4;Lz7z+ph)7}ae0&}<%k4CV{zWAH1X4@H;I$6&gQJ`As%J_h0=OGM<1Q&ujpgoTu=WTA!_O4W)P!~fwqA@ZUoKraU)34$IWC5eGGy((VvLEP9Hak zHu@MOchD~o;&%ENWOvfXAaxi0DMGBt{Jiz3$J&mnvEJgpOCju~eWHJk07w61H~ypA zMgL?ELNNw=kM!f3oKH+z?;(kO#B8G8Zi&Yd^H$KiTVmF9%un1UF>5>q4MguwiT%Vy z#4QpR5YHoSmUs$r3GsS~rx7nB4oX}^93(E0csB7m;v$LJQnB^Keu>#)F&3!TCvgdJ zGqI3(3Go)_Ot4 z60axTP23`JE%6@WW{G3O-Nfr9ZXiBL9F(}3_z-c4#3|y##6=R*QesDl{Swn+VivJa z;uhj##6sdW;#0)Oe$VpnAV&X0`6a%cn78BJLlW;K=Ix@lTjINj#}V%ap82cmGCym5 z_gHXsMXUh#gV>4hxILe*jQKm<9_tQdcw*;H*kUM1jl?zE_g#0f5FM@e0kn6goc)J7 zuKnnxeUbqAzh@W-tWOYvBTH?TI%e~$<}W7F5>>zveD_jo*c3|UfP#AscilJ zQT<3tOmMspEa%T?raoCl#K zIWHZ|I0fSZ*n<6Y9sxsaH&WL-t$Q$9weI)#ejnPR=>2p4o<#8=^xeRUo-^e!6j>&z zRH79aBE5gk9;SO4LaiA{*Sdd7{enuX;3FuvPb!zXsKk1xO25&5GLtS>}OQsL&#M5 z<{~cGcQy(q?W^-|EZ9THdWouyFR*+{l5DQd$cbyN3%|4vO(~nlFPp~``!R;@STDq` zwT@ua)g?+#l)ACH*Y?jDhkA;Ypx#iai!jvn&ceUvAzVZcwEqHG4i|ekwTiRUexD=tUe6hQho1^FVYeQalVX^9>i#eOllJtZ9O$C<-`A6 zZ(9SBm9_q1%*`!KEQQ`%3yo9x9yjb9S!@@K;gMbcD8NM`V|VMYOk$K((^kN zMOiQmSz7CH8p?KL#a#M3nM;|;Eq$kKTw1^NZ~q}}sqbo8YvXZ0XRRHE<;dz;4~=#8 z>^E^;s(R+32z}GCl2rD_SdOryPL;is{33<`V}feDLdiPTcru6fS{E#v_~NB!wxGIh z%6SG-QQPR2T^M{aKid0_yZ`YLkgN+K0Xp?Rz62;X{~>l=@>p-4Vi7dze|#C4e}}H$ z8St?T{b{s?WSphSda|_~ERNJDC|2+2KhqC{8r8!j$5M0<&<+HSQkere`@au%grvpG z&U9(c4-mKQ{7(0#y+@oKJ4;ZN9WBf7KjSRBbPkC(y(8H#ozqQD)RQ~*8|WOHK*eW2 zu?MkM9gH>;I4YYnJq8KMd(-Q!+l~rv+Z*U(&?5E$5?I4f#$5+N1+SC~`)KD7-`l~i zNbP#t@z44$%IJOmO#hR7kcoDtfj_Jh6|ckBpguRAww&1ip}U zHB3CSFXJw-K9b!6)ywQd)@2_^gTd&Z;jlv)^}>1&`kA5Qh4z!)_7hMEW$sTOtq-a^ z_M3#>_DUEonf^&K<%7vO32N^pD9f`{sO2*LM?|8){PMFeSXZ~>Pr3l zM^8h`{@o(;_VMq2@XtL<^8SEqhNp9pYIy2H-MG>WX0jrf0nDL)cKZ_ctFum;YRWSP z@zXw!HjVRQY<{KF`iJFU^Ggiby$_)UpKf9Kq1fkIF8!IQ_)rz#9@^1F`7qol8 z-Qzf~@1h<@e&56%ho^6Rk7GpN**%WoeP`S@rgQNN3(wh{)4902&v~0`hof{6>*&cI zhftQ92^{OVm&OW>>mAQ9t86iU!eV~jdJc+bTrjhi%SaZf^I19uJJuZQ_yQC67g=j{ z9r_@*!`;r<1HujE2Dka4-|1TLwijRof@QI-M<9#)yU^=)b>rXt-)3k(L%iMWI>-=j zH#Ns4oZQ4pms^t0Jh;Av-p7TsNeZ7CMRTmH1+*b)J~|9V%|dYfdC6@@4mULZ_)hI zH=&ET*Gl3GcH-0?_a&mwpH2BKMMScE$G=T|8|vLrg&{`X1qYRv)9CDdXdDc=eyp{I zO7(>L(|8nuAq6eJY{qf*j*e~FPOQ4{hh#0|&5ztiB=>PVVe=-L_VjP^S@+61_qD&7 z&Utne21OR!k$KyU1xn20QcOO?gtU-Hm5@;XrhID+L>$0egce+eTQjONQ|8GWGX*gn zksT$qru9ajkT+a*fEyqfHQ0J%zKP4XqAKnH?%R}D3W*0wJp>0zQBZm?c$9%Rr}>IG z+b1hS9clLW*2`KRXD9a_$dLi?TaRMgIgoRNouBYaNa}q)VsUd!!Ghf}GR3^8 zj?Pj?(Axi7sU^qXQ#GkD{@s$(dMeZhnDn-DLV)RfPRr#YJ+k#ODc{@1cW%(I(&}e` z&n@2e*AQ&EG~{i6$q+BZTsbqlzPsrEAO1#WoY~ix^DHO=t#oEBg_;*>oN1FW)ihMq!WpH2MSn+YLxf9_USHw+$-k`RF(1iJ2DB zyAJH_{V5OBeO2W5zerQfl@ z9f4PMabN8Z9BkcoaLg^ad^k&Afn4RiAoVM@EbkTPB1C)dmCvne)UVXkV(X)$=%BLO z_i|DdZQb_jFmL-ZP7(W4G{ef6Z1;sq14htrrUS~F$^>4)5Jf#-L2+J2-y)R7*^bwd zQt2(Q-fW4_Q-bV8kPnxbHz>`YfksO6^$1b(8PL3MB+Zh2%eqQ3sDgBA=3=W@g+i7C z+|tte%8bYjnrp5#5+OBiLNk&_z)E6_bQhrU@YDiJbJXV|HX#%FGjC)L*+y0Q^vx>@Gn&J)%)i7A4%l}wL5I-_Ki_9>MkQE&evP%gG z$uXu=)@RVVRZnn7L{)s$VL@Dz=6p z#5XiQ#KMybM>8r zQ6gxKLxFS)d++M@KH0r&;^Dytgi#qY*p6*JJ;vb{ox>-EbCHP}YW;g)=i8ChvkXa7 zMSDW8kX3`KkzeE6Je#SQ<~i&XjNTH{e_)jMCbWT?{G35|3GK&l?C*@xy@g_7Gp zFfYl@tMV~t*nU|X?wnmHVLJan4#V9~SX>u+y|NU(f>qX|+)WgA! zK)wUFxJ#_lvKi2(TXG%y=D&Z_+ZnsH$)~XZXJ^kyN!_Ul^#b$g5GLPyRm#kZ?cKL{ z@CsE9ni>7V+UPO1zJ$Zp6Tg5(VJbk|ximY%{RXj!qlEO_mdmDq&pCKs1#X+iho#%V!$$AL!FiPslajDh#}|KnfGa0>aEL%eJ(WDmGj?EoNp~e^?v;I35JvW^qT|o!0ksL zL06E*q~pPuy^FJR)7Gprluh1iUs1nWI;VcPSKYSOhg%|gJsZF%~%`F@D zAVohyscVsU>sF5=o#TCBU*9S`T6K6c2cbsh<-Nbm@$PE7o636c>~8B$O{;gt`q8v8 z|8rQ7u}+{iGcTeMqphbM={!tL@0Mfl@E0?O^z_zLDf^i07uR~M-y>Ov4=d7vz3u;qdPLdzbpMTihkk0w$=k*UGdbygfX10V$_{X5 z%jFAFzW%_`j=)jSaNnG_AbqT3+tHRQ7O1-|Ub6M5*BcHs&SpN?EslRT^YRC;;ifAG zYxPdif!A#-W#vMj%2*x7pP=;|B3k#4lXu$fB=&G*St?_F@&WI{)@{eo5s`n7Sdi%{ z>vMHT)x7Oj!HVU5&={JwGxoUj+|Fx#>P2|{*m_4Sj@+#EQfxlwSo$6LD3l*FQP`PK zZ^q?AHsn->tPww>c)VDk#o_HP(fiU9$uO!C<;6dc6b-rc#b zKb=Quy>nTcCv}?48&|f!mOkG1bLv`gzlGJr)>Hk@(#K+1pBw9!c;fC)pIC>kAbB|H z<2vUiY$C8OLJ6dbjFcC8B<{LS5xR19aM9{zBHdI~-4OAo;(oP7Wy<6v|E7)*eq0JH zRNm#2lOslm;EK?)MHK<~r44nFrj!72R_y}GiiahpS&?o?)ip!vimIAeT~ov#j3-hn zs<3M%;a|<{l3@HJ*ML5H>kL&h3VbGM6ZOe5CMO3m`RkJYrg+Mqh$NfiP1wbVmvj#OdGi=SI^m@pYhY(fdRo8^xe0O@P%l3I5p zPB%riG-G2=q_$z}L}WE$auiMZR$A+#AFtLqI*!hL#gI4S-m;39Q zq(4H0Hl|ADZZ9LRv1(%^ggsf2)B^wJSRFd4+-p{qqNFOnBKf0A&;rOBVo(3a^woRu$oU)DrqXY$7}L^P(8DSr|@r7pVFpNd8N%@{NMP3gw!NMeMzZgP#d zd2(?r%Tv)9N^DA?Bg~kbq}9aJDStfbZ;Uj?6I-PL4bJgX^l1%zS_A*DYv2TCD4&!k z_XvMdyovK+;c+aSACA*3;&W}d0O2VbGffe~vu&6!3I8`=U6?4|gz|8;m`lHHI?To2 z+jN-Azjx{|7l6O6!(0OXmJV|f_z@lEGVtR%d<5ZA9ex+#&*`v*@G2cXig2Y4A4B*$ z9X^3@tqz|;xKW3>l_#mg0&Cqj=&&2%FX*rb;T<}hkMK?%_96T=9UhBtmkx7N)k8Ww z9^uDyxB%g&b(mXpp4Z`N2=CM3B7}da!?O`Sq{H(N{;>}G5&l;l#?Dmn4#Gc&!^_G4 z)ff8=-@yIw)_*+%Mox!sXtE^l%r!&4nw2tAvZfDXs8- zX=&*K{}gpea_TRlYClZpBl`k z{pElEaKSNfJe<16tC?am#>N5?^fe8>Q~0q?uIRis@!rCF74KPhfq#3)P(RcItM-p#$@$n*ojf8p`=l zFAnEIP(02~*Q@ZIokUL5in|43WF*b@;B8S4-p; zP8Sr;&zX=n7CyFn;=haAtaHX6Qv7*8LOZ!u?P99;5zINiS0~Larh++6`7Kz^q`YfD zb#@Rb2@|<-kvYm_7pi0)kz0sl_&DrVP~Eu~jch|QF_Ko`7;oOlluwf%(`0p9{_uv^W^1m*H%p2Lt>K-{B(;=rA$YgBp;=C=0%3a48 z*Ja?zt$zS1pj6&z2*|y54B}iLfj4(!wBtr z@>Y_zECHH}CXu`CVTg538k@(jadWSHn1l$cp((C>invvwX|6O<pkk-rI62c z5zGCpUrQ-trFrJ=Qsz0%wU7mQK((u1oZ}cu@(kmpfJqKhW_TLdPmi6P1hKv|i zF$6U+(|(TNX)y4 z!B!cJGuS2ty-h*wGRV7|^Hyq|cRj1;i<0+y4DOH&50W<{gVV@+yVPnOgPoJ9m5U5t z;%J_?oXPHx!mecrc1mGKsOy)d1U@r5@07fcG58f3Tf<~`$=Ky8eaii+jOCU| zr|fQd?=kl4lA)ck-;luwv$$XKUd`ZlWbiAL`+%&HJDBVtsaY3eAC{WUCGU4-?3E0D zPsWaC@CQ=v18k%xcyr0y!(>nH#11{Ldoru`%cMQUfiG_zY0tHy8g@GKNNz3A`K=^0pzL+{czmUYd4Z1!y^Q*fH{c&fq09LAHy?nI}u| zb(AY-zBEG~%aOC79kjC#Bdz0{dzev%m71f0T}Y~ zE=O<}f*uw0oNbda{46CShY3dv2!y$43Z!F32jSU#M@~VS@en$S+As&DkJl^mB)9KO z#D6BA2}kEZ7o62V5^g^iNiCd^!L;RO>b*G{ zSK#33CrfK2?Q}*;@F7VNEBfV4dJC>B#PE;07U2BKnV!ax>O&De5@#cx!x^=?%uk-`B3MY z<(M#S!i2RGoWoItXaqjtxh_QH0%xv@;bV_0PqWq5PMbD;!2~Ccl5p@D#VP*BHD>qL z8uPKzb%q1^JCtsGzH|+DXpB)AD)C(;$TxZN-R>Poh&soUSMjWH3deS&>KY?5u(Wj5 z%F6Kam8Gjz1j_@f0|*A!tOiGIRc&oTvJ9k>?F1=xsc;mhlTadCPI4t0>Kfwuc3HwQ!le&;I=gJg~Ra?SMkw|T%HXKdF8^NWruTI4`*Jvs6 zXjJkhlXW+!8bNP_C}YmZEg7wAsA)poG{ zlni4ViB=)ac7bxbDRi}&c2MaW;#Dk0ytNd$EwiKeOs4j5gl(ETvY!z>E_ugVAYjK zV~Wf8rFKJZ!qIU`$>e6#h%MdnmDpOe5`$`>qC$?RxP^1Lk}Mmvy~#4_u%sd{d)e$p zfPoYC00!MKOINO{2q@_x#AfSQH9un5-&9GINM#TlUd@1H>sf^zH!!rHA(NT~c& zLKI@YQiwf!NsM%ZKp3<{q=Eb@(A<-m zeuT}%2R>di;W&XKAJ=aAm{S60==ACMegSiC$;UO_7!vG+V_L9F9?oB`fWuT#@^KCL z6cTgo*IXvJ6cL!}#F;PGZp-it$p=l7FXQP}!*RWrVXpb!3%(MB?fC)NqYl0cg_}Jg2NCnVexC4a@OEVU9#<=}=+3)WF5@Kw<8$ACivOYf?LVs-tmfyeVIO@B} zmI3ilej8bq|G%r>P?|EfKP@=!K42c-WzwImhHy|-|L_mNQ-vHc$;Kj=BKzRwch1LoJ=20sbRJ*5Ue$MpEJ+u;4c{IJ{T_cH1FqWsr};J1fh zE*+VR{Hzatj@*Js*2iy$(7CO_T;$I~#{9|@gA4f2!b+B*s`fi~2$uYN2l#n*p1COR zvqSLgA@~a5kG|seBv4;G8@mj6=U4H3p>a7d_f|6*R#U|15!my$IJ+L0{9M4}NnM7{ z70DvpzcOA;9yz3~onYPnX++X}gry0qnQY?;+OX&1h)wP2lIhf&KJa zzumw`Z2V6FmmvSiQXY=!1r7pVukjned`^G|X`{r?5GdMZ&PLA{Tu)yQer|PhE`2{#IZCF4N`v7BIheF!CM*e&joD&oZ64MNP+BM&jRz5JHx*p*ls^R0p_v5MLPX^ zz&t(A@c$P02>MgGrhf#?ZyF5Ug(>-cdcI0~^aAsIVQ$`He;!K={ZGsEcrOBc>|5%7 zNBRO_yFORiupjzY0v92@v6mPyzu7SPHvyN}=r;qK`TQC9g@rcYAn4{S|9gPf+xQ;^ z=Jy&#-s8YaY-jsF4b1N_480q;8RA-%_P5qSs7Xfo`2+Lmq%x`-P|24n^`uRi*xCH#hK5qc- z{wBUTkox3{Tgb03Km4x%2Pq$6%6|m70rx-Cza9tPZ_Do);O4vCo?lD;e9;ZeZ-Iyf|e?}%8)H?dd;^7BpV<$vbt)5o`snDy+1B&DX; zHC-;Ms_Ul97t??4#@vtRg3T$EinrWYFXt<2EV=*Xmd5|@HoknKwpjD$UI41fx9Cup zzh`0l`3J7XCsXNY6r^yt6zjy{^5vn`;V{sWK`5{+TvoAYMIgL5uypxK$?&<-EO&EZ zkwDAhqycWvXYtxO5TTO0(l}iRPGeBiz5VJAi z0jctJzm)oyZ2_+IC!4wA(GWqU$WP_r&(w?XgCR{3R8Lc6Gq+>R5?Cy2#1C(Tlnx&T9-h~EX5sG$0cY`qt1$863JX_6EPb_$8MM! zfwk3{5C{)@(&=Fz3ccA-7GB|O5#o=GP@hDksp(INS>Ct#Pqd9n!sB#B4yO*uu#dQt^ZrxGYGeEc6ahY4UM z20wyQQeuAAq-fv(f+b;jgn(3CwQN}ayh?b*SYA4)hGgyP0}v>)vDq$DDq@eo zc7t$i^PocNUzf3S>EC3rN2$X;>@oVJ1#^l57As*5ShGr0ZRMh%K6*j@N{u}oI$bhu zR@tQmq3G&J1^rtwShknKg#2lk;0kHwx(&c9Pit5bmZvjB@vAZF2!iYn+Sv1A>)Wxi zQh82;`cWJ^QT@D*Jw~7SAafcx$3V!FAEZTb#}fLUJ&ieeV&G?Z>{Rn}H}*JrhD3;l zCDgCos9*iDv&i4du}7%0DvZskjw|~!Ow81!tv#^Di%w{(UUpoXqdvJ}pnI6ZF|h8Q zOlo!Hul9WGcYN&XnErz-DA{HOT>jKz)DQjGlPOL4t3~!$)%NN~hX&$j@~4To&d%Xr zAi7AO>7fn_(Pwsm(+nJTPSf`^S^eUXok1PlA^)?-*-82o4S8pZ*VrkjMb#SW_m%9V za73+QFD+lS7%TFt%F054)#25P7MBOYxXnZcl|yzK9vx$spbiiTs?$WUD$n1BveQEe zIfP}8AjzIyB1fD7PTuQmEu-yW@U{pPQT`+o*UjS_2hw6_VsGOSEBuM7L4?2(j>TAE zkJ_Wvk3!jFvL~=`*I3pf*qo6;<>A0C!k^m|u83^luT~{W8>*5?tbq^u)u{o=xGAY0 ipn{4V7v!DEc4muGTU$ByIM{ztio<2|)&W^X;y(fMrVCgA literal 0 HcmV?d00001 diff --git a/contrib/fits2jpeg/FITS2jpeg.c b/contrib/fits2jpeg/FITS2jpeg.c new file mode 100644 index 00000000..b5e412dc --- /dev/null +++ b/contrib/fits2jpeg/FITS2jpeg.c @@ -0,0 +1,390 @@ +/*-----------------------------------------------------------------------*/ +/* Copyright (C) 1996 */ +/* Associated Universities, Inc. Washington DC, USA. */ +/* This program is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU General Public License as */ +/* published by the Free Software Foundation; either version 2 of */ +/* the License, or (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public */ +/* License along with this program; if not, write to the Free */ +/* Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, */ +/* MA 02139, USA. */ +/* */ +/* Correspondence concerning FITS2jpeg should be addressed as follows: */ +/* Internet email: bcotton@nrao.edu. */ +/* Postal address: William Cotton */ +/* National Radio Astronomy Observatory */ +/* 520 Edgemont Road */ +/* Charlottesville, VA 22903-2475 USA */ +/*-----------------------------------------------------------------------*/ +#include "jpegsubs.h" +#include "fitsio.h" +#include +#include +#include +#include +#include + +/* global variables */ +char *infile; /* input FITS file name */ +char *outfile; /* output jpeg file name */ +float *DataArray=NULL; /* image data as 1-D array */ +float fblank; /* undefined pixel value */ +float vmax,vmin; /* Max. and Min. values to be displayed */ +int NonLinear; /* if true then nonlinear transfer fn. */ +int quality; /* jpeg quality factor [1-100] */ +int GotMaxMin; /* if true then already have max/min to display */ +int naxis; /* number of axes */ +long inaxes[7]; /* dimensions of axes */ +fitsfile *fptr; /* cfitsio i/o file pointer */ + +/* internal prototypes */ +void jpgfin (int argc, char **argv, int *ierr); +void Usage(void); +void jpegim (int *ierr); +void gethed (int *ierr); +void gtinfo (int *ierr); +void getdat (int *ierr); +int get_histogram_region(float* img, int size, float start, float end, int* startValue, int* endValue); +/*----------------------------------------------------------------------- */ +/* Program to convert a FITS image to a jpeg file */ +/*----------------------------------------------------------------------- */ +int main ( int argc, char **argv ) +{ + int iret; +/* Startup */ + jpgfin (argc, argv, &iret); + if (iret!=0) return iret; +/* Convert to jpeg */ + jpegim(&iret); + return iret; +} /* end of main */ + +void jpgfin (int argc, char **argv, int *ierr) +/*----------------------------------------------------------------------- */ +/* Parse control info from command line */ +/* Input: */ +/* argc Number of arguments from command line */ +/* argv Array of strings from command line */ +/* Output: */ +/* ierr Error code: 0 => ok */ +/* infile FITS file name */ +/* NonLinear True if nonlinear function desired. */ +/* vmax, vmin Max and min values(image units) to be displayed */ +/*----------------------------------------------------------------------- */ +{ + int ax; + char *arg; + +/* copyright to get it into the executable */ +char *copyright="Copyright 1996 NRAO/AUI"; + + /* Set undefined value */ + fblank = 1.234567e25; + vmax = fblank; + vmin = fblank; + infile = NULL; + outfile = NULL; + NonLinear = 0; + quality = 100; + + if (argc<=1) Usage(); /* must have arguments */ +/* parse command line */ + for (ax=1; ax ok */ +/*----------------------------------------------------------------------- */ +{ + int iptr, nrow, lrow, irow, i, iln, nx, ny, donon, lname; + + *ierr = 0; +/* Open */ + if ( fits_open_file(&fptr, infile, READONLY, ierr) ) { + fprintf(stderr,"ERROR opening input FITS file %s \n", infile); + *ierr = 1; + return; + } +/* Get header information */ + gethed (ierr); + if (*ierr!=0) { + fprintf(stderr,"ERROR getting FITS file header info \n"); + return; + } +/* Read FITS image */ + getdat (ierr); + if (*ierr!=0) { + fprintf(stderr,"ERROR getting image pixel values \n"); + return; + } +/* Close FITS file */ + fits_close_file (fptr, ierr); +/* Initialize output */ + nx = inaxes[0]; + ny = inaxes[1]; + jpgini (outfile, nx, ny, vmax, vmin, NonLinear, quality, ierr); + if (*ierr!=0) { + fprintf(stderr,"error %d initializing jpeg output \n", + *ierr); + return; + } + +/* Write, loop over image */ +/* write backwards to get right side up */ + lrow = inaxes[0]; + nrow = inaxes[1]; + iptr = (nrow-1) * lrow; + irow = nrow; + for (i=0;i ok */ +/*----------------------------------------------------------------------- */ +{ + int bitpix, simple, extend, maxdim=7; + long pcount, gcount; + + fits_read_imghdr (fptr, maxdim, &simple, &bitpix, &naxis, inaxes, + &pcount, &gcount, &extend, ierr); + if (*ierr!=0) { + fprintf(stderr,"fits_read_imghdr error %d reading FITS header \n", *ierr); + return; + } +/* Max/min if necessary */ + gtinfo (ierr); + if (*ierr!=0) { + fprintf(stderr,"gtinfo error %d reading FITS header \n", *ierr); + return; + } +} /* end gethed */ + +void gtinfo (int *ierr) +/*----------------------------------------------------------------------- */ +/* Read FITS header info from INUNIT and save in common */ +/* Inputs in common: */ +/* fptr Input FITS fitsio unit number */ +/* GotMaxMin If true already have Max and Min values */ +/* Output: */ +/* ierr Error code: 0 => ok */ +/* Output in common: */ +/* vmax Maximum image value */ +/* vmin Minimum image value */ +/* GotMaxMin If true already have Max and Min values */ +/*----------------------------------------------------------------------- */ +{ + char commnt[81]; + float tmax=0.0, tmin=0.0; + int GotMax=0, GotMin=0; +/* Read keyword values */ + fits_read_key_flt (fptr, "DATAMAX", &tmax, commnt, ierr); + GotMax = (*ierr==0); + if (*ierr==202) *ierr = 0; + fits_read_key_flt (fptr, "DATAMIN", &tmin, commnt, ierr); + GotMin = (*ierr==0); + if (*ierr==202) *ierr = 0; + if (*ierr!=0) { + fprintf(stderr,"ERROR reading input FITS header \n"); + return; + } + if (GotMaxMin) { +/* Don't put vmax,vmin outside */ +/* of actual range */ + if (GotMin && (vmintmax)) vmax = tmax; + } + else if (GotMin && GotMax) { + GotMaxMin = 1; + vmax = tmax; + vmin = tmin; + } +*ierr = 0; /* OK */ +} /* end gtinfo */ + +void getdat (int *ierr) +/*----------------------------------------------------------------------- */ +/* Read FITS file and determine max. and min. values */ +/* Inputs in common: */ +/* fptr Input FITS fitsio unit number */ +/* Output: */ +/* ierr I Error code: 0 => ok */ +/* Output in common: */ +/* vmax Maximum image value */ +/* vmin Minimum image value */ +/* GotMaxMin If true already have Max and Min values */ +/*----------------------------------------------------------------------- */ +{ + long size,incs[7]={1,1,1,1,1,1,1},blc[7]={1,1,1,1,1,1,1},trc[7]; + int group=0, i, anyf; + float tmax,tmin; + int s_max, s_min; + +/* How big is the array? */ + size = inaxes[0]; + size = size * inaxes[1]; +/* allocate floating array */ + if (DataArray) free(DataArray); /* free any old allocations */ + DataArray = (float*)malloc(sizeof(float)*size); + if (!DataArray) { /* cannot allocate */ + fprintf(stderr,"Cannot allocate memory for image array \n"); + *ierr = 1; + return; + } +/* Take all of image */ + trc[0] = inaxes[0]; + trc[1] = inaxes[1]; +/* but only first plane */ + for (i=2;itmax) tmax = DataArray[i]; */ +/* if (DataArray[i]tmax) vmax = tmax; */ +/* } */ +/* else { /\* set to full range *\/ */ +/* GotMaxMin = 1; */ +/* vmax = tmax; */ +/* vmin = tmin; */ +/* } */ +} /* end getdat */ + +int get_histogram_region(float* img, int size, float start, float end, int* startValue, int* endValue) { + + int startTarget, endTarget; + int default_max = (2<<15) -1; + int sum = 0; + int i; + int* histo = (int *)calloc(default_max, sizeof(int)); + + startTarget = (start/100) * size; + endTarget = (end/100) * size; + + for (i = 0; i < size; i++) { + histo[(int)truncf(img[i])]++; + } + +/* printf("%d %f %f %d %d\n", size, start, end, startTarget, endTarget); */ + +/* for (i = 0; i < default_max; i++) { */ +/* printf("%d\n", histo[i]); */ +/* } */ + + for (i = 0; i < default_max; i++) { + + sum += histo[i]; + + if ( (sum < startTarget) && (startTarget < (sum + histo[i+1])) ) + *startValue = i; + + if ( (sum < endTarget) && (endTarget < (sum + histo[i+1])) ) + *endValue = i; + + } + + return 1; + +} diff --git a/contrib/fits2jpeg/LICENSE b/contrib/fits2jpeg/LICENSE new file mode 100644 index 00000000..48f57956 --- /dev/null +++ b/contrib/fits2jpeg/LICENSE @@ -0,0 +1,343 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your freedom +to share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free software--to +make sure the software is free for all its users. This General Public +License applies to most of the Free Software Foundation's software and +to any other program whose authors commit to using it. (Some other Free +Software Foundation software is covered by the GNU Library General +Public License instead.) You can apply it to your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, and +can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based on +the Program, the distribution of the whole must be on the terms of this +License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of a +storage or distribution medium does not bring the other work under the +scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; + or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your cost + of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to control +compilation and installation of the executable. However, as a special +exception, the source code distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies the +executable. + +If distribution of executable or object code is made by offering access +to copy from a designated place, then offering equivalent access to copy +the source code from the same place counts as distribution of the source +code, even though third parties are not compelled to copy the source +along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt otherwise +to copy, modify, sublicense or distribute the Program is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this License +will not have their licenses terminated so long as such parties remain +in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and all +its terms and conditions for copying, distributing or modifying the +Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further restrictions +on the recipients' exercise of the rights granted herein. You are not +responsible for enforcing compliance by third parties to this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new +versions of the General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Program does not specify a version +number of this License, you may choose any version ever published by the +Free Software Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the +author to ask for permission. For software which is copyrighted by the +Free Software Foundation, write to the Free Software Foundation; we +sometimes make exceptions for this. Our decision will be guided by the +two goals of preserving the free status of all derivatives of our free +software and of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, +EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE +ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH +YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL +NECESSARY SERVICING, REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR +DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL +DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM +(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED +INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF +THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR +OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type + `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items--whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program +`Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications +with the library. If this is what you want to do, use the GNU Library +General Public License instead of this License. + diff --git a/contrib/fits2jpeg/Makefile.am b/contrib/fits2jpeg/Makefile.am new file mode 100644 index 00000000..741bed95 --- /dev/null +++ b/contrib/fits2jpeg/Makefile.am @@ -0,0 +1,6 @@ +include $(top_srcdir)/rules.make + +bin_PROGRAMS = fits2jpeg + +fits2jpeg_SOURCES = FITS2jpeg.c jpegsubs.c jpegsubs.h +fits2jpeg_LDADD = -lm -lcfitsio -ljpeg diff --git a/contrib/fits2jpeg/jpegsubs.c b/contrib/fits2jpeg/jpegsubs.c new file mode 100644 index 00000000..1533ea1a --- /dev/null +++ b/contrib/fits2jpeg/jpegsubs.c @@ -0,0 +1,216 @@ +/*--------------------------------------------------------------------*/ +/*; Copyright (C) 1996 */ +/*; Associated Universities, Inc. Washington DC, USA. */ +/*; */ +/*; This program is free software; you can redistribute it and/or */ +/*; modify it under the terms of the GNU General Public License as */ +/*; published by the Free Software Foundation; either version 2 of */ +/*; the License, or (at your option) any later version. */ +/*; */ +/*; This program is distributed in the hope that it will be useful, */ +/*; but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/*; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/*; GNU General Public License for more details. */ +/*; */ +/*; You should have received a copy of the GNU General Public */ +/*; License along with this program; if not, write to the Free */ +/*; Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, */ +/*; MA 02139, USA. */ +/*; */ +/*; Correspondence should be addressed as follows: */ +/*; Internet email: bcotton@nrao.edu. */ +/*; Postal address: William Cotton */ +/*; National Radio Astronomy Observatory */ +/*; 520 Edgemont Road */ +/*; Charlottesville, VA 22903-2475 USA */ +/*--------------------------------------------------------------------*/ +#include "jpegsubs.h" + +/* JPEG compress routines */ + +/* global values for jpeg I/O; allows 1 at a time */ +int nx, ny; /* size of image */ +int nonlinear; /* if true use nonlinear mapping */ +float vmax, vmin; /* max and min unscaled values */ +char *name=NULL; /* name of output jpeg file */ +JSAMPROW idata=NULL; /* buffer for scaled version of row */ +FILE *outfile=NULL; /* output jpeg stream pointer */ +struct jpeg_compress_struct cinfo; /* jpeg compress structure */ +struct jpeg_error_mgr jerr; /* jpeg error handler structure */ +JSAMPROW row_pointer[1]; /* pointer to a single row */ + + +void jpgini (char *iname, int inx, int iny, float ivmax, float ivmin, + int nonLin, int quality, int *ierr) +/*--------------------------------------------------------------------*/ +/* Initializes i/o to jpeg output routines */ +/* Inputs: */ +/* iname Name of output file */ +/* ilname Length of name */ +/* inx number of columns in image */ +/* iny Number of rows in image */ +/* ivmax Maximum image value (values larger get this value) */ +/* ivmin Minimum image value (values smaller get this value) */ +/* nonLin if > 0.0 use non linear function */ +/* quality jpeg quality factor [1-100] */ +/* Output: */ +/* ierr 0.0 => OK */ +/*--------------------------------------------------------------------*/ +{ + int lname, i; + + /* get values */ + nx = inx; + ny = iny; + lname = strlen(iname); + vmax = ivmax; + vmin = ivmin; + nonlinear = nonLin; + + /* file name */ + if (name) free(name); + name = (char*)malloc(lname+1); + if (name == NULL) { + fprintf(stderr, "can't allocate file name"); + *ierr = 1; /* set error return */ + return; /* failed */ + } + for (i=0;i100) quality = 100; + jpeg_set_quality (&cinfo, quality, TRUE); /* set quality factor */ + + /* initialize compression */ + jpeg_start_compress(&cinfo, TRUE); + + *ierr = 0; /* OK */ +} /* end of jpgini */ + +void jpgwri (float *data, float blank, int *ierr) +/*--------------------------------------------------------------------*/ +/* Write row of a jpeg image, floating values scaled and converted */ +/* as specified to jpgini. Grayscale only. */ +/* Only does gray scale at present */ +/* Pure black reserved for blanked pixels. */ +/* Inputs: */ +/* data floating values */ +/* blank value of undefined pixel */ +/* Output: */ +/* ierr 0.0 => OK */ +/*--------------------------------------------------------------------*/ +{ + int i, icol, maxcolor=255; + float val, irange, c1, c2; + double arg; + + /* scaling parameters */ + /* + irange = vmax - vmin; + if (fabs(irange)<1.0e-25) + irange = 1.0; + else + irange = 1.0 / irange; + + c1 = (maxcolor - 1.0) * irange; + c2 = vmin * c1 - 0.5; + */ + + // get vmax and vmin from image histogram + + if(nonlinear) { + c1 = maxcolor / log10(vmax - vmin + 1.0); + } else { + c1 = maxcolor / (vmax - vmin + 1.0); + } + + /* convert row */ + for (i=0;ivmax) val=vmax; + if (nonlinear) {/* nonlinear */ + icol = log10(val - vmin + 1) * c1; + //arg = ((val-vmin) * irange); + //icol = 0.5 + ((maxcolor-1.0) * log10(arg)); + } + else /* Linear */ + icol = (val - vmin + 1) * c1; + //icol = c1 * val - c2; + + if (icol<1) icol = 1; /* minimum color = 1 */ + if (icol>=maxcolor) icol = maxcolor-1; + idata[i] = GETJSAMPLE(icol); + } + } /* end loop over row pixels */ + + row_pointer[0] = (JSAMPROW)idata; /* set row pointer */ + + /* compress/write row */ + jpeg_write_scanlines(&cinfo, row_pointer, 1); + + *ierr = 0; /* OK */ +} /* end of jpgwri */ + +void jpgclo (int *ierr) +/*--------------------------------------------------------------------*/ +/* Close/flush i/o to jpeg image file */ +/* Output: */ +/* ierr 0.0 => OK */ +/*--------------------------------------------------------------------*/ +{ + /* finish compression/ flush output */ + jpeg_finish_compress(&cinfo); + + /* close output file */ + if (outfile) fclose(outfile); + outfile = NULL; + + jpeg_destroy_compress(&cinfo); /* delete jpeg structures */ + + /* delete file structures: file name */ + if (name) free(name); + /* row buffer */ + if (idata) free(idata); + + *ierr = 0; /* OK */ +} /* end of jpgclo */ + diff --git a/contrib/fits2jpeg/jpegsubs.h b/contrib/fits2jpeg/jpegsubs.h new file mode 100644 index 00000000..41f0e733 --- /dev/null +++ b/contrib/fits2jpeg/jpegsubs.h @@ -0,0 +1,46 @@ +/*--------------------------------------------------------------------*/ +/*; Copyright (C) 1996 */ +/*; Associated Universities, Inc. Washington DC, USA. */ +/*; */ +/*; This program is free software; you can redistribute it and/or */ +/*; modify it under the terms of the GNU General Public License as */ +/*; published by the Free Software Foundation; either version 2 of */ +/*; the License, or (at your option) any later version. */ +/*; */ +/*; This program is distributed in the hope that it will be useful, */ +/*; but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/*; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/*; GNU General Public License for more details. */ +/*; */ +/*; You should have received a copy of the GNU General Public */ +/*; License along with this program; if not, write to the Free */ +/*; Software Foundation, Inc., 675 Massachusetts Ave, Cambridge, */ +/*; MA 02139, USA. */ +/*; */ +/*; Correspondence should be addressed as follows: */ +/*; Internet email: bcotton@nrao.edu. */ +/*; Postal address: William Cotton */ +/*; National Radio Astronomy Observatory */ +/*; 520 Edgemont Road */ +/*; Charlottesville, VA 22903-2475 USA */ +/*--------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include "jpeglib.h" +#ifndef JPEGSUBS_H +#define JPEGSUBS_H + +/* includes for JPEG routines */ + +/* initialize jpeg output file */ +void jpgini (char *name, int nx, int ny, float vmax, float vmin, + int nonLin, int quality, int *ierr); +/* write row in jpeg image */ +void jpgwri (float *data, float blank, int *ierr); +/* close jpeg image */ +void jpgclo (int *ierr); + +#endif /* JPEGSUBS_H */ diff --git a/contrib/include/Makefile.am b/contrib/include/Makefile.am new file mode 100644 index 00000000..05eac403 --- /dev/null +++ b/contrib/include/Makefile.am @@ -0,0 +1,3 @@ +EXTRA_DIST = sbigcam.h drvrsmem.h fitsio.h rdcolor.h setproto.h xstdlib.h \ + csbigimg.h fitsio2.h longnam.h sbigudrv.h sockets.h xtdio.h + diff --git a/contrib/include/csbigcam.h b/contrib/include/csbigcam.h new file mode 100644 index 00000000..264f8aea --- /dev/null +++ b/contrib/include/csbigcam.h @@ -0,0 +1,113 @@ +/* + + csbigcam.h - Contains the interface to the csbigcam + camera class + + 1. This software (c)2004 Santa Barbara Instrument Group. + 2. This free software is provided as an example of how + to communicate with SBIG cameras. It is provided AS-IS + without any guarantees by SBIG of suitability for a + particular purpose and without any guarantee to be + bug-free. If you use it you agree to these terms and + agree to do so at your own risk. + 3. Any distribution of this source code to include these + terms. + +*/ +#ifndef _CSBIGCAM_ +#define _CSBIGCAM_ + +#ifndef _PARDRV_ + #include "sbigudrv.h" +#endif + +#ifndef _CSBIGIMG_ + #include "csbigimg.h" +#endif + +#include +using namespace std; + +typedef enum {RELAY_XPLUS, RELAY_XMINUS, RELAY_YPLUS, RELAY_YMINUS } CAMERA_RELAY; +typedef enum {SBDF_LIGHT_ONLY, SBDF_DARK_ONLY, SBDF_DARK_ALSO } SBIG_DARK_FRAME; + +class CSBIGCam { +private: + PAR_ERROR m_eLastError; + PAR_COMMAND m_eLastCommand; + short m_nDrvHandle; + CAMERA_TYPE m_eCameraType; + CCD_REQUEST m_eActiveCCD; + double m_dExposureTime; + unsigned short m_uReadoutMode; + ABG_STATE7 m_eABGState; + +public: + // Constructors/Destructors + CSBIGCam(); + CSBIGCam(OpenDeviceParams odp); + CSBIGCam(SBIG_DEVICE_TYPE dev); + ~CSBIGCam(); + void Init(); + + // Error Reporting Routines + PAR_ERROR GetError(); + string GetErrorString(); + string GetErrorString(PAR_ERROR err); + PAR_COMMAND GetCommand(); + + // Accessor Functions + double GetExposureTime(void) { return m_dExposureTime; } + void SetExposureTime(double exp) { m_dExposureTime = exp; } + CCD_REQUEST GetActiveCCD(void) { return m_eActiveCCD; } + void SetActiveCCD(CCD_REQUEST ccd) { m_eActiveCCD = ccd; } + unsigned short GetReadoutMode(void) { return m_uReadoutMode; } + void SetReadoutMode(unsigned short rm) { m_uReadoutMode = rm; } + CAMERA_TYPE GetCameraType(void) { return m_eCameraType; } + ABG_STATE7 GetABGState(void) { return m_eABGState; } + void SetABGState(ABG_STATE7 abgState) { m_eABGState = abgState; } + + // Driver/Device Routines + PAR_ERROR OpenDriver(); + PAR_ERROR CloseDriver(); + PAR_ERROR OpenDevice(OpenDeviceParams odp); + PAR_ERROR CloseDevice(); + PAR_ERROR GetDriverInfo(DRIVER_REQUEST request, GetDriverInfoResults0 &gdir); + + // High-Level Exposure Related Commands + PAR_ERROR GrabImage(CSBIGImg *pImg, SBIG_DARK_FRAME dark); + + // Low-Level Exposure Related Commands + PAR_ERROR StartExposure(SHUTTER_COMMAND shutterState); + PAR_ERROR EndExposure(void); + PAR_ERROR IsExposureComplete(MY_LOGICAL &complete); + PAR_ERROR StartReadout(StartReadoutParams srp); + PAR_ERROR EndReadout(void); + PAR_ERROR ReadoutLine(ReadoutLineParams rlp, MY_LOGICAL darkSubtract, unsigned short *dest); + PAR_ERROR DumpLines(unsigned short noLines); + + //Temperature Related Commands + PAR_ERROR GetCCDTemperature(double &ccdTemp); + PAR_ERROR SetTemperatureRegulation(MY_LOGICAL enable, double setpoint); + PAR_ERROR QueryTemperatureStatus(MY_LOGICAL &enabled, double &ccdTemp, + double &setpointTemp, double &percentTE); + + // Control Related Commands + PAR_ERROR ActivateRelay(CAMERA_RELAY relay, double time); + PAR_ERROR AOTipTilt(AOTipTiltParams attp); + PAR_ERROR CFWCommand(CFWParams cfwp, CFWResults &cfwr); + + // General Purpose Commands + PAR_ERROR EstablishLink(void); + string GetCameraTypeString(void); + + // Utility functions + MY_LOGICAL CheckLink(void); + unsigned short DegreesCToAD(double degC, MY_LOGICAL ccd = TRUE); + double ADToDegreesC(unsigned short ad, MY_LOGICAL ccd = TRUE); + + // Allows access directly to driver + PAR_ERROR SBIGUnivDrvCommand(short command, void *Params, void *Results); +}; + +#endif /* #ifndef _CSBIGCAM_ */ diff --git a/contrib/include/csbigimg.h b/contrib/include/csbigimg.h new file mode 100644 index 00000000..3d7bf720 --- /dev/null +++ b/contrib/include/csbigimg.h @@ -0,0 +1,157 @@ +/* + + csbigimg.h - Contains the definition of the interface to + the SBIG Image Class + + 1. This software (c)2004 Santa Barbara Instrument Group. + 2. This free software is provided as an example of how + to communicate with SBIG cameras. It is provided AS-IS + without any guarantees by SBIG of suitability for a + particular purpose and without any guarantee to be + bug-free. If you use it you agree to these terms and + agree to do so at your own risk. + 3. Any distribution of this source code to include these + terms. + +*/ +#ifndef _CSBIGIMG_ +#define _CSBIGIMG_ + +#ifndef _PARDRV_ + #include "sbigudrv.h" +#endif + +#include +#include +using namespace std; + +/* + + Exposure State Field Defines + +*/ +#define ES_ABG_MASK 0x0003 +#define ES_ABG_UNKNOWN 0x0000 +#define ES_ABG_LOW 0x0001 +#define ES_ABG_CLOCKED 0x0002 +#define ES_ABG_MID 0x0003 + +#define ES_ABG_RATE_MASK 0x00C0 +#define ES_ABG_RATE_FIXED 0x0000 +#define ES_ABG_RATE_LOW 0x0040 +#define ES_ABG_RATE_MED 0x0080 +#define ES_ABG_RATE_HI 0x00C0 + +#define ES_DCS_MASK 0x000c +#define ES_DCS_UNKNOWN 0x0000 +#define ES_DCS_ENABLED 0x0004 +#define ES_DCS_DISABLED 0x0008 + +#define ES_DCR_MASK 0x0030 +#define ES_DCR_UNKNOWN 0x0000 +#define ES_DCR_ENABLED 0x0010 +#define ES_DCR_DISABLED 0x0020 + +#define ES_AUTOBIAS_MASK 0x0100 +#define ES_AUTOBIAS_ENABLED 0x0100 +#define ES_AUTOBIAS_DISABLED 0x0000 + + +typedef enum { SBIF_COMPRESSED, SBIF_UNCOMPRESSED } SBIG_IMAGE_FORMAT; +typedef enum {SBFE_NO_ERROR, SBFE_OPEN_ERROR, SBRE_CLOSE_ERROR, SBFE_READ_ERROR, SBFE_WRITE_ERROR, + SBFE_FORMAT_ERROR, SBFE_MEMORY_ERROR } SBIG_FILE_ERROR; + +class CSBIGImg { +private: + int m_nHeight, m_nWidth; // image size in pixels + unsigned short *m_pImage; // pointer to image data + time_t m_imageStartTime; // time that light exposure started + double m_dCCDTemperature; // CCD Temp at start of exposure + double m_dExposureTime; // Exposure time in seconds + double m_dTrackExposure; // Exposure when tracking + double m_dEachExposure; // Snapshot time in seconds + double m_dFocalLength; // Lens/Telescope Focal Length in inches + double m_dApertureArea; // Lens/Telescope Aperture Are in Sq-Inches + double m_dResponseFactor; // Magnitude Calibration Factor + double m_dPixelHeight, m_dPixelWidth; // Pixel Dimensions in mm + double m_dEGain; // Electronic Gain, e-/ADU + unsigned short m_uBackground, m_uRange; // Display Background and Range + unsigned short m_uNumberExposures; // Number of exposures co-added + unsigned short m_uSaturationLevel; // Pixels at this level are saturated + unsigned short m_uPedestal; // Image Pedestal + unsigned short m_uExposureState; // Exposure State + unsigned short m_uReadoutMode; // Camera Readout Mode use to acquire image + string m_cImageNote; // Note attached to image + string m_cObserver; // Observer name + string m_cHistory; // Image History string of modification chars + string m_cFilter; // Filter name imaged through + string m_cSoftware; // Software App Name and Version + string m_cCameraModel; // Model of camera used to acquire image + +public: + /* Constructors/Destructor */ + CSBIGImg(); + CSBIGImg(int height, int width); + ~CSBIGImg(); + void Init(); + + /* Accessor Functions */ + int GetHeight() {return m_nHeight;} + int GetWidth() {return m_nWidth;} + unsigned short *GetImagePointer() {return m_pImage;} + void SetImageStartTime(time_t startTime){m_imageStartTime = startTime;} + time_t GetImageStartTime(void) {return m_imageStartTime;} + void SetCCDTemperature(double temp) {m_dCCDTemperature = temp;} + double GetCCDTemperature(void) {return m_dCCDTemperature;} + void SetExposureTime(double exp) {m_dExposureTime = exp;} + double GetExposureTime(void) {return m_dExposureTime;} + void SetEachExposure(double exp) {m_dEachExposure = exp;} + double GetEachExposure(void) {return m_dEachExposure;} + void SetFocalLength(double fl) {m_dFocalLength = fl;} + double GetFocalLength(void) {return m_dFocalLength;} + void SetApertureArea(double ap) {m_dApertureArea = ap;} + double GetApertureArea(void) {return m_dApertureArea;} + void SetResponseFactor(double resp) {m_dResponseFactor = resp;} + double GetResponseFactor(void) {return m_dResponseFactor;} + void SetPixelHeight(double ht) {m_dPixelHeight = ht;} + double GetPixelHeight(void) {return m_dPixelHeight;} + void SetPixelWidth(double wd) {m_dPixelWidth = wd;} + double GetPixelWidth(void) {return m_dPixelWidth;} + void SetEGain(double gn) {m_dEGain = gn;} + double GetEGain(void) {return m_dEGain;} + void SetBackground(unsigned short back) {m_uBackground = back;} + unsigned short GetBackground(void) {return m_uBackground;} + void SetRange(unsigned short range) {m_uRange = range;} + unsigned short GetRange(void) {return m_uRange;} + void SetSaturationLevel(unsigned short sat) {m_uSaturationLevel = sat;} + unsigned short GetSaturationLevel(void) {return m_uSaturationLevel;} + void SetNumberExposures(unsigned short no) {m_uNumberExposures = no;} + unsigned short GetNumberExposures(void) {return m_uNumberExposures;} + void SetTrackExposure(double exp) {m_dTrackExposure = exp;} + double GetTrackExposure(void) {return m_dTrackExposure;} + void SetReadoutMode(unsigned short rm) {m_uReadoutMode = rm;} + unsigned short GetReadoutMode(void) {return m_uReadoutMode;} + void SetPedestal(unsigned short ped) {m_uPedestal = ped;} + unsigned short GetPedestal(void) {return m_uPedestal;} + void SetExposureState(unsigned short es) {m_uExposureState = es;} + unsigned short GetExposureState(void) {return m_uExposureState;} + void SetImageNote(string str) {m_cImageNote = str;} + string GetImageNote(void) {return m_cImageNote;} + void SetObserver(string str) {m_cObserver = str;} + string GetObserver(void) {return m_cObserver;} + void SetHistory(string str) {m_cHistory = str;} + string GetHistory(void) {return m_cHistory;} + void SetCameraModel(string str) {m_cCameraModel = str;} + string GetCameraModel(void) {return m_cCameraModel;} + + /* File IO Routines */ + SBIG_FILE_ERROR SaveImage(const char *pFullPath, SBIG_IMAGE_FORMAT fmt); + + /* Utility Functions */ + MY_LOGICAL AllocateImageBuffer(int height, int width); + void CreateSBIGHeader(char *pHeader, MY_LOGICAL isCompressed); + int CompressSBIGData(unsigned char *pCmpData, int imgRow); + void IntelCopyBytes(unsigned char *pRevData, int imgRow); +}; + +#endif /* #ifndef _CSBIGIMG_ */ diff --git a/contrib/include/drvrsmem.h b/contrib/include/drvrsmem.h new file mode 100644 index 00000000..0e3953b3 --- /dev/null +++ b/contrib/include/drvrsmem.h @@ -0,0 +1,178 @@ +/* S H A R E D M E M O R Y D R I V E R + ======================================= + + by Jerzy.Borkowski@obs.unige.ch + +09-Mar-98 : initial version 1.0 released +23-Mar-98 : shared_malloc now accepts new handle as an argument +*/ + + +#include /* this is necessary for Solaris/Linux */ +#include +#include + +#ifdef _AIX +#include +#else +#include +#endif + + /* configuration parameters */ + +#define SHARED_MAXSEG (16) /* maximum number of shared memory blocks */ + +#define SHARED_KEYBASE (14011963) /* base for shared memory keys, may be overriden by getenv */ +#define SHARED_FDNAME ("/tmp/.shmem-lockfile") /* template for lock file name */ + +#define SHARED_ENV_KEYBASE ("SHMEM_LIB_KEYBASE") /* name of environment variable */ +#define SHARED_ENV_MAXSEG ("SHMEM_LIB_MAXSEG") /* name of environment variable */ + + /* useful constants */ + +#define SHARED_RDONLY (0) /* flag for shared_(un)lock, lock for read */ +#define SHARED_RDWRITE (1) /* flag for shared_(un)lock, lock for write */ +#define SHARED_WAIT (0) /* flag for shared_lock, block if cannot lock immediate */ +#define SHARED_NOWAIT (2) /* flag for shared_lock, fail if cannot lock immediate */ +#define SHARED_NOLOCK (0x100) /* flag for shared_validate function */ + +#define SHARED_RESIZE (4) /* flag for shared_malloc, object is resizeable */ +#define SHARED_PERSIST (8) /* flag for shared_malloc, object is not deleted after last proc detaches */ + +#define SHARED_INVALID (-1) /* invalid handle for semaphore/shared memory */ + +#define SHARED_EMPTY (0) /* entries for shared_used table */ +#define SHARED_USED (1) + +#define SHARED_GRANUL (16384) /* granularity of shared_malloc allocation = phys page size, system dependent */ + + + + /* checkpoints in shared memory segments - might be omitted */ + +#define SHARED_ID_0 ('J') /* first byte of identifier in BLKHEAD */ +#define SHARED_ID_1 ('B') /* second byte of identifier in BLKHEAD */ + +#define BLOCK_REG (0) /* value for tflag member of BLKHEAD */ +#define BLOCK_SHARED (1) /* value for tflag member of BLKHEAD */ + + /* generic error codes */ + +#define SHARED_OK (0) + +#define SHARED_ERR_MIN_IDX SHARED_BADARG +#define SHARED_ERR_MAX_IDX SHARED_NORESIZE + + +#define DAL_SHM_FREE (0) +#define DAL_SHM_USED (1) + +#define DAL_SHM_ID0 ('D') +#define DAL_SHM_ID1 ('S') +#define DAL_SHM_ID2 ('M') + +#define DAL_SHM_SEGHEAD_ID (0x19630114) + + + + /* data types */ + +/* BLKHEAD object is placed at the beginning of every memory segment (both + shared and regular) to allow automatic recognition of segments type */ + +typedef union + { struct BLKHEADstruct + { char ID[2]; /* ID = 'JB', just as a checkpoint */ + char tflag; /* is it shared memory or regular one ? */ + int handle; /* this is not necessary, used only for non-resizeable objects via ptr */ + } s; + double d; /* for proper alignment on every machine */ + } BLKHEAD; + +typedef void *SHARED_P; /* generic type of shared memory pointer */ + +typedef struct SHARED_GTABstruct /* data type used in global table */ + { int sem; /* access semaphore (1 field): process count */ + int semkey; /* key value used to generate semaphore handle */ + int key; /* key value used to generate shared memory handle (realloc changes it) */ + int handle; /* handle of shared memory segment */ + int size; /* size of shared memory segment */ + int nprocdebug; /* attached proc counter, helps remove zombie segments */ + char attr; /* attributes of shared memory object */ + } SHARED_GTAB; + +typedef struct SHARED_LTABstruct /* data type used in local table */ + { BLKHEAD *p; /* pointer to segment (may be null) */ + int tcnt; /* number of threads in this process attached to segment */ + int lkcnt; /* >=0 <- number of read locks, -1 - write lock */ + long seekpos; /* current pointer position, read/write/seek operations change it */ + } SHARED_LTAB; + + + /* system dependent definitions */ + +#ifndef HAVE_FLOCK_T +typedef struct flock flock_t; +#define HAVE_FLOCK_T +#endif + +#ifndef HAVE_UNION_SEMUN +union semun + { int val; + struct semid_ds *buf; + unsigned short *array; + }; +#define HAVE_UNION_SEMUN +#endif + + +typedef struct DAL_SHM_SEGHEAD_STRUCT DAL_SHM_SEGHEAD; + +struct DAL_SHM_SEGHEAD_STRUCT + { int ID; /* ID for debugging */ + int h; /* handle of sh. mem */ + int size; /* size of data area */ + int nodeidx; /* offset of root object (node struct typically) */ + }; + + /* API routines */ + +#ifdef __cplusplus +extern "C" { +#endif + +void shared_cleanup(void); /* must be called at exit/abort */ +int shared_init(int debug_msgs); /* must be called before any other shared memory routine */ +int shared_recover(int id); /* try to recover dormant segment(s) after applic crash */ +int shared_malloc(long size, int mode, int newhandle); /* allocate n-bytes of shared memory */ +int shared_attach(int idx); /* attach to segment given index to table */ +int shared_free(int idx); /* release shared memory */ +SHARED_P shared_lock(int idx, int mode); /* lock segment for reading */ +SHARED_P shared_realloc(int idx, long newsize); /* reallocate n-bytes of shared memory (ON LOCKED SEGMENT ONLY) */ +int shared_size(int idx); /* get size of attached shared memory segment (ON LOCKED SEGMENT ONLY) */ +int shared_attr(int idx); /* get attributes of attached shared memory segment (ON LOCKED SEGMENT ONLY) */ +int shared_set_attr(int idx, int newattr); /* set attributes of attached shared memory segment (ON LOCKED SEGMENT ONLY) */ +int shared_unlock(int idx); /* unlock segment (ON LOCKED SEGMENT ONLY) */ +int shared_set_debug(int debug_msgs); /* set/reset debug mode */ +int shared_set_createmode(int mode); /* set/reset debug mode */ +int shared_list(int id); /* list segment(s) */ +int shared_uncond_delete(int id); /* uncondintionally delete (NOWAIT operation) segment(s) */ + +int smem_init(void); +int smem_shutdown(void); +int smem_setoptions(int options); +int smem_getoptions(int *options); +int smem_getversion(int *version); +int smem_open(char *filename, int rwmode, int *driverhandle); +int smem_create(char *filename, int *driverhandle); +int smem_close(int driverhandle); +int smem_remove(char *filename); +int smem_size(int driverhandle, OFF_T *size); +int smem_flush(int driverhandle); +int smem_seek(int driverhandle, OFF_T offset); +int smem_read(int driverhandle, void *buffer, long nbytes); +int smem_write(int driverhandle, void *buffer, long nbytes); + +#ifdef __cplusplus +} +#endif diff --git a/contrib/include/fitsio.h b/contrib/include/fitsio.h new file mode 100644 index 00000000..332664fe --- /dev/null +++ b/contrib/include/fitsio.h @@ -0,0 +1,1557 @@ +/* Version Info: This file is distributed with version 2.490 of CFITSIO */ + +/* The FITSIO software was written by William Pence at the High Energy */ +/* Astrophysic Science Archive Research Center (HEASARC) at the NASA */ +/* Goddard Space Flight Center. */ +/* + +Copyright (Unpublished--all rights reserved under the copyright laws of +the United States), U.S. Government as represented by the Administrator +of the National Aeronautics and Space Administration. No copyright is +claimed in the United States under Title 17, U.S. Code. + +Permission to freely use, copy, modify, and distribute this software +and its documentation without fee is hereby granted, provided that this +copyright notice and disclaimer of warranty appears in all copies. + +DISCLAIMER: + +THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, +EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, +ANY WARRANTY THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY +IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE, AND FREEDOM FROM INFRINGEMENT, AND ANY WARRANTY THAT THE +DOCUMENTATION WILL CONFORM TO THE SOFTWARE, OR ANY WARRANTY THAT THE +SOFTWARE WILL BE ERROR FREE. IN NO EVENT SHALL NASA BE LIABLE FOR ANY +DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, INDIRECT, SPECIAL OR +CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, OR IN ANY WAY +CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, +CONTRACT, TORT , OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY +PERSONS OR PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED +FROM, OR AROSE OUT OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR +SERVICES PROVIDED HEREUNDER." + +*/ + +#ifndef _FITSIO_H +#define _FITSIO_H + +#include + +#if defined(linux) || defined(__APPLE__) +# include /* apparently needed on debian linux systems */ +#endif /* to define off_t */ + +#include /* apparently needed to define size_t with gcc 2.8.1 */ +#include /* needed for LLONG_MAX and INT64_MAX definitions */ + +/* Define the datatype for variables which store file offset values. */ +/* The new 'off_t' datatype should be used for this purpose, but some */ +/* older compilers do not recognize this type, in which case we use 'long' */ +/* instead. Note that _OFF_T is defined (or not) in stdio.h depending */ +/* on whether _LARGEFILE_SOURCE is defined in sys/feature_tests.h */ +/* (at least on Solaris platforms using cc) */ + +/* Debian systems require the 2nd test, below, */ +/* i.e, "(defined(linux) && defined(__off_t_defined))" */ +#if defined(_OFF_T) || (defined(linux) && defined(__off_t_defined)) || defined(_MIPS_SZLONG) || defined(__APPLE__) || defined(_AIX) +# define OFF_T off_t +#else +# define OFF_T long +#endif + +/* typedef the 'LONGLONG' data type to the intrinsice 8-byte integer type */ + +#if defined(HAVE_LONGLONG) || defined(__APPLE__) + typedef long long LONGLONG; +# ifndef HAVE_LONGLONG +# define HAVE_LONGLONG 1 +# endif +#elif defined(_MSC_VER) /* Windows PCs; Visual C++, but not Borland C++ */ + typedef __int64 LONGLONG; +# ifndef HAVE_LONGLONG +# define HAVE_LONGLONG 1 +# endif +#else + typedef long LONGLONG; /* intrinsic 8-byte integer not supported */ +#endif + +/* The following exclusion if __CINT__ is defined is needed for ROOT */ +#ifndef __CINT__ +#include "longnam.h" +#endif + +/* global variables */ + +#define FLEN_FILENAME 1025 /* max length of a filename */ +#define FLEN_KEYWORD 72 /* max length of a keyword (HIERARCH convention) */ +#define FLEN_CARD 81 /* length of a FITS header card */ +#define FLEN_VALUE 71 /* max length of a keyword value string */ +#define FLEN_COMMENT 73 /* max length of a keyword comment string */ +#define FLEN_ERRMSG 81 /* max length of a FITSIO error message */ +#define FLEN_STATUS 31 /* max length of a FITSIO status text string */ + +#define TBIT 1 /* codes for FITS table data types */ +#define TBYTE 11 +#define TSBYTE 12 +#define TLOGICAL 14 +#define TSTRING 16 +#define TUSHORT 20 +#define TSHORT 21 +#define TUINT 30 +#define TINT 31 +#define TULONG 40 +#define TLONG 41 +#define TINT32BIT 41 /* used when returning datatype of a column */ +#define TFLOAT 42 +#define TLONGLONG 81 +#define TDOUBLE 82 +#define TCOMPLEX 83 +#define TDBLCOMPLEX 163 + +#define TYP_STRUC_KEY 10 +#define TYP_CMPRS_KEY 20 +#define TYP_SCAL_KEY 30 +#define TYP_NULL_KEY 40 +#define TYP_DIM_KEY 50 +#define TYP_RANG_KEY 60 +#define TYP_UNIT_KEY 70 +#define TYP_DISP_KEY 80 +#define TYP_HDUID_KEY 90 +#define TYP_CKSUM_KEY 100 +#define TYP_WCS_KEY 110 +#define TYP_REFSYS_KEY 120 +#define TYP_COMM_KEY 130 +#define TYP_CONT_KEY 140 +#define TYP_USER_KEY 150 + + +#define INT32BIT int /* 32-bit integer datatype. Currently this */ + /* datatype is an 'int' on all useful platforms */ + /* however, it is possible that that are cases */ + /* where 'int' is a 2-byte integer, in which case */ + /* INT32BIT would need to be defined as 'long'. */ + +#define BYTE_IMG 8 /* BITPIX code values for FITS image types */ +#define SHORT_IMG 16 +#define LONG_IMG 32 +#define LONGLONG_IMG 64 +#define FLOAT_IMG -32 +#define DOUBLE_IMG -64 + /* The following 2 codes are not true FITS */ + /* datatypes; these codes are only used internally */ + /* within cfitsio to make it easier for users */ + /* to deal with unsigned integers. */ +#define SBYTE_IMG 10 +#define USHORT_IMG 20 +#define ULONG_IMG 40 + +#define IMAGE_HDU 0 /* Primary Array or IMAGE HDU */ +#define ASCII_TBL 1 /* ASCII table HDU */ +#define BINARY_TBL 2 /* Binary table HDU */ +#define ANY_HDU -1 /* matches any HDU type */ + +#define READONLY 0 /* options when opening a file */ +#define READWRITE 1 + +/* adopt a hopefully obscure number to use as a null value flag */ +/* could be problems if the FITS files contain data with these values */ +#define FLOATNULLVALUE -9.11912E-36F +#define DOUBLENULLVALUE -9.1191291391491E-36 + +/* Image compression algorithm types */ +#define MAX_COMPRESS_DIM 6 +#define RICE_1 11 +#define GZIP_1 21 +#define PLIO_1 31 +#define HCOMPRESS_1 41 + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define CASESEN 1 /* do case-sensitive string match */ +#define CASEINSEN 0 /* do case-insensitive string match */ + +#define GT_ID_ALL_URI 0 /* hierarchical grouping parameters */ +#define GT_ID_REF 1 +#define GT_ID_POS 2 +#define GT_ID_ALL 3 +#define GT_ID_REF_URI 11 +#define GT_ID_POS_URI 12 + +#define OPT_RM_GPT 0 +#define OPT_RM_ENTRY 1 +#define OPT_RM_MBR 2 +#define OPT_RM_ALL 3 + +#define OPT_GCP_GPT 0 +#define OPT_GCP_MBR 1 +#define OPT_GCP_ALL 2 + +#define OPT_MCP_ADD 0 +#define OPT_MCP_NADD 1 +#define OPT_MCP_REPL 2 +#define OPT_MCP_MOV 3 + +#define OPT_MRG_COPY 0 +#define OPT_MRG_MOV 1 + +#define OPT_CMT_MBR 1 +#define OPT_CMT_MBR_DEL 11 + +typedef struct /* structure used to store table column information */ +{ + char ttype[70]; /* column name = FITS TTYPEn keyword; */ + long tbcol; /* offset in row to first byte of each column */ + int tdatatype; /* datatype code of each column */ + OFF_T trepeat; /* repeat count of column; number of elements */ + double tscale; /* FITS TSCALn linear scaling factor */ + double tzero; /* FITS TZEROn linear scaling zero point */ + long tnull; /* FITS null value for int image or binary table cols */ + char strnull[20]; /* FITS null value string for ASCII table columns */ + char tform[10]; /* FITS tform keyword value */ + long twidth; /* width of each ASCII table column */ +}tcolumn; + +#define VALIDSTRUC 555 /* magic value used to identify if structure is valid */ + +typedef struct /* structure used to store basic FITS file information */ +{ + int filehandle; /* handle returned by the file open function */ + int driver; /* defines which set of I/O drivers should be used */ + int open_count; /* number of opened 'fitsfiles' using this structure */ + char *filename; /* file name */ + int validcode; /* magic value used to verify that structure is valid */ + OFF_T filesize; /* current size of the physical disk file in bytes */ + OFF_T logfilesize; /* logical size of file, including unflushed buffers */ + int lasthdu; /* is this the last HDU in the file? 0 = no, else yes */ + OFF_T bytepos; /* current logical I/O pointer position in file */ + OFF_T io_pos; /* current I/O pointer position in the physical file */ + int curbuf; /* number of I/O buffer currently in use */ + int curhdu; /* current HDU number; 0 = primary array */ + int hdutype; /* 0 = primary array, 1 = ASCII table, 2 = binary table */ + int writemode; /* 0 = readonly, 1 = readwrite */ + int maxhdu; /* highest numbered HDU known to exist in the file */ + int MAXHDU; /* dynamically allocated dimension of headstart array */ + OFF_T *headstart; /* byte offset in file to start of each HDU */ + OFF_T headend; /* byte offest in file to end of the current HDU header */ + OFF_T nextkey; /* byte offset in file to beginning of next keyword */ + OFF_T datastart;/* byte offset in file to start of the current data unit */ + int tfield; /* number of fields in the table (primary array has 2 */ + long origrows; /* original number of rows (value of NAXIS2 keyword) */ + long numrows; /* number of rows in the table (dynamically updated) */ + OFF_T rowlength; /* length of a table row or image size (bytes) */ + tcolumn *tableptr; /* pointer to the table structure */ + OFF_T heapstart; /* heap start byte relative to start of data unit */ + long heapsize; /* size of the heap, in bytes */ + + /* the following elements are related to compressed images */ + int request_compress_type; /* requested image compression algorithm */ + long request_tilesize[MAX_COMPRESS_DIM]; /* requested tiling size */ + int request_rice_nbits; /* requested noise bit parameter value */ + + int compressimg; /* 1 if HDU contains a compressed image, else 0 */ + char zcmptype[12]; /* compression type string */ + int compress_type; /* type of compression algorithm */ + int zbitpix; /* FITS data type of image (BITPIX) */ + int zndim; /* dimension of image */ + long znaxis[MAX_COMPRESS_DIM]; /* length of each axis */ + long tilesize[MAX_COMPRESS_DIM]; /* size of compression tiles */ + long maxtilelen; /* max number of pixels in each image tile */ + long maxelem; /* maximum length of variable length arrays */ + + int cn_compressed; /* column number for COMPRESSED_DATA column */ + int cn_uncompressed; /* column number for UNCOMPRESSED_DATA column */ + int cn_zscale; /* column number for ZSCALE column */ + int cn_zzero; /* column number for ZZERO column */ + int cn_zblank; /* column number for the ZBLANK column */ + + double zscale; /* scaling value, if same for all tiles */ + double zzero; /* zero pt, if same for all tiles */ + double cn_bscale; /* value of the BSCALE keyword in header */ + double cn_bzero; /* value of the BZERO keyword in header */ + int zblank; /* value for null pixels, if not a column */ + + int rice_blocksize; /* first compression parameter */ + int rice_nbits; /* second compression parameter */ +} FITSfile; + +typedef struct /* structure used to store basic HDU information */ +{ + int HDUposition; /* HDU position in file; 0 = first HDU */ + FITSfile *Fptr; /* pointer to FITS file structure */ +}fitsfile; + +typedef struct /* structure for the iterator function column information */ +{ + /* elements required as input to fits_iterate_data: */ + + fitsfile *fptr; /* pointer to the HDU containing the column */ + int colnum; /* column number in the table (use name if < 1) */ + char colname[70]; /* name (= TTYPEn value) of the column (optional) */ + int datatype; /* output datatype (converted if necessary */ + int iotype; /* = InputCol, InputOutputCol, or OutputCol */ + + /* output elements that may be useful for the work function: */ + + void *array; /* pointer to the array (and the null value) */ + long repeat; /* binary table vector repeat value */ + long tlmin; /* legal minimum data value */ + long tlmax; /* legal maximum data value */ + char tunit[70]; /* physical unit string */ + char tdisp[70]; /* suggested display format */ + +} iteratorCol; + +#define InputCol 0 /* flag for input only iterator column */ +#define InputOutputCol 1 /* flag for input and output iterator column */ +#define OutputCol 2 /* flag for output only iterator column */ + +/* error status codes */ + +#define SKIP_TABLE -104 /* move to 1st image when opening file */ +#define SKIP_IMAGE -103 /* move to 1st table when opening file */ +#define SKIP_NULL_PRIMARY -102 /* skip null primary array when opening file */ +#define USE_MEM_BUFF -101 /* use memory buffer when opening file */ +#define OVERFLOW_ERR -11 /* overflow during datatype conversion */ +#define PREPEND_PRIMARY -9 /* used in ffiimg to insert new primary array */ +#define SAME_FILE 101 /* input and output files are the same */ +#define TOO_MANY_FILES 103 /* tried to open too many FITS files */ +#define FILE_NOT_OPENED 104 /* could not open the named file */ +#define FILE_NOT_CREATED 105 /* could not create the named file */ +#define WRITE_ERROR 106 /* error writing to FITS file */ +#define END_OF_FILE 107 /* tried to move past end of file */ +#define READ_ERROR 108 /* error reading from FITS file */ +#define FILE_NOT_CLOSED 110 /* could not close the file */ +#define ARRAY_TOO_BIG 111 /* array dimensions exceed internal limit */ +#define READONLY_FILE 112 /* Cannot write to readonly file */ +#define MEMORY_ALLOCATION 113 /* Could not allocate memory */ +#define BAD_FILEPTR 114 /* invalid fitsfile pointer */ +#define NULL_INPUT_PTR 115 /* NULL input pointer to routine */ +#define SEEK_ERROR 116 /* error seeking position in file */ + +#define BAD_URL_PREFIX 121 /* invalid URL prefix on file name */ +#define TOO_MANY_DRIVERS 122 /* tried to register too many IO drivers */ +#define DRIVER_INIT_FAILED 123 /* driver initialization failed */ +#define NO_MATCHING_DRIVER 124 /* matching driver is not registered */ +#define URL_PARSE_ERROR 125 /* failed to parse input file URL */ +#define RANGE_PARSE_ERROR 126 /* failed to parse input file URL */ + +#define SHARED_ERRBASE (150) +#define SHARED_BADARG (SHARED_ERRBASE + 1) +#define SHARED_NULPTR (SHARED_ERRBASE + 2) +#define SHARED_TABFULL (SHARED_ERRBASE + 3) +#define SHARED_NOTINIT (SHARED_ERRBASE + 4) +#define SHARED_IPCERR (SHARED_ERRBASE + 5) +#define SHARED_NOMEM (SHARED_ERRBASE + 6) +#define SHARED_AGAIN (SHARED_ERRBASE + 7) +#define SHARED_NOFILE (SHARED_ERRBASE + 8) +#define SHARED_NORESIZE (SHARED_ERRBASE + 9) + +#define HEADER_NOT_EMPTY 201 /* header already contains keywords */ +#define KEY_NO_EXIST 202 /* keyword not found in header */ +#define KEY_OUT_BOUNDS 203 /* keyword record number is out of bounds */ +#define VALUE_UNDEFINED 204 /* keyword value field is blank */ +#define NO_QUOTE 205 /* string is missing the closing quote */ +#define BAD_KEYCHAR 207 /* illegal character in keyword name or card */ +#define BAD_ORDER 208 /* required keywords out of order */ +#define NOT_POS_INT 209 /* keyword value is not a positive integer */ +#define NO_END 210 /* couldn't find END keyword */ +#define BAD_BITPIX 211 /* illegal BITPIX keyword value*/ +#define BAD_NAXIS 212 /* illegal NAXIS keyword value */ +#define BAD_NAXES 213 /* illegal NAXISn keyword value */ +#define BAD_PCOUNT 214 /* illegal PCOUNT keyword value */ +#define BAD_GCOUNT 215 /* illegal GCOUNT keyword value */ +#define BAD_TFIELDS 216 /* illegal TFIELDS keyword value */ +#define NEG_WIDTH 217 /* negative table row size */ +#define NEG_ROWS 218 /* negative number of rows in table */ +#define COL_NOT_FOUND 219 /* column with this name not found in table */ +#define BAD_SIMPLE 220 /* illegal value of SIMPLE keyword */ +#define NO_SIMPLE 221 /* Primary array doesn't start with SIMPLE */ +#define NO_BITPIX 222 /* Second keyword not BITPIX */ +#define NO_NAXIS 223 /* Third keyword not NAXIS */ +#define NO_NAXES 224 /* Couldn't find all the NAXISn keywords */ +#define NO_XTENSION 225 /* HDU doesn't start with XTENSION keyword */ +#define NOT_ATABLE 226 /* the CHDU is not an ASCII table extension */ +#define NOT_BTABLE 227 /* the CHDU is not a binary table extension */ +#define NO_PCOUNT 228 /* couldn't find PCOUNT keyword */ +#define NO_GCOUNT 229 /* couldn't find GCOUNT keyword */ +#define NO_TFIELDS 230 /* couldn't find TFIELDS keyword */ +#define NO_TBCOL 231 /* couldn't find TBCOLn keyword */ +#define NO_TFORM 232 /* couldn't find TFORMn keyword */ +#define NOT_IMAGE 233 /* the CHDU is not an IMAGE extension */ +#define BAD_TBCOL 234 /* TBCOLn keyword value < 0 or > rowlength */ +#define NOT_TABLE 235 /* the CHDU is not a table */ +#define COL_TOO_WIDE 236 /* column is too wide to fit in table */ +#define COL_NOT_UNIQUE 237 /* more than 1 column name matches template */ +#define BAD_ROW_WIDTH 241 /* sum of column widths not = NAXIS1 */ +#define UNKNOWN_EXT 251 /* unrecognizable FITS extension type */ +#define UNKNOWN_REC 252 /* unrecognizable FITS record */ +#define END_JUNK 253 /* END keyword is not blank */ +#define BAD_HEADER_FILL 254 /* Header fill area not blank */ +#define BAD_DATA_FILL 255 /* Data fill area not blank or zero */ +#define BAD_TFORM 261 /* illegal TFORM format code */ +#define BAD_TFORM_DTYPE 262 /* unrecognizable TFORM datatype code */ +#define BAD_TDIM 263 /* illegal TDIMn keyword value */ +#define BAD_HEAP_PTR 264 /* invalid BINTABLE heap address */ + +#define BAD_HDU_NUM 301 /* HDU number < 1 or > MAXHDU */ +#define BAD_COL_NUM 302 /* column number < 1 or > tfields */ +#define NEG_FILE_POS 304 /* tried to move before beginning of file */ +#define NEG_BYTES 306 /* tried to read or write negative bytes */ +#define BAD_ROW_NUM 307 /* illegal starting row number in table */ +#define BAD_ELEM_NUM 308 /* illegal starting element number in vector */ +#define NOT_ASCII_COL 309 /* this is not an ASCII string column */ +#define NOT_LOGICAL_COL 310 /* this is not a logical datatype column */ +#define BAD_ATABLE_FORMAT 311 /* ASCII table column has wrong format */ +#define BAD_BTABLE_FORMAT 312 /* Binary table column has wrong format */ +#define NO_NULL 314 /* null value has not been defined */ +#define NOT_VARI_LEN 317 /* this is not a variable length column */ +#define BAD_DIMEN 320 /* illegal number of dimensions in array */ +#define BAD_PIX_NUM 321 /* first pixel number greater than last pixel */ +#define ZERO_SCALE 322 /* illegal BSCALE or TSCALn keyword = 0 */ +#define NEG_AXIS 323 /* illegal axis length < 1 */ + +#define NOT_GROUP_TABLE 340 +#define HDU_ALREADY_MEMBER 341 +#define MEMBER_NOT_FOUND 342 +#define GROUP_NOT_FOUND 343 +#define BAD_GROUP_ID 344 +#define TOO_MANY_HDUS_TRACKED 345 +#define HDU_ALREADY_TRACKED 346 +#define BAD_OPTION 347 +#define IDENTICAL_POINTERS 348 +#define BAD_GROUP_ATTACH 349 +#define BAD_GROUP_DETACH 350 + +#define BAD_I2C 401 /* bad int to formatted string conversion */ +#define BAD_F2C 402 /* bad float to formatted string conversion */ +#define BAD_INTKEY 403 /* can't interprete keyword value as integer */ +#define BAD_LOGICALKEY 404 /* can't interprete keyword value as logical */ +#define BAD_FLOATKEY 405 /* can't interprete keyword value as float */ +#define BAD_DOUBLEKEY 406 /* can't interprete keyword value as double */ +#define BAD_C2I 407 /* bad formatted string to int conversion */ +#define BAD_C2F 408 /* bad formatted string to float conversion */ +#define BAD_C2D 409 /* bad formatted string to double conversion */ +#define BAD_DATATYPE 410 /* bad keyword datatype code */ +#define BAD_DECIM 411 /* bad number of decimal places specified */ +#define NUM_OVERFLOW 412 /* overflow during datatype conversion */ + +# define DATA_COMPRESSION_ERR 413 /* error in imcompress routines */ +# define DATA_DECOMPRESSION_ERR 414 /* error in imcompress routines */ +# define NO_COMPRESSED_TILE 415 /* compressed tile doesn't exist */ + +#define BAD_DATE 420 /* error in date or time conversion */ + +#define PARSE_SYNTAX_ERR 431 /* syntax error in parser expression */ +#define PARSE_BAD_TYPE 432 /* expression did not evaluate to desired type */ +#define PARSE_LRG_VECTOR 433 /* vector result too large to return in array */ +#define PARSE_NO_OUTPUT 434 /* data parser failed not sent an out column */ +#define PARSE_BAD_COL 435 /* bad data encounter while parsing column */ +#define PARSE_BAD_OUTPUT 436 /* Output file not of proper type */ + +#define ANGLE_TOO_BIG 501 /* celestial angle too large for projection */ +#define BAD_WCS_VAL 502 /* bad celestial coordinate or pixel value */ +#define WCS_ERROR 503 /* error in celestial coordinate calculation */ +#define BAD_WCS_PROJ 504 /* unsupported type of celestial projection */ +#define NO_WCS_KEY 505 /* celestial coordinate keywords not found */ +#define APPROX_WCS_KEY 506 /* approximate WCS keywords were calculated */ + +#define NO_CLOSE_ERROR 999 /* special value used internally to switch off */ + /* the error message from ffclos and ffchdu */ + +/*------- following error codes are used in the grparser.c file -----------*/ +#define NGP_ERRBASE (360) /* base chosen so not to interfere with CFITSIO */ +#define NGP_OK (0) +#define NGP_NO_MEMORY (NGP_ERRBASE + 0) /* malloc failed */ +#define NGP_READ_ERR (NGP_ERRBASE + 1) /* read error from file */ +#define NGP_NUL_PTR (NGP_ERRBASE + 2) /* null pointer passed as argument */ +#define NGP_EMPTY_CURLINE (NGP_ERRBASE + 3) /* line read seems to be empty */ +#define NGP_UNREAD_QUEUE_FULL (NGP_ERRBASE + 4) /* cannot unread more then 1 line (or single line twice) */ +#define NGP_INC_NESTING (NGP_ERRBASE + 5) /* too deep include file nesting (inf. loop ?) */ +#define NGP_ERR_FOPEN (NGP_ERRBASE + 6) /* fopen() failed, cannot open file */ +#define NGP_EOF (NGP_ERRBASE + 7) /* end of file encountered */ +#define NGP_BAD_ARG (NGP_ERRBASE + 8) /* bad arguments passed */ +#define NGP_TOKEN_NOT_EXPECT (NGP_ERRBASE + 9) /* token not expected here */ + +/* The following exclusion if __CINT__ is defined is needed for ROOT */ +#ifndef __CINT__ +/* the following 3 lines are needed to support C++ compilers */ +#ifdef __cplusplus +extern "C" { +#endif +#endif + +/*---------------- FITS file URL parsing routines -------------*/ +int fits_get_token(char **ptr, char *delimiter, char *token, int *isanumber); +char *fits_split_names(char *list); +int ffiurl(char *url, char *urltype, char *infile, + char *outfile, char *extspec, char *rowfilter, + char *binspec, char *colspec, int *status); +int ffrtnm(char *url, char *rootname, int *status); +int ffexts(char *extspec, int *extnum, char *extname, int *extvers, + int *hdutype, char *colname, char *rowexpress, int *status); +int ffextn(char *url, int *extension_num, int *status); +int ffurlt(fitsfile *fptr, char *urlType, int *status); +int ffbins(char *binspec, int *imagetype, int *haxis, + char colname[4][FLEN_VALUE], double *minin, + double *maxin, double *binsizein, + char minname[4][FLEN_VALUE], char maxname[4][FLEN_VALUE], + char binname[4][FLEN_VALUE], double *weight, char *wtname, + int *recip, int *status); +int ffbinr(char **binspec, char *colname, double *minin, + double *maxin, double *binsizein, char *minname, + char *maxname, char *binname, int *status); +int ffimport_file( char *filename, char **contents, int *status ); +int ffrwrg( char *rowlist, long maxrows, int maxranges, int *numranges, + long *minrow, long *maxrow, int *status); + +/*---------------- FITS file I/O routines -------------*/ +int ffomem(fitsfile **fptr, const char *name, int mode, void **buffptr, + size_t *buffsize, size_t deltasize, + void *(*mem_realloc)(void *p, size_t newsize), + int *status); +int ffopen(fitsfile **fptr, const char *filename, int iomode, int *status); +int ffdopn(fitsfile **fptr, const char *filename, int iomode, int *status); +int fftopn(fitsfile **fptr, const char *filename, int iomode, int *status); +int ffiopn(fitsfile **fptr, const char *filename, int iomode, int *status); +int ffreopen(fitsfile *openfptr, fitsfile **newfptr, int *status); +int ffinit(fitsfile **fptr, const char *filename, int *status); +int ffimem(fitsfile **fptr, void **buffptr, + size_t *buffsize, size_t deltasize, + void *(*mem_realloc)(void *p, size_t newsize), + int *status); +int fftplt(fitsfile **fptr, const char *filename, const char *tempname, + int *status); +int ffflus(fitsfile *fptr, int *status); +int ffflsh(fitsfile *fptr, int clearbuf, int *status); +int ffclos(fitsfile *fptr, int *status); +int ffdelt(fitsfile *fptr, int *status); +int ffflnm(fitsfile *fptr, char *filename, int *status); +int ffflmd(fitsfile *fptr, int *filemode, int *status); + +/*---------------- utility routines -------------*/ +float ffvers(float *version); +void ffupch(char *string); +void ffgerr(int status, char *errtext); +void ffpmsg(const char *err_message); +void ffpmrk(void); +int ffgmsg(char *err_message); +void ffcmsg(void); +void ffcmrk(void); +void ffrprt(FILE *stream, int status); +void ffcmps(char *templt, char *colname, int casesen, int *match, + int *exact); +int fftkey(char *keyword, int *status); +int fftrec(char *card, int *status); +int ffnchk(fitsfile *fptr, int *status); +int ffkeyn(char *keyroot, int value, char *keyname, int *status); +int ffnkey(int value, char *keyroot, char *keyname, int *status); +int ffgkcl(char *card); +int ffdtyp(char *cval, char *dtype, int *status); +int ffpsvc(char *card, char *value, char *comm, int *status); +int ffgknm(char *card, char *name, int *length, int *status); +int ffgthd(char *tmplt, char *card, int *hdtype, int *status); +int ffasfm(char *tform, int *datacode, long *width, int *decim, int *status); +int ffbnfm(char *tform, int *datacode, long *repeat, long *width, int *status); +int ffgabc(int tfields, char **tform, int space, long *rowlen, long *tbcol, + int *status); +int fits_get_section_range(char **ptr,long *secmin,long *secmax,long *incre, + int *status); + +/*----------------- write single keywords --------------*/ +int ffpky(fitsfile *fptr, int datatype, char *keyname, void *value, + char *comm, int *status); +int ffprec(fitsfile *fptr, const char *card, int *status); +int ffpcom(fitsfile *fptr, const char *comm, int *status); +int ffpunt(fitsfile *fptr, char *keyname, char *unit, int *status); +int ffphis(fitsfile *fptr, const char *history, int *status); +int ffpdat(fitsfile *fptr, int *status); +int ffgstm(char *timestr, int *timeref, int *status); +int ffgsdt(int *day, int *month, int *year, int *status); +int ffdt2s(int year, int month, int day, char *datestr, int *status); +int fftm2s(int year, int month, int day, int hour, int minute, double second, + int decimals, char *datestr, int *status); +int ffs2dt(char *datestr, int *year, int *month, int *day, int *status); +int ffs2tm(char *datestr, int *year, int *month, int *day, int *hour, + int *minute, double *second, int *status); +int ffpkyu(fitsfile *fptr, char *keyname, char *comm, int *status); +int ffpkys(fitsfile *fptr, char *keyname, char *value, char *comm,int *status); +int ffpkls(fitsfile *fptr, char *keyname, char *value, char *comm,int *status); +int ffplsw(fitsfile *fptr, int *status); +int ffpkyl(fitsfile *fptr, char *keyname, int value, char *comm, int *status); +int ffpkyj(fitsfile *fptr, char *keyname, long value, char *comm, int *status); +int ffpkyf(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffpkye(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffpkyg(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffpkyd(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffpkyc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffpkym(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); +int ffpkfc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffpkfm(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); +int ffpkyt(fitsfile *fptr, char *keyname, long intval, double frac, char *comm, + int *status); +int ffptdm( fitsfile *fptr, int colnum, int naxis, long naxes[], int *status); + +/*----------------- write array of keywords --------------*/ +int ffpkns(fitsfile *fptr, char *keyroot, int nstart, int nkey, char *value[], + char *comm[], int *status); +int ffpknl(fitsfile *fptr, char *keyroot, int nstart, int nkey, int *value, + char *comm[], int *status); +int ffpknj(fitsfile *fptr, char *keyroot, int nstart, int nkey, long *value, + char *comm[], int *status); +int ffpknf(fitsfile *fptr, char *keyroot, int nstart, int nkey, float *value, + int decim, char *comm[], int *status); +int ffpkne(fitsfile *fptr, char *keyroot, int nstart, int nkey, float *value, + int decim, char *comm[], int *status); +int ffpkng(fitsfile *fptr, char *keyroot, int nstart, int nkey, double *value, + int decim, char *comm[], int *status); +int ffpknd(fitsfile *fptr, char *keyroot, int nstart, int nkey, double *value, + int decim, char *comm[], int *status); +int ffcpky(fitsfile *infptr,fitsfile *outfptr,int incol,int outcol, + char *rootname, int *status); + +/*----------------- write required header keywords --------------*/ +int ffphps( fitsfile *fptr, int bitpix, int naxis, long naxes[], int *status); +int ffphpr( fitsfile *fptr, int simple, int bitpix, int naxis, long naxes[], + long pcount, long gcount, int extend, int *status); +int ffphtb(fitsfile *fptr, long naxis1, long naxis2, int tfields, char **ttype, + long *tbcol, char **tform, char **tunit, char *extname, int *status); +int ffphbn(fitsfile *fptr, long naxis2, int tfields, char **ttype, + char **tform, char **tunit, char *extname, long pcount, int *status); + +/*----------------- write template keywords --------------*/ +int ffpktp(fitsfile *fptr, const char *filename, int *status); + +/*------------------ get header information --------------*/ +int ffghsp(fitsfile *fptr, int *nexist, int *nmore, int *status); +int ffghps(fitsfile *fptr, int *nexist, int *position, int *status); + +/*------------------ move position in header -------------*/ +int ffmaky(fitsfile *fptr, int nrec, int *status); +int ffmrky(fitsfile *fptr, int nrec, int *status); + +/*------------------ read single keywords -----------------*/ +int ffgnxk(fitsfile *fptr, char **inclist, int ninc, char **exclist, + int nexc, char *card, int *status); +int ffgrec(fitsfile *fptr, int nrec, char *card, int *status); +int ffgcrd(fitsfile *fptr, char *keyname, char *card, int *status); +int ffgunt(fitsfile *fptr, char *keyname, char *unit, int *status); +int ffgkyn(fitsfile *fptr, int nkey, char *keyname, char *keyval, char *comm, + int *status); +int ffgkey(fitsfile *fptr, char *keyname, char *keyval, char *comm, + int *status); + +int ffgky( fitsfile *fptr, int datatype, char *keyname, void *value, + char *comm, int *status); +int ffgkys(fitsfile *fptr, char *keyname, char *value, char *comm, int *status); +int ffgkls(fitsfile *fptr, char *keyname, char **value, char *comm, int *status) +; +int ffgkyl(fitsfile *fptr, char *keyname, int *value, char *comm, int *status); +int ffgkyj(fitsfile *fptr, char *keyname, long *value, char *comm, int *status); +int ffgkye(fitsfile *fptr, char *keyname, float *value, char *comm,int *status); +int ffgkyd(fitsfile *fptr, char *keyname, double *value,char *comm,int *status); +int ffgkyc(fitsfile *fptr, char *keyname, float *value, char *comm,int *status); +int ffgkym(fitsfile *fptr, char *keyname, double *value,char *comm,int *status); +int ffgkyt(fitsfile *fptr, char *keyname, long *ivalue, double *dvalue, + char *comm, int *status); +int ffgtdm(fitsfile *fptr, int colnum, int maxdim, int *naxis, long naxes[], + int *status); +int ffdtdm(fitsfile *fptr, char *tdimstr, int colnum, int maxdim, + int *naxis, long naxes[], int *status); + +/*------------------ read array of keywords -----------------*/ +int ffgkns(fitsfile *fptr, char *keyname, int nstart, int nmax, char *value[], + int *nfound, int *status); +int ffgknl(fitsfile *fptr, char *keyname, int nstart, int nmax, int *value, + int *nfound, int *status); +int ffgknj(fitsfile *fptr, char *keyname, int nstart, int nmax, long *value, + int *nfound, int *status); +int ffgkne(fitsfile *fptr, char *keyname, int nstart, int nmax, float *value, + int *nfound, int *status); +int ffgknd(fitsfile *fptr, char *keyname, int nstart, int nmax, double *value, + int *nfound, int *status); +int ffh2st(fitsfile *fptr, char **header, int *status); +int ffhdr2str( fitsfile *fptr, int exclude_comm, char **exclist, + int nexc, char **header, int *nkeys, int *status); + +/*----------------- read required header keywords --------------*/ +int ffghpr(fitsfile *fptr, int maxdim, int *simple, int *bitpix, int *naxis, + long naxes[], long *pcount, long *gcount, int *extend, int *status); + +int ffghtb(fitsfile *fptr,int maxfield, long *naxis1, long *naxis2, + int *tfields, char **ttype, long *tbcol, char **tform, char **tunit, + char *extname, int *status); + +int ffghbn(fitsfile *fptr, int maxfield, long *naxis2, int *tfields, + char **ttype, char **tform, char **tunit, char *extname, + long *pcount, int *status); + +/*--------------------- update keywords ---------------*/ +int ffuky(fitsfile *fptr, int datatype, char *keyname, void *value, + char *comm, int *status); +int ffucrd(fitsfile *fptr, char *keyname, char *card, int *status); +int ffukyu(fitsfile *fptr, char *keyname, char *comm, int *status); +int ffukys(fitsfile *fptr, char *keyname, char *value, char *comm, int *status); +int ffukls(fitsfile *fptr, char *keyname, char *value, char *comm, int *status); +int ffukyl(fitsfile *fptr, char *keyname, int value, char *comm, int *status); +int ffukyj(fitsfile *fptr, char *keyname, long value, char *comm, int *status); +int ffukyf(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffukye(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffukyg(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffukyd(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffukyc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffukym(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); +int ffukfc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffukfm(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); + +/*--------------------- modify keywords ---------------*/ +int ffmrec(fitsfile *fptr, int nkey, char *card, int *status); +int ffmcrd(fitsfile *fptr, char *keyname, char *card, int *status); +int ffmnam(fitsfile *fptr, char *oldname, char *newname, int *status); +int ffmcom(fitsfile *fptr, char *keyname, char *comm, int *status); +int ffmkyu(fitsfile *fptr, char *keyname, char *comm, int *status); +int ffmkys(fitsfile *fptr, char *keyname, char *value, char *comm,int *status); +int ffmkls(fitsfile *fptr, char *keyname, char *value, char *comm,int *status); +int ffmkyl(fitsfile *fptr, char *keyname, int value, char *comm, int *status); +int ffmkyj(fitsfile *fptr, char *keyname, long value, char *comm, int *status); +int ffmkyf(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffmkye(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffmkyg(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffmkyd(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffmkyc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffmkym(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); +int ffmkfc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffmkfm(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); + +/*--------------------- insert keywords ---------------*/ +int ffirec(fitsfile *fptr, int nkey, char *card, int *status); +int ffikey(fitsfile *fptr, char *card, int *status); +int ffikyu(fitsfile *fptr, char *keyname, char *comm, int *status); +int ffikys(fitsfile *fptr, char *keyname, char *value, char *comm,int *status); +int ffikls(fitsfile *fptr, char *keyname, char *value, char *comm,int *status); +int ffikyl(fitsfile *fptr, char *keyname, int value, char *comm, int *status); +int ffikyj(fitsfile *fptr, char *keyname, long value, char *comm, int *status); +int ffikyf(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffikye(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffikyg(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffikyd(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffikyc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffikym(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); +int ffikfc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffikfm(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); + +/*--------------------- delete keywords ---------------*/ +int ffdkey(fitsfile *fptr, char *keyname, int *status); +int ffdrec(fitsfile *fptr, int keypos, int *status); + +/*--------------------- get HDU information -------------*/ +int ffghdn(fitsfile *fptr, int *chdunum); +int ffghdt(fitsfile *fptr, int *exttype, int *status); +int ffghad(fitsfile *fptr, long *headstart, long *datastart, long *dataend, + int *status); +int ffghof(fitsfile *fptr, OFF_T *headstart, OFF_T *datastart, OFF_T *dataend, + int *status); +int ffgipr(fitsfile *fptr, int maxaxis, int *imgtype, int *naxis, + long *naxes, int *status); +int ffgidt(fitsfile *fptr, int *imgtype, int *status); +int ffgiet(fitsfile *fptr, int *imgtype, int *status); +int ffgidm(fitsfile *fptr, int *naxis, int *status); +int ffgisz(fitsfile *fptr, int nlen, long *naxes, int *status); + +/*--------------------- HDU operations -------------*/ +int ffmahd(fitsfile *fptr, int hdunum, int *exttype, int *status); +int ffmrhd(fitsfile *fptr, int hdumov, int *exttype, int *status); +int ffmnhd(fitsfile *fptr, int exttype, char *hduname, int hduvers, + int *status); +int ffthdu(fitsfile *fptr, int *nhdu, int *status); +int ffcrhd(fitsfile *fptr, int *status); +int ffcrim(fitsfile *fptr, int bitpix, int naxis, long *naxes, int *status); +int ffcrtb(fitsfile *fptr, int tbltype, long naxis2, int tfields, char **ttype, + char **tform, char **tunit, char *extname, int *status); +int ffiimg(fitsfile *fptr, int bitpix, int naxis, long *naxes, int *status); +int ffitab(fitsfile *fptr, long naxis1, long naxis2, int tfields, char **ttype, + long *tbcol, char **tform, char **tunit, char *extname, int *status); +int ffibin(fitsfile *fptr,long naxis2, int tfields, char **ttype, char **tform, + char **tunit, char *extname, long pcount, int *status); +int ffrsim(fitsfile *fptr, int bitpix, int naxis, long *naxes, int *status); +int ffdhdu(fitsfile *fptr, int *hdutype, int *status); +int ffcopy(fitsfile *infptr, fitsfile *outfptr, int morekeys, int *status); +int ffcpfl(fitsfile *infptr, fitsfile *outfptr, int prev, int cur, int follow, + int *status); +int ffcphd(fitsfile *infptr, fitsfile *outfptr, int *status); +int ffcpdt(fitsfile *infptr, fitsfile *outfptr, int *status); +int ffchfl(fitsfile *fptr, int *status); +int ffcdfl(fitsfile *fptr, int *status); + +int ffrdef(fitsfile *fptr, int *status); +int ffhdef(fitsfile *fptr, int morekeys, int *status); +int ffpthp(fitsfile *fptr, long theap, int *status); + +int ffcsum(fitsfile *fptr, long nrec, unsigned long *sum, int *status); +void ffesum(unsigned long sum, int complm, char *ascii); +unsigned long ffdsum(char *ascii, int complm, unsigned long *sum); +int ffpcks(fitsfile *fptr, int *status); +int ffupck(fitsfile *fptr, int *status); +int ffvcks(fitsfile *fptr, int *datastatus, int *hdustatus, int *status); +int ffgcks(fitsfile *fptr, unsigned long *datasum, unsigned long *hdusum, + int *status); + +/*--------------------- define scaling or null values -------------*/ +int ffpscl(fitsfile *fptr, double scale, double zero, int *status); +int ffpnul(fitsfile *fptr, long nulvalue, int *status); +int fftscl(fitsfile *fptr, int colnum, double scale, double zero, int *status); +int fftnul(fitsfile *fptr, int colnum, long nulvalue, int *status); +int ffsnul(fitsfile *fptr, int colnum, char *nulstring, int *status); + +/*--------------------- get column information -------------*/ +int ffgcno(fitsfile *fptr, int casesen, char *templt, int *colnum, + int *status); +int ffgcnn(fitsfile *fptr, int casesen, char *templt, char *colname, + int *colnum, int *status); + +int ffgtcl(fitsfile *fptr, int colnum, int *typecode, long *repeat, + long *width, int *status); +int ffeqty(fitsfile *fptr, int colnum, int *typecode, long *repeat, + long *width, int *status); +int ffgncl(fitsfile *fptr, int *ncols, int *status); +int ffgnrw(fitsfile *fptr, long *nrows, int *status); +int ffgacl(fitsfile *fptr, int colnum, char *ttype, long *tbcol, + char *tunit, char *tform, double *tscal, double *tzero, + char *tnull, char *tdisp, int *status); +int ffgbcl(fitsfile *fptr, int colnum, char *ttype, char *tunit, + char *dtype, long *repeat, double *tscal, double *tzero, + long *tnull, char *tdisp, int *status); +int ffgrsz(fitsfile *fptr, long *nrows, int *status); +int ffgcdw(fitsfile *fptr, int colnum, int *width, int *status); + +/*--------------------- read primary array or image elements -------------*/ +int ffgpxv(fitsfile *fptr, int datatype, long *firstpix, long nelem, + void *nulval, void *array, int *anynul, int *status); +int ffgpxf(fitsfile *fptr, int datatype, long *firstpix, long nelem, + void *array, char *nullarray, int *anynul, int *status); +int ffgsv(fitsfile *fptr, int datatype, long *blc, long *trc, long *inc, + void *nulval, void *array, int *anynul, int *status); +int ffgpv(fitsfile *fptr, int datatype, long firstelem, long nelem, + void *nulval, void *array, int *anynul, int *status); +int ffgpf(fitsfile *fptr, int datatype, long firstelem, long nelem, + void *array, char *nullarray, int *anynul, int *status); +int ffgpvb(fitsfile *fptr, long group, long firstelem, long nelem, unsigned + char nulval, unsigned char *array, int *anynul, int *status); +int ffgpvsb(fitsfile *fptr, long group, long firstelem, long nelem, signed + char nulval, signed char *array, int *anynul, int *status); +int ffgpvui(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned short nulval, unsigned short *array, int *anynul, + int *status); +int ffgpvi(fitsfile *fptr, long group, long firstelem, long nelem, + short nulval, short *array, int *anynul, int *status); +int ffgpvuj(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned long nulval, unsigned long *array, int *anynul, + int *status); +int ffgpvj(fitsfile *fptr, long group, long firstelem, long nelem, + long nulval, long *array, int *anynul, int *status); +int ffgpvjj(fitsfile *fptr, long group, long firstelem, long nelem, + LONGLONG nulval, LONGLONG *array, int *anynul, int *status); +int ffgpvuk(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned int nulval, unsigned int *array, int *anynul, int *status); +int ffgpvk(fitsfile *fptr, long group, long firstelem, long nelem, + int nulval, int *array, int *anynul, int *status); +int ffgpve(fitsfile *fptr, long group, long firstelem, long nelem, + float nulval, float *array, int *anynul, int *status); +int ffgpvd(fitsfile *fptr, long group, long firstelem, long nelem, + double nulval, double *array, int *anynul, int *status); + +int ffgpfb(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned char *array, char *nularray, int *anynul, int *status); +int ffgpfsb(fitsfile *fptr, long group, long firstelem, long nelem, + signed char *array, char *nularray, int *anynul, int *status); +int ffgpfui(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned short *array, char *nularray, int *anynul, int *status); +int ffgpfi(fitsfile *fptr, long group, long firstelem, long nelem, + short *array, char *nularray, int *anynul, int *status); +int ffgpfuj(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned long *array, char *nularray, int *anynul, int *status); +int ffgpfj(fitsfile *fptr, long group, long firstelem, long nelem, + long *array, char *nularray, int *anynul, int *status); +int ffgpfjj(fitsfile *fptr, long group, long firstelem, long nelem, + LONGLONG *array, char *nularray, int *anynul, int *status); +int ffgpfuk(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned int *array, char *nularray, int *anynul, int *status); +int ffgpfk(fitsfile *fptr, long group, long firstelem, long nelem, + int *array, char *nularray, int *anynul, int *status); +int ffgpfe(fitsfile *fptr, long group, long firstelem, long nelem, + float *array, char *nularray, int *anynul, int *status); +int ffgpfd(fitsfile *fptr, long group, long firstelem, long nelem, + double *array, char *nularray, int *anynul, int *status); + +int ffg2db(fitsfile *fptr, long group, unsigned char nulval, long ncols, + long naxis1, long naxis2, unsigned char *array, + int *anynul, int *status); +int ffg2dsb(fitsfile *fptr, long group, signed char nulval, long ncols, + long naxis1, long naxis2, signed char *array, + int *anynul, int *status); +int ffg2dui(fitsfile *fptr, long group, unsigned short nulval, long ncols, + long naxis1, long naxis2, unsigned short *array, + int *anynul, int *status); +int ffg2di(fitsfile *fptr, long group, short nulval, long ncols, + long naxis1, long naxis2, short *array, + int *anynul, int *status); +int ffg2duj(fitsfile *fptr, long group, unsigned long nulval, long ncols, + long naxis1, long naxis2, unsigned long *array, + int *anynul, int *status); +int ffg2dj(fitsfile *fptr, long group, long nulval, long ncols, + long naxis1, long naxis2, long *array, + int *anynul, int *status); +int ffg2djj(fitsfile *fptr, long group, LONGLONG nulval, long ncols, + long naxis1, long naxis2, LONGLONG *array, + int *anynul, int *status); +int ffg2duk(fitsfile *fptr, long group, unsigned int nulval, long ncols, + long naxis1, long naxis2, unsigned int *array, + int *anynul, int *status); +int ffg2dk(fitsfile *fptr, long group, int nulval, long ncols, + long naxis1, long naxis2, int *array, + int *anynul, int *status); +int ffg2de(fitsfile *fptr, long group, float nulval, long ncols, + long naxis1, long naxis2, float *array, + int *anynul, int *status); +int ffg2dd(fitsfile *fptr, long group, double nulval, long ncols, + long naxis1, long naxis2, double *array, + int *anynul, int *status); + +int ffg3db(fitsfile *fptr, long group, unsigned char nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + unsigned char *array, int *anynul, int *status); +int ffg3dsb(fitsfile *fptr, long group, signed char nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + signed char *array, int *anynul, int *status); +int ffg3dui(fitsfile *fptr, long group, unsigned short nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + unsigned short *array, int *anynul, int *status); +int ffg3di(fitsfile *fptr, long group, short nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + short *array, int *anynul, int *status); +int ffg3duj(fitsfile *fptr, long group, unsigned long nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + unsigned long *array, int *anynul, int *status); +int ffg3dj(fitsfile *fptr, long group, long nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + long *array, int *anynul, int *status); +int ffg3djj(fitsfile *fptr, long group, LONGLONG nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + LONGLONG *array, int *anynul, int *status); +int ffg3duk(fitsfile *fptr, long group, unsigned int nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + unsigned int *array, int *anynul, int *status); +int ffg3dk(fitsfile *fptr, long group, int nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + int *array, int *anynul, int *status); +int ffg3de(fitsfile *fptr, long group, float nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + float *array, int *anynul, int *status); +int ffg3dd(fitsfile *fptr, long group, double nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + double *array, int *anynul, int *status); + +int ffgsvb(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned char nulval, unsigned char *array, + int *anynul, int *status); +int ffgsvsb(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, signed char nulval, signed char *array, + int *anynul, int *status); +int ffgsvui(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned short nulval, unsigned short *array, + int *anynul, int *status); +int ffgsvi(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, short nulval, short *array, int *anynul, int *status); +int ffgsvuj(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned long nulval, unsigned long *array, + int *anynul, int *status); +int ffgsvj(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, long nulval, long *array, int *anynul, int *status); +int ffgsvjj(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, LONGLONG nulval, LONGLONG *array, int *anynul, + int *status); +int ffgsvuk(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned int nulval, unsigned int *array, + int *anynul, int *status); +int ffgsvk(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, int nulval, int *array, int *anynul, int *status); +int ffgsve(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, float nulval, float *array, int *anynul, int *status); +int ffgsvd(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, double nulval, double *array, int *anynul, + int *status); + +int ffgsfb(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned char *array, char *flagval, + int *anynul, int *status); +int ffgsfsb(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, signed char *array, char *flagval, + int *anynul, int *status); +int ffgsfui(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned short *array, char *flagval, int *anynul, + int *status); +int ffgsfi(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, short *array, char *flagval, int *anynul, int *status); +int ffgsfuj(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned long *array, char *flagval, int *anynul, + int *status); +int ffgsfj(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, long *array, char *flagval, int *anynul, int *status); +int ffgsfjj(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, LONGLONG *array, char *flagval, int *anynul, + int *status); +int ffgsfuk(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned int *array, char *flagval, int *anynul, + int *status); +int ffgsfk(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, int *array, char *flagval, int *anynul, int *status); +int ffgsfe(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, float *array, char *flagval, int *anynul, int *status); +int ffgsfd(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, double *array, char *flagval, int *anynul, + int *status); + +int ffggpb(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned char *array, int *status); +int ffggpsb(fitsfile *fptr, long group, long firstelem, long nelem, + signed char *array, int *status); +int ffggpui(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned short *array, int *status); +int ffggpi(fitsfile *fptr, long group, long firstelem, long nelem, + short *array, int *status); +int ffggpuj(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned long *array, int *status); +int ffggpj(fitsfile *fptr, long group, long firstelem, long nelem, + long *array, int *status); +int ffggpjj(fitsfile *fptr, long group, long firstelem, long nelem, + LONGLONG *array, int *status); +int ffggpuk(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned int *array, int *status); +int ffggpk(fitsfile *fptr, long group, long firstelem, long nelem, + int *array, int *status); +int ffggpe(fitsfile *fptr, long group, long firstelem, long nelem, + float *array, int *status); +int ffggpd(fitsfile *fptr, long group, long firstelem, long nelem, + double *array, int *status); + +/*--------------------- read column elements -------------*/ +int ffgcv( fitsfile *fptr, int datatype, int colnum, long firstrow, + long firstelem, long nelem, void *nulval, void *array, int *anynul, + int *status); +int ffgcf( fitsfile *fptr, int datatype, int colnum, long firstrow, + long firstelem, long nelem, void *array, char *nullarray, + int *anynul, int *status); +int ffgcvs(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, char *nulval, char **array, int *anynul, int *status); +int ffgcl (fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, char *array, int *status); +int ffgcvl (fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, char nulval, char *array, int *anynul, int *status); +int ffgcvb(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned char nulval, unsigned char *array, + int *anynul, int *status); +int ffgcvsb(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, signed char nulval, signed char *array, + int *anynul, int *status); +int ffgcvui(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned short nulval, unsigned short *array, + int *anynul, int *status); +int ffgcvi(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, short nulval, short *array, int *anynul, int *status); +int ffgcvuj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned long nulval, unsigned long *array, int *anynul, + int *status); +int ffgcvj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, long nulval, long *array, int *anynul, int *status); +int ffgcvjj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, LONGLONG nulval, LONGLONG *array, int *anynul, + int *status); +int ffgcvuk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned int nulval, unsigned int *array, int *anynul, + int *status); +int ffgcvk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, int nulval, int *array, int *anynul, int *status); +int ffgcve(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, float nulval, float *array, int *anynul, int *status); +int ffgcvd(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, double nulval, double *array, int *anynul, int *status); +int ffgcvc(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, float nulval, float *array, int *anynul, int *status); +int ffgcvm(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, double nulval, double *array, int *anynul, int *status); +int ffgcx(fitsfile *fptr, int colnum, long firstrow, long firstbit, + long nbits, char *larray, int *status); +int ffgcxui(fitsfile *fptr, int colnum, long firstrow, long nrows, + long firstbit, int nbits, unsigned short *array, int *status); +int ffgcxuk(fitsfile *fptr, int colnum, long firstrow, long nrows, + long firstbit, int nbits, unsigned int *array, int *status); + +int ffgcfs(fitsfile *fptr, int colnum, long firstrow, long firstelem, long + nelem, char **array, char *nularray, int *anynul, int *status); +int ffgcfl(fitsfile *fptr, int colnum, long firstrow, long firstelem, long + nelem, char *array, char *nularray, int *anynul, int *status); +int ffgcfb(fitsfile *fptr, int colnum, long firstrow, long firstelem, long + nelem, unsigned char *array, char *nularray, int *anynul, int *status); +int ffgcfsb(fitsfile *fptr, int colnum, long firstrow, long firstelem, long + nelem, signed char *array, char *nularray, int *anynul, int *status); +int ffgcfui(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned short *array, char *nularray, int *anynul, + int *status); +int ffgcfi(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, short *array, char *nularray, int *anynul, int *status); +int ffgcfuj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned long *array, char *nularray, int *anynul, + int *status); +int ffgcfj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, long *array, char *nularray, int *anynul, int *status); +int ffgcfjj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, LONGLONG *array, char *nularray, int *anynul, int *status); +int ffgcfuk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned int *array, char *nularray, int *anynul, + int *status); +int ffgcfk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, int *array, char *nularray, int *anynul, int *status); +int ffgcfe(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, float *array, char *nularray, int *anynul, int *status); +int ffgcfd(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, double *array, char *nularray, int *anynul, int *status); +int ffgcfc(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, float *array, char *nularray, int *anynul, int *status); +int ffgcfm(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, double *array, char *nularray, int *anynul, int *status); + +int ffgdes(fitsfile *fptr, int colnum, long rownum, long *length, + long *heapaddr, int *status); + +int ffgdess(fitsfile *fptr, int colnum, long firstrow, long nrows, long *length, + long *heapaddr, int *status); + +int fftheap(fitsfile *fptr, long *heapsize, long *unused, long *overlap, + int *valid, int *status); +int ffcmph(fitsfile *fptr, int *status); + +int ffgtbb(fitsfile *fptr, long firstrow, long firstchar, long nchars, + unsigned char *values, int *status); + +/*------------ write primary array or image elements -------------*/ +int ffppx(fitsfile *fptr, int datatype, long *firstpix, long nelem, + void *array, int *status); +int ffppxn(fitsfile *fptr, int datatype, long *firstpix, long nelem, + void *array, void *nulval, int *status); +int ffppr(fitsfile *fptr, int datatype, long firstelem, long nelem, + void *array, int *status); +int ffpprb(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned char *array, int *status); +int ffpprsb(fitsfile *fptr, long group, long firstelem, + long nelem, signed char *array, int *status); +int ffpprui(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned short *array, int *status); +int ffppri(fitsfile *fptr, long group, long firstelem, + long nelem, short *array, int *status); +int ffppruj(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned long *array, int *status); +int ffpprj(fitsfile *fptr, long group, long firstelem, + long nelem, long *array, int *status); +int ffppruk(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned int *array, int *status); +int ffpprk(fitsfile *fptr, long group, long firstelem, + long nelem, int *array, int *status); +int ffppre(fitsfile *fptr, long group, long firstelem, + long nelem, float *array, int *status); +int ffpprd(fitsfile *fptr, long group, long firstelem, + long nelem, double *array, int *status); +int ffpprjj(fitsfile *fptr, long group, long firstelem, + long nelem, LONGLONG *array, int *status); + +int ffppru(fitsfile *fptr, long group, long firstelem, long nelem, + int *status); +int ffpprn(fitsfile *fptr, long firstelem, long nelem, int *status); + +int ffppn(fitsfile *fptr, int datatype, long firstelem, long nelem, + void *array, void *nulval, int *status); +int ffppnb(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned char *array, unsigned char nulval, int *status); +int ffppnsb(fitsfile *fptr, long group, long firstelem, long nelem, + signed char *array, signed char nulval, int *status); +int ffppnui(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned short *array, unsigned short nulval, + int *status); +int ffppni(fitsfile *fptr, long group, long firstelem, + long nelem, short *array, short nulval, int *status); +int ffppnj(fitsfile *fptr, long group, long firstelem, + long nelem, long *array, long nulval, int *status); +int ffppnuj(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned long *array, unsigned long nulval, int *status); +int ffppnuk(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned int *array, unsigned int nulval, int *status); +int ffppnk(fitsfile *fptr, long group, long firstelem, + long nelem, int *array, int nulval, int *status); +int ffppne(fitsfile *fptr, long group, long firstelem, + long nelem, float *array, float nulval, int *status); +int ffppnd(fitsfile *fptr, long group, long firstelem, + long nelem, double *array, double nulval, int *status); +int ffppnjj(fitsfile *fptr, long group, long firstelem, + long nelem, LONGLONG *array, long nulval, int *status); + +int ffp2db(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, unsigned char *array, int *status); +int ffp2dsb(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, signed char *array, int *status); +int ffp2dui(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, unsigned short *array, int *status); +int ffp2di(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, short *array, int *status); +int ffp2duj(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, unsigned long *array, int *status); +int ffp2dj(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, long *array, int *status); +int ffp2duk(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, unsigned int *array, int *status); +int ffp2dk(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, int *array, int *status); +int ffp2de(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, float *array, int *status); +int ffp2dd(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, double *array, int *status); +int ffp2djj(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, LONGLONG *array, int *status); + +int ffp3db(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, unsigned char *array, int *status); +int ffp3dsb(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, signed char *array, int *status); +int ffp3dui(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, unsigned short *array, int *status); +int ffp3di(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, short *array, int *status); +int ffp3duj(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, unsigned long *array, int *status); +int ffp3dj(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, long *array, int *status); +int ffp3duk(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, unsigned int *array, int *status); +int ffp3dk(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, int *array, int *status); +int ffp3de(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, float *array, int *status); +int ffp3dd(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, double *array, int *status); +int ffp3djj(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, LONGLONG *array, int *status); + +int ffpss(fitsfile *fptr, int datatype, + long *fpixel, long *lpixel, void *array, int *status); +int ffpssb(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, unsigned char *array, int *status); +int ffpsssb(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, signed char *array, int *status); +int ffpssui(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, unsigned short *array, int *status); +int ffpssi(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, short *array, int *status); +int ffpssuj(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, unsigned long *array, int *status); +int ffpssj(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, long *array, int *status); +int ffpssuk(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, unsigned int *array, int *status); +int ffpssk(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, int *array, int *status); +int ffpsse(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, float *array, int *status); +int ffpssd(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, double *array, int *status); +int ffpssjj(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, LONGLONG *array, int *status); + +int ffpgpb(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned char *array, int *status); +int ffpgpsb(fitsfile *fptr, long group, long firstelem, + long nelem, signed char *array, int *status); +int ffpgpui(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned short *array, int *status); +int ffpgpi(fitsfile *fptr, long group, long firstelem, + long nelem, short *array, int *status); +int ffpgpuj(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned long *array, int *status); +int ffpgpj(fitsfile *fptr, long group, long firstelem, + long nelem, long *array, int *status); +int ffpgpuk(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned int *array, int *status); +int ffpgpk(fitsfile *fptr, long group, long firstelem, + long nelem, int *array, int *status); +int ffpgpe(fitsfile *fptr, long group, long firstelem, + long nelem, float *array, int *status); +int ffpgpd(fitsfile *fptr, long group, long firstelem, + long nelem, double *array, int *status); +int ffpgpjj(fitsfile *fptr, long group, long firstelem, + long nelem, LONGLONG *array, int *status); + +/*--------------------- iterator functions -------------*/ +int fits_iter_set_by_name(iteratorCol *col, fitsfile *fptr, char *colname, + int datatype, int iotype); +int fits_iter_set_by_num(iteratorCol *col, fitsfile *fptr, int colnum, + int datatype, int iotype); +int fits_iter_set_file(iteratorCol *col, fitsfile *fptr); +int fits_iter_set_colname(iteratorCol *col, char *colname); +int fits_iter_set_colnum(iteratorCol *col, int colnum); +int fits_iter_set_datatype(iteratorCol *col, int datatype); +int fits_iter_set_iotype(iteratorCol *col, int iotype); + +fitsfile * fits_iter_get_file(iteratorCol *col); +char * fits_iter_get_colname(iteratorCol *col); +int fits_iter_get_colnum(iteratorCol *col); +int fits_iter_get_datatype(iteratorCol *col); +int fits_iter_get_iotype(iteratorCol *col); +void * fits_iter_get_array(iteratorCol *col); +long fits_iter_get_tlmin(iteratorCol *col); +long fits_iter_get_tlmax(iteratorCol *col); +long fits_iter_get_repeat(iteratorCol *col); +char * fits_iter_get_tunit(iteratorCol *col); +char * fits_iter_get_tdisp(iteratorCol *col); + +int ffiter(int ncols, iteratorCol *data, long offset, long nPerLoop, + int (*workFn)( long totaln, long offset, long firstn, + long nvalues, int narrays, iteratorCol *data, void *userPointer), + void *userPointer, int *status); + +/*--------------------- write column elements -------------*/ +int ffpcl(fitsfile *fptr, int datatype, int colnum, long firstrow, + long firstelem, long nelem, void *array, int *status); +int ffpcls(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, char **array, int *status); +int ffpcll(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, char *array, int *status); +int ffpclb(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned char *array, int *status); +int ffpclsb(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, signed char *array, int *status); +int ffpclui(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned short *array, int *status); +int ffpcli(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, short *array, int *status); +int ffpcluj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned long *array, int *status); +int ffpclj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, long *array, int *status); +int ffpcluk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned int *array, int *status); +int ffpclk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, int *array, int *status); +int ffpcle(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, float *array, int *status); +int ffpcld(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, double *array, int *status); +int ffpclc(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, float *array, int *status); +int ffpclm(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, double *array, int *status); +int ffpclu(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, int *status); +int ffpclx(fitsfile *fptr, int colnum, long frow, long fbit, long nbit, + char *larray, int *status); +int ffpcljj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, LONGLONG *array, int *status); + +int ffpcn(fitsfile *fptr, int datatype, int colnum, long firstrow, + long firstelem, long nelem, void *array, void *nulval, int *status); +int ffpcns( fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, char **array, char *nulvalue, int *status); +int ffpcnl( fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, char *array, char nulvalue, int *status); +int ffpcnb(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned char *array, unsigned char nulvalue, + int *status); +int ffpcnsb(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, signed char *array, signed char nulvalue, + int *status); +int ffpcnui(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned short *array, unsigned short nulvalue, + int *status); +int ffpcni(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, short *array, short nulvalue, int *status); +int ffpcnuj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned long *array, unsigned long nulvalue, + int *status); +int ffpcnj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, long *array, long nulvalue, int *status); +int ffpcnuk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned int *array, unsigned int nulvalue, + int *status); +int ffpcnk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, int *array, int nulvalue, int *status); +int ffpcne(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, float *array, float nulvalue, int *status); +int ffpcnd(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, double *array, double nulvalue, int *status); +int ffpcnjj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, LONGLONG *array, LONGLONG nulvalue, int *status); + +int ffpdes(fitsfile *fptr, int colnum, long rownum, long length, + long heapaddr, int *status); + +int ffptbb(fitsfile *fptr, long firstrow, long firstchar, long nchars, + unsigned char *values, int *status); + +int ffirow(fitsfile *fptr, long firstrow, long nrows, int *status); +int ffdrow(fitsfile *fptr, long firstrow, long nrows, int *status); +int ffdrrg(fitsfile *fptr, char *ranges, int *status); +int ffdrws(fitsfile *fptr, long *rownum, long nrows, int *status); +int fficol(fitsfile *fptr, int numcol, char *ttype, char *tform, int *status); +int fficls(fitsfile *fptr, int firstcol, int ncols, char **ttype, + char **tform, int *status); +int ffmvec(fitsfile *fptr, int colnum, long newveclen, int *status); +int ffdcol(fitsfile *fptr, int numcol, int *status); +int ffcpcl(fitsfile *infptr, fitsfile *outfptr, int incol, int outcol, + int create_col, int *status); + +/*--------------------- WCS Utilities ------------------*/ +int ffgics(fitsfile *fptr, double *xrval, double *yrval, double *xrpix, + double *yrpix, double *xinc, double *yinc, double *rot, + char *type, int *status); +int ffgtcs(fitsfile *fptr, int xcol, int ycol, double *xrval, + double *yrval, double *xrpix, double *yrpix, double *xinc, + double *yinc, double *rot, char *type, int *status); +int ffwldp(double xpix, double ypix, double xref, double yref, + double xrefpix, double yrefpix, double xinc, double yinc, + double rot, char *type, double *xpos, double *ypos, int *status); +int ffxypx(double xpos, double ypos, double xref, double yref, + double xrefpix, double yrefpix, double xinc, double yinc, + double rot, char *type, double *xpix, double *ypix, int *status); + +/* WCS support routines (provide interface to Doug Mink's WCS library */ +int ffgiwcs(fitsfile *fptr, char **header, int *status); +int ffgtwcs(fitsfile *fptr, int xcol, int ycol, char **header, int *status); + +/*--------------------- lexical parsing routines ------------------*/ +int fftexp( fitsfile *fptr, char *expr, int maxdim, + int *datatype, long *nelem, int *naxis, + long *naxes, int *status ); + +int fffrow( fitsfile *infptr, char *expr, + long firstrow, long nrows, + long *n_good_rows, char *row_status, int *status); + +int ffffrw( fitsfile *fptr, char *expr, long *rownum, int *status); + +int fffrwc( fitsfile *fptr, char *expr, char *timeCol, + char *parCol, char *valCol, long ntimes, + double *times, char *time_status, int *status ); + +int ffsrow( fitsfile *infptr, fitsfile *outfptr, char *expr, + int *status); + +int ffcrow( fitsfile *fptr, int datatype, char *expr, + long firstrow, long nelements, void *nulval, + void *array, int *anynul, int *status ); + +int ffcalc_rng( fitsfile *infptr, char *expr, fitsfile *outfptr, + char *parName, char *parInfo, int nRngs, + long *start, long *end, int *status ); + +int ffcalc( fitsfile *infptr, char *expr, fitsfile *outfptr, + char *parName, char *parInfo, int *status ); + + /* ffhist is not really intended as a user-callable routine */ + /* but it may be useful for some specialized applications */ + +int ffhist(fitsfile **fptr, char *outfile, int imagetype, int naxis, + char colname[4][FLEN_VALUE], + double *minin, double *maxin, double *binsizein, + char minname[4][FLEN_VALUE], char maxname[4][FLEN_VALUE], + char binname[4][FLEN_VALUE], + double weightin, char wtcol[FLEN_VALUE], + int recip, char *rowselect, int *status); + +int fits_select_image_section(fitsfile **fptr, char *outfile, + char *imagesection, int *status); +int fits_select_section( fitsfile *infptr, fitsfile *outfptr, + char *imagesection, int *status); + +/*--------------------- grouping routines ------------------*/ + +int ffgtcr(fitsfile *fptr, char *grpname, int grouptype, int *status); +int ffgtis(fitsfile *fptr, char *grpname, int grouptype, int *status); +int ffgtch(fitsfile *gfptr, int grouptype, int *status); +int ffgtrm(fitsfile *gfptr, int rmopt, int *status); +int ffgtcp(fitsfile *infptr, fitsfile *outfptr, int cpopt, int *status); +int ffgtmg(fitsfile *infptr, fitsfile *outfptr, int mgopt, int *status); +int ffgtcm(fitsfile *gfptr, int cmopt, int *status); +int ffgtvf(fitsfile *gfptr, long *firstfailed, int *status); +int ffgtop(fitsfile *mfptr,int group,fitsfile **gfptr,int *status); +int ffgtam(fitsfile *gfptr, fitsfile *mfptr, int hdupos, int *status); +int ffgtnm(fitsfile *gfptr, long *nmembers, int *status); +int ffgmng(fitsfile *mfptr, long *nmembers, int *status); +int ffgmop(fitsfile *gfptr, long member, fitsfile **mfptr, int *status); +int ffgmcp(fitsfile *gfptr, fitsfile *mfptr, long member, int cpopt, + int *status); +int ffgmtf(fitsfile *infptr, fitsfile *outfptr, long member, int tfopt, + int *status); +int ffgmrm(fitsfile *fptr, long member, int rmopt, int *status); + +/*--------------------- group template parser routines ------------------*/ + +int fits_execute_template(fitsfile *ff, char *ngp_template, int *status); + +/*--------------------- image compression routines ------------------*/ + +int fits_set_compression_type(fitsfile *fptr, int ctype, int *status); +int fits_set_tile_dim(fitsfile *fptr, int ndim, long *dims, int *status); +int fits_set_noise_bits(fitsfile *fptr, int noisebits, int *status); + +int fits_get_compression_type(fitsfile *fptr, int *ctype, int *status); +int fits_get_tile_dim(fitsfile *fptr, int ndim, long *dims, int *status); +int fits_get_noise_bits(fitsfile *fptr, int *noisebits, int *status); + +int fits_compress_img(fitsfile *infptr, fitsfile *outfptr, int compress_type, + long *tilesize, int parm1, int parm2, int *status); +int fits_is_compressed_image(fitsfile *fptr, int *status); +int fits_decompress_img (fitsfile *infptr, fitsfile *outfptr, int *status); + +/* The following exclusion if __CINT__ is defined is needed for ROOT */ +#ifndef __CINT__ +#ifdef __cplusplus +} +#endif +#endif + +#endif + diff --git a/contrib/include/fitsio2.h b/contrib/include/fitsio2.h new file mode 100644 index 00000000..b1f8023c --- /dev/null +++ b/contrib/include/fitsio2.h @@ -0,0 +1,1109 @@ +#ifndef _FITSIO2_H +#define _FITSIO2_H + +#include "fitsio.h" + +/* Setting SUPPORT_64_BIT_INTEGERS to 1 will enable CFITSIO to read */ +/* and write images with BITPIX = 64 and binary table columns with */ +/* TFORMn = 'K'. Otherwise, setting SUPPORT_64_BIT_INTEGERS to 0 */ +/* will cause CFITSIO to not recognize these non-standard 64-bit */ +/* FITS datatypes. */ + +#define SUPPORT_64BIT_INTEGERS 1 + +/* + If REPLACE_LINKS is defined, then whenever CFITSIO fails to open + a file with write access because it is a soft link to a file that + only has read access, then CFITSIO will attempt to replace + the link with a local copy of the file, with write access. This + feature was originally added to support the ftools in the Hera + environment, where many of the user's data file are soft links. +*/ +#if defined(BUILD_HERA) +#define REPLACE_LINKS 1 +#endif + +#define USE_LARGE_VALUE -99 /* flag used when writing images */ + +#define DBUFFSIZE 28800 /* size of data buffer in bytes */ + +#define NIOBUF 40 /* number of IO buffers to create */ + /* !! Significantly increasing NIOBUF may degrade performance !! */ +#define NMAXFILES 300 /* maximum number of FITS files that can be opened */ + /* CFITSIO will allocate (NMAXFILES * 80) bytes of memory */ + +#define IOBUFLEN 2880 /* size in bytes of each IO buffer (DONT CHANGE!) */ +#define MINDIRECT 8640 /* minimum size for direct reads and writes */ + /* MINDIRECT must have a value >= 8640 */ + +#define NATIVE 0 /* a generic machine that uses IEEE formats */ +#define ULTRIX 1 +#define ALPHA_OSF 2 +#define VAXVMS 3 +#define ALPHAVMS 4 +#define IBMPC 5 +#define CRAY 6 +#define PC64BIT 7 + +#define GFLOAT 1 +#define IEEEFLOAT 2 + +/* the following are used to determine what type machine we are running on */ + +/* the following block determines the size of longs on SGI IRIX machines */ +#if defined(_MIPS_SZLONG) +# if _MIPS_SZLONG == 32 +# define LONGSIZE 32 +# elif _MIPS_SZLONG == 64 +# define LONGSIZE 64 +# else +# error "can't handle long size given by _MIPS_SZLONG" +# endif +#endif + +#if defined(vax) && defined(VMS) + +#define MACHINE VAXVMS +#define BYTESWAPPED TRUE + +#elif defined(__alpha) && defined(__VMS) + +#if (__D_FLOAT == TRUE) + +/* this float option is the same as for VAX/VMS machines. */ +#define MACHINE VAXVMS +#define BYTESWAPPED TRUE + +#elif (__G_FLOAT == TRUE) + +/* G_FLOAT is the default for ALPHA VMS systems */ +#define MACHINE ALPHAVMS +#define BYTESWAPPED TRUE +#define FLOATTYPE GFLOAT + +#elif (__IEEE_FLOAT == TRUE) + +#define MACHINE ALPHAVMS +#define BYTESWAPPED TRUE +#define FLOATTYPE IEEEFLOAT + +#endif + +#elif defined(__alpha) && ( defined(__unix__) || defined(__NetBSD__) ) + +#define MACHINE ALPHA_OSF +#define BYTESWAPPED TRUE +#define LONGSIZE 64 + +#elif defined(ultrix) && defined(unix) + +#define MACHINE ULTRIX +#define BYTESWAPPED TRUE + +#elif defined(__sparcv9) + +/* SUN Solaris7 in 64-bit mode */ +#define BYTESWAPPED FALSE +#define MACHINE NATIVE +#define LONGSIZE 64 + +#elif defined(__i386) || defined(__i386__) || defined(__i486__) || defined(__i586__) + +/* IBM PC */ +#define MACHINE IBMPC +#define BYTESWAPPED TRUE + +#elif defined(_MSC_VER) || defined(__BORLANDC__) || defined(__TURBOC__) + +/* IBM PC running DOS or Windows */ +#define MACHINE IBMPC +#define BYTESWAPPED TRUE + +#elif defined(_NI_mswin_) || defined(__EMX__) + +/* LabWindows/CVI with Windows, or PC runnin OS/2 */ +#define MACHINE IBMPC +#define BYTESWAPPED TRUE + +#elif defined(__ia64__) || defined(__x86_64__) + +/* Intel itanium 64-bit PC, or AMD opteron 64-bit PC */ +#define BYTESWAPPED TRUE +#define MACHINE PC64BIT +#define LONGSIZE 64 + +#else + +/* assume machine uses the same IEEE formats as used in FITS files */ +#define MACHINE NATIVE +#define BYTESWAPPED FALSE + +#endif + +/* assume longs are 4 bytes long, unless previously set otherwise */ +#ifndef LONGSIZE +#define LONGSIZE 32 +#endif + +#define IGNORE_EOF 1 +#define REPORT_EOF 0 +#define DATA_UNDEFINED -1 +#define NULL_UNDEFINED 1234554321 +#define ASCII_NULL_UNDEFINED 1 /* indicate no defined null value */ + +#define maxvalue(A,B) ((A) > (B) ? (A) : (B)) +#define minvalue(A,B) ((A) < (B) ? (A) : (B)) + +/* faster string comparison macros */ +#define FSTRCMP(a,b) ((a)[0]<(b)[0]? -1:(a)[0]>(b)[0]?1:strcmp((a),(b))) +#define FSTRNCMP(a,b,n) ((a)[0]<(b)[0]?-1:(a)[0]>(b)[0]?1:strncmp((a),(b),(n))) + +#if defined(__VMS) || defined(VMS) + +#define FNANMASK 0xFFFF /* mask all bits */ +#define DNANMASK 0xFFFF /* mask all bits */ + +#else + +#define FNANMASK 0x7F80 /* mask bits 1 - 8; all set on NaNs */ + /* all 0 on underflow or 0. */ + +#define DNANMASK 0x7FF0 /* mask bits 1 - 11; all set on NaNs */ + /* all 0 on underflow or 0. */ + +#endif + +#if MACHINE == CRAY + /* + Cray machines: the large negative integer corresponds + to the 3 most sig digits set to 1. If these + 3 bits are set in a floating point number (64 bits), then it represents + a reserved value (i.e., a NaN) + */ +#define fnan(L) ( (L) >= 0xE000000000000000 ? 1 : 0) ) + +#else + /* these functions work for both big and little endian machines */ + /* that use the IEEE floating point format for internal numbers */ + + /* These functions tests whether the float value is a reserved IEEE */ + /* value such as a Not-a-Number (NaN), or underflow, overflow, or */ + /* infinity. The functions returns 1 if the value is a NaN, overflow */ + /* or infinity; it returns 2 if the value is an denormalized underflow */ + /* value; otherwise it returns 0. fnan tests floats, dnan tests doubles */ + +#define fnan(L) \ + ( (L & FNANMASK) == FNANMASK ? 1 : (L & FNANMASK) == 0 ? 2 : 0) + +#define dnan(L) \ + ( (L & DNANMASK) == DNANMASK ? 1 : (L & DNANMASK) == 0 ? 2 : 0) + +#endif + +#define DSCHAR_MAX 127.49 /* max double value that fits in an signed char */ +#define DSCHAR_MIN -128.49 /* min double value that fits in an signed char */ +#define DUCHAR_MAX 255.49 /* max double value that fits in an unsigned char */ +#define DUCHAR_MIN -0.49 /* min double value that fits in an unsigned char */ +#define DUSHRT_MAX 65535.49 /* max double value that fits in a unsigned short*/ +#define DUSHRT_MIN -0.49 /* min double value that fits in an unsigned short */ +#define DSHRT_MAX 32767.49 /* max double value that fits in a short */ +#define DSHRT_MIN -32768.49 /* min double value that fits in a short */ + +#if LONGSIZE == 32 +# define DLONG_MAX 2147483647.49 /* max double value that fits in a long */ +# define DLONG_MIN -2147483648.49 /* min double value that fits in a long */ +# define DULONG_MAX 4294967295.49 /* max double that fits in a unsigned long */ +#else +# define DLONG_MAX 9.2233720368547752E18 /* max double value long */ +# define DLONG_MIN -9.2233720368547752E18 /* min double value long */ +# define DULONG_MAX 1.84467440737095504E19 /* max double value ulong */ +#endif + +#define DULONG_MIN -0.49 /* min double value that fits in an unsigned long */ +#define DLONGLONG_MAX 9.2233720368547752E18 /* max double value longlong */ +#define DLONGLONG_MIN -9.2233720368547752E18 /* min double value longlong */ +#define DUINT_MAX 4294967295.49 /* max dbl that fits in a unsigned 4-byte int */ +#define DUINT_MIN -0.49 /* min dbl that fits in an unsigned 4-byte int */ +#define DINT_MAX 2147483647.49 /* max double value that fits in a 4-byte int */ +#define DINT_MIN -2147483648.49 /* min double value that fits in a 4-byte int */ + +#ifndef UINT32_MAX +#define UINT32_MAX 4294967295U /* max unsigned 32-bit integer */ +#endif +#ifndef INT32_MAX +#define INT32_MAX 2147483647 /* max 32-bit integer */ +#endif +#ifndef INT32_MIN +#define INT32_MIN (-INT32_MAX -1) /* min 32-bit integer */ +#endif + +#ifndef LONGLONG_MAX + +#ifdef LLONG_MAX +#define LONGLONG_MAX LLONG_MAX +#define LONGLONG_MIN LLONG_MIN + +#elif defined(INT64_MAX) +#define LONGLONG_MAX INT64_MAX +#define LONGLONG_MIN INT64_MIN + +#elif (LONGSIZE == 64) || defined(HAVE_LONGLONG) +#define LONGLONG_MAX 9223372036854775807L /* max 64-bit integer */ +#define LONGLONG_MIN (-LONGLONG_MAX -1L) /* min 64-bit integer */ + +#else +/* define a default value, even if it is never used */ +#define LONGLONG_MAX LONG_MAX +#define LONGLONG_MIN LONG_MIN +#endif +#endif /* end of ndef LONGLONG_MAX section */ + + +#define COMPRESS_NULL_VALUE -2147483647 + +int ffmkky(char *keyname, char *keyval, char *comm, char *card, int *status); +int ffgnky(fitsfile *fptr, char *card, int *status); +void ffcfmt(char *tform, char *cform); +void ffcdsp(char *tform, char *cform); +void ffswap2(short *values, long nvalues); +void ffswap4(INT32BIT *values, long nvalues); +void ffswap8(double *values, long nvalues); +int ffi2c(long ival, char *cval, int *status); +int ffl2c(int lval, char *cval, int *status); +int ffs2c(char *instr, char *outstr, int *status); +int ffr2f(float fval, int decim, char *cval, int *status); +int ffr2e(float fval, int decim, char *cval, int *status); +int ffd2f(double dval, int decim, char *cval, int *status); +int ffd2e(double dval, int decim, char *cval, int *status); +int ffc2ii(char *cval, long *ival, int *status); +int ffc2ll(char *cval, int *lval, int *status); +int ffc2rr(char *cval, float *fval, int *status); +int ffc2dd(char *cval, double *dval, int *status); +int ffc2x(char *cval, char *dtype, long *ival, int *lval, char *sval, + double *dval, int *status); +int ffc2s(char *instr, char *outstr, int *status); +int ffc2i(char *cval, long *ival, int *status); +int ffc2r(char *cval, float *fval, int *status); +int ffc2d(char *cval, double *dval, int *status); +int ffc2l(char *cval, int *lval, int *status); +void ffxmsg(int action, char *err_message); +int ffgcnt(fitsfile *fptr, char *value, int *status); +int ffgtkn(fitsfile *fptr, int numkey, char *keyname, long *value, int *status); +int fftkyn(fitsfile *fptr, int numkey, char *keyname, char *value, int *status); +int ffgphd(fitsfile *fptr, int maxdim, int *simple, int *bitpix, int *naxis, + long naxes[], long *pcount, long *gcount, int *extend, double *bscale, + double *bzero, long *blank, int *nspace, int *status); +int ffgttb(fitsfile *fptr, long *rowlen, long *nrows, long *pcount, + long *tfield, int *status); + +int ffmkey(fitsfile *fptr, char *card, int *status); + +int ffmbyt(fitsfile *fptr, OFF_T bytpos, int ignore_err, int *status); +int ffgbyt(fitsfile *fptr, long nbytes, void *buffer, int *status); +int ffpbyt(fitsfile *fptr, long nbytes, void *buffer, int *status); +int ffgbytoff(fitsfile *fptr, long gsize, long ngroups, long offset, + void *buffer, int *status); +int ffpbytoff(fitsfile *fptr, long gsize, long ngroups, long offset, + void *buffer, int *status); +int ffldrc(fitsfile *fptr, long record, int err_mode, int *status); +int ffwhbf(fitsfile *fptr, int *nbuff); +int ffbfeof(fitsfile *fptr, int *status); +int ffbfwt(int nbuff, int *status); +int fits_get_num_files(void); +int ffpxsz(int datatype); + +int ffourl(char *url, char *urltype, char *outfile, char *tmplfile, + char *compspec, int *status); +int ffparsecompspec(fitsfile *fptr, char *compspec, int *status); +int ffoptplt(fitsfile *fptr, const char *tempname, int *status); +int fits_is_this_a_copy(char *urltype); +int fits_store_Fptr(FITSfile *Fptr, int *status); +int fits_clear_Fptr(FITSfile *Fptr, int *status); +int fits_already_open(fitsfile **fptr, char *url, + char *urltype, char *infile, char *extspec, char *rowfilter, + char *binspec, char *colspec, int mode,int *isopen, int *status); +int ffedit_columns(fitsfile **fptr, char *outfile, char *expr, int *status); +int fits_get_col_minmax(fitsfile *fptr, int colnum, float *datamin, + float *datamax, int *status); +int ffwritehisto(long totaln, long offset, long firstn, long nvalues, + int narrays, iteratorCol *imagepars, void *userPointer); +int ffcalchist(long totalrows, long offset, long firstrow, long nrows, + int ncols, iteratorCol *colpars, void *userPointer); +int fits_copy_image_cell(fitsfile **fptr, char *outfile, char *colname, + long rownum, int *status); +int fits_copy_image_keywords(fitsfile *infptr, fitsfile *outfptr, int *status); +int ffrhdu(fitsfile *fptr, int *hdutype, int *status); +int ffpinit(fitsfile *fptr, int *status); +int ffainit(fitsfile *fptr, int *status); +int ffbinit(fitsfile *fptr, int *status); +int ffchdu(fitsfile *fptr, int *status); +int ffwend(fitsfile *fptr, int *status); +int ffpdfl(fitsfile *fptr, int *status); +int ffuptf(fitsfile *fptr, int *status); + +int ffdblk(fitsfile *fptr, long nblocks, int *status); +int ffgext(fitsfile *fptr, int moveto, int *exttype, int *status); +int ffgtbc(fitsfile *fptr, long *totalwidth, int *status); +int ffgtbp(fitsfile *fptr, char *name, char *value, int *status); +int ffiblk(fitsfile *fptr, long nblock, int headdata, int *status); +int ffshft(fitsfile *fptr, OFF_T firstbyte, OFF_T nbytes, OFF_T nshift, + int *status); + +int ffgcpr(fitsfile *fptr, int colnum, long firstrow, OFF_T firstelem, + long nelem, int writemode, double *scale, double *zero, char *tform, + long *twidth, int *tcode, int *maxelem, OFF_T *startpos, + OFF_T *elemnum, long *incre, OFF_T *repeat, OFF_T *rowlen, + int *hdutype, long *tnull, char *snull, int *status); + +int ffflushx(FITSfile *fptr); +int ffseek(FITSfile *fptr, OFF_T position); +int ffread(FITSfile *fptr, long nbytes, void *buffer, + int *status); +int ffwrite(FITSfile *fptr, long nbytes, void *buffer, + int *status); +int fftrun(fitsfile *fptr, OFF_T filesize, int *status); + +int ffgcll(fitsfile *fptr, int colnum, long firstrow, OFF_T firstelem, long + nelem, int nultyp, char nulval, char *array, char *nularray, + int *anynul, int *status); +int ffgcls(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, int nultyp, char *nulval, + char **array, char *nularray, int *anynul, int *status); +int ffgcls2(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, int nultyp, char *nulval, + char **array, char *nularray, int *anynul, int *status); +int ffgclb(fitsfile *fptr, int colnum, long firstrow, OFF_T firstelem, + long nelem, long elemincre, int nultyp, unsigned char nulval, + unsigned char *array, char *nularray, int *anynul, int *status); +int ffgclsb(fitsfile *fptr, int colnum, long firstrow, OFF_T firstelem, + long nelem, long elemincre, int nultyp, signed char nulval, + signed char *array, char *nularray, int *anynul, int *status); +int ffgclui(fitsfile *fptr, int colnum, long firstrow, OFF_T firstelem, + long nelem, long elemincre, int nultyp, unsigned short nulval, + unsigned short *array, char *nularray, int *anynul, int *status); +int ffgcli(fitsfile *fptr, int colnum, long firstrow, OFF_T firstelem, + long nelem, long elemincre, int nultyp, short nulval, + short *array, char *nularray, int *anynul, int *status); +int ffgcluj(fitsfile *fptr, int colnum, long firstrow, OFF_T firstelem, + long nelem, long elemincre, int nultyp, unsigned long nulval, + unsigned long *array, char *nularray, int *anynul, int *status); +int ffgcljj(fitsfile *fptr, int colnum, long firstrow, OFF_T firstelem, + long nelem, long elemincre, int nultyp, LONGLONG nulval, + LONGLONG *array, char *nularray, int *anynul, int *status); +int ffgclj(fitsfile *fptr, int colnum, long firstrow, OFF_T firstelem, + long nelem, long elemincre, int nultyp, long nulval, long *array, + char *nularray, int *anynul, int *status); +int ffgcluk(fitsfile *fptr, int colnum, long firstrow, OFF_T firstelem, + long nelem, long elemincre, int nultyp, unsigned int nulval, + unsigned int *array, char *nularray, int *anynul, int *status); +int ffgclk(fitsfile *fptr, int colnum, long firstrow, OFF_T firstelem, + long nelem, long elemincre, int nultyp, int nulval, int *array, + char *nularray, int *anynul, int *status); +int ffgcle(fitsfile *fptr, int colnum, long firstrow, OFF_T firstelem, + long nelem, long elemincre, int nultyp, float nulval, float *array, + char *nularray, int *anynul, int *status); +int ffgcld(fitsfile *fptr, int colnum, long firstrow, OFF_T firstelem, + long nelem, long elemincre, int nultyp, double nulval, + double *array, char *nularray, int *anynul, int *status); + +int ffpi1b(fitsfile *fptr, long nelem, long incre, unsigned char *buffer, + int *status); +int ffpi2b(fitsfile *fptr, long nelem, long incre, short *buffer, int *status); +int ffpi4b(fitsfile *fptr, long nelem, long incre, INT32BIT *buffer, + int *status); +int ffpi8b(fitsfile *fptr, long nelem, long incre, long *buffer, int *status); +int ffpr4b(fitsfile *fptr, long nelem, long incre, float *buffer, int *status); +int ffpr8b(fitsfile *fptr, long nelem, long incre, double *buffer, int *status); + +int ffgi1b(fitsfile *fptr, OFF_T pos, long nelem, long incre, + unsigned char *buffer, int *status); +int ffgi2b(fitsfile *fptr, OFF_T pos, long nelem, long incre, short *buffer, + int *status); +int ffgi4b(fitsfile *fptr, OFF_T pos, long nelem, long incre, INT32BIT *buffer, + int *status); +int ffgi8b(fitsfile *fptr, OFF_T pos, long nelem, long incre, long *buffer, + int *status); +int ffgr4b(fitsfile *fptr, OFF_T pos, long nelem, long incre, float *buffer, + int *status); +int ffgr8b(fitsfile *fptr, OFF_T pos, long nelem, long incre, double *buffer, + int *status); + +int ffcins(fitsfile *fptr, long naxis1, long naxis2, long nbytes, + long bytepos, int *status); +int ffcdel(fitsfile *fptr, long naxis1, long naxis2, long nbytes, + long bytepos, int *status); +int ffkshf(fitsfile *fptr, int firstcol, int tfields, int nshift, int *status); + +int fffi1i1(unsigned char *input, long ntodo, double scale, double zero, + int nullcheck, unsigned char tnull, unsigned char nullval, char + *nullarray, int *anynull, unsigned char *output, int *status); +int fffi2i1(short *input, long ntodo, double scale, double zero, + int nullcheck, short tnull, unsigned char nullval, char *nullarray, + int *anynull, unsigned char *output, int *status); +int fffi4i1(INT32BIT *input, long ntodo, double scale, double zero, + int nullcheck, INT32BIT tnull, unsigned char nullval, char *nullarray, + int *anynull, unsigned char *output, int *status); +int fffi8i1(LONGLONG *input, long ntodo, double scale, double zero, + int nullcheck, long tnull, unsigned char nullval, char *nullarray, + int *anynull, unsigned char *output, int *status); +int fffr4i1(float *input, long ntodo, double scale, double zero, + int nullcheck, unsigned char nullval, char *nullarray, + int *anynull, unsigned char *output, int *status); +int fffr8i1(double *input, long ntodo, double scale, double zero, + int nullcheck, unsigned char nullval, char *nullarray, + int *anynull, unsigned char *output, int *status); +int fffstri1(char *input, long ntodo, double scale, double zero, + long twidth, double power, int nullcheck, char *snull, + unsigned char nullval, char *nullarray, int *anynull, + unsigned char *output, int *status); + +int fffi1s1(unsigned char *input, long ntodo, double scale, double zero, + int nullcheck, unsigned char tnull, signed char nullval, char + *nullarray, int *anynull, signed char *output, int *status); +int fffi2s1(short *input, long ntodo, double scale, double zero, + int nullcheck, short tnull, signed char nullval, char *nullarray, + int *anynull, signed char *output, int *status); +int fffi4s1(INT32BIT *input, long ntodo, double scale, double zero, + int nullcheck, INT32BIT tnull, signed char nullval, char *nullarray, + int *anynull, signed char *output, int *status); +int fffi8s1(LONGLONG *input, long ntodo, double scale, double zero, + int nullcheck, long tnull, signed char nullval, char *nullarray, + int *anynull, signed char *output, int *status); +int fffr4s1(float *input, long ntodo, double scale, double zero, + int nullcheck, signed char nullval, char *nullarray, + int *anynull, signed char *output, int *status); +int fffr8s1(double *input, long ntodo, double scale, double zero, + int nullcheck, signed char nullval, char *nullarray, + int *anynull, signed char *output, int *status); +int fffstrs1(char *input, long ntodo, double scale, double zero, + long twidth, double power, int nullcheck, char *snull, + signed char nullval, char *nullarray, int *anynull, + signed char *output, int *status); + +int fffi1u2(unsigned char *input, long ntodo, double scale, double zero, + int nullcheck, unsigned char tnull, unsigned short nullval, + char *nullarray, + int *anynull, unsigned short *output, int *status); +int fffi2u2(short *input, long ntodo, double scale, double zero, + int nullcheck, short tnull, unsigned short nullval, char *nullarray, + int *anynull, unsigned short *output, int *status); +int fffi4u2(INT32BIT *input, long ntodo, double scale, double zero, + int nullcheck, INT32BIT tnull, unsigned short nullval, char *nullarray, + int *anynull, unsigned short *output, int *status); +int fffi8u2(LONGLONG *input, long ntodo, double scale, double zero, + int nullcheck, long tnull, unsigned short nullval, char *nullarray, + int *anynull, unsigned short *output, int *status); +int fffr4u2(float *input, long ntodo, double scale, double zero, + int nullcheck, unsigned short nullval, char *nullarray, + int *anynull, unsigned short *output, int *status); +int fffr8u2(double *input, long ntodo, double scale, double zero, + int nullcheck, unsigned short nullval, char *nullarray, + int *anynull, unsigned short *output, int *status); +int fffstru2(char *input, long ntodo, double scale, double zero, + long twidth, double power, int nullcheck, char *snull, + unsigned short nullval, char *nullarray, int *anynull, + unsigned short *output, int *status); + +int fffi1i2(unsigned char *input, long ntodo, double scale, double zero, + int nullcheck, unsigned char tnull, short nullval, char *nullarray, + int *anynull, short *output, int *status); +int fffi2i2(short *input, long ntodo, double scale, double zero, + int nullcheck, short tnull, short nullval, char *nullarray, + int *anynull, short *output, int *status); +int fffi4i2(INT32BIT *input, long ntodo, double scale, double zero, + int nullcheck, INT32BIT tnull, short nullval, char *nullarray, + int *anynull, short *output, int *status); +int fffi8i2(LONGLONG *input, long ntodo, double scale, double zero, + int nullcheck, long tnull, short nullval, char *nullarray, + int *anynull, short *output, int *status); +int fffr4i2(float *input, long ntodo, double scale, double zero, + int nullcheck, short nullval, char *nullarray, + int *anynull, short *output, int *status); +int fffr8i2(double *input, long ntodo, double scale, double zero, + int nullcheck, short nullval, char *nullarray, + int *anynull, short *output, int *status); +int fffstri2(char *input, long ntodo, double scale, double zero, + long twidth, double power, int nullcheck, char *snull, + short nullval, char *nullarray, int *anynull, short *output, + int *status); + +int fffi1u4(unsigned char *input, long ntodo, double scale, double zero, + int nullcheck, unsigned char tnull, unsigned long nullval, + char *nullarray, + int *anynull, unsigned long *output, int *status); +int fffi2u4(short *input, long ntodo, double scale, double zero, + int nullcheck, short tnull, unsigned long nullval, char *nullarray, + int *anynull, unsigned long *output, int *status); +int fffi4u4(INT32BIT *input, long ntodo, double scale, double zero, + int nullcheck, INT32BIT tnull, unsigned long nullval, char *nullarray, + int *anynull, unsigned long *output, int *status); +int fffi8u4(LONGLONG *input, long ntodo, double scale, double zero, + int nullcheck, long tnull, unsigned long nullval, char *nullarray, + int *anynull, unsigned long *output, int *status); +int fffr4u4(float *input, long ntodo, double scale, double zero, + int nullcheck, unsigned long nullval, char *nullarray, + int *anynull, unsigned long *output, int *status); +int fffr8u4(double *input, long ntodo, double scale, double zero, + int nullcheck, unsigned long nullval, char *nullarray, + int *anynull, unsigned long *output, int *status); +int fffstru4(char *input, long ntodo, double scale, double zero, + long twidth, double power, int nullcheck, char *snull, + unsigned long nullval, char *nullarray, int *anynull, + unsigned long *output, int *status); + +int fffi1i4(unsigned char *input, long ntodo, double scale, double zero, + int nullcheck, unsigned char tnull, long nullval, char *nullarray, + int *anynull, long *output, int *status); +int fffi2i4(short *input, long ntodo, double scale, double zero, + int nullcheck, short tnull, long nullval, char *nullarray, + int *anynull, long *output, int *status); +int fffi4i4(INT32BIT *input, long ntodo, double scale, double zero, + int nullcheck, INT32BIT tnull, long nullval, char *nullarray, + int *anynull, long *output, int *status); +int fffi8i4(LONGLONG *input, long ntodo, double scale, double zero, + int nullcheck, long tnull, long nullval, char *nullarray, + int *anynull, long *output, int *status); +int fffr4i4(float *input, long ntodo, double scale, double zero, + int nullcheck, long nullval, char *nullarray, + int *anynull, long *output, int *status); +int fffr8i4(double *input, long ntodo, double scale, double zero, + int nullcheck, long nullval, char *nullarray, + int *anynull, long *output, int *status); +int fffstri4(char *input, long ntodo, double scale, double zero, + long twidth, double power, int nullcheck, char *snull, + long nullval, char *nullarray, int *anynull, long *output, + int *status); + +int fffi1int(unsigned char *input, long ntodo, double scale, double zero, + int nullcheck, unsigned char tnull, int nullval, char *nullarray, + int *anynull, int *output, int *status); +int fffi2int(short *input, long ntodo, double scale, double zero, + int nullcheck, short tnull, int nullval, char *nullarray, + int *anynull, int *output, int *status); +int fffi4int(INT32BIT *input, long ntodo, double scale, double zero, + int nullcheck, INT32BIT tnull, int nullval, char *nullarray, + int *anynull, int *output, int *status); +int fffi8int(LONGLONG *input, long ntodo, double scale, double zero, + int nullcheck, long tnull, int nullval, char *nullarray, + int *anynull, int *output, int *status); +int fffr4int(float *input, long ntodo, double scale, double zero, + int nullcheck, int nullval, char *nullarray, + int *anynull, int *output, int *status); +int fffr8int(double *input, long ntodo, double scale, double zero, + int nullcheck, int nullval, char *nullarray, + int *anynull, int *output, int *status); +int fffstrint(char *input, long ntodo, double scale, double zero, + long twidth, double power, int nullcheck, char *snull, + int nullval, char *nullarray, int *anynull, int *output, + int *status); + +int fffi1uint(unsigned char *input, long ntodo, double scale, double zero, + int nullcheck, unsigned char tnull, unsigned int nullval, + char *nullarray, int *anynull, unsigned int *output, int *status); +int fffi2uint(short *input, long ntodo, double scale, double zero, + int nullcheck, short tnull, unsigned int nullval, char *nullarray, + int *anynull, unsigned int *output, int *status); +int fffi4uint(INT32BIT *input, long ntodo, double scale, double zero, + int nullcheck, INT32BIT tnull, unsigned int nullval, char *nullarray, + int *anynull, unsigned int *output, int *status); +int fffi8uint(LONGLONG *input, long ntodo, double scale, double zero, + int nullcheck, long tnull, unsigned int nullval, char *nullarray, + int *anynull, unsigned int *output, int *status); +int fffr4uint(float *input, long ntodo, double scale, double zero, + int nullcheck, unsigned int nullval, char *nullarray, + int *anynull, unsigned int *output, int *status); +int fffr8uint(double *input, long ntodo, double scale, double zero, + int nullcheck, unsigned int nullval, char *nullarray, + int *anynull, unsigned int *output, int *status); +int fffstruint(char *input, long ntodo, double scale, double zero, + long twidth, double power, int nullcheck, char *snull, + unsigned int nullval, char *nullarray, int *anynull, + unsigned int *output, int *status); + +int fffi1i8(unsigned char *input, long ntodo, double scale, double zero, + int nullcheck, unsigned char tnull, LONGLONG nullval, + char *nullarray, int *anynull, LONGLONG *output, int *status); +int fffi2i8(short *input, long ntodo, double scale, double zero, + int nullcheck, short tnull, LONGLONG nullval, char *nullarray, + int *anynull, LONGLONG *output, int *status); +int fffi4i8(INT32BIT *input, long ntodo, double scale, double zero, + int nullcheck, INT32BIT tnull, LONGLONG nullval, char *nullarray, + int *anynull, LONGLONG *output, int *status); +int fffi8i8(LONGLONG *input, long ntodo, double scale, double zero, + int nullcheck, LONGLONG tnull, LONGLONG nullval, char *nullarray, + int *anynull, LONGLONG *output, int *status); +int fffr4i8(float *input, long ntodo, double scale, double zero, + int nullcheck, LONGLONG nullval, char *nullarray, + int *anynull, LONGLONG *output, int *status); +int fffr8i8(double *input, long ntodo, double scale, double zero, + int nullcheck, LONGLONG nullval, char *nullarray, + int *anynull, LONGLONG *output, int *status); +int fffstri8(char *input, long ntodo, double scale, double zero, + long twidth, double power, int nullcheck, char *snull, + LONGLONG nullval, char *nullarray, int *anynull, LONGLONG *output, + int *status); + +int fffi1r4(unsigned char *input, long ntodo, double scale, double zero, + int nullcheck, unsigned char tnull, float nullval, char *nullarray, + int *anynull, float *output, int *status); +int fffi2r4(short *input, long ntodo, double scale, double zero, + int nullcheck, short tnull, float nullval, char *nullarray, + int *anynull, float *output, int *status); +int fffi4r4(INT32BIT *input, long ntodo, double scale, double zero, + int nullcheck, INT32BIT tnull, float nullval, char *nullarray, + int *anynull, float *output, int *status); +int fffi8r4(LONGLONG *input, long ntodo, double scale, double zero, + int nullcheck, long tnull, float nullval, char *nullarray, + int *anynull, float *output, int *status); +int fffr4r4(float *input, long ntodo, double scale, double zero, + int nullcheck, float nullval, char *nullarray, + int *anynull, float *output, int *status); +int fffr8r4(double *input, long ntodo, double scale, double zero, + int nullcheck, float nullval, char *nullarray, + int *anynull, float *output, int *status); +int fffstrr4(char *input, long ntodo, double scale, double zero, + long twidth, double power, int nullcheck, char *snull, + float nullval, char *nullarray, int *anynull, float *output, + int *status); + +int fffi1r8(unsigned char *input, long ntodo, double scale, double zero, + int nullcheck, unsigned char tnull, double nullval, char *nullarray, + int *anynull, double *output, int *status); +int fffi2r8(short *input, long ntodo, double scale, double zero, + int nullcheck, short tnull, double nullval, char *nullarray, + int *anynull, double *output, int *status); +int fffi4r8(INT32BIT *input, long ntodo, double scale, double zero, + int nullcheck, INT32BIT tnull, double nullval, char *nullarray, + int *anynull, double *output, int *status); +int fffi8r8(LONGLONG *input, long ntodo, double scale, double zero, + int nullcheck, long tnull, double nullval, char *nullarray, + int *anynull, double *output, int *status); +int fffr4r8(float *input, long ntodo, double scale, double zero, + int nullcheck, double nullval, char *nullarray, + int *anynull, double *output, int *status); +int fffr8r8(double *input, long ntodo, double scale, double zero, + int nullcheck, double nullval, char *nullarray, + int *anynull, double *output, int *status); +int fffstrr8(char *input, long ntodo, double scale, double zero, + long twidth, double power, int nullcheck, char *snull, + double nullval, char *nullarray, int *anynull, double *output, + int *status); + +int ffi1fi1(unsigned char *array, long ntodo, double scale, double zero, + unsigned char *buffer, int *status); +int ffs1fi1(signed char *array, long ntodo, double scale, double zero, + unsigned char *buffer, int *status); +int ffu2fi1(unsigned short *array, long ntodo, double scale, double zero, + unsigned char *buffer, int *status); +int ffi2fi1(short *array, long ntodo, double scale, double zero, + unsigned char *buffer, int *status); +int ffu4fi1(unsigned long *array, long ntodo, double scale, double zero, + unsigned char *buffer, int *status); +int ffi4fi1(long *array, long ntodo, double scale, double zero, + unsigned char *buffer, int *status); +int ffi8fi1(LONGLONG *array, long ntodo, double scale, double zero, + unsigned char *buffer, int *status); +int ffuintfi1(unsigned int *array, long ntodo, double scale, double zero, + unsigned char *buffer, int *status); +int ffintfi1(int *array, long ntodo, double scale, double zero, + unsigned char *buffer, int *status); +int ffr4fi1(float *array, long ntodo, double scale, double zero, + unsigned char *buffer, int *status); +int ffr8fi1(double *array, long ntodo, double scale, double zero, + unsigned char *buffer, int *status); + +int ffi1fi2(unsigned char *array, long ntodo, double scale, double zero, + short *buffer, int *status); +int ffs1fi2(signed char *array, long ntodo, double scale, double zero, + short *buffer, int *status); +int ffu2fi2(unsigned short *array, long ntodo, double scale, double zero, + short *buffer, int *status); +int ffi2fi2(short *array, long ntodo, double scale, double zero, + short *buffer, int *status); +int ffu4fi2(unsigned long *array, long ntodo, double scale, double zero, + short *buffer, int *status); +int ffi4fi2(long *array, long ntodo, double scale, double zero, + short *buffer, int *status); +int ffi8fi2(LONGLONG *array, long ntodo, double scale, double zero, + short *buffer, int *status); +int ffuintfi2(unsigned int *array, long ntodo, double scale, double zero, + short *buffer, int *status); +int ffintfi2(int *array, long ntodo, double scale, double zero, + short *buffer, int *status); +int ffr4fi2(float *array, long ntodo, double scale, double zero, + short *buffer, int *status); +int ffr8fi2(double *array, long ntodo, double scale, double zero, + short *buffer, int *status); + +int ffi1fi4(unsigned char *array, long ntodo, double scale, double zero, + INT32BIT *buffer, int *status); +int ffs1fi4(signed char *array, long ntodo, double scale, double zero, + INT32BIT *buffer, int *status); +int ffu2fi4(unsigned short *array, long ntodo, double scale, double zero, + INT32BIT *buffer, int *status); +int ffi2fi4(short *array, long ntodo, double scale, double zero, + INT32BIT *buffer, int *status); +int ffu4fi4(unsigned long *array, long ntodo, double scale, double zero, + INT32BIT *buffer, int *status); +int ffi4fi4(long *array, long ntodo, double scale, double zero, + INT32BIT *buffer, int *status); +int ffi8fi4(LONGLONG *array, long ntodo, double scale, double zero, + INT32BIT *buffer, int *status); +int ffuintfi4(unsigned int *array, long ntodo, double scale, double zero, + INT32BIT *buffer, int *status); +int ffintfi4(int *array, long ntodo, double scale, double zero, + INT32BIT *buffer, int *status); +int ffr4fi4(float *array, long ntodo, double scale, double zero, + INT32BIT *buffer, int *status); +int ffr8fi4(double *array, long ntodo, double scale, double zero, + INT32BIT *buffer, int *status); + +int fflongfi8(long *array, long ntodo, double scale, double zero, + LONGLONG *buffer, int *status); +int ffi8fi8(LONGLONG *array, long ntodo, double scale, double zero, + LONGLONG *buffer, int *status); +int ffi2fi8(short *array, long ntodo, double scale, double zero, + LONGLONG *buffer, int *status); +int ffi1fi8(unsigned char *array, long ntodo, double scale, double zero, + LONGLONG *buffer, int *status); +int ffs1fi8(signed char *array, long ntodo, double scale, double zero, + LONGLONG *buffer, int *status); +int ffr4fi8(float *array, long ntodo, double scale, double zero, + LONGLONG *buffer, int *status); +int ffr8fi8(double *array, long ntodo, double scale, double zero, + LONGLONG *buffer, int *status); +int ffintfi8(int *array, long ntodo, double scale, double zero, + LONGLONG *buffer, int *status); +int ffu2fi8(unsigned short *array, long ntodo, double scale, double zero, + LONGLONG *buffer, int *status); +int ffu4fi8(unsigned long *array, long ntodo, double scale, double zero, + LONGLONG *buffer, int *status); +int ffuintfi8(unsigned int *array, long ntodo, double scale, double zero, + LONGLONG *buffer, int *status); + +int ffi1fr4(unsigned char *array, long ntodo, double scale, double zero, + float *buffer, int *status); +int ffs1fr4(signed char *array, long ntodo, double scale, double zero, + float *buffer, int *status); +int ffu2fr4(unsigned short *array, long ntodo, double scale, double zero, + float *buffer, int *status); +int ffi2fr4(short *array, long ntodo, double scale, double zero, + float *buffer, int *status); +int ffu4fr4(unsigned long *array, long ntodo, double scale, double zero, + float *buffer, int *status); +int ffi4fr4(long *array, long ntodo, double scale, double zero, + float *buffer, int *status); +int ffi8fr4(LONGLONG *array, long ntodo, double scale, double zero, + float *buffer, int *status); +int ffuintfr4(unsigned int *array, long ntodo, double scale, double zero, + float *buffer, int *status); +int ffintfr4(int *array, long ntodo, double scale, double zero, + float *buffer, int *status); +int ffr4fr4(float *array, long ntodo, double scale, double zero, + float *buffer, int *status); +int ffr8fr4(double *array, long ntodo, double scale, double zero, + float *buffer, int *status); + +int ffi1fr8(unsigned char *array, long ntodo, double scale, double zero, + double *buffer, int *status); +int ffs1fr8(signed char *array, long ntodo, double scale, double zero, + double *buffer, int *status); +int ffu2fr8(unsigned short *array, long ntodo, double scale, double zero, + double *buffer, int *status); +int ffi2fr8(short *array, long ntodo, double scale, double zero, + double *buffer, int *status); +int ffu4fr8(unsigned long *array, long ntodo, double scale, double zero, + double *buffer, int *status); +int ffi4fr8(long *array, long ntodo, double scale, double zero, + double *buffer, int *status); +int ffi8fr8(LONGLONG *array, long ntodo, double scale, double zero, + double *buffer, int *status); +int ffuintfr8(unsigned int *array, long ntodo, double scale, double zero, + double *buffer, int *status); +int ffintfr8(int *array, long ntodo, double scale, double zero, + double *buffer, int *status); +int ffr4fr8(float *array, long ntodo, double scale, double zero, + double *buffer, int *status); +int ffr8fr8(double *array, long ntodo, double scale, double zero, + double *buffer, int *status); + +int ffi1fstr(unsigned char *input, long ntodo, double scale, double zero, + char *cform, long twidth, char *output, int *status); +int ffs1fstr(signed char *input, long ntodo, double scale, double zero, + char *cform, long twidth, char *output, int *status); +int ffu2fstr(unsigned short *input, long ntodo, double scale, double zero, + char *cform, long twidth, char *output, int *status); +int ffi2fstr(short *input, long ntodo, double scale, double zero, + char *cform, long twidth, char *output, int *status); +int ffu4fstr(unsigned long *input, long ntodo, double scale, double zero, + char *cform, long twidth, char *output, int *status); +int ffi4fstr(long *input, long ntodo, double scale, double zero, + char *cform, long twidth, char *output, int *status); +int ffi8fstr(LONGLONG *input, long ntodo, double scale, double zero, + char *cform, long twidth, char *output, int *status); +int ffintfstr(int *input, long ntodo, double scale, double zero, + char *cform, long twidth, char *output, int *status); +int ffuintfstr(unsigned int *input, long ntodo, double scale, double zero, + char *cform, long twidth, char *output, int *status); +int ffr4fstr(float *input, long ntodo, double scale, double zero, + char *cform, long twidth, char *output, int *status); +int ffr8fstr(double *input, long ntodo, double scale, double zero, + char *cform, long twidth, char *output, int *status); + +/* the following 4 routines are VMS macros used on VAX or Alpha VMS */ +void ieevpd(double *inarray, double *outarray, long *nvals); +void ieevud(double *inarray, double *outarray, long *nvals); +void ieevpr(float *inarray, float *outarray, long *nvals); +void ieevur(float *inarray, float *outarray, long *nvals); + +/* routines related to the lexical parser */ +int ffselect_table(fitsfile **fptr, char *outfile, char *expr, int *status); +int ffiprs( fitsfile *fptr, int compressed, char *expr, int maxdim, + int *datatype, long *nelem, int *naxis, long *naxes, + int *status ); +void ffcprs( void ); +int ffcvtn( int inputType, void *input, char *undef, long ntodo, + int outputType, void *nulval, void *output, + int *anynull, int *status ); +int parse_data( long totalrows, long offset, long firstrow, + long nrows, int nCols, iteratorCol *colData, + void *userPtr ); +int uncompress_hkdata( fitsfile *fptr, long ntimes, + double *times, int *status ); +int ffffrw_work( long totalrows, long offset, long firstrow, + long nrows, int nCols, iteratorCol *colData, + void *userPtr ); + + +/* image compression routines */ +int fits_write_compressed_img(fitsfile *fptr, + int datatype, long *fpixel, long *lpixel, + int nullcheck, void *array, void *nulval, + int *status); +int fits_write_compressed_pixels(fitsfile *fptr, + int datatype, OFF_T fpixel, long npixels, + int nullcheck, void *array, void *nulval, + int *status); +int fits_write_compressed_img_plane(fitsfile *fptr, int datatype, + int bytesperpixel, long nplane, long *firstcoord, long *lastcoord, + long *naxes, int nullcheck, + void *array, void *nullval, long *nread, int *status); + +int imcomp_init_table(fitsfile *outfptr, int compress_type, + int bitpix, int naxis,long *naxes,long *tilesize, + int rice_blocksize,int rice_nbits,int *status); +int imcomp_calc_max_elem (int comptype, int nx, int blocksize); +int imcomp_copy_imheader(fitsfile *infptr, fitsfile *outfptr, + int *status); +int imcomp_compress_image (fitsfile *infptr, fitsfile *outfptr, + int *status); +int imcomp_compress_tile (fitsfile *outfptr, long row, + int datatype, void *tiledata, long tilelen, int *status); + +/* image decompression routines */ + +int fits_read_compressed_img(fitsfile *fptr, + int datatype, long *fpixel,long *lpixel,long *inc, + int nullcheck, void *nulval, void *array, char *nullarray, + int *anynul, int *status); +int fits_read_compressed_pixels(fitsfile *fptr, + int datatype, OFF_T fpixel, long npixels, + int nullcheck, void *nulval, void *array, char *nullarray, + int *anynul, int *status); +int fits_read_compressed_img_plane(fitsfile *fptr, int datatype, + int bytesperpixel, long nplane, long *firstcoord, long *lastcoord, + long *inc, long *naxes, int nullcheck, void *nullval, + void *array, char *nullarray, int *anynul, long *nread, int *status); + +int imcomp_get_compressed_image_par(fitsfile *infptr, int *status); +int imcomp_decompress_tile (fitsfile *infptr, + int nrow, int tilesize, int datatype, int nullcheck, + void *nulval, void *buffer, char *bnullarray, int *anynul, + int *status); +int imcomp_copy_overlap (char *tile, int pixlen, int ndim, + long *tfpixel, long *tlpixel, char *bnullarray, char *image, + long *fpixel, long *lpixel, long *inc, int nullcheck, char *nullarray, + int *status); +int imcomp_merge_overlap (char *tile, int pixlen, int ndim, + long *tfpixel, long *tlpixel, char *bnullarray, char *image, + long *fpixel, long *lpixel, int nullcheck, int *status); + +int fits_quantize_float (float fdata[], int nx, float in_null_value, + int noise_bits, int idata[], double *bscale, double *bzero, + int *iminval, int *imaxval); +int fits_quantize_double (double fdata[], int nx, double in_null_value, + int noise_bits, int idata[], double *bscale, double *bzero, + int *iminval, int *imaxval); +int fits_rcomp(int a[], int nx, unsigned char *c, int clen,int nblock); +int fits_rdecomp (unsigned char *c, int clen, unsigned int array[], int nx, + int nblock); + +int pl_p2li (int *pxsrc, int xs, short *lldst, int npix); +int pl_l2pi (short *ll_src, int xs, int *px_dst, int npix); + +/* general driver routines */ + +int urltype2driver(char *urltype, int *driver); +int fits_init_cfitsio(void); + +int fits_register_driver( char *prefix, + int (*init)(void), + int (*fitsshutdown)(void), + int (*setoptions)(int option), + int (*getoptions)(int *options), + int (*getversion)(int *version), + int (*checkfile) (char *urltype, char *infile, char *outfile), + int (*fitsopen)(char *filename, int rwmode, int *driverhandle), + int (*fitscreate)(char *filename, int *driverhandle), + int (*fitstruncate)(int driverhandle, OFF_T filesize), + int (*fitsclose)(int driverhandle), + int (*fremove)(char *filename), + int (*size)(int driverhandle, OFF_T *size), + int (*flush)(int driverhandle), + int (*seek)(int driverhandle, OFF_T offset), + int (*fitsread) (int driverhandle, void *buffer, long nbytes), + int (*fitswrite)(int driverhandle, void *buffer, long nbytes)); + +/* file driver I/O routines */ + +int file_init(void); +int file_setoptions(int options); +int file_getoptions(int *options); +int file_getversion(int *version); +int file_shutdown(void); +int file_checkfile(char *urltype, char *infile, char *outfile); +int file_open(char *filename, int rwmode, int *driverhandle); +int file_compress_open(char *filename, int rwmode, int *hdl); +int file_openfile(char *filename, int rwmode, FILE **diskfile); +int file_create(char *filename, int *driverhandle); +int file_truncate(int driverhandle, OFF_T filesize); +int file_size(int driverhandle, OFF_T *filesize); +int file_close(int driverhandle); +int file_remove(char *filename); +int file_flush(int driverhandle); +int file_seek(int driverhandle, OFF_T offset); +int file_read (int driverhandle, void *buffer, long nbytes); +int file_write(int driverhandle, void *buffer, long nbytes); +int file_is_compressed(char *filename); + +/* memory driver I/O routines */ + +int mem_init(void); +int mem_setoptions(int options); +int mem_getoptions(int *options); +int mem_getversion(int *version); +int mem_shutdown(void); +int mem_create(char *filename, int *handle); +int mem_create_comp(char *filename, int *handle); +int mem_openmem(void **buffptr, size_t *buffsize, size_t deltasize, + void *(*memrealloc)(void *p, size_t newsize), int *handle); +int mem_createmem(size_t memsize, int *handle); +int stdin_checkfile(char *urltype, char *infile, char *outfile); +int stdin_open(char *filename, int rwmode, int *handle); +int stdin2mem(int hd); +int stdin2file(int hd); +int stdout_close(int handle); +int mem_compress_openrw(char *filename, int rwmode, int *hdl); +int mem_compress_open(char *filename, int rwmode, int *hdl); +int mem_iraf_open(char *filename, int rwmode, int *hdl); +int mem_rawfile_open(char *filename, int rwmode, int *hdl); +int mem_size(int handle, OFF_T *filesize); +int mem_truncate(int handle, OFF_T filesize); +int mem_close_free(int handle); +int mem_close_keep(int handle); +int mem_close_comp(int handle); +int mem_seek(int handle, OFF_T offset); +int mem_read(int hdl, void *buffer, long nbytes); +int mem_write(int hdl, void *buffer, long nbytes); +int mem_uncompress2mem(char *filename, FILE *diskfile, int hdl); + +int iraf2mem(char *filename, char **buffptr, size_t *buffsize, + size_t *filesize, int *status); + +/* root driver I/O routines */ + +int root_init(void); +int root_setoptions(int options); +int root_getoptions(int *options); +int root_getversion(int *version); +int root_shutdown(void); +int root_open(char *filename, int rwmode, int *driverhandle); +int root_create(char *filename, int *driverhandle); +int root_close(int driverhandle); +int root_flush(int driverhandle); +int root_seek(int driverhandle, OFF_T offset); +int root_read (int driverhandle, void *buffer, long nbytes); +int root_write(int driverhandle, void *buffer, long nbytes); +int root_size(int handle, OFF_T *filesize); + +/* http driver I/O routines */ + +int http_checkfile(char *urltype, char *infile, char *outfile); +int http_open(char *filename, int rwmode, int *driverhandle); +int http_file_open(char *filename, int rwmode, int *driverhandle); +int http_compress_open(char *filename, int rwmode, int *driverhandle); + +/* ftp driver I/O routines */ + +int ftp_checkfile(char *urltype, char *infile, char *outfile); +int ftp_open(char *filename, int rwmode, int *driverhandle); +int ftp_file_open(char *filename, int rwmode, int *driverhandle); +int ftp_compress_open(char *filename, int rwmode, int *driverhandle); + + +int uncompress2mem(char *filename, FILE *diskfile, + char **buffptr, size_t *buffsize, + void *(*mem_realloc)(void *p, size_t newsize), + size_t *filesize, int *status); + +int uncompress2mem_from_mem( + char *inmemptr, + size_t inmemsize, + char **buffptr, + size_t *buffsize, + void *(*mem_realloc)(void *p, size_t newsize), + size_t *filesize, + int *status); + +int uncompress2file(char *filename, + FILE *indiskfile, + FILE *outdiskfile, + int *status); + +int compress2mem_from_mem( + char *inmemptr, + size_t inmemsize, + char **buffptr, + size_t *buffsize, + void *(*mem_realloc)(void *p, size_t newsize), + size_t *filesize, + int *status); + +int compress2file_from_mem( + char *inmemptr, + size_t inmemsize, + FILE *outdiskfile, + size_t *filesize, /* O - size of file, in bytes */ + int *status); + +/* ==================== SHARED MEMORY DRIVER SECTION ======================= */ + +#ifdef HAVE_SHMEM_SERVICES +#include "drvrsmem.h" +#endif + +/* ==================== END OF SHARED MEMORY DRIVER SECTION ================ */ + +#endif + + +#if defined(vms) || defined(__vms) || defined(WIN32) || defined(__WIN32__) || (defined(macintosh) && !defined(TARGET_API_MAC_CARBON)) + +/* ================================================================== */ +/* A hack for nonunix machines, which lack strcasecmp and strncasecmp */ +/* ================================================================== */ + +int strcasecmp (const char *s1, const char *s2 ); +int strncasecmp(const char *s1, const char *s2, size_t n); + +#endif diff --git a/contrib/include/longnam.h b/contrib/include/longnam.h new file mode 100644 index 00000000..4e34f2c2 --- /dev/null +++ b/contrib/include/longnam.h @@ -0,0 +1,535 @@ +#ifndef _LONGNAME_H +#define _LONGNAME_H + +#define fits_parse_input_url ffiurl +#define fits_parse_rootname ffrtnm +#define fits_parse_output_url ffourl +#define fits_parse_extspec ffexts +#define fits_parse_extnum ffextn +#define fits_parse_binspec ffbins +#define fits_parse_binrange ffbinr +#define fits_parse_range ffrwrg +#define fits_open_memfile ffomem +#define fits_open_file ffopen +#define fits_open_data ffdopn +#define fits_open_table fftopn +#define fits_open_image ffiopn +#define fits_reopen_file ffreopen +#define fits_create_file ffinit +#define fits_create_memfile ffimem +#define fits_create_template fftplt +#define fits_flush_file ffflus +#define fits_flush_buffer ffflsh +#define fits_close_file ffclos +#define fits_delete_file ffdelt +#define fits_file_name ffflnm +#define fits_file_mode ffflmd +#define fits_url_type ffurlt + +#define fits_get_version ffvers +#define fits_uppercase ffupch +#define fits_get_errstatus ffgerr +#define fits_write_errmsg ffpmsg +#define fits_write_errmark ffpmrk +#define fits_read_errmsg ffgmsg +#define fits_clear_errmsg ffcmsg +#define fits_clear_errmark ffcmrk +#define fits_report_error ffrprt +#define fits_compare_str ffcmps +#define fits_test_keyword fftkey +#define fits_test_record fftrec +#define fits_null_check ffnchk +#define fits_make_keyn ffkeyn +#define fits_make_nkey ffnkey +#define fits_get_keyclass ffgkcl +#define fits_get_keytype ffdtyp +#define fits_parse_value ffpsvc +#define fits_get_keyname ffgknm +#define fits_parse_template ffgthd +#define fits_ascii_tform ffasfm +#define fits_binary_tform ffbnfm +#define fits_get_tbcol ffgabc +#define fits_get_rowsize ffgrsz +#define fits_get_col_display_width ffgcdw + +#define fits_write_record ffprec +#define fits_write_key ffpky +#define fits_write_key_unit ffpunt +#define fits_write_comment ffpcom +#define fits_write_history ffphis +#define fits_write_date ffpdat +#define fits_get_system_time ffgstm +#define fits_get_system_date ffgsdt +#define fits_date2str ffdt2s +#define fits_time2str fftm2s +#define fits_str2date ffs2dt +#define fits_str2time ffs2tm +#define fits_write_key_longstr ffpkls +#define fits_write_key_longwarn ffplsw +#define fits_write_key_null ffpkyu +#define fits_write_key_str ffpkys +#define fits_write_key_log ffpkyl +#define fits_write_key_lng ffpkyj +#define fits_write_key_fixflt ffpkyf +#define fits_write_key_flt ffpkye +#define fits_write_key_fixdbl ffpkyg +#define fits_write_key_dbl ffpkyd +#define fits_write_key_fixcmp ffpkfc +#define fits_write_key_cmp ffpkyc +#define fits_write_key_fixdblcmp ffpkfm +#define fits_write_key_dblcmp ffpkym +#define fits_write_key_triple ffpkyt +#define fits_write_tdim ffptdm +#define fits_write_keys_str ffpkns +#define fits_write_keys_log ffpknl +#define fits_write_keys_lng ffpknj +#define fits_write_keys_fixflt ffpknf +#define fits_write_keys_flt ffpkne +#define fits_write_keys_fixdbl ffpkng +#define fits_write_keys_dbl ffpknd +#define fits_copy_key ffcpky +#define fits_write_imghdr ffphps +#define fits_write_grphdr ffphpr +#define fits_write_atblhdr ffphtb +#define fits_write_btblhdr ffphbn +#define fits_write_key_template ffpktp + +#define fits_get_hdrspace ffghsp +#define fits_get_hdrpos ffghps +#define fits_movabs_key ffmaky +#define fits_movrel_key ffmrky +#define fits_find_nextkey ffgnxk + +#define fits_read_record ffgrec +#define fits_read_card ffgcrd +#define fits_read_key_unit ffgunt +#define fits_read_keyn ffgkyn +#define fits_read_key ffgky +#define fits_read_keyword ffgkey +#define fits_read_key_str ffgkys +#define fits_read_key_log ffgkyl +#define fits_read_key_lng ffgkyj +#define fits_read_key_flt ffgkye +#define fits_read_key_dbl ffgkyd +#define fits_read_key_cmp ffgkyc +#define fits_read_key_dblcmp ffgkym +#define fits_read_key_triple ffgkyt +#define fits_read_key_longstr ffgkls +#define fits_read_tdim ffgtdm +#define fits_decode_tdim ffdtdm +#define fits_read_keys_str ffgkns +#define fits_read_keys_log ffgknl +#define fits_read_keys_lng ffgknj +#define fits_read_keys_flt ffgkne +#define fits_read_keys_dbl ffgknd +#define fits_read_imghdr ffghpr +#define fits_read_atblhdr ffghtb +#define fits_read_btblhdr ffghbn +#define fits_hdr2str ffhdr2str + +#define fits_update_card ffucrd +#define fits_update_key ffuky +#define fits_update_key_null ffukyu +#define fits_update_key_str ffukys +#define fits_update_key_longstr ffukls +#define fits_update_key_log ffukyl +#define fits_update_key_lng ffukyj +#define fits_update_key_fixflt ffukyf +#define fits_update_key_flt ffukye +#define fits_update_key_fixdbl ffukyg +#define fits_update_key_dbl ffukyd +#define fits_update_key_fixcmp ffukfc +#define fits_update_key_cmp ffukyc +#define fits_update_key_fixdblcmp ffukfm +#define fits_update_key_dblcmp ffukym + +#define fits_modify_record ffmrec +#define fits_modify_card ffmcrd +#define fits_modify_name ffmnam +#define fits_modify_comment ffmcom +#define fits_modify_key_null ffmkyu +#define fits_modify_key_str ffmkys +#define fits_modify_key_longstr ffmkls +#define fits_modify_key_log ffmkyl +#define fits_modify_key_lng ffmkyj +#define fits_modify_key_fixflt ffmkyf +#define fits_modify_key_flt ffmkye +#define fits_modify_key_fixdbl ffmkyg +#define fits_modify_key_dbl ffmkyd +#define fits_modify_key_fixcmp ffmkfc +#define fits_modify_key_cmp ffmkyc +#define fits_modify_key_fixdblcmp ffmkfm +#define fits_modify_key_dblcmp ffmkym + +#define fits_insert_record ffirec +#define fits_insert_card ffikey +#define fits_insert_key_null ffikyu +#define fits_insert_key_str ffikys +#define fits_insert_key_longstr ffikls +#define fits_insert_key_log ffikyl +#define fits_insert_key_lng ffikyj +#define fits_insert_key_fixflt ffikyf +#define fits_insert_key_flt ffikye +#define fits_insert_key_fixdbl ffikyg +#define fits_insert_key_dbl ffikyd +#define fits_insert_key_fixcmp ffikfc +#define fits_insert_key_cmp ffikyc +#define fits_insert_key_fixdblcmp ffikfm +#define fits_insert_key_dblcmp ffikym + +#define fits_delete_key ffdkey +#define fits_delete_record ffdrec +#define fits_get_hdu_num ffghdn +#define fits_get_hdu_type ffghdt +#define fits_get_hduaddr ffghad +#define fits_get_hduoff ffghof + +#define fits_get_img_param ffgipr +#define fits_get_img_type ffgidt +#define fits_get_img_equivtype ffgiet +#define fits_get_img_dim ffgidm +#define fits_get_img_size ffgisz + +#define fits_movabs_hdu ffmahd +#define fits_movrel_hdu ffmrhd +#define fits_movnam_hdu ffmnhd +#define fits_get_num_hdus ffthdu +#define fits_create_img ffcrim +#define fits_create_tbl ffcrtb +#define fits_create_hdu ffcrhd +#define fits_insert_img ffiimg +#define fits_insert_atbl ffitab +#define fits_insert_btbl ffibin +#define fits_resize_img ffrsim +#define fits_delete_hdu ffdhdu +#define fits_copy_hdu ffcopy +#define fits_copy_file ffcpfl +#define fits_copy_header ffcphd +#define fits_copy_data ffcpdt + +#define fits_set_hdustruc ffrdef +#define fits_set_hdrsize ffhdef +#define fits_write_theap ffpthp + +#define fits_encode_chksum ffesum +#define fits_decode_chksum ffdsum +#define fits_write_chksum ffpcks +#define fits_update_chksum ffupck +#define fits_verify_chksum ffvcks +#define fits_get_chksum ffgcks + +#define fits_set_bscale ffpscl +#define fits_set_tscale fftscl +#define fits_set_imgnull ffpnul +#define fits_set_btblnull fftnul +#define fits_set_atblnull ffsnul + +#define fits_get_colnum ffgcno +#define fits_get_colname ffgcnn +#define fits_get_coltype ffgtcl +#define fits_get_eqcoltype ffeqty +#define fits_get_num_rows ffgnrw +#define fits_get_num_cols ffgncl +#define fits_get_acolparms ffgacl +#define fits_get_bcolparms ffgbcl + +#define fits_iterate_data ffiter + +#define fits_read_grppar_byt ffggpb +#define fits_read_grppar_sbyt ffggpsb +#define fits_read_grppar_usht ffggpui +#define fits_read_grppar_ulng ffggpuj +#define fits_read_grppar_sht ffggpi +#define fits_read_grppar_lng ffggpj +#define fits_read_grppar_lnglng ffggpjj +#define fits_read_grppar_int ffggpk +#define fits_read_grppar_uint ffggpuk +#define fits_read_grppar_flt ffggpe +#define fits_read_grppar_dbl ffggpd + +#define fits_read_pix ffgpxv +#define fits_read_pixnull ffgpxf +#define fits_read_img ffgpv +#define fits_read_imgnull ffgpf +#define fits_read_img_byt ffgpvb +#define fits_read_img_sbyt ffgpvsb +#define fits_read_img_usht ffgpvui +#define fits_read_img_ulng ffgpvuj +#define fits_read_img_sht ffgpvi +#define fits_read_img_lng ffgpvj +#define fits_read_img_lnglng ffgpvjj +#define fits_read_img_uint ffgpvuk +#define fits_read_img_int ffgpvk +#define fits_read_img_flt ffgpve +#define fits_read_img_dbl ffgpvd + +#define fits_read_imgnull_byt ffgpfb +#define fits_read_imgnull_sbyt ffgpfsb +#define fits_read_imgnull_usht ffgpfui +#define fits_read_imgnull_ulng ffgpfuj +#define fits_read_imgnull_sht ffgpfi +#define fits_read_imgnull_lng ffgpfj +#define fits_read_imgnull_lnglng ffgpfjj +#define fits_read_imgnull_uint ffgpfuk +#define fits_read_imgnull_int ffgpfk +#define fits_read_imgnull_flt ffgpfe +#define fits_read_imgnull_dbl ffgpfd + +#define fits_read_2d_byt ffg2db +#define fits_read_2d_sbyt ffg2dsb +#define fits_read_2d_usht ffg2dui +#define fits_read_2d_ulng ffg2duj +#define fits_read_2d_sht ffg2di +#define fits_read_2d_lng ffg2dj +#define fits_read_2d_lnglng ffg2djj +#define fits_read_2d_uint ffg2duk +#define fits_read_2d_int ffg2dk +#define fits_read_2d_flt ffg2de +#define fits_read_2d_dbl ffg2dd + +#define fits_read_3d_byt ffg3db +#define fits_read_3d_sbyt ffg3dsb +#define fits_read_3d_usht ffg3dui +#define fits_read_3d_ulng ffg3duj +#define fits_read_3d_sht ffg3di +#define fits_read_3d_lng ffg3dj +#define fits_read_3d_lnglng ffg3djj +#define fits_read_3d_uint ffg3duk +#define fits_read_3d_int ffg3dk +#define fits_read_3d_flt ffg3de +#define fits_read_3d_dbl ffg3dd + +#define fits_read_subset ffgsv +#define fits_read_subset_byt ffgsvb +#define fits_read_subset_sbyt ffgsvsb +#define fits_read_subset_usht ffgsvui +#define fits_read_subset_ulng ffgsvuj +#define fits_read_subset_sht ffgsvi +#define fits_read_subset_lng ffgsvj +#define fits_read_subset_lnglng ffgsvjj +#define fits_read_subset_uint ffgsvuk +#define fits_read_subset_int ffgsvk +#define fits_read_subset_flt ffgsve +#define fits_read_subset_dbl ffgsvd + +#define fits_read_subsetnull_byt ffgsfb +#define fits_read_subsetnull_sbyt ffgsfsb +#define fits_read_subsetnull_usht ffgsfui +#define fits_read_subsetnull_ulng ffgsfuj +#define fits_read_subsetnull_sht ffgsfi +#define fits_read_subsetnull_lng ffgsfj +#define fits_read_subsetnull_lnglng ffgsfjj +#define fits_read_subsetnull_uint ffgsfuk +#define fits_read_subsetnull_int ffgsfk +#define fits_read_subsetnull_flt ffgsfe +#define fits_read_subsetnull_dbl ffgsfd + +#define fits_compress_img fits_comp_img +#define fits_decompress_img fits_decomp_img + +#define fits_read_col ffgcv +#define fits_read_colnull ffgcf +#define fits_read_col_str ffgcvs +#define fits_read_col_log ffgcvl +#define fits_read_col_byt ffgcvb +#define fits_read_col_sbyt ffgcvsb +#define fits_read_col_usht ffgcvui +#define fits_read_col_ulng ffgcvuj +#define fits_read_col_sht ffgcvi +#define fits_read_col_lng ffgcvj +#define fits_read_col_lnglng ffgcvjj +#define fits_read_col_uint ffgcvuk +#define fits_read_col_int ffgcvk +#define fits_read_col_flt ffgcve +#define fits_read_col_dbl ffgcvd +#define fits_read_col_cmp ffgcvc +#define fits_read_col_dblcmp ffgcvm +#define fits_read_col_bit ffgcx +#define fits_read_col_bit_usht ffgcxui +#define fits_read_col_bit_uint ffgcxuk + +#define fits_read_colnull_str ffgcfs +#define fits_read_colnull_log ffgcfl +#define fits_read_colnull_byt ffgcfb +#define fits_read_colnull_sbyt ffgcfsb +#define fits_read_colnull_usht ffgcfui +#define fits_read_colnull_ulng ffgcfuj +#define fits_read_colnull_sht ffgcfi +#define fits_read_colnull_lng ffgcfj +#define fits_read_colnull_lnglng ffgcfjj +#define fits_read_colnull_uint ffgcfuk +#define fits_read_colnull_int ffgcfk +#define fits_read_colnull_flt ffgcfe +#define fits_read_colnull_dbl ffgcfd +#define fits_read_colnull_cmp ffgcfc +#define fits_read_colnull_dblcmp ffgcfm + +#define fits_read_descript ffgdes +#define fits_read_descripts ffgdess +#define fits_read_tblbytes ffgtbb + +#define fits_write_grppar_byt ffpgpb +#define fits_write_grppar_sbyt ffpgpsb +#define fits_write_grppar_usht ffpgpui +#define fits_write_grppar_ulng ffpgpuj +#define fits_write_grppar_sht ffpgpi +#define fits_write_grppar_lng ffpgpj +#define fits_write_grppar_lnglng ffpgpjj +#define fits_write_grppar_uint ffpgpuk +#define fits_write_grppar_int ffpgpk +#define fits_write_grppar_flt ffpgpe +#define fits_write_grppar_dbl ffpgpd + +#define fits_write_pix ffppx +#define fits_write_pixnull ffppxn +#define fits_write_img ffppr +#define fits_write_img_byt ffpprb +#define fits_write_img_sbyt ffpprsb +#define fits_write_img_usht ffpprui +#define fits_write_img_ulng ffppruj +#define fits_write_img_sht ffppri +#define fits_write_img_lng ffpprj +#define fits_write_img_lnglng ffpprjj +#define fits_write_img_uint ffppruk +#define fits_write_img_int ffpprk +#define fits_write_img_flt ffppre +#define fits_write_img_dbl ffpprd + +#define fits_write_imgnull ffppn +#define fits_write_imgnull_byt ffppnb +#define fits_write_imgnull_sbyt ffppnsb +#define fits_write_imgnull_usht ffppnui +#define fits_write_imgnull_ulng ffppnuj +#define fits_write_imgnull_sht ffppni +#define fits_write_imgnull_lng ffppnj +#define fits_write_imgnull_lnglng ffppnjj +#define fits_write_imgnull_uint ffppnuk +#define fits_write_imgnull_int ffppnk +#define fits_write_imgnull_flt ffppne +#define fits_write_imgnull_dbl ffppnd + +#define fits_write_img_null ffppru +#define fits_write_null_img ffpprn + +#define fits_write_2d_byt ffp2db +#define fits_write_2d_sbyt ffp2dsb +#define fits_write_2d_usht ffp2dui +#define fits_write_2d_ulng ffp2duj +#define fits_write_2d_sht ffp2di +#define fits_write_2d_lng ffp2dj +#define fits_write_2d_lnglng ffp2djj +#define fits_write_2d_uint ffp2duk +#define fits_write_2d_int ffp2dk +#define fits_write_2d_flt ffp2de +#define fits_write_2d_dbl ffp2dd + +#define fits_write_3d_byt ffp3db +#define fits_write_3d_sbyt ffp3dsb +#define fits_write_3d_usht ffp3dui +#define fits_write_3d_ulng ffp3duj +#define fits_write_3d_sht ffp3di +#define fits_write_3d_lng ffp3dj +#define fits_write_3d_lnglng ffp3djj +#define fits_write_3d_uint ffp3duk +#define fits_write_3d_int ffp3dk +#define fits_write_3d_flt ffp3de +#define fits_write_3d_dbl ffp3dd + +#define fits_write_subset ffpss +#define fits_write_subset_byt ffpssb +#define fits_write_subset_sbyt ffpsssb +#define fits_write_subset_usht ffpssui +#define fits_write_subset_ulng ffpssuj +#define fits_write_subset_sht ffpssi +#define fits_write_subset_lng ffpssj +#define fits_write_subset_lnglng ffpssjj +#define fits_write_subset_uint ffpssuk +#define fits_write_subset_int ffpssk +#define fits_write_subset_flt ffpsse +#define fits_write_subset_dbl ffpssd + +#define fits_write_col ffpcl +#define fits_write_col_str ffpcls +#define fits_write_col_log ffpcll +#define fits_write_col_byt ffpclb +#define fits_write_col_sbyt ffpclsb +#define fits_write_col_usht ffpclui +#define fits_write_col_ulng ffpcluj +#define fits_write_col_sht ffpcli +#define fits_write_col_lng ffpclj +#define fits_write_col_lnglng ffpcljj +#define fits_write_col_uint ffpcluk +#define fits_write_col_int ffpclk +#define fits_write_col_flt ffpcle +#define fits_write_col_dbl ffpcld +#define fits_write_col_cmp ffpclc +#define fits_write_col_dblcmp ffpclm +#define fits_write_col_null ffpclu +#define fits_write_col_bit ffpclx + +#define fits_write_colnull ffpcn +#define fits_write_colnull_str ffpcns +#define fits_write_colnull_log ffpcnl +#define fits_write_colnull_byt ffpcnb +#define fits_write_colnull_sbyt ffpcnsb +#define fits_write_colnull_usht ffpcnui +#define fits_write_colnull_ulng ffpcnuj +#define fits_write_colnull_sht ffpcni +#define fits_write_colnull_lng ffpcnj +#define fits_write_colnull_lnglng ffpcnjj +#define fits_write_colnull_uint ffpcnuk +#define fits_write_colnull_int ffpcnk +#define fits_write_colnull_flt ffpcne +#define fits_write_colnull_dbl ffpcnd + +#define fits_write_descript ffpdes +#define fits_compress_heap ffcmph +#define fits_test_heap fftheap + +#define fits_write_tblbytes ffptbb +#define fits_insert_rows ffirow +#define fits_delete_rows ffdrow +#define fits_delete_rowrange ffdrrg +#define fits_delete_rowlist ffdrws +#define fits_insert_col fficol +#define fits_insert_cols fficls +#define fits_delete_col ffdcol +#define fits_copy_col ffcpcl +#define fits_modify_vector_len ffmvec + +#define fits_read_img_coord ffgics +#define fits_read_tbl_coord ffgtcs +#define fits_pix_to_world ffwldp +#define fits_world_to_pix ffxypx + +#define fits_get_image_wcs_keys ffgiwcs +#define fits_get_table_wcs_keys ffgtwcs + +#define fits_find_rows fffrow +#define fits_find_first_row ffffrw +#define fits_find_rows_cmp fffrwc +#define fits_select_rows ffsrow +#define fits_calc_rows ffcrow +#define fits_calculator ffcalc +#define fits_calculator_rng ffcalc_rng +#define fits_test_expr fftexp + +#define fits_create_group ffgtcr +#define fits_insert_group ffgtis +#define fits_change_group ffgtch +#define fits_remove_group ffgtrm +#define fits_copy_group ffgtcp +#define fits_merge_groups ffgtmg +#define fits_compact_group ffgtcm +#define fits_verify_group ffgtvf +#define fits_open_group ffgtop +#define fits_add_group_member ffgtam +#define fits_get_num_members ffgtnm + +#define fits_get_num_groups ffgmng +#define fits_open_member ffgmop +#define fits_copy_member ffgmcp +#define fits_transfer_member ffgmtf +#define fits_remove_member ffgmrm + +#endif diff --git a/contrib/include/rdcolor.h b/contrib/include/rdcolor.h new file mode 100644 index 00000000..0aa99593 --- /dev/null +++ b/contrib/include/rdcolor.h @@ -0,0 +1,89 @@ +/* color.h: this file specifies that all colors are actually pointers to + * color strings. It is expected that the function "rdcolor()" will + * be used to initialize the colors. "rdcolor()" examines a colorfile + * called "color.dat" for color strings: color ... where the ellipsis + * is the desired escape string. The default value of such color strings + * is set for a color ASCII monitor (8 colors) and the IDS Prism printer. + */ +#ifndef RDCOLOR_H +#define RDCOLOR_H + +#ifndef __GL_GL_H__ +extern char *BLACK; +extern char *RED; +extern char *GREEN; +extern char *YELLOW; +extern char *BLUE; +extern char *MAGENTA; +extern char *CYAN; +extern char *WHITE; +#endif + +extern char *RD_BLACK; /* same as BLACK */ +extern char *RD_RED; /* same as RED */ +extern char *RD_GREEN; /* same as GREEN */ +extern char *RD_YELLOW; /* same as YELLOW */ +extern char *RD_BLUE; /* same as BLUE */ +extern char *RD_MAGENTA; /* same as MAGENTA */ +extern char *RD_CYAN; /* same as CYAN */ +extern char *RD_WHITE; /* same as WHITE */ + +extern char *UBLACK; +extern char *URED; +extern char *UGREEN; +extern char *UYELLOW; +extern char *UBLUE; +extern char *UMAGENTA; +extern char *UCYAN; +extern char *UWHITE; +extern char *RVBLACK; +extern char *RVRED; +extern char *RVGREEN; +extern char *RVYELLOW; +extern char *RVBLUE; +extern char *RVMAGENTA; +extern char *RVCYAN; +extern char *RVWHITE; +#ifndef VIM__H +extern char *CLEAR; +#endif +extern char *BOLD; +extern char *NRML; +extern char *PYELLOW; +extern char *PRED; +extern char *PCYAN; +extern char *PBLACK; + +/* for rdcputs's benefit... */ +#define XBLACK "\255X0" +#define XRED "\255X1" +#define XGREEN "\255X2" +#define XYELLOW "\255X3" +#define XBLUE "\255X4" +#define XMAGENTA "\255X5" +#define XCYAN "\255X6" +#define XWHITE "\255X7" +#define XUBLACK "\255X8" +#define XURED "\255X9" +#define XUGREEN "\255X10" +#define XUYELLOW "\255X11" +#define XUBLUE "\255X12" +#define XUMAGENTA "\255X13" +#define XUCYAN "\255X14" +#define XUWHITE "\255X15" +#define XRVBLACK "\255X16" +#define XRVRED "\255X17" +#define XRVGREEN "\255X18" +#define XRVYELLOW "\255X19" +#define XRVBLUE "\255X20" +#define XRVMAGENTA "\255X21" +#define XRVCYAN "\255X22" +#define XRVWHITE "\255X23" +#define XBOLD "\255X24" +#define XNRML "\255X25" +#define XPYELLOW "\255X26" +#define XPRED "\255X27" +#define XPCYAN "\255X28" +#define XPBLACK "\255X29" +#define XCLEAR "\255X30" +#endif diff --git a/contrib/include/sbigudrv.h b/contrib/include/sbigudrv.h new file mode 100644 index 00000000..830faf4b --- /dev/null +++ b/contrib/include/sbigudrv.h @@ -0,0 +1,668 @@ +/* + + SBIGUDRV.H + + Contains the function prototypes and enumerated constants + for the Universal Parallel/USB/Ethernet driver. + + This supports the following devices: + + ST-7E/8E/9E/10E + ST-5C/237/237A (PixCel255/237) + ST-1K, ST-2K + ST-L Large Format Camera + ST-F Feather Camera + AO-7 + + Version 4.35 - December 10, 2003 + + (c)1995-2003 - Santa Barbara Instrument Group + +*/ +#ifndef _PARDRV_ +#define _PARDRV_ + +/* + + SBIG Specific Code + +*/ +#ifndef TARGET + #define ENV_WIN 1 /* Target for Windows environment */ + #define ENV_WINVXD 2 /* SBIG Use Only, Win 9X VXD */ + #define ENV_WINSYS 3 /* SBIG Use Only, Win NT SYS */ + #define ENV_ESRVJK 4 /* SBIG Use Only, Ethernet Remote */ + #define ENV_ESRVWIN 5 /* SBIG Use Only, Ethernet Remote */ + #define ENV_MACOSX 6 /* SBIG Use Only, Mac OSX */ + #define ENV_LINUX 7 /* SBIG Use Only, Linux */ + #define TARGET ENV_LINUX /* Set for your target */ +#endif + +/* + + Enumerated Constants + + Note that the various constants are declared here as enums + for ease of declaration but in the structures that use the + enums unsigned shorts are used to force the various + 16 and 32 bit compilers to use 16 bits. + +*/ + +/* + + Supported Camera Commands + + These are the commands supported by the driver. + They are prefixed by CC_ to designate them as + camera commands and avoid conflicts with other + enums. + + Some of the commands are marked as SBIG use only + and have been included to enhance testability + of the driver for SBIG. + +*/ +typedef enum { + /* + + General Use Commands + + */ + CC_NULL, + + /* 1 - 10 */ + CC_START_EXPOSURE=1, CC_END_EXPOSURE, CC_READOUT_LINE, + CC_DUMP_LINES, CC_SET_TEMPERATURE_REGULATION, + CC_QUERY_TEMPERATURE_STATUS, CC_ACTIVATE_RELAY, CC_PULSE_OUT, + CC_ESTABLISH_LINK, CC_GET_DRIVER_INFO, + + /* 11 - 20 */ + CC_GET_CCD_INFO, CC_QUERY_COMMAND_STATUS, CC_MISCELLANEOUS_CONTROL, + CC_READ_SUBTRACT_LINE, CC_UPDATE_CLOCK, CC_READ_OFFSET, + CC_OPEN_DRIVER, CC_CLOSE_DRIVER, + CC_TX_SERIAL_BYTES, CC_GET_SERIAL_STATUS, + + /* 21 - 30 */ + CC_AO_TIP_TILT, CC_AO_SET_FOCUS, CC_AO_DELAY, + CC_GET_TURBO_STATUS, CC_END_READOUT, CC_GET_US_TIMER, + CC_OPEN_DEVICE, CC_CLOSE_DEVICE, CC_SET_IRQL, CC_GET_IRQL, + + /* 31 - 40 */ + CC_GET_LINE, CC_GET_LINK_STATUS, CC_GET_DRIVER_HANDLE, + CC_SET_DRIVER_HANDLE, CC_START_READOUT, CC_GET_ERROR_STRING, + CC_SET_DRIVER_CONTROL, CC_GET_DRIVER_CONTROL, + CC_USB_AD_CONTROL, CC_QUERY_USB, + + /* 41 - 50 */ + CC_GET_PENTIUM_CYCLE_COUNT, CC_RW_USB_I2C, CC_CFW, CC_BIT_IO, + + /* + + SBIG Use Only Commands + + */ + CC_SEND_BLOCK=90, CC_SEND_BYTE, CC_GET_BYTE, CC_SEND_AD, + CC_GET_AD, CC_CLOCK_AD, CC_SYSTEM_TEST, + CC_GET_DRIVER_OPTIONS, CC_SET_DRIVER_OPTIONS, + CC_LAST_COMMAND} PAR_COMMAND; + +/* + + Return Error Codes + + These are the error codes returned by the driver + function. They are prefixed with CE_ to designate + them as camera errors. + +*/ +#ifndef CE_ERROR_BASE + #define CE_ERROR_BASE 1 +#endif + +typedef enum { + /* 0 - 10 */ + CE_NO_ERROR, CE_CAMERA_NOT_FOUND=CE_ERROR_BASE, + CE_EXPOSURE_IN_PROGRESS, CE_NO_EXPOSURE_IN_PROGRESS, + CE_UNKNOWN_COMMAND, CE_BAD_CAMERA_COMMAND, CE_BAD_PARAMETER, + CE_TX_TIMEOUT, CE_RX_TIMEOUT, CE_NAK_RECEIVED, CE_CAN_RECEIVED, + + /* 11 - 20 */ + CE_UNKNOWN_RESPONSE, CE_BAD_LENGTH, + CE_AD_TIMEOUT, CE_KBD_ESC, CE_CHECKSUM_ERROR, CE_EEPROM_ERROR, + CE_SHUTTER_ERROR, CE_UNKNOWN_CAMERA, + CE_DRIVER_NOT_FOUND, CE_DRIVER_NOT_OPEN, + + /* 21 - 30 */ + CE_DRIVER_NOT_CLOSED, CE_SHARE_ERROR, CE_TCE_NOT_FOUND, CE_AO_ERROR, + CE_ECP_ERROR, CE_MEMORY_ERROR, CE_DEVICE_NOT_FOUND, + CE_DEVICE_NOT_OPEN, CE_DEVICE_NOT_CLOSED, + CE_DEVICE_NOT_IMPLEMENTED, + + /* 31 - 40 */ + CE_DEVICE_DISABLED, CE_OS_ERROR, CE_SOCK_ERROR, CE_SERVER_NOT_FOUND, + CE_CFW_ERROR, CE_NEXT_ERROR} PAR_ERROR; + +/* + + Camera Command State Codes + + These are the return status codes for the Query + Command Status command. Theyt are prefixed with + CS_ to designate them as camera status. + +*/ +typedef enum { CS_IDLE, CS_IN_PROGRESS, CS_INTEGRATING, + CS_INTEGRATION_COMPLETE } PAR_COMMAND_STATUS; +#define CS_PULSE_IN_ACTIVE 0x8000 +#define CS_WAITING_FOR_TRIGGER 0x8000 + +/* + Misc. Enumerated Constants + + ABG_STATE7 - Passed to Start Exposure Command + MY_LOGICAL - General purpose type + DRIVER_REQUEST - Used with Get Driver Info command + CCD_REQUEST - Used with Imaging commands to specify CCD + CCD_INFO_REQUEST - Used with Get CCD Info Command + PORT - Used with Establish Link Command + CAMERA_TYPE - Returned by Establish Link and Get CCD Info commands + SHUTTER_COMMAND, SHUTTER_STATE7 - Used with Start Exposure + and Miscellaneous Control Commands + TEMPERATURE_REGULATION - Used with Enable Temperature Regulation + LED_STATE - Used with the Miscellaneous Control Command + FILTER_COMMAND, FILTER_STATE - Used with the Miscellaneous + Control Command + AD_SIZE, FILTER_TYPE - Used with the GetCCDInfo3 Command + AO_FOCUS_COMMAND - Used with the AO Set Focus Command + SBIG_DEVICE_TYPE - Used with Open Device Command + DRIVER_CONTROL_PARAMS - Used with Get/SetDriverControl Command + USB_AD_CONTROL_COMMAND - Used with UsbADControl Command + CFW_MODEL_SELECT, CFW_STATUS, CFW_ERROR - Used with CFW command + CFW_POSITION, CFW_GET_INFO_SELECT - Used with CFW Command + BIT_IO_OPERATION, BIT_IO_NMAE - Used with BitIO command + + +*/ +typedef enum { ABG_LOW7, ABG_CLK_LOW7, ABG_CLK_MED7, ABG_CLK_HI7 } ABG_STATE7; +typedef unsigned short MY_LOGICAL; +#define FALSE 0 +#define TRUE 1 +typedef enum { DRIVER_STD, DRIVER_EXTENDED, DRIVER_USB_LOADER } DRIVER_REQUEST; +typedef enum { CCD_IMAGING, CCD_TRACKING, CCD_EXT_TRACKING } CCD_REQUEST; +typedef enum { CCD_INFO_IMAGING, CCD_INFO_TRACKING, + CCD_INFO_EXTENDED, CCD_INFO_EXTENDED_5C, CCD_INFO_EXTENDED2_IMAGING, + CCD_INFO_EXTENDED2_TRACKING } CCD_INFO_REQUEST; +typedef enum { ABG_NOT_PRESENT, ABG_PRESENT } IMAGING_ABG; +typedef enum { BR_AUTO, BR_9600, BR_19K, BR_38K, BR_57K, BR_115K } PORT_RATE; +typedef enum { ST7_CAMERA=4, ST8_CAMERA, ST5C_CAMERA, TCE_CONTROLLER, + ST237_CAMERA, STK_CAMERA, ST9_CAMERA, STV_CAMERA, ST10_CAMERA, + ST1K_CAMERA, ST2K_CAMERA, STL_CAMERA, STF_CAMERA, NEXT_CAMERA, NO_CAMERA=0xFFFF } CAMERA_TYPE; +typedef enum { SC_LEAVE_SHUTTER, SC_OPEN_SHUTTER, SC_CLOSE_SHUTTER, + SC_INITIALIZE_SHUTTER, SC_OPEN_EXT_SHUTTER, SC_CLOSE_EXT_SHUTTER} SHUTTER_COMMAND; +typedef enum { SS_OPEN, SS_CLOSED, SS_OPENING, SS_CLOSING } SHUTTER_STATE7; +typedef enum { REGULATION_OFF, REGULATION_ON, + REGULATION_OVERRIDE, REGULATION_FREEZE, REGULATION_UNFREEZE, + REGULATION_ENABLE_AUTOFREEZE, REGULATION_DISABLE_AUTOFREEZE } TEMPERATURE_REGULATION; +#define REGULATION_FROZEN_MASK 0x8000 +typedef enum { LED_OFF, LED_ON, LED_BLINK_LOW, LED_BLINK_HIGH } LED_STATE; +typedef enum { FILTER_LEAVE, FILTER_SET_1, FILTER_SET_2, FILTER_SET_3, + FILTER_SET_4, FILTER_SET_5, FILTER_STOP, FILTER_INIT } FILTER_COMMAND; +typedef enum { FS_MOVING, FS_AT_1, FS_AT_2, FS_AT_3, + FS_AT_4, FS_AT_5, FS_UNKNOWN } FILTER_STATE; +typedef enum { AD_UNKNOWN, AD_12_BITS, AD_16_BITS } AD_SIZE; +typedef enum { FW_UNKNOWN, FW_EXTERNAL, FW_VANE, FW_FILTER_WHEEL } FILTER_TYPE; +typedef enum { AOF_HARD_CENTER, AOF_SOFT_CENTER, AOF_STEP_IN, + AOF_STEP_OUT } AO_FOCUS_COMMAND; +typedef enum { DEV_NONE, DEV_LPT1, DEV_LPT2, DEV_LPT3, + DEV_USB=0x7F00, DEV_ETH, DEV_USB1, DEV_USB2, DEV_USB3, DEV_USB4 } SBIG_DEVICE_TYPE; +typedef enum { DCP_USB_FIFO_ENABLE, DCP_CALL_JOURNAL_ENABLE, + DCP_IVTOH_RATIO, DCP_USB_FIFO_SIZE, DCP_USB_DRIVER, DCP_KAI_RELGAIN, + DCP_USB_PIXEL_DL_ENABLE, DCP_HIGH_THROUGHPUT, DCP_LAST } DRIVER_CONTROL_PARAM; +typedef enum { USB_AD_IMAGING_GAIN, USB_AD_IMAGING_OFFSET, USB_AD_TRACKING_GAIN, + USB_AD_TRACKING_OFFSET } USB_AD_CONTROL_COMMAND; +typedef enum { USBD_SBIGE, USBD_SBIGI, USBD_SBIGM, USBD_NEXT } ENUM_USB_DRIVER; +typedef enum { CFWSEL_UNKNOWN, CFWSEL_CFW2, CFWSEL_CFW5, CFWSEL_CFW8, CFWSEL_CFWL, + CFWSEL_CFWF, CFWSEL_AUTO } CFW_MODEL_SELECT; +typedef enum { CFWC_QUERY, CFWC_GOTO, CFWC_INIT, CFWC_GET_INFO } CFW_COMMAND; +typedef enum { CFWS_UNKNOWN, CFWS_IDLE, CFWS_BUSY } CFW_STATUS; +typedef enum { CFWE_NONE, CFWE_BUSY, CFWE_BAD_COMMAND, CFWE_CAL_ERROR, CFWE_MOTOR_TIMEOUT, + CFWE_BAD_MODEL} CFW_ERROR; +typedef enum { CFWP_UNKNOWN, CFWP_1, CFWP_2, CFWP_3, CFWP_4, CFWP_5, CFWP_6 } CFW_POSITION; +typedef enum { CFWG_FIRMWARE_VERSION, CFWG_CAL_DATA, CFWG_DATA_REGISTERS } CFW_GETINFO_SELECT; +typedef enum { BITIO_WRITE, BITIO_READ } BITIO_OPERATION; +typedef enum { BITI_PS_LOW, BITO_IO1, BITO_IO2, BITI_IO3, BITO_FPGA_WE } BITIO_NAME; + + +/* + + General Purpose Flags + +*/ +#define END_SKIP_DELAY 0x8000 /* set in ccd parameter of EndExposure + command to skip synchronization + delay - Use this to increase the + rep rate when taking darks to later + be subtracted from SC_LEAVE_SHUTTER + exposures such as when tracking and + imaging */ +#define START_SKIP_VDD 0x8000 /* set in ccd parameter of StartExposure + command to skip lowering Imaging CCD + Vdd during integration. - Use this to + increase the rep rate when you don't + care about glow in the upper-left + corner of the imaging CCD */ +#define START_MOTOR_ALWAYS_ON 0x4000 /* set in ccd parameter of StartExposure + and EndExposure commands to force shutter + motor to stay on all the time which reduces + delays in Start and End Exposure timing and + yields higher image throughput. Don't + do this too often or camera head will + heat up */ +#define EXP_WAIT_FOR_TRIGGER_IN 0x80000000 /* set in exposureTime to wait + for trigger in pulse */ +#define EXP_SEND_TRIGGER_OUT 0x40000000 /* set in exposureTime to send + trigger out Y- */ +#define EXP_LIGHT_CLEAR 0x20000000 /* set to do light clear of the + CCD */ +#define EXP_TIME_MASK 0x00FFFFFF /* mask with exposure time to + remove flags */ + +/* + + Capabilities Bits - Bit Field Definitions for the + capabilitiesBits in the GetCCDInfoResults4 struct. + +*/ +#define CB_CCD_TYPE_MASK 0x0001 /* mask for CCD type */ +#define CB_CCD_TYPE_FULL_FRAME 0x0000 /* b0=0 is full frame CCD */ +#define CB_CCD_TYPE_FRAME_TRANSFER 0x0001 /* b0=1 is frame transfer CCD */ +#define CB_CCD_ESHUTTER_MASK 0x0002 /* mask for electronic shutter type */ +#define CB_CCD_ESHUTTER_NO 0x0000 /* b1=0 indicates no electronic shutter */ +#define CB_CCD_ESHUTTER_YES 0x0002 /* b1=1 indicates electronic shutter */ + + +/* + + Defines + +*/ +#define MIN_ST7_EXPOSURE 12 /* Minimum exposure in 1/100ths second */ +#define MIN_STF_EXPOSURE 4 /* Minimum exposure in 1/100ths second */ + +/* + + Command Parameter and Results Structs + + Make sure you set your compiler for byte structure alignment + as that is how the driver was built. + +*/ +/* Force 8 Byte Struct Align */ +#if TARGET == ENV_MACOSX || TARGET == ENV_LINUX + #pragma pack(push,8) +#else + #pragma pack(push) + #pragma pack(8) +#endif + +typedef struct { + unsigned short /* CCD_REQUEST */ ccd; + unsigned long exposureTime; + unsigned short /* ABG_STATE7 */ abgState; + unsigned short /* SHUTTER_COMMAND */ openShutter; + } StartExposureParams; + +typedef struct { + unsigned short /* CCD_REQUEST */ ccd; + } EndExposureParams; + +typedef struct { + unsigned short /* CCD_REQUEST */ ccd; + unsigned short readoutMode; + unsigned short pixelStart; + unsigned short pixelLength; + } ReadoutLineParams; + +typedef struct { + unsigned short /* CCD_REQUEST */ ccd; + unsigned short readoutMode; + unsigned short lineLength; + } DumpLinesParams; + +typedef struct { + unsigned short /* CCD_REQUEST */ ccd; + } EndReadoutParams; + +typedef struct { + unsigned short /* CCD_REQUEST */ ccd; + unsigned short readoutMode; + unsigned short top; + unsigned short left; + unsigned short height; + unsigned short width; +} StartReadoutParams; + +typedef struct { + unsigned short /* TEMPERATURE_REGULATION */ regulation; + unsigned short ccdSetpoint; + } SetTemperatureRegulationParams; + +typedef struct { + MY_LOGICAL enabled; + unsigned short ccdSetpoint; + unsigned short power; + unsigned short ccdThermistor; + unsigned short ambientThermistor; + } QueryTemperatureStatusResults; + +typedef struct { + unsigned short tXPlus; + unsigned short tXMinus; + unsigned short tYPlus; + unsigned short tYMinus; + } ActivateRelayParams; + +typedef struct { + unsigned short numberPulses; + unsigned short pulseWidth; + unsigned short pulsePeriod; + } PulseOutParams; + +typedef struct { + unsigned short dataLength; + unsigned char data[256]; + } TXSerialBytesParams; + +typedef struct { + unsigned short bytesSent; + } TXSerialBytesResults; + +typedef struct { + MY_LOGICAL clearToCOM; + } GetSerialStatusResults; + +typedef struct { + unsigned short sbigUseOnly; + } EstablishLinkParams; + +typedef struct { + unsigned short /* CAMERA_TYPE */ cameraType; + } EstablishLinkResults; + +typedef struct { + unsigned short /* DRIVER_REQUEST */ request; + } GetDriverInfoParams; +typedef struct { + unsigned short version; + char name[64]; + unsigned short maxRequest; + } GetDriverInfoResults0; + +typedef struct { + unsigned short /* CCD_INFO_REQUEST */ request; + } GetCCDInfoParams; +typedef struct { + unsigned short mode; + unsigned short width; + unsigned short height; + unsigned short gain; + unsigned long pixel_width; + unsigned long pixel_height; + } READOUT_INFO; +typedef struct { + unsigned short firmwareVersion; + unsigned short /* CAMERA_TYPE */ cameraType; + char name[64]; + unsigned short readoutModes; + struct { + unsigned short mode; + unsigned short width; + unsigned short height; + unsigned short gain; + unsigned long pixelWidth; + unsigned long pixelHeight; + } readoutInfo[20]; + } GetCCDInfoResults0; +typedef struct { + unsigned short badColumns; + unsigned short columns[4]; + unsigned short /* IMAGING_ABG */ imagingABG; + char serialNumber[10]; + } GetCCDInfoResults2; +typedef struct { + unsigned short /* AD_SIZE */ adSize; + unsigned short /* FILTER_TYPE */ filterType; + } GetCCDInfoResults3; +typedef struct { + unsigned short capabilitiesBits; + unsigned short dumpExtra; +} GetCCDInfoResults4; +typedef struct { + unsigned short command; + } QueryCommandStatusParams; +typedef struct { + unsigned short status; + } QueryCommandStatusResults; + +typedef struct { + MY_LOGICAL fanEnable; + unsigned short /* SHUTTER_COMMAND */ shutterCommand; + unsigned short /* LED_STATE */ ledState; + } MiscellaneousControlParams; + +typedef struct { + unsigned short /* CCD_REQUEST */ ccd; + } ReadOffsetParams; + +typedef struct { + unsigned short offset; + } ReadOffsetResults; + +typedef struct { + unsigned short xDeflection; + unsigned short yDeflection; + } AOTipTiltParams; + +typedef struct { + unsigned short /* AO_FOCUS_COMMAND */ focusCommand; + } AOSetFocusParams; + +typedef struct { + unsigned long delay; + } AODelayParams; + +typedef struct { + MY_LOGICAL turboDetected; + } GetTurboStatusResults; + +typedef struct { + unsigned short deviceType; /* SBIG_DEVICE_TYPE, specifies LPT, Ethernet, etc */ + unsigned short lptBaseAddress; /* DEV_LPTN: Windows 9x Only, Win NT uses deviceSelect */ + unsigned long ipAddress; /* DEV_ETH: Ethernet address */ + } OpenDeviceParams; + +typedef struct { + unsigned short level; + } SetIRQLParams; + +typedef struct { + unsigned short level; + } GetIRQLResults; + +typedef struct { + MY_LOGICAL linkEstablished; + unsigned short baseAddress; + unsigned short /* CAMERA_TYPE */ cameraType; + unsigned long comTotal; + unsigned long comFailed; + } GetLinkStatusResults; + +typedef struct { + unsigned long count; + } GetUSTimerResults; + +typedef struct { + unsigned short port; + unsigned short length; + unsigned char *source; + } SendBlockParams; + +typedef struct { + unsigned short port; + unsigned short data; + } SendByteParams; + +typedef struct { + unsigned short /* CCD_REQUEST */ ccd; + unsigned short readoutMode; + unsigned short pixelStart; + unsigned short pixelLength; + } ClockADParams; + +typedef struct { + unsigned short testClocks; + unsigned short testMotor; + unsigned short test5800; + unsigned short stlAlign; + unsigned short motorAlwaysOn; + } SystemTestParams; + +typedef struct { + unsigned short outLength; + unsigned char *outPtr; + unsigned short inLength; + unsigned char *inPtr; + } SendSTVBlockParams; + +typedef struct { + unsigned short errorNo; + } GetErrorStringParams; + +typedef struct { + char errorString[64]; + } GetErrorStringResults; + +typedef struct { + short handle; + } SetDriverHandleParams; + +typedef struct { + short handle; + } GetDriverHandleResults; + +typedef struct { + unsigned short /* DRIVER_CONTROL_PARAM */ controlParameter; + unsigned long controlValue; +} SetDriverControlParams; + +typedef struct { + unsigned short /* DRIVER_CONTROL_PARAM */ controlParameter; +} GetDriverControlParams; + +typedef struct { + unsigned long controlValue; +} GetDriverControlResults; + +typedef struct { + unsigned short /* USB_AD_CONTROL_COMMAND */ command; + short data; +} USBADControlParams; + +typedef struct { + MY_LOGICAL cameraFound; + unsigned short /* CAMERA_TYPE */ cameraType; + char name[64]; + char serialNumber[10]; +} QUERY_USB_INFO; + +typedef struct { + unsigned short camerasFound; + QUERY_USB_INFO usbInfo[4]; +} QueryUSBResults; + +typedef struct { + unsigned short rightShift; +} GetPentiumCycleCountParams; + +typedef struct { + unsigned long countLow; + unsigned long countHigh; +} GetPentiumCycleCountResults; + +typedef struct { + unsigned char address; + unsigned char data; + MY_LOGICAL write; + unsigned char deviceAddress; + } RWUSBI2CParams; +typedef struct { + unsigned char data; + } RWUSBI2CResults; + +typedef struct { + unsigned short /* CFW_MODEL_SELECT */ cfwModel; + unsigned short /* CFW_COMMAND */ cfwCommand; + unsigned long cwfParam1; + unsigned long cfwParam2; + unsigned short outLength; + + unsigned char *outPtr; + unsigned short inLength; + unsigned char *inPtr; +} CFWParams; + +typedef struct { + unsigned short /* CFW_MODEL_SELECT */ cfwModel; + unsigned short /* CFW_POSITION */ cfwPosition; + unsigned short /* CFW_STATUS */ cfwStatus; + unsigned short /* CFW_ERROR */ cfwError; + unsigned long cfwResult1; + unsigned long cfwResult2; +} CFWResults; + +typedef struct { + unsigned short /* BITIO_OPERATION */ bitOperation; + unsigned short /* BITIO_NAME */ bitName; + MY_LOGICAL setBit; +} BitIOParams; + +typedef struct { + MY_LOGICAL bitIsSet; +} BitIOResults; + +#pragma pack(pop) /* Restore previous struct align */ + +/* + + Function Prototypes + + This are the driver interface functions. + + SBIGUnivDrvCommand() - Supports Parallel, USB and Ethernet + based cameras. + + Each function takes a command parameter and pointers + to parameters and results structs. + + The calling program needs to allocate the memory for + the parameters and results structs and these routines + read them and fill them in respectively. + +*/ +#if TARGET == ENV_WIN + #ifdef __cplusplus + extern "C" short __stdcall SBIGUnivDrvCommand(short command, void *Params, void *Results); + #else + extern short __stdcall SBIGUnivDrvCommand(short command, void *Params, void *Results); + #endif +#else + #ifdef __cplusplus + extern "C" short SBIGUnivDrvCommand(short command, void *Params, void *Results); + #else + extern short SBIGUnivDrvCommand(short command, void *Params, void *Results); + #endif +#endif + +#endif /* ifndef _PARDRV_ */ + diff --git a/contrib/include/setproto.h b/contrib/include/setproto.h new file mode 100644 index 00000000..47dfdcdc --- /dev/null +++ b/contrib/include/setproto.h @@ -0,0 +1,28 @@ +/* : determine if prototype using or not */ +#ifndef SETPROTO_H +# define SETPROTO_H + +/* --------------------------------------------------------------------- */ + +# ifndef __PROTOTYPE__ + +# ifdef vms +# ifndef oldvms +# define __PROTOTYPE__ +# endif + +# else + +# if defined(sgi) || defined(apollo) || defined(__STDC__) || \ + defined(MCH_AMIGA) || defined(_AIX) || defined(__MSDOS__) || \ + defined(ultrix) || defined(__DECC) || defined(__alpha) || \ + defined(__osf__) || defined(__WIN32__) || defined(__linux) || \ + defined(_MSC_VER) || defined(os2) || defined(AS400) +# define __PROTOTYPE__ +# endif +# endif +# endif + +/* --------------------------------------------------------------------- */ + +#endif /* SETPROTO_H */ diff --git a/contrib/include/sockets.h b/contrib/include/sockets.h new file mode 100644 index 00000000..3b408fc4 --- /dev/null +++ b/contrib/include/sockets.h @@ -0,0 +1,440 @@ +/* sockets.h: this file is the header file for the tlm socket function + * library + * + * Version: 2.10f + * Authors: Dr. Charles E. Campbell, GSFC/NASA + * Terry McRoberts, GSFC/NASA + */ +#ifndef SOCKETS_H +#define SOCKETS_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include "xtdio.h" + +/* -------------------------------------------------------------------------- + * Standard Include Section + */ +#ifdef vms +# define Sscanf Sktscanf +# define Sprintf Sktprintf +# include +# include +# include +# include +# include +# include +# include +# ifdef OldVMS +# include +# else +# include +# include +# endif +# ifdef SSLNEEDTIME +# include time +# endif +typedef int fd_set; /* at least on my current version of vms I need this, sheesh */ +#endif + +#ifdef apollo +# include +# include +# include +# include +# include +# include +# ifdef SSLNEEDTIME +# include +# endif +#endif + +/* for ibm's AS/400 */ +#ifdef AS400 +# define STRTSRVR_PGM "*libl/startau17" +/* # define SSLNOSETSOCKOPT */ +# include +# include +# include +# include +# include +# include +# ifdef SSLNEEDTIME +# include +# endif +#endif + +/* for Watcomm C++ on OS/2 */ +#ifdef os2 +# define STRTSRVR_PGM "startsrv.cmd" +# include +/* # include */ +# include +# include +# include +# include +# include +# include +/* order is important */ +# ifndef TCPV40HDRS +# include +# include +# define _lswap(x) ((x<<24)|(x>>24)|((x&0xff00)<<8)|((x&0xff0000)>>8)) +# define _bswap(x) (((unsigned)x<<8)|((unsigned)x>>8)) +# endif +# ifdef SSLNEEDTIME +# include +# endif +#endif + +#ifdef sgi +# include +# include +# include +# include +# include +# include +# include +# include +# include +# ifdef SSLNEEDTIME +# include + int select(int,fd_set*,fd_set*,fd_set*,struct timeval *); +# endif +#endif + +#ifdef sun +# include +# include +# include +# include +# include +# include +# ifdef SSLNEEDTIME +# include +# endif +#endif + +#ifdef ultrix +# include +# include +# include +# include +# include +# include +# include +# ifdef SSLNEEDTIME +# include +# endif +#endif + +#ifdef MSC +# define SSLNOPEEK +# define SSLNOSETSOCKOPT +# define SSLNEEDTOSHAREPM +# define SSLSKTZERO +# define Sscanf sktscanf +# define Sprintf sktprintf +# define Stimeoutwait sktwait +# define close(skt) sock_close(skt) +# include + typedef unsigned short u_short; +# ifdef IRL_PC +# include +# include +# include +# include +# include +# include +# include +# ifdef SSLNEEDTIME +# include "c:\c600\iptcp\include\sys\time.h" +# endif +# endif +#endif + +/* for Microsoft Developer Studio */ +#ifdef _MSC_VER +# define close(s) closesocket(s) +# include +# include /* corresponds to version 2.2.x of the WinSock API */ +# include + typedef unsigned short u_short; +# ifdef SSLNEEDTIME +# include +# endif +#endif + +/* for amigas */ +#ifdef MCH_AMIGA + typedef unsigned short u_short; + typedef unsigned long fd_set; +# ifdef SSLNEEDTIME +# include +# endif +#endif + +/* for borland c */ +#ifdef __WIN32__ +# define SSLSKTZERO +# define Sscanf sktscanf +# define Sprintf sktprintf +# define Stimeoutwait sktwait +# define close(s) closesocket(s) +# include +# include +# include + typedef unsigned short u_short; +# ifdef SSLNEEDTIME +# include +# endif +#endif + + /* for ibm's AIX o/s */ +#ifdef _AIX +# define SSLNOSETSOCKOPT +# define _NO_BITFIELDS +# include +# include +struct ip_firstfour { /* copied from , who knows why it isn't getting defined!!! */ + u_char ip_fvhl; + u_char ip_ftos; + u_short ip_flen; +# define ip_fwin ip_flen + }; +# include +# include +# include +# include +# include +# include +# include +# ifdef SSLNEEDTIME +# include +# endif +#endif + +#ifdef vms +# define TCP_READ( MSG_SOCK, LINE, NCHARS ) netread( MSG_SOCK, LINE, NCHARS ) +# define TCP_CLOSE( MSG_SOCK ) netclose( MSG_SOCK ) +# define TCP_WRITE( SOCK, LINE, NCHARS ) netwrite( SOCK, LINE, NCHARS ) +#else +# define TCP_READ( MSG_SOCK, LINE, NCHARS ) recv( MSG_SOCK, LINE, NCHARS,0 ) +# define TCP_CLOSE( MSG_SOCK ) close( MSG_SOCK ) +# define TCP_WRITE( SOCK, LINE, NCHARS ) send( SOCK, LINE, NCHARS,0 ) +#endif + +/* for SCO Unix's cc compiler */ +#if defined(M_I386) && defined(M_SYSV) +# include +# include +# include +# include +# include +# include +# include +# ifdef SSLNEEDTIME +# include +# endif +#endif + +/* for OSF */ +#ifdef __osf__ +# include +# include +# include +# include +# include +# ifdef SSLNEEDTIME +# include +# endif +#endif + +/* for Linux */ +#ifdef __linux +# include +# include +# include +# include +# include +# include +# include +# include +# include +# ifdef SSLNEEDTIME +# include +# endif +# ifndef __USE_BSD + typedef unsigned short u_short; +#endif +#endif + +/* -------------------------------------------------------------------------- + * Definitions Section + */ +#define PORTMASTER ((u_short) 1750) /* standard PortMaster port -- IANA registered */ + +#ifdef vms +/* typedef unsigned long fd_set; */ +# define FD_SET(n,p) (*p|= (1<fds_bits[0] |= (1<fds_bits[0] &= ~(1<fds_bits[0] & (1<fds_bits[0] = 0) +#endif + +/* end of types ============================================================ */ + +/* PortMaster messages */ +#define PM_SERVER ((u_short) 1) +#define PM_CLIENT ((u_short) 2) +#define PM_CLOSE ((u_short) 3) +#define PM_RESEND ((u_short) 4) +#define PM_QUIT ((u_short) 5) +#define PM_SORRY ((u_short) 6) +#define PM_OK ((u_short) 7) +#define PM_ACCEPT ((u_short) 8) +#define PM_TABLE ((u_short) 9) +#define PM_RMSERVER ((u_short) 10) +#define PM_FWINIT ((u_short) 11) +#define PM_SHARE ((u_short) 12) +#define PM_OKSHARE ((u_short) 13) + +#define PM_BIGBUF 1024 +#define PM_MAXTRY 20 /* max number of resends to PortMaster */ +#define PM_MAXREQUESTS 10 /* max pending connects */ + +/* -------------------------------------------------------------------------- + * Typedef Section + */ +typedef int PrtMstrEvent; +typedef struct Skt_str Socket; +typedef struct Smask_str Smask; +typedef u_short SKTEVENT; +#if !defined(MCH_AMIGA) && !defined(__WIN32__) + typedef struct sockaddr_in sinpt; +#endif + +/* -------------------------------------------------------------------------- + * Data Structures: + */ +struct Skt_str { + int skt; /* skt handle */ + SKTEVENT port; /* associated port */ + int type; /* PM_SERVER, PM_CLIENT, PM_ACCEPT */ + char *sktname; /* name of socket */ + char *hostname; /* name of host */ + }; + +struct Smask_str { + fd_set mask; + unsigned waitall; + }; + +/* -------------------------------------------------------------------------- + * Prototypes: + */ +#ifdef __PROTOTYPE__ + +Socket *Saccept(Socket *); /* Saccept.c */ +void Sclose(Socket *); /* Sclose.c */ +char *Sgets( char *, int, Socket *); /* Sgets.c */ +void Sinit(void); /* Sinit.c */ +Socket *makeSocket(char *,char *,int); /* Smkskt.c */ +void freeSocket(Socket *); /* Smkskt.c */ +int Smaskwait(void); /* Smaskwait.c */ +void Smaskset(Socket *); /* Smaskwait.c */ +void Smaskfdset(int); /* Smaskwait.c */ +void Smasktime(long,long); /* Smaskwait.c */ +int Smasktest(void); /* Smaskwait.c */ +void Smaskunset(Socket *); /* Smaskwait.c */ +void Smaskunfdset(int); /* Smaskwait.c */ +Smask Smaskget(void); /* Smaskwait.c */ +void Smaskuse(Smask); /* Smaskwait.c */ +void Smaskpush(void); /* Smaskwait.c */ +void Smaskpop(void); /* Smaskwait.c */ +int Smaskisset(Socket *); /* Smaskwait.c */ +int Smaskfdisset(int); /* Smaskwait.c */ +Socket *Sopen( char *, char *); /* Sopen.c */ +Socket *Sopen_clientport( char *, char *, /* Sopen.c */ + u_short); +Socket *Sopenv( char *, char *, char *); /* Sopenv.c */ +int Speek( Socket *, void *, int); /* Speek.c */ +unsigned long Speeraddr(Socket *); /* Speeraddr.c */ +char *Speername(Socket *); /* Speername.c */ +int Sprintf( Socket *, char *, ...); /* Sprintf.c */ +char *Sprtskt(Socket *); /* Sprtskt.c */ +void Sputs( char *, Socket *); /* Sputs.c */ +int Sread( Socket *, void *, int); /* Sread.c */ +int Sreadbytes( Socket *, void *, int); /* Sreadbytes.c */ +SKTEVENT Srmsrvr(char *); /* Srmsrvr.c */ +int Sscanf(Socket *,char *,...); /* Sscanf.c */ +int Stest(Socket *); /* Stest.c */ +int Stimeoutwait(Socket *,long,long); /* Stimeoutwait.c */ +int Stvwaitmany(long,Socket **,int); /* Stvwaitmany.c */ +int Stwaitmany(long,int,...); /* Stwaitmany.c */ +int Svprintf(Socket *, char *, void *); /* Svprintf.c */ +int Svwaitmany(Socket **,int); /* Svwaitmany.c */ +int Swait(Socket *); /* Swait.c */ +int Swaitmany(int,...); /* Swaitmany.c */ +int Swrite( Socket *, void *, int); /* Swrite.c */ + +#else + +extern Socket *Saccept(); /* Saccept.c */ +extern void Sclose(); /* Sclose.c */ +extern char *Sgets(); /* Sgets.c */ +extern void Sinit(); /* Sinit.c */ +extern Socket *makeSocket(); /* Smkskt.c */ +extern void freeSocket(); /* Smkskt.c */ +extern int Smaskwait(); /* Smaskwait.c */ +extern void Smaskset(); /* Smaskwait.c */ +extern void Smaskfdset(); /* Smaskwait.c */ +extern void Smasktime(); /* Smaskwait.c */ +extern int Smasktest(); /* Smaskwait.c */ +extern void Smaskunset(); /* Smaskwait.c */ +extern void Smaskunfdset(); /* Smaskwait.c */ +extern Smask Smaskget(); /* Smaskwait.c */ +extern void Smaskuse(); /* Smaskwait.c */ +extern void Smaskpush(); /* Smaskwait.c */ +extern void Smaskpop(); /* Smaskwait.c */ +extern int Smaskisset(); /* Smaskwait.c */ +extern int Smaskfdisset(); /* Smaskwait.c */ +extern Socket *Sopen(); /* Sopen.c */ +extern Socket *Sopen_clientport(); /* Sopen.c */ +extern Socket *Sopenv(); /* Sopenv.c */ +extern int Speek(); /* Speek.c */ +extern unsigned long Speeraddr(); /* Speeraddr.c */ +extern char *Speername(); /* Speername.c */ +extern int Sprintf(); /* Sprintf.c */ +extern char *Sprtskt(); /* Sprtskt.c */ +extern void Sputs(); /* Sputs.c */ +extern int Sread(); /* Sread.c */ +extern int Sreadbytes(); /* Sreadbytes.c */ +extern SKTEVENT Srmsrvr(); /* Srmsrvr.c */ +extern int Sscanf(); /* Sscanf.c */ +extern int Stest(); /* Stest.c */ +extern int Stimeoutwait(); /* Stimeoutwait.c */ +extern int Stvwaitmany(); /* Stvwaitmany.c */ +extern int Stwaitmany(); /* Stwaitmany.c */ +extern int Stwaitmany(); /* Stwaitmany.c */ +extern int Svprintf(); /* Svprintf.c */ +extern int Svwaitmany(); /* Svwaitmany.c */ +extern int Swait(); /* Swait.c */ +extern int Swaitmany(); /* Swaitmany.c */ +extern int Swrite(); /* Swrite.c */ +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* #ifndef SOCKETS_H */ diff --git a/contrib/include/xstdlib.h b/contrib/include/xstdlib.h new file mode 100644 index 00000000..f128ca01 --- /dev/null +++ b/contrib/include/xstdlib.h @@ -0,0 +1,18 @@ +/* xstdlib.h: has standard library function prototypes */ +#ifndef XSTDLIB_H +#define XSTDLIB_H + +#include + +/* Prototypes for standard library functions */ +extern char *calloc(); +extern void exit(); +extern char *getenv(); +extern char *index(); +extern char *malloc(); +extern char *strchr(); +extern char *strrchr(); +#define remove unlink + + +#endif diff --git a/contrib/include/xtdio.h b/contrib/include/xtdio.h new file mode 100644 index 00000000..ad5f7ef7 --- /dev/null +++ b/contrib/include/xtdio.h @@ -0,0 +1,157 @@ +/* xtdio.h: */ +#ifndef XTDIO_H +#define XTDIO_H + +/* --------------------------------------------------------------------- */ + +#ifdef AS400 +# include +# include +# include +#endif + +#ifdef _AIX +# include +# include +# include +#endif + +#ifdef apollo +# include +# include +# include +#endif + +#ifdef __linux__ +# include +# include +# include +# include +#endif + +#ifdef _MSC_VER +# include +# include +# include +# include +#endif + +#ifdef os2 +# include +# include +# include +# include +#endif + +#ifdef MSDOS +# include +# include +# include +#endif + +/* SCO Unix */ +#if defined(M_I386) && defined(M_SYSV) +# include +# include +#endif + +#ifdef __osf__ +# include +# include +# include +#endif + +#ifdef sgi +# include +# include +# include +#endif + +#ifdef sun +# include +# ifdef __STDC__ +# include +# include +# else +# include "xstdlib.h" +# include "varargs.h" +# endif +#endif + +#ifdef ultrix +# include +# include "xstdlib.h" +# include +#endif + +#ifdef vms +# include +# include +# include +#endif + +#ifdef __WIN32__ +# include +# include +# include +#endif + +#include "rdcolor.h" +#include "setproto.h" + +/* --------------------------------------------------------------------- + * Definitions: + */ + +#define XTDIO_SEVERE 0 +#define XTDIO_ERROR 1 +#define XTDIO_WARNING 2 +#define XTDIO_NOTE 3 + +/* if your compiler supports prototyping, but does *not* support the keyword + * "const", then move the following line out of this comment +#define const + */ + +/* --------------------------------------------------------------------- + * Typedefs: + */ + +/* --------------------------------------------------------------------- + * Global Variables: + */ +#ifdef __PROTOTYPE__ +extern void (*error_exit)(int); +#else +extern void (*error_exit)(); +#endif + +/* --------------------------------------------------------------------- + * Prototypes: + */ +# ifdef __PROTOTYPE__ + +char *cprt(const char); /* cprt.c */ +void error(int, char *, ...); /* error.c */ +FILE *fopenv(char *, char *, char *); /* fopenv.c */ +void outofmem(void *, char *, ...); /* outofmem.c */ +void rdcolor(void); /* rdcolor.c */ +void rdcputs(char *, FILE *); /* rdcolor.c */ +char *sprt(const char *); /* sprt.c */ +void srmtrblk(char *); /* srmtrblk.c */ +char *stpblk(char *); /* stpblk.c */ +char *stpnxt(char *, char *); /* stpnxt.c */ +char *strnxtfmt(char *); /* strnxtfmt.c */ + +# else /* __PROTOTYPE__ */ + +extern char *cprt(); /* cprt.c */ +extern char *stpnxt(); /* stpnxt.c */ +extern char *stpblk(); /* stpblk.c */ +extern FILE *fopenv(); /* fopenv.c */ +extern char *sprt(); /* sprt.c */ +extern char *strnxtfmt(); /* strnxtfmt.c */ + +# endif /* __PROTOTYPE__ */ + +#endif diff --git a/contrib/lib/Makefile.am b/contrib/lib/Makefile.am new file mode 100644 index 00000000..27c4cf42 --- /dev/null +++ b/contrib/lib/Makefile.am @@ -0,0 +1,6 @@ +include $(top_srcdir)/rules.make + +dist_uts_lib_DATA = libcfitsio.a libsbigudrv.a libsimpleskts.a libcfitsio.so libsbigudrv.so \ + libsimpleskts.so libsimpleskts.so.2 sbiglptmod.o sbigusbmod.o \ + sbigusbmod_2_6.ko + diff --git a/contrib/lib/libcfitsio.a b/contrib/lib/libcfitsio.a new file mode 100644 index 0000000000000000000000000000000000000000..8b9c3dd0181d8ebb5513506b37f876330d80859f GIT binary patch literal 1221884 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 i%Ja>37X{!G_-80qm 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̕Y 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®RaVj`#7FuBCnmlbX5#G|8K*f|MdITe(!7cJU8Oc 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%0A(vja^z&n|Nx6hbWkN1m24 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%0A5Y?ye|0SgsVneB>j@SFG0U9!^7dfoVzNW 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

_ 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 Xt1xf$h4Bc|$yA)`0>GKGaOVF9Rnlkm literal 0 HcmV?d00001 diff --git a/contrib/lib/libsbigudrv.a b/contrib/lib/libsbigudrv.a new file mode 100644 index 0000000000000000000000000000000000000000..40d1a9f1618b7761d683f4be355b2316b6324cb6 GIT binary patch literal 90056 zcmeFa3w%`NnLmDJG6?}A6ESL3szE_PQKO>bJzS1LE1E^3wn{Z784?Va4KrM<(a1n$ z7_rnvOS`rUm9}ncTiRv)QMEN#O3-C3wUnYoH%qC_IH{#H#gt9S@B4k;=RI>~k^sHz z=fC^;|IR0q?|I(K^FHtM-mhoGb=6HZ^*0Q;q%8ZnwEU_oF1!5daaUeiR#q-n{{LlV zmtJ+*WyZ&SLmbEXq~jDEIsV_sCmiRh=>z{hdWYlut$uf3<2c9qtuJ+)4eO5Y_pJkt zllyyNyW{*Vem`97IDg)6(xl0g8)h{`8y>8?rD1NJQ$H_yZ_T{e+^7?+n=}93g$=cJ z^L($DIrI>lGoOwZIFlOds+*?Hn9$Tzy~MdK5?N3ewbbo((K~8u)$sNiS4=XF;C)(M z^@3Pa-Nc4!GMHEMz=X-pq?_)XG;hwF>bbR!J7dc2b#rT{HPkfCn@DS#+*}vUHh}AF z1Gt#n)UdFwY3kg_JbIa2x3Hn6Zt885=FN>Z&1tjnnI@Yv;wH%2|vS zIN)$+!`!L2Id@LIHP`bkl{3Wiyr!sw_es@r>YA!=0|yW}(!wNLT4SJrfS~%O&Z(Z& zFn5;GqMFTUs;+q;*N~d?T&Ygs-s(+WnFfZR(XlnO3_Vl$pE~{TZlTZ+(f3+)`-}#} z$hmEP-CVFYz+S3Gs{Q`;H@a#D3;O{}pxTIafz({GzL zslKk}fd#QSWNA#C%9woqB5LS}K@it+y&1KW?1UdDxNlt9R{ZntA z;DEZNA1Q7@puX%%SCgkty~CY;FO`dURW{786VE0D~#>njXv;+#b6>ifn9C$y5pIc*;$U4fE~$yl7>0w5DFk z+Q!D48|U3$-3YnwZU*R(`E-yW@w#uhlWl9@HlDyy5S=OEzvSTtJKIEoa~JcK}Mn-<=?VE!D(!Fx?j?ZxwK>t|cNE3PUlyP}*e-lg{cmE*>h zm0fj3`FPvvJ>uSSQy>sP<7b|Lljme8$GNZAn2!?}?VR8gI#r2Z#XAd=ZgC{x9*MN& z-;cJl^blTZ@zUFimwI_gx`he1JKo$~5G}<2!ss38y7lA}Nmg7Lc{Tz^;aA)v@$Nu0 znEr?|>VCvB;tLs7oSmMbmVHBk;a_1el z$2)I9ToUfaWK~IWT4{3T@MNqk**r4oj!w8clJ3}qyFKZSOSoH;?)Ze;nshfJ;s+y% zssoY4v=1VQnVpeD?EOfhxg%M%1IF(q-R*F(KUuXE)V`$K3aY)$-OIQqUHEc$MILQF z;1oYx5NUIPpmu#WC=@W>8A`hQ!{2s0)>ih$ih5&(9f>2oz45KZ60s8{9`V*t!rhZT zL5TtoNq0{qKHdp0{}AD{}Bo@7T)ui=$3u7F!>oIHh&5>+oJ-IwEw zi$dYY7(x>C#G);!Nh*lDyzot+`h z`iW_-!Zb`tZxgH+)6Ft2V_SWIkBVs8?;W`Je=3Z~IXc zaF+^wIt1;3E@PMpWSP&bI@($Nt0j>8JM})S|iJgwn{7!7POrK%=9zfyljg|XD zDwc>cER*s^9 zNq6U}x~{uFdGAct#_`TlRK=zqMlE>+)n^at*u!mZ7qsOo%boG&PASWsQkFXd(c086 zM4_E|(T~HDW%+d)bQV2&eIZ_0EEb&{Sv@jT$^X2o*5jdt(O>g~Lp@9{xMR z3e3hM;dZ6s!oE=2vcf{NSZDWf@2f7l(sxK@eE|drzmm^T$u*n5b&uMyD}CO8)@Yl$ zC}$VTy{`#~v{j%M)g}N$0js7iY1YuFm3W#&iw0#LhBCXM%tL6fy4dRA$yAOV$kn3I z4pAs__=MCN39dsb#||o#Du9%S7?W-jos5zo<$*XFwdgFAqlDX;t`kwmm>ep84nwCl z&Ytt}>io)MISi}A6Cpe`VaTN0nVi`pk&btcwsB^Jb90Z$*oP};2OxeXjWZ%K8RE@d zP897&SHkT`b&CSfmpBorI6GhqLq==+ z)Y*aAPJN5fU5}{~GHR2wzg!bH&FnJUfroy*%nqpt<&YGW_6KYYBq32gKX~NfV0r|i z_CX}M41r~``6~B7pm-I`d!6*|q+6P-IuL)gV$1u3A_?tLNT@b?Ch|$QLzOxu=@y|# zLZiq!ReiJ0)_C()QNmVH!qz}^O!`ci*J&B*X8Rc}DKk#m;~qq(hxR=GQwJZ~_0ekvJwr^4n z_IPub%HC#3q!z>(Ao|{WR1){}jM=FREKbi4liYMJ}mZToMiWBLHu`4J!X0PO! zy@BX8>HA@p$B_0i$IS2-o21(l$#ajW=Cg`UcluIOthGE#BO_%E&X__g5O91e3RyR6 z&~8(Mb~~~9zqI&NbszZ|9VmezSUKzgeBFrW3s^eUFqg)HPX!uWzw4 z?t|&EC=sYwx=?kpV#2Fb=j`K`sGhwJYbPpX&8%q43z<_jmtv4}M29@UOm1MEE4iUd zazj@D61p(Ui(Y{L%nj^pX~0J+QI}FxUcr{MMIVY19;vjIm_Nah1S$~$P1)$1Z><_rcKOAZUwmoV=-Xqr zyJeW^i!C1GI2SKiGACMnKQP*4u-?8k)iqum#e{@&aV_WDF1~-k0_S2HHqA2zHJB8u z!~E^>K4-%r^zEPzBhNVeUjbR|gJ{TeCjJlBcfz<*$9WExG^2mIE5vV<(s@8WFM}7< z<2{z&g)pIe>#h~{&5$_-g4fpgDN4^i`lajJ5O) zp!u8wrPBXYymN44X|dFQc}~7F1MlNcL~j|$@N3o1@XM8Vkb`M8@1fq7;78e?Kp4Z| zKY@3qC4VP52YdekXdk-&yd{97znw$>5Of*M@Q?n{UGN^df5)SY|MzofPVv}>?n6kR zG8*6?`LU6<5A8?f(3j=VSLV=e4*hpI^nIZ3L}8r@q(0{4*gu*>KaoTKQx5$vIrMhW z>jpW_A0RvV|18J;mpOEI4m}hJ`xFXx70l^>IOu;J>Nr1De&;gb^`U;sLBBuPaUNIq z6*>0Pa_CQhUU-7zysGXSK%Wc#Pl6Z2`}-XC59QFG&Y@T5&~{R{#=&yQ?bXp4^-WH+ zzNv0O{k+CnX94Dqo2sJ?^XAG@S;k_XtbRF-)eEBcHezw?(#)$Yn%&}G_F7n53+5M> zJN~)sftTXU?0f$!B>gRG`Ihc-mQBo3n$EhLWgsQ8rs^{cE<27%f36Zt$)3>n&-Lf5 z31lrG_BRpFsdjIL5i*jVT=SJ@%@ZQA^IECb(f)AWLzX6;3ku zmIp=scHv@xwNH`8EQ5k^W{pqZd`mV`p9N6yosPUtJV)_*A_~*ZN`GAOe#HkA7b-rY zxI&R*FZ%ySrJqy!yGnmgY1W+#mvs!?vu+`Njfi&dMI!890R~TYoS!I-IZmPfgEZV9 zAr?8#MJRhgG)PN`Xb-;yr2T&4sgBc0^V2ZsK{G-6G++?@j=PkuAq{1$B8_lAulN-7GgJ(F-uSp}E0{EdE#YA*)N`VaTD$;O&BWZ*;SLr3B5#Dmr_7ZPD#u6PA7_z}pX_IE4Zr&vdOG~^E} z9VZR_e?jSgQu}Wx{gPs<;*W^b4={+%&@R$Q&tI!~C(X;yxh4%cPeA)aJd+52=K_Nx zpf{y&Bn^2UBn>@(hj^~zd|xq5`;qXEb|yFqePtl+KSugI^aF|KJI*qi!~bU$|4p$N z?MrYB^g+A;{S_iQly4G8JI)`87r`&u75cxC2sv*C1}}D;CZ!)Djr820^d8diSAhNw z{oT%fPr2hX5HCevLh(n6Sd?()}rl;}sty zLhhwN%KePezf^n-{VK}$1tR3zsJK(j+m$Xs`$+k20y3OOiQv0}<`+B87m4U-@1!~G z+ZF#vL|F-;k4*Eki3t00VDJj)hj^vq#E9r9E~P!(^9i8rB8vzjIRxQV5Ud2*7o|HC zF+mgs!7@c;5uuT!f@2lS6(O>ik5}Y+Y-G{#ug8wNimS4G?i+Hf_1O6o9uLEG9B)1L z(asehb($RWb(nkMT3BKJqM;b_`qu5>1i9Wxy@0w8qu^bIoPt~BzFE1--Q^oR%RFd! z`D(ZVw}iU}!o*LBdlB?4|oO6_ta-M6V=+eEN!$ttXOPsE}% zRKkukU*d~Hf$(z9_lmdh6C?CBKm3_f$-k;7iDmG_#9(R=ND=1^EFm*0I%zWUPqI+0 z`6aykKM)nNPFB4UNmOQnOKpS2@oGD~oQW%0ueGnnf1nZj0V7uR16jJ4kY0o*XJysT zN5Hung3C{G7PV zUz$O)VpL+g>ZK!rUxCMrFeuKLmqV@D zZ!DwWkJY0c=>=ewtSpsK;N>L50mAl)Lohx0IGCN+zuezdb)^eTphsXuT5{iNKV1QtI;>npMRf$dpbUK!lJ1(^)*Ka7kXz0sqSoJ$oQS2pomg66D?AY)>`6{6!EU7F#8NEznR020tdtpK zM65}%84fWo@ddRhREqbu3h6txO_XXIiM+iacH4T!B-z56Ki+`=TjR~GMT^EJt6HJP z#jxvbM})ScZ5v5M6E>Wf@1#oSYHW~@Z161P`&jP~+G`WBz@#sB5$_6ncz=e^EB`YfMuYq#f`J0>WYD0*ws z-TK-O9fy7Q@%mc>{2U@qn)88|=x-}9KhMykp~rFhivnBp{R`myRs0yjM2rV_;K%n; zjNL}+@1wvc^nDS=YW&@c-y=Sx8EI?J_gVPO!|#9k`pg9wH`)hkkq6TbS?eDi7>iAT zj-dvONX%G_`47Wwp`m7Z?(^}Yf#r80-s%2W?qm!GcSt&U$PXpfdn`ZpgeZ%B%1^>P zj>?B=1qqko<*v^Zq3mEv!h0AOKJf0zM*2dybNQl=bw{+a(P}itwB;~lGQ*36TMVRZ zHm-Pe?t=bS@kJlY&(aqC=6}ul%m&Y$>n9~Jpxrxa&z5Z`1g$`%=zcWlV&s1e zS%jvZpM)YW>6hOiDRXcl599qYeJ7Bguh9OcPM@VNO!|fNQfL_ytiYIG0BJjN(56 z$(L(7l=B-(e@}5N@(TGrs5lI1M}9W|$>(_@?5{_GA)k*EVbACPi#Z;tAE3hu4S%xY z6vbNiuH<(it`ntifob@--i@i6qhNkP<&i*wc;AZwTjOuu2bBg z_?#l!C;vyI4jiOs3f9vkzL|@4y|KaAH!=cr zI;Imzc4h$0y&Vq}nW=*MGPo1Zy&LH{Gu70CZ4^FM7l@+_qaw?wSQtGU0v~TvmEAQ` zik?mnX0V*u=v(!O!+Zu<*-wZV2yh1nwr&h2^kOuP4pLVkoTt8we*=aOk4MmZ!Zs7P z2g8rL*|O=x_N8pNV_r?{#5nHQLL5fuhm8=2DFl=$LZr(uuW@)4gbWm?KVvyP3&{}g zE&{qsxw~cTJ{EMDJ$>nv8)oRtfD^knlOLh_;3%^-t~imG84!58ZwBCp>InT;!w+dJ zTXhFaDwVNRl137(y{zIH3B0@G0ei%O{a&z$w+=BGrQ=-BW_GY6VhPSA^_D~I-YZbY zc-f~Tf=(t9RMHdD_ppQi@%TlI=8%r|j3qXjO#v@;8XJWU_j)?IHW-ECdr-$Y>Ebxo z$DAQi^hzbpH0i(7z6SWsj zZyw2)n_x{on-ZHFDm%VmjR#7)Z(!J96@bm16e9H)gtj_3C!-*w-b&SoPY09wOGK!l zWB@gkAOooyif#R4SwIcy@2CnX8{y^Pe>NjZC9pAA;Xm+q~j@@)P6K$Jg zXWKY)C}s0#VaL;|0biW2>67lGO|G3R`B)PDR3!TB$u_FqCf(e0F4ThgXmhdCg7M7> zk2aUei&Ti+XCNt3oY2+k;(KP4CmEnPQ(U7y<5JDy8`TVqqo+Zb-dHg=A!e7{=P?gr zli%o}JH5c_;k=BJkoHfgR(yJxWJ<4FoGHHLCfVK6j3z!3N0d~AQmc>>FYTGU?+*wttd~2}_INtz_VlV0{`zvo*gGf9g%0RG)4y_&t7caK8QSf8Qu)^n|);Qn6FvH(|)t<4NT~g0@ z)Q?1g*4Sa2YESY+2ai#Wm02XrTS}&p#G?eU3BR)1(^~iy(b^cZ$OVe=L}<|SVdQ^x z-@+t?07%utfmU?O(Lq7Eu*x;z^{!wDD65f0Oo zx7ZKL^>X{LdE%2h->?{Ci_sX!A5b^s zLBE#YB|bB}I<4?@Lkr)`Q>Hi$NIC37cgHi2@E>p|{RQ=Cc@U9;-t#=>IVxd5nXz?6 zypGU~|_IXlIJWI@az6`%1Eu{11op#p#LcCMA zmd0|vHy=X(H-Kh*to==R=kq!IIPWk6nBkZ2%v8H>wSNG}$N0~8qImxdB6%^8{@Eq? zK4_K$(j13#e~`8RCg>BPUzQQtw*pzTnRgf;|C5K^%RllNQ0|}P&_BBb?-V%B z_tgDNpnVvhSvfTKeLjr@WIXIV3kQxh&YXMaSK=xO+}lt$cUH6>^!>32P3PJMLV+y(MqF7wE_$bp*>4Of{N@@@`+4A}+< z*LU**yyV{X0i&bbLt*w}s{Fj2VgdBeVMYLzKp7Efekm|`D)t?bMw(A1!u~dzpN6?H z(s1`6X}Duqpu3HVFA(8=3++qMUn33ohe^Zz6-ax!mpLf7y9qSiJ)-7cCyo6+n@Gdo zHpQRN92LW1wddId+LtJf0+N41=_g1JcbspMMtE;3{z|c1aTa*cewpHD6~Cg`O2ltF ze&qL4HJ=PWG@lBj`2(c!TcP+l#pe~sUCKUfs z@w-I0e-TLclTiLhPXUsiMH>E>DXvz0R`G{Kxc@gG-QR(Xz;uf!zNxrRv72~~qu2g!a(z6sB)qIK44=cSvakHAgqWC%ya{WZ< zUjTz;*#Acw;r&MOpkfgkIhu3Lg?OIgb&7vS1iw!Jsh9iJ9NAamizo|%ogn8Qs0Y?_ zM9hIT&X4{Xb6}TUdEEQBHbE9X>>sd);mv`C%w*Gg8sq+jyr^FHfU6iVjl+(3PkQ4a=xSh8fVG+i!(D9+l5|K5$=D44e2h+Qh2!YG3{ zJVDn(xupMSZUGTz5jet0^ddPyhIz93T^QNh5YnZ7o06A2)@4>|boW=dy(`_q6>iT; zw|9m6Cyy)7bqQDO)470Qdq%RB-G*gJYh6 zFM)&~cOAw&fG?Zzn!5pK^Fe-9vO^@}SrS-G&K-IJ1qNwOrC{`XYYcW24hnUT*}pOGbBCq~Da`Owjx zYzW5_?6`!2y4$rNvGC={)N^#1&g>(T=Hu~ytw0cK_rMthSp`bnjmYjBNSjX9fVq!0 zhYutaFZujJ8xx7!N?`vZz*OC`boM=I+JeW9e!vFRCFg-Z?UL%tv&EpAPa zlAcw~YGArh;DzI*f(fO_7YdJ~dbNqM{)jIi-$apIV!$kmfF)1p3IF~xY2UC5Bv%$^ zg?%6swy{1nD{Rz^2Q&w0mi(*2E;)MGrwtUgooDgll&SpDG}V`8vGvuyef*j9h99YH zhiCgsOIB%f!v0oF>u)b);ed%n!y%NzVM2C}31mg6CpoPUhkZCIzl34y8Cgvka0WQZkS+syqfQ!E#z2on^?6W|1lJ#T=~PJX0<#$=T`sAuTX+d? zVM7nh8MRnfpCNni*!02G8A@N|}nzW4rUY*Iv(GOTApnb2j_8!0apFcNcwP@IJPmBNI+>I_v z%ibT(W#H(N_fT%?<~sbE@T2`@K>N_%wFHp#$ME|Wev|Q|`#XUL@%x0*wZK~tDHdzm zF9dR|Z0W~9b2Xb=p=rMc^sP|b7uEf9z%=O3D*b)nLx|>!O1}ZT649KaG`oY(g1%Sj zUjl>3#1ARm4Sd)~4@J0#K{KB){L?{ShJ>7`G^?&C=uat4Ie!b9;nF?l1MEY6+?qq* zokrMPsQ$e-{aldR&pb-Qyqahu?;8F8>&>YP<~KFWjYgb# zxK$N5XyRH`suK#i#Ww-vuu%y>_oa%H6mL~z9txs>#Fc)6G|KnC5|QRVCZb@c6i-0% z(foWO%qJ<Z4(2={XoId{YO%GpQQ7ouFy{(QwsBFyJ1y;5ZyM>3ppH^H zuJ{Da&vKmqRJxNi(hqYSb9l$Go#pWE4jteBJ|EneU2Y~hj(t|`<%^IR@Yp#H@9xmj zZSIHI!+r=Ip96G&ArF!j1IyxY3^(bNu5?zoAEt+b9vVZJLg+(SJB@eb;W8oIR8|yz z>@H-KWK{=tDU(BQJNn0MQm3a}+K?j8)K!)OI;k=EA8#&^dy-0c)P=|6)a8lRC9;l_ z>cu~?6Mw%G3R|vwTADvBjQM67&c91}F1$*N9XsuO-}0#l@_6T9E#1S9hiclGxmA3G_wKIrlG#)kLCN~Py6JMeo_ zta|#jVB70$aw?0DcVfIYQ@0PqFvrt{GZfvG9e2szk}@Q=Z2U31M-HUE2k*&gWyz{C z^nZ;S%CNPlJ%G-240k%g7<5Gf!@I6tBLODc>`YrR$LXi{#>!+CE=T4xE$YjVVf#c! z_Tya3aKRmyPdQ_DCIaaTK*h&bFr|Aila8%812RDNlF2WdWOwOcXPf(Dl)*Pl+M(2y zFztTKQdcOYPUts~v8X6BgQ%B1@op&`7QIw9qEJm~sHhaG=)k#!T@*OHoCR$?1Vegm z#z91ro?MLLsYFfBDX`9@=ZBz}k9b!wZX1(rB0Z?-oYXt`hwTyZRI2KaWF+A!Rsip-HHv_{#u=L#2~p%9@&|CsgCjCk{&;G&T&*E61{a0u3eR>DbN zXG}_CxH7PZ9oF;(#^fupGuZEQR-Q5fK2XfJw1@KCU01(~BRrh9WnbJR%@imVQy1qL zXHus=A8&phb?U{iNmf0NI`tm7W}S+-V$Q02C5`~X1k(atC?UOY1k*04N{*WhU@bKm zq~ICP0eScw6&#Nv=xl)Ccw8;jhzd-k?#Q^?($1+N72NGMS}H~%#TH174P1AP>J2l7 z5Vb3^r2{k4A4;`;4*rK04aWs;#$(lokz|RqIXnlN`Y$vVu#3pK*VJ!>g3_S6e#C5F z>2|FMrrNBlz5Tj^DJCF;sdd)b{{Ec#`5)A1z#*c6l!~|KC4#893!=E_xTp|UcaCS# zbN9=+Abn%AV9dDifi&IEDNjk%iKxzm5^g*OjFg@@%x}1*BWO%99i4Hyr6bRnp2~1z zxN6dG7czFG>A{?SZmU3N)4ZSxGcV}Lm={#>c-dA_M!8IqnC28Gk`ohohhYKFjUqy$ zk&m4;DlonymhfklP=PALZ;It+#{*0GpfMZsqp@RG;8jA2KzW?qKV=$XzV^j zEsv=zZeg(B=^M}_uq8kL_`(UR9PlNJX3|8t zi2Ai|S7ry~xIXREIktUbizV$-u`wx-ea~!#`s(b_=E9}02tT$0QJ1C*_snmAiCm+| z?%n4xXQ|!0TDXyJTWLq!Gc_$6H?K%8>4u;)KXn>> zNG~GA-oIg+wM_cjA-hbPN{j3zIH?|VqSytCo?~f;^H`w>X;js*de>NPn1;Smr zIQ)9Tq8r$)3q8ep$cyhko9)f)X)Nl8=j9&HG`n-s*S`%h_c-%D+Chu1V}=Vo)$Ze* z6}iqo*vO?%()g5n0k4O!T!H45;5^U3gZ-FLFSng8n~dh_5%la2@v@8GBWJFoPaK~$ z&_jUZZO&J)hRePK7J1n_SrPQoy>MtI-8nTVhfWWL$P39r-wB-7Kjfe!lY@3kI+?`G zj9b~}v6q7vT6dc9Q!fgjnRLdLAv_4>L?^=%2{N-W~*mvTChUTCdXkX zMtp^K0GOy+6K`Gyzz8>)O4i6p!}O&zmYocJk`}$Op2QXm?nRA}_%%-Wsn!)3 z1xsc~?L#F(&pe_%VD>0sntZE%s_Dpm?8T0W;0DHbUd-6(8XM{X919u zC`7IV$*RfFp`4TfxsBKINcr(zQ%|9erZV%7rcCrm_eLHa-|H+weTuimLA;il*AQND zX$DOT@ET0tnyeD{NeA6%bpCPl!O#^!V}Q|sH3_K+dQeG9L200x5>VXTf%rBno=+>`K{tU~zHgYowVb39|%hyo=} z$Edg9)fO>Cq$_gm#NYzn4^*R{XDoxo+rS7eL~%i^QY zJ9glyUtmVmR85PEaw==GM)e@`O^1TEWM(xVyD&P}QEj~a5mQg^G8$GHT2Q;#F@nFE zu_cuZDgxfPSR|J5o#cazWX1NCUr;y?LTlXM8%>}%PK9AinR&sAKddZ~cgUb;FzYMn zQOgJm-S!=(7TD${i`g0^(L+z7E>C?IsSY>c7(U1VH;aRLybNr|$_fnJ^57s1&5L;` zRsg1D(v(!t%uYWJ*mf)vpQi0tR=l!m*@~*)r_npiFh4FfEj~6aXo6a#36i$aR->sd zRMJ#UZ~)anaxSN{5wU(i+Nv(~ugCB%eTkjaDKEUrRHxtxGE<$RWRS-FHD2{fy1>$? zen}Tv8nyF)GL3*tS`inUvVRE4&b+3c4tf0q9EHN9S2qUc*b9Puu@C>duK*Tc9m_ zY4ZDXSfCtJr>Nis{R+NW8EwuLd^4(^Y@s$IEHLVv#h(BaUAZn^ibJ2%vwHMsT_J~otK6bT%VZApdQ30k zQc&#j#e7ljN{n69c??c4;}CiFKID0ffjQ}d2`D3#9ArQJcXp60b08nnDHl4bX8>6v z>aoAda?E{X?IoZU$CkMYpwn<#t%|?J!db(ml9F=Yb}c1 zjCK+m`sD{?-rkIi&>Oi>zdRv2!ZuhWC^HaK@J%wtby5fL{7i1p6J9UWch6N#bV3giOm-BA3 zy$#1lcSXa-45DG?9hWDX_~ zv0VM?!zZt&9rlis#?4~zvLA7I&soBp@E%feC`NMc>0xCcEcY49eQLQ+ErIaz1?kLU zWpN$&$_n9G5`?+KG8~(r6xSFzUCj9f-sOh#%YoRi%@I5=Mj}Hh5~w|@Hl$}-lS4PE z$;YzTUGHNTh+UG+>~cIWUVeF=WmdddO@6P;5b76@6Tbmb*Rz02ah+cIA7e+ZsvTMQ zPX2tHK-Tg|Mj zH*q8X({Igr`WG{wd27yER=Z2k36trz^obyEe3(`8jR<#}`y9p#|6I1xeP)IGb*|e! zSBdX!g)_w&Yma*{ua)`^)P^KAEyT8M?$7X5u2t^OP;(Xb=X@!guVZdl;l75?TD^x% z@UH(9Id=C~?aMWMW2O7<3itJu?%oydziq=MatvboKtY_1AS4kmRfVjw4XHW=cb9C# zQHLjFWcNfMh_>O0U#^_`32n#*1Pq%I}i!C+mYk$ z+^mi-YB{lTL}e?Z68`p1z+S)aS0MnunfdC&XX5Quq|0tO3s19lo`r9xk#`mzTi^RR z3ooKY+FFXKPqab(oQyY@FgUTH)-BJ5v-a?xc8GV5myx$d79Dh+^+t)|enI0Ag@r~@ zjQ4()$MN3JhOBgdh6-QP@-5>`PtxNogrylCqOw<7JhP6Tl`p!E?`XTmBkIM2;^;+~ z$8+M{WY67bf^p^^5pnoEHYCdKV;*?7ciLdO9Fr5w>%4L{7>b=CQ8cwkag>)Cl5y%^ zAeeO%^}7jY+XjSh1*$=1iqZwya|`|vYzDIBbY_rgsm|t?sUX!D7-9$ zx70`R4>io&pL>MYb>gmAObhU8<~^0`DXU(fJv38lxp06mnVI1_PbYPEVkSc-q!~3V zbcEt_T3c4Stt;HU$Ksdb8EA^|^q(a{Y>GelQU3*D<;0ICfctW2W!1|os*WkZHg`w< zHuvAxVModCKvgu0|%%(44PU9u)PFdyt8`QF6K((aMVq&bxix{2M`}ilU#^WOD z-AZI7&&GK3MqECM|9R1SWOEi(@cBPW1>xmO!5Z@{PV^yM_hOS`OGiQcKtR@JgXx+- zpKa_CVL2v?WhldvH%Bv;>@Uj;XWQ5+bx zl#VNY(`V16H7;RJM3%c1zghTA$-}ZPa2NtS3%{B8{Sv>|@cSl8(T~wv;;#n6?8A@m zmp~{Y_dik0PvMZDjnG~ZwGz0(cNh+jX^aqh;C z_r&r2|NZ{=>wxK6hP1rg_ap89(ayRdK8WAvbI@}a=y5ZK01usp@93R`{p%P7 zd=c-rqT6=HDUS04yhl$(eg$3uJO?{eu0PXpJ_OD8|M&afuY*xY_a~7DzKP#<{gE!g z-39*N1(;B{8^3>6^ZU<0-H9LF90t}QUwj%ri!b2)Yijl*pm{!CVX#!O#9+&9O1Izm zKmFd{+Vf2e_#aihJd7UO8T%BCL;Cp0(fwm3oimcOgvZ-nwgr zyZh1B*r!Zi5RCA*R&=*kx`xI9dGIO6&oAGM@s87uc86wk&T#4WI{YZFeeSyG_|8Nv zf;-oG0$LtKD;uq&mpIO97$9hzaWUZ{tBD*ot{|PzWsu9~(ek66|M8puC1;{gt;&Py z4l?ICsMh4c+AHMtb7xxQ%l+A5$Te5(uZ8O}&=o*_hj9w9849C@(BpXt6lG^x z@C?zz6t7A9CnH@M|BYOKA?xea-fv{WUrC$8zY~a_CRw(6c~KLp$zf=2lp#L5;r>W`w>%c#pg^sS;Zvx$Nw&UzU8ql7)un*=eqBwp zu6BAIzEwDvxBKMWm819aOr1Ls_X6Ef-3ULvJ92ym6KWoyeFLr&s}mQ~>lVz%Ej)G3 z#8^XPtr*!$eiUzuMah-lK~xib=CHaZ+OV+h-Wq(Gwi?&@Os}i1ofotA2z`NkOHVE! z@}D!qQK=ls z!b#PQHL*r0$aO2H-!^SheO=813u1F5A;iR~j75!|BVqQrTqw5S{;4-laOU$ip_>{a z^Ke$IuDZ!MMpX1Wg9{c_&!?ah8=_Nhvr0#Hx*fj&%Dmh!jf0cdA%Bp*i)9rBnBT+X zxAlHcM8#Kx{24@lYB-R0t(>p)Or`6U=8ozB_!Yqo`AsF_C+E3PS4V@dDI|O|5}#cvQ1?hk=H@AZ4q z2=^S+n{?Mig#DKlzo~d9`nNRSr1&ev$59#3-)%@6+J8bZrP!tTBgBdJBM}$6zZbF* zPlpWDMw{egF)d3;&95 z6A}LViqpyreYfH>ieFbeNCfYK5kSa!s^YndmndGP_!yAkzM!~+<_Q1vbBy^VK!*QG z#W{*g6b~sDzz^M(C|*DuiSaIw-xeHC8gf;reME7I;#wkp>y+N8*h&PSH-MD$Z8dLK z{FR~uH{@SPgdC?To}*Z<_%R~GEd`O? z1j`g5zR;r;$10X9j#C`3c%5Q};$%hkXBgfsij|5p6z@{JNAW(zT1EDE=)X~MzG75y zvEoCDEsDz&S13NNxLR?I;#$RL6xS(kP<&4DdBu&2n-n)IzO2})xK(kx;ts`~in|o| zDDG8kSKP1Iq1dT-K(R})Td_xxT?*=p^Pf?5SBxqyR(wdYMRA$p z3dP42S1YbjT&wtu;yT3*iq9!Nueechlj3H@mlazTw<>N|+@ZKrahKvA#l4E{iu)Bi z6gw3UD0V4!EA}X|V^2Lx{~yRHZql;g0h9#;pxhM*EX9xZyeENJrZ`e@wBlIBa>a3q z;}xa9g7*rgCo4`-yhX86afaeuiuWkqr&z04uh^(KUoonR^%Kv!@WhZ zQgMdjU5fW8-ltfr$bKySH!99oj4Ccxd`PiHahc)@#m5y_E3Q#otN4uKI>il&&nZ5y zxKVMF;%3E{6hafRaJimMgZD6Um}Msc0u2F2$TpI6+dxJhxd;>(Jyidz-8 zEACL-sklpVkK$g%cE$aQ9g3Zb2Nb&$yA^vBIT=TNhZK1)2Wg(wAQmf@D3&S?S1eN; zsW@73tYW$1IK}ab*C|#gPFCb$X-R*@O2rw9cPZYZc%NdeV!dLcBJSI0s+(nR+bAnL z)_ogSTzOfUd5(SG2K$P9#>xND=sP3Ozx*!uXx-wd^Vi(rj-jH1W?j}IDUy+kZ{|Ug z%QFK1=?+zZJOjDI5Fu+$cp!IU;g0<5Lw6WHdXKfkw7(Z7EpNx~3cl!L-4T)XyoY_F z`#=rCuK+*BqZmKVCEGaQ)fo*>oaeQVWMcLmJIwHGEkRjU0b& z8*e{EejHySFI2HjDvU<#3(QxX_&wm=vYX(2r)8U)D%|FFU_(PYR_;5v`^dMCL3SU- zu55GJeZ(UA-a!us)1`2m=k9Ic(I&DAV3*KdY%Oe;%{kG+jzYM_tqIuuBHJ4`W7zA= z{NZ1b)A_A}zdE-whcOn+5qzkZhvd!tv$>t6!oLzR841W3F*nga-H{%pv`t@*-29K_ zcOeYv{#fqH;YO28*pT1xl!_n2-PPj5{IfiW{=7IJ#A=w?xKO@g{BlbL1)U6Y`&fRK zw&*uMcB!zjH@;a4Sn`_iBlfqFF~T_YZgaazX#(Z8g{Q`;n%_9Dy7s2J>S%pk)6JM; zozT=&y~L3@);^|Lv(c=vf=~#`A*NURDTi10f6LNkSBx(|-ddk6AfGZQ$k(i_5jSFcY`ED5cd+US zc$C&7PISzA%7(pY9MC$vB^qHA^SWhkkMOf}!CRvHx4~7Y!o!5#&Fp237`pvP+h~Yx zF8YNGa&ao|Lq4e6p0P`5klZi*7I%|J!rzvweR1TFH~qfNZHRkv_C?};lTL5*w8whj zJA@HJfeB%ydvJyOwuHm)^hyx#ZgYQ*eMuZVV@uKj+4#=ym!q=d1PpdJ{XEcGvBLd1 zP7%qS$mzGZv)XMRNHpKJVw~d2?BqpsaAR#%H{uQ*%;1D;=b@Sh0 z*?Jk$v@XM@WV!L_CfP7wgik_yndN|_fZb|4YMcAP2@=`gQoawewqtpFOA${iC2mK^ z&KtPu;!ziVNQtjbX1g6u$n6M3XYfC_ z+U-DoOZ@`s#cMFS2>FLJl z$LJ4ve-hmk92a-w7e9CG``yU~Cp{4va4>hHO5RGmx@8}Bz3+-_=`LIqD#nKKjlJi( zyO1yY%TQgicWfu}UQUG|ss8BVV&@SQT?Ey`swRCt)ZVfl_qVW7IOIPl zU@91R_{)2s|84E^O51osJo}p4F5iQOi&^kk?Q*MS0JnTBncFVY9V;*USa;p6) z0q!^$t_EX(Tyeg{=Ya8;gdgp0#*g+@K>N^s4u13Tdl*036YWF$m6iaKmgY#!NWX~R z2l#ygKlibO~tA?lB(pe-`K;gMI)= z`a<9}P)?=Vj{^;B(cekV!Cu?BsL6aM$9ymcU&g_r^KH2Ha}sa;G#{9u(JlR$^wEvXSra#%}btt`AgaP`KmtH*%_j&R;_AEj) z55nWI{Ym{YN#Co=!^{5_&iH*tG!L;+(p#{%mdC%(%gcLXXJxJ%mow|=t6UU>bL;M} zzTKQfAsOd?-fmu$2`)X`9=kC2s3(?|!coT?NQv}K>lwlLW+_L}@i@072E7|;V0%5w zTl-#oF3uutEo6oBmdAe$`bUk=_-64bEhp=>{;o&wD+AZYWTUrS;+N@gvr{Fve&Re? zj~q+Hkwktxq~F+t*We%k@3eCmd?$|yrg4Lso;;L${!EY8^w$3S8KYOtE+?;8gxvhN z%5lqGz{7_)(WZTLbXd3HV$p4GFOC9oMG?1+^5*tMCS>b+BRk&bZPY-$yB}MLIJ{0CPT;Uv6K;n@ljyJa^@w$=?$R zjIrs%duYdJJbw0_^f0(Po<7Z`a5sS-^szjMUYL02>~J{@QFR}+Ps3Z8f26z!mY>f^ zUj62ONuS2gk9`(2B@S2QE&}$Qdnj;5sD}#>Fx&Vmd<@tpxG9IeHHT(j#6EO?uO)z_ z>+mD*SwQk%1hfzBoALWB>^}~q{Tkr=KKh%WCqaH|uYHV0eAX|kkzc@lVe?HB-KST9 z=J-}jA+ss3Ti8%j=h&Ob{e1@Ct>okkKfYVZ&E4%;Ay(J=dJq0iMZdj`tiB84#PY!Q zJ^~CN4?Unr{qT147Sc$(&yp^39F7M`|3q=0;%{h=_INOKMtk0FPK+r2J&^r^jY_|y z^kC?e?n;2PuUC43(rnkG4aeWEUw-NM<8IgQVTFLlw(H(}Ee47lMq{*k1Xny`40$B6 zC9)+YZ!(%3)eCR9NpV#O^E*KuBhnXd29)oexIJ4sO2aQ;2#J?s>pt+R(1l7D2VM=o z00-@*7?LIe%4dokk>BHe%7nxEs$KkiN*=$O;;g64IN6lgnVhzjeR1c*?}VT(bJ$64 zT{CC8Fh)Yzj<>|y zyp{;Jg89DGlJ&_JTzbjt@Aw@s+d;6i8#DP4FbN$Rsp8>WGX*0j4KcFE@l*UigxrBc z_IWsDKLt8ItmE|fzA&hRLg9cZrb2L_pC;a{hLI}J3ck4K^>73?lj))EZ?gmP>ZAL3 zwu^H*=99p3zgYNub1hl;dhzkf|T)Zn8di5&IZsG>(;$Gale5$(Pwdhy@De854-NN)c8lMo|#z$ig zvv_&yl3@Bu#E*yjd&Q*^h!ngDA5NGB!9a;#A<{3IU}?C8eu`I|WJ6l^W~@WQDqIZ* zSW$sc`IO;zW{%HyRkpIJ!hID*y@jGf+>ueZ4IN#$|8*t}bh0rk4W!qF4i=6*AHk&N zoe%^yipk=+i*GHmN~Rd#as-v%kw9x6u1nz!^tdgn>PTWI=03xyS;A;+B0s@|I4TFO zNT6;&L1R{g5k4v*VZaMW2?n`1!DIcHK1V+2M)@?=N_vZpJPIHh8h%VLyPiK3U@d$I z*99E@=+&ItpG6W)n4~bIKr|Pe>Y1UISF>RH3NbChB_f4xz>>OVRHFh2m@N# zan3;Q%HalGG4G+fvpp(fhbb427p>2QyW^Q_-HN^?`+SrILk}4@6$3d=v9jS6XU_4J zalYu|m&uwD{pNqGaS8cei68s@IDjGVQ7;P)DSOYkGV zsX+VCo;xZgA`<9oi~T2oH-oOmkM226c@CYquc`fP;B8RU7^UX|7lY>bhwc{ww}C!O z=}!T<)6BYW0evDe8Pk;RKMfoUdb!e{1AYhe2Bn_^@&w2`O0&Oz84_-V(p!Nqf{rP@ z6UYy?&q5j*TP4iCoMdp|!^ZeAPb&7I{;TQUPjc_z(^&5b(IhB>hFk9}sqq-iow z{5W%~=gwQuSXVdSe*)3xuetj_MJe+ai%W>YAbx8|La9 zDGa8}YkDwaaC_|jXcLYKMJdSvByhMEkFh05x z>BezU2s-3xj|KO~YMT~%(~#b7rYo^}eMLE!urIa$ujJN}%dfuriYsAyJoAv-XCY8L zcDz!AQe?ii%jz4|IXWE>!|BamhT_(8y*Q2*b^H>UZW}Y4bhwXsWlZq~>c2DyoOk*?(OSw}yH4_Z`am4i@eBAL_LR=;A z?lz&v-a!m}WbAb5Z=q9I)bVpUwn;& zSx&dR1R&TQ# z(>BRfdRAtm=z-by*`XoTYsMJ&g&$+hZ?1SRLsh3&yccy8!&sI3F3gec z%pJ`rWoo)zOhc4!C?;=Z6pa;o$wX_0dd}8oJP(7c>N%M)jh^P2#xkaJPWMdj&6w7l z;h9dgro|mUMtiM%FSn*e9WKwD+N1g!Z9zITi(t#&FhnoMO(zmehn(~M~Xt*grM{fy~j zH+rUQHC2sMKkAus?n=|*b6)vQKdcV_J!A9$jLJ zuGcih>M>Y&TBcQ2asb8NybO;=;V$=Ptc25~yAqAY8c=B5;e~e` z!$nzN*DG+bR@Dm3FFctZ2Io(*|kTP3of-%e{ z96(0!+J<7YCMAO(X;?h^+NB8AS%_+=lO4y@DEt>KdAIswgHs`|by{+^F>hPHKbeN6kflO8a9-pAg)%-R44J%<9Cjn`ilh5FdAw4%Sy} zz)3zuv*hxJ@IR7R&01o%kvacyyk~7<@;)GjvA$eiiNSwo!hJlo8!g#-X2}otJj>2L zD(9!2=oM7*5Y7&+^^}`LUQ4*^jG;tGukjjUH6eypUlT=N*NGX_%9w>>>^$stVyF5| z@tGQ9T8v^^U$%abw_s^Ag6KD3TMYDIz6)mX^Q03yi?d$QGLgVIzi{bx*v7l_k;)u` zV--MB`~47dPNClFR&^kOZE2ja3kA047sCT8jbd&gd%yB%PEI|lZ7CYFE{U4ajOwKf zI=3p`4i!`6*ola2=DTgydIzkJ7Jl(j!#}nE@X;Ui!ry7bKf6z%5Q#k9TZwk5hv)<5 zliU)gM4UQK-*q%mPvuQ^M|Ci;w9KL5<^O~*M4QMyQuM>inXko&os`V?O!CkVyvUf# zS3L@%ro@@N$gj^ye+D-|+N{bn2ru7*!shcfG@Tl0aQ)VlYLYWmrQc_ zpE@AR)^pM9UV{i=0h`u9R=gr8hBZ0FBprj#PTTX zA*r+so0dYbz}iFJQu0bHpAUPnu_+lH^5`lWIxWzjSSOz%6I8@Ew7HuwAVn$J#Bar% zpPacVIc*azBTtqre;Gk0L#r4wd^9rjMZTIY_{3q5pNyoRnUD>Ibd_UX}vK?BfAXtr_N^Mrul=FzD*lR z>F}~&Lo#-2Vzb1;l%k>0u|6ss`gYikipUjuKN zKVht8BXC@0L$>*Pv73O*Yh%ay*6=e;5L)Rz#YPn&buZg;Tzs47ppug{g@E;=ynHV`KhmDTNKAk#R7e>>2mPxBb43te+YSHH@%Q12d>J` znABRieO^}Mw4zPa^dx$jkMA{mCYSc(B`Xw({4EO$JTF#H(k`$?vNd*BKMmwiuRib3 zmaR{>2$0+Q9WY|ary6a5Y*%di-qp5DDhg;uTR-uTY~d(Z7cGs+T2?HpLMQtTBgwU_ zWndafOhE0?$w4hz5Uo8rlhZm;Aq-f1boy%#R+grW;Obyod)$HQqZ|t~DD3#GMF*us zHPJ4;^>^hRhkao3HahuU@1FI(w_wCo0#nmU$?t@?eM>%;1+q&1flSHokRJ4}@ZYMe zGybws8OQk@tgTbqXw6XN12*rOSAOYdKVHKx$OiL$+z%fu7iA(dq6du3I0DNH`yl_D zd6+MXd_MAF^P8(C;`5mJLJ=Z`?$Z+4MQ_TmsSNryumduA8%&^|^f(l!eS>2|Nuu)# z&iJ0q*~bW)mo6u&c3}hi;6=A%6&)W0*nqkV-_l6B`zjz}I~ctS?q&r9al~gJe8y+P znh6Eh@s500yqm_Q=Pnuv<4+W!Q3@}AjVTK$0}oTFqVvB;W-4%8{SnDruTcFAJZe4ZkuCYyPH_iVvOz5XgHt z=dtYe3y^4$yqk*Gf|)SRDsB~6zD~WwPCX)n9f54u3d76a*Qn)ZqV_Z#F}A^nIdQZv z{}E0cwaa{yiF*tV{`bZG6}FGa<>BRDr=pnYGI9PgERP!JN9nu2p1Hr7+y%zijAz{4 zekpuxEw=TVbnyR%8K%FSw`v7b)ypY1mxKa)WGE9!R$?h`!l{XEkukeHS6TTfus!_B z=z5k6mTF*i>CZAbjJY`R z)T`>c@BXB2+$hzZE(0{VJgkGcKk1Lz4YPJZ!<)3iRu|LcdJW`3sO})HHAiiGknPKX z^kp>3e}ubZ(6rhH2J8GZFMQ-gx-@Oe0T~G#80$ZKu7Gc|=b z>A_xGk1)>4s+hCrvPG0hfY~_NEx^7|MW3lm(T%q|hBJ3^^6syhF8mCOiK-kY8chgD zkUhqbpWoPvc=DWMN2mS7tAA>1M`}7)WXHjFgLWGx>>TYfuS4+qrl`SoaLgQ)HlR^# zfWo8=c447RVa-&g50-Hgr7g<+OekfF@&Q!7yRGu`{6%@ISCqHnqI=|}H}Q#ke6QIS zj>56?x48$o zGmKl+e$VO6gQmk~3Pk2(AvQ0`n9^S_*-tUp1D&>>879HV7Sv&wpzR81mi8U++#YY< z9*SPf|BoP#ZO@Aqr|$yhN5|rSLGJaV#`kxr{irkIfAH2Ko~N!XA*Ga z!_Iyq)GeIqlB{lv=}wdJ<=Cm57WQ{0XXn{5rJ1lmZ;Df0sFv_;mEr0WyNb5F&+lHn zjb3*sa`meyANXdoRFjzt%5CIBmXALrim zde#y5dY#V_eN&%@tWYPJ76$6YjxTB6kZgyX#q3KRlFu*0s|;?aDy)}rWD0#v?u~U? zrQ$O?u<69i6NwOdHa- z^y>+?ZfxK_nl!#(1<_45sQ-0eQDro~AzffF>29za;V?V$T?1D_>*d;-6`Ysq#u?68}T=nE@VR+dy`WlK}P7~Augdx#U zgrewKgcI=rYQWGadkKfavJ>J^G3MOvhE_y^{3jusZ&d0FT97_ zYYi7M6QNgD!iFv%c_+fI`Cmi8a3mVnVRl)=xopikf6a|YRVpvGIuH*NSO35~C$_=+T-q;=p`HC#Iql+3n0 zdLG`y6nQ@9i^U&D91aF3HBT6@P3^d*Oa!EbM8~rjVJr+Mok=~N&VpI~4`UOY7r{1} z97F*8PR+@v#cYOrk{JbtFi2s6LMBzhof$a>NYsmv#*xzuK2n4C`Sntx?g*%1n1PF+7FAJ+^_wr z)nu@q*$4H)7pU>X#G%LGbggk(+^^GoRys(!Ap^zg9JRMALUu;FtbcZ_c*t&moTwA1 z{prU)Udqn@cED7DDYL0V0$WT&(7&6?l2suL6mRNZ$A4Y$LlJz@F@hgvf-jA44e2xp z7dE*WA3OKgOX9V)Oo5VYQns}$777&xzF3Kw;dpaLQTVafu)dnC>cC|BI8->j?bhpX zI6k7i-!^F-{-hONTgzlJ8f;xo%%aW$m9M$Sv@~IlViFg46jLy)eTVfTaI)n~Fhdh` z10cJ3dd65(08o3QKWmPdRd#e8=J#fcQ{O>6)(e-m7Rug}XDK#Vhx0A+p=VJq?|>OEsey${Jz@1L*thyG~veszxQt)8YS z`wDFWO`d5fSg$EW*)tSnKgW}nvR~{`ePti@SIgeoZDemoFUd+oy>ARzin3RD(o*jg zgRC88&rp;-JHMBDJSlsjM^W#k+POqmXtpM@$@HcRXFKQUcHRKj$5{k^_o0c-w@`rB zQ^85;7<03p*9T9AmUDYQ$`&JK?H9=9TY6cMJ_?1Pzgx} z5(y-dL_tvqWFUc%#AJqxf&>BzM2cFp9E%ndEuN~yO50em;-%oB9&d0e$I>=hY7>Wp zk*124Ht+MS_3W8Flc@Oiynnot&(3%M)_twL_S);d_E>dBIYynst1!>V$EY8-=9vsJ z>Wm-EGeKh1)2uolYmE9VtIkAbNrv&He5C0+Q!9SEizMy>TB z5Ub z2HJhutlF7<%3b>$%8KUYA&{c)z%WSBJjSYN)a1w*lc_N#uZ%G{CdTBqV@!^Rgn=4U zSF|P34n_x+U&i?>n!N2<f3hN=J8`jIMySi*}pQ+ti#GV&6nF>hn`=pD0?PN$HuGc$3M+()j#fqytwW79e4z zXov1&UtE#gR0*%J2p0Vu^9Q||22&@|42FRgvp8u}UK(|5<2b51BQ4Sh@QiP`<5qcR z!{kZN$<-NW$GG_wwmCi)V;k}IgeWU5(hTtUeI%JV5rcr;5Z`Vw7G~t(6Gb;Q)`k@D zSbWvB*dz9nZ+p&?VqGdYBbR!%>ylkX+q4WjRe2sOruXx)vXQ`@H0`6~sLX&Bt zo{lw5Wpqfyx%_2Lcv9T;Ft2?N^RR_SLTrc&(EO=OE8yy9vmHZEOf@%6}dUt@q{2Me+<>I)voZgMA> zNhAfoLv#pUNhAlaIN)DvhC(vXlr-j+nO^lI+pC^zXP#IiKG#UFD@`U~{^u~$U#9_- z5|*f*F%<0a9XmtII|^WCtfs6pbM^``KM|u`=sY}bB7%=lcNR44Pc&mNnJVwCx@pPR z)UK>SOP+_E*A>>FC4V>CHLWP-BTn!uZwr|8JdZN#D6aCtBn(d2AlsH5SA8TcOkyNn z{JX6R-)MaJTCOI#u0E#rBA1bw#7$ZEv?))%mUEFnUf(8=eQY2R=8iTVsn*ECv+!pk zb`eG}&{g0`fQJ~E4e8U`D&5{jY0XmzZurI>%wF!)q#$;SJ$@i9FO8ijyer_HNy-E! z+|#z986IGbh0b-2ac%lSrj0SS(Hb*ON1yAuWh|SX=YKriXAso7`zYW6zhWLRJ`N=% zCf8x`twVJ1Z6(yGx27GvBI)r&4FoF2o?m)AWiVcEko4YaM~`dL-DI&N4SneSpV~3{ zzL`>|P64-Sj(rWeZ0T<>A3odOpT!9NjH>Yg*=}7Z=>rq-dTXUOvK_q==}m2`hrlZ7 zdm@orQ`c=_TE`|1Bb49PYSTG#wx}JwzoFtqdrR8U`)}y+HAuu@y73JKMu z%nTfiiL;O}BXi9fnQLZ7G)8Oc?}HO5p#Ww@%|8HD!!-C{Z_IOmP8v!#tAZf~^!XXf z8*A=|WsU!(pY5ha@-C=i(okdVzOK=nbtz+H=2LPVX4DBrK~A9LvJRu<+74w?az&LM zwn`WYiJ01Hl{)@wL1TeyTsYJCGp*Pg%_mf6o=|LDuVS$9gpxwnq0YqmH2Q3?*ZeWJ zlV+2DeO#FySq7*z7Afm6xP@doR_EVs%5>c9VS+SadN@K0R~za$Z5a~1>JYkk2X6KC z>K4L0GnC>D^Xq!l9>g5%?qM_Zn;g;#3b%(mCC-?2>O88N{vR|&*qE5VAulnEYJF6d zjR&e)3)x=3;}&=fl^$P^1b5v-Jtg+ccmsf;ukL0neQlk8tr<(lj+^dG46FpkPb;TT zahhK@9p=qsm^M=#JaYg}bIM!Fx}8iKe3d$48h{CpUE1hA57x(Omn9~JRw=OjaCQ&P zV#B|)TZ2IE9f4CgW|>#*IL0DRL~^&CA?SpVM8~lcjrlOvHgv)$Zen-{I-N_lqi4kR z4C9s96X|IqqNQUv80&dXjb8Fs%r;cS&0^ZxG2dB&W*B#|#A;7Fzrlo1bBemSZpO^% z$g+z|y#_tm%WUu!!frzF;jme#>&`psihV*6*;5bW9iP8rJN{>}KF>$Y-GRxH6f12BM;TE>t+vMnNf_ux| z#qe|;PQmBqVAiZr#muT$1$QQ8obtxBM5#1hitoUBxOjTK^_JV|moQx3)bS*vnN|@* z(qM^IbGWZ*q>M8WVSCsVoSenbd;#K&yz0rNo;YK4?u+9biNSvEr)6Tt=(;nVHWYI9 zVM4HvS$cx$6vv%l-!N9e9#0Hjg8xauLE%*6Ja)T-?eK6TBw687BUpF2%Nml~CM>6Q zHk)j8Q)3F+b^q_v#GQ!5Rud*0pTcW^g*|SDMbMOXS!fY42c1EtO;uJqGXD|UFpm)R zspbTfC73kUt7heqj!Qb@f0w0Z21?60FojM5{n!@(x%y!3|*~pTR_6$Uq3Ad-qWx`)D7PYP zF*9&iKa-WYIv?}QzcMVwk@&hri<`WND@x0MMmyN(SAtVQZ;&@_%_QZ8n#-V^w=hyd zW0!%cC~}~P+QF$+C||E;Gq`Ro6x6~PfZlMt9xs00RXW^2TSaWj*q6*i8BAU@Wm5Q; zD6RhKxNE$X#m4Y)qa@P{yx;#D^UZXEJx!dDg-##S7;YJd+s=032M2!P|La^`yUG8Ww?Qhw>2Qzb%pd zK~!?NrD|gXUIKK}o)gNlmbn3vIf{hkQ}`w+VxsdoEO?$-7ApLdu1#8cd$>Q86EHhBNd08oEL|8L>Mla?w_pbv>_q?>?y+ibt6Pm{ zF<{&bt0g=RntWqWlWYw&N?&susoxv=Xw-Qu|Cy4pY^M5~#YQGROs14A+=mq=P?7Fw z6wK^scSQ3zW_G#vjT_Q6H^!>@F!L5MEjfioOhg~GR@88KE@h->FhS9G6y~FeqsW_b7CryP`j*?sJcg9B zk*)88WNT$s*lDst3Pe0+=FCdZH0EaGHqy3PrCetOd{-o3h|x>y&xH!?MHFjKrEqhY zou>Qk7x2*?>cuV%3lkPb=XcjK4Bp@{@9pe|ST@zb8XP~ud4inY7kQ*+<*Iq!3sHSM z;ugkqDQf7I+Lf=ghDN}ualLi7SKBCI1e5W{&-JmJ^$|N+!J0RYn8A)Z=S%kU=7}as zl7no(AQ-QHNib5@v8(A!gK`NYUyFYxl(qIL@#QzKWO%+w4fet&hS>~n3 z1Jh)L*l3?&2ws8Tte{~sHl|Oz{kCvatv(wr?9mZoD=KDk_i48*u!W~-5k^->xRuMc z4xQV^p|ELcv0VG#a}wLQcBYGroehhfV#S)iYMrNC-y(&XyJrI`LYJo8nSd;UyZ%f2 zB0W&^LFbYW?xECivtHAGL55}tkDv0W^Ud1V5d{+KOQ{iz3 zQE22&*2nBl)*m9@eFzgLF!?ydN&Y7j(0awH1#G3g(SM?L-*C36T3Ru3g&nZ9gEa0v zTXdfqgxPN#5Igg&t?3Uw`?K+Gz^DDJOu-9H9yjyF`C=m?Z8HZ7nSSW!z=)K|CTPkw z*^d+Y)yK4kR=P9LSDs2jwbF-?Lv3UDVnZ^j5=;j!#p+}$jz^HzNvkHrnmSNJLo7ux zGUBn0^k#v16aKST7|HwyRl?UG-(JL`g&YWvfM)6o935dfT)G-i&c?@ZmRx%{*!M{x0NAJBnp@sdClt_NcQu=&B#~+#mDYvl8vP!$R0K&*5mJ zexQb(bJVecSmQCY)^m?9os%pxjkxco`wu;4%y(VtCp_xSp8J!$PqTipUeSt`L!G0= zw-HWL-Q-v3V75V2;P(5)k+9mcaFBIkz9=#BSpU)B38?S@Sof{wMhiC`<)$1_dt+Y? zhT9lxFe9*diyN?s&1(AXz9a5_jNC|qpK<;UB%{*y1XX&Fv2+f6&+8obwRRN}2u^Uf{g`Pgd5B!^JcneM!9ArzfBR(A)Y z;3PAPu)*!7GYglPH!b?vaUH6RIE0YU$*#`r`Mg_=yR=m}p#iQ(dK8TZuIt86lrqe8 zi6Lwo<=Lexd~wXf4R=>S+6hqylf+-~Wn!$8LZc8cZi5%Z_U)Zl<0cIiTw8y)-KALT zdN=cTILq~Kf{E3b4yL~EE_tGBotE~cRh)sW*`(iY>2SN#R9QWOXJwT#61meS-vhGia4r7PT z)GzRqF+t(kuI{G14t9_j?=A)s=*_w;^ryh5ck>oT`axL*7>f77YSv>%It!1pFvTq4Hdu?TAQ&s~!IfN$VDH#pV zG-g5=u0UlqqH0eSP<03Oh(iNS{YRCfdLKO5@ZzAiXz8Ux@56wOFp1b`IW*AZ?^7JM zM?P=afR#RxuF)~_eFsBpJM&UP<$U_9mnCjYT-tHJ|GyK$S4rXLNk$>jt_FgM;~k7b zQn;5)T+-1fbO?8V#biFR1auD_-H9Vo@#s+czJq+=J#nTb+_%l(id}za8?^|q%HR^u zi)WMFt=>l0k)Bjz-8>Mpj`ZYQJ2S#=YcK-eDg=XTH6x`oW~ksKm94cax1N`BXG0S5 ze&JS(Vs}8Tu(jc>P=wv4ud`62X{hFo?aL$CyeZ_eEF!tEk z?|%ZX#xDD6{0Of`v@1f1oU(2z>{{1MgJPrqM_9vNixr$3(sP=IL-S1H!-O&cKp@|C zQtpd(3*|uNuoJw{JLj|BtRC-@o`d;tIbIEv>^Kc6$!at`gBYHP=iDtsVLyW6lJhg) zbb=@hn&+?X>?G9r_t`2KjwU*bif5s{^GZYsCvF%ePl8?-9{1J$hR$~*jF-jJ4^~n) zHBvj^K`xo; zJzQ*;e2S|xa(oZIkSO|gVI0OmCH=~?K~~XGS#RXa1-(kDLGP(fIOBm8d8iq^{B<1HQqy6pg6f|r9%sSAIk+yw)eqNTT=BnY6+VAeRYjGPQG{b4ihSi2LEj{t6jAK> zmsM6&hpGa;(sEy3RmFm;Ky|f4LS1;+r0r7bsm4D0VU?PKyeK<%`NImX<@Iy0W6YI$(Ur36w7gmN*ly z$c#8PC9r&MMNw6;&tE;S2qy(+lmzB2stzqyB>n!p>A6!S9~H_ zfIIdCK;N`Tf=GcW#G{K{1C-6t#>Gy4NfFY^re{Hh-(z=Tt{Z}kJl#zVEUu_pF2SBn z=t+H#g7wTCUT6%+GmnYv#g%1&#ewo5l1<8)rPW1q%OL0G%ZvPqd5Z!;R|r%sLDZv> z%b0Y%w0K&gZjo7@#e4BF2%u5Gw}ii_sn13$-gF7g@8axU7H zxTweLM%)K+y@~4sT&HnSJrj0%$PDFyxW-BTjohaCFwZ^Z`7*=%<+wKEdeoyv_5WY4 zT%-lhgXac+598t*v1thZFTpXGj_6+5xOcBPxOcB=+?SQkt)5%DfQO;@a1@I#b=auU zLvymH70%#$tBhfCQ+~nFoS`@rWrSnzMoh6csp4qyN^!&}*XV=^6P$IIJI=(xD6>(Z zr=we|aJc0^pho#w6lR-7pucY@p6xJfg%9)j>DiNg@a!sIW~ivlmywaF0wSU_<_mSk zexc5Y;a{jV8l{mb(yRvs86!uJicvveMvl5_VmoT_N+YJU7v7Om+V|V>!JoF#o7A4- z1g|1e3nK*R0_625E@wQ>Arh%Z+12-QIMj1+ZO3&OQ@8etCE{9e5Y+v+ig4BA!f~dK zDX4sobMR(-Gj6cteatlr&-$h~HJCX+-dsEKW~lwIoCQ`DC>vH>6fAOv&BZrp7{#gz zt1u5=929);=*)f>!x*XpSl(=ZsX!~ksUKc9^W*p=Kkx1@bs65lzV`b{`!vVz@*rH4 zQ8bxf+H8CFmPGT7=KTO%UiZq-xibeA`MsuH*FDye=&tLYQ-r#Q$D$8<{Efjye^JcZ zAI!~hBLx*V9*m#Iy7Suom}rJ_h5pjG>u(zF>7M(Wv*66mBwGb@*{&tfbt5KhP~TOG zdJ`hn1^1J1(O=ee7`8}Uao>0g^0Fr$u6xjm`FYj9{+VRSrpmVl;h{STsQmflx|O{l zV)!~bDB6sfEyjG3sjKnCC&q*J{a}{@yjNf%RPf3;f63$8QY%-2Kg>ni!vpH0i^@5M zgxf5Avicgw`J42A2>BG;W7^XEZg3(TzgO~$;33GnB)<*bhKlt%$$tW!>+v0yd+eF} zc!uLNfOL-oUJb|xwW!Nd=B$u;SXp;9B1E}2BdIhyBq!*?@LSCPIC@_amB*5Ro?81lhs(dWk#i_V39 zzVydI-iORj`^-OAL;gA3TPXc0kT0C#k3Kicyff8t-hmp!n}ApBW;BL;k~u&B_N%NN>i346mTt=m%A+7}$NQED^GvUa zkbjGpqZ{Ht{c9k7mxK~`74-S5fgk6<{f&@$ z593DuH|aOvo#B~&Pe4Xeo9i6sWNSU-RG;G%Abiy4`GB-*?VoHtB>PY@JCqEP?|2R(O?%3{+?(K_{-;R_294CJoCnw=Kx<9)2 zjX3$zIQjB8`3lJ1=ganr`=fi=aq`SKxj4>#MV$VcIC(>y{9v5?NSyp+oV+JaelAXa zDNeSdnl?s^&ws}0zY{0_K2H8~oXkm2?vL(a94GRl+%--@?cd(nd#7|b|wdc`2p!S zKqXVX&JR|V;s{Q*x=g#>TKCANT~FgyC$}9rnI9~w3c7taj=#J(az8y#RE&<893+R68CqP)8w7ynrA6cJEdo{$ zSX_y|q98g=rUw>;%8D3d$9V72f&;-&wKH*EuyjdL5GHxTsJ}XhZi&+B5_qzR0VQP; z6537K(=*cH>uAi7I~psW@+u!_eS-QJf!&uB7}xRcTRKx;ga} zj#yES<}a@f1{N0ts)NqN+=9}|g3>bHAr_M=<}rA=_UKdCE6g`Xp)%>Sr(c`H&>2<+ zZ8wtoi=0VSfxxYS0>2xsO!R2Y3+5NFHEFVl8w+Na0vvd{2syx2%P+{8eSLOjK^7X^ zIkU6;*^{#h%xzxw48K>)${qs+xp~H5j(?K1k)B&nkUQ0=O^-N}F>$JY`o!7O0B3R0 zqQLC(*^4WR@jQ(%cX_N#EhnR>Y#twCz$TpAW#&|yWX(joD;M2#HkP^hCWe#CD&`iI zQ7f90bMgvo7eiFZUyvoG;<7Sp2j|k_MJ&vmew|(vb_^7jmoCYyTH^&YmHQ5Gnw$}X5#RfVjNY;|2R zx-~O0rv{4Hv6^05RIRgfp%{JhD3mTJ4VK;-Fz=)}XcvLaiA{^^v!~g_WsprE&HI#B zkmE@(q!DvrUPV=qy0-t*X6H$PqF_m&YBCL3c(s%Yjw&~lVJN`e*TWe$p+ z;Oz2XMUew@=5XO)RT=tOt4+tHGZ&5{xhl~4YfKgu7gss7iA1a@FUKqBMDklSNf;4? zshqjReX()tzT5WJee-!ZvQ9{b0d|BU9ttC}9{Sa0S641JUH%F9u$a0o>S?HkoBHgH zB!AiUW2+TDi}03LR?5rfqUYeQr|4{4Aw-{ zak+6VVpA0pH+6v_Te=d8liWs5Ac?Io)x7LZX4P7oURa{Za>MX;sh~Ayg|GLWWKAF ze2@5u__P=nKNHXDYW>3onI>HQK&HE&MEXvZ`i&&wHCHT?`f`xwH%fj`+(W|rbK*X! zzb3vVhQ(p%_n`UF;??3c;@AKL{a_+GhNerNO&Q_8 zjWYaSBiLdNvO1)0x zm?`7;jQFy6RQym(Kn0!l&lUTEw3jM*l9(s;1(KJGtErA~trx#X!v53ZTjEhL5q-CR zk$jSbz0br}sh^8}EZV<7yj+|hmXWYmEv^vPi<`x_#M30)OYULWOB^XK6`vHJbM-Ms z!*aQXg#E|GzlfiS=k~PvoM%jPQ^3U87(bKzsQ4%GZ(>R>Yks^q8Kk`jNR$T;QyuYq zTzraz`v)bzCB7^53(m9pmx|vJ=ZO!BzYvd!AA$7ubIR~9;e5+3B>c-1*N9uhr^KV; zUqHHZN^-9Yto;)4F|l5JTV$sX{pkYI{&|!Ujy_^P@mpf1SV6+xGVvX;MI46yPU zxm8R-KP>H~idi7@Lmp+Azh0ar{bk}-@llZF#gmj_evkO9_=5Nj34i`5W_{DjH;S9Y z2SB|(1w4x~JXyb1LasoyH~`z6Tv%Tl<^+E zOnDCaMJXfPN5!uFpk9Rf4LKM6?&Lgte?ZzRrwn`Frwn_KiqD8Iif@STf{DfW-jfK| zwU=0X^TEV`=8JR1Tf`;eCh>dXR`E&kW$|_K8w0HSsbY?Jqj-n-u=uq2 zvUo`RKx`3nFSG7eh?~Wqh;N7=i7DT*=C2TQ#h|!F+#?}{2c&oTxykFcYJ|otPuZeGo{}j6pvHo5p4iv8xGsWrR9I-+S zitEKq;xENl#ovm@#FL^k)cW5|>>~~qhl`nFwzx!GE8Z(^7oQUMiU-9b;$Os%#iU^< zHx{5i2C}@mK=g|R;%qVr^GGBwmAqE+X35(m|48y4$uCHLP4YXEk4gSW@@dJPuCV@H zLLyv4#H&GuH&=3@SSpr_KM)@me@wW?d!hI(akQ8rUMCidRbq{} zS$t6ZvG}ZbKzv91v-nprVT27wir7~iERGek#60mf@w?(S@!!RIald$2d|zx9|0#ZB zqzy+8@iK9+m?2&(&K4Jow}~6X?~9L$&xkLH2gKir4dP$Lgi%Ttv9CB-94k&13&nZj zEnFTNzcBc2dj#LvW@sWyBUi5H7w#Vj!>esi=nKTCW-JS2VsGT-;T z(wa*HDgXW|EB78_Ia9n>{254dMK)`#Ag{1Ii;Pr85G+8ZQh zlbtYjEqS5%xVTR|C8mtC_7{n_i}#3+i3#JaxywnopDLz-iKt)YNuDLSO!6|x>m_fI zyi@XS$#RsSl`+FoG6n`%^i(MyJ_bwGj zgS2-mWrXVv$-Bs#F#mw+h{wy6kq?s7tUOpO5EqK8#m(Y_;*Z5YiD5BmqIIu3NcV?I z9z(eh^Ft&rqdX1uYH|kRNzO#Sq<9=;I6tEdcY38;`4W)wRLUJV=g@Mqc)vI-!>W%W z;Z7Pzd$XlpD{d6iGp+f_B+M6pG+!^T3B+~XSSPd{;(I9BwFH;4i89`PsQ@5LtZq2AIUFKUgkLOlFM-#fz&^zd@J%T z-f4!fFGza>B&UfBsD2yf0ZHCKc?ISKN&X>a*!z*>BVrTPS7Pp*@6<;A&<9i@EEV+9D?Blvf>@N-z$B5}-j(DB8jYPPf6TgWvmgzqPWO=(v z@-AJ>0{)`v)-#WhLcXNu=vm z$pPVyg7=F^{PH^3ayZVpI!F=e=YpX7(cx2cZrSTkijr+-NOY{Z4;F9vCUq?j#E z6U)Wr;#!dXdf%7)sJL5vUi^*tKFD#9lvahLdnxLf4BNv0FmvXR@w?cz>x zm-vL}ON4n`{l$Ue5OKK3Ig&ItMjS7uiJ4-Sm?P$i1>y{GmN-W&7CA?i{*;N8Vo+Qr zt`KX)wIb)R(*9lI-QpH;tGG?vF76a}iBE{T#XVxZxL4dKz9POVz9zmY9u|*?N5uy5 znAj*D7n{Wv@sxO4bnvZVd^?Dp#jav^v8TwjSTx^9^ojk&f#MKxxR@%A5yy*ZBG+Tl z-7GOj%o7X58R9H)j#w;~h-G4>7!;R@E5sUct+-yiOT1g$B5oD8iQC1U;x6$Cakscf ztQYr+`@~noS4AGU$MhH@ju+F!OfgH$5%a_XafUccoFf*CJPv^VY!SDL+r;hSPH~s` zgt%MeTu-`FFPixrkoQS`MSN9!O?*>4EFKY$ik$mQcaMpU;&HKAY!SI{0L^z6ImeT7 scd@5VriqzimY5^vi3Q>eahCXh0BpNT!2kdN literal 0 HcmV?d00001 diff --git a/contrib/lib/libsbigudrv.so b/contrib/lib/libsbigudrv.so new file mode 100755 index 0000000000000000000000000000000000000000..5758e41c1ae81301a75f2637642b3d3bea278998 GIT binary patch literal 89865 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 MW}{eA4Mh3--{D`U$p8QV literal 0 HcmV?d00001 diff --git a/contrib/lib/libsimpleskts.a b/contrib/lib/libsimpleskts.a new file mode 100644 index 0000000000000000000000000000000000000000..3124d045f61d87d1c057da1f29560b8a93a89bbc GIT binary patch literal 863048 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

$d%}-}p9#4pqg>L2gM^l!8sWL}7YfG-Cktl? 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 z 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 literal 0 HcmV?d00001 diff --git a/contrib/lib/libsimpleskts.so b/contrib/lib/libsimpleskts.so new file mode 100755 index 0000000000000000000000000000000000000000..bf00a0ee245de5eeb85cde98864df8b2f906f6dc GIT binary patch literal 362768 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

P=fz_-mcH3UgVur|?fOnA5z4U*{iR;a8DI3lx5U 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

Patterns

+# +# The following example generates a small XHTML document. +#
+#
+# from elementtree.SimpleXMLWriter import XMLWriter
+# import sys
+#
+# w = XMLWriter(sys.stdout)
+#
+# html = w.start("html")
+#
+# w.start("head")
+# w.element("title", "my document")
+# w.element("meta", name="generator", value="my application 1.0")
+# w.end()
+#
+# w.start("body")
+# w.element("h1", "this is a heading")
+# w.element("p", "this is a paragraph")
+#
+# w.start("p")
+# w.data("this is ")
+# w.element("b", "bold")
+# w.data(" and ")
+# w.element("i", "italic")
+# w.data(".")
+# w.end("p")
+#
+# w.close(html)
+# 
+## + +import re, sys, string + +try: + unicode("") +except NameError: + def encode(s, encoding): + # 1.5.2: application must use the right encoding + return s + _escape = re.compile(r"[&<>\"\x80-\xff]+") # 1.5.2 +else: + def encode(s, encoding): + return s.encode(encoding) + _escape = re.compile(eval(r'u"[&<>\"\u0080-\uffff]+"')) + +def encode_entity(text, pattern=_escape): + # map reserved and non-ascii characters to numerical entities + def escape_entities(m): + out = [] + for char in m.group(): + out.append("&#%d;" % ord(char)) + return string.join(out, "") + return encode(pattern.sub(escape_entities, text), "ascii") + +del _escape + +# +# the following functions assume an ascii-compatible encoding +# (or "utf-16") + +def escape_cdata(s, encoding=None, replace=string.replace): + s = replace(s, "&", "&") + s = replace(s, "<", "<") + s = replace(s, ">", ">") + if encoding: + try: + return encode(s, encoding) + except UnicodeError: + return encode_entity(s) + return s + +def escape_attrib(s, encoding=None, replace=string.replace): + s = replace(s, "&", "&") + s = replace(s, "'", "'") + s = replace(s, "\"", """) + s = replace(s, "<", "<") + s = replace(s, ">", ">") + if encoding: + try: + return encode(s, encoding) + except UnicodeError: + return encode_entity(s) + return s + +## +# XML writer class. +# +# @param file A file or file-like object. This object must implement +# a write method that takes an 8-bit string. +# @param encoding Optional encoding. + +class XMLWriter: + + def __init__(self, file, encoding="us-ascii"): + if not hasattr(file, "write"): + file = open(file, "w") + self.__write = file.write + if hasattr(file, "flush"): + self.flush = file.flush + self.__open = 0 # true if start tag is open + self.__tags = [] + self.__data = [] + self.__encoding = encoding + + def __flush(self): + # flush internal buffers + if self.__open: + self.__write(">") + self.__open = 0 + if self.__data: + data = string.join(self.__data, "") + self.__write(escape_cdata(data, self.__encoding)) + self.__data = [] + + ## + # Writes an XML declaration. + + def declaration(self): + encoding = self.__encoding + if encoding == "us-ascii" or encoding == "utf-8": + self.__write("\n") + else: + self.__write("\n" % encoding) + + ## + # Opens a new element. Attributes can be given as keyword + # arguments, or as a string/string dictionary. You can pass in + # 8-bit strings or Unicode strings; the former are assumed to use + # the encoding passed to the constructor. The method returns an + # opaque identifier that can be passed to the close method, + # to close all open elements up to and including this one. + # + # @param tag Element tag. + # @param attrib Attribute dictionary. Alternatively, attributes + # can be given as keyword arguments. + # @return An element identifier. + + def start(self, tag, attrib={}, **extra): + self.__flush() + tag = escape_cdata(tag, self.__encoding) + self.__data = [] + self.__tags.append(tag) + self.__write("<%s" % tag) + if attrib or extra: + attrib = attrib.copy() + attrib.update(extra) + attrib = attrib.items() + attrib.sort() + for k, v in attrib: + k = escape_cdata(k, self.__encoding) + v = escape_attrib(v, self.__encoding) + self.__write(" %s=\"%s\"" % (k, v)) + self.__open = 1 + return len(self.__tags)-1 + + ## + # Adds a comment to the output stream. + # + # @param comment Comment text, as an 8-bit string or Unicode string. + + def comment(self, comment): + self.__flush() + self.__write("\n" % escape_cdata(comment, self.__encoding)) + + ## + # Adds character data to the output stream. + # + # @param text Character data, as an 8-bit string or Unicode string. + + def data(self, text): + self.__data.append(text) + + ## + # Closes the current element (opened by the most recent call to + # start). + # + # @param tag Element tag. If given, the tag must match the start + # tag. If omitted, the current element is closed. + + def end(self, tag=None): + if tag: + assert self.__tags, "unbalanced end(%s)" % tag + assert escape_cdata(tag, self.__encoding) == self.__tags[-1],\ + "expected end(%s), got %s" % (self.__tags[-1], tag) + else: + assert self.__tags, "unbalanced end()" + tag = self.__tags.pop() + if self.__data: + self.__flush() + elif self.__open: + self.__open = 0 + self.__write(" />") + return + self.__write("" % tag) + + ## + # Closes open elements, up to (and including) the element identified + # by the given identifier. + # + # @param id Element identifier, as returned by the start method. + + def close(self, id): + while len(self.__tags) > id: + self.end() + + ## + # Adds an entire element. This is the same as calling start, + # data, and end in sequence. The text argument + # can be omitted. + + def element(self, tag, text=None, attrib={}, **extra): + apply(self.start, (tag, attrib), extra) + if text: + self.data(text) + self.end() + + ## + # Flushes the output stream. + + def flush(self): + pass # replaced by the constructor diff --git a/src/python/uts/util/etree/TidyHTMLTreeBuilder.py b/src/python/uts/util/etree/TidyHTMLTreeBuilder.py new file mode 100644 index 00000000..c851d97b --- /dev/null +++ b/src/python/uts/util/etree/TidyHTMLTreeBuilder.py @@ -0,0 +1,6 @@ +# +# ElementTree +# $Id: TidyHTMLTreeBuilder.py 2304 2005-03-01 17:42:41Z fredrik $ +# + +from elementtidy.TidyHTMLTreeBuilder import * diff --git a/src/python/uts/util/etree/TidyTools.py b/src/python/uts/util/etree/TidyTools.py new file mode 100644 index 00000000..f3a07415 --- /dev/null +++ b/src/python/uts/util/etree/TidyTools.py @@ -0,0 +1,128 @@ +# +# ElementTree +# $Id: TidyTools.py 1862 2004-06-18 07:31:02Z Fredrik $ +# +# tools to run the "tidy" command on an HTML or XHTML file, and return +# the contents as an XHTML element tree. +# +# history: +# 2002-10-19 fl added to ElementTree library; added getzonebody function +# +# Copyright (c) 1999-2004 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# http://www.pythonware.com +# + +## +# Tools to build element trees from HTML, using the external tidy +# utility. +## + +import glob, string, os, sys + +from ElementTree import ElementTree, Element + +NS_XHTML = "{http://www.w3.org/1999/xhtml}" + +## +# Convert an HTML or HTML-like file to XHTML, using the tidy +# command line utility. +# +# @param file Filename. +# @param new_inline_tags An optional list of valid but non-standard +# inline tags. +# @return An element tree, or None if not successful. + +def tidy(file, new_inline_tags=None): + + command = ["tidy", "-qn", "-asxml"] + + if new_inline_tags: + command.append("--new-inline-tags") + command.append(string.join(new_inline_tags, ",")) + + # FIXME: support more tidy options! + + # convert + os.system( + "%s %s >%s.out 2>%s.err" % (string.join(command), file, file, file) + ) + # check that the result is valid XML + try: + tree = ElementTree() + tree.parse(file + ".out") + except: + print "*** %s:%s" % sys.exc_info()[:2] + print ("*** %s is not valid XML " + "(check %s.err for info)" % (file, file)) + tree = None + else: + if os.path.isfile(file + ".out"): + os.remove(file + ".out") + if os.path.isfile(file + ".err"): + os.remove(file + ".err") + + return tree + +## +# Get document body from a an HTML or HTML-like file. This function +# uses the tidy function to convert HTML to XHTML, and cleans +# up the resulting XML tree. +# +# @param file Filename. +# @return A body element, or None if not successful. + +def getbody(file, **options): + # get clean body from text file + + # get xhtml tree + try: + tree = apply(tidy, (file,), options) + if tree is None: + return + except IOError, v: + print "***", v + return None + + NS = NS_XHTML + + # remove namespace uris + for node in tree.getiterator(): + if node.tag.startswith(NS): + node.tag = node.tag[len(NS):] + + body = tree.getroot().find("body") + + return body + +## +# Same as getbody, but turns plain text at the start of the +# document into an H1 tag. This function can be used to parse zone +# documents. +# +# @param file Filename. +# @return A body element, or None if not successful. + +def getzonebody(file, **options): + + body = getbody(file, **options) + if body is None: + return + + if body.text and string.strip(body.text): + title = Element("h1") + title.text = string.strip(body.text) + title.tail = "\n\n" + body.insert(0, title) + + body.text = None + + return body + +if __name__ == "__main__": + + import sys + for arg in sys.argv[1:]: + for file in glob.glob(arg): + print file, "...", tidy(file) diff --git a/src/python/uts/util/etree/XMLTreeBuilder.py b/src/python/uts/util/etree/XMLTreeBuilder.py new file mode 100644 index 00000000..405a7f28 --- /dev/null +++ b/src/python/uts/util/etree/XMLTreeBuilder.py @@ -0,0 +1,113 @@ +# +# ElementTree +# $Id: XMLTreeBuilder.py 2305 2005-03-01 17:43:09Z fredrik $ +# +# an XML tree builder +# +# history: +# 2001-10-20 fl created +# 2002-05-01 fl added namespace support for xmllib +# 2002-07-27 fl require expat (1.5.2 code can use SimpleXMLTreeBuilder) +# 2002-08-17 fl use tag/attribute name memo cache +# 2002-12-04 fl moved XMLTreeBuilder to the ElementTree module +# +# Copyright (c) 1999-2004 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2004 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +## +# Tools to build element trees from XML files. +## + +import ElementTree + +## +# (obsolete) ElementTree builder for XML source data, based on the +# expat parser. +#

+# This class is an alias for ElementTree.XMLTreeBuilder. New code +# should use that version instead. +# +# @see elementtree.ElementTree + +class TreeBuilder(ElementTree.XMLTreeBuilder): + pass + +## +# (experimental) An alternate builder that supports manipulation of +# new elements. + +class FancyTreeBuilder(TreeBuilder): + + def __init__(self, html=0): + TreeBuilder.__init__(self, html) + self._parser.StartNamespaceDeclHandler = self._start_ns + self._parser.EndNamespaceDeclHandler = self._end_ns + self.namespaces = [] + + def _start(self, tag, attrib_in): + elem = TreeBuilder._start(self, tag, attrib_in) + self.start(elem) + + def _start_list(self, tag, attrib_in): + elem = TreeBuilder._start_list(self, tag, attrib_in) + self.start(elem) + + def _end(self, tag): + elem = TreeBuilder._end(self, tag) + self.end(elem) + + def _start_ns(self, prefix, value): + self.namespaces.insert(0, (prefix, value)) + + def _end_ns(self, prefix): + assert self.namespaces.pop(0)[0] == prefix, "implementation confused" + + ## + # Hook method that's called when a new element has been opened. + # May access the namespaces attribute. + # + # @param element The new element. The tag name and attributes are, + # set, but it has no children, and the text and tail attributes + # are still empty. + + def start(self, element): + pass + + ## + # Hook method that's called when a new element has been closed. + # May access the namespaces attribute. + # + # @param element The new element. + + def end(self, element): + pass diff --git a/src/python/uts/util/etree/__init__.py b/src/python/uts/util/etree/__init__.py new file mode 100644 index 00000000..cef1a6bb --- /dev/null +++ b/src/python/uts/util/etree/__init__.py @@ -0,0 +1,30 @@ +# $Id: __init__.py 1821 2004-06-03 16:57:49Z fredrik $ +# elementtree package + +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2004 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- diff --git a/src/python/uts/util/observation.py b/src/python/uts/util/observation.py new file mode 100644 index 00000000..8814b196 --- /dev/null +++ b/src/python/uts/util/observation.py @@ -0,0 +1,47 @@ + +from catalog import Object + +class Observation(object): + + def __init__(self, target = None): + self.obj = Object() + self.nexp = 0 + self.exptime = 0 + self.filter = 0 + self.path = "" + self.filenameIndex = 0 + self.observationTime = None + self.frameType = None + + if(target): + self.fromList(target) + + def fromList(self, target): + self.obj = Object(target[1], target[2], target[3]) + self.nexp = target[4] + self.exptime = target[5] + self.filter = target[6] + self.path = target[7] + self.filenameIndex = 0 + self.observationTime = None + self.frameType = target[0] + + def __repr__(self): + s = "%s %s %s\n#%s exposures of %s seconds each." % (self.obj.name, self.obj.ra, self.obj.dec, self.nexp, self.exptime) + return s + +class ObservationPlan(object): + + def __init__(self): + + self.observations = [] + + def __len__(self): + return len(self.observations) + + def __iter__(self): + return iter(self.observations) + + def addObservation(self, obs): + self.observations.append(obs) + diff --git a/src/python/uts/util/output.py b/src/python/uts/util/output.py new file mode 100644 index 00000000..6508c229 --- /dev/null +++ b/src/python/uts/util/output.py @@ -0,0 +1,196 @@ +# Copyright 1998-2004 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Id: output.py,v 1.1 2006/03/06 18:13:31 henrique Exp $ + + +import os,sys,re + +havecolor=1 +dotitles=1 + +spinpos = 0 +spinner = "/-\\|/-\\|/-\\|/-\\|\\-/|\\-/|\\-/|\\-/|" + +esc_seq = "\x1b[" + +g_attr = {} +g_attr["normal"] = 0 + +g_attr["bold"] = 1 +g_attr["faint"] = 2 +g_attr["standout"] = 3 +g_attr["underline"] = 4 +g_attr["blink"] = 5 +g_attr["overline"] = 6 # Why is overline actually useful? +g_attr["reverse"] = 7 +g_attr["invisible"] = 8 + +g_attr["no-attr"] = 22 +g_attr["no-standout"] = 23 +g_attr["no-underline"] = 24 +g_attr["no-blink"] = 25 +g_attr["no-overline"] = 26 +g_attr["no-reverse"] = 27 +# 28 isn't defined? +# 29 isn't defined? +g_attr["black"] = 30 +g_attr["red"] = 31 +g_attr["green"] = 32 +g_attr["yellow"] = 33 +g_attr["blue"] = 34 +g_attr["magenta"] = 35 +g_attr["cyan"] = 36 +g_attr["white"] = 37 +# 38 isn't defined? +g_attr["default"] = 39 +g_attr["bg_black"] = 40 +g_attr["bg_red"] = 41 +g_attr["bg_green"] = 42 +g_attr["bg_yellow"] = 43 +g_attr["bg_blue"] = 44 +g_attr["bg_magenta"] = 45 +g_attr["bg_cyan"] = 46 +g_attr["bg_white"] = 47 +g_attr["bg_default"] = 49 + + +# make_seq("blue", "black", "normal") +def color(fg, bg="default", attr=["normal"]): + mystr = esc_seq[:] + "%02d" % g_attr[fg] + for x in [bg]+attr: + mystr += ";%02d" % g_attr[x] + return mystr+"m" + + + +codes={} +codes["reset"] = esc_seq + "39;49;00m" + +codes["bold"] = esc_seq + "01m" +codes["faint"] = esc_seq + "02m" +codes["standout"] = esc_seq + "03m" +codes["underline"] = esc_seq + "04m" +codes["blink"] = esc_seq + "05m" +codes["overline"] = esc_seq + "06m" # Who made this up? Seriously. + +codes["teal"] = esc_seq + "36m" +codes["turquoise"] = esc_seq + "36;01m" + +codes["fuchsia"] = esc_seq + "35;01m" +codes["purple"] = esc_seq + "35m" + +codes["blue"] = esc_seq + "34;01m" +codes["darkblue"] = esc_seq + "34m" + +codes["green"] = esc_seq + "32;01m" +codes["darkgreen"] = esc_seq + "32m" + +codes["yellow"] = esc_seq + "33;01m" +codes["brown"] = esc_seq + "33m" + +codes["red"] = esc_seq + "31;01m" +codes["darkred"] = esc_seq + "31m" + +def nc_len(mystr): + tmp = re.sub(esc_seq + "^m]+m", "", mystr); + return len(tmp) + +def xtermTitle(mystr): + if havecolor and dotitles and os.environ.has_key("TERM") and sys.stderr.isatty(): + myt=os.environ["TERM"] + legal_terms = ["xterm","Eterm","aterm","rxvt","screen","kterm","rxvt-unicode"] + for term in legal_terms: + if myt.startswith(term): + sys.stderr.write("\x1b]2;"+str(mystr)+"\x07") + sys.stderr.flush() + break + +def xtermTitleReset(): + if havecolor and dotitles and os.environ.has_key("TERM"): + myt=os.environ["TERM"] + xtermTitle(os.environ["TERM"]) + + +def notitles(): + "turn off title setting" + dotitles=0 + +def nocolor(): + "turn off colorization" + havecolor=0 + for x in codes.keys(): + codes[x]="" + +def resetColor(): + return codes["reset"] + +def ctext(color,text): + return codes[ctext]+text+codes["reset"] + +def bold(text): + return codes["bold"]+text+codes["reset"] +def white(text): + return bold(text) + +def teal(text): + return codes["teal"]+text+codes["reset"] +def turquoise(text): + return codes["turquoise"]+text+codes["reset"] +def darkteal(text): + return turquoise(text) + +def fuscia(text): # Don't use this one. It's spelled wrong! + return codes["fuchsia"]+text+codes["reset"] +def fuchsia(text): + return codes["fuchsia"]+text+codes["reset"] +def purple(text): + return codes["purple"]+text+codes["reset"] + +def blue(text): + return codes["blue"]+text+codes["reset"] +def darkblue(text): + return codes["darkblue"]+text+codes["reset"] + +def green(text): + return codes["green"]+text+codes["reset"] +def darkgreen(text): + return codes["darkgreen"]+text+codes["reset"] + +def yellow(text): + return codes["yellow"]+text+codes["reset"] +def brown(text): + return codes["brown"]+text+codes["reset"] +def darkyellow(text): + return brown(text) + +def red(text): + return codes["red"]+text+codes["reset"] +def darkred(text): + return codes["darkred"]+text+codes["reset"] + + +def update_basic_spinner(): + global spinner, spinpos + spinpos = (spinpos+1) % 500 + if (spinpos % 100) == 0: + if spinpos == 0: + sys.stdout.write(". ") + else: + sys.stdout.write(".") + sys.stdout.flush() + +def update_scroll_spinner(): + global spinner, spinpos + if(spinpos >= len(spinner)): + sys.stdout.write(darkgreen(" \b\b\b"+spinner[len(spinner)-1-(spinpos%len(spinner))])) + else: + sys.stdout.write(green("\b "+spinner[spinpos])) + sys.stdout.flush() + spinpos = (spinpos+1) % (2*len(spinner)) + + +def update_spinner(): + global spinner, spinpos + spinpos = (spinpos+1) % len(spinner) + sys.stdout.write("\b\b "+spinner[spinpos]) + sys.stdout.flush() diff --git a/src/sec/Makefile.am b/src/sec/Makefile.am new file mode 100644 index 00000000..93dab490 --- /dev/null +++ b/src/sec/Makefile.am @@ -0,0 +1,13 @@ +include $(top_srcdir)/rules.make + +noinst_LIBRARIES = libsec.a + +libsec_a_SOURCES = sec.c messages.c skterror.c sktlist.c fields.c \ + sec.h messages.h skterror.h sktlist.h fields.h + +bin_PROGRAMS = sec + +sec_SOURCES = server.c sec_config.c sec_server.c main.c \ + server.h sec_config.h sec_server.h main.h + +sec_LDADD = -lsimpleskts -lsec diff --git a/src/sec/SConscript b/src/sec/SConscript new file mode 100644 index 00000000..9031a372 --- /dev/null +++ b/src/sec/SConscript @@ -0,0 +1,24 @@ +#! /usr/bin/python + +Import("env") + +_sources = Split( + +""" +server.c sec_config.c sec_server.c main.c +""" +) + +_libSources = Split( +""" +sec.c messages.c skterror.c sktlist.c fields.c +""" +) + +env.Program('sec', _sources, + CPPPATH=['.', '#contrib/include'], + LIBPATH=['#contrib/lib', '.'], + LIBS=['simpleskts', 'sec']) + +env.StaticLibrary('sec', _libSources, + CPPPATH=['.', '#contrib/include']) diff --git a/src/sec/fields.c b/src/sec/fields.c new file mode 100644 index 00000000..158f23d0 --- /dev/null +++ b/src/sec/fields.c @@ -0,0 +1,61 @@ +/*************************************************************************** + fields.c - description + ------------------- + begin : Wed Jun 7 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include +#include +#include "fields.h" + +int getfield(char *command, field *f, const char *flist[]) +{ + int i; + char tmp[MAX_FIELD_SIZE]; + char *ptr; + + if (command == NULL || *command == '\0') return(FEMPTY_FIELD); + if (flist == NULL) return (EOF); + + /* find field type */ + ptr = tmp; + while (isspace(*command)) command++; + if (*command == FCOMMENT_CHAR || *command == '\0') + /* Comment or empty line */ + return(FEMPTY_FIELD); + + i = 0; + while (!isspace(*command) && *command != '\0' && i++ < MAX_FIELD_SIZE) + *ptr++ = *command++; + *ptr = '\0'; + + i = -1; + /* Look for the field index, */ + /* exit if reached the end of list */ + do + if (flist[++i] == NULL) return (FNO_MATCH); + while (strncmp(tmp, flist[i], MAX_FIELD_SIZE) != 0); + + /* do not care with field contents */ + if (f == NULL) return (i); + + /* store field content */ + f->index = i; + while (isspace(*command)) command++; + /* while (*ptr++ = *command++); */ + f->contents = command; + + return(i); +} + diff --git a/src/sec/fields.h b/src/sec/fields.h new file mode 100644 index 00000000..dfb13add --- /dev/null +++ b/src/sec/fields.h @@ -0,0 +1,68 @@ +/*************************************************************************** + fields.h - command identification + ------------------- + begin : Wed Jun 7 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef FIELDS_H +#define FIELDS_H + +#define MAX_FIELD_SIZE (50) +#define MAX_FIELD_CONTENTS (255) +#define FCOMMENT_CHAR '#' + + + +#define FNO_MATCH (-10) +#define FEMPTY_FIELD (-11) + +typedef struct { + int index; /* position of field in list */ + char *contents; /* points to field contents in command */ +} field; + + +/* getfield() breaks ``command'' in a field, contained in ``flist'', + and its contents. It ignores whitespace before and after field + (see example). Also, a `#' beginning character could be used as + ``comment character''. + + Field and contents are stored in a ``field'' structure; ``index'' + is the position of the field in ``flist''. The return value is index, + if successfull, FNO_MATCH if the command isn't in ``flist'' and + FEMPTY_FIELD if command is an empty command. + If ``f'' is NULL, getfield just gets the index and returns. + + Example: + + #include "getfield.h" + + (...) + + cmd = " FIELD2 CONTENTS"; + char *fl[] = { "FIELD1", "FIELD2", "FIELD3" }; + field f; + int i; + + (...) + + i = getfield(cmd, &f, fl); // i == 1 + printf("Index: %d\n", f.index); // Index: 1 + printf("Contents: %s\n", f.contents); // Contents: CONTENTS + + */ + +int getfield(char *command, field *f, const char *flist[]); + +#endif /* FIELDS_H */ diff --git a/src/sec/main.c b/src/sec/main.c new file mode 100644 index 00000000..6fb9be19 --- /dev/null +++ b/src/sec/main.c @@ -0,0 +1,281 @@ +/*************************************************************************** + main.c - description + ------------------- + begin : Tue Jul 11 14:18:57 BRT 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "sec_server.h" + +#define FALSE (0) +#define TRUE (1) + +#define PROGRAM_NAME "Socket Secretary" +#define URL "http://www.astro.ufsc.br/~andre/" +#ifndef VERSION +#define VERSION "Beta" +#endif + +#define ROOT_DIR_ENV_VAR "UTS_DIR" +#define DEF_ROOT_DIR "/usr/local/uts" +#define DEF_CONF_FILE_NAME "/etc/sec.conf" + + +/*** Global variables filled by get_args() ***/ +char *exec_name; /* Name of executable file */ +static char *conffname; /* configuration file */ +static char *logfname; /* log file */ +static int detach = FALSE; /* should the program detach from terminal? */ + + +static void print_title(void); +static void print_help(void); +static void print_version(void); +static void print_usage(void); +static void get_args(int argc, char **argv); +static void goto_bg(void); + + +/*-------------------------------------------------------------------------*/ + +int main(int argc, char **argv) +{ + + SecServer *sec; + + /* Parse arguments */ + get_args(argc, argv); + + /* Open log file and redirect stdout, if required */ + if (logfname != NULL) { + fprintf(stderr, " -> Logging to file \"%s\".\n", logfname); + stdout = freopen(logfname, "a", stdout); + if (stdout == NULL) { + fprintf(stderr, "*** Error opening file \"%s\".\n", + logfname); + exit(1); + } + } + + /*** The function exit() is not recommended hereafter. ***/ + /*** Use abort() instead. ***/ + + sec = sec_server_new(); + if(sec == NULL) abort(); + + /* Set signal handlers and get configuration */ + fprintf(stderr, " -> Getting configuration from file \"%s\".\n", conffname); + + if(sec_server_configure(sec, conffname) == FALSE) + abort(); + + /* Open the servers */ + fprintf(stderr, " -> Opening servers for instrument %s.\n", sec->config->instname); + + sec_server_run(sec); + + fprintf(stderr, " -> OK, working.\n"); + + if (detach) { + /* Detach from terminal (daemon mode) */ + fprintf(stderr, " -> Jumping into background...\n\n"); + goto_bg(); + /* close stdout if no logfile specified */ + if (logfname == NULL) fclose(stdout); + } + + + /*** Main loop ***/ + while (sec_server_wait(sec) >= 0) { + /* Do any periodic stuff here, but don't overload. */ + /* -> check inactive sockets every */ + sec_server_check(sec); + } + + /* waitsockets failed! */ + printf("*** Program crash.\n"); + abort(); +} + +/*-------------------------------------------------------------------------*/ + +static void print_title(void) +{ + printf("%s %s\n", PROGRAM_NAME, VERSION); +} + +/*-------------------------------------------------------------------------*/ + +static void print_help(void) +{ + print_title(); + printf("This program requires Socket PortMaster (Spm) running.\n"); +#ifdef NOTIFY_ON_CHANGE + printf("This version notifies only when status changes.\n\n\n"); +#else + printf("This version notifies always.\n\n\n"); +#endif + print_usage(); + printf("\nIf no config file is specified, this program will search " + "under the environment\n" + "variable %s:\n\n" + " ${%s}%s\n\n", ROOT_DIR_ENV_VAR, ROOT_DIR_ENV_VAR, + DEF_CONF_FILE_NAME); + printf("If this file does not exists, this program will try " + "the following file:\n\n" + " %s%s\n\n\n", DEF_ROOT_DIR, DEF_CONF_FILE_NAME); + printf("Command line options:\n\n" + " -h / --help\n" + " This help screen.\n\n" + + " -v / --version\n" + " Display version and copyright " + " information.\n\n" + + " -d / --detach\n" + " Daemon mode.\n" + " Put the program in background.\n\n" + + " -o / --output \n" + " Redirect output to ,\n" + " instead of showing it on the terminal.\n\n\n" + ); + + printf("Visit the home page\n" + "%s\n" + "for more information.\n\n", URL); + exit(0); +} + +/*-------------------------------------------------------------------------*/ + +static void print_version(void) +{ + print_title(); + printf("Copyright (C) 2001 Andre Luiz de Amorim\n"); + printf("%s comes with NO WARRANTY,\n" + "to the extent permitted by law.\n", PROGRAM_NAME); + printf("You may redistribute copies of %s\n" + "under the terms of the GNU General Public License.\n" + "For more information about these matters,\n" + "see the files named COPYING.\n", PROGRAM_NAME); + + exit(0); +} + +/*-------------------------------------------------------------------------*/ + +static void print_usage(void) +{ + printf("SYNTAX:\n" + " %s [-o ] [-d] [config_file]\n\n" + " %s {-h|--help}\n\n", exec_name, exec_name); +} + +/*-------------------------------------------------------------------------*/ + +static void get_args(int argc, char **argv) +{ + int next_opt; + const char *short_options = "hvdo:"; + const struct option long_options [] = { + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'v' }, + { "detach", 0, NULL, 'd' }, + { "output", 1, NULL, 'o' }, + { NULL, 0, NULL, 0 } + }; + + exec_name = argv[0]; + conffname = NULL; + logfname = NULL; + do { + next_opt = getopt_long(argc, argv, short_options, + long_options, NULL); + switch (next_opt) { + case 'h': + print_help(); + break; + + case 'v': + print_version(); + break; + + case 'd': + detach = TRUE; + break; + + case 'o': + /* log file name */ + logfname = optarg; + break; + + case '?': + /* Invalid option */ + print_usage(); + exit(1); + break; + + case -1: + /* end of options */ + break; + + default: + abort(); + break; + } + } while (next_opt != -1); + + if (optind < argc) + /* config file is the first non-option argument */ + conffname = argv[optind]; + + /* If config file is not specified, try to get it from environment */ + if (conffname == NULL) { + int n; + char *root_dir = getenv(ROOT_DIR_ENV_VAR); + if (root_dir == NULL) root_dir = DEF_ROOT_DIR; + + n = strlen(root_dir) + strlen(DEF_CONF_FILE_NAME) + 1; + conffname = calloc(n, sizeof(char)); + strncpy(conffname, root_dir, n); + strncat(conffname, DEF_CONF_FILE_NAME, n); + } +} + +/*-------------------------------------------------------------------------*/ + +static void goto_bg(void) +{ + int daemon = 0; + + daemon = fork(); + if (daemon < 0) fprintf(stderr, + "*** Error: Could not detach from terminal\n"); + else if (daemon) exit(0); /* We're the parent, so bye-bye! */ + + setsid(); /* release controlling terminal */ + +} + +/*-------------------------------------------------------------------------*/ diff --git a/src/sec/messages.c b/src/sec/messages.c new file mode 100644 index 00000000..13328bdd --- /dev/null +++ b/src/sec/messages.c @@ -0,0 +1,40 @@ +/*************************************************************************** + messages.c - description + ------------------- + begin : Thu Feb 1 2001 + copyright : (C) 2001 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include + +/* communication commands */ +const char *fcomm[] = { + "QUIT", "IDENT", "STATUS", "SETSTATUS", "NOTIFY", "UNOTIFY", + "CONTROL", "ANSWER", "RESET", "SHUTDOWN", NULL +}; /* The null pointer terminator prevents getfield() + to raise a `Segmentation fault' */ + +/* Responses */ +const char *fresponses[] = { + "HELLO", "OK", "ERROR", "ABORT","SERVER_FULL", + "STATUS", "NOTIFY", "RESET", NULL +}; + +/* instrument states */ +const char *finststatus[] = { + "OFFLINE", "BUSY", "DISABLED", "IDLE", NULL +}; + +/* TRUE & FALSE */ +#define S_TRUE "TRUE" +#define S_FALSE "FALSE" diff --git a/src/sec/messages.h b/src/sec/messages.h new file mode 100644 index 00000000..5a56ec41 --- /dev/null +++ b/src/sec/messages.h @@ -0,0 +1,40 @@ +/*************************************************************************** + messages.h - description + ------------------- + begin : Thu Feb 1 2001 + copyright : (C) 2001 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef MESSAGES_H +#define MESSAGES_H + +/* TRUE & FALSE */ +#define S_TRUE "TRUE" +#define S_FALSE "FALSE" + + +/* communication commands */ +extern const char *fcomm[]; +enum { C_QUIT, C_IDENT, C_STATUS, C_SETSTATUS, C_NOTIFY, C_UNOTIFY, + C_CONTROL, C_ANSWER, C_RESET, C_SHUTDOWN }; + +/* Responses */ +extern const char *fresponses[]; +enum { RES_HELLO, RES_OK, RES_ERROR, RES_ABORT, RES_SERVER_FULL, RES_STATUS, + RES_NOTIFY, RES_RESET }; + +/* instrument states */ +extern const char *finststatus[]; +enum { IST_OFFLINE, IST_BUSY, IST_DISABLED, IST_IDLE }; + +#endif /* MESSAGES_H */ diff --git a/src/sec/sdict-test.c b/src/sec/sdict-test.c new file mode 100644 index 00000000..da47cdef --- /dev/null +++ b/src/sec/sdict-test.c @@ -0,0 +1,56 @@ +#include "sdict.h" + +#include +#include +#include + +int main(void) { + + SDict* dict = s_dict_new(); + assert(dict != NULL); + + int i = s_dict_count(dict); + assert(i == 0); + + s_dict_add(dict, "NOME", "VALOR"); + + int j = s_dict_count(dict); + assert(j == 1); + + char* s = (char*) s_dict_get(dict, "NOME");; + assert(strcmp("VALOR", s) == 0);; + + char* keys[4] = {"NOME1", "NOME2", "NOME3", "NOME4"}; + char* values[4] = {"VALOR1", "VALOR2", "VALOR3", "VALOR4"}; + + s_dict_add_array(dict, keys, values, 4); + + char* s1 = (char*) s_dict_get(dict, "NOME1");; + assert(strcmp("VALOR1", s1) == 0);; + + char* s2 = (char*) s_dict_get(dict, "NOME2");; + assert(strcmp("VALOR2", s2) == 0);; + + char* s3 = (char*) s_dict_get(dict, "NOME3");; + assert(strcmp("VALOR3", s3) == 0);; + + char* s4 = (char*) s_dict_get(dict, "NOME4");; + assert(strcmp("VALOR4", s4) == 0);; + + s_dict_set(dict, "NOME4", "NOVO"); + + char* s5 = (char*) s_dict_get(dict, "NOME4");; + assert(strcmp("NOVO", s5) == 0);; + + assert(s_dict_has(dict, "NOME4")); + + s_dict_remove(dict, "NOME4"); + + assert(! s_dict_has(dict, "NOME4")); + + s_dict_free(dict); + assert(dict != NULL); + + return 0; + +} diff --git a/src/sec/sdict.c b/src/sec/sdict.c new file mode 100644 index 00000000..8fe46c31 --- /dev/null +++ b/src/sec/sdict.c @@ -0,0 +1,167 @@ +#include "sdict.h" + +#include +#include +#include + +static dnode_t* s_dict_get_node(SDict* dict, const void* key); + +SDict* s_dict_new(void) { + + SDict* dict = (SDict*)calloc(1, sizeof(SDict)); + + assert(dict != NULL); + + dict->priv = dict_create(DICTCOUNT_T_MAX, strcmp); + + return dict; + +} + +void s_dict_free(SDict* dict) { + + assert(dict != NULL); + + dict_free(dict->priv); + dict_destroy(dict->priv); + + free(dict); + +} + +int s_dict_add(SDict* dict, const void* key, void* value) { + + assert(dict != NULL); + assert(key != NULL); + assert(value != NULL); + + dnode_t* node = dnode_create(value); + + if(s_dict_has(dict, key)) + return FALSE; + + dict_insert(dict->priv, node, key); + + return TRUE; + +} + +int s_dict_add_array(SDict* dict, const void* keys[], void* values[], int n) { + + assert(dict != NULL); + assert(keys != NULL); + assert(values != NULL); + assert(n > 0); + + int i; + + for(i = 0; i < n; i++) + s_dict_add(dict, keys[i], values[i]); + + return TRUE; + +} + +int s_dict_remove(SDict* dict, const void* key) { + + assert(dict != NULL); + assert(key != NULL); + + dnode_t* node = s_dict_get_node(dict, key); + + if(node != NULL) { + + dict_delete(dict->priv, node); + + dnode_destroy(node); + + return TRUE; + + } else { + + return FALSE; + + } + +} + +void* s_dict_get(SDict* dict, const void* key) { + + assert(dict != NULL); + assert(key != NULL); + + dnode_t* node = s_dict_get_node(dict, key); + + if(node != NULL) { + return dnode_get(node); + }else { + return NULL; + + } + +} + +static dnode_t* s_dict_get_node(SDict* dict, const void* key) { + + assert(dict != NULL); + assert(key != NULL); + + dnode_t* found = dict_lookup(dict->priv, key); + + return found; + +} + +int s_dict_set(SDict* dict, const void* key, void* value) { + + assert(dict != NULL); + assert(key != NULL); + assert(value != NULL); + + dnode_t* node = s_dict_get_node(dict, key); + + if(node != NULL) { + + dnode_put(node, value); + return TRUE; + + } else { + + return FALSE; + + } + +} + +int s_dict_has(SDict* dict, const void* key) { + + assert(dict != NULL); + assert(key != NULL); + + dnode_t* node = s_dict_get_node(dict, key); + + if(node != NULL) { + + return TRUE; + + } else { + + return FALSE; + + } + +} + +int s_dict_foreach(SDict* dict, void* data, s_dict_clbk clbk) { + + return TRUE; + +} + +int s_dict_count(SDict* dict) { + + assert(dict != NULL); + + return (int)dict_count(dict->priv); + +} diff --git a/src/sec/sdict.h b/src/sec/sdict.h new file mode 100644 index 00000000..5f94ca24 --- /dev/null +++ b/src/sec/sdict.h @@ -0,0 +1,45 @@ +#ifndef _S_DICT_H_ +#define _S_DICT_H_ 1 + +#include + +#include "dict.h" + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +typedef struct _SDict SDict; + +struct _SDict { + + dict_t* priv; + +}; + +typedef void (*s_dict_clbk)(SDict* dict, void* key, void* value, void* data); + +SDict* s_dict_new(void); +void s_dict_free(SDict* dict); + +int s_dict_add(SDict* dict, const void* key, void* value); +int s_dict_add_array(SDict* dict, const void* keys[], void* values[], int n); + +int s_dict_remove(SDict* dict, const void* key); + +void* s_dict_get(SDict* dict, const void* key); + +int s_dict_set(SDict* dict, const void* key, void* value); + +int s_dict_has(SDict* dict, const void* key); + +int s_dict_foreach(SDict* dict, void* data, s_dict_clbk clbk); + +int s_dict_count(SDict* dict); + + +#endif /* !_S_DICT_H */ diff --git a/src/sec/sec.c b/src/sec/sec.c new file mode 100644 index 00000000..a54e5e6f --- /dev/null +++ b/src/sec/sec.c @@ -0,0 +1,468 @@ +/*************************************************************************** + sec.c - secretary control + ------------------- + begin : Mon May 7 2001 + copyright : (C) 2001 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include "sockets.h" +#include "skterror.h" +#include "fields.h" +#include "messages.h" + +#include "sec.h" + +#define TRUE (1) +#define FALSE (0) + + +/* Read socket `skt' and put data in `buf'; wait for `timeout' seconds. + Return value: 0 if did not read anything, EOF at error, and some positive + number if there is data available. */ +static int sktread(Socket *skt, char *buf, int timeout); + +/* Read Scretary's socket to `buf'; block for SEC_READ_BLKTIMEOUT seconds. + Return value: If successfull, returns the index of the command sent by + Secretary. Returns 0 if did not read anything, EOF at error, FNO_MATCH if + the secretary sent an invalid command, FEMPTY_FIELD if th secretary sent + an empty field (see fields.h fot more details in these two). */ +static int secreadb(Socket *sec, char *buf); + +/* The same as above, but don't block. */ +static int secread(Socket *sec, char *buf); + +/* Write buf to secretary's socket. */ +static int secwrite(Socket *sec, const char *buf); + +/* Removes all status fields entries */ +static void clearstatfields(sec_t *sec); + +/* Open connetion to secretary. Mode = "r" opens sec's status port, + "w" opens control port */ +sec_t *secconnect(char *sname, char *mode, char *ident); + +/* Synchronous status reading */ +char *getstatus(sec_t *sec, int stat); + +/* Asynchronous status reading */ +int notify(sec_t *sec, int stat, notify_handler hndl); + + +/* ----------------------------------------------------------------------- */ + +static int sktread(Socket *skt, char *buf, int timeout) +{ + int res; + + if (skt == NULL || buf == NULL || timeout < 0) return (EOF); + + res = Stimeoutwait(skt, timeout, 0); + if (res == -2) return (0); /* No data */ + else if (res == -1) return (EOF); /* error */ + + /* We have data */ + if (Sgets(buf, MAXBUFLEN, skt) == NULL) return (EOF); + + return (res); +} + +/* ----------------------------------------------------------------------- */ + +static int secreadb(Socket *sec, char *buf) +{ + char tmpbuf[MAXBUFLEN]; + field f; + int res; + + if (sec == NULL || buf == NULL) return (EOF); + + res = sktread(sec, tmpbuf, SEC_READ_BLKTIMEOUT); + if (res <= 0) return (res); + + /* Identify the response */ + res = getfield(tmpbuf, &f, fresponses); + if (res < 0) return (res); + strncpy(buf, f.contents, MAXBUFLEN); + return (res); +} + +/* ----------------------------------------------------------------------- */ + +static int secread(Socket *sec, char *buf) +{ + char tmpbuf[MAXBUFLEN]; + field f; + int res; + + if (sec == NULL || buf == NULL) return (EOF); + + res = sktread(sec, tmpbuf, SEC_READ_TIMEOUT); + if (res <= 0) return (res); + + /* Identify the response */ + res = getfield(tmpbuf, &f, fresponses); + if (res < 0) return (res); + strncpy(buf, f.contents, MAXBUFLEN); + return (res); +} + +/* ----------------------------------------------------------------------- */ + +static int secwrite(Socket *sec, const char *buf) +{ + if (sec == NULL) return (SEC_ERRSOCKET); + if (buf == NULL) return (EOF); + + /* write and verify error */ + watch(sec); + Sputs(buf, sec); + if (skterror()) { /* pipe error */ + Sclose(sec); + return (SEC_ERRSOCKET); + } + return (SEC_ERROK); +} + +/* ----------------------------------------------------------------------- */ + +static void clearstatfields(sec_t *sec) +{ + int i = 0; + + if (sec == NULL || sec->status == NULL) return; + for (i = 0; i < sec->status->nstat; i++) { + unotify(sec, i); + free(sec->status->fields[i]); + free(sec->status->contents[i]); + } + free(sec->status); +} + +/* ----------------------------------------------------------------------- */ + +sec_t *secconnect(char *sname, char *mode, char *ident) +{ + char buf[MAXBUFLEN]; + sec_t *sec; + int res; + + if (sname == NULL || mode == NULL) return (NULL); + if (*mode != SEC_CLIENT_CHAR && *mode != SEC_INST_CHAR) + return (NULL); + + sec = (sec_t *) calloc(1, sizeof(sec_t)); + if (sec == NULL) return (NULL); + sec->status = (status_t *) calloc(1, sizeof(status_t)); + if (sec->status == NULL) { + free(sec); + return (NULL); + } + + + sec->skt = Sopen(sname, "c"); + if (sec->skt == NULL) { + secclose(sec); + return (NULL); + } + + /* Get initial "HELLO" */ + res = secreadb(sec->skt, buf); + if (res < 0) { + fprintf(stderr, "Read timeout or socket error\n\n"); + secclose(sec); + return (NULL); + } + if (res != RES_HELLO) { + fprintf(stderr, "Expected \"%s\", but got \"%s\"\n", + fresponses[RES_HELLO], fresponses[res]); + secclose(sec); + return (NULL); + } + + /* Send identification string */ + if (*mode == SEC_CLIENT_CHAR) { /* client connection */ + snprintf(buf, MAXBUFLEN, "%s %s", fcomm[C_IDENT], ident); + secwrite(sec->skt, buf); + } + else if (*mode == SEC_INST_CHAR) { /* instrument connection */ + snprintf(buf, MAXBUFLEN, "%s %s", + fcomm[C_IDENT], SEC_INSTCLNT_IDENT); + secwrite(sec->skt, buf); + } + + res = secreadb(sec->skt, buf); + if (res < 0) { + fprintf(stderr, "Read timeout or socket error\n\n"); + secclose(sec); + return (NULL); + } + if (res != RES_OK) { + fprintf(stderr, "Unexpected response on IDENT: \"%s\"\n", + fresponses[res]); + secclose(sec); + return (NULL); + } + + sec->enable = TRUE; + Smaskset(sec->skt); + return (sec); +} + +/* ----------------------------------------------------------------------- */ + +void secclose(sec_t *sec) +{ + if (sec == NULL) return; + + clearstatfields(sec); + if (sec->skt != NULL) { + secwrite(sec->skt, fcomm[C_QUIT]); + Smaskunset(sec->skt); + Sclose(sec->skt); + } + + free(sec); + return; +} + +/* ----------------------------------------------------------------------- */ + +int addstatus(sec_t *sec, const char *statname) +{ + char *stf, *stc; + status_t *stp; + int sz; + + if (sec == NULL || statname == NULL) return (EOF); + stp = sec->status; + sz = strlen(statname); + if (sz > MAX_FIELD_SIZE) sz = MAX_FIELD_SIZE; + stc = (char *) calloc(MAX_FIELD_CONTENTS + 1, sizeof(char)); + if (stc == NULL) return (EOF); + stf = (char *) calloc(sz + 1, sizeof(char)); + if (stf == NULL) { + free(stc); + return (EOF); + } + strncpy(stf, statname, sz); + + stp->fields[stp->nstat] = stf; + stp->contents[stp->nstat] = stc; + return (stp->nstat++); +} + +/* ----------------------------------------------------------------------- */ + +int setnstatus(sec_t *sec, int stat, float content) +{ + char buf[MAXBUFLEN]; + int res; + + res = snprintf(buf, MAXBUFLEN, "%-5.3f", content); + if (res == 0) return (EOF); + + res = setstatus(sec, stat, buf); + return (res); +} + +/* ----------------------------------------------------------------------- */ + +int setstatus(sec_t *sec, int stat, const char *content) +{ + int res; + char buf[MAXBUFLEN]; + + if (sec == NULL || content == NULL) return (EOF); + if (stat < 0 || stat > sec->status->nstat) return (EOF); + + sprintf(buf, "%s %s %s", + fcomm[C_SETSTATUS], sec->status->fields[stat], content); + // FIXME + // fcomm[C_SETSTATUS], fsyncstat[stat], content); + res = secwrite(sec->skt, buf); + + //printf("\n%s\n", buf); + if (res < 0) return (EOF); + + res = secreadb(sec->skt, buf); + if (res < 0) { + fprintf(stderr, "Read timeout or socket error\n\n"); + Sclose(sec); + return (EOF); + } + if (res != RES_OK) { + fprintf(stderr, "Error setting status %d = %s\n", + stat, content); + } + return (res); +} + +/* ----------------------------------------------------------------------- */ + +char *getstatus(sec_t *sec, int stat) +{ + int res; + char buf[MAXBUFLEN]; + + if (sec == NULL || sec->status == NULL || + stat < 0 || stat > sec->status->nstat) + return (NULL); + + // FIXME (por que nao usar?) +/* /\* If notification handling is enabled, use saved value *\/ */ +/* if (sec->status->notify[stat]) */ +/* return (sec->status->contents[stat]); */ + + // ALWAYS ASK + /* else, ask secretary */ + + sprintf(buf, "%s %s", fcomm[C_STATUS], (sec->status->fields[stat])); + + res = secwrite(sec->skt, buf); + if (res != SEC_ERROK) return (NULL); + + res = secreadb(sec->skt, sec->status->contents[stat]); + if (res != RES_STATUS) return (NULL); + + printf("%s -> %s\n", buf, sec->status->contents[stat]); + + return (sec->status->contents[stat]); +} + +/* ----------------------------------------------------------------------- */ + +float getnstatus(sec_t *sec, int stat) +{ + char *buf; + + buf = getstatus(sec, stat); + if (buf == NULL) return (HUGE_VAL); + + return(atof(buf)); +} + +/* ----------------------------------------------------------------------- */ + +int notify(sec_t *sec, int stat, notify_handler hndl) +{ + int res; + char buf[MAXBUFLEN]; + + if (sec == NULL || sec->status == NULL || + stat < 0 || stat > sec->status->nstat) + return (EOF); + + /* Exit if notification handling is already enabled */ + if (sec->status->notify[stat]) { + sec->status->handler[stat] = hndl; + return (SEC_ERROK); + } + + /* else, ask secretary */ + sprintf(buf, "%s %s", fcomm[C_NOTIFY], sec->status->fields[stat]); + res = secwrite(sec->skt, buf); + if (res != SEC_ERROK) return (EOF); + + res = secreadb(sec->skt, buf); + if (res == RES_OK) { + sec->status->notify[stat] = TRUE; + sec->status->handler[stat] = hndl; + } + + return (res); +} + +/* ----------------------------------------------------------------------- */ + +int unotify(sec_t *sec, int stat) +{ + int res; + char buf[MAXBUFLEN]; + + if (sec == NULL || sec->status == NULL || + stat < 0 || stat > sec->status->nstat) + return (EOF); + + /* Exit if notification handling is already disabled */ + if (!sec->status->notify[stat]) + return (SEC_ERROK); + + /* else, ask secretary */ + sprintf(buf, "%s %s", fcomm[C_UNOTIFY], sec->status->fields[stat]); + res = secwrite(sec->skt, buf); + if (res != SEC_ERROK) return (EOF); + + res = secreadb(sec->skt, buf); + if (res == RES_OK) sec->status->notify[stat] = FALSE; + return (res); +} + +/* ----------------------------------------------------------------------- */ + +int wait_notify(int time) +{ + Smasktime(time, 0); + return Smaskwait(); +} + +/* ----------------------------------------------------------------------- */ + +int process_notifies(sec_t *sec) +{ + int res; + field f; + status_t *st; + char buf[MAXBUFLEN]; + + if (sec == NULL || sec->status == NULL) return(EOF); + st = sec->status; + res = Stest(sec->skt); + + if (res <= 0) return (res); + + /* Data received */ + res = secread(sec->skt, buf); + if (res < 0) return (EOF); + switch (res) { + case RES_STATUS: + case RES_NOTIFY: + res = getfield(buf, &f, (const char **)st->fields); + if (res < 0) return (EOF); + strncpy(st->contents[res], f.contents, MAX_FIELD_CONTENTS); + if (!st->notify[res]) return (SEC_ERROK); + /* Call status notification handler */ + (*st->handler[res])(res, st->contents[res]); + break; + + case RES_ERROR: + case RES_OK: + return (res); + break; + + case RES_ABORT: + secclose(sec); + return (res); + break; + + default: + break; + } + + return (SEC_ERROK); +} + +/* ----------------------------------------------------------------------- */ diff --git a/src/sec/sec.h b/src/sec/sec.h new file mode 100644 index 00000000..902f24e8 --- /dev/null +++ b/src/sec/sec.h @@ -0,0 +1,120 @@ +/*************************************************************************** + sec.h - secretary control + ------------------- + begin : Mon May 7 2001 + copyright : (C) 2001 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef SEC_H +#define SEC_H + + +#include "sockets.h" +// #include "server.h" +#include "fields.h" +#include "sec_config.h" + +#define MAXBUFLEN (128) + +#define SEC_READ_TIMEOUT (0) /* seconds */ + #define SEC_READ_BLKTIMEOUT (10) /* seconds */ + +#define SEC_CTRLSERVER_STR ("control") /* String to append InstName + to form control servername */ +#define SEC_STATSERVER_STR ("status") /* String to append InstName + to form status servername */ +#define SEC_INSTCLNT_IDENT ("INSTRUMENT") /* Identification String */ +#define SEC_CLIENT_CHAR ('r') +#define SEC_INST_CHAR ('w') +#define SEC_DEF_FVALUE ("EMPTY") /* Default field value */ + +/* Error codes */ +#define SEC_ERROK (0) +#define SEC_ERRSOCKET (-10) +#define SEC_ERRMODE (-11) + + +/* --Type definitions----------------------------------------------------- */ + +/* Event handler */ +typedef int (*notify_handler)(const int, const char *); + +/* Status fields data structure */ +typedef struct { + int nstat; + char *fields[MAX_STATUS_FIELDS]; + char *contents[MAX_STATUS_FIELDS]; + /* Notification event handlers */ + notify_handler handler[MAX_STATUS_FIELDS]; + int notify[MAX_STATUS_FIELDS]; /* TRUE = notification enabled */ + int notified[MAX_STATUS_FIELDS]; +} status_t; + +/* Secretary main structure */ +typedef struct { + int enable; + Socket *skt; + status_t *status; + +// /* merged from comm.h */ +// char *instname; +// Server *statsrv; +// Server *instsrv; + +} sec_t; + + +/* --Function Prototypes-------------------------------------------------- */ + +/* secconnect() - connect to a secretary. + With `mode' == "r" (read), secconnect() connects to the Status port - + i.e "SERVERNAMEstatus" port, where "SERVERNAME" is specified by `sname'. + In this case, it is also necessary to specify `ident'. With + `mode' == "w" (write), secconcets() connects to Control port. In this case + it is possible to use the functions: + setstatus() + setnstatus() */ +sec_t *secconnect(char *sname, char *mode, char *ident); + +/* Closes an open secretary */ +void secclose(sec_t *sec); + +/* Add a staus field with name `statname' to sec. Returns status index. */ +int addstatus(sec_t *sec, const char *statname); + +/* Fill status field indexed by stat with contents. */ +int setstatus(sec_t *sec, int stat, const char *content); + +/* The same as above, but use a numeric content. */ +int setnstatus(sec_t *sec, int stat, float content); + +/* Gets the value of status field indexed by stat */ +char *getstatus(sec_t *sec, int stat); +float getnstatus(sec_t *sec, int stat); +char *status(sec_t *sec, int stat); +float nstatus(sec_t *sec, int stat); + +/* Request notification of field stat to secretary, execute + the handler function hndl() */ +int notify(sec_t *sec, int stat, notify_handler hndl); + +/* Removes notification request */ +int unotify(sec_t *sec, int stat); + +/* Wait notification fot `time' seconds */ +int wait_notify(int time); + +/* Execute pending notifications */ +int process_notifies(sec_t *sec); + +#endif /* SEC_H */ diff --git a/src/sec/sec_config.c b/src/sec/sec_config.c new file mode 100644 index 00000000..27e0cad7 --- /dev/null +++ b/src/sec/sec_config.c @@ -0,0 +1,123 @@ +/*************************************************************************** + getconf.c - description + ------------------- + begin : Thu Sep 28 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include + +#include "fields.h" + +#include "sec_config.h" + +/* used to scan config file */ +static const char *foptions[] = { + "NSTATCLIENTS", "INSTNAME", "STATUS", NULL +}; +enum { NSTATCLIENTS, INSTNAME, STATUS }; + +SecConfig *sec_config_new(void) { + + SecConfig* config = (SecConfig *)calloc(1, sizeof(SecConfig)); + if (config == NULL) return (NULL); + + return config; + +} + +void sec_config_free(SecConfig* config) { + + free(config); + +} + +SecConfig *sec_config_new_from_file(char *conffile) +{ + SecConfig *opts; + FILE *cfile; + char buf[MAXBUFLEN]; + char *ptr; + field f; + int i, tmp; + int line = 0; + + if (conffile == NULL) return (NULL); + + opts = sec_config_new(); + + if(opts == NULL) return NULL; + + /* Number of status fields */ + opts->nstatus = 0; + opts->maxclnt = -1; + + cfile = fopen(conffile, "r"); + if (cfile == NULL) { + free(opts); + return (NULL); + } + + while (fgets(buf, MAXBUFLEN, cfile) != NULL) { + line++; + i = getfield(buf, &f, foptions); + switch (i) { + case NSTATCLIENTS: + /* Get max. clients */ + if (sscanf(f.contents,"%d",&tmp) == EOF) + tmp = DEF_MAXCLNT; + opts->maxclnt = tmp; + break; + + case INSTNAME: + ptr = opts->instname; + /* Get instrument name */ + while (!isspace(*f.contents) && *f.contents != '\0') + *ptr++ = *f.contents++; + break; + + case STATUS: + ptr = opts->fstatus[opts->nstatus]; + tmp = 0; + /* Store in status field table */ + while (!isspace(*f.contents) && *f.contents != '\0') { + *ptr++ = *f.contents++; + if (++tmp == MAX_FIELD_SIZE) break; + } + ptr = opts->fstatus[opts->nstatus]; + /* Avoid having an empty fieldname */ + if (*ptr != '\0') opts->nstatus++; + break; + + case FEMPTY_FIELD: + /* Comment or empty line */ + break; + + case FNO_MATCH: + case EOF: + default: + fprintf(stderr, + "*** Config file: Error in line %d.\n", line); + abort(); + break; + } + } + if (*opts->instname == '\0') strcpy(opts->instname, DEF_INSTNAME); + if (opts->maxclnt == -1) opts->maxclnt = DEF_MAXCLNT; + + fclose(cfile); + return (opts); +} diff --git a/src/sec/sec_config.h b/src/sec/sec_config.h new file mode 100644 index 00000000..187f2fcf --- /dev/null +++ b/src/sec/sec_config.h @@ -0,0 +1,46 @@ +/*************************************************************************** + getconf.h - description + ------------------- + begin : Fri Sep 29 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef _SEC_CONFIG_H_ +#define _SEC_CONFIG_H_ 1 + +#include "fields.h" + +/*** Default values ***/ +#define DEF_INSTNAME "SEC" +#define DEF_MAXCLNT (10) + +#define MAXBUFLEN (128) +#define MAX_SERVERNAME (32) + +#define MAX_STATUS_FIELDS (200) +#define MAX_STATUS_CONTENTS (256) + + +typedef struct _SecConfig SecConfig; + +struct _SecConfig { + char instname[MAX_SERVERNAME]; + int maxclnt; /* maximum number of clients for status server */ + char fstatus[MAX_STATUS_FIELDS][MAX_FIELD_SIZE]; + int nstatus; +}; + +SecConfig *sec_config_new(void); +void sec_config_free(SecConfig* config); +SecConfig *sec_config_new_from_file(char *conffile); + +#endif /* !_SEC_CONFIG_H */ diff --git a/src/sec/sec_server.c b/src/sec/sec_server.c new file mode 100644 index 00000000..694df955 --- /dev/null +++ b/src/sec/sec_server.c @@ -0,0 +1,918 @@ +/*************************************************************************** + comm.c - description + ------------------- + begin : Thu Sep 28 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include +#include + +#include "sockets.h" + +#include "skterror.h" +#include "sktlist.h" +#include "server.h" +#include "fields.h" +#include "messages.h" + +#include "sec_config.h" +#include "sec_server.h" + +#define FALSE (0) +#define TRUE (1) + +#define CONTROL_STR "control" +#define STATUS_STR "status" + +#define INST_CLIENT_NAME "INSTRUMENT" + +#define DEF_STATUS_CONTENT "EMPTY" +#define DEF_INSTSTATUS_CONTENT "OFFLINE" + +#define SKT_CTRLSERVER (SKT_SERVER +1) +#define SKT_STATSERVER (SKT_SERVER +2) + +typedef struct { + char contents[MAX_STATUS_CONTENTS]; + SktList *notify_table; +}statcontents_t; + +static char *fstatus[MAX_STATUS_FIELDS]; +static statcontents_t *status_contents[MAX_STATUS_FIELDS]; + +static int statcounter = 0; + + +static void notify_internal(int i); +static void free_notify_table(SktList *list); + + +static void controlc(int sig); +void ping_sockets(SecServer *sec); + +static Server *run_status_server(SecServer* ); +static Server *run_control_server(SecServer* ); + +/* Servers, for emergency use (aborting) */ +static SecServer *sec_ptr; + +/* status server connection handlers */ +static int accept_client(SktListItem *client); +static int statsrv_answer(SktListItem *client); +static int close_client_handler(SktListItem *client); + +/* control server connection handlers */ +static int instsrv_answer(SktListItem *client); +static int close_instclient_handler(SktListItem *client); + +/* protocol handler */ +static int request_status(SktListItem *client, char *name); +static int change_status(SktListItem *client, char *statstr); +static int client_notify(SktListItem *client, char *statname); +static int client_unotify(SktListItem *client, char *statname); +static int client_ident(SktListItem *client, char *name); +static int check_ident(SktListItem *client); + + +SecServer *sec_server_new(void) { + + SecServer* sec = (SecServer *)calloc(1, sizeof(SecServer)); + assert(sec != NULL); + +/* sec->status = s_dict_new(); */ +/* sec->notify = s_dict_new(); */ + + return sec; + +} + +void sec_server_free(SecServer* sec) { + + if(sec->config != NULL) + sec_config_free(sec->config); + + if(sec->instname != NULL) + free(sec->instname); + +/* if(sec->status != NULL) */ +/* s_dict_free(sec->status) */ + +/* if(sec->notify != NULL) */ +/* s_dict_free(sec->notify) */ + + free(sec); + +} + +int sec_server_configure(SecServer* sec, const char *cfile) +{ + + /* signal handlers */ + signal(SIGINT, controlc); + signal(SIGTERM, controlc); + signal(SIGABRT, controlc); + signal(SIGPIPE, pipe_error); + + /* ignored signals */ + signal(SIGHUP, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGUSR1, SIG_IGN); + signal(SIGUSR2, SIG_IGN); + + /*** Get custom configuration (server names and status fields) ***/ + + sec->config = sec_config_new_from_file(cfile); + + if (sec->config == NULL) { + /* Use default values */ + printf("*** Config file does not exist, using defaults.\n"); + sec->config = sec_config_new(); + strcpy(sec->config->instname, DEF_INSTNAME); + sec->config->maxclnt = DEF_MAXCLNT; + } + + return TRUE; +} + +int sec_server_run(SecServer* sec) +{ + + if(sec->config == NULL) return FALSE; + + /* Save instname */ + sec->instname = (char *) calloc(strlen(sec->config->instname) + 1, + sizeof(char)); + strcpy(sec->instname, sec->config->instname); + + /*** Status server ***/ + sec->statsrv = run_status_server(sec); + if (sec->statsrv == NULL) abort(); + + /* Configure Status Fields */ + fill_statlist(sec->config->instname, sec->config->fstatus, + sec->config->nstatus); + + /*** Instrument control server ***/ + sec->instsrv = run_control_server(sec); + if (sec->instsrv == NULL) abort(); + + /* save ``sec'' for signal handling */ + sec_ptr = sec; + + return TRUE; +} + +int sec_server_wait(SecServer *sec) +{ + int i; + + /* _MUST_ set Smasktime before Smaskwaiting */ + Smasktime(WAIT_TIME, 0); + i = Smaskwait(); + if (i > 0) { + /* Someone has new data, check it */ + server_verify(sec->statsrv); + server_verify(sec->instsrv); + } + else if (i < 0) { + /* Smaskwait crashed... fix it in the future; */ + /* by now, abort the program. */ + printf("*** Critical error: Smaskwait() failed!\n\n"); + abort(); + } + return (i); +} + + +int sec_server_check(SecServer *sec) +{ + ping_sockets(sec); + return(0); +} + +static void controlc(int sig) +{ + if (sig == SIGINT) printf("\n*** Control-C caught.\n"); + + /* Close all connections and exit. */ + printf("*** Closing servers...\n"); + if (sec_ptr != NULL) { + server_close(sec_ptr->statsrv); + server_close(sec_ptr->instsrv); + } + + sec_server_free(sec_ptr); + + exit(1); +} + + + +/* Open status server */ +Server* run_status_server(SecServer* sec) +{ + char sname[MAX_SERVER_NAME]; + Server *stsrv; + + /* build status server name based on instname */ + strcpy(sname, sec->instname); + strcat(sname, STATUS_STR); + /* e.g: instname == INST => buf == INSTstatus */ + + stsrv = server_init(sname, SKT_STATSERVER); + if (stsrv == NULL) return (NULL); + + stsrv->accept_handler = accept_client; + stsrv->answer_handler = statsrv_answer; + stsrv->close_handler = close_client_handler; + stsrv->maxclients = sec->config->maxclnt; + return (stsrv); +} + + +/* Open instrument control server */ +Server *run_control_server(SecServer* sec) +{ + char sname[MAX_SERVER_NAME]; + Server *isrv; + + /* build server name based on instname */ + strcpy(sname, sec->instname); + strcat(sname, CONTROL_STR); + /* e.g: instname == INST => buf == INSTcontrol */ + + isrv = server_init(sname, SKT_CTRLSERVER); + if (isrv == NULL) return (NULL); + + isrv->accept_handler = accept_client; + isrv->answer_handler = instsrv_answer; + isrv->close_handler = close_instclient_handler; + isrv->maxclients = 1; + return (isrv); +} + + + +/* Client will be able to ask server after identifying itself */ +static int client_ident(SktListItem *client, char *name) +{ + int i; + Server *server; + + if (client == NULL) return (EOF); + server = client->server; + if (!server_is_valid(server)) return(EOF); + + i = server_set_client_name(client, name); + if (i == EOF) { + server_write_client(fresponses[RES_ERROR], client); + server_close_client(client); + return (EOF); + } + i = server_write_client(fresponses[RES_OK], client); + if (i == EOF) return (EOF); + printf(" -> <%s> Identification: %s@%s\n", + server->name, client->name, Speername(client->skt)); + client->state = CST_IDLE; + + return (0); +} + + +static int instrument_ident(SktListItem *client, char *name) +{ + int i; + Server *server; + + if (client == NULL || name == NULL) return (EOF); + server = client->server; + if (!server_is_valid(server)) return(EOF); + + /* Verify instrument identification */ + i = strncmp(name, INST_CLIENT_NAME, MAX_CLIENT_NAME); + if (i != 0) { + /* bad identification string. */ + printf("*** <%s> Bad identification from %s, client closed\n", + server->name, Speername(client->skt)); + server_write_client(fresponses[RES_ERROR], client); + server_close_client(client); + return (EOF); + } + + /* Proceed as in client_ident() */ + + i = server_set_client_name(client, name); + if (i == EOF) { + server_write_client(fresponses[RES_ERROR], client); + server_close_client(client); + return (EOF); + } + i = server_write_client(fresponses[RES_OK], client); + if (i == EOF) return (EOF); + printf(" -> <%s> Identification: %s@%s\n", + server->name, client->name, Speername(client->skt)); + client->state = CST_IDLE; + + return (0); +} + + +static int check_ident(SktListItem *client) +{ + Server *server; + + if (client == NULL) return (EOF); + server = client->server; + if (!server_is_valid(server)) return (EOF); + + /* Only answer clients who have already identified */ + if (client->state == CST_IDENT_PENDING) { + if (server_write_client(fresponses[RES_ERROR], client) == EOF) + return (EOF); + printf("*** <%s> %s: Unidentified client, " + "access denied.\n", + server->name, Speername(client->skt)); + return (EOF); + } + return (0); +} + + +static int request_status(SktListItem *client, char *name) +{ + char buf[MAXBUFLEN]; + char *ptr; + int i; + + if (check_ident(client) == EOF) return (EOF); + ptr = get_status(name); + if (ptr == NULL) { + i = server_write_client(fresponses[RES_ERROR], client); + return (i); + } + snprintf(buf, MAXBUFLEN, "%s %s", fresponses[RES_STATUS], ptr); + i = server_write_client(buf, client); + return (i); +} + + +static int change_status(SktListItem *client, char *statstr) +{ + int i; + + i = set_status_str(statstr); + if (i == EOF) { + i = server_write_client(fresponses[RES_ERROR], client); + return (i); + } + i = server_write_client(fresponses[RES_OK], client); + return (i); +} + + +/* add client to "status"'s notification list */ +static int client_notify(SktListItem *client, char *statname) +{ + int i; + + if (check_ident(client) == EOF) return (EOF); + i = set_notify(client, statname); + if (i == EOF) { + i = server_write_client(fresponses[RES_ERROR], client); + return (i); + } + i = server_write_client(fresponses[RES_OK], client); + return (i); +} + + +/* remove client from "status"'s notification list */ +static int client_unotify(SktListItem *client, char *statname) +{ + int i; + + if (check_ident(client) == EOF) return (EOF); + i = rmv_notify(client, statname); + if (i == EOF) { + i = server_write_client(fresponses[RES_ERROR], client); + return (i); + } + i = server_write_client(fresponses[RES_OK], client); + return (i); +} + + +void ping_sockets(SecServer *sec) +{ + static int tc; + + /* Notify the special field PING every WAIT_TIME * 3 */ + if (tc % 3) return; + + /* SETSTATUS PING INSTNAME */ + set_status(STPING, sec->instname); +} + + +/* Additional proceedings after client connection */ +static int accept_client(SktListItem *client) +{ + int i; + + if (client == NULL) return (EOF); + if (!server_is_valid(client->server)) return(EOF); + + /* Tell client we accepted */ + i = server_write_client(fresponses[RES_HELLO], client); + if (i == EOF) return (EOF); + + client->state = CST_IDENT_PENDING; + return (0); +} + + +static int statsrv_answer(SktListItem *client) +{ + char buf[MAXBUFLEN]; + int fi; + field f; + Server *server; + + if (client == NULL) return (EOF); + server = client->server; + if (!server_is_valid(server)) return(EOF); + + /* Determine which command we received */ + Sgets(buf, MAXBUFLEN, client->skt); + fi = getfield(buf, &f, fcomm); + switch (fi) { + + case C_QUIT: + server_close_client(client); + return(1); + break; + + case C_IDENT: + client_ident(client, f.contents); + break; + + case C_STATUS: + if (check_ident(client) != EOF) + request_status(client, f.contents); + break; + + + case C_NOTIFY: + if (check_ident(client) != EOF) + client_notify(client, f.contents); + break; + + case C_UNOTIFY: + if (check_ident(client) != EOF) + client_unotify(client, f.contents); + break; + + case C_CONTROL: + case C_SETSTATUS: + case EOF: + default: + /* Invalid command or command unavailable */ + if (server_write_client(fresponses[RES_ERROR], client) == EOF) + return (EOF); + printf(" -> <%s> %s@%s: Invalid command - %s\n", + server->name, client->name, + Speername(client->skt), buf); + break; + } + return (0); +} + + + +static int instsrv_answer(SktListItem *client) +{ + char buf[MAXBUFLEN]; + int fi; + field f; + Server *server; + + if (client == NULL) return (EOF); + server = client->server; + if (!server_is_valid(server)) return(EOF); + + /* Determine which command we received */ + Sgets(buf, MAXBUFLEN, client->skt); + fi = getfield(buf, &f, fcomm); + switch (fi) { + + case C_QUIT: + server_close_client(client); + return(1); + break; + + case C_IDENT: + if (instrument_ident(client, f.contents) != 0) + return (1); + + break; + + case C_STATUS: + if (check_ident(client) != EOF) + request_status(client, f.contents); + break; + + case C_SETSTATUS: + change_status(client, f.contents); + break; + + case C_NOTIFY: + case C_UNOTIFY: + case EOF: + default: + /* Invalid command or command unavailable */ + if (server_write_client(fresponses[RES_ERROR], client) == EOF) + return (EOF); + printf(" -> <%s> %s@%s: Invalid command - %s\n", + server->name, client->name, + Speername(client->skt), buf); + break; + } + return (0); +} + + + +/* Things to do before closing the client */ +static int close_client_handler(SktListItem *client) +{ + clear_client_notifies(client); + return (0); +} + + +static int close_instclient_handler(SktListItem *client) +{ + clear_client_notifies(client); + + /* SETSTATUS INSTNAME OFFLINE */ + set_istatus(STATUS_INSTNAME, DEF_INSTSTATUS_CONTENT); + return (0); +} + +/* int sec_server_add_status(SecServer* sec, const char* status) { */ + +/* assert(sec != NULL); */ +/* assert(status != NULL); */ + +/* return (s_dict_add(sec->status, status, DEF_STATUS_CONTENS) && */ +/* s_dict_add(sec->notify, status, skt_list_new())); */ + +/* } */ + +/* int sec_server_free_status(SecServer* sec, const char* status) { */ + +/* assert(sec != NULL); */ +/* assert(status != NULL); */ + +/* return (s_dict_remove(sec->status, status) && */ +/* s_dict_remove(sec->notify, status)); */ + +/* } */ + +/* int sec_server_set_status(SecServer* sec, const char* status, const char* contents) { */ + +/* assert(sec != NULL); */ +/* assert(status != NULL); */ +/* assert(contents != NULL); */ + +/* return (s_dict_set(sec->status, status, contents)); */ + +/* } */ + +/* const char* sec_server_get_status(SecServer* sec, const char* status) { */ + +/* assert(sec != NULL); */ +/* assert(status != NULL); */ + +/* return (s_dict_get(sec->status, status); */ + + +/* } */ + +/* int sec_server_set_notify(SecServer* sec, SktListItem* client, const char* status) { */ + +/* assert(sec != NULL); */ +/* assert(client != NULL); */ +/* assert(status != NULL); */ + +/* SktList* list = s_dict_get(sec->notify, status); */ + +/* sktlist_add(list, client->skt); */ + +/* return RUE; */ + +/* } */ + +/* int sec_server_remove_notify(SecServer* sec, SktListItem* client, const char* status) { */ + +/* assert(sec != NULL); */ +/* assert(client != NULL); */ +/* assert(status != NULL); */ + +/* SktList* list = s_dict_get(sec->notify, status); */ + +/* sktlist_remove(list, client); */ + +/* return RUE; */ + +/* } */ + +/* Status field table */ + +/* This creates the status fields; should be avoided while runing */ +int fill_statlist(char *instname, + char fstatus[][MAX_FIELD_SIZE], int nst) +{ + int i; + + if ( instname == NULL || fstatus == NULL || nst <= 0 || + nst > MAX_STATUS_FIELDS ) + return (EOF); + + /* add a status field for instrument state */ + if (add_status(instname) < 0) return (EOF); + + /* add a status field for connection monitoring */ + if (add_status(STPING) < 0) return (EOF); + + + for (i = 0; i < nst; i++) + if (add_status((char *) fstatus[i]) < 0) return (EOF); + + return (0); +} + + +int add_status(char *name) +{ + char *status_ptr; + statcontents_t *contents_ptr; + + /* Do not allocate more than MAX_STATUS_FIELDS fields */ + if (statcounter >= MAX_STATUS_FIELDS) return (EOF); + if (name == NULL) return(EOF); + if (strlen(name) > MAX_FIELD_SIZE) return (EOF); + + /* Get memory for field stuff & notify list */ + status_ptr = (char *) calloc(strlen(name) + 1, sizeof(char)); + if (status_ptr == NULL) return(EOF); + contents_ptr = (statcontents_t *) calloc(1, sizeof(statcontents_t)); + if (contents_ptr == NULL) { + free(status_ptr); + return(EOF); + } + /* Fill the structures */ + strcpy(status_ptr, name); + if (statcounter == 0) /* INSTNAME Status field */ + strcpy(contents_ptr->contents, DEF_INSTSTATUS_CONTENT); + else + strcpy(contents_ptr->contents, DEF_STATUS_CONTENT); + + /* Create notification list */ + contents_ptr->notify_table = sktlist_new(); + if (contents_ptr->notify_table == NULL) { + free(status_ptr); + free(contents_ptr); + return (EOF); + } + + fstatus[statcounter] = status_ptr; + status_contents[statcounter] = contents_ptr; + + return(statcounter++); +} + + +/* Free _ALL_ status fields */ +void free_status(void) +{ + char **ptr1; + statcontents_t *ptr2; + + ptr1 = fstatus; + ptr2 = *status_contents; + while (statcounter > 0) { + if (ptr1 != NULL) free(ptr1++); + if (ptr2 != NULL) { + free_notify_table(ptr2->notify_table); + free(ptr2++); + } + statcounter--; + } + +} + + +/* Set status by index */ +int set_istatus(int i, char *contents) +{ + if (contents == NULL) return (EOF); + if (i < 0 || i > statcounter) return (EOF); + if (strlen(contents) > MAX_STATUS_CONTENTS) return (EOF); + + +#ifdef NOTIFY_ON_CHANGE + /* Notify clients only if status changed */ + if (strncmp(status_contents[i]->contents, + contents, MAX_STATUS_CONTENTS) != 0) { + strcpy(status_contents[i]->contents, contents); + notify_internal(i); /* FIXME */ + } +#else + /* Notify always */ + strcpy(status_contents[i]->contents, contents); + notify_internal(i); /* FIXME */ +#endif + + return (0); +} + + +/* Set status by name */ +int set_status(char *name, char *contents) +{ + int i, res; + + if (name == NULL) return (EOF); + if (contents == NULL) return (EOF); + if (strlen(contents) > MAX_STATUS_CONTENTS) return (EOF); + + i = getfield(name, NULL, (const char **) fstatus); + res = set_istatus(i, contents); + + return(res); +} + + +/* set status by string: "STATUS VALUE" */ +int set_status_str(char *statstr) +{ + int i, res; + field f; + + if (statstr == NULL) return (EOF); + + i = getfield(statstr, &f, (const char **) fstatus); + res = set_istatus(i, f.contents); + + return (res); + +} + + +/* Get status by index */ +char *get_istatus(int i) +{ + if (i < 0 || i > statcounter) return (NULL); + + return (status_contents[i]->contents); +} + + +/* Get status by name */ +char *get_status(char *name) +{ + int i; + + if (name == NULL) return (NULL); + + i = getfield(name, NULL, (const char **) fstatus); + if (i < 0 || i > statcounter) return (NULL); + + return (status_contents[i]->contents); +} + + +int set_notify(SktListItem *client, char *statfield) +{ + int i, res; + + if (client == NULL) return (EOF); + i = getfield(statfield, NULL, (const char **) fstatus); + res = set_inotify(client, i); + return (res); +} + + +int set_inotify(SktListItem *client, int i) +{ + SktListItem *item; + + if (client == NULL) return (EOF); + if (i < 0 || i > statcounter) return (EOF); + + /* Check if this client already requested notification */ + for (item = status_contents[i]->notify_table->first; + item != NULL; item = item->next) { + if (item->type == SKT_SENTINEL) continue; + if (item->id == client->id) + /* This client is already registered */ + return (0); + } + + /* Else, add client to list */ + item = sktlist_add(status_contents[i]->notify_table, + client->skt, client->type); + if (item == NULL) return (EOF); + /* Save id for future removal */ + item->id = client->id; + /* Save server for future writing */ + item->server = client->server; + return (0); + +} + + +int rmv_notify(SktListItem *client, char *statfield) +{ + int i, res; + + if (client == NULL) return (EOF); + i = getfield(statfield, NULL, (const char **) fstatus); + res = rmv_inotify(client, i); + return (res); +} + + +int rmv_inotify(SktListItem *client, int i) +{ + SktListItem *item, *tmp; + + if (client == NULL) return (EOF); + if (i < 0 || i > statcounter) return (EOF); + + /* Find who has this id */ + for (item = status_contents[i]->notify_table->first; + item != NULL; item = item->next) { + if (item->type == SKT_SENTINEL) continue; + if (item->id == client->id) { + /* id found, remove it */ + tmp = item; + item = item->previous; + sktlist_remove(status_contents[i]->notify_table, tmp); + return (0); + } + } + /* id not found */ + return (EOF); +} + +void clear_client_notifies(SktListItem *client) +{ + int i; + + /* Clear notifies for each Status entry */ + for (i = 0; i < statcounter; i++) + rmv_inotify(client, i); +} + + +static void notify_internal(int i) +{ + char buf[MAXBUFLEN]; + SktListItem *client; + + /* Notify everyone in list */ + for (client = status_contents[i]->notify_table->first; + client != NULL; client = client->next) { + if (client->type == SKT_SENTINEL) continue; + snprintf(buf, MAXBUFLEN, "NOTIFY %s %s", + fstatus[i], status_contents[i]->contents); + server_write_client(buf, client); + } +} + + +static void free_notify_table(SktList *list) +{ + if (list == NULL) return; + + while (list->first->type == SKT_CLIENT) sktlist_remove(list, list->first); + sktlist_free(list); +} + + diff --git a/src/sec/sec_server.h b/src/sec/sec_server.h new file mode 100644 index 00000000..4731dc3b --- /dev/null +++ b/src/sec/sec_server.h @@ -0,0 +1,67 @@ +#ifndef _SEC_SERVER_H_ +#define _SEC_SERVER_H_ 1 + +// #include "sdict.h" +#include "server.h" +#include "sec_config.h" + +#define WAIT_TIME (1) /* seconds */ +#define DEF_STATUS_CONTENT "EMPTY" +#define DEF_INSTSTATUS_CONTENT "OFFLINE" + +/* Field name for connection monitoring */ +#define STPING "PING" + +#define STATUS_INSTNAME (0) +#define STATUS_PING (1) + +typedef struct _SecServer SecServer; + +struct _SecServer { + char *instname; + + SecConfig *config; + +// SDict *status; +// SDict* notify; + + Server *statsrv; + Server *instsrv; + +}; + +SecServer *sec_server_new(void); + +void sec_server_free(SecServer* sec); +int sec_server_configure(SecServer* sec, const char *cfile); +int sec_server_run(SecServer* sec); +int sec_server_wait(SecServer* sec); +int sec_server_check(SecServer* sec); + +// int sec_server_add_status(SecServer* sec, const char* status); +// int sec_server_free_status(SecServer* sec, const char* status); +// int sec_server_set_status(SecServer* sec, const char* status, const char* contents); +// const char* sec_server_get_status(SecServer* sec, const char* status); + +// int sec_server_set_notify(SecServer* sec, SktListItem* client, const char* status); +// int sec_server_remove_notify(SecServer* sec, SktListItem* client, const char* status);); + +/* from status.c */ + +int fill_statlist(char *instname, + char fstatus[][MAX_FIELD_SIZE], int nst); +int add_status(char *name); +void free_status(void); +int set_istatus(int i, char *contents); +int set_status(char *name, char *contents); +int set_status_str(char *statstr); +char *get_istatus(int i); +char *get_status(char *name); + +int set_notify(SktListItem *client, char *statfield); +int set_inotify(SktListItem *client, int i); +int rmv_notify(SktListItem *client, char *statfield); +int rmv_inotify(SktListItem *client, int i); + + +#endif /* !_SEC_SERVER_H_ */ diff --git a/src/sec/server.c b/src/sec/server.c new file mode 100644 index 00000000..9c8c9662 --- /dev/null +++ b/src/sec/server.c @@ -0,0 +1,267 @@ +/*************************************************************************** + multiskt.c - description + ------------------- + begin : Tue Sep 12 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include +#include + +#include "sockets.h" + +#include "skterror.h" +#include "sktlist.h" + +#include "server.h" + +int server_is_valid(Server *srv) +{ + if (srv == NULL) return (0); + if (srv->name == NULL) return (0); + if (srv->clientlist == NULL) return (0); + if (srv->skt == NULL) return (0); + return (1); +} + + +Server *server_init(char *sname, int type) +{ + Server *srv; + + if (sname == NULL) return (NULL); + srv = (Server *) calloc(1, sizeof(Server)); + if (srv == NULL) return (NULL); + + srv->name = (char *) calloc(strlen(sname) + 1, sizeof(char)); + if (srv->name == NULL) { + free(srv); + return (NULL); + } + strcpy(srv->name, sname); + srv->skt = Sopen(sname, "s"); + if (srv->skt == NULL) { + /* force opening, remove existing server */ + Srmsrvr(sname); + srv->skt = Sopen(sname, "s"); + } + if (srv->skt == NULL) { + /* could not open server */ + printf("*** Error opening \"%s\" server.\n", sname); + printf("*** The PortMaster could be down.\n"); + free(srv); + return (NULL); + } + srv->clientlist = sktlist_new(); + if (srv->clientlist == NULL) { + printf("*** Error allocating client list.\n"); + Sclose(srv->skt); + free(srv); + return(NULL); + } + Smaskset(srv->skt); + + printf(" -> Server \"%s\" open.\n", sname); + + return (srv); +} + + +void server_close(Server *srv) +{ + SktList *tmp; + + if (srv == NULL) return; + + if (srv->name != NULL) free(srv->name); + if (srv->clientlist != NULL) { + /* disconnect all clients... */ + tmp = srv->clientlist; + while (tmp->first->type == SKT_CLIENT) { + Sputs(ABORT_MESSAGE, tmp->first->skt); + server_close_client(tmp->first); + } + sktlist_free(srv->clientlist); + } + if (srv->skt != NULL) { + Smaskunset(srv->skt); + Sclose(srv->skt); + } + free(srv); +} + + +SktListItem *server_accept(Server *srv) +{ + Socket *skt; + SktListItem *item; + + if (!server_is_valid(srv)) return(NULL); + + skt = Saccept(srv->skt); + if (skt == NULL) return (NULL); +/* if (srv->nclients >= srv->maxclients) { */ +/* /\* Deny access, server full *\/ */ +/* printf("*** <%s> %s: Connection denied.\n", */ +/* srv->name, Speername(skt)); */ +/* Sputs(SERVER_FULL_MESSAGE, skt); */ +/* Sclose(skt); */ +/* return (NULL); */ +/* } */ + item = sktlist_add(srv->clientlist, skt, SKT_CLIENT); + if (item == NULL) { + printf("*** <%s> %s: Error allocating Socket List \ + entry. Connection denied.\n", + srv->name, Speername(skt)); + Sputs(ERROR_MESSAGE, skt); + Sclose(skt); + return (NULL); + } + + Smaskset(skt); + srv->nclients++; + srv->nconn++; + + /* Save pointer to server in client */ + item->server = srv; /* e.g: srv->clientlist->last->server = srv; */ + + printf(" -> <%s> %s Connected.\n", + srv->name, Speername(skt)); + + return (item); +} + + +void server_close_client(SktListItem *clnt) +{ + Server *srv; + + if (clnt == NULL) return; + srv = clnt->server; + if (!server_is_valid(srv)) return; + + /* Execute close handler */ + if (srv->close_handler != NULL) + (*srv->close_handler) (clnt); + + if (clnt->name != NULL) free(clnt->name); + + if (clnt->skt != NULL) { + Smaskunset(clnt->skt); + Sclose(clnt->skt); + } + sktlist_remove(srv->clientlist, clnt); + + srv->nclients--; +} + + +int server_write_client(const char *buf, SktListItem *clnt) +{ + Server *srv; + + if (buf == NULL || clnt == NULL) return (EOF); + srv = clnt->server; + if (!server_is_valid(srv)) return (EOF); + + watch(clnt->skt); + Sputs((char *)buf, clnt->skt); + if (skterror()) { + server_close_client(clnt); + return (EOF); + } + return (0); +} + + +int server_set_client_name(SktListItem *clnt, char *name) +{ + char buf[MAX_CLIENT_NAME]; + char *ptr; + int i = 0; + + if (clnt == NULL) return (EOF); + if (clnt->name != NULL) free(clnt->name); + + /* Remove white space, and keep 1 char for '\0' */ + ptr = buf; + while (!isspace(*name) && (*name != '\0') && (++i < MAX_CLIENT_NAME)) + *ptr++ = *name++; + *ptr = '\0'; + + /* String size already checked, safe calling strlen and stcpy */ + clnt->name = (char *) calloc(strlen(buf) + 1, sizeof(char)); + if (clnt->name == NULL) return (EOF); + strcpy(clnt->name, buf); + return (0); +} + + +int server_answer(SktListItem *clnt) +{ + /* Dummy routine */ + return (1); +} + + +/* Look for sockets that have changed and process them */ +int server_verify(Server *srv) +{ + SktListItem *client, *tmp; + int res; + + if (!server_is_valid(srv)) return(EOF); + /*** Do not even dream calling a NULL function pointer ***/ + if (srv->accept_handler == NULL || srv->answer_handler == NULL) + return (EOF); + + /*** Process server ***/ + res = Stest(srv->skt); + if (res > 0) { + /* Accept connection (ATTENTION: call to function pointer) */ + tmp = server_accept(srv); + (*srv->accept_handler) (tmp); + } + else if (res == EOF) { + /* Socket error, must restart server */ + return (EOF); + } + + /*** Process Clients ***/ + /* seek client list */ + for (client = srv->clientlist->first; + client != NULL; client = client->next) { + if (client->type == SKT_SENTINEL) continue; + res = Stest(client->skt); + if (res > 0) { + /* Save next pointer */ + tmp = client->previous; + /* Answer the request */ + /* (ATTENTION: call to function pointer) */ + res = (*srv->answer_handler) (client); + if (res == 1) client = tmp; + } + else if (res == EOF) { + /* Socket error, close the client */ + printf("*** <%s> %s@%s - Connection broken.\n", + srv->name, client->name, Speername(client->skt)); + + tmp = client; + client = client->previous; + server_close_client(tmp); + } + } + return (0); +} diff --git a/src/sec/server.h b/src/sec/server.h new file mode 100644 index 00000000..394816ff --- /dev/null +++ b/src/sec/server.h @@ -0,0 +1,73 @@ +/*************************************************************************** + multisrv.h - description + ------------------- + begin : Tue Sep 12 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef MULTISKT_H +#define MULTISKT_H + +#include "sockets.h" + +#include "sktlist.h" + +#define ABORT_MESSAGE "ABORT" +#define ERROR_MESSAGE "ERROR" +#define SERVER_FULL_MESSAGE "SERVER_FULL" + +#define MAX_CLIENT_NAME (32) +#define MAX_SERVER_NAME (32) + +typedef struct _Server Server; + +/* pointer to handler functions */ +typedef int (*accept_hndl) (SktListItem *); +typedef int (*answer_hndl) (SktListItem *); +typedef int (*close_hndl) (SktListItem *); + +struct _Server { + char *name; + int type; + + Socket *skt; + int nconn; + int nclients; + int maxclients; + SktList *clientlist; + + /* pointer to `accept' handler */ + accept_hndl accept_handler; + /* pointer to `answer' handler */ + answer_hndl answer_handler; + /* pointer to `server_close_client' handler */ + close_hndl close_handler; + /* pointer to any useful thing */ +}; + + +Server *server_init(char *sname, int type); +void server_close(Server *srv); + +int server_is_valid(Server *srv); +int server_verify(Server *srv); + +int server_write_client(const char *buf, SktListItem *clnt); +int server_set_client_name(SktListItem *clnt, char *name); +void server_close_client(SktListItem *clnt); + +/* default accept routine */ +SktListItem *server_accept(Server *srv); +int server_answer(SktListItem *clnt); + +#endif /* MULTISKT_H */ diff --git a/src/sec/skterror.c b/src/sec/skterror.c new file mode 100644 index 00000000..caaa385f --- /dev/null +++ b/src/sec/skterror.c @@ -0,0 +1,50 @@ +/*************************************************************************** + skterror.c - description + ------------------- + begin : Tue Oct 3 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include "sockets.h" + +#include "skterror.h" + +#define TRUE (1) +#define FALSE (0) + +static Socket *watch_socket; +static int s_error; + +/* prepare to write to Status socket */ +void watch(Socket *skt) +{ + watch_socket = skt; + s_error = FALSE; +} + + +int skterror(void) +{ + if (s_error) return (1); + return (0); +} + + +void pipe_error(int sig) +{ + /* Error writing to a socket */ + printf("\n\n*** Error: Attempt to write to a broken socket.\n"); + + s_error = TRUE; + +} diff --git a/src/sec/skterror.h b/src/sec/skterror.h new file mode 100644 index 00000000..1fb97bab --- /dev/null +++ b/src/sec/skterror.h @@ -0,0 +1,30 @@ +/*************************************************************************** + skterror.h - description + ------------------- + begin : Wed Oct 4 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef SKTERROR_H +#define SKTERROR_H + +#include "sockets.h" + +/* ask if there were some error while writing to socket */ +int skterror(void); +/* Tell sigpipe() who is trying to write to socket */ +void watch(Socket *skt); + +void pipe_error(int sig); + +#endif /* STKERROR_H */ diff --git a/src/sec/sktlist.c b/src/sec/sktlist.c new file mode 100644 index 00000000..49fd9fba --- /dev/null +++ b/src/sec/sktlist.c @@ -0,0 +1,118 @@ +/*************************************************************************** + sktlist.c - description + ------------------- + begin : Tue Sep 12 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include +#include "sockets.h" +#include "sktlist.h" + +static int id_counter = 0; + +SktList *sktlist_new(void) +{ + SktList *list; + + list = (SktList *) calloc(1, sizeof(SktList)); + if (list == NULL) return (NULL); + + /* create ``sentinels'' */ + /* Null SocketList entries, just to mark the end/beginning of the list. */ + list->head = (SktListItem *) calloc(1, sizeof(SktListItem)); + if (list->head == NULL) return (NULL); + list->tail = (SktListItem *) calloc(1, sizeof(SktListItem)); + if (list->tail == NULL) return (NULL); + + /* head points to tail */ + list->head->previous = NULL; + list->head->next = list->tail; + list->head->skt = NULL; + list->head->type = SKT_SENTINEL; + + /* tail points to head */ + list->tail->previous = list->head; + list->tail->next = NULL; + list->tail->skt = NULL; + list->tail->type = SKT_SENTINEL; + + /* `first' == `head->next' */ + list->first = list->head->next; + + return (list); +} + + +int sktlist_free(SktList *list) +{ + if (list == NULL) return (0); + + /* List must be empty */ + if (list->head->next != NULL) return (-1); + free(list->head); + free(list->tail); + free(list); + return (0); +} + + +SktListItem *sktlist_add(SktList *list, Socket *skt, int type) +{ + SktListItem *new; + + /* Add a _connected_ socket only */ + if (skt == NULL || list == NULL) return (NULL); + new = (SktListItem *) calloc(1, sizeof(SktListItem)); + if (new == NULL) return (NULL); + + /* append new item to the tail */ + list->tail->previous->next = new; + new->previous = list->tail->previous; + list->tail->previous = new; + new->next = list->tail; + new->type = type; + + /* Increment id_counter, if reached MAX_ID, restart counting */ + /* I don't know what to do when this happen, hope it won't */ + /* bring any trouble. */ + new->id = id_counter++ % MAX_ID; + + /* Point the first item to the right place */ + list->first = list->head->next; + + new->skt = skt; + + return (new); +} + + +/* this does NOT disconnect the socket! */ +void sktlist_remove(SktList *list, SktListItem *item) +{ + if (item == NULL || list == NULL) return; + + /* keep the sentinels */ + if (item->type == SKT_SENTINEL) return; + + /* link neighbors together */ + item->previous->next = item->next; + if (item->next != NULL) item->next->previous = item->previous; + + /* Point the first item to the right place */ + list->first = list->head->next; + + free(item); +} + diff --git a/src/sec/sktlist.h b/src/sec/sktlist.h new file mode 100644 index 00000000..f156cccd --- /dev/null +++ b/src/sec/sktlist.h @@ -0,0 +1,69 @@ +/*************************************************************************** + sktlist.h - description + ------------------- + begin : Tue Sep 12 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef SKTLIST_H +#define SKTLIST_H + +#include "sockets.h" + +/* socket types */ +#define SKT_SENTINEL (0) +#define SKT_CLIENT (1) +#define SKT_SERVER (2) + +/* to use static id's, start them at (MAX_ID + 1) */ +#define MAX_ID (1000000L) +#define MAXBUFLEN (128) + +/* Client states */ +/* CST_IDENT_PENDING - Server is waiting response from client, + don't send anything */ +/* CST_NOTIFY - Notify the client when an event occurs */ +/* CST_IDLE - Client is idle */ +/* CST_WAITING_NOTIFY - Client is waiting notification from server */ +enum { CST_IDENT_PENDING, CST_NOTIFY, CST_IDLE, CST_WAITING }; + +/* item of sockets linked list */ +typedef struct _SktListItem SktListItem; + +struct _SktListItem { + int id; + char *name; + void *server; /* pointer to Server structure */ + int type; + int state; + Socket *skt; + + SktListItem *next, *previous; +}; + +/* Socket linked list */ +typedef struct { + SktListItem *head; + SktListItem *first; + SktListItem *tail; +} SktList; + + +/* Socket list utilities */ +SktList *sktlist_new(void); +int sktlist_free(SktList *list); +SktListItem *sktlist_add(SktList *list, Socket *skt, int type); +void sktlist_remove(SktList *list, SktListItem *item); + + +#endif /* SKTLIST_H */ diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am new file mode 100644 index 00000000..d5ab0a3c --- /dev/null +++ b/src/tools/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = client + +bin_SCRIPTS = uts-gen-conf diff --git a/src/tools/client/Makefile.am b/src/tools/client/Makefile.am new file mode 100644 index 00000000..40b03a1c --- /dev/null +++ b/src/tools/client/Makefile.am @@ -0,0 +1,6 @@ +include $(top_srcdir)/rules.make + +bin_PROGRAMS = sclient + +sclient_SOURCES = main.c +sclient_LDADD = -lsimpleskts \ No newline at end of file diff --git a/src/tools/client/SConscript b/src/tools/client/SConscript new file mode 100644 index 00000000..e0851baa --- /dev/null +++ b/src/tools/client/SConscript @@ -0,0 +1,15 @@ +#! /usr/bin/python + +Import("env") + +_sources = Split( + +""" +main.c +""" +) + +env.Program('sclient', _sources, + CPPPATH=['.', '#contrib/include'], + LIBPATH=['#contrib/lib'], + LIBS=['simpleskts']) diff --git a/src/tools/client/main.c b/src/tools/client/main.c new file mode 100644 index 00000000..41c4863c --- /dev/null +++ b/src/tools/client/main.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include "sockets.h" + +int main (int argc, char **argv) +{ + Socket *skt; + int i, n; + char buffer[500]; + int waitresponse = 0; + + skt = Sopen(argv[1],"c"); + if (skt == NULL) exit (1); + + Smaskset(skt); + Smaskfdset(0); + + for(;;) { + // is there anything new? + i = Smaskwait(); + if (i <= 0) break; + + i = Stest(skt); + if (i <= -1) break; // socket error + else if (i > 0) { + // something new from server + Sgets(buffer, 500, skt); + printf("server> %s\n", buffer); + waitresponse = 0; + continue; + } + + // only get new command after server response + if (waitresponse) continue; + + if (Smaskfdisset(0)) { + // something new from stdin + if (fgets(buffer, 500, stdin) == NULL) break; + n = strlen(buffer); + buffer[n-1] = '\0'; + if (n > 1) { + Sputs(buffer, skt); + printf("client> %s\n", buffer); + waitresponse = 1; + } + } + } + Sputs("QUIT", skt); +} diff --git a/src/tools/client/sterm.c b/src/tools/client/sterm.c new file mode 100644 index 00000000..13856cd6 --- /dev/null +++ b/src/tools/client/sterm.c @@ -0,0 +1,31 @@ +#include +#include +#include +#include "sockets.h" + +int main (int argc, char **argv) +{ + Socket *skt; + int i, n; + char buffer[500]; + + skt = Sopen(argv[1],"c"); + if (skt == NULL) exit (1); + + for(;;) { + printf("client> "); + fflush(stdout); + fgets(buffer, 500, stdin); + n = strlen(buffer); + buffer[n-1] = '\0'; + if (n > 1) + Sputs(buffer, skt); + usleep(100000); + i = Stest(skt); + if (i == -1) exit(-1); + else if (i > 0) { + Sgets(buffer, 500, skt); + printf("server> %s\n", buffer); + } + } +} diff --git a/src/tools/uts-gen-conf b/src/tools/uts-gen-conf new file mode 100644 index 00000000..81f90224 --- /dev/null +++ b/src/tools/uts-gen-conf @@ -0,0 +1,101 @@ +#! /usr/bin/python +# -*- coding: iso-8859-1 -*- +# +# a parser to generate .conf file to 'sec' +# + +import sys +import imp +import os.path +import glob + +daemons = {} +rpc = [] + +default_uts_etc = "/etc/uts/sec" + +# build instruments list +inst_list = [] + +for dir in sys.path: + s = os.path.join(dir, 'uts/instruments/[a-zA-Z]*.py') + inst_list += glob.glob(s) + +# look on ~/uts +dir = os.path.expanduser('~/uts') +inst_list += glob.glob(os.path.join(dir, '[a-zA-Z]*.py')) + +try: + + for d in inst_list: + + daemonFullPath = os.path.abspath(d) + + daemonPath, daemonFile = os.path.split(daemonFullPath) + + daemon = os.path.splitext(daemonFile)[0] + + sys.path.append(daemonPath) + + (f, p, d) = imp.find_module(daemon) + + mod = imp.load_module(daemon, f, p, d) + + me = "" + + if(hasattr(mod, '_me')): + me = mod._me + else: + me = mod.__name__ + + # me found + daemons[me] = {"rpc": [], "notify": []} + + if(hasattr(mod, '_rpc')): + daemons[me]["rpc"] = mod._rpc + + if(hasattr(mod, '_notify')): + daemons[me]["notify"] = mod._notify + + # make rpc list + for (d, v) in daemons.items(): + for r in v["rpc"]: + rpc.append(d+"_"+r) + + # save rpc list to file + rpc_file = file(os.path.join(default_uts_etc, "rpc.conf"), "w+") + + # header + rpc_file.write("INSTNAME RPC\n") + rpc_file.write("NSTATCLIENTS 10\n\n") + + # save each notify list to a file named daemon.conf + for (d,v) in daemons.items(): + + d_file = file(os.path.join(default_uts_etc, d.lower()+".conf"), "w+") + + # header + d_file.write("INSTNAME %s\n" % d.upper()) + d_file.write("NSTATCLIENTS 10\n\n") + + for n in v["notify"]: + d_file.write("STATUS %s\n" % n) + + # write rpc entries + for r in v["rpc"]: + rpc_file.write("STATUS %s_%s\n" % (d, r)) + + rpc_file.write("STATUS %s_END_OF_LINE\n\n" % d.upper()) + + d_file.close() + + rpc_file.close() + +except ImportError, e: + print "Cannot import module:", str(e) + +except AttributeError, e: + print "Cannot get attribute:", str(e) + +except IOError, e: + print "Cannot write file:", str(e) diff --git a/src/www/LEIAME b/src/www/LEIAME new file mode 100644 index 00000000..142bd95a --- /dev/null +++ b/src/www/LEIAME @@ -0,0 +1,71 @@ +Interface web para o UTS (e outros) - v. 0.1a + +procedimento de instalação + ++ descompactação do arquivo + + +Se você está lendo isto, esta tarefa já está concluída. :-) + ++ configuração (uts.inc.php) + + +Há algumas variáveis no arquivo uts.inc.php (o arquivo se encontra +dentro do diretório config) que devem ser editatas **ANTES** de tentar +utilizar a interface. São elas: + + +//UTS_PLATFORM//: "linux" ou "win32", logo farei o código descobrir isso por si só. + +//UTS_DB_DSN//: define a forma de acesso ao banco de dados (somente MySQL +enquanto). A URL é da forma: (os valores entre [] devem ser editados) + +``` "mysql://[usuario_mysql]:[senha_usuario_mysql]@[servidor_mysql]/[bando_de_dados]" + +//UTS_WIN_SCRIPTS_DIR//: local onde os arquivos serão gravados para o Orchestrate. **SOMENTE PARA USO COM O ORCHESTRATE.** + +//UTS_DEFAULT_SERVER//: deve conter o nome do servidor onde o UTS está sendo executado. **SOMENTE PARA O UTS.** + +//UTS_SPMTABLE_PATH//: caminho do programa (o valor padrão costuma funcionar). **SOMENTE PARA O UTS.** + + ++ criação do banco de dados + + +No diretório 'db' há um arquivo chamado 'db-schema.sql'. Este arquivo +contém as tabelas usadas pelo sistema. O arquivo está escrito na +linguagem SQL. Para criar as tabelas no MySQL você pode usar o +PHPMyAdmin, que facilitará muito sua vida (não só neste momento). A +instalação do PHPMyAdmin é muito simples, e uma vez feita, você poderá +executar arquivos SQL diretamente em um banco de dados. Ou, usar o +utilitário mysql (cliente de acesso do MySQL) para executar o arquivo +SQL. + +Mas, é **EXTREMAMENTE** recomendado, que você use o PHPMyAdmin caso seja +iniciante no MySQL. + +O PHPMyAdmin pode ser obtido em www.phpmyadmin.net + + +++ (quase) o fim. ++ + +Pronto!!! Basta acessar a URL do seu servidor web, +ex. www.observatorio.ufsc.br e entrar no diretório onde foi +instalada a interface, ex www.observatorio.ufsc.br/uts. + + ++ primeiro acesso + + +Ao acessar a tela inicial da interface, haverá um formulário para +login. Para criar um usuário é necessário acessar a tela de +administração (ex. www.observatorio.ufsc.br/uts/admin). No primeiro +acesso a esta página, será cadastrado um usuário e uma senha, que +servirão de proteção para acessos indevidos à página de +admnistração. Após cadastrar este usuário, basta adicionar +observadores, voltar a tela de login de usuários, +ex. www.observatorio.ufsc.br/uts e iniciar a observação. + + += dúvidas = + +Sugestões e dúvidas, email para [pH henrique@astro.ufsc.br], ou consulte o +código fonte. (vantagens do software livre :-) + +(copyleft) 2003-2005 - P. Henrique Silva diff --git a/src/www/LEIAME.html b/src/www/LEIAME.html new file mode 100644 index 00000000..80fff7c2 --- /dev/null +++ b/src/www/LEIAME.html @@ -0,0 +1,111 @@ + + + + +Interface web para o UTS (e outros) - v. 0.1a + +

Interface web para o UTS (e outros) - v. 0.1a

+ +procedimento de instalação +
+ +

+
+

+ + +

+
+

+ +

1. descompactação do arquivo

+

+Se você está lendo isto, esta tarefa já está concluída. :-) +

+ +

2. configuração (uts.inc.php)

+

+Há algumas variáveis no arquivo uts.inc.php (o arquivo se encontra +dentro do diretório config) que devem ser editatas ANTES de tentar +utilizar a interface. São elas: +

+

+UTS_PLATFORM: "linux" ou "win32", logo farei o código descobrir isso por si só. +

+

+UTS_DB_DSN: define a forma de acesso ao banco de dados (somente MySQL +enquanto). A URL é da forma: (os valores entre [] devem ser editados) +

+
+  "mysql://[usuario_mysql]:[senha_usuario_mysql]@[servidor_mysql]/[bando_de_dados]"
+
+

+

+UTS_WIN_SCRIPTS_DIR: local onde os arquivos serão gravados para o Orchestrate. SOMENTE PARA USO COM O ORCHESTRATE. +

+

+UTS_DEFAULT_SERVER: deve conter o nome do servidor onde o UTS está sendo executado. SOMENTE PARA O UTS. +

+

+UTS_SPMTABLE_PATH: caminho do programa (o valor padrão costuma funcionar). SOMENTE PARA O UTS. +

+ +

3. criação do banco de dados

+

+No diretório 'db' há um arquivo chamado 'db-schema.sql'. Este arquivo +contém as tabelas usadas pelo sistema. O arquivo está escrito na +linguagem SQL. Para criar as tabelas no MySQL você pode usar o +PHPMyAdmin, que facilitará muito sua vida (não só neste momento). A +instalação do PHPMyAdmin é muito simples, e uma vez feita, você poderá +executar arquivos SQL diretamente em um banco de dados. Ou, usar o +utilitário mysql (cliente de acesso do MySQL) para executar o arquivo +SQL. +

+

+Mas, é EXTREMAMENTE recomendado, que você use o PHPMyAdmin caso seja +iniciante no MySQL. +

+

+O PHPMyAdmin pode ser obtido em www.phpmyadmin.net +

+ +

3.1. (quase) o fim.

+

+Pronto!!! Basta acessar a URL do seu servidor web, +ex. www.observatorio.ufsc.br e entrar no diretório onde foi +instalada a interface, ex www.observatorio.ufsc.br/uts. +

+ +

4. primeiro acesso

+

+Ao acessar a tela inicial da interface, haverá um formulário para +login. Para criar um usuário é necessário acessar a tela de +administração (ex. www.observatorio.ufsc.br/uts/admin). No primeiro +acesso a esta página, será cadastrado um usuário e uma senha, que +servirão de proteção para acessos indevidos à página de +admnistração. Após cadastrar este usuário, basta adicionar +observadores, voltar a tela de login de usuários, +ex. www.observatorio.ufsc.br/uts e iniciar a observação. +

+ +

dúvidas

+

+Sugestões e dúvidas, email para pH, ou consulte o +código fonte. (vantagens do software livre :-) +

+

+(copyleft) 2003-2005 - P. Henrique Silva +

+ + + + diff --git a/src/www/LICENSE b/src/www/LICENSE new file mode 100644 index 00000000..5b6e7c66 --- /dev/null +++ b/src/www/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/src/www/admin/change-pass.php b/src/www/admin/change-pass.php new file mode 100644 index 00000000..97602543 --- /dev/null +++ b/src/www/admin/change-pass.php @@ -0,0 +1,102 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>query($sql); + + Header("Location: " . getMsg("home.php", "Senha alterada com sucesso.")); + +} + +?> + + + + +Observatórios Virtuais ::: Entrar + + + + + + + + +


+
+

+
"> + + + + + + + + +
Nova senha
+
+ + + diff --git a/src/www/admin/home.php b/src/www/admin/home.php new file mode 100644 index 00000000..c779baee --- /dev/null +++ b/src/www/admin/home.php @@ -0,0 +1,117 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>query($sql); + +?> + + + + +Telescópios na Escola ::: Administração +::: Página principal + + + + + + +
Telescópios na Escola
+
+
+
+ + + + + + + + +numRows()) { + + $i = 0; + while($res->fetchInto($data, DB_FETCHMODE_ASSOC)) { + + if($i % 2) { + +?> + + + + + + + + + + +
NomeUsuárioOpções
+ +">Editar | `');"> +Remover
Nenhum usuário +cadastrado.
+ + + diff --git a/src/www/admin/index.php b/src/www/admin/index.php new file mode 100644 index 00000000..06810e63 --- /dev/null +++ b/src/www/admin/index.php @@ -0,0 +1,215 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>query($sql); + +if(!$res->numRows()) { + Header("Location: setup.php"); + exit(0); +} + + +// start +session_start(); + +if($_SESSION['admin']) { + Header("Location: home.php"); + exit(0); +} + + +?> + + + + +Telescópios na Escola ::: Administração ::: +Entrada + + + + + + + + +
Telescópios na Escola
+
+
+ +
+ + + + + + + + + + + + + +
usuário
senha
 
+
+
+
+
+ + + + + + + + + +
+
+"INPE"
+INPE
+
+
+"UFRGS"
+UFRGS
+
+
+"UFRJ"
+UFRJ
+
+
+"UFRN"
+UFRN
+
+
+"UFSC"
+UFSC
+
+
+"USP"
+USP
+
+ + + + +
+

+APOIO:
+
+ + + + + + + + + +
+
+
  +
VITAE
+
+
CNPQ
+
  
+ + + +" . get_include_path() . "
P=fz_-mcH3UgVur|?fOnA5z4U*{iR;a8DI3lx5U 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 +# + +set -e + +# Read config file if it is present. +if [ -r /etc/default/uts ]; then + . /etc/default/uts +else + echo "Error: cannot read '/etc/default/uts'. Check yout installation." + exit -1 +fi + +NAME=sbigcam +DESC="UTS CCD Camera drivers" +SCRIPTNAME=/etc/init.d/$NAME + +# Gracefully exit if the package has been removed. +test -x $UTS_BIN/sbig_load_usb_suse || exit 0 +test -x $UTS_BIN/sbig_unload_usb || exit 0 +test -x $UTS_BIN/sbig_load_lpt_suse || exit 0 +test -x $UTS_BIN/sbig_unload_lpt || exit 0 + +# +# Function that starts the daemon/service. +# +d_start() { + start-stop-daemon --start --quiet --exec $UTS_BIN/sbig_load_usb_suse + start-stop-daemon --start --quiet --exec $UTS_BIN/sbig_load_lpt_suse +} + +# +# Function that stops the daemon/service. +# +d_stop() { + start-stop-daemon --stop --quiet --name $UTS_BIN/sbig_unload_lpt + start-stop-daemon --stop --quiet --name $UTS_BIN/sbig_unload_usb +} + +case "$1" in + start) + echo -n "Starting $DESC: $NAME" + d_start + echo "." + ;; + stop) + echo -n "Stopping $DESC: $NAME" + d_stop + echo "." + ;; + + restart) + echo -n "Restarting $DESC: $NAME" + d_stop + sleep 1 + d_start + echo "." + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|restart}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/src/config/initscripts/debian/secd b/src/config/initscripts/debian/secd new file mode 100644 index 00000000..d64610e6 --- /dev/null +++ b/src/config/initscripts/debian/secd @@ -0,0 +1,72 @@ +#! /bin/sh +# +# secd UTS Socket secretaries +# Author: P. Henrique Silva +# + +set -e + +# Read config file if it is present. +if [ -r /etc/default/uts ]; then + . /etc/default/uts +else + echo "Error: cannot read '/etc/default/uts'. Check yout installation." + exit -1 +fi + +NAME=secd +DESC="UTS Socket secretaries" +DAEMON=$UTS_BIN/$NAME +SCRIPTNAME=/etc/init.d/$NAME + +# Gracefully exit if the package has been removed. +test -x $DAEMON || exit 0 + +# +# Function that starts the daemon/service. +# +d_start() { + + for i in `ls -1 $UTS_SEC/*.conf`; do + start-stop-daemon --start --quiet --exec $DAEMON -- -d $i + done + +} + +# +# Function that stops the daemon/service. +# +d_stop() { + + for i in `ls -1 $UTS_SEC/*.conf`; do + start-stop-daemon --stop --quiet --name $NAME + done + +} + +case "$1" in + start) + echo -n "Starting $DESC: $NAME" + d_start + echo "." + ;; + stop) + echo -n "Stopping $DESC: $NAME" + d_stop + echo "." + ;; + + restart) + echo -n "Restarting $DESC: $NAME" + d_stop + sleep 1 + d_start + echo "." + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|restart}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/src/config/initscripts/debian/spmd b/src/config/initscripts/debian/spmd new file mode 100644 index 00000000..531dc369 --- /dev/null +++ b/src/config/initscripts/debian/spmd @@ -0,0 +1,64 @@ +#! /bin/sh +# +# spmd UTS Sockets (Spm) +# Author: P. Henrique Silva +# + +set -e + +# Read config file if it is present. +if [ -r /etc/default/uts ]; then + . /etc/default/uts +else + echo "Error: cannot read '/etc/default/uts'. Check yout installation." + exit -1 +fi + +NAME=Spm +DESC="UTS Sockets (Spm)" +DAEMON=$UTS_BIN/$NAME +SCRIPTNAME=/etc/init.d/$NAME + +# Gracefully exit if the package has been removed. +test -x $DAEMON || exit 0 + +# +# Function that starts the daemon/service. +# +d_start() { + start-stop-daemon --start --quiet --exec $DAEMON +} + +# +# Function that stops the daemon/service. +# +d_stop() { + start-stop-daemon --stop --quiet --name $NAME +} + +case "$1" in + start) + echo -n "Starting $DESC: $NAME" + d_start + echo "." + ;; + stop) + echo -n "Stopping $DESC: $NAME" + d_stop + echo "." + ;; + + restart) + echo -n "Restarting $DESC: $NAME" + d_stop + sleep 1 + d_start + echo "." + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|restart}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/src/config/initscripts/debian/uts.in b/src/config/initscripts/debian/uts.in new file mode 100644 index 00000000..f26731be --- /dev/null +++ b/src/config/initscripts/debian/uts.in @@ -0,0 +1,9 @@ +UTS_DIR=@prefix@ +UTS_BIN=$UTS_DIR/bin +UTS_ETC=/etc/uts +UTS_SEC=$UTS_ETC/sec + +PATH=/bin:/sbin:/usr/bin:/usr/local/bin + +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$UTS_DIR/lib +export PATH=$UTS_BIN:$PATH diff --git a/src/config/initscripts/redhat/Makefile.am b/src/config/initscripts/redhat/Makefile.am new file mode 100644 index 00000000..96316a5a --- /dev/null +++ b/src/config/initscripts/redhat/Makefile.am @@ -0,0 +1,6 @@ +include $(top_srcdir)/rules.make + +dist_uts_initd_SCRIPTS = sbigcam secd spmd +dist_uts_initconfig_DATA = uts + +EXTRA_DIST = uts.in diff --git a/src/config/initscripts/redhat/sbigcam b/src/config/initscripts/redhat/sbigcam new file mode 100644 index 00000000..f7433ce5 --- /dev/null +++ b/src/config/initscripts/redhat/sbigcam @@ -0,0 +1,56 @@ +#!/bin/bash +# +# /etc/rc.d/init.d/sbigcam +# +# Loads camera modules. +# +# chkconfig: 3 40 62 +# description: Loads SBIG proprietary modules for ST-7 cameras. +# processname: sbigcam + +# Source function library. +. /etc/rc.d/init.d/functions + +. /etc/sysconfig/uts + +test -x $UTS_BIN/sbig_load_usb || exit 0 +test -x $UTS_BIN/sbig_unload_usb || exit 0 +test -x $UTS_BIN/sbig_load_lpt || exit 0 +test -x $UTS_BIN/sbig_unload_lpt || exit 0 + +RETVAL=0 + +# +# See how we were called. +# +case "$1" in + start) + # Check if modules are already loaded + if [ ! -f /var/lock/subsys/sbigcam ]; then + echo 'Loading SBIG proprietary ST-7E camera modules:' + $UTS_BIN/sbig_load_usb + $UTS_BIN/sbig_load_lpt + RETVAL=$? + [ $RETVAL -eq 0 ] && touch /var/lock/subsys/sbigcam + echo + fi + ;; + stop) + echo 'Unloading SBIG proprietary ST-7E camera modules:' + $UTS_BIN/sbig_unload_usb + $UTS_BIN/sbig_unload_lpt + RETVAL=$? + [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/sbigcam + echo + ;; + reload|restart) + $0 stop + $0 start + RETVAL=$? + ;; + *) + echo "Usage: /etc/rc.d/init.d/spmd {start|stop|restart|reload}" + exit 1 +esac + +exit $RETVAL diff --git a/src/config/initscripts/redhat/secd b/src/config/initscripts/redhat/secd new file mode 100644 index 00000000..d6a12801 --- /dev/null +++ b/src/config/initscripts/redhat/secd @@ -0,0 +1,52 @@ +#!/bin/bash +# +# /etc/rc.d/init.d/secd +# +# Starts the Socket Secretary daemon +# +# chkconfig: 3 41 61 +# description: Run socket secretaries for each instrument. + +# Source function library. +. /etc/rc.d/init.d/functions + +. /etc/sysconfig/uts + +test -x $UTS_BIN/sec || exit 0 + +RETVAL=0 + +# +# See how we were called. +# +case "$1" in + start) + echo -n 'Starting Socket Secretary daemons... ' + #daemon $UTS_BIN/runsecs.sh + for INST in `ls -1 $UTS_SEC/*.conf`; do + daemon $UTS_BIN/sec -d $INST; + done + touch /var/lock/subsys/secd + echo + ;; + stop) + echo -n 'Stopping Socket Secretary daemon: ' + killproc sec + rm -f /var/lock/subsys/secd + echo + ;; + reload|restart) + $0 stop + $0 start + RETVAL=$? + ;; + status) + status $UTS_BIN/sec + RETVAL=$? + ;; + *) + echo "Usage: /etc/rc.d/init.d/secd {start|stop|restart|reload|status}" + exit 1 +esac + +exit $RETVAL diff --git a/src/config/initscripts/redhat/spmd b/src/config/initscripts/redhat/spmd new file mode 100644 index 00000000..df0fa3d2 --- /dev/null +++ b/src/config/initscripts/redhat/spmd @@ -0,0 +1,57 @@ +#!/bin/bash +# +# /etc/rc.d/init.d/spmd +# +# Starts the Spm daemon +# +# chkconfig: 4 40 62 +# description: Spm is the Simple Sockets Library (SSL) +# PortMaster. It controls server names and their port numbers. +# processname: Spm + +# Source function library. +. /etc/rc.d/init.d/functions + +. /etc/sysconfig/uts + +test -x $UTS_BIN/Spm || exit 0 + +RETVAL=0 + +# +# See how we were called. +# +case "$1" in + start) + # Check if Spm is already running + if [ ! -f /var/lock/subsys/Spm ]; then + echo -n 'Starting Spm daemon: ' + daemon $UTS_BIN/Spm + RETVAL=$? + [ $RETVAL -eq 0 ] && touch /var/lock/subsys/Spm + echo + sleep 2 + fi + ;; + stop) + echo -n 'Stopping Spm daemon: ' + killproc $UTS_BIN/Spm + RETVAL=$? + [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/Spm + echo + ;; + reload|restart) + $0 stop + $0 start + RETVAL=$? + ;; + status) + status $UTS_BIN/Spm + RETVAL=$? + ;; + *) + echo "Usage: /etc/rc.d/init.d/spmd {start|stop|restart|reload|status}" + exit 1 +esac + +exit $RETVAL diff --git a/src/config/initscripts/redhat/uts.in b/src/config/initscripts/redhat/uts.in new file mode 100644 index 00000000..f26731be --- /dev/null +++ b/src/config/initscripts/redhat/uts.in @@ -0,0 +1,9 @@ +UTS_DIR=@prefix@ +UTS_BIN=$UTS_DIR/bin +UTS_ETC=/etc/uts +UTS_SEC=$UTS_ETC/sec + +PATH=/bin:/sbin:/usr/bin:/usr/local/bin + +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$UTS_DIR/lib +export PATH=$UTS_BIN:$PATH diff --git a/src/config/initscripts/suse/Makefile.am b/src/config/initscripts/suse/Makefile.am new file mode 100644 index 00000000..84cedf0f --- /dev/null +++ b/src/config/initscripts/suse/Makefile.am @@ -0,0 +1,7 @@ +include $(top_srcdir)/rules.make + +dist_uts_initd_SCRIPTS = sbigcam secd spmd +dist_uts_initconfig_DATA = uts + +EXTRA_DIST = uts.in + diff --git a/src/config/initscripts/suse/sbigcam b/src/config/initscripts/suse/sbigcam new file mode 100644 index 00000000..31cc5921 --- /dev/null +++ b/src/config/initscripts/suse/sbigcam @@ -0,0 +1,146 @@ +#! /bin/sh +# Copyright (c) 2005 P. Henrique Silva +# +# Based on skeleton +# Copyright (c) 1995-2004 SUSE Linux AG, Nuernberg, Germany. +# All rights reserved. +# Author: Kurt Garloff +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# LSB compatible service control script; see http://www.linuxbase.org/spec/ +# +# Note: This template uses functions rc_XXX defined in /etc/rc.status on +# UnitedLinux (UL) based Linux distributions. If you want to base your +# script on this template and ensure that it works on non UL based LSB +# compliant Linux distributions, you either have to provide the rc.status +# functions from UL or change the script to work without them. +# +### BEGIN INIT INFO +# Provides: sbigcam +# Default-Start: 3 5 +# Default-Stop: 0 1 2 6 +# Short-Description: UTS SBIG kernel modules +# Description: Start UTS SBIG kernel modules +### END INIT INFO + + +# Check for existence of needed config file and read it +UTS_CONFIG=/etc/sysconfig/uts +test -r $UTS_CONFIG || { echo "$UTS_CONFIG not existing"; + if [ "$1" = "stop" ]; then exit 0; + else exit 6; fi; } + +# Read config +. $UTS_CONFIG + +for i in `ls -1 $UTS_BIN/sbig_*`; do + test -x $i || { echo "$i not installed"; + if [ "$1" = "stop" ]; then exit 0; + else exit 5; fi; } +done + +# Source LSB init functions +# providing start_daemon, killproc, pidofproc, +# log_success_msg, log_failure_msg and log_warning_msg. +# This is currently not used by UnitedLinux based distributions and +# not needed for init scripts for UnitedLinux only. If it is used, +# the functions from rc.status should not be sourced or used. +#. /lib/lsb/init-functions + +# Shell functions sourced from /etc/rc.status: +# rc_check check and set local and overall rc status +# rc_status check and set local and overall rc status +# rc_status -v be verbose in local rc status and clear it afterwards +# rc_status -v -r ditto and clear both the local and overall rc status +# rc_status -s display "skipped" and exit with status 3 +# rc_status -u display "unused" and exit with status 3 +# rc_failed set local and overall rc status to failed +# rc_failed set local and overall rc status to +# rc_reset clear both the local and overall rc status +# rc_exit exit appropriate to overall rc status +# rc_active checks whether a service is activated by symlinks +. /etc/rc.status + +# Reset status of this service +rc_reset + +# Return values acc. to LSB for all commands but status: +# 0 - success +# 1 - generic or unspecified error +# 2 - invalid or excess argument(s) +# 3 - unimplemented feature (e.g. "reload") +# 4 - user had insufficient privileges +# 5 - program is not installed +# 6 - program is not configured +# 7 - program is not running +# 8--199 - reserved (8--99 LSB, 100--149 distrib, 150--199 appl) +# +# Note that starting an already running service, stopping +# or restarting a not-running service as well as the restart +# with force-reload (in case signaling is not supported) are +# considered a success. + +case "$1" in + start) + echo -n "Starting sbigcam " + ## Start daemon with startproc(8). If this fails + ## the return value is set appropriately by startproc. + startproc $UTS_BIN/sbig_load_usb_suse + + # Remember status and be verbose + rc_status -v + ;; + stop) + echo -n "Shutting down sbigcam " + ## Stop daemon with killproc(8) and if this fails + ## killproc sets the return value according to LSB. + + $UTS_BIN/sbig_unload_usb + + # Remember status and be verbose + rc_status -v + ;; + try-restart|condrestart) + ## Do a restart only if the service was active before. + ## Note: try-restart is now part of LSB (as of 1.9). + ## RH has a similar command named condrestart. + if test "$1" = "condrestart"; then + echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}" + fi + $0 status + if test $? = 0; then + $0 restart + else + rc_reset # Not running is not a failure. + fi + # Remember status and be quiet + rc_status + ;; + restart) + ## Stop the service and regardless of whether it was + ## running or not, start it again. + $0 stop + $0 start + + # Remember status and be quiet + rc_status + ;; + *) + echo "Usage: $0 {start|stop|restart}" + exit 1 + ;; +esac +rc_exit diff --git a/src/config/initscripts/suse/secd b/src/config/initscripts/suse/secd new file mode 100644 index 00000000..9ec04150 --- /dev/null +++ b/src/config/initscripts/suse/secd @@ -0,0 +1,208 @@ +#! /bin/sh +# Copyright (c) 2005 P. Henrique Silva +# +# Based on skeleton +# Copyright (c) 1995-2004 SUSE Linux AG, Nuernberg, Germany. +# All rights reserved. +# Author: Kurt Garloff +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# LSB compatible service control script; see http://www.linuxbase.org/spec/ +# +# Note: This template uses functions rc_XXX defined in /etc/rc.status on +# UnitedLinux (UL) based Linux distributions. If you want to base your +# script on this template and ensure that it works on non UL based LSB +# compliant Linux distributions, you either have to provide the rc.status +# functions from UL or change the script to work without them. +# +### BEGIN INIT INFO +# Provides: secd +# Required-Start: spmd +# Required-Stop: teld camd +# Default-Start: 3 5 +# Default-Stop: 0 1 2 6 +# Short-Description: UTS Secretary daemon +# Description: Start UTS Secretary daemon +### END INIT INFO + + +# Check for existence of needed config file and read it +UTS_CONFIG=/etc/sysconfig/uts +test -r $UTS_CONFIG || { echo "$UTS_CONFIG not existing"; + if [ "$1" = "stop" ]; then exit 0; + else exit 6; fi; } + +# Read config +. $UTS_CONFIG + +SECD_BIN=$UTS_BIN/sec +test -x $SECD_BIN || { echo "$SECD_BIN not installed"; + if [ "$1" = "stop" ]; then exit 0; + else exit 5; fi; } + +GENCONF_BIN=$UTS_BIN/uts-gen-conf +test -x $GENCONF_BIN || { echo "$GENCONF_BIN not installed"; + if [ "$1" = "stop" ]; then exit 0; + else exit 5; fi; } + +# Source LSB init functions +# providing start_daemon, killproc, pidofproc, +# log_success_msg, log_failure_msg and log_warning_msg. +# This is currently not used by UnitedLinux based distributions and +# not needed for init scripts for UnitedLinux only. If it is used, +# the functions from rc.status should not be sourced or used. +#. /lib/lsb/init-functions + +# Shell functions sourced from /etc/rc.status: +# rc_check check and set local and overall rc status +# rc_status check and set local and overall rc status +# rc_status -v be verbose in local rc status and clear it afterwards +# rc_status -v -r ditto and clear both the local and overall rc status +# rc_status -s display "skipped" and exit with status 3 +# rc_status -u display "unused" and exit with status 3 +# rc_failed set local and overall rc status to failed +# rc_failed set local and overall rc status to +# rc_reset clear both the local and overall rc status +# rc_exit exit appropriate to overall rc status +# rc_active checks whether a service is activated by symlinks +. /etc/rc.status + +# Reset status of this service +rc_reset + +# Return values acc. to LSB for all commands but status: +# 0 - success +# 1 - generic or unspecified error +# 2 - invalid or excess argument(s) +# 3 - unimplemented feature (e.g. "reload") +# 4 - user had insufficient privileges +# 5 - program is not installed +# 6 - program is not configured +# 7 - program is not running +# 8--199 - reserved (8--99 LSB, 100--149 distrib, 150--199 appl) +# +# Note that starting an already running service, stopping +# or restarting a not-running service as well as the restart +# with force-reload (in case signaling is not supported) are +# considered a success. + +case "$1" in + start) + echo -n "Starting secd " + ## Start daemon with startproc(8). If this fails + ## the return value is set appropriately by startproc. + + # update .conf files + $GENCONF_BIN + + for i in `ls -1 $UTS_SEC/*.conf`; do + startproc -f -q $SECD_BIN -d $i + done + + # Remember status and be verbose + rc_status -v + ;; + stop) + echo -n "Shutting down secd " + ## Stop daemon with killproc(8) and if this fails + ## killproc sets the return value according to LSB. + + for i in `ls -1 $UTS_SEC/*.conf`; do + killproc -TERM $SECD_BIN + done + + # Remember status and be verbose + rc_status -v + ;; + try-restart|condrestart) + ## Do a restart only if the service was active before. + ## Note: try-restart is now part of LSB (as of 1.9). + ## RH has a similar command named condrestart. + if test "$1" = "condrestart"; then + echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}" + fi + $0 status + if test $? = 0; then + $0 restart + else + rc_reset # Not running is not a failure. + fi + # Remember status and be quiet + rc_status + ;; + restart) + ## Stop the service and regardless of whether it was + ## running or not, start it again. + $0 stop + $0 start + + # Remember status and be quiet + rc_status + ;; + force-reload) + ## Signal the daemon to reload its config. Most daemons + ## do this on signal 1 (SIGHUP). + ## If it does not support it, restart. + + echo -n "Reload service secd " + ## if it supports it: + killproc -HUP $SECD_BIN + #touch /var/run/secd.pid + rc_status -v + + ## Otherwise: + #$0 try-restart + #rc_status + ;; + reload) + ## Like force-reload, but if daemon does not support + ## signaling, do nothing (!) + + # If it supports signaling: + echo -n "Reload service secd " + killproc -HUP $SECD_BIN + #touch /var/run/secd.pid + rc_status -v + + ## Otherwise if it does not support reload: + #rc_failed 3 + #rc_status -v + ;; + status) + echo -n "Checking for service secd " + ## Check status with checkproc(8), if process is running + ## checkproc will return with exit status 0. + + # Return value is slightly different for the status command: + # 0 - service up and running + # 1 - service dead, but /var/run/ pid file exists + # 2 - service dead, but /var/lock/ lock file exists + # 3 - service not running (unused) + # 4 - service status unknown :-( + # 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.) + + # NOTE: checkproc returns LSB compliant status values. + checkproc $SECD_BIN + # NOTE: rc_status knows that we called this init script with + # "status" option and adapts its messages accordingly. + rc_status -v + ;; + *) + echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload}" + exit 1 + ;; +esac +rc_exit diff --git a/src/config/initscripts/suse/spmd b/src/config/initscripts/suse/spmd new file mode 100644 index 00000000..a8a4eaa7 --- /dev/null +++ b/src/config/initscripts/suse/spmd @@ -0,0 +1,195 @@ +#! /bin/sh +# Copyright (c) 2005 P. Henrique Silva +# +# Based on skeleton +# Copyright (c) 1995-2004 SUSE Linux AG, Nuernberg, Germany. +# All rights reserved. +# Author: Kurt Garloff +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# LSB compatible service control script; see http://www.linuxbase.org/spec/ +# +# Note: This template uses functions rc_XXX defined in /etc/rc.status on +# UnitedLinux (UL) based Linux distributions. If you want to base your +# script on this template and ensure that it works on non UL based LSB +# compliant Linux distributions, you either have to provide the rc.status +# functions from UL or change the script to work without them. +# +### BEGIN INIT INFO +# Provides: spmd +# Required-Stop: secd +# Default-Start: 3 5 +# Default-Stop: 0 1 2 6 +# Short-Description: UTS sockets daemon +# Description: Start UTS sockets daemon +### END INIT INFO + + +# Check for existence of needed config file and read it +UTS_CONFIG=/etc/sysconfig/uts +test -r $UTS_CONFIG || { echo "$UTS_CONFIG not existing"; + if [ "$1" = "stop" ]; then exit 0; + else exit 6; fi; } + +# Read config +. $UTS_CONFIG + +SPMD_BIN=$UTS_BIN/Spm +test -x $SPMD_BIN || { echo "$SPMD_BIN not installed"; + if [ "$1" = "stop" ]; then exit 0; + else exit 5; fi; } + + +# Source LSB init functions +# providing start_daemon, killproc, pidofproc, +# log_success_msg, log_failure_msg and log_warning_msg. +# This is currently not used by UnitedLinux based distributions and +# not needed for init scripts for UnitedLinux only. If it is used, +# the functions from rc.status should not be sourced or used. +#. /lib/lsb/init-functions + +# Shell functions sourced from /etc/rc.status: +# rc_check check and set local and overall rc status +# rc_status check and set local and overall rc status +# rc_status -v be verbose in local rc status and clear it afterwards +# rc_status -v -r ditto and clear both the local and overall rc status +# rc_status -s display "skipped" and exit with status 3 +# rc_status -u display "unused" and exit with status 3 +# rc_failed set local and overall rc status to failed +# rc_failed set local and overall rc status to +# rc_reset clear both the local and overall rc status +# rc_exit exit appropriate to overall rc status +# rc_active checks whether a service is activated by symlinks +. /etc/rc.status + +# Reset status of this service +rc_reset + +# Return values acc. to LSB for all commands but status: +# 0 - success +# 1 - generic or unspecified error +# 2 - invalid or excess argument(s) +# 3 - unimplemented feature (e.g. "reload") +# 4 - user had insufficient privileges +# 5 - program is not installed +# 6 - program is not configured +# 7 - program is not running +# 8--199 - reserved (8--99 LSB, 100--149 distrib, 150--199 appl) +# +# Note that starting an already running service, stopping +# or restarting a not-running service as well as the restart +# with force-reload (in case signaling is not supported) are +# considered a success. + +case "$1" in + start) + echo -n "Starting spmd " + ## Start daemon with startproc(8). If this fails + ## the return value is set appropriately by startproc. + startproc $SPMD_BIN &> /dev/null + + # Remember status and be verbose + rc_status -v + ;; + stop) + echo -n "Shutting down spmd " + ## Stop daemon with killproc(8) and if this fails + ## killproc sets the return value according to LSB. + + killproc -TERM $SPMD_BIN + + # Remember status and be verbose + rc_status -v + ;; + try-restart|condrestart) + ## Do a restart only if the service was active before. + ## Note: try-restart is now part of LSB (as of 1.9). + ## RH has a similar command named condrestart. + if test "$1" = "condrestart"; then + echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}" + fi + $0 status + if test $? = 0; then + $0 restart + else + rc_reset # Not running is not a failure. + fi + # Remember status and be quiet + rc_status + ;; + restart) + ## Stop the service and regardless of whether it was + ## running or not, start it again. + $0 stop + $0 start + + # Remember status and be quiet + rc_status + ;; + force-reload) + ## Signal the daemon to reload its config. Most daemons + ## do this on signal 1 (SIGHUP). + ## If it does not support it, restart. + + echo -n "Reload service spmd " + ## if it supports it: + killproc -HUP $SPMD_BIN + #touch /var/run/spmd.pid + rc_status -v + + ## Otherwise: + #$0 try-restart + #rc_status + ;; + reload) + ## Like force-reload, but if daemon does not support + ## signaling, do nothing (!) + + # If it supports signaling: + echo -n "Reload service spmd " + killproc -HUP $SPMD_BIN + #touch /var/run/spmd.pid + rc_status -v + + ## Otherwise if it does not support reload: + #rc_failed 3 + #rc_status -v + ;; + status) + echo -n "Checking for service spmd " + ## Check status with checkproc(8), if process is running + ## checkproc will return with exit status 0. + + # Return value is slightly different for the status command: + # 0 - service up and running + # 1 - service dead, but /var/run/ pid file exists + # 2 - service dead, but /var/lock/ lock file exists + # 3 - service not running (unused) + # 4 - service status unknown :-( + # 5--199 reserved (5--99 LSB, 100--149 distro, 150--199 appl.) + + # NOTE: checkproc returns LSB compliant status values. + checkproc $SPMD_BIN + # NOTE: rc_status knows that we called this init script with + # "status" option and adapts its messages accordingly. + rc_status -v + ;; + *) + echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload}" + exit 1 + ;; +esac +rc_exit diff --git a/src/config/initscripts/suse/uts.in b/src/config/initscripts/suse/uts.in new file mode 100644 index 00000000..d05f62ab --- /dev/null +++ b/src/config/initscripts/suse/uts.in @@ -0,0 +1,11 @@ +UTS_DIR=@prefix@ +UTS_BIN=$UTS_DIR/bin +UTS_ETC=/etc/uts +UTS_SEC=$UTS_ETC/sec + +PYTHON=@PYTHON@ + +PATH=/bin:/sbin:/usr/bin:/usr/local/bin + +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$UTS_DIR/lib +export PATH=$UTS_BIN:$PATH diff --git a/src/config/rpc.conf b/src/config/rpc.conf new file mode 100644 index 00000000..e69de29b diff --git a/src/config/site.xml b/src/config/site.xml new file mode 100644 index 00000000..f4f643fb --- /dev/null +++ b/src/config/site.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/drivers/Makefile.am b/src/drivers/Makefile.am new file mode 100644 index 00000000..ff379002 --- /dev/null +++ b/src/drivers/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = camfits.fake camfits.st4 camfits.st7 telgo.fake telgo.lx200 telgo.paramount weather.wx200 + diff --git a/src/drivers/camfits.fake/Makefile.am b/src/drivers/camfits.fake/Makefile.am new file mode 100644 index 00000000..6ca6a9df --- /dev/null +++ b/src/drivers/camfits.fake/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/rules.make + +bin_PROGRAMS = camfits.fake + +camfits_fake_SOURCES = ccd.c ccd.h errors.h main.c ser.c ser.h + +camfits_fake_LDADD = -lm -lcfitsio + +uts_driver_DATA = fake.camera.xml diff --git a/src/drivers/camfits.fake/ccd.c b/src/drivers/camfits.fake/ccd.c new file mode 100644 index 00000000..042a8dde --- /dev/null +++ b/src/drivers/camfits.fake/ccd.c @@ -0,0 +1,465 @@ +#include +#include "errors.h" +#include "ccd.h" + + +#undef CCDTEST +#ifdef CCDTEST + +char minhatty[] = "/dev/ttyS1"; + +cam_info *camera; + +int main() +{ + int i; + + puts("Programa para teste do CCD."); + printf("tty = %s\n\n", minhatty); + + camera = alloc_camera(minhatty); + if (camera == NULL) { + puts("alloc_camera()"); + exit(1); + } + + camera->ccd->baudrate = CAM_B57600; + camera->ccd->rom_version = ROM_VERSION; + if (init_camera(camera) != ERR_OK) { + puts("init_camera()"); + exit(0); + } + + camera->ccd->exptime = 5; + camera->ccd->first_line = CAM_FIRST_LINE; + camera->ccd->first_column = 0; + camera->ccd->nlines = CAM_MAX_NLINES; + camera->ccd->ncolumns = CAM_MAX_NCOLS; + camera->ccd->offset = 0; + camera->ccd->vref_plus = 255; + camera->ccd->vref_minus = 0; + camera->ccd->format_flag = 0; + camera->ccd->mode_flag = (CAM_MODE_FEXP | CAM_MODE_LA); + set_camera(camera); + take_image(camera, camera->ccd->image); + + free_camera(camera); +} +#endif + + +int init_camera(cam_info *camera) +{ + int res; + + if (!is_valid_camera(camera)) + return(ERR_CAM); + + camera->serial->waitread = TRUE; + camera->serial->baudrate = B9600; + camera->serial->parity = PAR_EVEN; + camera->serial->stopbits = 1; + camera->serial->minchars = 0; + camera->serial->timeout = 5; + set_serial(camera->serial); + if (camera->serial->err_code != ERR_OK) + return(ERR_SERIAL); + + res = set_baudrate(camera, camera->ccd->baudrate); + return(res); + +} + + +int is_valid_camera(cam_info *camera) +{ + if (camera == NULL) + return(FALSE); + if (camera->ccd == NULL) + return(FALSE); + if (camera->serial == NULL) + return(FALSE); + return(TRUE); +} + + +cam_info *alloc_camera(char *ttyname) +{ + cam_info *camera; + int i; + + camera = (cam_info *) calloc(sizeof(cam_info), 1); + if (camera == NULL) + return(NULL); + camera->serial = (ser_info *) calloc(sizeof(ser_info), 1); + if (camera->serial == NULL) + return(NULL); + camera->ccd = (ccd_info *) calloc(sizeof(ccd_info), 1); + if (camera->ccd == NULL) + return(NULL); + open_serial(camera->serial, ttyname); + return(camera); +} + + +void free_camera(cam_info *camera) +{ + if (camera == NULL) + return; + if (camera->serial != NULL) { + if (camera->serial->ttyfd > 0) { + set_baudrate(camera, CAM_B9600); + close_serial(camera->serial); + } + free(camera->serial); + } + if (camera->ccd != NULL) + free(camera->ccd); + free(camera); +} + + +int set_camera(cam_info *camera) +{ + long int exptmp; + int res; + cmd_t cmd; + + if (!is_valid_camera(camera)) + return(ERR_CAM); +/* Set modes and format */ + cmd.nbytes = 2; + cmd.address = CAM_MODE; + cmd.ram = CAM_INT_RAM; + camera->ccd->mode_flag &= ~CAM_MODE_STEXP; + cmd.data[0] = camera->ccd->mode_flag; + cmd.data[1] = camera->ccd->format_flag; + res = write_mem(camera, &cmd); + if (res != ERR_OK) + return(ERR_WRITE); + +/* Set exposure time */ +/* cmd.nbytes = 2; + cmd.address = CAM_EXPTIME; + exptmp = camera->ccd->exptime * 100; + cmd.data[0] = exptmp & 0x00FF; + cmd.data[1] = (exptmp >> 8) & 0x00FF; + res = write_mem(camera, &cmd); + if (res != ERR_OK) + return(ERR_WRITE); */ + +/* Set first pixel and number of pixels per line */ + cmd.nbytes = 2; + cmd.address = CAM_XPOINTER; + cmd.data[0] = camera->ccd->first_column; + cmd.data[1] = camera->ccd->ncolumns; + res = write_mem(camera, &cmd); + if (res != ERR_OK) + return(ERR_WRITE); + +/* Set additional offset and Vref+ */ + cmd.nbytes = 2; + cmd.address = CAM_OFFSET; + cmd.data[0] = camera->ccd->offset; + cmd.data[1] = camera->ccd->vref_plus; + res = write_mem(camera, &cmd); + if (res != ERR_OK) + return(ERR_WRITE); + +/* Set Vref- */ + cmd.nbytes = 1; + cmd.address = CAM_VREFMINUS; + cmd.data[0] = camera->ccd->vref_minus; + res = write_mem(camera, &cmd); + if (res != ERR_OK) + return(ERR_WRITE); + +return(ERR_OK); +} + + +int write_mem(cam_info *camera, cmd_t *cmd) +{ + unsigned char buf[CAM_MAX_CMD], *pbuf, chksum, tmp; + int i, res; + + if (!is_valid_camera(camera)) + return(ERR_CAM); + if (cmd == NULL) + return(ERR_CMD); + + pbuf = buf; + *pbuf++ = CAM_WRITE_MEM; + chksum = CAM_WRITE_MEM; + tmp = cmd->nbytes + 3; + *pbuf++ = tmp; + chksum += tmp; + *pbuf++ = cmd->ram; + chksum += cmd->ram; + tmp = cmd->address & 0x00FF; + *pbuf++ = tmp; + chksum += tmp; + tmp = (cmd->address >> 8) & 0x00FF; + *pbuf++ = tmp; + chksum += tmp; + + for (i = 0; i < (cmd->nbytes); i++) { + *pbuf++ = cmd->data[i]; + chksum += cmd->data[i]; + } + *pbuf = chksum; + if (write_serial(camera->serial, buf, (6 + cmd->nbytes)) == ERR_WRITE) + return(ERR_WRITE); + res = wait_response(camera); + if (res == CAM_NAK) + return(ERR_READ); + return(ERR_OK); +} + + +unsigned char wait_response(cam_info *camera) +{ + unsigned char c; + int res; + + if (!is_valid_camera(camera)) + return; + + if (read_serial(camera->serial, &c, 1) == ERR_READ) + return(CAM_NAK); + if (c != CAM_ACK) + return(CAM_NAK); + return(CAM_ACK); +} + + +int read_mem(cam_info *camera, cmd_t *cmd) +{ + unsigned char buf[CAM_MAX_CMD], *pbuf, tmp, chksum; + int res, i; + + if (!is_valid_camera(camera)) + return(ERR_CAM); + if (cmd == NULL) + return(ERR_CMD); +/* Set command in buffer */ + pbuf = buf; + *pbuf++ = CAM_READ_MEM; + chksum = CAM_READ_MEM; + *pbuf++ = cmd->nbytes; + chksum += cmd->nbytes; + *pbuf++ = cmd->ram; + chksum += cmd->ram; + tmp = cmd->address & 0x00FF; + *pbuf++ = tmp; + chksum += tmp; + tmp = (cmd->address >> 8) & 0x00FF; + *pbuf++ = tmp; + chksum += tmp; + *pbuf = chksum; + res = write_serial(camera->serial, buf, 6); + if (res < 6) + return(ERR_WRITE); +/* Read header */ + res = read_serial(camera->serial, buf, 2); + if ((res < 2) || (buf[0] != 2) || (buf[1] != cmd->nbytes)) + return(ERR_READ); +/* Read data */ + res = read_serial(camera->serial, buf, cmd->nbytes); + if (res < cmd->nbytes) + return(ERR_READ); + pbuf = buf; + for (i = 0; i < cmd->nbytes;) + cmd->data[i++] = *pbuf++; +/* Checksum */ + res = read_serial(camera->serial, buf, 1); + if (res < 1) + return(ERR_READ); +/* Evaluate checksum in the futute */ + return(ERR_OK); +} + + +int set_baudrate(cam_info *camera, unsigned char baudrate) +{ + cmd_t cmd; + int tmp; + + if (!is_valid_camera(camera)) + return(ERR_CAM); + +/* Write ROM version */ + cmd.nbytes = 1; + cmd.ram = CAM_INT_RAM; + cmd.address = CAM_ROMVER; + cmd.data[0] = camera->ccd->rom_version; + if (write_mem(camera, &cmd) != ERR_OK) + return(ERR_WRITE); + +/* Set baud rate */ + cmd.nbytes = 1; + cmd.address = CAM_BAUDRATE; + cmd.data[0] = baudrate; + if (write_mem(camera, &cmd) != ERR_OK) + return(ERR_WRITE); + + camera->ccd->baudrate = baudrate; + switch (baudrate) { + case CAM_B57600: + camera->serial->baudrate = B57600; + break; + case CAM_B19200: + camera->serial->baudrate = B19200; + break; + case CAM_B9600: + camera->serial->baudrate = B9600; + break; + case CAM_B4800: + camera->serial->baudrate = B4800; + break; + case CAM_B2400: + camera->serial->baudrate = B2400; + break; + case CAM_B1200: + camera->serial->baudrate = B1200; + break; + case CAM_B600: + camera->serial->baudrate = B600; + break; + case CAM_B300: + camera->serial->baudrate = B300; + break; + default: + return(ERR_BAUDRATE); + } + set_serial(camera->serial); + if (camera->serial->err_code != ERR_OK) + return(ERR_SERIAL); + + /* Read ROM version for testing */ + cmd.nbytes = 1; + cmd.address = CAM_ROMVER; + tmp = read_mem(camera, &cmd); + if (tmp != ERR_OK) + return(ERR_READ); + if (cmd.data[0] != camera->ccd->rom_version) + return(ERR_SERIAL); + + return(ERR_OK); +} + + +int expose_ccd(cam_info *camera, float time) +{ +cmd_t cmd; +int tmp, res; + +if (!is_valid_camera(camera)) + return(ERR_CAM); + +/* Set exposure time */ + cmd.nbytes = 2; + cmd.address = CAM_EXPTIME; + cmd.ram = CAM_INT_RAM; + camera->ccd->exptime = time; + tmp = time * 100; + cmd.data[0] = tmp & 0x00FF; + cmd.data[1] = (tmp >> 8) & 0x00FF; + res = write_mem(camera, &cmd); + if (res != ERR_OK) + return(res); + +/* Start exposure */ + cmd.nbytes = 1; + cmd.address = CAM_MODE; + camera->ccd->mode_flag &= ~CAM_MODE_STEXP; + cmd.data[0] = camera->ccd->mode_flag | CAM_MODE_STEXP; + res = write_mem(camera, &cmd); + if (res != ERR_OK) + return(res); + +/* Wait until exposure is done */ + cmd.nbytes = 1; + cmd.address = CAM_MODE; + sleep((int)time); + do { + if ((res = read_mem(camera, &cmd)) != ERR_OK) + return(res); + sleep(1); + } while((cmd.data[0] & CAM_MODE_EXPDONE) == CAM_MODE_EXPDONE); + +return(ERR_OK); +} + + +int read_line(cam_info *camera, int line, unsigned char *buf) +{ + unsigned char chksum; + int res; + + if (!is_valid_camera(camera)) + return(ERR_CAM); + if ((line < CAM_FIRST_LINE ) || (line > CAM_LAST_LINE)) + return; + buf[0] = line; + buf[1] = line; + res = write_serial(camera->serial, buf, 2); + if (res < 2) + return(ERR_WRITE); + res = read_serial(camera->serial, buf, 2); + if (res < 2) + return(ERR_READ); + if ((buf[0] != line) || (buf[1] != camera->ccd->ncolumns)) + return(ERR_READ); + + res = read_serial(camera->serial, buf, camera->ccd->ncolumns); + if (res < camera->ccd->ncolumns) + return(ERR_READ); + + res = read_serial(camera->serial, &chksum, 1); + if (res < 1) + return(ERR_READ); +/* Evaluate checksum in future */ + + return(ERR_OK); +} + + +int take_image(cam_info *camera, unsigned char *image) +{ + int res; + if (!is_valid_camera(camera)) + return(ERR_CAM); + if (camera->ccd->image == NULL) + return(ERR_MEM); + + if ((res = expose_ccd(camera, camera->ccd->exptime)) != ERR_OK) + return(res); + + if ((res = download_image(camera, image)) != ERR_OK) + return(res); + + return(ERR_OK); + +} + + +int download_image(cam_info *camera, unsigned char *image) +{ + int i, l; + + if (!is_valid_camera(camera)) + return(ERR_CAM); + if (image == NULL) + return(ERR_MEM); + + l = camera->ccd->first_line; + for (i = 0; i < camera->ccd->nlines; i++, l++) { + if (read_line(camera, l , image) != ERR_OK) + return(ERR_READ); +/* Pad to next line */ + image += CAM_MAX_NCOLS; + } + + return(ERR_OK); +} diff --git a/src/drivers/camfits.fake/ccd.h b/src/drivers/camfits.fake/ccd.h new file mode 100644 index 00000000..57741571 --- /dev/null +++ b/src/drivers/camfits.fake/ccd.h @@ -0,0 +1,120 @@ +#include "ser.h" + +#define ROM_VERSION (3) + +#define CAM_MAX_NLINES (165) +#define CAM_MAX_NCOLS (192) + +#define CAM_FIRST_LINE (64) +#define CAM_LAST_LINE (228) + +/* Commands */ +#define CAM_WRITE_MEM (1) +#define CAM_READ_MEM (2) +#define CAM_EXT_RAM (0) +#define CAM_INT_RAM (1) + +/* Responses */ +#define CAM_ACK (0x06) +#define CAM_NAK (0x15) + +/* Mode flags (46) */ +#define CAM_MODE_FEXP (0x80) /* Full exposure */ +#define CAM_MODE_LA (0x40) /* Light array */ +#define CAM_MODE_STEXP (0x20) /* Start exposure */ +#define CAM_MODE_EXPDONE (0x10) /* Exposure done */ +#define CAM_MODE_CMPRSIMG (0x08) /* Compress image in memory */ +#define CAM_MODE_SUBDARKF (0x04) /* Subtract dark frame */ +#define CAM_MODE_CMPRSDAT (0x02) /* Enable data compression */ +#define CAM_MODE_SMTH (0x01) /* Smooth image in memory */ + +/* Format flags (47) */ +#define CAM_FMT_FFOCUS (0x80) /* Find and focus mode */ +#define CAM_FMT_ANTIBLOOM (0x40) /* Anti-blooming gate enable */ +#define CAM_FMT_INTERRUPT (0x20) /* Interrupt from Find and focus, Track */ + /* and Calibrate modes. */ +#define CAM_FMT_TRACK (0x10) /* Track mode */ +#define CAM_FMT_CALIBRATE (0x08) /* Calibrate mode */ +#define CAM_FMT_CLOSE_R0 (0x04) /* Close `left' relay */ +#define CAM_FMT_CLOSE_R1 (0x05) /* Close `right' relay */ +#define CAM_FMT_CLOSE_R2 (0x06) /* Close `up' relay */ +#define CAM_FMT_CLOSE_R3 (0x07) /* Close `down' relay */ + +/* Baud rates */ +#define CAM_B57600 (255) +#define CAM_B19200 (253) +#define CAM_B9600 (250) +#define CAM_B4800 (244) +#define CAM_B2400 (232) +#define CAM_B1200 (208) +#define CAM_B600 (160) +#define CAM_B300 (64) + +/* Addresses */ +#define CAM_LAST_KEY (45) +#define CAM_MODE (46) +#define CAM_FORMAT (47) +#define CAM_EXPTIME (48) +#define CAM_XPOINTER (50) +#define CAM_NBYTES (51) +#define CAM_OFFSET (52) +#define CAM_VREFPLUS (53) +#define CAM_VREFMINUS (54) +#define CAM_ROMVER (55) +#define CAM_BAUDRATE (56) +#define CAM_INSTRUCT (58) + + +#define CAM_MAX_DATA (4) +#define CAM_MAX_CMD (CAM_MAX_DATA + 6) + + +typedef struct { + float exptime; /* seconds */ + unsigned char mode_flag; + unsigned char format_flag; + unsigned char first_line; + unsigned char first_column; + unsigned char nlines; + unsigned char ncolumns; + unsigned char offset; + unsigned char vref_plus; + unsigned char vref_minus; + unsigned char rom_version; + unsigned char baudrate; /* note: this is the constant specified */ + /* above, e.g. CAM_BXXXXX. */ + unsigned char *image; +} ccd_info; + +typedef struct { + ccd_info *ccd; + ser_info *serial; +} cam_info; + +typedef struct { + int nbytes; + int ram; + int address; + unsigned char data[CAM_MAX_DATA]; +} cmd_t; + + +/* Camera structure management */ +int is_valid_camera(cam_info *camera); +cam_info *alloc_camera(char *ttyname); +void free_camera(cam_info *camera); + +/* Low level functions */ +int set_baudrate(cam_info *camera, unsigned char baudrate); + /* values of baud rates are defined in camera.h */ +int init_camera(cam_info *camera); +int write_mem(cam_info *camera, cmd_t *command); +int read_mem(cam_info *camera, cmd_t *command); +unsigned char wait_response(cam_info *camera); + +/* user functions */ +int expose_ccd(cam_info *camera, float time); /* seconds */ +int read_line(cam_info *camera, int line, unsigned char *buf); +int download_image(cam_info *camera, unsigned char *image); +int set_camera(cam_info *camera); +int take_image(cam_info *camera, unsigned char *image); diff --git a/src/drivers/camfits.fake/errors.h b/src/drivers/camfits.fake/errors.h new file mode 100644 index 00000000..41941c97 --- /dev/null +++ b/src/drivers/camfits.fake/errors.h @@ -0,0 +1,19 @@ +#define TRUE (1) +#define FALSE (0) + + + +#define ERR_OK (0) +#define ERR_OPENTTY (-1) +#define ERR_SERIAL (-2) +#define ERR_BAUDRATE (-3) +#define ERR_PARITY (-4) +#define ERR_STOPB (-5) +#define ERR_WRITE (-6) +#define ERR_READ (-7) +#define ERR_MEM (-8) +#define ERR_NOTRESPOND (-9) + +#define ERR_CAM (-50) +#define ERR_CMD (-51) +#define ERR_EXPOSE (-52) diff --git a/src/drivers/camfits.fake/fake.camera.xml b/src/drivers/camfits.fake/fake.camera.xml new file mode 100644 index 00000000..46d28f52 --- /dev/null +++ b/src/drivers/camfits.fake/fake.camera.xml @@ -0,0 +1,44 @@ + + + + + Fake + Camera + + +camfits.fake -t $exp_time -n $n_exp -o $output + + + + + + exp_time + float + 1.0 + Integration time in seconds. Min and max comes from CCDcap. + + + + n_exp + int + 1 + Number of frames to take. + + + + output + str + img-$time-$object-$filter + Filename of the output image (wildcards allowed: $time, $object, $filter). + + + + verbose + bool + True + Verbose mode. + + + + + diff --git a/src/drivers/camfits.fake/main.c b/src/drivers/camfits.fake/main.c new file mode 100644 index 00000000..6116deb8 --- /dev/null +++ b/src/drivers/camfits.fake/main.c @@ -0,0 +1,323 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include "errors.h" +#include "ccd.h" +#include "fitsio.h" + +#define TRUE (1) +#define FALSE (0) + +int getimage(cam_info *camera, float texp, char *bfname, int imindex); +int fileexists(char *fname); +void usage(void); +void setup(void); +void check_alloc_error(void); +void expose_error(int err); +void save_fits(char *fname); +void print_image(unsigned char *image); +void controlc(int signumber); +void hangup(int signumber); + + +cam_info *camera; +char command[128]; +int output = FALSE; /* Enable output to terminal */ +int nexp = 0; /* Number of images to take */ +char *porta; /* Serial port */ +char *bfname; /* Image base-file-name */ +int imindex; /* index to bfname */ +float texp; /* Exposure time */ +fitsfile *fptr; /* Image file */ +int fitstatus; /* Status returned by fits library */ + +int nimg; /* Number of images taken */ +int stop_imaging = FALSE; /* if TRUE stop imaging */ + +unsigned char image[CAM_MAX_NLINES][CAM_MAX_NCOLS]; +long naxes[2] = {CAM_MAX_NCOLS, CAM_MAX_NLINES}; + +int main(int argc, char ** argv) +{ + int res; + + signal(SIGINT, controlc); + signal(SIGHUP, hangup); + signal(SIGABRT, controlc); + + strcpy(command, argv[0]); + fprintf(stderr, "CCD faker, FITS file format support.\n" + "Ver0.6f - Image sequence enabled.\n\n"); + if (argc < 8) { + usage(); + exit(1); + } + +/* strcpy(porta, argv[1]); + strcpy(bfname, argv[4]); */ + porta = argv[1]; + bfname = argv[6]; + if (*bfname == '-') { + if (*(++bfname) == '\0') { + output = TRUE; + bfname = NULL; + } else { + usage(); + exit(1); + } + } + + +/* get index */ + if (!output) { + res = sscanf(argv[7], "%d", &imindex); + if (res != 1) { + fprintf(stderr, "\nError: \"%s\" is an invalid index.\n\n", argv[2]); + usage(); + exit(1); + } + } + +/* get exposure time */ + res = sscanf(argv[2], "%f", &texp); + if (res != 1) { + fprintf(stderr, "\nError: \"%s\" is an invalid exposure time.\n\n", argv[2]); + usage(); + exit(1); + } + +/* get number of exposures */ + res = sscanf(argv[3], "%d", &nexp); + if (res != 1) { + fprintf(stderr, "\nError: \"%s\" is an invalid number of exposures.\n\n", argv[2]); + usage(); + exit(1); + } + + setup(); + + for (nimg = 0; nimg < nexp; nimg++) { + fprintf(stderr, "\nTaking image #%d.\n", nimg); + getimage(camera, texp, bfname, (nimg + imindex)); + if (stop_imaging) break; + } + + free_camera(camera); + exit(nimg++); +} + + +int getimage(cam_info *camera, float texp, char *bfname, int imindex) +{ + char fname[128]; + char tmp[128]; + int res; + + fprintf(stderr, "Exposing CCD..."); +/* if ((res = expose_ccd(camera, texp)) != ERR_OK) { + expose_error(res); + abort(); + } */ + sleep((int) texp); + fprintf(stderr, "OK.\n"); + + fprintf(stderr, "Downloading image..."); +/* if ((res = download_image(camera, camera->ccd->image)) != ERR_OK) { + fprintf(stderr, "Error in download_image()\n"); + abort(); + } */ + sleep(5); + fprintf(stderr, "OK.\n"); + + if (output) { +/* fprintf(stderr, "Printing image...\n"); + print_image(camera->ccd->image); + fprintf(stderr, "\nDone.\n"); */ + } else { + sprintf(fname, "%s%04d.fits", bfname, imindex); + fprintf(stderr, "Saving image %s...", fname); + if (fileexists(fname)) + fprintf(stderr, " file already exists.\n"); + else { + snprintf(tmp,128, "touch %s", fname); + system(tmp); + /* save_fits(fname); */ + fprintf(stderr, "OK\n"); + } + } +} + + + +void usage(void) +{ + fprintf(stderr, "Specify terminal, exposure time (in seconds), # of exposures, base-file-name and indexer.\n"); + fprintf(stderr, "Use \"-\" instead of file name to output the image to stdout.\n\n"); + fprintf(stderr, "SYNTAX: %s terminal_device exposure_time nexposures x1:x2 y1:y2 { output_file indexer | - }\n\n", command); + fprintf(stderr, "E.g.: %s /dev/ttyS1 10.2 25 0:192 0:165 junk 0000\n", command); + fprintf(stderr, "E.g.: %s /dev/ttyS1 10.2 1 50:100 46:120 -\n\n\n", command); +} + + +/* test if file already exists */ +int fileexists(char *fname) +{ + int res; + + if ((res = open(fname, O_RDONLY)) != -1) { + close(res); + return (TRUE); /* File exists! */ + } + return (FALSE); /* File does not exist */ +} + + + +/* sets up camera structs and initializes its parameters */ +void setup(void) +{ +/* int tmp; + + camera = alloc_camera(porta); + check_alloc_error(); + + camera->ccd->image = &(image[0][0]); + + camera->ccd->baudrate = CAM_B57600; + camera->ccd->rom_version = ROM_VERSION; + if ((tmp = init_camera(camera)) != ERR_OK) { + fprintf(stderr, "Error in init_camera(): %d\n", tmp); + abort(); + } + camera->ccd->first_line = CAM_FIRST_LINE; + camera->ccd->first_column = 0; + camera->ccd->nlines = CAM_MAX_NLINES; + camera->ccd->ncolumns = CAM_MAX_NCOLS; + camera->ccd->offset = 0; + camera->ccd->vref_plus = 255; + camera->ccd->vref_minus = 0; + camera->ccd->format_flag = 0; + camera->ccd->mode_flag = (CAM_MODE_FEXP | CAM_MODE_LA); + if ((tmp = set_camera(camera)) != ERR_OK) { + fprintf(stderr, "Error in set_camera()\n"); + abort(); + } */ +} + + +void print_image(unsigned char *image) +{ + int i, j, lastline, lastcol; + + lastline = camera->ccd->nlines; + lastcol = camera->ccd->ncolumns; + for (i = 0; i < lastline; i++) { + for (j = 0; j < lastcol; j++) + printf("%d\n", *(image++)); + } + printf("$"); /* image terminator */ +} + + +void save_fits(char *fname) +{ + int x, y, i, j, lastline, lastcol; + unsigned char ** imgptr; + +/* Gradient fill + for (i = 0; i < 165; i++) + for (j = 0; j < 192; j++) + image[i][j] = (i+j)/2; +*/ + + naxes[0] = camera->ccd->ncolumns; + naxes[1] = camera->ccd->nlines; + +/* + imgptr = camera->ccd->image; + lastline = camera->ccd->nlines + camera->ccd->first_line; + lastcol = camera->ccd->first_column + camera->ccd->ncolumns; + + for (i = camera->ccd->first_line, x = 0; i < lastline; i++, x++, imgptr++) { + for (j = camera->ccd->first_column, y = 0; j < lastcol; j++, y++) + rawimage[x][y] = (*imgptr)[y]; + } +*/ + +/* open file for writing a FITS image */ + + if (fits_create_file(&fptr, fname, &fitstatus) || + fits_create_img(fptr, BYTE_IMG, 2, naxes, &fitstatus) || + fits_write_img(fptr, TBYTE, 1, (naxes[0] * naxes[1]), image, &fitstatus) || + fits_update_key(fptr, TFLOAT, "EXPTIME", &texp, "Exposure Time (secs.)", &fitstatus) || + fits_close_file(fptr, &fitstatus)) { + fits_report_error(stderr, fitstatus); + abort(); + } +} + + +void check_alloc_error(void) +{ + if (!is_valid_camera(camera)) { + fprintf(stderr, "Error allocating camera structures.\n"); + abort(); + } + if (camera->serial->err_code == ERR_OK) return; + + fprintf(stderr, "Error setting up camera, "); + if (camera->serial->err_code == ERR_READ) + fprintf(stderr, "could not read serial port.\n"); + else if (camera->serial->err_code == ERR_WRITE) + fprintf(stderr, "could not write serial port.\n"); + else if (camera->serial->err_code == ERR_OPENTTY) + fprintf(stderr, "could not open serial port.\n"); + else if (camera->serial->err_code == ERR_MEM) + fprintf(stderr, "serial structures could not be created.\n"); + else fprintf(stderr, "unexpected error.\n"); + abort(); +} + + +void expose_error(int err) +{ + fprintf(stderr, "Problem exposing CCD, "); + if (err == ERR_CAM) fprintf(stderr, "camera not allocated.\n"); + else if (err == ERR_WRITE) fprintf(stderr, "error writing camera.\n"); + else if (err == ERR_READ) fprintf(stderr, "error reading camera.\n"); + else fprintf(stderr, "unexpected error.\n"); + abort(); +} + + +void controlc(int signumber) +{ + if (signumber == SIGINT) + fprintf(stderr, "Control-C signal caught, exiting.\n"); + else + fprintf(stderr, "Aborting...\n"); + if (is_valid_camera(camera)) + tcflush(camera->serial->ttyfd, TCOFLUSH); + free_camera(camera); + exit(nimg++); +} + +void hangup(int signumber) +{ + fprintf(stderr, "\nHangup signal received, stopping imaging.\n"); + stop_imaging = TRUE; +} diff --git a/src/drivers/camfits.fake/ser.c b/src/drivers/camfits.fake/ser.c new file mode 100644 index 00000000..4925c033 --- /dev/null +++ b/src/drivers/camfits.fake/ser.c @@ -0,0 +1,250 @@ +/* This file contains the functions for serial communication */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "errors.h" +#include "ser.h" + +static int set_minchars(ser_info *serial, int nbytes); +static void rdtimeout(int signumber); +static ser_info *sertmp; + +#ifdef COMMTEST + +/* This example program writes a string in port `tty', and waits until + timeout for a response. */ + + +char teste[] = "Write test..."; +unsigned char buf[25]; +char tty[] = "/dev/ttyS1"; + + +int main(void) +{ + int result; + ser_info porta; + + open_serial(&porta, tty); + if (porta.err_code != ERR_OK) return(-1); + puts("Communication test.\n\n"); + + /* 9600,8e1 */ + porta.baudrate = B9600; + porta.parity = PAR_EVEN; + porta.stopbits = 1; + porta.timeout = 10; + porta.minchars = 25; + porta.waitread = FALSE; + set_serial(&porta); + write_serial(&porta, teste, strlen(teste)); + + if (read_serial(&porta, buf, 24)) + printf("Text received: %s\n\n", buf); + else printf("Read timeout.\n\n"); + + close_serial(&porta); + return(0); +} +#endif /* COMMTEST */ + + +int is_valid_serial(ser_info *serial) +{ + if ((serial == NULL) || + (serial->tty_name == NULL) || + (serial->tio == NULL) || + (serial->oldtio == NULL)) + return(FALSE); + return(TRUE); +} + + +void open_serial(ser_info *serial, char *ttyname) +{ + if (serial == NULL) return; + + serial->err_code = ERR_OK; + signal(SIGALRM, rdtimeout); + +/* Allocate memory for the structures */ + serial->tio = (struct termios *) malloc(sizeof(struct termios)); + if (serial->tio == NULL) { + serial->err_code = ERR_MEM; + return; + } + serial->oldtio = (struct termios *) malloc(sizeof(struct termios)); + if (serial->oldtio == NULL) { + serial->err_code = ERR_MEM; + return; + } + + serial->tty_name = (char *) calloc(strlen(ttyname), sizeof(char)); + if (serial->tty_name == NULL) { + serial->err_code = ERR_MEM; + return; + } + strcpy(serial->tty_name, ttyname); + + + serial->ttyfd = open(serial->tty_name, O_RDWR | O_NOCTTY); + if (serial->ttyfd < 0) { + serial->err_code = ERR_OPENTTY; + close_serial(serial); + return; + } + +/* Save current config */ + tcgetattr(serial->ttyfd, serial->oldtio); + serial->isopen = TRUE; +} + + +int close_serial(ser_info *serial) +{ + if (!is_valid_serial(serial)) return(ERR_MEM); + signal(SIGALRM, SIG_DFL); +/* Restore terminal to its previous state; free memory */ + if (serial->isopen) { + tcsetattr(serial->ttyfd,TCSANOW,serial->tio); + close(serial->ttyfd); + } + if (serial->tio != NULL) free(serial->tio); + if (serial->oldtio != NULL) free(serial->oldtio); + if (serial->tty_name != NULL) free(serial->tty_name); + return(ERR_OK); +} + + +int set_serial(ser_info *serial) +{ + if (!is_valid_serial(serial)) return(ERR_MEM); + if (!serial->isopen) { + serial->err_code = ERR_SERIAL; + return(ERR_SERIAL); + } + memset(serial->tio, 0, sizeof(struct termios)); + + cfsetospeed(serial->tio, serial->baudrate); + cfsetispeed(serial->tio, serial->baudrate); + +/* Set byte size to 8bit, enable reading and set local mode */ + serial->tio->c_cflag |= CS8 | CLOCAL | CREAD; + switch (serial->parity) { + case PAR_NONE: + serial->tio->c_iflag |= IGNPAR; + serial->tio->c_cflag &= ~PARENB; + break; + + case PAR_EVEN: + serial->tio->c_iflag &= ~IGNPAR; + serial->tio->c_cflag |= PARENB; + serial->tio->c_cflag &= ~PARODD; + break; + + case PAR_ODD: + serial->tio->c_iflag &= ~IGNPAR; + serial->tio->c_cflag |= PARENB; + serial->tio->c_cflag &= PARODD; + break; + default: + serial->err_code = ERR_PARITY; + return; + } + + if (serial->stopbits == 1) + serial->tio->c_cflag &= ~CSTOPB; + else if (serial->stopbits ==2) + serial->tio->c_cflag |= CSTOPB; + else { + serial->err_code = ERR_STOPB; + return; + } + +/* Raw input */ + serial-> tio->c_lflag = 0; + + serial->tio->c_cc[VTIME] = serial->timeout; + if (serial->waitread) + serial->tio->c_cc[VMIN] = serial->minchars; + + tcsetattr(serial->ttyfd,TCSANOW,serial->tio); +} + + +int write_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes) +{ + int result; + + if (!is_valid_serial(serial)) return(ERR_MEM); + if (!serial->isopen) { + serial->err_code = ERR_SERIAL; + return(0); + } + result = write(serial->ttyfd, buf, nbytes); + if (result < nbytes) { + serial->err_code = ERR_WRITE; + return(result); + } + serial->err_code = ERR_OK; + return(result); +} + + +int read_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes) +{ + int result; + + if (!is_valid_serial(serial)) return(ERR_MEM); + if (!serial->isopen) { + serial->err_code = ERR_SERIAL; + return(0); + } + serial->err_code = ERR_OK; + +/* Set the minimum number of chars to read before returning */ + if (serial->waitread && (serial->minchars != nbytes)) { + if (set_minchars(serial, nbytes) != ERR_OK) { + serial->err_code = ERR_SERIAL; + return(0); + } + } +/* save serial stuff for alarm() timeout. */ + sertmp = serial; + alarm(serial->timeout); + result = read(serial->ttyfd, buf, nbytes); + alarm(0); + serial->bytesread = result; + if (result < nbytes) + serial->err_code = ERR_READ; + return(result); +} + + +static int set_minchars(ser_info *serial, int nbytes) +{ + if (!is_valid_serial(serial)) + return(ERR_MEM); + if (!serial->waitread) + return(ERR_READ); + serial->minchars = nbytes; + serial->tio->c_cc[VMIN] = nbytes; + tcsetattr(serial->ttyfd,TCSANOW,serial->tio); + return(ERR_OK); +} + +static void rdtimeout(int signumber) +{ + fprintf(stderr, "Serial communication failed, exiting...\n"); + if (sertmp != NULL) close_serial(sertmp); + exit(-1); +} + diff --git a/src/drivers/camfits.fake/ser.h b/src/drivers/camfits.fake/ser.h new file mode 100644 index 00000000..1a8145c5 --- /dev/null +++ b/src/drivers/camfits.fake/ser.h @@ -0,0 +1,34 @@ +#include + +#define RD_TIMEOUT (2) +#define TRUE (1) +#define FALSE (0) + +#define MAX_BUF_LEN (255) +#define PAR_NONE 'n' +#define PAR_EVEN 'e' +#define PAR_ODD 'o' + +typedef struct { + int isopen; + int waitread; + int err_code; + char *tty_name; + int ttyfd; + cc_t minchars; /* not used if waitread == FALSE */ + int bytesread; + int baudrate; + char parity; + int stopbits; + cc_t timeout; /* interchar time (1/10 secs), and read timeout (secs) */ + struct termios *tio; /* |- don't mess with these structs, */ + struct termios *oldtio; /* | the routines will handle it. */ +} ser_info; + +int is_valid_serial(ser_info *serial); +void open_serial(ser_info *serial, char *ttyname); +int close_serial(ser_info *serial); +int set_serial(ser_info *serial); +//static int set_minchars(ser_info *serial, int nbytes); +int read_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes); +int write_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes); diff --git a/src/drivers/camfits.st4/Makefile.am b/src/drivers/camfits.st4/Makefile.am new file mode 100644 index 00000000..f66d9a2e --- /dev/null +++ b/src/drivers/camfits.st4/Makefile.am @@ -0,0 +1,10 @@ +include $(top_srcdir)/rules.make + +bin_PROGRAMS = camfits.st4 + +camfits_st4_SOURCES = camera.c ccd.c ser.c \ + camera.h ccd.h ser.h + +camfits_st4_LDADD = -lcfitsio -lm + +uts_driver_DATA = st4.camera.xml diff --git a/src/drivers/camfits.st4/camera.c b/src/drivers/camfits.st4/camera.c new file mode 100644 index 00000000..fe208ac9 --- /dev/null +++ b/src/drivers/camfits.st4/camera.c @@ -0,0 +1,243 @@ +#include +#include +#include +#include +#include + +#include "errors.h" +#include "ccd.h" +#include "fitsio.h" + +#define TRUE (1) +#define FALSE (0) + +void usage(char *command); +void setup(void); +void check_alloc_error(void); +void expose_error(int err); +void save_fits(void); +void print_image(unsigned char *image); +void controlc(int signumber); + +cam_info *camera; +int output = FALSE; +char *porta; +char *imagefile; +float texp; +fitsfile *fptr; +int fitstatus; +unsigned char image[CAM_MAX_NLINES][CAM_MAX_NCOLS]; +long naxes[2] = {CAM_MAX_NCOLS, CAM_MAX_NLINES}; + +int main(int argc, char ** argv) +{ + int res; + + signal(SIGINT, controlc); + signal(SIGABRT, controlc); + + fprintf(stderr, "CCD dump, FITS file format support.\n"); + if (argc < 4) { + usage(argv[0]); + exit(1); + } + +/* strcpy(porta, argv[1]); + strcpy(imagefile, argv[3]); */ + porta = argv[1]; + imagefile = argv[3]; + if (*imagefile == '-') { + if (*(++imagefile) == '\0') { + output = TRUE; + imagefile = NULL; + } else { + usage(argv[0]); + exit(1); + } + } + +/* get exposure time */ + res = sscanf(argv[2], "%f", &texp); + if (res != 1) { + fprintf(stderr, "\nError: \"%s\" is an invalid exposure time.\n\n", argv[2]); + usage(argv[0]); + exit(1); + } + +/* test if file already exists */ + if (!output && (res = open(imagefile, O_RDONLY)) != -1) { + close(res); + fprintf(stderr, "Error: File \"%s\" already exists.\n\n", argv[3]); + usage(argv[0]); + exit(1); + } + + setup(); + + fprintf(stderr, "Exposing CCD...\n"); + if ((res = expose_ccd(camera, texp)) != ERR_OK) { + expose_error(res); + abort(); + } + fprintf(stderr, "Exposure OK.\n\n"); + + fprintf(stderr, "Downloading image...\n"); + if ((res = download_image(camera, camera->ccd->image)) != ERR_OK) { + fprintf(stderr, "Error in download_image()\n"); + abort(); + } + fprintf(stderr, "Download done.\n\n"); + + if (output) { + fprintf(stderr, "Printing image...\n"); + print_image(camera->ccd->image); + fprintf(stderr, "\nDone.\n"); + } else { + fprintf(stderr, "Saving image...\n"); + save_fits(); + fprintf(stderr, "Image taken successfully.\n\n"); + } + + free_camera(camera); + return(0); +} + + +void usage(char *command) +{ + fprintf(stderr, "Specify terminal, exposure time (in seconds) and file name.\n"); + fprintf(stderr, "Use \"-\" instead of file name to output the image to stdout.\n\n"); + fprintf(stderr, "SYNTAX: %s terminal_device exposure_time { output_file | - }\n\n", command); + fprintf(stderr, "E.g.: %s /dev/ttyS1 10.2 output.fit\n", command); + fprintf(stderr, "E.g.: %s /dev/ttyS1 10.2 -\n\n\n", command); +} + + +/* sets up camera structs and initializes its parameters */ +void setup(void) +{ + int tmp; + + camera = alloc_camera(porta); + check_alloc_error(); + + camera->ccd->image = &(image[0][0]); + + camera->ccd->baudrate = CAM_B57600; + camera->ccd->rom_version = ROM_VERSION; + if ((tmp = init_camera(camera)) != ERR_OK) { + fprintf(stderr, "Error in init_camera(): %d\n", tmp); + abort(); + } + camera->ccd->first_line = CAM_FIRST_LINE; + camera->ccd->first_column = 0; + camera->ccd->nlines = CAM_MAX_NLINES; + camera->ccd->ncolumns = CAM_MAX_NCOLS; + camera->ccd->offset = 0; + camera->ccd->vref_plus = 255; + camera->ccd->vref_minus = 0; + camera->ccd->format_flag = 0; + camera->ccd->mode_flag = (CAM_MODE_FEXP | CAM_MODE_LA); + if ((tmp = set_camera(camera)) != ERR_OK) { + fprintf(stderr, "Error in set_camera()\n"); + abort(); + } +} + + +void print_image(unsigned char *image) +{ + int i, j, lastline, lastcol; + + lastline = camera->ccd->nlines; + lastcol = camera->ccd->ncolumns; + for (i = 0; i < lastline; i++) { + for (j = 0; j < lastcol; j++) + printf("%d\n", *(image++)); + } +} + + +void save_fits(void) +{ + int x, y, i, j, lastline, lastcol; + unsigned char ** imgptr; + +/* Gradient fill + for (i = 0; i < 165; i++) + for (j = 0; j < 192; j++) + image[i][j] = (i+j)/2; +*/ + + naxes[0] = camera->ccd->ncolumns; + naxes[1] = camera->ccd->nlines; + +/* + imgptr = camera->ccd->image; + lastline = camera->ccd->nlines + camera->ccd->first_line; + lastcol = camera->ccd->first_column + camera->ccd->ncolumns; + + for (i = camera->ccd->first_line, x = 0; i < lastline; i++, x++, imgptr++) { + for (j = camera->ccd->first_column, y = 0; j < lastcol; j++, y++) + rawimage[x][y] = (*imgptr)[y]; + } +*/ + +/* open file for writing a FITS image */ + + if (fits_create_file(&fptr, imagefile, &fitstatus) || + fits_create_img(fptr, BYTE_IMG, 2, naxes, &fitstatus) || + fits_write_img(fptr, TBYTE, 1, (naxes[0] * naxes[1]), image, &fitstatus) || + fits_update_key(fptr, TFLOAT, "EXPTIME", &texp, "Exposure Time (secs.)", &fitstatus) || + fits_close_file(fptr, &fitstatus)) { + fits_report_error(stderr, fitstatus); + abort(); + } +} + + +void check_alloc_error(void) +{ + if (!is_valid_camera(camera)) { + fprintf(stderr, "Error allocating camera structures.\n"); + abort(); + } + if (camera->serial->err_code == ERR_OK) return; + + fprintf(stderr, "Error setting up camera, "); + if (camera->serial->err_code == ERR_READ) + fprintf(stderr, "could not read serial port.\n"); + else if (camera->serial->err_code == ERR_WRITE) + fprintf(stderr, "could not write serial port.\n"); + else if (camera->serial->err_code == ERR_OPENTTY) + fprintf(stderr, "could not open serial port.\n"); + else if (camera->serial->err_code == ERR_MEM) + fprintf(stderr, "serial structures could not be created.\n"); + else fprintf(stderr, "unexpected error.\n"); + abort(); +} + + +void expose_error(int err) +{ + fprintf(stderr, "Problem exposing CCD, "); + if (err == ERR_CAM) fprintf(stderr, "camera not allocated.\n"); + else if (err == ERR_WRITE) fprintf(stderr, "error writing camera.\n"); + else if (err == ERR_READ) fprintf(stderr, "error reading camera.\n"); + else fprintf(stderr, "unexpected error.\n"); + abort(); +} + + +void controlc(int signumber) +{ + if (signumber == SIGINT) + fprintf(stderr, "Control-C signal caught, exiting.\n"); + else + fprintf(stderr, "Aborting...\n"); + if (is_valid_camera(camera)) + tcflush(camera->serial->ttyfd, TCOFLUSH); + free_camera(camera); + exit(2); +} + diff --git a/src/drivers/camfits.st4/ccd.c b/src/drivers/camfits.st4/ccd.c new file mode 100644 index 00000000..47e8082c --- /dev/null +++ b/src/drivers/camfits.st4/ccd.c @@ -0,0 +1,470 @@ +#include +#include "errors.h" +#include "ccd.h" + + +#undef CCDTEST +#ifdef CCDTEST + +char deftty[] = "/dev/ttyS1"; + +cam_info *camera; + +int main() +{ + int i; + + puts("ST-4 test program."); + printf("tty = %s\n\n", deftty); + + camera = alloc_camera(deftty); + if (camera == NULL) { + puts("alloc_camera()"); + exit(1); + } + + camera->ccd->baudrate = CAM_B57600; + camera->ccd->rom_version = ROM_VERSION; + if (init_camera(camera) != ERR_OK) { + puts("init_camera()"); + exit(0); + } + + camera->ccd->exptime = 5; + camera->ccd->first_line = CAM_FIRST_LINE; + camera->ccd->first_column = 0; + camera->ccd->nlines = CAM_MAX_NLINES; + camera->ccd->ncolumns = CAM_MAX_NCOLS; + camera->ccd->offset = 0; + camera->ccd->vref_plus = 255; + camera->ccd->vref_minus = 0; + camera->ccd->format_flag = 0; + camera->ccd->mode_flag = (CAM_MODE_FEXP | CAM_MODE_LA); + set_camera(camera); + take_image(camera, camera->ccd->image); + + free_camera(camera); +} +#endif + + +/* initializes camera's comm parameters */ +int init_camera(cam_info *camera) +{ + int res; + + if (!is_valid_camera(camera)) + return(ERR_CAM); + + camera->serial->waitread = TRUE; + camera->serial->baudrate = B9600; + camera->serial->parity = PAR_EVEN; + camera->serial->stopbits = 1; + camera->serial->minchars = 0; + camera->serial->timeout = 5; + set_serial(camera->serial); + if (camera->serial->err_code != ERR_OK) + return(ERR_SERIAL); + + res = set_baudrate(camera, camera->ccd->baudrate); + return(res); + +} + + +int is_valid_camera(cam_info *camera) +{ + if (camera == NULL) + return(FALSE); + if (camera->ccd == NULL) + return(FALSE); + if (camera->serial == NULL) + return(FALSE); + return(TRUE); +} + + +/* allocates memory for camera's structures */ +cam_info *alloc_camera(char *ttyname) +{ + cam_info *camera; + int i; + + camera = (cam_info *) calloc(sizeof(cam_info), 1); + if (camera == NULL) + return(NULL); + camera->serial = (ser_info *) calloc(sizeof(ser_info), 1); + if (camera->serial == NULL) + return(NULL); + camera->ccd = (ccd_info *) calloc(sizeof(ccd_info), 1); + if (camera->ccd == NULL) + return(NULL); + open_serial(camera->serial, ttyname); + return(camera); +} + + +/* frees the memory allocated for camera's structures */ +void free_camera(cam_info *camera) +{ + if (camera == NULL) + return; + if (camera->serial != NULL) { + if (camera->serial->ttyfd > 0) { + set_baudrate(camera, CAM_B9600); + close_serial(camera->serial); + } + free(camera->serial); + } + if (camera->ccd != NULL) + free(camera->ccd); + free(camera); +} + + +/* sets up all the camera's parameters */ +int set_camera(cam_info *camera) +{ + long int exptmp; + int res; + cmd_t cmd; + + if (!is_valid_camera(camera)) + return(ERR_CAM); +/* Set modes and format */ + cmd.nbytes = 2; + cmd.address = CAM_MODE; + cmd.ram = CAM_INT_RAM; + camera->ccd->mode_flag &= ~CAM_MODE_STEXP; + cmd.data[0] = camera->ccd->mode_flag; + cmd.data[1] = camera->ccd->format_flag; + res = write_mem(camera, &cmd); + if (res != ERR_OK) + return(ERR_WRITE); + +/* Set exposure time */ +/* cmd.nbytes = 2; + cmd.address = CAM_EXPTIME; + exptmp = camera->ccd->exptime * 100; + cmd.data[0] = exptmp & 0x00FF; + cmd.data[1] = (exptmp >> 8) & 0x00FF; + res = write_mem(camera, &cmd); + if (res != ERR_OK) + return(ERR_WRITE); */ + +/* Set first pixel and number of pixels per line */ + cmd.nbytes = 2; + cmd.address = CAM_XPOINTER; + cmd.data[0] = camera->ccd->first_column; + cmd.data[1] = camera->ccd->ncolumns; + res = write_mem(camera, &cmd); + if (res != ERR_OK) + return(ERR_WRITE); + +/* Set additional offset and Vref+ */ + cmd.nbytes = 2; + cmd.address = CAM_OFFSET; + cmd.data[0] = camera->ccd->offset; + cmd.data[1] = camera->ccd->vref_plus; + res = write_mem(camera, &cmd); + if (res != ERR_OK) + return(ERR_WRITE); + +/* Set Vref- */ + cmd.nbytes = 1; + cmd.address = CAM_VREFMINUS; + cmd.data[0] = camera->ccd->vref_minus; + res = write_mem(camera, &cmd); + if (res != ERR_OK) + return(ERR_WRITE); + +return(ERR_OK); +} + + +/* writes data in camera's ROM */ +int write_mem(cam_info *camera, cmd_t *cmd) +{ + unsigned char buf[CAM_MAX_CMD], *pbuf, chksum, tmp; + int i, res; + + if (!is_valid_camera(camera)) + return(ERR_CAM); + if (cmd == NULL) + return(ERR_CMD); + + pbuf = buf; + *pbuf++ = CAM_WRITE_MEM; + chksum = CAM_WRITE_MEM; + tmp = cmd->nbytes + 3; + *pbuf++ = tmp; + chksum += tmp; + *pbuf++ = cmd->ram; + chksum += cmd->ram; + tmp = cmd->address & 0x00FF; + *pbuf++ = tmp; + chksum += tmp; + tmp = (cmd->address >> 8) & 0x00FF; + *pbuf++ = tmp; + chksum += tmp; + + for (i = 0; i < (cmd->nbytes); i++) { + *pbuf++ = cmd->data[i]; + chksum += cmd->data[i]; + } + *pbuf = chksum; + if (write_serial(camera->serial, buf, (6 + cmd->nbytes)) == ERR_WRITE) + return(ERR_WRITE); + res = wait_response(camera); + if (res == CAM_NAK) + return(ERR_READ); + return(ERR_OK); +} + + +unsigned char wait_response(cam_info *camera) +{ + unsigned char c; + int res; + + if (!is_valid_camera(camera)) + return; + + if (read_serial(camera->serial, &c, 1) == ERR_READ) + return(CAM_NAK); + if (c != CAM_ACK) + return(CAM_NAK); + return(CAM_ACK); +} + + +int read_mem(cam_info *camera, cmd_t *cmd) +{ + unsigned char buf[CAM_MAX_CMD], *pbuf, tmp, chksum; + int res, i; + + if (!is_valid_camera(camera)) + return(ERR_CAM); + if (cmd == NULL) + return(ERR_CMD); +/* Set command in buffer */ + pbuf = buf; + *pbuf++ = CAM_READ_MEM; + chksum = CAM_READ_MEM; + *pbuf++ = cmd->nbytes; + chksum += cmd->nbytes; + *pbuf++ = cmd->ram; + chksum += cmd->ram; + tmp = cmd->address & 0x00FF; + *pbuf++ = tmp; + chksum += tmp; + tmp = (cmd->address >> 8) & 0x00FF; + *pbuf++ = tmp; + chksum += tmp; + *pbuf = chksum; + res = write_serial(camera->serial, buf, 6); + if (res < 6) + return(ERR_WRITE); +/* Read header */ + res = read_serial(camera->serial, buf, 2); + if ((res < 2) || (buf[0] != 2) || (buf[1] != cmd->nbytes)) + return(ERR_READ); +/* Read data */ + res = read_serial(camera->serial, buf, cmd->nbytes); + if (res < cmd->nbytes) + return(ERR_READ); + pbuf = buf; + for (i = 0; i < cmd->nbytes;) + cmd->data[i++] = *pbuf++; +/* Checksum */ + res = read_serial(camera->serial, buf, 1); + if (res < 1) + return(ERR_READ); +/* Evaluate checksum in the futute */ + return(ERR_OK); +} + + +int set_baudrate(cam_info *camera, unsigned char baudrate) +{ + cmd_t cmd; + int tmp; + + if (!is_valid_camera(camera)) + return(ERR_CAM); + +/* Write ROM version */ + cmd.nbytes = 1; + cmd.ram = CAM_INT_RAM; + cmd.address = CAM_ROMVER; + cmd.data[0] = camera->ccd->rom_version; + if (write_mem(camera, &cmd) != ERR_OK) + return(ERR_WRITE); + +/* Set baud rate */ + cmd.nbytes = 1; + cmd.address = CAM_BAUDRATE; + cmd.data[0] = baudrate; + if (write_mem(camera, &cmd) != ERR_OK) + return(ERR_WRITE); + + camera->ccd->baudrate = baudrate; + switch (baudrate) { + case CAM_B57600: + camera->serial->baudrate = B57600; + break; + case CAM_B19200: + camera->serial->baudrate = B19200; + break; + case CAM_B9600: + camera->serial->baudrate = B9600; + break; + case CAM_B4800: + camera->serial->baudrate = B4800; + break; + case CAM_B2400: + camera->serial->baudrate = B2400; + break; + case CAM_B1200: + camera->serial->baudrate = B1200; + break; + case CAM_B600: + camera->serial->baudrate = B600; + break; + case CAM_B300: + camera->serial->baudrate = B300; + break; + default: + return(ERR_BAUDRATE); + } + set_serial(camera->serial); + if (camera->serial->err_code != ERR_OK) + return(ERR_SERIAL); + + /* Read ROM version for testing */ + cmd.nbytes = 1; + cmd.address = CAM_ROMVER; + tmp = read_mem(camera, &cmd); + if (tmp != ERR_OK) + return(ERR_READ); + if (cmd.data[0] != camera->ccd->rom_version) + return(ERR_SERIAL); + + return(ERR_OK); +} + + +int expose_ccd(cam_info *camera, float time) +{ +cmd_t cmd; +int tmp, res; + +if (!is_valid_camera(camera)) + return(ERR_CAM); + +/* Set exposure time */ + cmd.nbytes = 2; + cmd.address = CAM_EXPTIME; + cmd.ram = CAM_INT_RAM; + camera->ccd->exptime = time; + tmp = time * 100; + cmd.data[0] = tmp & 0x00FF; + cmd.data[1] = (tmp >> 8) & 0x00FF; + res = write_mem(camera, &cmd); + if (res != ERR_OK) + return(res); + +/* Start exposure */ + cmd.nbytes = 1; + cmd.address = CAM_MODE; + camera->ccd->mode_flag &= ~CAM_MODE_STEXP; + cmd.data[0] = camera->ccd->mode_flag | CAM_MODE_STEXP; + res = write_mem(camera, &cmd); + if (res != ERR_OK) + return(res); + +/* Wait until exposure is done */ + cmd.nbytes = 1; + cmd.address = CAM_MODE; + sleep((int)time); + do { + if ((res = read_mem(camera, &cmd)) != ERR_OK) + return(res); + sleep(1); + } while((cmd.data[0] & CAM_MODE_EXPDONE) == CAM_MODE_EXPDONE); + +return(ERR_OK); +} + + +int read_line(cam_info *camera, int line, unsigned char *buf) +{ + unsigned char chksum; + int res; + + if (!is_valid_camera(camera)) + return(ERR_CAM); + if ((line < CAM_FIRST_LINE ) || (line > CAM_LAST_LINE)) + return; + buf[0] = line; + buf[1] = line; + res = write_serial(camera->serial, buf, 2); + if (res < 2) + return(ERR_WRITE); + res = read_serial(camera->serial, buf, 2); + if (res < 2) + return(ERR_READ); + if ((buf[0] != line) || (buf[1] != camera->ccd->ncolumns)) + return(ERR_READ); + + res = read_serial(camera->serial, buf, camera->ccd->ncolumns); + if (res < camera->ccd->ncolumns) + return(ERR_READ); + + res = read_serial(camera->serial, &chksum, 1); + if (res < 1) + return(ERR_READ); +/* Evaluate checksum in future */ + + return(ERR_OK); +} + + +int take_image(cam_info *camera, unsigned char *image) +{ + int res; + if (!is_valid_camera(camera)) + return(ERR_CAM); + if (camera->ccd->image == NULL) + return(ERR_MEM); + + if ((res = expose_ccd(camera, camera->ccd->exptime)) != ERR_OK) + return(res); + + if ((res = download_image(camera, image)) != ERR_OK) + return(res); + + return(ERR_OK); + +} + + +int download_image(cam_info *camera, unsigned char *image) +{ + int i, l; + + if (!is_valid_camera(camera)) + return(ERR_CAM); + if (image == NULL) + return(ERR_MEM); + + l = camera->ccd->first_line; + for (i = 0; i < camera->ccd->nlines; i++, l++) { + if (read_line(camera, l , image) != ERR_OK) + return(ERR_READ); +/* Pad to next line */ + image += CAM_MAX_NCOLS; + } + + return(ERR_OK); +} diff --git a/src/drivers/camfits.st4/ccd.h b/src/drivers/camfits.st4/ccd.h new file mode 100644 index 00000000..57741571 --- /dev/null +++ b/src/drivers/camfits.st4/ccd.h @@ -0,0 +1,120 @@ +#include "ser.h" + +#define ROM_VERSION (3) + +#define CAM_MAX_NLINES (165) +#define CAM_MAX_NCOLS (192) + +#define CAM_FIRST_LINE (64) +#define CAM_LAST_LINE (228) + +/* Commands */ +#define CAM_WRITE_MEM (1) +#define CAM_READ_MEM (2) +#define CAM_EXT_RAM (0) +#define CAM_INT_RAM (1) + +/* Responses */ +#define CAM_ACK (0x06) +#define CAM_NAK (0x15) + +/* Mode flags (46) */ +#define CAM_MODE_FEXP (0x80) /* Full exposure */ +#define CAM_MODE_LA (0x40) /* Light array */ +#define CAM_MODE_STEXP (0x20) /* Start exposure */ +#define CAM_MODE_EXPDONE (0x10) /* Exposure done */ +#define CAM_MODE_CMPRSIMG (0x08) /* Compress image in memory */ +#define CAM_MODE_SUBDARKF (0x04) /* Subtract dark frame */ +#define CAM_MODE_CMPRSDAT (0x02) /* Enable data compression */ +#define CAM_MODE_SMTH (0x01) /* Smooth image in memory */ + +/* Format flags (47) */ +#define CAM_FMT_FFOCUS (0x80) /* Find and focus mode */ +#define CAM_FMT_ANTIBLOOM (0x40) /* Anti-blooming gate enable */ +#define CAM_FMT_INTERRUPT (0x20) /* Interrupt from Find and focus, Track */ + /* and Calibrate modes. */ +#define CAM_FMT_TRACK (0x10) /* Track mode */ +#define CAM_FMT_CALIBRATE (0x08) /* Calibrate mode */ +#define CAM_FMT_CLOSE_R0 (0x04) /* Close `left' relay */ +#define CAM_FMT_CLOSE_R1 (0x05) /* Close `right' relay */ +#define CAM_FMT_CLOSE_R2 (0x06) /* Close `up' relay */ +#define CAM_FMT_CLOSE_R3 (0x07) /* Close `down' relay */ + +/* Baud rates */ +#define CAM_B57600 (255) +#define CAM_B19200 (253) +#define CAM_B9600 (250) +#define CAM_B4800 (244) +#define CAM_B2400 (232) +#define CAM_B1200 (208) +#define CAM_B600 (160) +#define CAM_B300 (64) + +/* Addresses */ +#define CAM_LAST_KEY (45) +#define CAM_MODE (46) +#define CAM_FORMAT (47) +#define CAM_EXPTIME (48) +#define CAM_XPOINTER (50) +#define CAM_NBYTES (51) +#define CAM_OFFSET (52) +#define CAM_VREFPLUS (53) +#define CAM_VREFMINUS (54) +#define CAM_ROMVER (55) +#define CAM_BAUDRATE (56) +#define CAM_INSTRUCT (58) + + +#define CAM_MAX_DATA (4) +#define CAM_MAX_CMD (CAM_MAX_DATA + 6) + + +typedef struct { + float exptime; /* seconds */ + unsigned char mode_flag; + unsigned char format_flag; + unsigned char first_line; + unsigned char first_column; + unsigned char nlines; + unsigned char ncolumns; + unsigned char offset; + unsigned char vref_plus; + unsigned char vref_minus; + unsigned char rom_version; + unsigned char baudrate; /* note: this is the constant specified */ + /* above, e.g. CAM_BXXXXX. */ + unsigned char *image; +} ccd_info; + +typedef struct { + ccd_info *ccd; + ser_info *serial; +} cam_info; + +typedef struct { + int nbytes; + int ram; + int address; + unsigned char data[CAM_MAX_DATA]; +} cmd_t; + + +/* Camera structure management */ +int is_valid_camera(cam_info *camera); +cam_info *alloc_camera(char *ttyname); +void free_camera(cam_info *camera); + +/* Low level functions */ +int set_baudrate(cam_info *camera, unsigned char baudrate); + /* values of baud rates are defined in camera.h */ +int init_camera(cam_info *camera); +int write_mem(cam_info *camera, cmd_t *command); +int read_mem(cam_info *camera, cmd_t *command); +unsigned char wait_response(cam_info *camera); + +/* user functions */ +int expose_ccd(cam_info *camera, float time); /* seconds */ +int read_line(cam_info *camera, int line, unsigned char *buf); +int download_image(cam_info *camera, unsigned char *image); +int set_camera(cam_info *camera); +int take_image(cam_info *camera, unsigned char *image); diff --git a/src/drivers/camfits.st4/errors.h b/src/drivers/camfits.st4/errors.h new file mode 100644 index 00000000..41941c97 --- /dev/null +++ b/src/drivers/camfits.st4/errors.h @@ -0,0 +1,19 @@ +#define TRUE (1) +#define FALSE (0) + + + +#define ERR_OK (0) +#define ERR_OPENTTY (-1) +#define ERR_SERIAL (-2) +#define ERR_BAUDRATE (-3) +#define ERR_PARITY (-4) +#define ERR_STOPB (-5) +#define ERR_WRITE (-6) +#define ERR_READ (-7) +#define ERR_MEM (-8) +#define ERR_NOTRESPOND (-9) + +#define ERR_CAM (-50) +#define ERR_CMD (-51) +#define ERR_EXPOSE (-52) diff --git a/src/drivers/camfits.st4/fitsio.h b/src/drivers/camfits.st4/fitsio.h new file mode 100644 index 00000000..93301278 --- /dev/null +++ b/src/drivers/camfits.st4/fitsio.h @@ -0,0 +1,1355 @@ +#ifndef _FITSIO_H +#define _FITSIO_H + +#include +/* stddef.h is apparently needed to define size_t */ +#include + +/* The following exclusion if __CINT__ is defined is needed for ROOT */ +#ifndef __CINT__ +#include "longnam.h" +#endif + +/* global variables */ + +#define FLEN_FILENAME 1025 /* max length of a filename */ +#define FLEN_KEYWORD 72 /* max length of a keyword (HIERARCH convention) */ +#define FLEN_CARD 81 /* length of a FITS header card */ +#define FLEN_VALUE 71 /* max length of a keyword value string */ +#define FLEN_COMMENT 73 /* max length of a keyword comment string */ +#define FLEN_ERRMSG 81 /* max length of a FITSIO error message */ +#define FLEN_STATUS 31 /* max length of a FITSIO status text string */ + +#define TBIT 1 /* codes for FITS table data types */ +#define TBYTE 11 +#define TLOGICAL 14 +#define TSTRING 16 +#define TUSHORT 20 +#define TSHORT 21 +#define TUINT 30 +#define TINT 31 +#define TULONG 40 +#define TLONG 41 +#define TFLOAT 42 +#define TDOUBLE 82 +#define TCOMPLEX 83 +#define TDBLCOMPLEX 163 + +#define TYP_STRUC_KEY 10 +#define TYP_CMPRS_KEY 20 +#define TYP_SCAL_KEY 30 +#define TYP_NULL_KEY 40 +#define TYP_DIM_KEY 50 +#define TYP_RANG_KEY 60 +#define TYP_UNIT_KEY 70 +#define TYP_DISP_KEY 80 +#define TYP_HDUID_KEY 90 +#define TYP_CKSUM_KEY 100 +#define TYP_WCS_KEY 110 +#define TYP_REFSYS_KEY 120 +#define TYP_COMM_KEY 130 +#define TYP_CONT_KEY 140 +#define TYP_USER_KEY 150 + +#define INT32BIT int /* 32-bit integer datatype. Currently this */ + /* datatype is an 'int' on all useful platforms */ + /* however, it is possible that that are cases */ + /* where 'int' is a 2-byte integer, in which case */ + /* FITSINT would need to be defined as 'long'. */ + +#define BYTE_IMG 8 /* BITPIX code values for FITS image types */ +#define SHORT_IMG 16 +#define LONG_IMG 32 +#define FLOAT_IMG -32 +#define DOUBLE_IMG -64 + /* The following 2 codes are not true FITS */ + /* datatypes; these codes are only used internally */ + /* within cfitsio to make it easier for users */ + /* to deal with unsigned integers. */ +#define USHORT_IMG 20 +#define ULONG_IMG 40 + +#define IMAGE_HDU 0 /* Primary Array or IMAGE HDU */ +#define ASCII_TBL 1 /* ASCII table HDU */ +#define BINARY_TBL 2 /* Binary table HDU */ +#define ANY_HDU -1 /* matches any HDU type */ + +#define READONLY 0 /* options when opening a file */ +#define READWRITE 1 + +/* adopt a hopefully obscure number to use as a null value flag */ +/* could be problems if the FITS files contain data with these values */ +#define FLOATNULLVALUE -9.11912E-36F +#define DOUBLENULLVALUE -9.1191291391491E-36 + +/* Image compression algorithm types */ +#define MAX_COMPRESS_DIM 6 +#define RICE_1 11 +#define GZIP_1 21 +#define PLIO_1 31 +#define HCOMPRESS_1 41 + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define CASESEN 1 /* do case-sensitive string match */ +#define CASEINSEN 0 /* do case-insensitive string match */ + +#define MAXHDU 1000 /* maximum number of extensions allowed in a FITS file */ + +#define GT_ID_ALL_URI 0 /* hierarchical grouping parameters */ +#define GT_ID_REF 1 +#define GT_ID_POS 2 +#define GT_ID_ALL 3 +#define GT_ID_REF_URI 11 +#define GT_ID_POS_URI 12 + +#define OPT_RM_GPT 0 +#define OPT_RM_ENTRY 1 +#define OPT_RM_MBR 2 +#define OPT_RM_ALL 3 + +#define OPT_GCP_GPT 0 +#define OPT_GCP_MBR 1 +#define OPT_GCP_ALL 2 + +#define OPT_MCP_ADD 0 +#define OPT_MCP_NADD 1 +#define OPT_MCP_REPL 2 +#define OPT_MCP_MOV 3 + +#define OPT_MRG_COPY 0 +#define OPT_MRG_MOV 1 + +#define OPT_CMT_MBR 1 +#define OPT_CMT_MBR_DEL 11 + +typedef struct /* structure used to store table column information */ +{ + char ttype[70]; /* column name = FITS TTYPEn keyword; */ + long tbcol; /* offset in row to first byte of each column */ + int tdatatype; /* datatype code of each column */ + long trepeat; /* repeat count of column; number of elements */ + double tscale; /* FITS TSCALn linear scaling factor */ + double tzero; /* FITS TZEROn linear scaling zero point */ + long tnull; /* FITS null value for int image or binary table cols */ + char strnull[20]; /* FITS null value string for ASCII table columns */ + char tform[10]; /* FITS tform keyword value */ + long twidth; /* width of each ASCII table column */ +}tcolumn; + +#define VALIDSTRUC 555 /* magic value used to identify if structure is valid */ + +typedef struct /* structure used to store basic FITS file information */ +{ + int filehandle; /* handle returned by the file open function */ + int driver; /* defines which set of I/O drivers should be used */ + int open_count; /* number of opened 'fitsfiles' using this structure */ + char *filename; /* file name */ + int validcode; /* magic value used to verify that structure is valid */ + long filesize; /* current size of the physical disk file in bytes */ + long logfilesize; /* logical size of file, including unflushed buffers */ + int lasthdu; /* is this the last HDU in the file? 0 = no, else yes */ + long bytepos; /* current logical I/O pointer position in file */ + long io_pos; /* current I/O pointer position in the physical file */ + int curbuf; /* number of I/O buffer currently in use */ + int curhdu; /* current HDU number; 0 = primary array */ + int hdutype; /* 0 = primary array, 1 = ASCII table, 2 = binary table */ + int writemode; /* 0 = readonly, 1 = readwrite */ + int maxhdu; /* highest numbered HDU known to exist in the file */ + long headstart[MAXHDU + 1]; /* byte offset in file to start of each HDU */ + long headend; /* byte offest in file to end of the current HDU header */ + long nextkey; /* byte offset in file to beginning of next keyword */ + long datastart; /* byte offset in file to start of the current data unit */ + int tfield; /* number of fields in the table (primary array has 2 */ + long origrows; /* original number of rows (value of NAXIS2 keyword) */ + long numrows; /* number of rows in the table (dynamically updated) */ + long rowlength; /* total length of a table row, in bytes */ + tcolumn *tableptr; /* pointer to the table structure */ + long heapstart; /* heap start byte relative to start of data unit */ + long heapsize; /* size of the heap, in bytes */ + + /* the following elements are related to compress images */ + int compressimg; /* 1 if HDU contains a compressed image, else 0 */ + char zcmptype[12]; /* compression type string */ + int compress_type; /* type of compression algorithm */ + int zbitpix; /* FITS data type of image (BITPIX) */ + int zndim; /* dimension of image */ + long znaxis[MAX_COMPRESS_DIM]; /* length of each axis */ + long tilesize[MAX_COMPRESS_DIM]; /* size of compression tiles */ + long maxtilelen; /* max number of pixels in each image tile */ + long maxelem; /* maximum length of variable length arrays */ + + int cn_compressed; /* column number for COMPRESSED_DATA column */ + int cn_uncompressed; /* column number for UNCOMPRESSED_DATA column */ + int cn_zscale; /* column number for ZSCALE column */ + int cn_zzero; /* column number for ZZERO column */ + int cn_zblank; /* column number for the ZBLANK column */ + + double zscale; /* scaling value, if same for all tiles */ + double zzero; /* zero pt, if same for all tiles */ + int zblank; /* value for null pixels, if not a column */ + + int rice_blocksize; /* first compression parameter */ + int rice_nbits; /* second compression parameter */ +} FITSfile; + +typedef struct /* structure used to store basic HDU information */ +{ + int HDUposition; /* HDU position in file; 0 = first HDU */ + FITSfile *Fptr; /* pointer to FITS file structure */ +}fitsfile; + +typedef struct /* structure for the iterator function column information */ +{ + /* elements required as input to fits_iterate_data: */ + + fitsfile *fptr; /* pointer to the HDU containing the column */ + int colnum; /* column number in the table (use name if < 1) */ + char colname[70]; /* name (= TTYPEn value) of the column (optional) */ + int datatype; /* output datatype (converted if necessary */ + int iotype; /* = InputCol, InputOutputCol, or OutputCol */ + + /* output elements that may be useful for the work function: */ + + void *array; /* pointer to the array (and the null value) */ + long repeat; /* binary table vector repeat value */ + long tlmin; /* legal minimum data value */ + long tlmax; /* legal maximum data value */ + char tunit[70]; /* physical unit string */ + char tdisp[70]; /* suggested display format */ + +} iteratorCol; + +#define InputCol 0 /* flag for input only iterator column */ +#define InputOutputCol 1 /* flag for input and output iterator column */ +#define OutputCol 2 /* flag for output only iterator column */ + +/* error status codes */ + +#define USE_MEM_BUFF -101 /* use memory buffer when opening file */ +#define OVERFLOW_ERR -11 /* overflow during datatype conversion */ +#define SAME_FILE 101 /* input and output files are the same */ +#define TOO_MANY_FILES 103 /* tried to open too many FITS files */ +#define FILE_NOT_OPENED 104 /* could not open the named file */ +#define FILE_NOT_CREATED 105 /* could not create the named file */ +#define WRITE_ERROR 106 /* error writing to FITS file */ +#define END_OF_FILE 107 /* tried to move past end of file */ +#define READ_ERROR 108 /* error reading from FITS file */ +#define FILE_NOT_CLOSED 110 /* could not close the file */ +#define ARRAY_TOO_BIG 111 /* array dimensions exceed internal limit */ +#define READONLY_FILE 112 /* Cannot write to readonly file */ +#define MEMORY_ALLOCATION 113 /* Could not allocate memory */ +#define BAD_FILEPTR 114 /* invalid fitsfile pointer */ +#define NULL_INPUT_PTR 115 /* NULL input pointer to routine */ +#define SEEK_ERROR 116 /* error seeking position in file */ + +#define BAD_URL_PREFIX 121 /* invalid URL prefix on file name */ +#define TOO_MANY_DRIVERS 122 /* tried to register too many IO drivers */ +#define DRIVER_INIT_FAILED 123 /* driver initialization failed */ +#define NO_MATCHING_DRIVER 124 /* matching driver is not registered */ +#define URL_PARSE_ERROR 125 /* failed to parse input file URL */ + +#define SHARED_ERRBASE (150) +#define SHARED_BADARG (SHARED_ERRBASE + 1) +#define SHARED_NULPTR (SHARED_ERRBASE + 2) +#define SHARED_TABFULL (SHARED_ERRBASE + 3) +#define SHARED_NOTINIT (SHARED_ERRBASE + 4) +#define SHARED_IPCERR (SHARED_ERRBASE + 5) +#define SHARED_NOMEM (SHARED_ERRBASE + 6) +#define SHARED_AGAIN (SHARED_ERRBASE + 7) +#define SHARED_NOFILE (SHARED_ERRBASE + 8) +#define SHARED_NORESIZE (SHARED_ERRBASE + 9) + +#define HEADER_NOT_EMPTY 201 /* header already contains keywords */ +#define KEY_NO_EXIST 202 /* keyword not found in header */ +#define KEY_OUT_BOUNDS 203 /* keyword record number is out of bounds */ +#define VALUE_UNDEFINED 204 /* keyword value field is blank */ +#define NO_QUOTE 205 /* string is missing the closing quote */ +#define BAD_KEYCHAR 207 /* illegal character in keyword name or card */ +#define BAD_ORDER 208 /* required keywords out of order */ +#define NOT_POS_INT 209 /* keyword value is not a positive integer */ +#define NO_END 210 /* couldn't find END keyword */ +#define BAD_BITPIX 211 /* illegal BITPIX keyword value*/ +#define BAD_NAXIS 212 /* illegal NAXIS keyword value */ +#define BAD_NAXES 213 /* illegal NAXISn keyword value */ +#define BAD_PCOUNT 214 /* illegal PCOUNT keyword value */ +#define BAD_GCOUNT 215 /* illegal GCOUNT keyword value */ +#define BAD_TFIELDS 216 /* illegal TFIELDS keyword value */ +#define NEG_WIDTH 217 /* negative table row size */ +#define NEG_ROWS 218 /* negative number of rows in table */ +#define COL_NOT_FOUND 219 /* column with this name not found in table */ +#define BAD_SIMPLE 220 /* illegal value of SIMPLE keyword */ +#define NO_SIMPLE 221 /* Primary array doesn't start with SIMPLE */ +#define NO_BITPIX 222 /* Second keyword not BITPIX */ +#define NO_NAXIS 223 /* Third keyword not NAXIS */ +#define NO_NAXES 224 /* Couldn't find all the NAXISn keywords */ +#define NO_XTENSION 225 /* HDU doesn't start with XTENSION keyword */ +#define NOT_ATABLE 226 /* the CHDU is not an ASCII table extension */ +#define NOT_BTABLE 227 /* the CHDU is not a binary table extension */ +#define NO_PCOUNT 228 /* couldn't find PCOUNT keyword */ +#define NO_GCOUNT 229 /* couldn't find GCOUNT keyword */ +#define NO_TFIELDS 230 /* couldn't find TFIELDS keyword */ +#define NO_TBCOL 231 /* couldn't find TBCOLn keyword */ +#define NO_TFORM 232 /* couldn't find TFORMn keyword */ +#define NOT_IMAGE 233 /* the CHDU is not an IMAGE extension */ +#define BAD_TBCOL 234 /* TBCOLn keyword value < 0 or > rowlength */ +#define NOT_TABLE 235 /* the CHDU is not a table */ +#define COL_TOO_WIDE 236 /* column is too wide to fit in table */ +#define COL_NOT_UNIQUE 237 /* more than 1 column name matches template */ +#define BAD_ROW_WIDTH 241 /* sum of column widths not = NAXIS1 */ +#define UNKNOWN_EXT 251 /* unrecognizable FITS extension type */ +#define UNKNOWN_REC 252 /* unrecognizable FITS record */ +#define END_JUNK 253 /* END keyword is not blank */ +#define BAD_HEADER_FILL 254 /* Header fill area not blank */ +#define BAD_DATA_FILL 255 /* Data fill area not blank or zero */ +#define BAD_TFORM 261 /* illegal TFORM format code */ +#define BAD_TFORM_DTYPE 262 /* unrecognizable TFORM datatype code */ +#define BAD_TDIM 263 /* illegal TDIMn keyword value */ + +#define BAD_HDU_NUM 301 /* HDU number < 1 or > MAXHDU */ +#define BAD_COL_NUM 302 /* column number < 1 or > tfields */ +#define NEG_FILE_POS 304 /* tried to move before beginning of file */ +#define NEG_BYTES 306 /* tried to read or write negative bytes */ +#define BAD_ROW_NUM 307 /* illegal starting row number in table */ +#define BAD_ELEM_NUM 308 /* illegal starting element number in vector */ +#define NOT_ASCII_COL 309 /* this is not an ASCII string column */ +#define NOT_LOGICAL_COL 310 /* this is not a logical datatype column */ +#define BAD_ATABLE_FORMAT 311 /* ASCII table column has wrong format */ +#define BAD_BTABLE_FORMAT 312 /* Binary table column has wrong format */ +#define NO_NULL 314 /* null value has not been defined */ +#define NOT_VARI_LEN 317 /* this is not a variable length column */ +#define BAD_DIMEN 320 /* illegal number of dimensions in array */ +#define BAD_PIX_NUM 321 /* first pixel number greater than last pixel */ +#define ZERO_SCALE 322 /* illegal BSCALE or TSCALn keyword = 0 */ +#define NEG_AXIS 323 /* illegal axis length < 1 */ + +#define NOT_GROUP_TABLE 340 +#define HDU_ALREADY_MEMBER 341 +#define MEMBER_NOT_FOUND 342 +#define GROUP_NOT_FOUND 343 +#define BAD_GROUP_ID 344 +#define TOO_MANY_HDUS_TRACKED 345 +#define HDU_ALREADY_TRACKED 346 +#define BAD_OPTION 347 +#define IDENTICAL_POINTERS 348 + +#define BAD_I2C 401 /* bad int to formatted string conversion */ +#define BAD_F2C 402 /* bad float to formatted string conversion */ +#define BAD_INTKEY 403 /* can't interprete keyword value as integer */ +#define BAD_LOGICALKEY 404 /* can't interprete keyword value as logical */ +#define BAD_FLOATKEY 405 /* can't interprete keyword value as float */ +#define BAD_DOUBLEKEY 406 /* can't interprete keyword value as double */ +#define BAD_C2I 407 /* bad formatted string to int conversion */ +#define BAD_C2F 408 /* bad formatted string to float conversion */ +#define BAD_C2D 409 /* bad formatted string to double conversion */ +#define BAD_DATATYPE 410 /* bad keyword datatype code */ +#define BAD_DECIM 411 /* bad number of decimal places specified */ +#define NUM_OVERFLOW 412 /* overflow during datatype conversion */ + +# define DATA_COMPRESSION_ERR 413 /* error in imcompress routines */ +# define DATA_DECOMPRESSION_ERR 414 /* error in imcompress routines */ + +#define BAD_DATE 420 /* error in date or time conversion */ + +#define PARSE_SYNTAX_ERR 431 /* syntax error in parser expression */ +#define PARSE_BAD_TYPE 432 /* expression did not evaluate to desired type */ +#define PARSE_LRG_VECTOR 433 /* vector result too large to return in array */ +#define PARSE_NO_OUTPUT 434 /* data parser failed not sent an out column */ +#define PARSE_BAD_COL 435 /* bad data encounter while parsing column */ +#define PARSE_BAD_OUTPUT 436 /* Output file not of proper type */ + +#define ANGLE_TOO_BIG 501 /* celestial angle too large for projection */ +#define BAD_WCS_VAL 502 /* bad celestial coordinate or pixel value */ +#define WCS_ERROR 503 /* error in celestial coordinate calculation */ +#define BAD_WCS_PROJ 504 /* unsupported type of celestial projection */ +#define NO_WCS_KEY 505 /* celestial coordinate keywords not found */ +#define APPROX_WCS_KEY 506 /* approximate WCS keywords were calculated */ + +/*------- following error codes are used in the grparser.c file -----------*/ +#define NGP_ERRBASE (360) /* base chosen so not to interfere with CFITSIO */ +#define NGP_OK (0) +#define NGP_NO_MEMORY (NGP_ERRBASE + 0) /* malloc failed */ +#define NGP_READ_ERR (NGP_ERRBASE + 1) /* read error from file */ +#define NGP_NUL_PTR (NGP_ERRBASE + 2) /* null pointer passed as argument */ +#define NGP_EMPTY_CURLINE (NGP_ERRBASE + 3) /* line read seems to be empty */ +#define NGP_UNREAD_QUEUE_FULL (NGP_ERRBASE + 4) /* cannot unread more then 1 line (or single line twice) */ +#define NGP_INC_NESTING (NGP_ERRBASE + 5) /* too deep include file nesting (inf. loop ?) */ +#define NGP_ERR_FOPEN (NGP_ERRBASE + 6) /* fopen() failed, cannot open file */ +#define NGP_EOF (NGP_ERRBASE + 7) /* end of file encountered */ +#define NGP_BAD_ARG (NGP_ERRBASE + 8) /* bad arguments passed */ +#define NGP_TOKEN_NOT_EXPECT (NGP_ERRBASE + 9) /* token not expected here */ + +/* The following exclusion if __CINT__ is defined is needed for ROOT */ +#ifndef __CINT__ +/* the following 3 lines are needed to support C++ compilers */ +#ifdef __cplusplus +extern "C" { +#endif +#endif + +/*---------------- FITS file URL parsing routines -------------*/ +int fits_get_token(char **ptr, char *delimiter, char *token, int *isanumber); +int ffiurl(char *url, char *urltype, char *infile, + char *outfile, char *extspec, char *rowfilter, + char *binspec, char *colspec, int *status); +int ffrtnm(char *url, char *rootname, int *status); +int ffourl(char *url, char *urltype, char *outfile, char *tmplfile, + int *status); +int ffexts(char *extspec, int *extnum, char *extname, int *extvers, + int *hdutype, char *colname, char *rowexpress, int *status); +int ffextn(char *url, int *extension_num, int *status); +int ffurlt(fitsfile *fptr, char *urlType, int *status); +int ffbins(char *binspec, int *imagetype, int *haxis, + char colname[4][FLEN_VALUE], double *minin, + double *maxin, double *binsizein, + char minname[4][FLEN_VALUE], char maxname[4][FLEN_VALUE], + char binname[4][FLEN_VALUE], double *weight, char *wtname, + int *recip, int *status); +int ffbinr(char **binspec, char *colname, double *minin, + double *maxin, double *binsizein, char *minname, + char *maxname, char *binname, int *status); +int ffimport_file( char *filename, char **contents, int *status ); + +/*---------------- FITS file I/O routines -------------*/ +int ffomem(fitsfile **fptr, const char *name, int mode, void **buffptr, + size_t *buffsize, size_t deltasize, + void *(*mem_realloc)(void *p, size_t newsize), + int *status); +int ffopen(fitsfile **fptr, const char *filename, int iomode, int *status); +int ffreopen(fitsfile *openfptr, fitsfile **newfptr, int *status); +int ffinit(fitsfile **fptr, const char *filename, int *status); +int fftplt(fitsfile **fptr, const char *filename, const char *tempname, + int *status); +int ffflus(fitsfile *fptr, int *status); +int ffclos(fitsfile *fptr, int *status); +int ffdelt(fitsfile *fptr, int *status); +int ffflnm(fitsfile *fptr, char *filename, int *status); +int ffflmd(fitsfile *fptr, int *filemode, int *status); + +/*---------------- utility routines -------------*/ +float ffvers(float *version); +void ffupch(char *string); +void ffgerr(int status, char *errtext); +void ffpmsg(const char *err_message); +int ffgmsg(char *err_message); +void ffcmsg(void); +void ffrprt(FILE *stream, int status); +void ffcmps(char *templt, char *colname, int casesen, int *match, + int *exact); +int fftkey(char *keyword, int *status); +int fftrec(char *card, int *status); +int ffnchk(fitsfile *fptr, int *status); +int ffkeyn(char *keyroot, int value, char *keyname, int *status); +int ffnkey(int value, char *keyroot, char *keyname, int *status); +int ffgkcl(char *card); +int ffdtyp(char *cval, char *dtype, int *status); +int ffpsvc(char *card, char *value, char *comm, int *status); +int ffgknm(char *card, char *name, int *length, int *status); +int ffgthd(char *tmplt, char *card, int *hdtype, int *status); +int ffasfm(char *tform, int *datacode, long *width, int *decim, int *status); +int ffbnfm(char *tform, int *datacode, long *repeat, long *width, int *status); +int ffgabc(int tfields, char **tform, int space, long *rowlen, long *tbcol, + int *status); + +/*----------------- write single keywords --------------*/ +int ffpky(fitsfile *fptr, int datatype, char *keyname, void *value, + char *comm, int *status); +int ffprec(fitsfile *fptr, const char *card, int *status); +int ffpcom(fitsfile *fptr, const char *comm, int *status); +int ffpunt(fitsfile *fptr, char *keyname, char *unit, int *status); +int ffphis(fitsfile *fptr, const char *history, int *status); +int ffpdat(fitsfile *fptr, int *status); +int ffgstm(char *timestr, int *timeref, int *status); +int ffgsdt(int *day, int *month, int *year, int *status); +int ffdt2s(int year, int month, int day, char *datestr, int *status); +int fftm2s(int year, int month, int day, int hour, int minute, double second, + int decimals, char *datestr, int *status); +int ffs2dt(char *datestr, int *year, int *month, int *day, int *status); +int ffs2tm(char *datestr, int *year, int *month, int *day, int *hour, + int *minute, double *second, int *status); +int ffpkyu(fitsfile *fptr, char *keyname, char *comm, int *status); +int ffpkys(fitsfile *fptr, char *keyname, char *value, char *comm,int *status); +int ffpkls(fitsfile *fptr, char *keyname, char *value, char *comm,int *status); +int ffplsw(fitsfile *fptr, int *status); +int ffpkyl(fitsfile *fptr, char *keyname, int value, char *comm, int *status); +int ffpkyj(fitsfile *fptr, char *keyname, long value, char *comm, int *status); +int ffpkyf(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffpkye(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffpkyg(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffpkyd(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffpkyc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffpkym(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); +int ffpkfc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffpkfm(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); +int ffpkyt(fitsfile *fptr, char *keyname, long intval, double frac, char *comm, + int *status); +int ffptdm( fitsfile *fptr, int colnum, int naxis, long naxes[], int *status); + +/*----------------- write array of keywords --------------*/ +int ffpkns(fitsfile *fptr, char *keyroot, int nstart, int nkey, char *value[], + char *comm[], int *status); +int ffpknl(fitsfile *fptr, char *keyroot, int nstart, int nkey, int *value, + char *comm[], int *status); +int ffpknj(fitsfile *fptr, char *keyroot, int nstart, int nkey, long *value, + char *comm[], int *status); +int ffpknf(fitsfile *fptr, char *keyroot, int nstart, int nkey, float *value, + int decim, char *comm[], int *status); +int ffpkne(fitsfile *fptr, char *keyroot, int nstart, int nkey, float *value, + int decim, char *comm[], int *status); +int ffpkng(fitsfile *fptr, char *keyroot, int nstart, int nkey, double *value, + int decim, char *comm[], int *status); +int ffpknd(fitsfile *fptr, char *keyroot, int nstart, int nkey, double *value, + int decim, char *comm[], int *status); +int ffcpky(fitsfile *infptr,fitsfile *outfptr,int incol,int outcol, + char *rootname, int *status); + +/*----------------- write required header keywords --------------*/ +int ffphps( fitsfile *fptr, int bitpix, int naxis, long naxes[], int *status); +int ffphpr( fitsfile *fptr, int simple, int bitpix, int naxis, long naxes[], + long pcount, long gcount, int extend, int *status); +int ffphtb(fitsfile *fptr, long naxis1, long naxis2, int tfields, char **ttype, + long *tbcol, char **tform, char **tunit, char *extname, int *status); +int ffphbn(fitsfile *fptr, long naxis2, int tfields, char **ttype, + char **tform, char **tunit, char *extname, long pcount, int *status); + +/*----------------- write template keywords --------------*/ +int ffpktp(fitsfile *fptr, const char *filename, int *status); + +/*------------------ get header information --------------*/ +int ffghsp(fitsfile *fptr, int *nexist, int *nmore, int *status); +int ffghps(fitsfile *fptr, int *nexist, int *position, int *status); + +/*------------------ move position in header -------------*/ +int ffmaky(fitsfile *fptr, int nrec, int *status); +int ffmrky(fitsfile *fptr, int nrec, int *status); + +/*------------------ read single keywords -----------------*/ +int ffgnxk(fitsfile *fptr, char **inclist, int ninc, char **exclist, + int nexc, char *card, int *status); +int ffgrec(fitsfile *fptr, int nrec, char *card, int *status); +int ffgcrd(fitsfile *fptr, char *keyname, char *card, int *status); +int ffgunt(fitsfile *fptr, char *keyname, char *unit, int *status); +int ffgkyn(fitsfile *fptr, int nkey, char *keyname, char *keyval, char *comm, + int *status); +int ffgkey(fitsfile *fptr, char *keyname, char *keyval, char *comm, + int *status); + +int ffgky( fitsfile *fptr, int datatype, char *keyname, void *value, + char *comm, int *status); +int ffgkys(fitsfile *fptr, char *keyname, char *value, char *comm, int *status); +int ffgkls(fitsfile *fptr, char *keyname, char **value, char *comm, int *status) +; +int ffgkyl(fitsfile *fptr, char *keyname, int *value, char *comm, int *status); +int ffgkyj(fitsfile *fptr, char *keyname, long *value, char *comm, int *status); +int ffgkye(fitsfile *fptr, char *keyname, float *value, char *comm,int *status); +int ffgkyd(fitsfile *fptr, char *keyname, double *value,char *comm,int *status); +int ffgkyc(fitsfile *fptr, char *keyname, float *value, char *comm,int *status); +int ffgkym(fitsfile *fptr, char *keyname, double *value,char *comm,int *status); +int ffgkyt(fitsfile *fptr, char *keyname, long *ivalue, double *dvalue, + char *comm, int *status); +int ffgtdm(fitsfile *fptr, int colnum, int maxdim, int *naxis, long naxes[], + int *status); +int ffdtdm(fitsfile *fptr, char *tdimstr, int colnum, int maxdim, + int *naxis, long naxes[], int *status); + +/*------------------ read array of keywords -----------------*/ +int ffgkns(fitsfile *fptr, char *keyname, int nstart, int nmax, char *value[], + int *nfound, int *status); +int ffgknl(fitsfile *fptr, char *keyname, int nstart, int nmax, int *value, + int *nfound, int *status); +int ffgknj(fitsfile *fptr, char *keyname, int nstart, int nmax, long *value, + int *nfound, int *status); +int ffgkne(fitsfile *fptr, char *keyname, int nstart, int nmax, float *value, + int *nfound, int *status); +int ffgknd(fitsfile *fptr, char *keyname, int nstart, int nmax, double *value, + int *nfound, int *status); +int ffh2st(fitsfile *fptr, char **header, int *status); + +/*----------------- read required header keywords --------------*/ +int ffghpr(fitsfile *fptr, int maxdim, int *simple, int *bitpix, int *naxis, + long naxes[], long *pcount, long *gcount, int *extend, int *status); + +int ffghtb(fitsfile *fptr,int maxfield, long *naxis1, long *naxis2, + int *tfields, char **ttype, long *tbcol, char **tform, char **tunit, + char *extname, int *status); + +int ffghbn(fitsfile *fptr, int maxfield, long *naxis2, int *tfields, + char **ttype, char **tform, char **tunit, char *extname, + long *pcount, int *status); + +/*--------------------- update keywords ---------------*/ +int ffuky(fitsfile *fptr, int datatype, char *keyname, void *value, + char *comm, int *status); +int ffucrd(fitsfile *fptr, char *keyname, char *card, int *status); +int ffukyu(fitsfile *fptr, char *keyname, char *comm, int *status); +int ffukys(fitsfile *fptr, char *keyname, char *value, char *comm, int *status); +int ffukls(fitsfile *fptr, char *keyname, char *value, char *comm, int *status); +int ffukyl(fitsfile *fptr, char *keyname, int value, char *comm, int *status); +int ffukyj(fitsfile *fptr, char *keyname, long value, char *comm, int *status); +int ffukyf(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffukye(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffukyg(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffukyd(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffukyc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffukym(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); +int ffukfc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffukfm(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); + +/*--------------------- modify keywords ---------------*/ +int ffmrec(fitsfile *fptr, int nkey, char *card, int *status); +int ffmcrd(fitsfile *fptr, char *keyname, char *card, int *status); +int ffmnam(fitsfile *fptr, char *oldname, char *newname, int *status); +int ffmcom(fitsfile *fptr, char *keyname, char *comm, int *status); +int ffmkyu(fitsfile *fptr, char *keyname, char *comm, int *status); +int ffmkys(fitsfile *fptr, char *keyname, char *value, char *comm,int *status); +int ffmkls(fitsfile *fptr, char *keyname, char *value, char *comm,int *status); +int ffmkyl(fitsfile *fptr, char *keyname, int value, char *comm, int *status); +int ffmkyj(fitsfile *fptr, char *keyname, long value, char *comm, int *status); +int ffmkyf(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffmkye(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffmkyg(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffmkyd(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffmkyc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffmkym(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); +int ffmkfc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffmkfm(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); + +/*--------------------- insert keywords ---------------*/ +int ffirec(fitsfile *fptr, int nkey, char *card, int *status); +int ffikyu(fitsfile *fptr, char *keyname, char *comm, int *status); +int ffikys(fitsfile *fptr, char *keyname, char *value, char *comm,int *status); +int ffikls(fitsfile *fptr, char *keyname, char *value, char *comm,int *status); +int ffikyl(fitsfile *fptr, char *keyname, int value, char *comm, int *status); +int ffikyj(fitsfile *fptr, char *keyname, long value, char *comm, int *status); +int ffikyf(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffikye(fitsfile *fptr, char *keyname, float value, int decim, char *comm, + int *status); +int ffikyg(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffikyd(fitsfile *fptr, char *keyname, double value, int decim, char *comm, + int *status); +int ffikyc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffikym(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); +int ffikfc(fitsfile *fptr, char *keyname, float *value, int decim, char *comm, + int *status); +int ffikfm(fitsfile *fptr, char *keyname, double *value, int decim, char *comm, + int *status); + +/*--------------------- delete keywords ---------------*/ +int ffdkey(fitsfile *fptr, char *keyname, int *status); +int ffdrec(fitsfile *fptr, int keypos, int *status); + +/*--------------------- get HDU information -------------*/ +int ffghdn(fitsfile *fptr, int *chdunum); +int ffghdt(fitsfile *fptr, int *exttype, int *status); +int ffghad(fitsfile *fptr, long *headstart, long *datastart, long *dataend, + int *status); +int ffgipr(fitsfile *fptr, int maxaxis, int *imgtype, int *naxis, + long *naxes, int *status); +int ffgidt(fitsfile *fptr, int *imgtype, int *status); +int ffgidm(fitsfile *fptr, int *naxis, int *status); +int ffgisz(fitsfile *fptr, int nlen, long *naxes, int *status); + +/*--------------------- HDU operations -------------*/ +int ffmahd(fitsfile *fptr, int hdunum, int *exttype, int *status); +int ffmrhd(fitsfile *fptr, int hdumov, int *exttype, int *status); +int ffmnhd(fitsfile *fptr, int exttype, char *hduname, int hduvers, + int *status); +int ffthdu(fitsfile *fptr, int *nhdu, int *status); +int ffcrhd(fitsfile *fptr, int *status); +int ffcrim(fitsfile *fptr, int bitpix, int naxis, long *naxes, int *status); +int ffcrtb(fitsfile *fptr, int tbltype, long naxis2, int tfields, char **ttype, + char **tform, char **tunit, char *extname, int *status); +int ffiimg(fitsfile *fptr, int bitpix, int naxis, long *naxes, int *status); +int ffitab(fitsfile *fptr, long naxis1, long naxis2, int tfields, char **ttype, + long *tbcol, char **tform, char **tunit, char *extname, int *status); +int ffibin(fitsfile *fptr,long naxis2, int tfields, char **ttype, char **tform, + char **tunit, char *extname, long pcount, int *status); +int ffrsim(fitsfile *fptr, int bitpix, int naxis, long *naxes, int *status); +int ffdhdu(fitsfile *fptr, int *hdutype, int *status); +int ffcopy(fitsfile *infptr, fitsfile *outfptr, int morekeys, int *status); +int ffcphd(fitsfile *infptr, fitsfile *outfptr, int *status); +int ffcpdt(fitsfile *infptr, fitsfile *outfptr, int *status); +int ffchfl(fitsfile *fptr, int *status); +int ffcdfl(fitsfile *fptr, int *status); + +int ffrdef(fitsfile *fptr, int *status); +int ffhdef(fitsfile *fptr, int morekeys, int *status); +int ffpthp(fitsfile *fptr, long theap, int *status); + +int ffcsum(fitsfile *fptr, long nrec, unsigned long *sum, int *status); +void ffesum(unsigned long sum, int complm, char *ascii); +unsigned long ffdsum(char *ascii, int complm, unsigned long *sum); +int ffpcks(fitsfile *fptr, int *status); +int ffupck(fitsfile *fptr, int *status); +int ffvcks(fitsfile *fptr, int *datastatus, int *hdustatus, int *status); +int ffgcks(fitsfile *fptr, unsigned long *datasum, unsigned long *hdusum, + int *status); + +/*--------------------- define scaling or null values -------------*/ +int ffpscl(fitsfile *fptr, double scale, double zero, int *status); +int ffpnul(fitsfile *fptr, long nulvalue, int *status); +int fftscl(fitsfile *fptr, int colnum, double scale, double zero, int *status); +int fftnul(fitsfile *fptr, int colnum, long nulvalue, int *status); +int ffsnul(fitsfile *fptr, int colnum, char *nulstring, int *status); + +/*--------------------- get column information -------------*/ +int ffgcno(fitsfile *fptr, int casesen, char *templt, int *colnum, + int *status); +int ffgcnn(fitsfile *fptr, int casesen, char *templt, char *colname, + int *colnum, int *status); + +int ffgtcl(fitsfile *fptr, int colnum, int *typecode, long *repeat, + long *width, int *status); +int ffgncl(fitsfile *fptr, int *ncols, int *status); +int ffgnrw(fitsfile *fptr, long *nrows, int *status); +int ffgacl(fitsfile *fptr, int colnum, char *ttype, long *tbcol, + char *tunit, char *tform, double *tscal, double *tzero, + char *tnull, char *tdisp, int *status); +int ffgbcl(fitsfile *fptr, int colnum, char *ttype, char *tunit, + char *dtype, long *repeat, double *tscal, double *tzero, + long *tnull, char *tdisp, int *status); +int ffgrsz(fitsfile *fptr, long *nrows, int *status); +int ffgcdw(fitsfile *fptr, int colnum, int *width, int *status); + +/*--------------------- read primary array or image elements -------------*/ +int ffgpv(fitsfile *fptr, int datatype, long firstelem, long nelem, + void *nulval, void *array, int *anynul, int *status); +int ffgpf(fitsfile *fptr, int datatype, long firstelem, long nelem, + void *array, char *nullarray, int *anynul, int *status); +int ffgpvb(fitsfile *fptr, long group, long firstelem, long nelem, unsigned + char nulval, unsigned char *array, int *anynul, int *status); +int ffgpvui(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned short nulval, unsigned short *array, int *anynul, + int *status); +int ffgpvi(fitsfile *fptr, long group, long firstelem, long nelem, + short nulval, short *array, int *anynul, int *status); +int ffgpvuj(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned long nulval, unsigned long *array, int *anynul, + int *status); +int ffgpvj(fitsfile *fptr, long group, long firstelem, long nelem, + long nulval, long *array, int *anynul, int *status); +int ffgpvuk(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned int nulval, unsigned int *array, int *anynul, int *status); +int ffgpvk(fitsfile *fptr, long group, long firstelem, long nelem, + int nulval, int *array, int *anynul, int *status); +int ffgpve(fitsfile *fptr, long group, long firstelem, long nelem, + float nulval, float *array, int *anynul, int *status); +int ffgpvd(fitsfile *fptr, long group, long firstelem, long nelem, + double nulval, double *array, int *anynul, int *status); + +int ffgpfb(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned char *array, char *nularray, int *anynul, int *status); +int ffgpfui(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned short *array, char *nularray, int *anynul, int *status); +int ffgpfi(fitsfile *fptr, long group, long firstelem, long nelem, + short *array, char *nularray, int *anynul, int *status); +int ffgpfuj(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned long *array, char *nularray, int *anynul, int *status); +int ffgpfj(fitsfile *fptr, long group, long firstelem, long nelem, + long *array, char *nularray, int *anynul, int *status); +int ffgpfuk(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned int *array, char *nularray, int *anynul, int *status); +int ffgpfk(fitsfile *fptr, long group, long firstelem, long nelem, + int *array, char *nularray, int *anynul, int *status); +int ffgpfe(fitsfile *fptr, long group, long firstelem, long nelem, + float *array, char *nularray, int *anynul, int *status); +int ffgpfd(fitsfile *fptr, long group, long firstelem, long nelem, + double *array, char *nularray, int *anynul, int *status); + +int ffg2db(fitsfile *fptr, long group, unsigned char nulval, long ncols, + long naxis1, long naxis2, unsigned char *array, + int *anynul, int *status); +int ffg2dui(fitsfile *fptr, long group, unsigned short nulval, long ncols, + long naxis1, long naxis2, unsigned short *array, + int *anynul, int *status); +int ffg2di(fitsfile *fptr, long group, short nulval, long ncols, + long naxis1, long naxis2, short *array, + int *anynul, int *status); +int ffg2duj(fitsfile *fptr, long group, unsigned long nulval, long ncols, + long naxis1, long naxis2, unsigned long *array, + int *anynul, int *status); +int ffg2dj(fitsfile *fptr, long group, long nulval, long ncols, + long naxis1, long naxis2, long *array, + int *anynul, int *status); +int ffg2duk(fitsfile *fptr, long group, unsigned int nulval, long ncols, + long naxis1, long naxis2, unsigned int *array, + int *anynul, int *status); +int ffg2dk(fitsfile *fptr, long group, int nulval, long ncols, + long naxis1, long naxis2, int *array, + int *anynul, int *status); +int ffg2de(fitsfile *fptr, long group, float nulval, long ncols, + long naxis1, long naxis2, float *array, + int *anynul, int *status); +int ffg2dd(fitsfile *fptr, long group, double nulval, long ncols, + long naxis1, long naxis2, double *array, + int *anynul, int *status); + +int ffg3db(fitsfile *fptr, long group, unsigned char nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + unsigned char *array, int *anynul, int *status); +int ffg3dui(fitsfile *fptr, long group, unsigned short nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + unsigned short *array, int *anynul, int *status); +int ffg3di(fitsfile *fptr, long group, short nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + short *array, int *anynul, int *status); +int ffg3duj(fitsfile *fptr, long group, unsigned long nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + unsigned long *array, int *anynul, int *status); +int ffg3dj(fitsfile *fptr, long group, long nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + long *array, int *anynul, int *status); +int ffg3duk(fitsfile *fptr, long group, unsigned int nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + unsigned int *array, int *anynul, int *status); +int ffg3dk(fitsfile *fptr, long group, int nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + int *array, int *anynul, int *status); +int ffg3de(fitsfile *fptr, long group, float nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + float *array, int *anynul, int *status); +int ffg3dd(fitsfile *fptr, long group, double nulval, long ncols, + long nrows, long naxis1, long naxis2, long naxis3, + double *array, int *anynul, int *status); + +int ffgsvb(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned char nulval, unsigned char *array, + int *anynul, int *status); +int ffgsvui(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned short nulval, unsigned short *array, + int *anynul, int *status); +int ffgsvi(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, short nulval, short *array, int *anynul, int *status); +int ffgsvuj(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned long nulval, unsigned long *array, + int *anynul, int *status); +int ffgsvj(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, long nulval, long *array, int *anynul, int *status); +int ffgsvuk(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned int nulval, unsigned int *array, + int *anynul, int *status); +int ffgsvk(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, int nulval, int *array, int *anynul, int *status); +int ffgsve(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, float nulval, float *array, int *anynul, int *status); +int ffgsvd(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, double nulval, double *array, int *anynul, + int *status); + +int ffgsfb(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned char *array, char *flagval, + int *anynul, int *status); +int ffgsfui(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned short *array, char *flagval, int *anynul, + int *status); +int ffgsfi(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, short *array, char *flagval, int *anynul, int *status); +int ffgsfuj(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned long *array, char *flagval, int *anynul, + int *status); +int ffgsfj(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, long *array, char *flagval, int *anynul, int *status); +int ffgsfuk(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, unsigned int *array, char *flagval, int *anynul, + int *status); +int ffgsfk(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, int *array, char *flagval, int *anynul, int *status); +int ffgsfe(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, float *array, char *flagval, int *anynul, int *status); +int ffgsfd(fitsfile *fptr, int colnum, int naxis, long *naxes, long *blc, + long *trc, long *inc, double *array, char *flagval, int *anynul, + int *status); + +int ffggpb(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned char *array, int *status); +int ffggpui(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned short *array, int *status); +int ffggpi(fitsfile *fptr, long group, long firstelem, long nelem, + short *array, int *status); +int ffggpuj(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned long *array, int *status); +int ffggpj(fitsfile *fptr, long group, long firstelem, long nelem, + long *array, int *status); +int ffggpuk(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned int *array, int *status); +int ffggpk(fitsfile *fptr, long group, long firstelem, long nelem, + int *array, int *status); +int ffggpe(fitsfile *fptr, long group, long firstelem, long nelem, + float *array, int *status); +int ffggpd(fitsfile *fptr, long group, long firstelem, long nelem, + double *array, int *status); + +/*--------------------- read column elements -------------*/ +int ffgcv( fitsfile *fptr, int datatype, int colnum, long firstrow, + long firstelem, long nelem, void *nulval, void *array, int *anynul, + int *status); +int ffgcf( fitsfile *fptr, int datatype, int colnum, long firstrow, + long firstelem, long nelem, void *array, char *nullarray, + int *anynul, int *status); +int ffgcvs(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, char *nulval, char **array, int *anynul, int *status); +int ffgcl (fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, char *array, int *status); +int ffgcvl (fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, char nulval, char *array, int *anynul, int *status); +int ffgcvb(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned char nulval, unsigned char *array, + int *anynul, int *status); +int ffgcvui(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned short nulval, unsigned short *array, + int *anynul, int *status); +int ffgcvi(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, short nulval, short *array, int *anynul, int *status); +int ffgcvuj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned long nulval, unsigned long *array, int *anynul, + int *status); +int ffgcvj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, long nulval, long *array, int *anynul, int *status); +int ffgcvuk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned int nulval, unsigned int *array, int *anynul, + int *status); +int ffgcvk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, int nulval, int *array, int *anynul, int *status); +int ffgcve(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, float nulval, float *array, int *anynul, int *status); +int ffgcvd(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, double nulval, double *array, int *anynul, int *status); +int ffgcvc(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, float nulval, float *array, int *anynul, int *status); +int ffgcvm(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, double nulval, double *array, int *anynul, int *status); +int ffgcx(fitsfile *fptr, int colnum, long firstrow, long firstbit, + long nbits, char *larray, int *status); +int ffgcxui(fitsfile *fptr, int colnum, long firstrow, long nrows, + long firstbit, int nbits, unsigned short *array, int *status); +int ffgcxuk(fitsfile *fptr, int colnum, long firstrow, long nrows, + long firstbit, int nbits, unsigned int *array, int *status); + +int ffgcfs(fitsfile *fptr, int colnum, long firstrow, long firstelem, long + nelem, char **array, char *nularray, int *anynul, int *status); +int ffgcfl(fitsfile *fptr, int colnum, long firstrow, long firstelem, long + nelem, char *array, char *nularray, int *anynul, int *status); +int ffgcfb(fitsfile *fptr, int colnum, long firstrow, long firstelem, long + nelem, unsigned char *array, char *nularray, int *anynul, int *status); +int ffgcfui(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned short *array, char *nularray, int *anynul, + int *status); +int ffgcfi(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, short *array, char *nularray, int *anynul, int *status); +int ffgcfuj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned long *array, char *nularray, int *anynul, + int *status); +int ffgcfj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, long *array, char *nularray, int *anynul, int *status); +int ffgcfuk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned int *array, char *nularray, int *anynul, + int *status); +int ffgcfk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, int *array, char *nularray, int *anynul, int *status); +int ffgcfe(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, float *array, char *nularray, int *anynul, int *status); +int ffgcfd(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, double *array, char *nularray, int *anynul, int *status); +int ffgcfc(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, float *array, char *nularray, int *anynul, int *status); +int ffgcfm(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, double *array, char *nularray, int *anynul, int *status); + +int ffgdes(fitsfile *fptr, int colnum, long rownum, long *length, + long *heapaddr, int *status); + +int ffgdess(fitsfile *fptr, int colnum, long firstrow, long nrows, long *length, + long *heapaddr, int *status); + +int ffgtbb(fitsfile *fptr, long firstrow, long firstchar, long nchars, + unsigned char *values, int *status); + +/*------------ write primary array or image elements -------------*/ +int ffppr(fitsfile *fptr, int datatype, long firstelem, long nelem, + void *array, int *status); +int ffpprb(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned char *array, int *status); +int ffpprui(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned short *array, int *status); +int ffppri(fitsfile *fptr, long group, long firstelem, + long nelem, short *array, int *status); +int ffppruj(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned long *array, int *status); +int ffpprj(fitsfile *fptr, long group, long firstelem, + long nelem, long *array, int *status); +int ffppruk(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned int *array, int *status); +int ffpprk(fitsfile *fptr, long group, long firstelem, + long nelem, int *array, int *status); +int ffppre(fitsfile *fptr, long group, long firstelem, + long nelem, float *array, int *status); +int ffpprd(fitsfile *fptr, long group, long firstelem, + long nelem, double *array, int *status); + +int ffppru(fitsfile *fptr, long group, long firstelem, long nelem, + int *status); +int ffpprn(fitsfile *fptr, long firstelem, long nelem, int *status); + +int ffppn(fitsfile *fptr, int datatype, long firstelem, long nelem, + void *array, void *nulval, int *status); +int ffppnb(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned char *array, unsigned char nulval, int *status); +int ffppnui(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned short *array, unsigned short nulval, + int *status); +int ffppni(fitsfile *fptr, long group, long firstelem, + long nelem, short *array, short nulval, int *status); +int ffppnj(fitsfile *fptr, long group, long firstelem, + long nelem, long *array, long nulval, int *status); +int ffppnuj(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned long *array, unsigned long nulval, int *status); +int ffppnuk(fitsfile *fptr, long group, long firstelem, long nelem, + unsigned int *array, unsigned int nulval, int *status); +int ffppnk(fitsfile *fptr, long group, long firstelem, + long nelem, int *array, int nulval, int *status); +int ffppne(fitsfile *fptr, long group, long firstelem, + long nelem, float *array, float nulval, int *status); +int ffppnd(fitsfile *fptr, long group, long firstelem, + long nelem, double *array, double nulval, int *status); + +int ffp2db(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, unsigned char *array, int *status); +int ffp2dui(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, unsigned short *array, int *status); +int ffp2di(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, short *array, int *status); +int ffp2duj(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, unsigned long *array, int *status); +int ffp2dj(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, long *array, int *status); +int ffp2duk(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, unsigned int *array, int *status); +int ffp2dk(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, int *array, int *status); +int ffp2de(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, float *array, int *status); +int ffp2dd(fitsfile *fptr, long group, long ncols, long naxis1, + long naxis2, double *array, int *status); + +int ffp3db(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, unsigned char *array, int *status); +int ffp3dui(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, unsigned short *array, int *status); +int ffp3di(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, short *array, int *status); +int ffp3duj(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, unsigned long *array, int *status); +int ffp3dj(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, long *array, int *status); +int ffp3duk(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, unsigned int *array, int *status); +int ffp3dk(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, int *array, int *status); +int ffp3de(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, float *array, int *status); +int ffp3dd(fitsfile *fptr, long group, long ncols, long nrows, long naxis1, + long naxis2, long naxis3, double *array, int *status); + +int ffpssb(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, unsigned char *array, int *status); +int ffpssui(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, unsigned short *array, int *status); +int ffpssi(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, short *array, int *status); +int ffpssuj(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, unsigned long *array, int *status); +int ffpssj(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, long *array, int *status); +int ffpssuk(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, unsigned int *array, int *status); +int ffpssk(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, int *array, int *status); +int ffpsse(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, float *array, int *status); +int ffpssd(fitsfile *fptr, long group, long naxis, long *naxes, + long *fpixel, long *lpixel, double *array, int *status); + +int ffpgpb(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned char *array, int *status); +int ffpgpui(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned short *array, int *status); +int ffpgpi(fitsfile *fptr, long group, long firstelem, + long nelem, short *array, int *status); +int ffpgpuj(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned long *array, int *status); +int ffpgpj(fitsfile *fptr, long group, long firstelem, + long nelem, long *array, int *status); +int ffpgpuk(fitsfile *fptr, long group, long firstelem, + long nelem, unsigned int *array, int *status); +int ffpgpk(fitsfile *fptr, long group, long firstelem, + long nelem, int *array, int *status); +int ffpgpe(fitsfile *fptr, long group, long firstelem, + long nelem, float *array, int *status); +int ffpgpd(fitsfile *fptr, long group, long firstelem, + long nelem, double *array, int *status); + +/*--------------------- iterator functions -------------*/ +int fits_iter_set_by_name(iteratorCol *col, fitsfile *fptr, char *colname, + int datatype, int iotype); +int fits_iter_set_by_num(iteratorCol *col, fitsfile *fptr, int colnum, + int datatype, int iotype); +int fits_iter_set_file(iteratorCol *col, fitsfile *fptr); +int fits_iter_set_colname(iteratorCol *col, char *colname); +int fits_iter_set_colnum(iteratorCol *col, int colnum); +int fits_iter_set_datatype(iteratorCol *col, int datatype); +int fits_iter_set_iotype(iteratorCol *col, int iotype); + +fitsfile * fits_iter_get_file(iteratorCol *col); +char * fits_iter_get_colname(iteratorCol *col); +int fits_iter_get_colnum(iteratorCol *col); +int fits_iter_get_datatype(iteratorCol *col); +int fits_iter_get_iotype(iteratorCol *col); +void * fits_iter_get_array(iteratorCol *col); +long fits_iter_get_tlmin(iteratorCol *col); +long fits_iter_get_tlmax(iteratorCol *col); +long fits_iter_get_repeat(iteratorCol *col); +char * fits_iter_get_tunit(iteratorCol *col); +char * fits_iter_get_tdisp(iteratorCol *col); + +int ffiter(int ncols, iteratorCol *data, long offset, long nPerLoop, + int (*workFn)( long totaln, long offset, long firstn, + long nvalues, int narrays, iteratorCol *data, void *userPointer), + void *userPointer, int *status); + +/*--------------------- write column elements -------------*/ +int ffpcl(fitsfile *fptr, int datatype, int colnum, long firstrow, + long firstelem, long nelem, void *array, int *status); +int ffpcls(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, char **array, int *status); +int ffpcll(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, char *array, int *status); +int ffpclb(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned char *array, int *status); +int ffpclui(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned short *array, int *status); +int ffpcli(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, short *array, int *status); +int ffpcluj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned long *array, int *status); +int ffpclj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, long *array, int *status); +int ffpcluk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned int *array, int *status); +int ffpclk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, int *array, int *status); +int ffpcle(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, float *array, int *status); +int ffpcld(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, double *array, int *status); +int ffpclc(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, float *array, int *status); +int ffpclm(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, double *array, int *status); +int ffpclu(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, int *status); +int ffpclx(fitsfile *fptr, int colnum, long frow, long fbit, long nbit, + char *larray, int *status); + +int ffpcn(fitsfile *fptr, int datatype, int colnum, long firstrow, + long firstelem, long nelem, void *array, void *nulval, int *status); +int ffpcns( fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, char **array, char *nulvalue, int *status); +int ffpcnl( fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, char *array, char nulvalue, int *status); +int ffpcnb(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned char *array, unsigned char nulvalue, + int *status); +int ffpcnui(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned short *array, unsigned short nulvalue, + int *status); +int ffpcni(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, short *array, short nulvalue, int *status); +int ffpcnuj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned long *array, unsigned long nulvalue, + int *status); +int ffpcnj(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, long *array, long nulvalue, int *status); +int ffpcnuk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, unsigned int *array, unsigned int nulvalue, + int *status); +int ffpcnk(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, int *array, int nulvalue, int *status); +int ffpcne(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, float *array, float nulvalue, int *status); +int ffpcnd(fitsfile *fptr, int colnum, long firstrow, long firstelem, + long nelem, double *array, double nulvalue, int *status); + +int ffpdes(fitsfile *fptr, int colnum, long rownum, long length, + long heapaddr, int *status); + +int ffptbb(fitsfile *fptr, long firstrow, long firstchar, long nchars, + unsigned char *values, int *status); + +int ffirow(fitsfile *fptr, long firstrow, long nrows, int *status); +int ffdrow(fitsfile *fptr, long firstrow, long nrows, int *status); +int ffdrws(fitsfile *fptr, long *rownum, long nrows, int *status); +int fficol(fitsfile *fptr, int numcol, char *ttype, char *tform, int *status); +int fficls(fitsfile *fptr, int firstcol, int ncols, char **ttype, + char **tform, int *status); +int ffmvec(fitsfile *fptr, int colnum, long newveclen, int *status); +int ffdcol(fitsfile *fptr, int numcol, int *status); +int ffcpcl(fitsfile *infptr, fitsfile *outfptr, int incol, int outcol, + int create_col, int *status); + +/*--------------------- WCS Utilities ------------------*/ +int ffgics(fitsfile *fptr, double *xrval, double *yrval, double *xrpix, + double *yrpix, double *xinc, double *yinc, double *rot, + char *type, int *status); +int ffgtcs(fitsfile *fptr, int xcol, int ycol, double *xrval, + double *yrval, double *xrpix, double *yrpix, double *xinc, + double *yinc, double *rot, char *type, int *status); +int ffwldp(double xpix, double ypix, double xref, double yref, + double xrefpix, double yrefpix, double xinc, double yinc, + double rot, char *type, double *xpos, double *ypos, int *status); +int ffxypx(double xpos, double ypos, double xref, double yref, + double xrefpix, double yrefpix, double xinc, double yinc, + double rot, char *type, double *xpix, double *ypix, int *status); + +/* WCS support routines (provide interface to Doug Mink's WCS library */ +int ffgiwcs(fitsfile *fptr, char **header, int *status); +int ffgtwcs(fitsfile *fptr, int xcol, int ycol, char **header, int *status); + +/*--------------------- lexical parsing routines ------------------*/ +int fftexp( fitsfile *fptr, char *expr, int maxdim, + int *datatype, long *nelem, int *naxis, + long *naxes, int *status ); + +int fffrow( fitsfile *infptr, char *expr, + long firstrow, long nrows, + long *n_good_rows, char *row_status, int *status); + +int ffffrw( fitsfile *fptr, char *expr, long *rownum, int *status); + +int fffrwc( fitsfile *fptr, char *expr, char *timeCol, + char *parCol, char *valCol, long ntimes, + double *times, char *time_status, int *status ); + +int ffsrow( fitsfile *infptr, fitsfile *outfptr, char *expr, + int *status); + +int ffcrow( fitsfile *fptr, int datatype, char *expr, + long firstrow, long nelements, void *nulval, + void *array, int *anynul, int *status ); + +int ffcalc_rng( fitsfile *infptr, char *expr, fitsfile *outfptr, + char *parName, char *parInfo, int nRngs, + long *start, long *end, int *status ); + +int ffcalc( fitsfile *infptr, char *expr, fitsfile *outfptr, + char *parName, char *parInfo, int *status ); + + /* ffhist is not really intended as a user-callable routine */ + /* but it may be useful for some specialized applications */ + +int ffhist(fitsfile **fptr, char *outfile, int imagetype, int naxis, + char colname[4][FLEN_VALUE], + double *minin, double *maxin, double *binsizein, + char minname[4][FLEN_VALUE], char maxname[4][FLEN_VALUE], + char binname[4][FLEN_VALUE], + double weightin, char wtcol[FLEN_VALUE], + int recip, char *rowselect, int *status); + +int fits_select_image_section(fitsfile **fptr, char *outfile, + char *imagesection, int *status); +int fits_select_section( fitsfile *infptr, fitsfile *outfptr, + char *imagesection, int *status); + +/*--------------------- grouping routines ------------------*/ + +int ffgtcr(fitsfile *fptr, char *grpname, int grouptype, int *status); +int ffgtis(fitsfile *fptr, char *grpname, int grouptype, int *status); +int ffgtch(fitsfile *gfptr, int grouptype, int *status); +int ffgtrm(fitsfile *gfptr, int rmopt, int *status); +int ffgtcp(fitsfile *infptr, fitsfile *outfptr, int cpopt, int *status); +int ffgtmg(fitsfile *infptr, fitsfile *outfptr, int mgopt, int *status); +int ffgtcm(fitsfile *gfptr, int cmopt, int *status); +int ffgtvf(fitsfile *gfptr, long *firstfailed, int *status); +int ffgtop(fitsfile *mfptr,int group,fitsfile **gfptr,int *status); +int ffgtam(fitsfile *gfptr, fitsfile *mfptr, int hdupos, int *status); +int ffgtnm(fitsfile *gfptr, long *nmembers, int *status); +int ffgmng(fitsfile *mfptr, long *nmembers, int *status); +int ffgmop(fitsfile *gfptr, long member, fitsfile **mfptr, int *status); +int ffgmcp(fitsfile *gfptr, fitsfile *mfptr, long member, int cpopt, + int *status); +int ffgmtf(fitsfile *infptr, fitsfile *outfptr, long member, int tfopt, + int *status); +int ffgmrm(fitsfile *fptr, long member, int rmopt, int *status); + +/*--------------------- group template parser routines ------------------*/ + +int fits_execute_template(fitsfile *ff, char *ngp_template, int *status); + +/*--------------------- image compression routines ------------------*/ + +int fits_comp_img(fitsfile *infptr, fitsfile *outfptr, int compress_type, + long *tilesize, int parm1, int parm2, int *status); +int fits_is_compressed_image(fitsfile *fptr, int *status); +int fits_decomp_img (fitsfile *infptr, fitsfile *outfptr, int *status); +int fits_read_compressed_img(fitsfile *fptr, + int datatype, long *fpixel,long *lpixel,long *inc, + int nullcheck, void *nulval, void *array, char *nullarray, + int *anynul, int *status); + +int fits_read_compressed_pixels(fitsfile *fptr, + int datatype, long fpixel, long npixels, + int nullcheck, void *nulval, void *array, char *nullarray, + int *anynul, int *status); + +int fits_quantize_float (float fdata[], int nx, float in_null_value, + int noise_bits, int idata[], double *bscale, double *bzero, + int *iminval, int *imaxval); +int fits_quantize_double (double fdata[], int nx, double in_null_value, + int noise_bits, int idata[], double *bscale, double *bzero, + int *iminval, int *imaxval); +int fits_rcomp(int a[], int nx, unsigned char *c, int clen,int nblock); +int fits_rdecomp (unsigned char *c, int clen, unsigned int array[], int nx, + int nblock); + +/* The following exclusion if __CINT__ is defined is needed for ROOT */ +#ifndef __CINT__ +#ifdef __cplusplus +} +#endif +#endif + +#endif + diff --git a/src/drivers/camfits.st4/longnam.h b/src/drivers/camfits.st4/longnam.h new file mode 100644 index 00000000..ac083bc5 --- /dev/null +++ b/src/drivers/camfits.st4/longnam.h @@ -0,0 +1,476 @@ +#ifndef _LONGNAME_H +#define _LONGNAME_H + +#define fits_parse_input_url ffiurl +#define fits_parse_rootname ffrtnm +#define fits_parse_output_url ffourl +#define fits_parse_extspec ffexts +#define fits_parse_extnum ffextn +#define fits_parse_binspec ffbins +#define fits_parse_binrange ffbinr +#define fits_open_memfile ffomem +#define fits_open_file ffopen +#define fits_reopen_file ffreopen +#define fits_create_file ffinit +#define fits_create_template fftplt +#define fits_flush_file ffflus +#define fits_close_file ffclos +#define fits_delete_file ffdelt +#define fits_file_name ffflnm +#define fits_file_mode ffflmd +#define fits_url_type ffurlt + +#define fits_get_version ffvers +#define fits_uppercase ffupch +#define fits_get_errstatus ffgerr +#define fits_write_errmsg ffpmsg +#define fits_read_errmsg ffgmsg +#define fits_clear_errmsg ffcmsg +#define fits_report_error ffrprt +#define fits_compare_str ffcmps +#define fits_test_keyword fftkey +#define fits_test_record fftrec +#define fits_null_check ffnchk +#define fits_make_keyn ffkeyn +#define fits_make_nkey ffnkey +#define fits_get_keyclass ffgkcl +#define fits_get_keytype ffdtyp +#define fits_parse_value ffpsvc +#define fits_get_keyname ffgknm +#define fits_parse_template ffgthd +#define fits_ascii_tform ffasfm +#define fits_binary_tform ffbnfm +#define fits_get_tbcol ffgabc +#define fits_get_rowsize ffgrsz +#define fits_get_col_display_width ffgcdw + +#define fits_write_record ffprec +#define fits_write_key ffpky +#define fits_write_key_unit ffpunt +#define fits_write_comment ffpcom +#define fits_write_history ffphis +#define fits_write_date ffpdat +#define fits_get_system_time ffgstm +#define fits_get_system_date ffgsdt +#define fits_date2str ffdt2s +#define fits_time2str fftm2s +#define fits_str2date ffs2dt +#define fits_str2time ffs2tm +#define fits_write_key_longstr ffpkls +#define fits_write_key_longwarn ffplsw +#define fits_write_key_null ffpkyu +#define fits_write_key_str ffpkys +#define fits_write_key_log ffpkyl +#define fits_write_key_lng ffpkyj +#define fits_write_key_fixflt ffpkyf +#define fits_write_key_flt ffpkye +#define fits_write_key_fixdbl ffpkyg +#define fits_write_key_dbl ffpkyd +#define fits_write_key_fixcmp ffpkfc +#define fits_write_key_cmp ffpkyc +#define fits_write_key_fixdblcmp ffpkfm +#define fits_write_key_dblcmp ffpkym +#define fits_write_key_triple ffpkyt +#define fits_write_tdim ffptdm +#define fits_write_keys_str ffpkns +#define fits_write_keys_log ffpknl +#define fits_write_keys_lng ffpknj +#define fits_write_keys_fixflt ffpknf +#define fits_write_keys_flt ffpkne +#define fits_write_keys_fixdbl ffpkng +#define fits_write_keys_dbl ffpknd +#define fits_copy_key ffcpky +#define fits_write_imghdr ffphps +#define fits_write_grphdr ffphpr +#define fits_write_atblhdr ffphtb +#define fits_write_btblhdr ffphbn +#define fits_write_key_template ffpktp + +#define fits_get_hdrspace ffghsp +#define fits_get_hdrpos ffghps +#define fits_movabs_key ffmaky +#define fits_movrel_key ffmrky +#define fits_find_nextkey ffgnxk + +#define fits_read_record ffgrec +#define fits_read_card ffgcrd +#define fits_read_key_unit ffgunt +#define fits_read_keyn ffgkyn +#define fits_read_key ffgky +#define fits_read_keyword ffgkey +#define fits_read_key_str ffgkys +#define fits_read_key_log ffgkyl +#define fits_read_key_lng ffgkyj +#define fits_read_key_flt ffgkye +#define fits_read_key_dbl ffgkyd +#define fits_read_key_cmp ffgkyc +#define fits_read_key_dblcmp ffgkym +#define fits_read_key_triple ffgkyt +#define fits_read_key_longstr ffgkls +#define fits_read_tdim ffgtdm +#define fits_decode_tdim ffdtdm +#define fits_read_keys_str ffgkns +#define fits_read_keys_log ffgknl +#define fits_read_keys_lng ffgknj +#define fits_read_keys_flt ffgkne +#define fits_read_keys_dbl ffgknd +#define fits_read_imghdr ffghpr +#define fits_read_atblhdr ffghtb +#define fits_read_btblhdr ffghbn +#define fits_header2str ffh2st + +#define fits_update_card ffucrd +#define fits_update_key ffuky +#define fits_update_key_null ffukyu +#define fits_update_key_str ffukys +#define fits_update_key_longstr ffukls +#define fits_update_key_log ffukyl +#define fits_update_key_lng ffukyj +#define fits_update_key_fixflt ffukyf +#define fits_update_key_flt ffukye +#define fits_update_key_fixdbl ffukyg +#define fits_update_key_dbl ffukyd +#define fits_update_key_fixcmp ffukfc +#define fits_update_key_cmp ffukyc +#define fits_update_key_fixdblcmp ffukfm +#define fits_update_key_dblcmp ffukym + +#define fits_modify_record ffmrec +#define fits_modify_card ffmcrd +#define fits_modify_name ffmnam +#define fits_modify_comment ffmcom +#define fits_modify_key_null ffmkyu +#define fits_modify_key_str ffmkys +#define fits_modify_key_longstr ffmkls +#define fits_modify_key_log ffmkyl +#define fits_modify_key_lng ffmkyj +#define fits_modify_key_fixflt ffmkyf +#define fits_modify_key_flt ffmkye +#define fits_modify_key_fixdbl ffmkyg +#define fits_modify_key_dbl ffmkyd +#define fits_modify_key_fixcmp ffmkfc +#define fits_modify_key_cmp ffmkyc +#define fits_modify_key_fixdblcmp ffmkfm +#define fits_modify_key_dblcmp ffmkym + +#define fits_insert_record ffirec +#define fits_insert_key_null ffikyu +#define fits_insert_key_str ffikys +#define fits_insert_key_longstr ffikls +#define fits_insert_key_log ffikyl +#define fits_insert_key_lng ffikyj +#define fits_insert_key_fixflt ffikyf +#define fits_insert_key_flt ffikye +#define fits_insert_key_fixdbl ffikyg +#define fits_insert_key_dbl ffikyd +#define fits_insert_key_fixcmp ffikfc +#define fits_insert_key_cmp ffikyc +#define fits_insert_key_fixdblcmp ffikfm +#define fits_insert_key_dblcmp ffikym + +#define fits_delete_key ffdkey +#define fits_delete_record ffdrec +#define fits_get_hdu_num ffghdn +#define fits_get_hdu_type ffghdt +#define fits_get_hduaddr ffghad + +#define fits_get_img_param ffgipr +#define fits_get_img_type ffgidt +#define fits_get_img_dim ffgidm +#define fits_get_img_size ffgisz + +#define fits_movabs_hdu ffmahd +#define fits_movrel_hdu ffmrhd +#define fits_movnam_hdu ffmnhd +#define fits_get_num_hdus ffthdu +#define fits_create_img ffcrim +#define fits_create_tbl ffcrtb +#define fits_create_hdu ffcrhd +#define fits_insert_img ffiimg +#define fits_insert_atbl ffitab +#define fits_insert_btbl ffibin +#define fits_resize_img ffrsim +#define fits_delete_hdu ffdhdu +#define fits_copy_hdu ffcopy +#define fits_copy_header ffcphd +#define fits_copy_data ffcpdt + +#define fits_set_hdustruc ffrdef +#define fits_set_hdrsize ffhdef +#define fits_write_theap ffpthp + +#define fits_encode_chksum ffesum +#define fits_decode_chksum ffdsum +#define fits_write_chksum ffpcks +#define fits_update_chksum ffupck +#define fits_verify_chksum ffvcks +#define fits_get_chksum ffgcks + +#define fits_set_bscale ffpscl +#define fits_set_tscale fftscl +#define fits_set_imgnull ffpnul +#define fits_set_btblnull fftnul +#define fits_set_atblnull ffsnul + +#define fits_get_colnum ffgcno +#define fits_get_colname ffgcnn +#define fits_get_coltype ffgtcl +#define fits_get_num_rows ffgnrw +#define fits_get_num_cols ffgncl +#define fits_get_acolparms ffgacl +#define fits_get_bcolparms ffgbcl + +#define fits_iterate_data ffiter + +#define fits_read_grppar_byt ffggpb +#define fits_read_grppar_usht ffggpui +#define fits_read_grppar_ulng ffggpuj +#define fits_read_grppar_sht ffggpi +#define fits_read_grppar_lng ffggpj +#define fits_read_grppar_int ffggpk +#define fits_read_grppar_uint ffggpuk +#define fits_read_grppar_flt ffggpe +#define fits_read_grppar_dbl ffggpd + +#define fits_read_img ffgpv +#define fits_read_imgnull ffgpf +#define fits_read_img_byt ffgpvb +#define fits_read_img_usht ffgpvui +#define fits_read_img_ulng ffgpvuj +#define fits_read_img_sht ffgpvi +#define fits_read_img_lng ffgpvj +#define fits_read_img_uint ffgpvuk +#define fits_read_img_int ffgpvk +#define fits_read_img_flt ffgpve +#define fits_read_img_dbl ffgpvd + +#define fits_read_imgnull_byt ffgpfb +#define fits_read_imgnull_usht ffgpfui +#define fits_read_imgnull_ulng ffgpfuj +#define fits_read_imgnull_sht ffgpfi +#define fits_read_imgnull_lng ffgpfj +#define fits_read_imgnull_uint ffgpfuk +#define fits_read_imgnull_int ffgpfk +#define fits_read_imgnull_flt ffgpfe +#define fits_read_imgnull_dbl ffgpfd + +#define fits_read_2d_byt ffg2db +#define fits_read_2d_usht ffg2dui +#define fits_read_2d_ulng ffg2duj +#define fits_read_2d_sht ffg2di +#define fits_read_2d_lng ffg2dj +#define fits_read_2d_uint ffg2duk +#define fits_read_2d_int ffg2dk +#define fits_read_2d_flt ffg2de +#define fits_read_2d_dbl ffg2dd + +#define fits_read_3d_byt ffg3db +#define fits_read_3d_usht ffg3dui +#define fits_read_3d_ulng ffg3duj +#define fits_read_3d_sht ffg3di +#define fits_read_3d_lng ffg3dj +#define fits_read_3d_uint ffg3duk +#define fits_read_3d_int ffg3dk +#define fits_read_3d_flt ffg3de +#define fits_read_3d_dbl ffg3dd + +#define fits_read_subset_byt ffgsvb +#define fits_read_subset_usht ffgsvui +#define fits_read_subset_ulng ffgsvuj +#define fits_read_subset_sht ffgsvi +#define fits_read_subset_lng ffgsvj +#define fits_read_subset_uint ffgsvuk +#define fits_read_subset_int ffgsvk +#define fits_read_subset_flt ffgsve +#define fits_read_subset_dbl ffgsvd + +#define fits_read_subsetnull_byt ffgsfb +#define fits_read_subsetnull_usht ffgsfui +#define fits_read_subsetnull_ulng ffgsfuj +#define fits_read_subsetnull_sht ffgsfi +#define fits_read_subsetnull_lng ffgsfj +#define fits_read_subsetnull_uint ffgsfuk +#define fits_read_subsetnull_int ffgsfk +#define fits_read_subsetnull_flt ffgsfe +#define fits_read_subsetnull_dbl ffgsfd + +#define fits_read_col ffgcv +#define fits_read_colnull ffgcf +#define fits_read_col_str ffgcvs +#define fits_read_col_log ffgcvl +#define fits_read_col_byt ffgcvb +#define fits_read_col_usht ffgcvui +#define fits_read_col_ulng ffgcvuj +#define fits_read_col_sht ffgcvi +#define fits_read_col_lng ffgcvj +#define fits_read_col_uint ffgcvuk +#define fits_read_col_int ffgcvk +#define fits_read_col_flt ffgcve +#define fits_read_col_dbl ffgcvd +#define fits_read_col_cmp ffgcvc +#define fits_read_col_dblcmp ffgcvm +#define fits_read_col_bit ffgcx +#define fits_read_col_bit_usht ffgcxui +#define fits_read_col_bit_uint ffgcxuk + +#define fits_read_colnull_str ffgcfs +#define fits_read_colnull_log ffgcfl +#define fits_read_colnull_byt ffgcfb +#define fits_read_colnull_usht ffgcfui +#define fits_read_colnull_ulng ffgcfuj +#define fits_read_colnull_sht ffgcfi +#define fits_read_colnull_lng ffgcfj +#define fits_read_colnull_uint ffgcfuk +#define fits_read_colnull_int ffgcfk +#define fits_read_colnull_flt ffgcfe +#define fits_read_colnull_dbl ffgcfd +#define fits_read_colnull_cmp ffgcfc +#define fits_read_colnull_dblcmp ffgcfm + +#define fits_read_descript ffgdes +#define fits_read_descripts ffgdess +#define fits_read_tblbytes ffgtbb + +#define fits_write_grppar_byt ffpgpb +#define fits_write_grppar_usht ffpgpui +#define fits_write_grppar_ulng ffpgpuj +#define fits_write_grppar_sht ffpgpi +#define fits_write_grppar_lng ffpgpj +#define fits_write_grppar_uint ffpgpuk +#define fits_write_grppar_int ffpgpk +#define fits_write_grppar_flt ffpgpe +#define fits_write_grppar_dbl ffpgpd + +#define fits_write_img ffppr +#define fits_write_img_byt ffpprb +#define fits_write_img_usht ffpprui +#define fits_write_img_ulng ffppruj +#define fits_write_img_sht ffppri +#define fits_write_img_lng ffpprj +#define fits_write_img_uint ffppruk +#define fits_write_img_int ffpprk +#define fits_write_img_flt ffppre +#define fits_write_img_dbl ffpprd + +#define fits_write_imgnull ffppn +#define fits_write_imgnull_byt ffppnb +#define fits_write_imgnull_usht ffppnui +#define fits_write_imgnull_ulng ffppnuj +#define fits_write_imgnull_sht ffppni +#define fits_write_imgnull_lng ffppnj +#define fits_write_imgnull_uint ffppnuk +#define fits_write_imgnull_int ffppnk +#define fits_write_imgnull_flt ffppne +#define fits_write_imgnull_dbl ffppnd + +#define fits_write_img_null ffppru +#define fits_write_null_img ffpprn + +#define fits_write_2d_byt ffp2db +#define fits_write_2d_usht ffp2dui +#define fits_write_2d_ulng ffp2duj +#define fits_write_2d_sht ffp2di +#define fits_write_2d_lng ffp2dj +#define fits_write_2d_uint ffp2duk +#define fits_write_2d_int ffp2dk +#define fits_write_2d_flt ffp2de +#define fits_write_2d_dbl ffp2dd + +#define fits_write_3d_byt ffp3db +#define fits_write_3d_usht ffp3dui +#define fits_write_3d_ulng ffp3duj +#define fits_write_3d_sht ffp3di +#define fits_write_3d_lng ffp3dj +#define fits_write_3d_uint ffp3duk +#define fits_write_3d_int ffp3dk +#define fits_write_3d_flt ffp3de +#define fits_write_3d_dbl ffp3dd + +#define fits_write_subset_byt ffpssb +#define fits_write_subset_usht ffpssui +#define fits_write_subset_ulng ffpssuj +#define fits_write_subset_sht ffpssi +#define fits_write_subset_lng ffpssj +#define fits_write_subset_uint ffpssuk +#define fits_write_subset_int ffpssk +#define fits_write_subset_flt ffpsse +#define fits_write_subset_dbl ffpssd + +#define fits_write_col ffpcl +#define fits_write_col_str ffpcls +#define fits_write_col_log ffpcll +#define fits_write_col_byt ffpclb +#define fits_write_col_usht ffpclui +#define fits_write_col_ulng ffpcluj +#define fits_write_col_sht ffpcli +#define fits_write_col_lng ffpclj +#define fits_write_col_uint ffpcluk +#define fits_write_col_int ffpclk +#define fits_write_col_flt ffpcle +#define fits_write_col_dbl ffpcld +#define fits_write_col_cmp ffpclc +#define fits_write_col_dblcmp ffpclm +#define fits_write_col_null ffpclu +#define fits_write_col_bit ffpclx + +#define fits_write_colnull ffpcn +#define fits_write_colnull_str ffpcns +#define fits_write_colnull_log ffpcnl +#define fits_write_colnull_byt ffpcnb +#define fits_write_colnull_usht ffpcnui +#define fits_write_colnull_ulng ffpcnuj +#define fits_write_colnull_sht ffpcni +#define fits_write_colnull_lng ffpcnj +#define fits_write_colnull_uint ffpcnuk +#define fits_write_colnull_int ffpcnk +#define fits_write_colnull_flt ffpcne +#define fits_write_colnull_dbl ffpcnd + +#define fits_write_descript ffpdes + +#define fits_write_tblbytes ffptbb +#define fits_insert_rows ffirow +#define fits_delete_rows ffdrow +#define fits_delete_rowlist ffdrws +#define fits_insert_col fficol +#define fits_insert_cols fficls +#define fits_delete_col ffdcol +#define fits_copy_col ffcpcl +#define fits_modify_vector_len ffmvec + +#define fits_read_img_coord ffgics +#define fits_read_tbl_coord ffgtcs +#define fits_pix_to_world ffwldp +#define fits_world_to_pix ffxypx + +#define fits_get_image_wcs_keys ffgiwcs +#define fits_get_table_wcs_keys ffgtwcs + +#define fits_find_rows fffrow +#define fits_find_first_row ffffrw +#define fits_find_rows_cmp fffrwc +#define fits_select_rows ffsrow +#define fits_calc_rows ffcrow +#define fits_calculator ffcalc +#define fits_calculator_rng ffcalc_rng +#define fits_test_expr fftexp + +#define fits_create_group ffgtcr +#define fits_insert_group ffgtis +#define fits_change_group ffgtch +#define fits_remove_group ffgtrm +#define fits_copy_group ffgtcp +#define fits_merge_groups ffgtmg +#define fits_compact_group ffgtcm +#define fits_verify_group ffgtvf +#define fits_open_group ffgtop +#define fits_add_group_member ffgtam +#define fits_get_num_members ffgtnm + +#define fits_get_num_groups ffgmng +#define fits_open_member ffgmop +#define fits_copy_member ffgmcp +#define fits_transfer_member ffgmtf +#define fits_remove_member ffgmrm + +#endif diff --git a/src/drivers/camfits.st4/ser.c b/src/drivers/camfits.st4/ser.c new file mode 100644 index 00000000..2eec8c93 --- /dev/null +++ b/src/drivers/camfits.st4/ser.c @@ -0,0 +1,249 @@ +/* This file contains the functions for serial communication */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "errors.h" +#include "ser.h" + +static void rdtimeout(int signumber); +static ser_info *sertmp; + +#ifdef COMMTEST + +/* This example program writes a string in port `tty', and waits until + timeout for a response. */ + + +char teste[] = "Write test..."; +unsigned char buf[25]; +char tty[] = "/dev/ttyS1"; + + +int main(void) +{ + int result; + ser_info porta; + + open_serial(&porta, tty); + if (porta.err_code != ERR_OK) return(-1); + puts("Communication test.\n\n"); + + /* 9600,8e1 */ + porta.baudrate = B9600; + porta.parity = PAR_EVEN; + porta.stopbits = 1; + porta.timeout = 10; + porta.minchars = 25; + porta.waitread = FALSE; + set_serial(&porta); + write_serial(&porta, teste, strlen(teste)); + + if (read_serial(&porta, buf, 24)) + printf("Text received: %s\n\n", buf); + else printf("Read timeout.\n\n"); + + close_serial(&porta); + return(0); +} +#endif /* COMMTEST */ + + +int is_valid_serial(ser_info *serial) +{ + if ((serial == NULL) || + (serial->tty_name == NULL) || + (serial->tio == NULL) || + (serial->oldtio == NULL)) + return(FALSE); + return(TRUE); +} + + +void open_serial(ser_info *serial, char *ttyname) +{ + if (serial == NULL) return; + + serial->err_code = ERR_OK; + signal(SIGALRM, rdtimeout); + +/* Allocate memory for the structures */ + serial->tio = (struct termios *) malloc(sizeof(struct termios)); + if (serial->tio == NULL) { + serial->err_code = ERR_MEM; + return; + } + serial->oldtio = (struct termios *) malloc(sizeof(struct termios)); + if (serial->oldtio == NULL) { + serial->err_code = ERR_MEM; + return; + } + + serial->tty_name = (char *) calloc(strlen(ttyname), sizeof(char)); + if (serial->tty_name == NULL) { + serial->err_code = ERR_MEM; + return; + } + strcpy(serial->tty_name, ttyname); + + + serial->ttyfd = open(serial->tty_name, O_RDWR | O_NOCTTY); + if (serial->ttyfd < 0) { + serial->err_code = ERR_OPENTTY; + close_serial(serial); + return; + } + +/* Save current config */ + tcgetattr(serial->ttyfd, serial->oldtio); + serial->isopen = TRUE; +} + + +int close_serial(ser_info *serial) +{ + if (!is_valid_serial(serial)) return(ERR_MEM); + signal(SIGALRM, SIG_DFL); +/* Restore terminal to its previous state; free memory */ + if (serial->isopen) { + tcsetattr(serial->ttyfd,TCSANOW,serial->tio); + close(serial->ttyfd); + } + if (serial->tio != NULL) free(serial->tio); + if (serial->oldtio != NULL) free(serial->oldtio); + if (serial->tty_name != NULL) free(serial->tty_name); + return(ERR_OK); +} + + +int set_serial(ser_info *serial) +{ + if (!is_valid_serial(serial)) return(ERR_MEM); + if (!serial->isopen) { + serial->err_code = ERR_SERIAL; + return(ERR_SERIAL); + } + memset(serial->tio, 0, sizeof(struct termios)); + + cfsetospeed(serial->tio, serial->baudrate); + cfsetispeed(serial->tio, serial->baudrate); + +/* Set byte size to 8bit, enable reading and set local mode */ + serial->tio->c_cflag |= CS8 | CLOCAL | CREAD; + switch (serial->parity) { + case PAR_NONE: + serial->tio->c_iflag |= IGNPAR; + serial->tio->c_cflag &= ~PARENB; + break; + + case PAR_EVEN: + serial->tio->c_iflag &= ~IGNPAR; + serial->tio->c_cflag |= PARENB; + serial->tio->c_cflag &= ~PARODD; + break; + + case PAR_ODD: + serial->tio->c_iflag &= ~IGNPAR; + serial->tio->c_cflag |= PARENB; + serial->tio->c_cflag &= PARODD; + break; + default: + serial->err_code = ERR_PARITY; + return; + } + + if (serial->stopbits == 1) + serial->tio->c_cflag &= ~CSTOPB; + else if (serial->stopbits ==2) + serial->tio->c_cflag |= CSTOPB; + else { + serial->err_code = ERR_STOPB; + return; + } + +/* Raw input */ + serial-> tio->c_lflag = 0; + + serial->tio->c_cc[VTIME] = serial->timeout; + if (serial->waitread) + serial->tio->c_cc[VMIN] = serial->minchars; + + tcsetattr(serial->ttyfd,TCSANOW,serial->tio); +} + + +int write_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes) +{ + int result; + + if (!is_valid_serial(serial)) return(ERR_MEM); + if (!serial->isopen) { + serial->err_code = ERR_SERIAL; + return(0); + } + result = write(serial->ttyfd, buf, nbytes); + if (result < nbytes) { + serial->err_code = ERR_WRITE; + return(result); + } + serial->err_code = ERR_OK; + return(result); +} + + +int read_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes) +{ + int result; + + if (!is_valid_serial(serial)) return(ERR_MEM); + if (!serial->isopen) { + serial->err_code = ERR_SERIAL; + return(0); + } + serial->err_code = ERR_OK; + +/* Set the minimum number of chars to read before returning */ + if (serial->waitread && (serial->minchars != nbytes)) { + if (set_minchars(serial, nbytes) != ERR_OK) { + serial->err_code = ERR_SERIAL; + return(0); + } + } +/* save serial stuff for alarm() timeout. */ + sertmp = serial; + alarm(serial->timeout); + result = read(serial->ttyfd, buf, nbytes); + alarm(0); + serial->bytesread = result; + if (result < nbytes) + serial->err_code = ERR_READ; + return(result); +} + + +static int set_minchars(ser_info *serial, int nbytes) +{ + if (!is_valid_serial(serial)) + return(ERR_MEM); + if (!serial->waitread) + return(ERR_READ); + serial->minchars = nbytes; + serial->tio->c_cc[VMIN] = nbytes; + tcsetattr(serial->ttyfd,TCSANOW,serial->tio); + return(ERR_OK); +} + +static void rdtimeout(int signumber) +{ + fprintf(stderr, "Serial communication failed, exiting...\n"); + if (sertmp != NULL) close_serial(sertmp); + exit(-1); +} + diff --git a/src/drivers/camfits.st4/ser.h b/src/drivers/camfits.st4/ser.h new file mode 100644 index 00000000..eb4ca68c --- /dev/null +++ b/src/drivers/camfits.st4/ser.h @@ -0,0 +1,34 @@ +#include + +#define RD_TIMEOUT (2) +#define TRUE (1) +#define FALSE (0) + +#define MAX_BUF_LEN (255) +#define PAR_NONE 'n' +#define PAR_EVEN 'e' +#define PAR_ODD 'o' + +typedef struct { + int isopen; + int waitread; + int err_code; + char *tty_name; + int ttyfd; + cc_t minchars; /* not used if waitread == FALSE */ + int bytesread; + int baudrate; + char parity; + int stopbits; + cc_t timeout; /* interchar time (1/10 secs), and read timeout (secs) */ + struct termios *tio; /* |- don't mess with these structs, */ + struct termios *oldtio; /* | the routines will handle it. */ +} ser_info; + +int is_valid_serial(ser_info *serial); +void open_serial(ser_info *serial, char *ttyname); +int close_serial(ser_info *serial); +int set_serial(ser_info *serial); +static int set_minchars(ser_info *serial, int nbytes); +int read_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes); +int write_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes); diff --git a/src/drivers/camfits.st4/st4.camera.xml b/src/drivers/camfits.st4/st4.camera.xml new file mode 100644 index 00000000..aed346fa --- /dev/null +++ b/src/drivers/camfits.st4/st4.camera.xml @@ -0,0 +1,64 @@ + + + + + SBIG_ST4 + Camera + + +camfits.st4 -d $device -s $shutter -c $ccd -t $exp_time -n $n_exp -o $output + + + + + device + int + 0 + Device to connect. 0 = USB, 1 = LPT1, 2 = LPT2, ... + + + + ccd + int + 0 + CCD chip to use. 0 = Imaging, 1 = Tracking. + + + + exp_time + float + 1.0 + Integration time in seconds. Min and max comes from CCDcap. + + + + n_exp + int + 1 + Number of frames to take. + + + + shutter + bool + True + Open the shutter?. + + + + output + str + img-$time-$object-$filter + Filename of the output image (wildcards allowed: $time, $object, $filter). + + + + verbose + bool + True + Verbose mode. + + + + + diff --git a/src/drivers/camfits.st7/Makefile.am b/src/drivers/camfits.st7/Makefile.am new file mode 100644 index 00000000..332514fc --- /dev/null +++ b/src/drivers/camfits.st7/Makefile.am @@ -0,0 +1,11 @@ +include $(top_srcdir)/rules.make + +bin_PROGRAMS = camfits.st7 ccdcap + +camfits_st7_SOURCES = csbigimg.h csbigimg.cpp csbigcam.h csbigcam.cpp getindex.h getindex.c main.cpp +camfits_st7_LDADD = -lm -lcfitsio -lsbigudrv + +ccdcap_SOURCES = ccdcap-test.cpp ccdcap.h sbigcamcap.cpp sbigcamcap.h csbigimg.h csbigimg.cpp csbigcam.h csbigcam.cpp +ccdcap_LDADD = -lsbigudrv + +uts_driver_DATA = st7.camera.xml diff --git a/src/drivers/camfits.st7/ccdcap-test.cpp b/src/drivers/camfits.st7/ccdcap-test.cpp new file mode 100644 index 00000000..c4564965 --- /dev/null +++ b/src/drivers/camfits.st7/ccdcap-test.cpp @@ -0,0 +1,81 @@ +/* + * camfits.st7 - SBIG ST 7/8 Controller + * Copyright (C) 2005 Paulo Henrique Silva + * + * This file is part of camfits.st7 + * camfits.st7 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * camfits.st7 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA + * + */ + +/* $Id: ccdcap-test.cpp,v 1.1.2.1 2006/05/17 16:32:41 henrique Exp $ */ + +#include "sbigudrv.h" +#include "sbigcamcap.h" + +#include + +void cameraDump(const CCDCap& cap) { + + printf("%s\n===\n", cap.ccdModel().c_str()); + + int n = cap.nReadoutModes(); + const vector rmodes = cap.readoutModes(); + + for(int i = 0; i < n; i++) { + printf("%20s : %hd\n" + "%20s : %hd pixel\n" + "%20s : %hd pixel\n" + "%20s : %0x um\n" + "%20s : %0x um\n" + "%20s : %0x e-/ADU\n\n", "Mode", rmodes[i]->modeFlag, "Width", rmodes[i]->width, "Height", rmodes[i]->height, + "Pixel Width", rmodes[i]->pixelWidth, "Pixel Height", rmodes[i]->pixelHeight, "Gain", rmodes[i]->gain); + } + + printf("%20s : %d\n" + "%20s : %d\n" + "%20s : %d\n" + "%20s : %d\n\n", "Full Frame", cap.fullFrame(), "Fraame Transfer", cap.frameTransfer(), + "Interline", cap.interline(), "Shutter", cap.shutter()); + + printf("%20s : %0x\n", "Firmware Version", cap.firmwareVersion()); + +} + +int main() { + + CSBIGCam *cam = new CSBIGCam(DEV_LPT1); + + PAR_ERROR err; + + err = cam->EstablishLink(); + if (err != CE_NO_ERROR) { + printf("Error linking to camera.\n"); + delete cam; + exit(1); + } + + CCDCap img = SBIGCamCap(cam, CCD_IMAGING); + CCDCap track= SBIGCamCap(cam, CCD_TRACKING); + + cameraDump(img); + + printf("\n"); + + cameraDump(track); + + return 0; + + +} diff --git a/src/drivers/camfits.st7/ccdcap.h b/src/drivers/camfits.st7/ccdcap.h new file mode 100644 index 00000000..442ffac8 --- /dev/null +++ b/src/drivers/camfits.st7/ccdcap.h @@ -0,0 +1,85 @@ +/* + * camfits.st7 - SBIG ST 7/8 Controller + * Copyright (C) 2005 Paulo Henrique Silva + * + * This file is part of camfits.st7 + * camfits.st7 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * camfits.st7 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA + * + */ + +/* $Id: ccdcap.h,v 1.1.2.1 2006/05/17 16:32:33 henrique Exp $ */ + +#ifndef _CCD_CAP_H_ +#define _CCD_CAP_H_ 1 + +#include +#include + +struct ReadoutMode { + unsigned short width; + unsigned short height; + unsigned long pixelWidth; + unsigned long pixelHeight; + unsigned short gain; + unsigned short modeFlag; +}; + +class CCDCap { + + public: + + CCDCap() { loadData(); }; + virtual ~CCDCap() { }; + + virtual int loadData() { return 1; }; + + std::string ccdModel() const { return _ccdModel; } + std::string ccdSubModel() const { return _ccdSubModel; } + std::string ccdMaker() const { return _ccdMaker; } + + bool fullFrame() const { return _fullFrame; } + bool frameTransfer() const { return _frameTransfer; } + bool interline() const { return _interline; } + bool shutter() const { return _shutter; } + bool antiBlooming() const { return _antiBlooming; } + + unsigned short firmwareVersion() const { return _firmwareVersion; } + + int nReadoutModes() const { return _nReadoutModes; } + + const std::vector& readoutModes() const { return _readoutModes; } + + +protected: + + std::string _ccdModel; + std::string _ccdSubModel; + std::string _ccdMaker; + + bool _fullFrame; + bool _frameTransfer; + bool _interline; + bool _shutter; + bool _antiBlooming; + + unsigned short _firmwareVersion; + + int _nReadoutModes; + + std::vector _readoutModes; + +}; + +#endif // !_CAM_CAP_H_ diff --git a/src/drivers/camfits.st7/cfw8.filter.xml b/src/drivers/camfits.st7/cfw8.filter.xml new file mode 100644 index 00000000..547827e1 --- /dev/null +++ b/src/drivers/camfits.st7/cfw8.filter.xml @@ -0,0 +1,38 @@ + + + + + CFW8 + FilterWheel + + builtin + + + + 0 + clear + + + + 1 + red + + + + 2 + green + + + + 3 + blue + + + + 4 + lunar + + + + + diff --git a/src/drivers/camfits.st7/csbigcam.cpp b/src/drivers/camfits.st7/csbigcam.cpp new file mode 100644 index 00000000..41dda814 --- /dev/null +++ b/src/drivers/camfits.st7/csbigcam.cpp @@ -0,0 +1,981 @@ +/* + + csbigcam.cpp - Contains the code for the sbigcam class + + 1. This software (c)2004 Santa Barbara Instrument Group. + 2. This free software is provided as an example of how + to communicate with SBIG cameras. It is provided AS-IS + without any guarantees by SBIG of suitability for a + particular purpose and without any guarantee to be + bug-free. If you use it you agree to these terms and + agree to do so at your own risk. + 3. Any distribution of this source code to include these + terms. + + Revision History + Date Modification + ========================================================= + 1/26/04 Initial release + +*/ +#include "sbigudrv.h" +#include "csbigcam.h" +#include "csbigimg.h" +#include +#include + +using namespace std; + +#ifndef INVALID_HANDLE_VALUE + #define INVALID_HANDLE_VALUE -1 +#endif + +/* + + Temperature Conversion Constants + Defined in the SBIG Universal Driver Documentation + +*/ +#define T0 25.0 +#define R0 3.0 +#define DT_CCD 25.0 +#define DT_AMB 45.0 +#define RR_CCD 2.57 +#define RR_AMB 7.791 +#define RB_CCD 10.0 +#define RB_AMB 3.0 +#define MAX_AD 4096 + +/* + + hex2double: + + Convert the passed hex value to double. + The hex value is assumed to be in the + format: XXXXXX.XX + +*/ +static double hex2double(unsigned long ul) +{ + double res, mult; + int i; + + res = 0.0; + mult = 1.0; + for (i=0; i<8; i++) + { + res += mult * (double)(ul & 0x0F); + ul >>= 4; + mult *= 10.0; + } + return res / 100.0; + +} + +/* + + Init: + + Initialize the base variables. + +*/ +void CSBIGCam::Init() +{ + m_eLastError = CE_NO_ERROR; + m_eLastCommand = CC_NULL; + m_nDrvHandle = INVALID_HANDLE_VALUE; + m_eCameraType = NO_CAMERA; + m_eActiveCCD = CCD_IMAGING; + m_dExposureTime = 1.0; + m_uReadoutMode = 0; + m_eABGState = ABG_CLK_MED7; +} + +/* + + GetCameraTypeString: + + Return a string describing the model camera + that has been linked to. + +*/ +//typedef enum { ST7_CAMERA=4, ST8_CAMERA, ST5C_CAMERA, TCE_CONTROLLER, +// ST237_CAMERA, STK_CAMERA, ST9_CAMERA, STV_CAMERA, ST10_CAMERA, +// ST1K_CAMERA, ST2K_CAMERA, STL_CAMERA, STF_CAMERA, NEXT_CAMERA, NO_CAMERA=0xFFFF } CAMERA_TYPE; +static const char *CAM_NAMES[] = { + "Type 0", "Type 1", "Type 2", "Type 3", + "ST-7", "ST-8", "ST-5C", "TCE", + "ST-237", "ST-K", "ST-9", "STV", "ST-10", + "ST-1K", "ST-2K", "ST-L", "ST-F" }; +string CSBIGCam::GetCameraTypeString(void) +{ + string s; + GetCCDInfoParams gcip; + GetCCDInfoResults0 gcir; + char *p1, *p2; + + if ( m_eCameraType < (CAMERA_TYPE)(sizeof(CAM_NAMES)/sizeof(const char *)) ) { + // default name + s = CAM_NAMES[m_eCameraType]; + + // see if ST-237A and if so indicate it in the name + if ( m_eCameraType == ST237_CAMERA ) { + gcip.request = CCD_INFO_IMAGING; + if ( SBIGUnivDrvCommand(CC_GET_CCD_INFO, &gcip, &gcir) == CE_NO_ERROR ) + if ( gcir.readoutInfo[0].gain >= 0x100 ) + s += "A"; + } + + // include the ST-L sub-models + if ( m_eCameraType == STL_CAMERA ) { + gcip.request = CCD_INFO_IMAGING; + if ( SBIGUnivDrvCommand(CC_GET_CCD_INFO, &gcip, &gcir) == CE_NO_ERROR ) { + // driver reports name as "SBIG ST-L-XXX..." + p1 = gcir.name + 5; + if ( (p2 = strchr(p1,' ')) != NULL ) { + *p2 = 0; + s = p1; + } + } + } + } + else if ( m_eCameraType == NO_CAMERA ) + s = "No Camera"; + else + s = "Unknown"; + return s; +} + + +/* + + CSBIGCam: + + Stamdard constructor. Initialize appropriate member variables. + +*/ +CSBIGCam::CSBIGCam() +{ + Init(); +} + +/* + + CSBIGCam: + + Alternate constructor. Init the vars, Open the driver and then + try to open the passed device. + + If you want to use the Ethernet connection this is the best + constructor. If you're using the LPT or USB connections + the alternate constructor below may make more sense. + +*/ +CSBIGCam::CSBIGCam(OpenDeviceParams odp) +{ + Init(); + if ( OpenDriver() == CE_NO_ERROR ) + m_eLastError = OpenDevice(odp); +} + +/* + + CSBIGCam: + + Alternate constructor. Init the vars, Open the driver and then + try to open the passed device. + + This won't work the Ethernet port because no IP address + is specified. Use the constructor above where you can + pass the OpenDeviceParams struct. + +*/ +CSBIGCam::CSBIGCam(SBIG_DEVICE_TYPE dev) +{ + OpenDeviceParams odp; + + Init(); + if ( dev == DEV_ETH ) + m_eLastError = CE_BAD_PARAMETER; + else { + odp.deviceType = dev; + if ( OpenDriver() == CE_NO_ERROR ) + m_eLastError = OpenDevice(odp); + } +} + + +/* + + ~CSBIGCam: + + Standard destructor. Close the device then the driver. + +*/ +CSBIGCam::~CSBIGCam() +{ + CloseDevice(); + CloseDriver(); +} + +/* + + GetError: + + Return the error generated in the previous driver call. + +*/ +PAR_ERROR CSBIGCam::GetError() +{ + return m_eLastError; +} + +/* + + GetCommand: + + Return the command last passed to the driver. + +*/ +PAR_COMMAND CSBIGCam::GetCommand() +{ + return m_eLastCommand; +} + +/* + + SBIGUnivDrvCommand: + + Bottleneck function for all calls to the driver that logs + the command and error. First it activates our handle and + then it calls the driver. Activating the handle first + allows having multiple instances of this class dealing + with multiple cameras on different communications port. + + Also allows direct access to the SBIG Universal Driver after + the driver has been opened. + +*/ +PAR_ERROR CSBIGCam::SBIGUnivDrvCommand(short command, void *Params, void *Results) +{ + SetDriverHandleParams sdhp; + + // make sure we have a valid handle to the driver + m_eLastCommand = (PAR_COMMAND)command; + if ( m_nDrvHandle == INVALID_HANDLE_VALUE ) + m_eLastError = CE_DRIVER_NOT_OPEN; + else + { + // handle is valid so install it in the driver + sdhp.handle = m_nDrvHandle; + if ( (m_eLastError = (PAR_ERROR)::SBIGUnivDrvCommand(CC_SET_DRIVER_HANDLE, &sdhp, NULL)) == CE_NO_ERROR ) + // call the desired command + m_eLastError = (PAR_ERROR)::SBIGUnivDrvCommand(command, Params, Results); + } + return m_eLastError; +} + +/* + + OpenDriver: + + Open the driver. Must be made before any other calls and + should be called only once per instance of the camera class. + Based on the results of the open call to the driver this can + open a new handle to the driver. + + The alternate constructors do this for you when you specify + the communications port to use. + +*/ +PAR_ERROR CSBIGCam::OpenDriver() +{ + short res; + GetDriverHandleResults gdhr; + SetDriverHandleParams sdhp; + + // call the driver directly so doesn't install our handle + res = ::SBIGUnivDrvCommand(m_eLastCommand = CC_OPEN_DRIVER, NULL, NULL); + if ( res == CE_DRIVER_NOT_CLOSED ) + { + /* + the driver is open already which we interpret + as having been opened by another instance of + the class so get the driver to allocate a new + handle and then record it + */ + sdhp.handle = INVALID_HANDLE_VALUE; + res = ::SBIGUnivDrvCommand(CC_SET_DRIVER_HANDLE, &sdhp, NULL); + if ( res == CE_NO_ERROR ) { + res = ::SBIGUnivDrvCommand(CC_OPEN_DRIVER, NULL, NULL); + if ( res == CE_NO_ERROR ) { + res = ::SBIGUnivDrvCommand(CC_GET_DRIVER_HANDLE, NULL, &gdhr); + if ( res == CE_NO_ERROR ) + m_nDrvHandle = gdhr.handle; + } + } + } + else if ( res == CE_NO_ERROR ) + { + /* + the driver was not open so record the driver handle + so we can support multiple instances of this class + talking to multiple cameras + */ + res = ::SBIGUnivDrvCommand(CC_GET_DRIVER_HANDLE, NULL, &gdhr); + if ( res == CE_NO_ERROR ) + m_nDrvHandle = gdhr.handle; + } + return m_eLastError = (PAR_ERROR)res; +} + +/* + + CloseDriver: + + Should be called for every call to OpenDriver. + Standard destructor does this for you as well. + Closing the Drriver multiple times won't hurt + but will return an error. + + The destructor will do this for you if you + don't do it explicitly. + +*/ +PAR_ERROR CSBIGCam::CloseDriver() +{ + PAR_ERROR res; + + res = SBIGUnivDrvCommand(CC_CLOSE_DRIVER, NULL, NULL); + if ( res == CE_NO_ERROR ) + m_nDrvHandle = INVALID_HANDLE_VALUE; + return res; +} + +/* + + OpenDevice: + + Call once to open a particular port (USB, LPT, + Ethernet, etc). Must be balanced with a call + to CloseDevice. + + Note that the alternate constructors will make + this call for you so you don't have to do it + explicitly. + +*/ +PAR_ERROR CSBIGCam::OpenDevice(OpenDeviceParams odp) +{ + return SBIGUnivDrvCommand(CC_OPEN_DEVICE, &odp, NULL); +} + +/* + + CloseDevice: + + Closes which ever device was opened by OpenDriver. + + The destructor does this for you so you don't have + to call it explicitly. + +*/ +PAR_ERROR CSBIGCam::CloseDevice() +{ + return SBIGUnivDrvCommand(CC_CLOSE_DEVICE, NULL, NULL); +} + +/* + + GetErrorString: + + Return a string object describing the passed error code. + +*/ +string CSBIGCam::GetErrorString(PAR_ERROR err) +{ + GetErrorStringParams gesp; + GetErrorStringResults gesr; + string s; + + gesp.errorNo = err; + SBIGUnivDrvCommand(CC_GET_ERROR_STRING, &gesp, &gesr); + s = gesr.errorString; + return s; +} + +/* + + GetDriverInfo: + + Get the requested driver info for the passed request. + This call only works with the DRIVER_STANDARD and + DRIVER_EXTENDED requests as you pass it a result + reference that only works with those 2 requests. + For other requests simply call the + SBIGUnivDrvCommand class function. + +*/ +PAR_ERROR CSBIGCam::GetDriverInfo(DRIVER_REQUEST request, GetDriverInfoResults0 &gdir) +{ + GetDriverInfoParams gdip; + + gdip.request = request; + m_eLastCommand = CC_GET_DRIVER_INFO; + if ( request > DRIVER_EXTENDED ) + return m_eLastError = CE_BAD_PARAMETER; + else + return SBIGUnivDrvCommand(CC_GET_DRIVER_INFO, &gdip, &gdir); +} + +/* + + GrabImage: + + Grab an image into the passed image of the passed type. + This does the whole processing for you: Starts + and Ends the Exposure then Readsout the data. + +*/ +PAR_ERROR CSBIGCam::GrabImage(CSBIGImg *pImg, SBIG_DARK_FRAME dark) +{ + int height, width; + GetCCDInfoResults0 gcir; + GetCCDInfoParams gcip; + double ccdTemp; + unsigned short vertNBinning; + unsigned short rm; + string s; + unsigned short es; + MY_LOGICAL expComp; + PAR_ERROR err; + StartReadoutParams srp; + int i; + ReadoutLineParams rlp; + + // Get the image dimensions + vertNBinning = m_uReadoutMode >> 8; + if ( vertNBinning == 0 ) + vertNBinning = 1; + rm = m_uReadoutMode & 0xFF; + gcip.request = (m_eActiveCCD == CCD_IMAGING ? CCD_INFO_IMAGING : CCD_INFO_TRACKING); + if ( SBIGUnivDrvCommand(CC_GET_CCD_INFO, &gcip, &gcir) != CE_NO_ERROR ) + return m_eLastError; + if ( rm >= gcir.readoutModes ) + return CE_BAD_PARAMETER; + width = gcir.readoutInfo[rm].width; + height = gcir.readoutInfo[rm].height / vertNBinning; + + // try to allocate the image buffer + if ( !pImg->AllocateImageBuffer(height, width) ) + return CE_MEMORY_ERROR; + + // initialize some image header params + if ( GetCCDTemperature(ccdTemp) != CE_NO_ERROR ) + return m_eLastError; + pImg->SetCCDTemperature(ccdTemp); + pImg->SetEachExposure(m_dExposureTime); + pImg->SetEGain(hex2double(gcir.readoutInfo[rm].gain)); + pImg->SetPixelHeight(hex2double(gcir.readoutInfo[rm].pixelHeight) * + vertNBinning / 1000.0); + pImg->SetPixelWidth(hex2double(gcir.readoutInfo[rm].pixelWidth) / 1000.0); + es = ES_DCS_ENABLED | ES_DCR_DISABLED | ES_AUTOBIAS_ENABLED; + if ( m_eCameraType == ST5C_CAMERA ) + es |= (ES_ABG_CLOCKED | ES_ABG_RATE_MED); + else if ( m_eCameraType == ST237_CAMERA ) + es |= (ES_ABG_CLOCKED | ES_ABG_RATE_FIXED); + else if ( m_eActiveCCD == CCD_TRACKING ) + es |= (ES_ABG_CLOCKED | ES_ABG_RATE_MED); + else + es |= ES_ABG_LOW; + pImg->SetExposureState(es); + pImg->SetExposureTime(m_dExposureTime); + pImg->SetNumberExposures(1); + pImg->SetReadoutMode(m_uReadoutMode); + s = GetCameraTypeString(); + if ( m_eCameraType == ST5C_CAMERA || ( m_eCameraType == ST237_CAMERA && + s.find("ST-237A", 0) == string::npos) ) + pImg->SetSaturationLevel(4095); + else + pImg->SetSaturationLevel(65535); + s = gcir.name; + pImg->SetCameraModel(s); + + // end any exposure in case one in progress + EndExposure(); + if ( m_eLastError != CE_NO_ERROR && m_eLastError != CE_NO_EXPOSURE_IN_PROGRESS ) + return m_eLastError; + + // start the exposure + if ( StartExposure(dark == SBDF_LIGHT_ONLY ? SC_OPEN_SHUTTER : SC_CLOSE_SHUTTER) != CE_NO_ERROR ) + return m_eLastError; + pImg->SetImageStartTime(time(NULL)); + + // wait for exposure to complete + do { + } while ((err = IsExposureComplete(expComp)) == CE_NO_ERROR && !expComp ); + EndExposure(); + if ( err != CE_NO_ERROR ) + return err; + if ( m_eLastError != CE_NO_ERROR ) + return m_eLastError; + + // readout the CCD + srp.ccd = m_eActiveCCD; + srp.left = srp.top = 0; + srp.height = height; + srp.width = width; + srp.readoutMode = m_uReadoutMode; + if ( (err = StartReadout(srp)) == CE_NO_ERROR ) { + rlp.ccd = m_eActiveCCD; + rlp.pixelStart = 0; + rlp.pixelLength = width; + rlp.readoutMode = m_uReadoutMode; + for (i=0; iGetImagePointer() + (long)i * width); + } + EndReadout(); + if ( err != CE_NO_ERROR ) + return err; + if ( m_eLastError != CE_NO_ERROR ) + return err; + + // we're done unless we wanted a dark also image + if ( dark != SBDF_DARK_ALSO ) + return CE_NO_ERROR; + + // start the light exposure + if ( StartExposure(SC_OPEN_SHUTTER) != CE_NO_ERROR ) + return m_eLastError; + pImg->SetImageStartTime(time(NULL)); + + // wait for exposure to complete + do { + } while ((err = IsExposureComplete(expComp)) == CE_NO_ERROR && !expComp ); + EndExposure(); + if ( err != CE_NO_ERROR ) + return err; + if ( m_eLastError != CE_NO_ERROR ) + return m_eLastError; + + // readout the CCD + srp.ccd = m_eActiveCCD; + srp.left = srp.top = 0; + srp.height = height; + srp.width = width; + srp.readoutMode = m_uReadoutMode; + if ( (err = StartReadout(srp)) == CE_NO_ERROR ) { + rlp.ccd = m_eActiveCCD; + rlp.pixelStart = 0; + rlp.pixelLength = width; + rlp.readoutMode = m_uReadoutMode; + for (i=0; iGetImagePointer() + (long)i * width); + } + EndReadout(); + if ( err != CE_NO_ERROR ) + return err; + if ( m_eLastError != CE_NO_ERROR ) + return err; + + // record dark subtraction in history + if ( m_eCameraType == ST5C_CAMERA || m_eCameraType == ST237_CAMERA ) + pImg->SetHistory("f"); + else + pImg->SetHistory("R"); + + return CE_NO_ERROR; +} + + +/* + + StartExposure: + + Start an exposure in the camera. Should be matched + with an EndExposure call. + +*/ +PAR_ERROR CSBIGCam::StartExposure(SHUTTER_COMMAND shutterState) +{ + StartExposureParams sep; + + sep.ccd = m_eActiveCCD; + sep.exposureTime = (unsigned long)(m_dExposureTime * 100.0 + 0.5); + if ( sep.exposureTime < 1 ) + sep.exposureTime = 1; + sep.abgState = m_eABGState; + sep.openShutter = shutterState; + if ( CheckLink() ) + return SBIGUnivDrvCommand(CC_START_EXPOSURE, &sep, NULL); + else + return m_eLastError; +} + +/* + + EndExposure: + + End or abort an exposure in the camera. Should be + matched to a StartExposure but no damage is done + by calling it by itself if you don't know if an + exposure was started for example. + +*/ +PAR_ERROR CSBIGCam::EndExposure(void) +{ + EndExposureParams eep; + + eep.ccd = m_eActiveCCD; + if ( CheckLink() ) + return SBIGUnivDrvCommand(CC_END_EXPOSURE, &eep, NULL); + else + return m_eLastError; +} + +/* + + IsExposueComplete: + + Query the camera to see if the exposure in progress is complete. + This returns TRUE if the CCD is idle (an exposure was never + started) or if the CCD exposure is complete. + +*/ +PAR_ERROR CSBIGCam::IsExposureComplete(MY_LOGICAL &complete) +{ + QueryCommandStatusParams qcsp; + QueryCommandStatusResults qcsr; + + complete = FALSE; + if ( CheckLink() ) { + qcsp.command = CC_START_EXPOSURE; + if ( SBIGUnivDrvCommand(CC_QUERY_COMMAND_STATUS, &qcsp, &qcsr) == CE_NO_ERROR ) { + if ( m_eActiveCCD == CCD_IMAGING ) + complete = (qcsr.status & 0x03) != 0x02; + else + complete = (qcsr.status & 0x0C) != 0x08; + } + } + return m_eLastError; +} + +/* + + StartReadout: + + Start the readout process. This should be called + after EndExposure and should be matched with an + EndExposure call. + +*/ +PAR_ERROR CSBIGCam::StartReadout(StartReadoutParams srp) +{ + if ( CheckLink() ) + return SBIGUnivDrvCommand(CC_START_READOUT, &srp, NULL); + else + return m_eLastError; +} + +/* + + EndReadout: + + End a readout started with StartReadout. + Don't forget to make this call to prepare the + CCD for idling. + +*/ +PAR_ERROR CSBIGCam::EndReadout(void) +{ + EndReadoutParams erp; + + erp.ccd = m_eActiveCCD; + if ( CheckLink() ) + return SBIGUnivDrvCommand(CC_END_READOUT, &erp, NULL); + else + return m_eLastError; +} + +/* + + ReadoutLine: + + Readout a line of data from the camera, optionally + performing a dark subtraction, placing the data + at dest. + +*/ +PAR_ERROR CSBIGCam::ReadoutLine(ReadoutLineParams rlp, MY_LOGICAL darkSubtract, + unsigned short *dest) +{ + if ( CheckLink() ) { + if ( darkSubtract ) + return SBIGUnivDrvCommand(CC_READ_SUBTRACT_LINE, &rlp, dest); + else + return SBIGUnivDrvCommand(CC_READOUT_LINE, &rlp, dest); + } + else + return m_eLastError; + +} + +/* + + DumpLines: + + Discard data from one or more lines in the camera. + +*/ +PAR_ERROR CSBIGCam::DumpLines(unsigned short noLines) +{ + DumpLinesParams dlp; + + dlp.ccd = m_eActiveCCD; + dlp.lineLength = noLines; + dlp.readoutMode = m_uReadoutMode; + if ( CheckLink() ) + return SBIGUnivDrvCommand(CC_DUMP_LINES, &dlp, NULL); + else + return m_eLastError; +} + +/* + + SetTemperatureRegulation: + + Enable or disable the temperatre controll at + the passed setpoint which is the absolute + (not delta) temperature in degrees C. + +*/ +PAR_ERROR CSBIGCam::SetTemperatureRegulation(MY_LOGICAL enable, double setpoint) +{ + PAR_ERROR e; + + SetTemperatureRegulationParams strp; + + if ( CheckLink() ) { + strp.regulation = enable ? REGULATION_ON : REGULATION_OFF; + strp.ccdSetpoint = DegreesCToAD(setpoint, TRUE); + e = SBIGUnivDrvCommand(CC_SET_TEMPERATURE_REGULATION, &strp, NULL); + if (e != CE_NO_ERROR) return e; + // enable autofreeze, by Andre + strp.regulation = REGULATION_ENABLE_AUTOFREEZE; + return SBIGUnivDrvCommand(CC_SET_TEMPERATURE_REGULATION, &strp, NULL); + } + else + return m_eLastError; +} + +/* + + QueryTemperatureStatus: + + Get whether the cooling is enabled, the CCD temp + and setpoint in degrees C and the percent power + applied to the TE cooler. + +*/ +PAR_ERROR CSBIGCam::QueryTemperatureStatus(MY_LOGICAL &enabled, double &ccdTemp, + double &setpointTemp, double &percentTE) +{ + QueryTemperatureStatusResults qtsr; + + if ( CheckLink() ) { + if ( SBIGUnivDrvCommand(CC_QUERY_TEMPERATURE_STATUS, NULL, &qtsr) == CE_NO_ERROR ) + { + enabled = qtsr.enabled; + ccdTemp = ADToDegreesC(qtsr.ccdThermistor, TRUE); + setpointTemp = ADToDegreesC(qtsr.ccdSetpoint, TRUE); + percentTE = qtsr.power/255.0 * 100; + } + } + return m_eLastError; +} + +/* + + GetCCDTemperature: + + Read and return the current CCD temperature. + +*/ +PAR_ERROR CSBIGCam::GetCCDTemperature(double &ccdTemp) +{ + double setpointTemp, percentTE; + MY_LOGICAL teEnabled; + + return QueryTemperatureStatus(teEnabled, ccdTemp, setpointTemp, percentTE); +} + + +/* + + ActivateRelay: + + Activate one of the four relays for the passed + period of time. Cancel a relay by passing + zero for the time. + +*/ +PAR_ERROR CSBIGCam::ActivateRelay(CAMERA_RELAY relay, double time) +{ + ActivateRelayParams arp; + unsigned short ut; + + if ( CheckLink() ) { + arp.tXMinus = arp.tXPlus = arp.tYMinus = arp.tYPlus = 0; + if ( time >= 655.35 ) + ut = 65535; + else + ut = (unsigned short)(time/0.01); + switch ( relay ){ + case RELAY_XPLUS: arp.tXPlus = ut; break; + case RELAY_XMINUS: arp.tXPlus = ut; break; + case RELAY_YPLUS: arp.tXPlus = ut; break; + case RELAY_YMINUS: arp.tXPlus = ut; break; + } + return SBIGUnivDrvCommand(CC_ACTIVATE_RELAY, &arp, NULL); + } + else + return m_eLastError; +} + +/* + + AOTipTilt: + + Send a tip/tilt command to the AO-7. + +*/ +PAR_ERROR CSBIGCam::AOTipTilt(AOTipTiltParams attp) +{ + if ( CheckLink() ) + return SBIGUnivDrvCommand(CC_AO_TIP_TILT, &attp, NULL); + else + return m_eLastError; +} + +/* + + CFWCommand: + + Send a command to the Color Filter Wheel. + +*/ +PAR_ERROR CSBIGCam::CFWCommand(CFWParams cfwp, CFWResults &cfwr) +{ + if ( CheckLink() ) + return SBIGUnivDrvCommand(CC_CFW, &cfwp, &cfwr); + else + return m_eLastError; +} + +/* + + EstablishLink: + + Once the driver and device are open call this to + establish a communications link with the camera. + May be called multiple times without problem. + + If there's no error and you want to find out what + model of camera was found use the GetCameraType() + function. + +*/ +PAR_ERROR CSBIGCam::EstablishLink(void) +{ + PAR_ERROR res; + EstablishLinkResults elr; + EstablishLinkParams elp; + + res = SBIGUnivDrvCommand(CC_ESTABLISH_LINK, &elp, &elr); + if ( res == CE_NO_ERROR ) + m_eCameraType = (CAMERA_TYPE)elr.cameraType; + return res; +} + +/* + + GetErrorString: + + Returns a ANSI C++ standard string object describing + the error code returned from the lass call to the driver. + +*/ +string CSBIGCam::GetErrorString() +{ + return GetErrorString(m_eLastError); +} + +/* + + CheckLink: + + If a link has been established to a camera return TRUE. + Otherwise try to establish a link and if successful + return TRUE. If fails return FALSE. + +*/ +MY_LOGICAL CSBIGCam::CheckLink(void) +{ + if ( m_eCameraType != NO_CAMERA || EstablishLink() == CE_NO_ERROR ) + return TRUE; + else + return FALSE; +} + +/* + + DegreesCToAD: + + Convert temperatures in degrees C to + camera AD setpoints. + +*/ +unsigned short CSBIGCam::DegreesCToAD(double degC, MY_LOGICAL ccd /* = TRUE */) +{ + double r; + unsigned short setpoint; + + if ( degC < -50.0 ) + degC = -50.0; + else if ( degC > 35.0 ) + degC = 35.0; + if ( ccd ) { + r = R0 * exp(log(RR_CCD)*(T0 - degC)/DT_CCD); + setpoint = (unsigned short)(MAX_AD/((RB_CCD/r) + 1.0) + 0.5); + } else { + r = R0 * exp(log(RR_AMB)*(T0 - degC)/DT_AMB); + setpoint = (unsigned short)(MAX_AD/((RB_AMB/r) + 1.0) + 0.5); + } + return setpoint; +} + +/* + + ADToDegreesC: + + Convert camera AD temperatures to + degrees C + +*/ +double CSBIGCam::ADToDegreesC(unsigned short ad, MY_LOGICAL ccd /* = TRUE */) +{ + double r, degC; + + if ( ad < 1 ) + ad = 1; + else if ( ad >= MAX_AD - 1 ) + ad = MAX_AD - 1; + if ( ccd ) { + r = RB_CCD/(((double)MAX_AD/ad) - 1.0); + degC = T0 - DT_CCD*(log(r/R0)/log(RR_CCD)); + } else { + r = RB_AMB/(((double)MAX_AD/ad) - 1.0); + degC = T0 - DT_AMB*(log(r/R0)/log(RR_AMB)); + } + return degC; +} diff --git a/src/drivers/camfits.st7/csbigcam.h b/src/drivers/camfits.st7/csbigcam.h new file mode 100644 index 00000000..264f8aea --- /dev/null +++ b/src/drivers/camfits.st7/csbigcam.h @@ -0,0 +1,113 @@ +/* + + csbigcam.h - Contains the interface to the csbigcam + camera class + + 1. This software (c)2004 Santa Barbara Instrument Group. + 2. This free software is provided as an example of how + to communicate with SBIG cameras. It is provided AS-IS + without any guarantees by SBIG of suitability for a + particular purpose and without any guarantee to be + bug-free. If you use it you agree to these terms and + agree to do so at your own risk. + 3. Any distribution of this source code to include these + terms. + +*/ +#ifndef _CSBIGCAM_ +#define _CSBIGCAM_ + +#ifndef _PARDRV_ + #include "sbigudrv.h" +#endif + +#ifndef _CSBIGIMG_ + #include "csbigimg.h" +#endif + +#include +using namespace std; + +typedef enum {RELAY_XPLUS, RELAY_XMINUS, RELAY_YPLUS, RELAY_YMINUS } CAMERA_RELAY; +typedef enum {SBDF_LIGHT_ONLY, SBDF_DARK_ONLY, SBDF_DARK_ALSO } SBIG_DARK_FRAME; + +class CSBIGCam { +private: + PAR_ERROR m_eLastError; + PAR_COMMAND m_eLastCommand; + short m_nDrvHandle; + CAMERA_TYPE m_eCameraType; + CCD_REQUEST m_eActiveCCD; + double m_dExposureTime; + unsigned short m_uReadoutMode; + ABG_STATE7 m_eABGState; + +public: + // Constructors/Destructors + CSBIGCam(); + CSBIGCam(OpenDeviceParams odp); + CSBIGCam(SBIG_DEVICE_TYPE dev); + ~CSBIGCam(); + void Init(); + + // Error Reporting Routines + PAR_ERROR GetError(); + string GetErrorString(); + string GetErrorString(PAR_ERROR err); + PAR_COMMAND GetCommand(); + + // Accessor Functions + double GetExposureTime(void) { return m_dExposureTime; } + void SetExposureTime(double exp) { m_dExposureTime = exp; } + CCD_REQUEST GetActiveCCD(void) { return m_eActiveCCD; } + void SetActiveCCD(CCD_REQUEST ccd) { m_eActiveCCD = ccd; } + unsigned short GetReadoutMode(void) { return m_uReadoutMode; } + void SetReadoutMode(unsigned short rm) { m_uReadoutMode = rm; } + CAMERA_TYPE GetCameraType(void) { return m_eCameraType; } + ABG_STATE7 GetABGState(void) { return m_eABGState; } + void SetABGState(ABG_STATE7 abgState) { m_eABGState = abgState; } + + // Driver/Device Routines + PAR_ERROR OpenDriver(); + PAR_ERROR CloseDriver(); + PAR_ERROR OpenDevice(OpenDeviceParams odp); + PAR_ERROR CloseDevice(); + PAR_ERROR GetDriverInfo(DRIVER_REQUEST request, GetDriverInfoResults0 &gdir); + + // High-Level Exposure Related Commands + PAR_ERROR GrabImage(CSBIGImg *pImg, SBIG_DARK_FRAME dark); + + // Low-Level Exposure Related Commands + PAR_ERROR StartExposure(SHUTTER_COMMAND shutterState); + PAR_ERROR EndExposure(void); + PAR_ERROR IsExposureComplete(MY_LOGICAL &complete); + PAR_ERROR StartReadout(StartReadoutParams srp); + PAR_ERROR EndReadout(void); + PAR_ERROR ReadoutLine(ReadoutLineParams rlp, MY_LOGICAL darkSubtract, unsigned short *dest); + PAR_ERROR DumpLines(unsigned short noLines); + + //Temperature Related Commands + PAR_ERROR GetCCDTemperature(double &ccdTemp); + PAR_ERROR SetTemperatureRegulation(MY_LOGICAL enable, double setpoint); + PAR_ERROR QueryTemperatureStatus(MY_LOGICAL &enabled, double &ccdTemp, + double &setpointTemp, double &percentTE); + + // Control Related Commands + PAR_ERROR ActivateRelay(CAMERA_RELAY relay, double time); + PAR_ERROR AOTipTilt(AOTipTiltParams attp); + PAR_ERROR CFWCommand(CFWParams cfwp, CFWResults &cfwr); + + // General Purpose Commands + PAR_ERROR EstablishLink(void); + string GetCameraTypeString(void); + + // Utility functions + MY_LOGICAL CheckLink(void); + unsigned short DegreesCToAD(double degC, MY_LOGICAL ccd = TRUE); + double ADToDegreesC(unsigned short ad, MY_LOGICAL ccd = TRUE); + + // Allows access directly to driver + PAR_ERROR SBIGUnivDrvCommand(short command, void *Params, void *Results); +}; + +#endif /* #ifndef _CSBIGCAM_ */ diff --git a/src/drivers/camfits.st7/csbigimg.cpp b/src/drivers/camfits.st7/csbigimg.cpp new file mode 100644 index 00000000..9851e207 --- /dev/null +++ b/src/drivers/camfits.st7/csbigimg.cpp @@ -0,0 +1,379 @@ +/* + + csbigimg.cpp - SBIG Image Class + + 1. This software (c)2004 Santa Barbara Instrument Group. + 2. This free software is provided as an example of how + to communicate with SBIG cameras. It is provided AS-IS + without any guarantees by SBIG of suitability for a + particular purpose and without any guarantee to be + bug-free. If you use it you agree to these terms and + agree to do so at your own risk. + 3. Any distribution of this source code to include these + terms. + + Revision History + Date Modification + ========================================================= + 1/26/04 Initial release + +*/ +#include "csbigimg.h" +#include +#include + +/* + + Local Constants + +*/ +#define FILE_VERSION 3 /* current header version written */ +#define DATA_VERSION 1 /* current data version written */ +#define HEADER_LEN 2048 +#define VERSION_STR "1.0" /* version of this class */ +#ifndef PI + #define PI 3.1415926535 +#endif + +/* + + CSBIGImg: + + Standard constructor. Init member variables. + +*/ +CSBIGImg::CSBIGImg() +{ + Init(); +} + +/* + + CSBIGImg: + + Alternate constructor. Try to allocate the image buffer. + +*/ +CSBIGImg::CSBIGImg(int height, int width) +{ + Init(); + AllocateImageBuffer(height, width); +} + +/* + + ~CSBIGImg: + + Deallocate the image buffer. + +*/ +CSBIGImg::~CSBIGImg() +{ + if ( m_pImage ) + delete m_pImage; + m_pImage = NULL; +} + +/* + + Init: + + Initialize the member variables with reasonable default values. + +*/ +void CSBIGImg::Init() +{ + string s1, s2; + + m_nHeight = m_nWidth = 0; + m_pImage = NULL; + m_imageStartTime = time(NULL); + m_dCCDTemperature = 25.0; + m_dExposureTime = m_dEachExposure = 1.0; + m_dTrackExposure = 0.0; + m_dFocalLength = 80.0; + m_dApertureArea = PI * 4.0 * 4.0; + m_dResponseFactor = 2000.0; + m_dPixelHeight = m_dPixelWidth = 0.009; + m_dEGain = 2.3; + m_uBackground = 0; + m_uRange = 65535; + m_uNumberExposures = 1; + m_uSaturationLevel = 65535; + m_uPedestal = 0; + m_uExposureState = ES_ABG_LOW | ES_ABG_RATE_FIXED | + ES_DCS_ENABLED | ES_DCR_DISABLED | ES_AUTOBIAS_ENABLED; + m_uReadoutMode = 0; + m_cImageNote = "Image acquired with CSBIGImg"; + m_cObserver = ""; + m_cHistory = "0"; + m_cFilter = "None"; + s1 = "CSBIGImg Ver "; + s2 = VERSION_STR; + m_cSoftware = s1 + s2; + m_cCameraModel = "ST-7"; +} + +/* + + SaveImage: + + Save the image in passed path and format. + Returns any file errors that occur. + +*/ +SBIG_FILE_ERROR CSBIGImg::SaveImage(const char *pFullPath, SBIG_IMAGE_FORMAT fmt) +{ + char header[HEADER_LEN]; + FILE *fp; + SBIG_FILE_ERROR res; + int i, cmpWidth; + unsigned char *pCmpData, *pRevData; + unsigned short byteTest = 0x1234; + MY_LOGICAL reverseBytes; + + switch ( fmt ) { + case SBIF_COMPRESSED: + /* SBIG Commpressed Format - create and write the image header + then compress and write each row of the image */ + CreateSBIGHeader(header, TRUE); + res = SBFE_MEMORY_ERROR; + pCmpData = new unsigned char[m_nWidth*2 + 2]; + if ( pCmpData ) + { + res = SBFE_OPEN_ERROR; + if ( (fp = fopen(pFullPath, "wb")) != 0 ) + { + res = SBFE_WRITE_ERROR; + if ( fwrite(header, HEADER_LEN, 1, fp) == 1 ) + { + for (i=0; i 0 && width > 0 ) { + m_pImage = new unsigned short[(long)height * width * sizeof(unsigned short)]; + if ( m_pImage ) { + m_nHeight = height; + m_nWidth = width; + } + memset(m_pImage, 0, 2L * m_nHeight * m_nWidth); + } + return m_pImage != NULL; +} + +void CSBIGImg::CreateSBIGHeader(char *pHeader, MY_LOGICAL isCompressed) +{ + char *p; + struct tm *plt; + + memset(pHeader, 0, HEADER_LEN); + plt = gmtime(&m_imageStartTime); + p = pHeader; + p += sprintf(p,"SBIG %sImage\n\r", isCompressed ? "Compressed " : ""); + p += sprintf(p,"File_version = %d\n\r",FILE_VERSION); + p += sprintf(p,"Data_version = %d\n\r",DATA_VERSION); + p += sprintf(p,"Exposure = %ld\n\r",m_dExposureTime < 0.01 ? 1 : + (long)(m_dExposureTime * 100.0 + 0.5)); + p += sprintf(p,"Focal_length = %1.3lf\n\r", m_dFocalLength); + p += sprintf(p,"Aperture = %1.4lf\n\r", m_dApertureArea); + p += sprintf(p,"Response_factor = %1.3lf\n\r",m_dResponseFactor); + p += sprintf(p,"Note = %s\n\r", m_cImageNote.length() == 0 ? "-" : + m_cImageNote.c_str()); + p += sprintf(p,"Background = %u\n\r", m_uBackground); + p += sprintf(p,"Range = %u\n\r", m_uRange); + p += sprintf(p,"Height = %d\n\r", m_nHeight); + p += sprintf(p,"Width = %d\n\r", m_nWidth); + p += sprintf(p,"Date = %02d/%02d/%02d\n\r", plt->tm_mon+1, plt->tm_mday, plt->tm_year % 100); + p += sprintf(p,"Time = %02d:%02d:%02d\n\r", plt->tm_hour, plt->tm_min, plt->tm_sec); + p += sprintf(p,"Exposure_state = %u\n\r", m_uExposureState); + p += sprintf(p,"Temperature = %1.2lf\n\r", m_dCCDTemperature); + p += sprintf(p,"Number_exposures = %d\n\r", m_uNumberExposures); + p += sprintf(p,"Each_exposure = %ld\n\r", m_dEachExposure < 0.01 ? 1 : + (long)(m_dEachExposure * 100.0 + 0.5)); + p += sprintf(p,"History = %s\n\r", m_cHistory.c_str()); + p += sprintf(p,"Observer = %s\n\r", m_cObserver.length() == 0 ? "-" : + m_cObserver.c_str()); + p += sprintf(p,"X_pixel_size = %1.4lf\n\r", m_dPixelWidth); + p += sprintf(p,"Y_pixel_size = %1.4lf\n\r", m_dPixelHeight); + p += sprintf(p,"Pedestal = %u\n\r", m_uPedestal); + p += sprintf(p,"E_gain = %1.2lf\n\r", m_dEGain); + + /* create user parameters */ + p += sprintf(p,"User_1 = %s\n\r", m_cSoftware.length() == 0 ? "-" : + m_cSoftware.c_str()); + p += sprintf(p,"User_2 = %s\n\r", m_cCameraModel.length() == 0 ? "-" : + m_cCameraModel.c_str()); + p += sprintf(p,"User_3 = Exposure = %1.3lf, Each_exposure = %1.3lf\n\r", + m_dExposureTime, m_dEachExposure); + p += sprintf(p,"User_4 = %s%d\n\r", "Y2KYear = ", plt->tm_year + 1900); + + /* create filter string */ + p += sprintf(p,"Filter = %s\n\r", m_cFilter.length() == 0 ? "-" : + m_cFilter.c_str()); + + /* create readout mode */ + p += sprintf(p,"Readout_mode = %u\n\r", m_uReadoutMode); + + /* create track time */ + p += sprintf(p,"Track_time = %ld\n\r", m_dTrackExposure < 0.01 ? 0 : + (long)(m_dTrackExposure * 100.0 + 0.5)); + + /* create saturation level */ + p += sprintf(p,"Sat_level = %u\n\r", m_uSaturationLevel); + p += sprintf(p,"End\n\r%c",0x1a); +} + +/* + + CompressSBIGData: + + Compress the imgRow row of pixel data into the pCmpData buffer, + returning the length of the combressed data in bytes. + +*/ +int CSBIGImg::CompressSBIGData(unsigned char *pCmpData, int imgRow) +{ + unsigned short us, *pImg; + unsigned char *puc; + int cmpLen, i; + long delta; + + pImg = m_pImage + (long)imgRow * m_nWidth; + puc = pCmpData + 2; // offset passed length + cmpLen = 0; + + // encode first pixel as is + us = *pImg++; + *puc++ = (unsigned char)(us & 0xFF); // ls byte first + *puc++ = (unsigned char)(us >> 8); + cmpLen += 2; + + // compress remaining pixels + for (i=1; i= -127 && delta <= 127 ) + { + // encode pixel as delta; + *puc++ = (unsigned char)delta; + cmpLen++; + if ( cmpLen >= 2*m_nWidth ) // make syre don't overwrite buffer + break; + } + else + { + // encode pixel directly + if ( cmpLen+3 >= 2*m_nWidth ) + break; + *puc++ = 0x80; + *puc++ = (unsigned char)(us & 0xFF); // ls byte first + *puc++ = (unsigned char)(us >> 8); + cmpLen += 3; + } + } + if ( i < m_nWidth ) + { + // compressed data is longer, simply copy uncompressed data + // note we don't use memcpy here because the the byte order + // in memory may be different that ls then ms required by + // the file + IntelCopyBytes(pCmpData + 2, imgRow); + cmpLen = 2 * m_nWidth; + } + // encode length at start of buffer + pCmpData[0] = (unsigned char)(cmpLen & 0xFF); // ls byte of len + pCmpData[1] = (unsigned char)(cmpLen >> 8); + return cmpLen + 2; +} + +/* + + IntelCopyBytes: + + Copy the imgRow row of pixels to the passed buffer + preserving the Intel byte order (ls them ms). + +*/ +void CSBIGImg::IntelCopyBytes(unsigned char *pRevData, int imgRow) +{ + int i; + unsigned short us, *pImg; + unsigned char *puc; + + pImg = m_pImage + (long)imgRow * m_nWidth; + puc = pRevData; + for (i=0; i> 8); + } +} diff --git a/src/drivers/camfits.st7/csbigimg.h b/src/drivers/camfits.st7/csbigimg.h new file mode 100644 index 00000000..3d7bf720 --- /dev/null +++ b/src/drivers/camfits.st7/csbigimg.h @@ -0,0 +1,157 @@ +/* + + csbigimg.h - Contains the definition of the interface to + the SBIG Image Class + + 1. This software (c)2004 Santa Barbara Instrument Group. + 2. This free software is provided as an example of how + to communicate with SBIG cameras. It is provided AS-IS + without any guarantees by SBIG of suitability for a + particular purpose and without any guarantee to be + bug-free. If you use it you agree to these terms and + agree to do so at your own risk. + 3. Any distribution of this source code to include these + terms. + +*/ +#ifndef _CSBIGIMG_ +#define _CSBIGIMG_ + +#ifndef _PARDRV_ + #include "sbigudrv.h" +#endif + +#include +#include +using namespace std; + +/* + + Exposure State Field Defines + +*/ +#define ES_ABG_MASK 0x0003 +#define ES_ABG_UNKNOWN 0x0000 +#define ES_ABG_LOW 0x0001 +#define ES_ABG_CLOCKED 0x0002 +#define ES_ABG_MID 0x0003 + +#define ES_ABG_RATE_MASK 0x00C0 +#define ES_ABG_RATE_FIXED 0x0000 +#define ES_ABG_RATE_LOW 0x0040 +#define ES_ABG_RATE_MED 0x0080 +#define ES_ABG_RATE_HI 0x00C0 + +#define ES_DCS_MASK 0x000c +#define ES_DCS_UNKNOWN 0x0000 +#define ES_DCS_ENABLED 0x0004 +#define ES_DCS_DISABLED 0x0008 + +#define ES_DCR_MASK 0x0030 +#define ES_DCR_UNKNOWN 0x0000 +#define ES_DCR_ENABLED 0x0010 +#define ES_DCR_DISABLED 0x0020 + +#define ES_AUTOBIAS_MASK 0x0100 +#define ES_AUTOBIAS_ENABLED 0x0100 +#define ES_AUTOBIAS_DISABLED 0x0000 + + +typedef enum { SBIF_COMPRESSED, SBIF_UNCOMPRESSED } SBIG_IMAGE_FORMAT; +typedef enum {SBFE_NO_ERROR, SBFE_OPEN_ERROR, SBRE_CLOSE_ERROR, SBFE_READ_ERROR, SBFE_WRITE_ERROR, + SBFE_FORMAT_ERROR, SBFE_MEMORY_ERROR } SBIG_FILE_ERROR; + +class CSBIGImg { +private: + int m_nHeight, m_nWidth; // image size in pixels + unsigned short *m_pImage; // pointer to image data + time_t m_imageStartTime; // time that light exposure started + double m_dCCDTemperature; // CCD Temp at start of exposure + double m_dExposureTime; // Exposure time in seconds + double m_dTrackExposure; // Exposure when tracking + double m_dEachExposure; // Snapshot time in seconds + double m_dFocalLength; // Lens/Telescope Focal Length in inches + double m_dApertureArea; // Lens/Telescope Aperture Are in Sq-Inches + double m_dResponseFactor; // Magnitude Calibration Factor + double m_dPixelHeight, m_dPixelWidth; // Pixel Dimensions in mm + double m_dEGain; // Electronic Gain, e-/ADU + unsigned short m_uBackground, m_uRange; // Display Background and Range + unsigned short m_uNumberExposures; // Number of exposures co-added + unsigned short m_uSaturationLevel; // Pixels at this level are saturated + unsigned short m_uPedestal; // Image Pedestal + unsigned short m_uExposureState; // Exposure State + unsigned short m_uReadoutMode; // Camera Readout Mode use to acquire image + string m_cImageNote; // Note attached to image + string m_cObserver; // Observer name + string m_cHistory; // Image History string of modification chars + string m_cFilter; // Filter name imaged through + string m_cSoftware; // Software App Name and Version + string m_cCameraModel; // Model of camera used to acquire image + +public: + /* Constructors/Destructor */ + CSBIGImg(); + CSBIGImg(int height, int width); + ~CSBIGImg(); + void Init(); + + /* Accessor Functions */ + int GetHeight() {return m_nHeight;} + int GetWidth() {return m_nWidth;} + unsigned short *GetImagePointer() {return m_pImage;} + void SetImageStartTime(time_t startTime){m_imageStartTime = startTime;} + time_t GetImageStartTime(void) {return m_imageStartTime;} + void SetCCDTemperature(double temp) {m_dCCDTemperature = temp;} + double GetCCDTemperature(void) {return m_dCCDTemperature;} + void SetExposureTime(double exp) {m_dExposureTime = exp;} + double GetExposureTime(void) {return m_dExposureTime;} + void SetEachExposure(double exp) {m_dEachExposure = exp;} + double GetEachExposure(void) {return m_dEachExposure;} + void SetFocalLength(double fl) {m_dFocalLength = fl;} + double GetFocalLength(void) {return m_dFocalLength;} + void SetApertureArea(double ap) {m_dApertureArea = ap;} + double GetApertureArea(void) {return m_dApertureArea;} + void SetResponseFactor(double resp) {m_dResponseFactor = resp;} + double GetResponseFactor(void) {return m_dResponseFactor;} + void SetPixelHeight(double ht) {m_dPixelHeight = ht;} + double GetPixelHeight(void) {return m_dPixelHeight;} + void SetPixelWidth(double wd) {m_dPixelWidth = wd;} + double GetPixelWidth(void) {return m_dPixelWidth;} + void SetEGain(double gn) {m_dEGain = gn;} + double GetEGain(void) {return m_dEGain;} + void SetBackground(unsigned short back) {m_uBackground = back;} + unsigned short GetBackground(void) {return m_uBackground;} + void SetRange(unsigned short range) {m_uRange = range;} + unsigned short GetRange(void) {return m_uRange;} + void SetSaturationLevel(unsigned short sat) {m_uSaturationLevel = sat;} + unsigned short GetSaturationLevel(void) {return m_uSaturationLevel;} + void SetNumberExposures(unsigned short no) {m_uNumberExposures = no;} + unsigned short GetNumberExposures(void) {return m_uNumberExposures;} + void SetTrackExposure(double exp) {m_dTrackExposure = exp;} + double GetTrackExposure(void) {return m_dTrackExposure;} + void SetReadoutMode(unsigned short rm) {m_uReadoutMode = rm;} + unsigned short GetReadoutMode(void) {return m_uReadoutMode;} + void SetPedestal(unsigned short ped) {m_uPedestal = ped;} + unsigned short GetPedestal(void) {return m_uPedestal;} + void SetExposureState(unsigned short es) {m_uExposureState = es;} + unsigned short GetExposureState(void) {return m_uExposureState;} + void SetImageNote(string str) {m_cImageNote = str;} + string GetImageNote(void) {return m_cImageNote;} + void SetObserver(string str) {m_cObserver = str;} + string GetObserver(void) {return m_cObserver;} + void SetHistory(string str) {m_cHistory = str;} + string GetHistory(void) {return m_cHistory;} + void SetCameraModel(string str) {m_cCameraModel = str;} + string GetCameraModel(void) {return m_cCameraModel;} + + /* File IO Routines */ + SBIG_FILE_ERROR SaveImage(const char *pFullPath, SBIG_IMAGE_FORMAT fmt); + + /* Utility Functions */ + MY_LOGICAL AllocateImageBuffer(int height, int width); + void CreateSBIGHeader(char *pHeader, MY_LOGICAL isCompressed); + int CompressSBIGData(unsigned char *pCmpData, int imgRow); + void IntelCopyBytes(unsigned char *pRevData, int imgRow); +}; + +#endif /* #ifndef _CSBIGIMG_ */ diff --git a/src/drivers/camfits.st7/getindex.c b/src/drivers/camfits.st7/getindex.c new file mode 100644 index 00000000..42d616ad --- /dev/null +++ b/src/drivers/camfits.st7/getindex.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include + +static int matchfits(const struct dirent *d); + +static char pattern[256]; + + +// returns last index of bfname-####.fits +int getindex(char *bfname) +{ + struct dirent **namelist; + int nd; + char scanfmt[256]; + char dirc[256]; + char basec[256]; + char *dir, *base; + char *lastent; + int lastindex; + + strncpy(dirc, bfname, 255); + strncpy(basec, bfname, 255); + dir = dirname(dirc); + base = basename(basec); + + snprintf(pattern, 255, "%s-[0-9][0-9][0-9][0-9].fits", base); + + //find files that match pattern, see matchfits() below + nd = scandir(dir, &namelist, matchfits, alphasort); + if (nd == 0) // no file with this base name + return -1; + + // extract index from filename + snprintf(scanfmt, 255, "%s-%%d.fits", base); + lastent = namelist[nd-1]->d_name; + sscanf(lastent, scanfmt, &lastindex); + + /* TODO: must somehow free the space allocated by scandir() + for (i = 0; i < nd; i++) { + free(namelist[i]->d_name); + } + free(namelist); + */ + + return lastindex; + +} + + + +int matchfits(const struct dirent *d) +{ + //printf("pattern: %s\nfname : %s\n\n", pattern, d->d_name); + return !fnmatch(pattern, d->d_name, FNM_PATHNAME); +} diff --git a/src/drivers/camfits.st7/getindex.h b/src/drivers/camfits.st7/getindex.h new file mode 100644 index 00000000..87886373 --- /dev/null +++ b/src/drivers/camfits.st7/getindex.h @@ -0,0 +1,14 @@ +#ifndef GETINDEX_H + +#ifdef __cplusplus +extern "C" { +#endif + +int getindex(char *bfname); + +#ifdef __cplusplus +} +#endif + +#define GETINDEX_H +#endif diff --git a/src/drivers/camfits.st7/main.cpp b/src/drivers/camfits.st7/main.cpp new file mode 100644 index 00000000..fcf40eb3 --- /dev/null +++ b/src/drivers/camfits.st7/main.cpp @@ -0,0 +1,671 @@ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include + +#include "fitsio.h" +#include "csbigcam.h" +#include "getindex.h" + +using namespace std; + + +#define PROGRAM_NAME "SBIG CCD camera controller" + +#define boolstring(x) (x?"true":"false") +#define shutterstate (openshutter?"OPEN":"CLOSED") +#define selectedccd (ccd?"tracking":"imaging") + + +// filled by get_args() +int verbose; +char *exec_name; // Name of executable file +static char *conffname; // configuration file +static char *bfname; // image base file name +static char imagefname[256]; // image file name +static double exptime; // seconds +static int interval; // seconds +static int nexp; // number of exposures +static int openshutter; // 1: open, 0: closed +static unsigned short binning; +static SBIG_DEVICE_TYPE device;// camera device +static CCD_REQUEST ccd; // imaging=0, tracking=1 +static ABG_STATE7 abg; // abg state +static unsigned short filter; // color filter. +static unsigned short top, left, width, height; // image window +static int tr; // set temperature regulation (if set to 1) +static double setpoint; // target temperature for regulation + +static void print_title(void); +static void print_help(void); +static void print_version(void); +static void print_usage(void); +static void get_args(int argc, char **argv); + + +/*-------------------------------------------------------------------------*/ + +int main(int argc, char **argv) +{ + CSBIGCam *cam; + PAR_ERROR err; + unsigned short *data; + + char errmsg[255]; + char buffer[80]; + + // get command line arguments + get_args(argc, argv); + + if (verbose) { + print_version(); + // list arguments + printf("\n*** Options:\n\n"); + printf("Exptime : %'.2f seconds\n", exptime); + printf("Interval: %d seconds\n", interval); + printf("Nexp : %d\n", nexp); + printf("Shutter : %s\n", shutterstate); + printf("Binning : %d\n", binning); + printf("Filter : %d\n", filter); + printf("Device : %x\n", device); + printf("CCD : %s\n", selectedccd); + printf("Temperature regulation: %d\n", tr); + printf("Setpoint temperature : %'.1f C\n", setpoint); + printf("ABG state: %d\n", abg); + printf("top : %d\n", top); + printf("height: %d\n", height); + printf("left : %d\n", left); + printf("width : %d\n", width); + printf("Output: %s-####.fits\n\n", bfname); + //printf("Config file: %s\n", conffname); + } + + + // start talking to camera + cam = new CSBIGCam(device); + + if (verbose) { + printf("Establishing link... "); + fflush(stdout); + } + + err = cam->EstablishLink(); + if (err != CE_NO_ERROR) { + printf("Error linking to camera.\n"); + delete cam; + exit(1); + } + + if (verbose) printf("OK.\nCamera type: %s\n", + (cam->GetCameraTypeString()).c_str()); + + + // Do temperature regulation stuff + double ccd_temperature; + MY_LOGICAL ccd_tr_enabled; + double percentTE; + double ccd_setpoint; + + if (verbose) printf("\nReading temperature regulation status...\n"); + cam->QueryTemperatureStatus(ccd_tr_enabled, ccd_temperature, + ccd_setpoint, percentTE); + + if (verbose) { + printf("Temperature regulation: %s\n", + boolstring(ccd_tr_enabled)); + printf("Chip temperature: %'.1f C\n", + ccd_temperature); + printf("Setpoint temperature: %'.1f\n", + ccd_setpoint); + printf("TE regulation power: %'.0f%%\n\n", + percentTE); + } + + switch (tr) { + case 2: // set regulation and wait stabilization + if (setpoint == 1000) setpoint = ccd_setpoint; + printf("Activating temperature regulation.\n"); + printf("Target temperature: %'.1f C\n", + setpoint); + cam->SetTemperatureRegulation(1, setpoint); + + printf("Waiting temperature stabilization...\n"); + + // wait temperature regulation + for (;;) { + cam->QueryTemperatureStatus(ccd_tr_enabled, + ccd_temperature, + ccd_setpoint, percentTE); + if (verbose) printf("Current temperature is %'.1f C." + " Target: %'.1f C." + " TE regulation power: %'.0f%%\n", + ccd_temperature, setpoint, + percentTE); + if (fabs(ccd_temperature - setpoint) < 0.5) break; + sleep(5); + } + printf("Temperature regulation completed.\n"); + delete cam; + exit(0); + break; + + } + + + // allocate image buffer + data = (unsigned short*)calloc(width*height, sizeof(unsigned short)); + if (data == NULL) { + printf("Error malloc\'ing! :-/\n"); + delete cam; + exit(1); + } + + + // set up camera + cam->SetActiveCCD(ccd); + cam->SetExposureTime(exptime); + cam->SetReadoutMode(binning); + cam->SetABGState(abg); + + + // set filter + if (filter != 0) { + CFWResults cfwr; + CFWParams cfwp = {CFWSEL_CFW8, CFWC_GOTO, filter, + 0,0,NULL,0,NULL}; + + printf("Moving filter wheel to position %d... ", filter); + fflush(stdout); + cam->CFWCommand(cfwp, cfwr); + + //wait GOTO command finish + do { + usleep(500000); // .5 sec + cfwp.cfwCommand = CFWC_QUERY; + cam->CFWCommand(cfwp, cfwr); + //printf("status: %d\n", cfwr.cfwStatus); + } while (cfwr.cfwStatus == CFWS_BUSY); + printf("Done.\n"); + } + + + // start taking images + int i; + int index; + + // find last image + index = getindex(bfname) + 1; + + if (verbose) + printf("Taking %d images of %'.2f sec., with %d sec. interval.\n", + nexp, exptime, interval); + + for (i = 0; i < nexp; i++){ + int fitstatus = 0; + long int naxes[2]; + fitsfile *fptr = NULL; + + + // initialize file and header + naxes[0] = width; + naxes[1] = height; + + + // create file name + sprintf(imagefname,"%s-%04d.fits", bfname, index + i); + if(fits_create_file(&fptr, imagefname, &fitstatus)) { + fits_get_errstatus(fitstatus, errmsg); + printf("%s - criando arquivo\n", errmsg); + } + + + if(fits_create_img(fptr, USHORT_IMG, 2, naxes, &fitstatus)) { + fits_get_errstatus(fitstatus, errmsg); + printf("%s - criando imagem\n", errmsg); + } + + + // fill the header // FIXME add more header here + fits_update_key(fptr, TDOUBLE, "EXPTIME", &exptime, "Exposure Time (secs.)", &fitstatus); + +// // DETSIZE +// snprintf(buffer, 80, "[%d:%d,%d:%d]", 1, 2, 3, 4); +// fits_update_key(fptr, TSTRING, "DETSIZE", buffer, "Size of CCD detector", &fitstatus); + +// // CCDSEC +// snprintf(buffer, 80, "[%d:%d,%d:%d]", top, width, left, height); +// fits_update_key(fptr, TSTRING, "CCDSEC", buffer, "Region of CCD read", &fitstatus); + +// // DATASEC +// snprintf(buffer, 80, "[%d:%d,%d:%d]", top, width, left, height); +// fits_update_key(fptr, TSTRING, "DATASEC", buffer, "Region of CCD read", &fitstatus); + +// // BIASSEC +// snprintf(buffer, 80, "[%d:%d,%d:%d]", 1, 2, 3, 4); +// fits_update_key(fptr, TSTRING, "BIASSEC", buffer, "Bias level section", &fitstatus); + +// // TRIMSEC +// snprintf(buffer, 80, "[%d:%d,%d:%d]", 1, 2, 3, 4); +// fits_update_key(fptr, TSTRING, "TRIMSEC", buffer, "Useful section", &fitstatus); + +// // CCDSUM (for binning) +// snprintf(buffer, 80, "%d %d", &binning, &binning); +// fits_update_key(fptr, TSTRING, "CCDSUM", buffer, "CCD on-chip summing", &fitstatus); + + fits_write_date(fptr, &fitstatus); + + + // expose + printf("\nFile: %s\n", imagefname); + printf("Starting a %'.2f seconds exposure (shutter is %s)... ", + exptime, shutterstate); + fflush(stdout); + if (openshutter) + cam->StartExposure(SC_OPEN_SHUTTER); + else + cam->StartExposure(SC_CLOSE_SHUTTER); + + + // wait exposure done + MY_LOGICAL expcomplete; + + if (exptime < 0.5) + usleep((unsigned long)exptime*1000000UL + 50000UL); + else + sleep((unsigned int)(exptime+1.0)); + + cam->IsExposureComplete(expcomplete); + if (!expcomplete) do { + // check every 0.2 sec + usleep(200000UL); + cam->IsExposureComplete(expcomplete); + } while (!expcomplete); + cam->EndExposure(); + printf("Done.\n"); + + + // exposure complete, download data + StartReadoutParams srp; + ReadoutLineParams rlp; + + srp.ccd = ccd; + srp.readoutMode = binning; + srp.top = top; + srp.left = left; + srp.height = height; + srp.width = width; + + rlp.ccd = ccd; + rlp.readoutMode = binning; + rlp.pixelStart = left; + rlp.pixelLength = width; + + printf("Downloading image of size %dx%d... ", width, height); + fflush(stdout); + cam->StartReadout(srp); + unsigned short *dest = data; + int j; + for (j = 0; j < height; j++) { + dest = data + j*width; + cam->ReadoutLine(rlp, FALSE, dest); + } + cam->EndReadout(); + printf("Done.\n"); + + + // save file + //fitstatus = 0; + if(fits_write_img(fptr, TUSHORT, 1, (naxes[0] * naxes[1]), + data, &fitstatus)) { + fits_get_errstatus(fitstatus, errmsg); + printf("%s\n", errmsg); + } + + //fits_write_2d_usht(fptr, 0, naxes[0], + // naxes[0], naxes[1], data, &fitstatus); + + fits_close_file(fptr, &fitstatus); + + + // sleep until next image + if (i == nexp) break; + if (interval == 0) continue; + printf("Sleeping %d seconds until next image... ", interval); + fflush(stdout); + sleep(interval); + printf("Done.\n"); + + + } // imaging loop + printf("\nImaging complete.\n"); + + + // all done, clean up the mess + free(data); + delete cam; + + exit(0); +} + + +/*-------------------------------------------------------------------------*/ + +static void print_title(void) +{ + printf("%s - version: %s\n", PROGRAM_NAME, VERSION); +} + +/*-------------------------------------------------------------------------*/ + +static void print_help(void) +{ + print_title(); + print_usage(); + printf("Command line options:\n\n" + " -h / --help\n" + " This help screen.\n\n" + + " -v / --version\n" + " Display version and copyright information.\n\n" + + " -V / --verbose\n" + " Display more progress stuff.\n\n" + + " -C / --config-file CONFFILE\n" + " Read options from CONFFILE.\n\n" + + " -d / --device DEVICE\n" + " Use camera connected to DEVICE.\n" + " 0: USB (default)\n" + " 1: LPT1\n" + " 2: LPT2\n" + " 3: LPT3\n\n" + + " -c / --CCD N\n" + " Use imaging (0) or tracking (1) ccd.\n" + " Default: imaging.\n\n" + + " -r / --tregulation N\n" + " Control temperature regulation.\n" + " -1: Keep previous settings (default).\n" + " 0: Disable.\n" + " 1: Enable. If -T is present, change\n" + " setpoint temperature.\n" + " 2: Enable and wait temperature\n" + " stabilization. If -T is present,\n" + " change setpoint temperature.\n" + " With this option no imaging will\n" + " take place.\n\n" + + " -T / --temperature N\n" + " Setpoint temperature for regulation, in\n" + " degrees Celsius.\n\n" + + " -a / --ABG ABG_STATE\n" + " Antiblooming gate state during integration.\n" + " 0: Low (default).\n" + " 1: Clocked low.\n" + " 2: Clocked medium.\n" + " 3: Clocked high.\n\n" + + " -f / --filter N\n" + " Use Nth filter. N = 1 to 5\n" + " N = 0 (default) keeps the current filter.\n\n" + + " -t / --exptime TIME\n" + " Exposure time, in seconds.\n" + " Exposure times can be from 0.01 to 9999\n" + " seconds, in 0.01 sec. steps.\n" + " Default: 0 seconds.\n\n" + + " -n / --nexp N\n" + " Number of images to take.\n" + " Default: 1 image.\n\n" + + " -s / --openshutter 0/1\n" + " Open shutter? 0: No, 1: Yes.\n" + " Default: 0 - Shutter closed.\n\n" + + " -i / --interval TIME\n" + " Time interval between images, in seconds.\n" + " Default: 0 seconds.\n\n" + + " -w / --window X1:X2,Y1:Y2\n" + " Image window to be downloaded (zero-based).\n" + " Default: 0:765,0:510.\n\n" + + " -b / --binning N\n" + " Type of binning.\n" + " 0: No binning (default)\n" + " 1: 2x2\n" + " 2: 3x3\n\n" + + " -o / --output IMAGEFILE\n" + " Save image to IMAGEFILE.\n\n" + ); + + printf("\nFor more information visit the home page: %s\n", PROGRAM_URL); + exit(0); +} + +/*-------------------------------------------------------------------------*/ + +static void print_version(void) +{ + print_title(); + printf("Copyright (C) 2004 Andre Luiz de Amorim\n"); + printf("%s comes with NO WARRANTY,\n" + "to the extent permitted by law.\n", PROGRAM_NAME); + printf("You may redistribute copies of %s\n" + "under the terms of the GNU General Public License.\n" + "For more information about these matters,\n" + "see the files named COPYING.\n", PROGRAM_NAME); + + //exit(0); +} + +/*-------------------------------------------------------------------------*/ + +static void print_usage(void) +{ + printf("SYNTAX:\n" + " %s -t exptime -n nexp -d device -o imagefile\n\n" + " %s {-h|--help}\n\n", exec_name, exec_name); +} + +/*-------------------------------------------------------------------------*/ + +static void get_args(int argc, char **argv) +{ + int next_opt; + const char *short_options = "hvVC:d:c:r:T:a:f:t:n:s:i:w:b:o:"; + const struct option long_options [] = { + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'v' }, + { "verbose", 0, NULL, 'V' }, + + { "config-file", 1, NULL, 'C' }, + { "device", 1, NULL, 'd' }, + { "CCD", 1, NULL, 'c' }, + { "tregulation", 1, NULL, 'r' }, + { "temperature", 1, NULL, 'T' }, + { "ABG", 1, NULL, 'a' }, + { "filter", 1, NULL, 'f' }, + { "exptime", 1, NULL, 't' }, + { "nexp", 1, NULL, 'n' }, + { "openshutter", 1, NULL, 's' }, + { "interval", 1, NULL, 'i' }, + { "window", 1, NULL, 'w' }, + { "binning", 1, NULL, 'b' }, + { "output", 1, NULL, 'o' }, + { NULL, 0, NULL, 0 } + }; + + // default values + exec_name = argv[0]; + verbose = 0; + conffname = NULL; + bfname = "img"; + filter = 0; // keep the same + device = DEV_USB; + ccd = CCD_IMAGING; + tr = -1; // keep the same + setpoint = 1000; // default value will be read from camera + abg = ABG_LOW7; + exptime = 0; + nexp = 1; + openshutter = 0; // dont open shutter + interval = 0; + binning = 0; // no binning + + top = 0; // full frame + left = 0; + width = 765; + height = 510; + + do { + next_opt = getopt_long(argc, argv, short_options, + long_options, NULL); + switch (next_opt) { + case 'h': + print_help(); + break; + + case 'v': + print_version(); + exit(0); + break; + + case 'V': + verbose = 1; + break; + + case 'C': + /* config file */ + conffname = optarg; + break; + + case 'd': + /* device */ + int temp; + sscanf(optarg, "%d", &temp); + switch (temp) { + case 0: + device = DEV_USB; + break; + + case 1: + device = DEV_LPT1; + break; + + case 2: + device = DEV_LPT2; + break; + + case 3: + device = DEV_LPT3; + break; + } // temp + break; + + case 'c': + /* ccd */ + sscanf(optarg, "%hu", &ccd); + break; + + case 'r': + /* temperature regulation */ + sscanf(optarg, "%d", &tr); + break; + + case 'T': + /* target temperature */ + sscanf(optarg, "%lf", &setpoint); + break; + + case 'a': + /* abg state */ + sscanf(optarg, "%hu", &abg); + break; + + case 'f': + /* filter */ + sscanf(optarg, "%hu", &filter); + break; + + case 't': + /* exposure time */ + sscanf(optarg, "%lf", &exptime); + break; + + case 'n': + /* number of images */ + sscanf(optarg, "%d", &nexp); + break; + + case 's': + /* open shutter? */ + sscanf(optarg, "%d", &openshutter); + break; + + case 'i': + /* time interval between images */ + sscanf(optarg, "%d", &interval); + break; + + case 'w': + /* set image window */ + sscanf(optarg, "%hu:%hu,%hu:%hu", + &left, &width, &top, &height); + // optarg comes as X1:X2,Y1:Y2, fix width and height + width -= left; + height -= top; + break; + + case 'b': + /* set readout mode */ + sscanf(optarg, "%hu", &binning); + break; + + case 'o': + /* base image file name */ + //imagefname = optarg; + bfname = optarg; + break; + + case '?': + /* Invalid option */ + print_usage(); + exit(1); + break; + + case -1: + /* end of options */ + break; + + default: + printf("Tell author he should read the getopt_long man page.\n"); + exit(1); + break; + } + } while (next_opt != -1); + +} + +/*-------------------------------------------------------------------------*/ + diff --git a/src/drivers/camfits.st7/sbigcamcap.cpp b/src/drivers/camfits.st7/sbigcamcap.cpp new file mode 100644 index 00000000..c4964165 --- /dev/null +++ b/src/drivers/camfits.st7/sbigcamcap.cpp @@ -0,0 +1,98 @@ +/* + * camfits.st7 - SBIG ST 7/8 Controller + * Copyright (C) 2005 Paulo Henrique Silva + * + * This file is part of camfits.st7 + * camfits.st7 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * camfits.st7 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA + * + */ + +/* $Id: sbigcamcap.cpp,v 1.1.2.1 2006/05/17 16:32:34 henrique Exp $ */ + +#include "sbigcamcap.h" +#include "csbigcam.h" + +using namespace std; + +int SBIGCamCap::loadData() { + + if(ccd == CCD_IMAGING) { + infoPars.request = 0; + } else if(ccd == CCD_TRACKING) { + infoPars.request = 1; + } + + if(cam->SBIGUnivDrvCommand(CC_GET_CCD_INFO, &infoPars, &infoRes0) == CE_NO_ERROR) { + + _ccdModel = infoRes0.name; + _ccdMaker = "SBIG"; + _ccdSubModel = ""; + + _nReadoutModes = infoRes0.readoutModes; + + for(int i = 0; i < _nReadoutModes; i++) { + ReadoutMode* m = new ReadoutMode; + m->width = infoRes0.readoutInfo[i].width; + m->height = infoRes0.readoutInfo[i].height; + m->pixelWidth = infoRes0.readoutInfo[i].pixelWidth; + m->pixelHeight = infoRes0.readoutInfo[i].pixelHeight; + m->gain = infoRes0.readoutInfo[i].gain; + m->modeFlag = infoRes0.readoutInfo[i].mode; + + _readoutModes.push_back(m); + + } + + _firmwareVersion = infoRes0.firmwareVersion; + + } else { + return 1; + } + + infoPars.request = 2; + + if(cam->SBIGUnivDrvCommand(CC_GET_CCD_INFO, &infoPars, &infoRes2) == CE_NO_ERROR) { + + _antiBlooming = infoRes2.imagingABG; + + } else { + return 1; + } + + if(ccd == CCD_IMAGING) { + infoPars.request = 4; + } else if(ccd == CCD_TRACKING) { + infoPars.request = 5; + } + + if(cam->SBIGUnivDrvCommand(CC_GET_CCD_INFO, &infoPars, &infoRes4) == CE_NO_ERROR) { + + unsigned short capbits; + + capbits = infoRes4.capabilitiesBits & CB_CCD_TYPE_MASK; + + _fullFrame = (((capbits) & (CB_CCD_TYPE_FULL_FRAME)) != 0); + _frameTransfer = (((capbits) & (CB_CCD_TYPE_FRAME_TRANSFER)) != 0); + + capbits = infoRes4.capabilitiesBits & CB_CCD_ESHUTTER_MASK; + + _interline = (((capbits) & (CB_CCD_ESHUTTER_YES)) != 0); + _shutter = (((capbits) & (CB_CCD_ESHUTTER_NO)) != 0); + + } else { + return 1; + } + +} diff --git a/src/drivers/camfits.st7/sbigcamcap.h b/src/drivers/camfits.st7/sbigcamcap.h new file mode 100644 index 00000000..035c1a60 --- /dev/null +++ b/src/drivers/camfits.st7/sbigcamcap.h @@ -0,0 +1,53 @@ +/* + * camfits.st7 - SBIG ST 7/8 Controller + * Copyright (C) 2005 Paulo Henrique Silva + * + * This file is part of camfits.st7 + * camfits.st7 is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * camfits.st7 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA + * + */ + +/* $Id: sbigcamcap.h,v 1.1.2.1 2006/05/17 16:32:35 henrique Exp $ */ + +#ifndef _SBIG_CAM_CAP_h_ +#define _SBIG_CAM_CAP_h_ + +#include "ccdcap.h" +#include "csbigcam.h" + +class SBIGCamCap: public CCDCap { + +public: + SBIGCamCap(CSBIGCam* mCam, CCD_REQUEST mCcd): cam(mCam), + ccd(mCcd) { loadData(); }; + + virtual ~SBIGCamCap() { }; + +private: + + int loadData(); + + CSBIGCam* cam; + CCD_REQUEST ccd; + + GetCCDInfoParams infoPars; + GetCCDInfoResults0 infoRes0; + GetCCDInfoResults2 infoRes2; + GetCCDInfoResults4 infoRes4; + +}; + + +#endif // !_SBIG_CAM_CAP_H_ diff --git a/src/drivers/camfits.st7/st7.camera.xml b/src/drivers/camfits.st7/st7.camera.xml new file mode 100644 index 00000000..5183414a --- /dev/null +++ b/src/drivers/camfits.st7/st7.camera.xml @@ -0,0 +1,92 @@ + + + + + SBIG + Camera + + +camfits.st7 --device $device --CCD $ccd --filter $filter --exptime $exp_time --nexp $n_exp --openshutter $shutter --interval $interval --window $window --binning $binning --output $output + + + + + device + int + 0 + Device to connect. 0 = USB, 1 = LPT1, 2 = LPT2, ... + + + + ccd + int + 0 + CCD chip to use. 0 = Imaging, 1 = Tracking. + + + + filter + str + clear + Filter to use. Names come from filter device used. (Always will exists a clear one). + + + + exp_time + float + 1.0 + Integration time in seconds. Min and max comes from CCDcap. + + + + n_exp + int + 1 + Number of frames to take. + + + + shutter + bool + True + Open the shutter?. + + + + interval + float + 1.0 + Interval between exposures. + + + + window + str + 0:$max,0,$max + Sub-window to be readout. x1:y1,x2:y2. $max means the maximun value in the given direction. Math can be used, e.g. $max /2 (half chip) + + + + binning + int + 0 + Binning mode. Values come from CCDcap. + + + + output + str + img-$time-$object-$filter + Filename of the output image (wildcards allowed: $time, $object, $filter). + + + + verbose + bool + True + Verbose mode. + + + + + diff --git a/src/drivers/telgo.fake/Makefile.am b/src/drivers/telgo.fake/Makefile.am new file mode 100644 index 00000000..d302263e --- /dev/null +++ b/src/drivers/telgo.fake/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/rules.make + +bin_PROGRAMS = telgo.fake + +telgo_fake_SOURCES = errors.h fields.c fields.h main.c ser.c ser.h + +telgo_fake_LDADD = -lm -lcfitsio + +uts_driver_DATA = fake.telescope.xml diff --git a/src/drivers/telgo.fake/errors.h b/src/drivers/telgo.fake/errors.h new file mode 100644 index 00000000..e68db79a --- /dev/null +++ b/src/drivers/telgo.fake/errors.h @@ -0,0 +1,35 @@ +/*************************************************************************** + errors.h - description + ------------------- + begin : Wed Jun 14 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#define TRUE (1) +#define FALSE (0) + + + +#define ERR_OK (0) +#define ERR_OPENTTY (-1) +#define ERR_SERIAL (-2) +#define ERR_BAUDRATE (-3) +#define ERR_PARITY (-4) +#define ERR_STOPB (-5) +#define ERR_WRITE (-6) +#define ERR_READ (-7) +#define ERR_MEM (-8) +#define ERR_NOTRESPOND (-9) + +#define ERR_CAM (-50) +#define ERR_CMD (-51) +#define ERR_EXPOSE (-52) diff --git a/src/drivers/telgo.fake/fake.telescope.xml b/src/drivers/telgo.fake/fake.telescope.xml new file mode 100644 index 00000000..6112e058 --- /dev/null +++ b/src/drivers/telgo.fake/fake.telescope.xml @@ -0,0 +1,50 @@ + + + + + Fake + Telescope + + + telgo.fake -d $device -P $paranoia $ra,$dec + + + + + device + str + /dev/ttyS0 + Device to connect. + + + + paranoia + int + 1 + Iteration on Paranoia's mode. see man telgo.lx200 for more on Paranoia mode. + + + + ra + str + 00:00:00 + RA to slew. Hours mode. + + + + dec + str + 00:00:00 + DEC to slew. Sexagesimal mode. + + + + verbose + bool + True + Verbose mode. + + + + + diff --git a/src/drivers/telgo.fake/fields.c b/src/drivers/telgo.fake/fields.c new file mode 100644 index 00000000..494980c3 --- /dev/null +++ b/src/drivers/telgo.fake/fields.c @@ -0,0 +1,67 @@ +/*************************************************************************** + fields.c - description + ------------------- + begin : Wed Jun 7 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include +#include +#include "fields.h" + +/* char *fieldnames[] = { "EXPTIME", "START", "NEXP", "BFNAME", "INDEX", "BEGPIXX", "BEGPIXY", "ENDPIXX", "ENDPIXY" }; */ +extern char *fieldnames[]; + +#ifdef FIELDTEST +int main(void) +{ + field f; + char texto[] = " # EXPTIME 1.3 "; + + if (get_field(texto, &f) == EOF) { + puts("Comment line"); + return(1); + } + printf("The field is %s. Contents: %s\n\n", fieldnames[f.index], f.contents); + return(0); +} +#endif /* FIELDTEST */ + + + +int get_field(char *command, field *f) +{ + int i; + char tmp[MAX_FIELD_SIZE]; + char *ptr; + + if (command == NULL || *command == '\0') return(EOF); + /* find field type */ + ptr = tmp; + while (isspace(*command)) command++; + if (*command == COMMENT_CHAR) return(EOF); + while (!isspace(*command)) *ptr++ = *command++; + *ptr = '\0'; + for (i = 0; (i < 6) && strncmp(tmp, fieldnames[i], MAX_FIELD_SIZE); i++); + f->index = i; + + /* store field content */ + ptr = f->contents; + while (isspace(*command)) command++; + while (*ptr++ = *command++); + *(ptr -= 2) = '\0'; + +/* strcpy(f->contents, command); */ + return(i); +} + diff --git a/src/drivers/telgo.fake/fields.h b/src/drivers/telgo.fake/fields.h new file mode 100644 index 00000000..7e7df0ab --- /dev/null +++ b/src/drivers/telgo.fake/fields.h @@ -0,0 +1,32 @@ +/*************************************************************************** + fields.h - description + ------------------- + begin : Wed Jun 7 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef FIELDS_H +#define FIELDS_H +#define MAX_FIELD_SIZE 20 +#define MAX_FIELD_CONTENT 64 +#define COMMENT_CHAR '#' + +//extern char **fieldnames; + +typedef struct { + int index; + char contents[MAX_FIELD_CONTENT]; +} field; + +int get_field(char *command, field *f); + +#endif /* FIELDS_H */ diff --git a/src/drivers/telgo.fake/main.c b/src/drivers/telgo.fake/main.c new file mode 100644 index 00000000..f35d6193 --- /dev/null +++ b/src/drivers/telgo.fake/main.c @@ -0,0 +1,237 @@ +/*************************************************************************** + tel.c - description + ------------------- + begin : Wed Aug 1 2001 + copyright : (C) 2001 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include "fields.h" +#include "errors.h" +#include "ser.h" + +/* enum { FALSE, TRUE };*/ +typedef enum { BUSY, WAIT } stat_t; + +/* mount types */ +enum { LAND, POLAR, ALTAZ }; + +struct ras { + int hh; + int mm; + int ss; +}; + +struct decs { + int dd; + int mm; + int ss; +}; + +typedef struct { + struct ras ra; + float ra_range; + struct decs dec; + float dec_range; + float lat; + float lon; + stat_t status; + int moving; + int mnt_type; + ser_info *serial; + char *outbuf; +} tel_info; + + +void wait_tel(tel_info *tel); +void usage(char *command); +void setup(void); +void status(tel_info *tel, stat_t stat); +void controlc(int signumber); + + +field f; +tel_info tel; +char *porta; +char linebuf[128]; +char outbuf[128]; +char fmask[20]; +char imgfname[50]; +int ram, rah, decm, decd; +float ras, decs; + +enum { QUIT, RA, DEC, ACTION }; +char *fieldnames[] = { "QUIT", "RA", "DEC", "ACTION" }; + +int main(int argc, char ** argv) +{ + + signal(SIGINT, controlc); + signal(SIGABRT, controlc); + + fprintf(stderr, "LX-200 Fake Telescope controller ver. 0.01f.\n\n"); + if (argc < 4) { + usage(argv[0]); + exit(1); + } + + porta = argv[1]; + + sscanf(argv[2], "%d:%d:%d", &tel.ra.hh, &tel.ra.mm, &tel.ra.ss); + sscanf(argv[3], "%d:%d:%d", &tel.dec.dd, &tel.dec.mm, &tel.dec.ss); + + printf("Moving to ra=%s dec=%s\n", argv[2], argv[3]); + //setup(); + + /*** Write Coords. ***/ + + tel.moving = TRUE; + /*set RA */ +/* sprintf(tel.outbuf, ":Sr%02d:%4.1f#", + tel.ra.hh, (double)tel.ra.mm + (double)tel.ra.ss / 60); + //printf(tel.outbuf); + //fflush(stdout); + write_serial(tel.serial, tel.outbuf, strlen(outbuf)); + read_serial(tel.serial, tel.outbuf, 128); + if (*(tel.outbuf) == '0') { + sprintf(tel.outbuf, ":Sr%02d:%4.1f#", + tel.ra.hh, (double)(tel.ra.mm + (tel.ra.ss / 60))); + write_serial(tel.serial, tel.outbuf, strlen(outbuf)); + read_serial(tel.serial, tel.outbuf, 128); + } */ +// fprintf(stderr, "%c", *(tel.outbuf)); + + /* set DEC */ +/* sprintf(tel.outbuf, ":Sd%02d*%02d#", + tel.dec.dd, tel.dec.mm); + write_serial(tel.serial, tel.outbuf, strlen(outbuf)); + read_serial(tel.serial, tel.outbuf, 128); + if (*(tel.outbuf) == '0') { + sprintf(tel.outbuf, ":Sd%02d*%02d#", + tel.dec.dd, tel.dec.mm); + write_serial(tel.serial, tel.outbuf, strlen(outbuf)); + read_serial(tel.serial, tel.outbuf, 128); + } */ +// fprintf(stderr, "%c", *(tel.outbuf)); + + /* move telescope */ +/* sprintf(tel.outbuf, ":MS#"); + write_serial(tel.serial, tel.outbuf, strlen(outbuf)); + read_serial(tel.serial, tel.outbuf, 128); +// fprintf(stderr, "%c", *(tel.outbuf)); +// fprintf(stderr, "\n%s\n", tel.outbuf); + + if (*(tel.outbuf) == '0') { + wait_tel(&tel); */ + sleep(5); + fprintf(stderr, "\nDone\n"); +/* } */ + //else fprintf(stderr, "\nCould not move\n"); + + + exit(0); +} + + +void wait_tel(tel_info *tel) +{ + int rah; + float ram; + int decd, decm; + float delta_dec; + float delta_ra; + char buf[64]; + + while (tel->moving) { + /* get current RA */ + strcpy(buf, ":GR#"); + write_serial(tel->serial, buf, strlen(buf)); + read_serial(tel->serial, buf, 64); + sscanf(buf, "%d:%f#", &rah, &ram); + + + /* get current DEC */ + strcpy(buf, ":GD#"); + write_serial(tel->serial, buf, strlen(buf)); + read_serial(tel->serial, buf, 64); + sscanf(buf, "%d%*c%d#", &decd, &decm); + + + /* are DEC and RA in range? */ + decd -= tel->dec.dd; + decm -= tel->dec.mm; + delta_dec = fabs(decd + (decm / 60)); + + rah -= tel->ra.hh; + ram -= tel->ra.mm; + ram -= ((double)tel->ra.ss / 60); + delta_ra = fabs(rah + (ram / 60)); + /* limpa a linha e escreve os deltas */ + fprintf(stderr, "\r "); + fprintf(stderr, "\rdelta-dec = %f; delta-ra = %f", delta_dec, delta_ra); + fflush(stderr); + if ((delta_dec < tel->dec_range) && (delta_ra < tel->ra_range)) + tel->moving = FALSE; + sleep(1); + } + sleep(1); +} + + +void usage(char *command) +{ + fprintf(stderr, "SYNTAX: %s terminal_device RA DEC\n\n", command); + fprintf(stderr, "E.g.: %s /dev/ttyS1 12:12:12 13:12:14\n\n", command); +} + + +/* sets up telescope structs and initializes its parameters */ +void setup(void) +{ + tel.serial = (ser_info*) calloc(sizeof(ser_info), 1); + open_serial(tel.serial, porta); + if (tel.serial->err_code != ERR_OK) { + free(tel.serial); + abort(); + } + + /* 9600,8n1 */ + tel.serial->baudrate = B9600; + tel.serial->parity = PAR_NONE; + tel.serial->stopbits = 1; + tel.serial->timeout = 5; + tel.serial->minchars = 20; + tel.serial->waitread = TRUE; + set_serial(tel.serial); + + tel.ra_range = 0.007; /* 1min */ + tel.dec_range = 0.007; /* 1min */ + tel.moving = FALSE; + tel.status = BUSY; + tel.outbuf = outbuf; + +} + + +void controlc(int signumber) +{ + if (signumber == SIGINT) + fprintf(stderr, "\nControl-C signal caught, exiting.\n"); + else + fprintf(stderr, "\nAborting...\n"); + exit(2); +} diff --git a/src/drivers/telgo.fake/ser.c b/src/drivers/telgo.fake/ser.c new file mode 100644 index 00000000..1a250b50 --- /dev/null +++ b/src/drivers/telgo.fake/ser.c @@ -0,0 +1,267 @@ +/*************************************************************************** + ser.c - description + ------------------- + begin : Wed Jun 7 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +/* This file contains the functions for serial communication */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "errors.h" +#include "ser.h" + +static void rdtimeout(int signumber); +static int set_minchars(ser_info *serial, int nbytes); +static ser_info *sertmp; + +#ifdef COMMTEST + +/* This example program writes a string in port `tty', and waits until + timeout for a response. */ + + +char teste[] = "Write test..."; +unsigned char buf[25]; +char tty[] = "/dev/ttyS1"; + + +int main(void) +{ + int result; + ser_info porta; + + open_serial(&porta, tty); + if (porta.err_code != ERR_OK) return(-1); + puts("Communication test.\n\n"); + + /* 9600,8e1 */ + porta.baudrate = B9600; + porta.parity = PAR_EVEN; + porta.stopbits = 1; + porta.timeout = 10; + porta.minchars = 25; + porta.waitread = FALSE; + set_serial(&porta); + write_serial(&porta, teste, strlen(teste)); + + if (read_serial(&porta, buf, 24)) + printf("Text received: %s\n\n", buf); + else printf("Read timeout.\n\n"); + + close_serial(&porta); + return(0); +} +#endif /* COMMTEST */ + + +int is_valid_serial(ser_info *serial) +{ + if ((serial == NULL) || + (serial->tty_name == NULL) || + (serial->tio == NULL) || + (serial->oldtio == NULL)) + return(FALSE); + return(TRUE); +} + + +void open_serial(ser_info *serial, char *ttyname) +{ + if (serial == NULL) return; + + serial->err_code = ERR_OK; + signal(SIGALRM, rdtimeout); + +/* Allocate memory for the structures */ + serial->tio = (struct termios *) malloc(sizeof(struct termios)); + if (serial->tio == NULL) { + serial->err_code = ERR_MEM; + return; + } + serial->oldtio = (struct termios *) malloc(sizeof(struct termios)); + if (serial->oldtio == NULL) { + serial->err_code = ERR_MEM; + return; + } + + serial->tty_name = (char *) calloc(strlen(ttyname), sizeof(char)); + if (serial->tty_name == NULL) { + serial->err_code = ERR_MEM; + return; + } + strcpy(serial->tty_name, ttyname); + + + serial->ttyfd = open(serial->tty_name, O_RDWR | O_NOCTTY); + if (serial->ttyfd < 0) { + serial->err_code = ERR_OPENTTY; + close_serial(serial); + return; + } + +/* Save current config */ + tcgetattr(serial->ttyfd, serial->oldtio); + serial->isopen = TRUE; +} + + +int close_serial(ser_info *serial) +{ + if (!is_valid_serial(serial)) return(ERR_MEM); + signal(SIGALRM, SIG_DFL); +/* Restore terminal to its previous state; free memory */ + if (serial->isopen) { + tcsetattr(serial->ttyfd,TCSANOW,serial->tio); + close(serial->ttyfd); + } + if (serial->tio != NULL) free(serial->tio); + if (serial->oldtio != NULL) free(serial->oldtio); + if (serial->tty_name != NULL) free(serial->tty_name); + return(ERR_OK); +} + + +int set_serial(ser_info *serial) +{ + if (!is_valid_serial(serial)) return(ERR_MEM); + if (!serial->isopen) { + serial->err_code = ERR_SERIAL; + return(ERR_SERIAL); + } + memset(serial->tio, 0, sizeof(struct termios)); + + cfsetospeed(serial->tio, serial->baudrate); + cfsetispeed(serial->tio, serial->baudrate); + +/* Set byte size to 8bit, enable reading and set local mode */ + serial->tio->c_cflag |= CS8 | CLOCAL | CREAD; + switch (serial->parity) { + case PAR_NONE: + serial->tio->c_iflag |= IGNPAR; + serial->tio->c_cflag &= ~PARENB; + break; + + case PAR_EVEN: + serial->tio->c_iflag &= ~IGNPAR; + serial->tio->c_cflag |= PARENB; + serial->tio->c_cflag &= ~PARODD; + break; + + case PAR_ODD: + serial->tio->c_iflag &= ~IGNPAR; + serial->tio->c_cflag |= PARENB; + serial->tio->c_cflag &= PARODD; + break; + default: + serial->err_code = ERR_PARITY; + return(ERR_PARITY); + } + + if (serial->stopbits == 1) + serial->tio->c_cflag &= ~CSTOPB; + else if (serial->stopbits ==2) + serial->tio->c_cflag |= CSTOPB; + else { + serial->err_code = ERR_STOPB; + return(ERR_STOPB); + } + +/* Raw input */ + serial-> tio->c_lflag = 0; + + serial->tio->c_cc[VTIME] = serial->timeout; + if (serial->waitread) + serial->tio->c_cc[VMIN] = serial->minchars; + + tcsetattr(serial->ttyfd,TCSANOW,serial->tio); + return(ERR_OK); +} + + +int write_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes) +{ + int result; + + if (!is_valid_serial(serial)) return(ERR_MEM); + if (!serial->isopen) { + serial->err_code = ERR_SERIAL; + return(0); + } + result = write(serial->ttyfd, buf, nbytes); + if (result < nbytes) { + serial->err_code = ERR_WRITE; + return(result); + } + serial->err_code = ERR_OK; + return(result); +} + + +int read_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes) +{ + int result; + + if (!is_valid_serial(serial)) return(ERR_MEM); + if (!serial->isopen) { + serial->err_code = ERR_SERIAL; + return(0); + } + serial->err_code = ERR_OK; + +/* Set the minimum number of chars to read before returning */ + if (serial->waitread && (serial->minchars != nbytes)) { + if (set_minchars(serial, nbytes) != ERR_OK) { + serial->err_code = ERR_SERIAL; + return(0); + } + } +/* save serial stuff for alarm() timeout. */ + sertmp = serial; + + alarm(serial->timeout); + result = read(serial->ttyfd, buf, nbytes); + alarm(0); + serial->bytesread = result; + if (result < nbytes) + serial->err_code = ERR_READ; + return(result); +} + + +static int set_minchars(ser_info *serial, int nbytes) +{ + if (!is_valid_serial(serial)) + return(ERR_MEM); + if (!serial->waitread) + return(ERR_READ); + serial->minchars = nbytes; + serial->tio->c_cc[VMIN] = nbytes; + tcsetattr(serial->ttyfd,TCSANOW,serial->tio); + return(ERR_OK); +} + +static void rdtimeout(int signumber) +{ + fprintf(stderr, "Serial communication failed, closing %s...\n", sertmp->tty_name); + if (sertmp != NULL) close_serial(sertmp); + /* exit(-1); */ +} + diff --git a/src/drivers/telgo.fake/ser.h b/src/drivers/telgo.fake/ser.h new file mode 100644 index 00000000..5547455e --- /dev/null +++ b/src/drivers/telgo.fake/ser.h @@ -0,0 +1,49 @@ +/*************************************************************************** + ser.h - description + ------------------- + begin : Wed Jun 7 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include + +#define RD_TIMEOUT (2) +#define TRUE (1) +#define FALSE (0) + +#define MAX_BUF_LEN (255) +#define PAR_NONE 'n' +#define PAR_EVEN 'e' +#define PAR_ODD 'o' + +typedef struct { + int isopen; + int waitread; + int err_code; + char *tty_name; + int ttyfd; + cc_t minchars; /* not used if waitread == FALSE */ + int bytesread; + int baudrate; + char parity; + int stopbits; + cc_t timeout; /* interchar time (1/10 secs), and read timeout (secs) */ + struct termios *tio; /* |- don't mess with these structs, */ + struct termios *oldtio; /* | the routines will handle it. */ +} ser_info; + +int is_valid_serial(ser_info *serial); +void open_serial(ser_info *serial, char *ttyname); +int close_serial(ser_info *serial); +int set_serial(ser_info *serial); +int read_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes); +int write_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes); diff --git a/src/drivers/telgo.lx200/Makefile.am b/src/drivers/telgo.lx200/Makefile.am new file mode 100644 index 00000000..376c48c2 --- /dev/null +++ b/src/drivers/telgo.lx200/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/rules.make + +bin_PROGRAMS = telgo.lx200 + +telgo_lx200_SOURCES = errors.h main.c ser.c ser.h lx200.c lx200.h + +telgo_lx200_LDADD = -lm + +uts_driver_DATA = lx200.telescope.xml diff --git a/src/drivers/telgo.lx200/errors.h b/src/drivers/telgo.lx200/errors.h new file mode 100644 index 00000000..e68db79a --- /dev/null +++ b/src/drivers/telgo.lx200/errors.h @@ -0,0 +1,35 @@ +/*************************************************************************** + errors.h - description + ------------------- + begin : Wed Jun 14 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#define TRUE (1) +#define FALSE (0) + + + +#define ERR_OK (0) +#define ERR_OPENTTY (-1) +#define ERR_SERIAL (-2) +#define ERR_BAUDRATE (-3) +#define ERR_PARITY (-4) +#define ERR_STOPB (-5) +#define ERR_WRITE (-6) +#define ERR_READ (-7) +#define ERR_MEM (-8) +#define ERR_NOTRESPOND (-9) + +#define ERR_CAM (-50) +#define ERR_CMD (-51) +#define ERR_EXPOSE (-52) diff --git a/src/drivers/telgo.lx200/lx200.c b/src/drivers/telgo.lx200/lx200.c new file mode 100644 index 00000000..b3ba94cd --- /dev/null +++ b/src/drivers/telgo.lx200/lx200.c @@ -0,0 +1,348 @@ +#include +#include +#include +#include +#include +#include +#include "errors.h" +#include "ser.h" + +#include "lx200.h" + + +static void controlc(int signumber); + + +/*-------------------------------------------------------------------------*/ + + +int lx200_point(tel_info *tel, struct ras ra, struct decs dec) +{ + lx200_set_coords(tel, ra, dec); + lx200_slew(tel); + lx200_wait_slew(tel); + + return (0); +} + + +/*-------------------------------------------------------------------------*/ + + +int lx200_wait_slew(tel_info *tel) +{ + struct ras Dra; //delta RA + struct decs Ddec; //delta DEC + int i = LX_SLEW_TIMEOUT; + + + + while (i--) { + lx200_update_coords(tel); + + // evaluate displacement. + Ddec.dd = tel->pdec.dd - tel->cdec.dd; + Ddec.mm = tel->pdec.mm - tel->cdec.mm; + Ddec.ss = tel->pdec.ss - tel->cdec.ss; + + Dra.hh = tel->pra.hh - tel->cra.hh; + Dra.mm = tel->pra.mm - tel->cra.mm; + Dra.ss = tel->pra.ss - tel->cra.ss; + + if (abs(Dra.hh) <= tel->ra_range.hh && + abs(Dra.mm) <= tel->ra_range.mm && + abs(Dra.ss) <= tel->ra_range.ss && + + abs(Ddec.dd) <= tel->dec_range.dd && + abs(Ddec.mm) <= tel->dec_range.mm && + abs(Ddec.ss) <= tel->dec_range.ss) { + //Close enough + sleep(10); + return (0); + } + sleep(1); + } + // bad, timeout :( + return (-1); +} + + +/*-------------------------------------------------------------------------*/ + + +int lx200_update_coords(tel_info *tel) +{ + char cmd1[] = ":GR#"; + char cmd2[] = ":GD#"; + + /* get current RA */ + write_serial(tel->serial, cmd1, strlen(cmd1)); + read_serial(tel->serial, tel->outbuf, LX_BUFSIZE); + + sscanf(tel->outbuf, "%d:%d:%d#", + &(tel->cra.hh), + &(tel->cra.mm), + &(tel->cra.ss)); + + + /* get current DEC */ + write_serial(tel->serial, cmd2, strlen(cmd2)); + read_serial(tel->serial, tel->outbuf, LX_BUFSIZE); + + sscanf(tel->outbuf, "%d%*c%d:%d#", + &(tel->cdec.dd), + &(tel->cdec.mm), + &(tel->cdec.ss)); + return(0); +} + + +/*-------------------------------------------------------------------------*/ + + +int lx200_slew(tel_info *tel) +{ + char cmd[] = ":MS#"; + + // make sure tracking is ON + lx200_ra_drive_on(tel); + + write_serial(tel->serial, cmd, strlen(cmd)); + read_serial(tel->serial, tel->outbuf, LX_BUFSIZE); + + if (*(tel->outbuf) != '0') // can not slew + return (-1); + + return (0); +} + + +/*-------------------------------------------------------------------------*/ + + +int lx200_match_coords(tel_info *tel) +{ + char cmd[] = ":CM#"; + + write_serial(tel->serial, cmd, strlen(cmd)); + read_serial(tel->serial, tel->outbuf, LX_BUFSIZE); + + // Finda way to check the success of this operation + + return (0); +} + + +/*-------------------------------------------------------------------------*/ + + +int lx200_set_coords(tel_info *tel, struct ras ra, struct decs dec) +{ + /*set RA */ + sprintf(tel->outbuf, ":Sr %02d:%02d:%02d#", + ra.hh, ra.mm, ra.ss); + + write_serial(tel->serial, tel->outbuf, strlen(tel->outbuf)); + read_serial(tel->serial, tel->outbuf, LX_BUFSIZE); + + /* set DEC */ + sprintf(tel->outbuf, ":Sd %02d*%02d:%02d#", + dec.dd, dec.mm, dec.ss); + + write_serial(tel->serial, tel->outbuf, strlen(tel->outbuf)); + read_serial(tel->serial, tel->outbuf, LX_BUFSIZE); + + tel->pra = ra; + tel->pdec = dec; + return (0); +} + + +/*-------------------------------------------------------------------------*/ + + +int lx200_set_precision(tel_info *tel, int precision) +{ + char cmd1[] = ":GR#"; + char cmd2[] = ":U#"; + int nb; + int rdprec; + + write_serial(tel->serial, cmd1, strlen(cmd1)); + nb = read_serial(tel->serial, tel->outbuf, LX_BUFSIZE); + + //depending on precision, "get RA returns: + // :NN:NN:NN# == 10 char, or + // :NN:NN.N# == 9 char. + + rdprec = (nb==LX_HIPRECISION_RESP)?LX_PRECISION_HIGH:LX_PRECISION_LOW; + if (rdprec != precision) write_serial(tel->serial, cmd2, strlen(cmd2)); + + return (0); + } + + +/*-------------------------------------------------------------------------*/ + + +/* sets up telescope structs and initializes its parameters */ +tel_info *lx200_open(char *device) +{ + tel_info *tel; + ser_info *ser; + + tel = calloc(sizeof(tel_info), 1); + tel->outbuf = calloc(sizeof(char), LX_BUFSIZE); + if(device == NULL) device = LX_DEFAULT_DEVICE; + ser = open_serial(device); + if (ser == NULL) { + free(tel->outbuf); + free(tel); + return (NULL); + } + /* 9600,8n1 */ + ser->baudrate = B9600; + ser->parity = PAR_NONE; + ser->stopbits = 1; + ser->timeout = 5; + ser->minchars = 20; + ser->waitread = TRUE; + set_serial(ser); + + tel->ra_range.hh = 0; + tel->ra_range.mm = 0; + tel->ra_range.ss = 1; + + tel->dec_range.dd = 0; + tel->dec_range.mm = 0; + tel->dec_range.ss = 3; + + tel->moving = FALSE; + tel->serial = ser; + + signal(SIGINT, controlc); + signal(SIGABRT, controlc); + + lx200_set_precision(tel, LX_PRECISION_HIGH); + lx200_update_coords(tel); + return (tel); +} + + +/*-------------------------------------------------------------------------*/ + + +void lx200_close(tel_info *tel) +{ + // stop/wait slewing and focus? + + close_serial(tel->serial); + free(tel); +} + + +/*-------------------------------------------------------------------------*/ + + +static void controlc(int signumber) +{ + if (signumber == SIGINT) + fprintf(stderr, "\nInterrupt signal caught, exiting.\n"); + else + fprintf(stderr, "\nAborting...\n"); + + /* if (tel.focus) + write_serial(tel.serial, ":FQ#", 4);*/ + exit(2); +} + + +/*-------------------------------------------------------------------------*/ + + +int lx200_change_focus(tel_info *tel, int steps, int speed) +{ + if (steps == 0) { + lx200_focus_control(tel, LX_FOCUS_STOP, speed); + return (0); + } + + // start motor in which direction? + if (steps < 0) + lx200_focus_control(tel, LX_FOCUS_MINUS, speed); + + else + lx200_focus_control(tel, LX_FOCUS_PLUS, speed); + + // this kind of focuser really s**** + // sleep then stop motor + usleep(abs(steps) * LX_FOCUS_GAIN); + + lx200_focus_control(tel, LX_FOCUS_STOP, speed); + + return(0); +} + + +/*-------------------------------------------------------------------------*/ + + +int lx200_focus_control(tel_info *tel, int direction, int speed) +{ + char cmdSlow[] = ":FS#"; + char cmdFast[] = ":FF#"; + char cmdMinus[] = ":F-#"; + char cmdPlus[] = ":F+#"; + char cmdStop[] = ":FQ#"; + + + if (speed == LX_FOCUS_FAST) + write_serial(tel->serial, cmdFast, strlen(cmdFast)); + else + write_serial(tel->serial, cmdSlow, strlen(cmdFast)); + + switch (direction) { + case LX_FOCUS_MINUS: + write_serial(tel->serial, cmdMinus, strlen(cmdMinus)); + break; + case LX_FOCUS_PLUS: + write_serial(tel->serial, cmdPlus, strlen(cmdPlus)); + break; + case LX_FOCUS_STOP: + write_serial(tel->serial, cmdStop, strlen(cmdStop)); + break; + } + + return (0); +} + + +/*-------------------------------------------------------------------------*/ + + +int lx200_ra_drive_off(tel_info *tel) +{ + char cmdGuideRate[] = ":RG#"; + char cmdMoveEast[] = ":Me#"; + + write_serial(tel->serial, cmdGuideRate, strlen(cmdGuideRate)); + write_serial(tel->serial, cmdMoveEast, strlen(cmdMoveEast)); + return (0); +} + + +/*-------------------------------------------------------------------------*/ + + +int lx200_ra_drive_on(tel_info *tel) +{ + char cmdStop[] = ":Qe#"; + + write_serial(tel->serial, cmdStop, strlen(cmdStop)); + return (0); +} + + +/*-------------------------------------------------------------------------*/ + + diff --git a/src/drivers/telgo.lx200/lx200.h b/src/drivers/telgo.lx200/lx200.h new file mode 100644 index 00000000..328e379b --- /dev/null +++ b/src/drivers/telgo.lx200/lx200.h @@ -0,0 +1,77 @@ +#ifndef LX200_H +#define LX200_H + +#include "ser.h" +#include "lx200.h" + + +#define LX_BUFSIZE 128 +#define LX_SLEW_TIMEOUT 30 // ~seconds +#define LX_DEFAULT_DEVICE "/dev/ttyS0" + +/* precision */ +#define LX_HIPRECISION_RESP 9 +#define LX_LOPRECISION_RESP 8 +enum { LX_PRECISION_LOW, LX_PRECISION_HIGH }; + +#define LX_FOCUS_GAIN 10000 /* 1/100 seconds */ +enum {LX_FOCUS_MINUS, LX_FOCUS_PLUS, LX_FOCUS_STOP}; // direction +enum {LX_FOCUS_SLOW, LX_FOCUS_FAST}; // speed + + + +struct ras { + int hh; + int mm; + int ss; +}; + + +struct decs { + int he; // celestial hesmisphere. +1 = N, -1 = S. + int dd; + int mm; + int ss; +}; + + +typedef struct { + struct ras pra; //pointed RA + struct ras cra; //current RA + struct ras ra_range; //maximum displacement allowed in RA + + struct decs pdec; //pointed DEC + struct decs cdec; //current DEC + struct decs dec_range; //maximum displacement allowed in DEC + + float lat; + float lon; + int moving; + int focus; + int mnt_type; + ser_info *serial; + char *outbuf; +} tel_info; + + + + +int lx200_set_coords(tel_info *tel, struct ras ra, struct decs dec); +int lx200_slew(tel_info *tel); +int lx200_update_coords(tel_info *tel); +int lx200_wait_slew(tel_info *tel); +int lx200_match_coords(tel_info *tel); + +int lx200_point(tel_info *tel, struct ras ra, struct decs dec); + +tel_info *lx200_open(char *device); +void lx200_close(tel_info *tel); + +int lx200_change_focus(tel_info *tel, int steps, int speed); +int lx200_focus_control(tel_info *tel, int direction, int speed); + +int lx200_ra_drive_on(tel_info *tel); +int lx200_ra_drive_off(tel_info *tel); + + +#endif /* LX200_H */ diff --git a/src/drivers/telgo.lx200/lx200.telescope.xml b/src/drivers/telgo.lx200/lx200.telescope.xml new file mode 100644 index 00000000..14cc9044 --- /dev/null +++ b/src/drivers/telgo.lx200/lx200.telescope.xml @@ -0,0 +1,50 @@ + + + + + MeadeLX200 + Telescope + + + telgo.lx200 -d $device -P $paranoia $ra,$dec + + + + + device + str + /dev/ttyS0 + Device to connect. + + + + paranoia + int + 1 + Iteration on Paranoia's mode. see man telgo.lx200 for more on Paranoia mode. + + + + ra + str + 00:00:00 + RA to slew. Hours mode. + + + + dec + str + 00:00:00 + DEC to slew. Sexagesimal mode. + + + + verbose + bool + True + Verbose mode. + + + + + diff --git a/src/drivers/telgo.lx200/main.c b/src/drivers/telgo.lx200/main.c new file mode 100644 index 00000000..9d17a789 --- /dev/null +++ b/src/drivers/telgo.lx200/main.c @@ -0,0 +1,460 @@ +/*************************************************************************** + tel.c - description + ------------------- + begin : Wed Aug 1 2001 + copyright : (C) 2001 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "lx200.h" + +/*-------------------------------------------------------------------------*/ + +#define PROGRAM_NAME "LX200/Compatible Telescope Controller" + +/*-------------------------------------------------------------------------*/ + +// filled by get_args() +int verbose; +char *exec_name; // Name of executable file +char *conffname; // configuration file +char *device; // serial device to connect + + +// Modes of operation +// TEL_POINT - Point to given coordinates +// TEL_MATCH - Perform coordinates match +// TEL_STOP - Stop RA drive / stop tracking + +#define TEL_POINT 0 +#define TEL_MATCH 1 +#define TEL_STOP 2 +#define TEL_FOCUS 3 +int mode; + +int focsteps; // how much? +int focspeed; // how fast? + +int paranoia; // level of paranoia while pointing. + // actually # of pointing iterations. + +int coords_present; // coordinates were given? +struct ras ra; // RA to point at. +struct decs dec; // DEC to point at. + +/*-------------------------------------------------------------------------*/ + +static void print_title(void); +static void print_help(void); +static void print_version(void); +static void print_usage(void); +static void show_options(void); +static void get_args(int argc, char **argv); + +int match_coords(tel_info *tel); +int point_telescope(tel_info *tel); +int change_focus(tel_info *tel); + + +/*-------------------------------------------------------------------------*/ + + +int main(int argc, char ** argv) +{ + tel_info *tel; + + // get command line arguments + get_args(argc, argv); + + if (verbose) { + print_version(); + show_options(); + } + + + if (verbose) printf("Opening device %s.\n", device); + tel = lx200_open(device); + + // match coords is exclusive + switch (mode) { + case TEL_POINT: + point_telescope(tel); + break; + + case TEL_MATCH: + match_coords(tel); + break; + + case TEL_FOCUS: + change_focus(tel); + break; + + case TEL_STOP: + printf("Stopping RA drive.\n"); + lx200_ra_drive_off(tel); + if (verbose) printf("Done.\n"); + break; + } + + lx200_close(tel); + exit (0); +} + + +/*-------------------------------------------------------------------------*/ + + +int point_telescope(tel_info *tel) +{ + int i; + + printf("Pointing Telescope.\n"); + + if (verbose) + printf("Current position:\n" + " RA %02d:%02d:%02d\n" + " DEC %02d:%02d:%02d\n", + tel->cra.hh, tel->cra.mm, tel->cra.ss, + tel->cdec.dd, tel->cdec.mm, tel->cdec.ss); + + + if (verbose) printf("Setting destination coordinates:\n" + " RA %02d:%02d:%02d\n" + " DEC %02d:%02d:%02d\n", + ra.hh, ra.mm, ra.ss, + dec.dd, dec.mm, dec.ss); + + lx200_set_coords(tel, ra, dec); + + + for(i = 1; i <= paranoia; i++) { + if (verbose) printf("Sending GOTO command, pass #%d\n", i); + lx200_slew(tel); + + if (verbose) printf("Waiting telescope movement.\n"); + lx200_wait_slew(tel); + + if (verbose) + printf("New position:\n" + " RA %02d:%02d:%02d\n" + " DEC %02d:%02d:%02d\n", + tel->cra.hh, tel->cra.mm, tel->cra.ss, + tel->cdec.dd, tel->cdec.mm, tel->cdec.ss); + } + + printf("Pointing completed.\n"); + return (0); +} + + +/*-------------------------------------------------------------------------*/ + + +int change_focus(tel_info *tel) +{ + if (verbose) + printf("Changing focus.\n" + "Amount: %d\n", focsteps); + + lx200_change_focus(tel, focsteps, focspeed); + + if (verbose) printf("Focus changed.\n"); + return (0); +} + + +/*-------------------------------------------------------------------------*/ + + +int match_coords(tel_info *tel) +{ + printf("Performing coordinates match.\n"); + + lx200_update_coords(tel); + lx200_match_coords(tel); + + if (verbose) printf("Coordinates matched:\n" + " RA %02d:%02d:%02d\n" + " DEC %02d:%02d:%02d\n", + tel->cra.hh, tel->cra.mm, tel->cra.ss, + tel->cdec.dd, tel->cdec.mm, tel->cdec.ss); + + printf("Coordinates match completed.\n"); + return (0); +} + + +/*-------------------------------------------------------------------------*/ + + +static void print_title(void) +{ + printf("%s - version: %s\n", PROGRAM_NAME, VERSION); +} + + +/*-------------------------------------------------------------------------*/ + + +void show_options(void) +{ + // list arguments + // default values + printf("\n*** Options:\n\n"); + printf("Device : %s\n", device); + printf("Paranoia : %d\n\n", paranoia); + + switch (mode) { + + case TEL_POINT: + printf("Mode: POINT\n"); + printf("\n*** Coordinates:\n\n"); + printf("RA : %02d:%02d:%02d\n", + ra.hh, ra.mm, ra.ss); + printf("DEC : %02d:%02d:%02d\n\n", + dec.dd, dec.mm, dec.ss); + break; + + case TEL_MATCH: + printf("Mode: MATCH\n"); + break; + + + case TEL_STOP: + printf("Mode: STOP\n"); + break; + + case TEL_FOCUS: + printf("Mode: FOCUS\n"); + printf("Speed : %d\n", focspeed); + printf("Steps : %d\n\n", focsteps); + break; + } +} + + +/*-------------------------------------------------------------------------*/ + + +static void print_help(void) +{ + print_title(); + print_usage(); + printf("Command line options:\n\n" + " -h / --help\n" + " This help screen.\n\n" + + " -v / --version\n" + " Display version and copyright information.\n\n" + + " -V / --verbose\n" + " Display more progress stuff.\n\n" + + " -C / --config-file CONFFILE\n" + " Read options from CONFFILE.\n\n" + + " -d / --device DEVICE\n" + " Use given serial port.\n" + " Default: /dev/ttyS0\n\n" + + " -m / --match-coords\n" + " Match the position of the telescope in the\n" + " sky to the last pointed coordinates.\n\n" + + " -S / --stop-ra-drive\n" + " Stops RA drive, no tracking will occur.\n\n" + + " -f / --focus N\n" + " Move focus N \"steps\", where N is a\n" + " positive or negative integer.\n\n" + + " -s / --focus-speed N\n" + " Speed of focus motor.\n" + " 0: Slow.\n" + " 1: Fast. (default)\n\n" + + " -P / --paranoia N\n" + " Paranoid pointing checking.\n\n" + " Perform N pointing iterations.\n" + " Default: 1 iteration.\n\n" + ); + + printf("\nFor more information visit our home page: %s\n", PROGRAM_URL); + exit(0); +} + +/*-------------------------------------------------------------------------*/ + +static void print_version(void) +{ + print_title(); + printf("Copyright (C) 2004 Andre Luiz de Amorim\n"); + printf("%s comes with NO WARRANTY,\n" + "to the extent permitted by law.\n", PROGRAM_NAME); + printf("You may redistribute copies of %s\n" + "under the terms of the GNU General Public License.\n" + "For more information about these matters,\n" + "see the files named COPYING.\n", PROGRAM_NAME); + + //exit(0); +} + +/*-------------------------------------------------------------------------*/ + +static void print_usage(void) +{ + printf("SYNTAX:\n" + " %s [options] \n\n" + " %s {-h|--help}\n\n" + " in the form HH:MM:SS.S\n" + " in the form +DD:MM:SS.S\n\n", exec_name, exec_name); +} + +/*-------------------------------------------------------------------------*/ + +static void get_args(int argc, char **argv) +{ + int next_opt; + const char *short_options = "hvVC:d:mSf:s:P:"; + const struct option long_options [] = { + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'v' }, + { "verbose", 0, NULL, 'V' }, + { "config-file", 1, NULL, 'C' }, + { "device", 1, NULL, 'd' }, + { "match-coords", 0, NULL, 'm' }, + { "stop-ra-drive", 0, NULL, 'S' }, + { "focus", 1, NULL, 'f' }, + { "focus-speed", 1, NULL, 's' }, + { "paranoia", 1, NULL, 'P' }, + { NULL, 0, NULL, 0 } + }; + + // default values + exec_name = argv[0]; + verbose = 0; + conffname = NULL; + device = LX_DEFAULT_DEVICE; + mode = TEL_POINT; + focsteps = 0; + focspeed = 1; + paranoia = 1; + + + + do { + next_opt = getopt_long(argc, argv, short_options, + long_options, NULL); + switch (next_opt) { + case 'h': + print_help(); + break; + + case 'v': + print_version(); + exit(0); + break; + + case 'V': + verbose = 1; + break; + + case 'C': + /* config file */ + conffname = optarg; + break; + + case 'd': + /* device */ + device = optarg; + break; + + case 'm': + /* device */ + mode = TEL_MATCH; + return; // do nothing else when matching + break; + + case 'S': + /* stop RA drive */ + mode = TEL_STOP; + return; + break; + + + case 'f': + /* focus control */ + sscanf(optarg, "%d", &focsteps); + mode = TEL_FOCUS; + break; + + case 's': + /* focus speed */ + sscanf(optarg, "%d", &focspeed); + if (focspeed == 0) focspeed = LX_FOCUS_SLOW; + else focspeed = LX_FOCUS_FAST; + break; + + case 'P': + /* level of paranoia */ + sscanf(optarg, "%d", ¶noia); + break; + + + + case '?': + /* Invalid option */ + print_usage(); + exit(1); + break; + + case -1: + /* end of options */ + break; + + default: + printf("Tell author he should read the getopt_long man page.\n"); + exit(1); + break; + } + } while (next_opt != -1); + + if (mode == TEL_FOCUS) { + return; + } + + + // extra argument mandatory if in point mode + if(optind != argc-1 && mode == TEL_POINT) { + print_usage(); + exit(1); + } + + // read RA and DEC + coords_present = 1; + sscanf(argv[optind], "%d:%d:%d,%d:%d:%d", + &ra.hh, &ra.mm, &ra.ss, &dec.dd, &dec.mm, &dec.ss); + +} + +/*-------------------------------------------------------------------------*/ + diff --git a/src/drivers/telgo.lx200/ser.c b/src/drivers/telgo.lx200/ser.c new file mode 100644 index 00000000..174cea80 --- /dev/null +++ b/src/drivers/telgo.lx200/ser.c @@ -0,0 +1,269 @@ +/*************************************************************************** + ser.c - description + ------------------- + begin : Wed Jun 7 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +/* This file contains the functions for serial communication */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "errors.h" +#include "ser.h" + +static void rdtimeout(int signumber); +static int set_minchars(ser_info *serial, int nbytes); +static ser_info *sertmp; + +#ifdef COMMTEST + +/* This example program writes a string in port `tty', and waits until + timeout for a response. */ + + +char teste[] = "Write test..."; +unsigned char buf[25]; +char tty[] = "/dev/ttyS1"; + + +int main(void) +{ + int result; + ser_info porta; + + open_serial(&porta, tty); + if (porta.err_code != ERR_OK) return(-1); + puts("Communication test.\n\n"); + + /* 9600,8e1 */ + porta.baudrate = B9600; + porta.parity = PAR_EVEN; + porta.stopbits = 1; + porta.timeout = 10; + porta.minchars = 25; + porta.waitread = FALSE; + set_serial(&porta); + write_serial(&porta, teste, strlen(teste)); + + if (read_serial(&porta, buf, 24)) + printf("Text received: %s\n\n", buf); + else printf("Read timeout.\n\n"); + + close_serial(&porta); + return(0); +} +#endif /* COMMTEST */ + + +int is_valid_serial(ser_info *serial) +{ + if ((serial == NULL) || + (serial->tty_name == NULL) || + (serial->tio == NULL) || + (serial->oldtio == NULL)) + return(FALSE); + return(TRUE); +} + + +ser_info *open_serial(char *ttyname) +{ + ser_info *serial; + +/* Allocate memory for the structures */ + serial = (ser_info *) calloc(sizeof(ser_info), 1); + if (serial == NULL) return (NULL); + + serial->tio = (struct termios *) calloc(sizeof(struct termios), 1); + serial->oldtio = (struct termios *) calloc(sizeof(struct termios), 1); + serial->tty_name = (char *) calloc(strlen(ttyname), sizeof(char)); + + if (serial->tio == NULL || + serial->oldtio == NULL || + serial->tty_name == NULL) { + free(serial->tio); + free(serial->oldtio); + free(serial->tty_name); + free(serial); + return(NULL); + } + + serial->err_code = ERR_OK; + signal(SIGALRM, rdtimeout); + strcpy(serial->tty_name, ttyname); + + serial->ttyfd = open(serial->tty_name, O_RDWR | O_NOCTTY); + if (serial->ttyfd < 0) { + serial->err_code = ERR_OPENTTY; + close_serial(serial); + return (NULL); + } + +/* Save current config */ + tcgetattr(serial->ttyfd, serial->oldtio); + serial->isopen = TRUE; + return (serial); +} + + +int close_serial(ser_info *serial) +{ + if (!is_valid_serial(serial)) return(ERR_MEM); + signal(SIGALRM, SIG_DFL); +/* Restore terminal to its previous state; free memory */ + if (serial->isopen) { + tcsetattr(serial->ttyfd,TCSANOW,serial->tio); + close(serial->ttyfd); + } + free(serial->tio); + free(serial->oldtio); + free(serial->tty_name); + free(serial); + return(ERR_OK); +} + + +int set_serial(ser_info *serial) +{ + if (!is_valid_serial(serial)) return(ERR_MEM); + if (!serial->isopen) { + serial->err_code = ERR_SERIAL; + return(ERR_SERIAL); + } + memset(serial->tio, 0, sizeof(struct termios)); + + cfsetospeed(serial->tio, serial->baudrate); + cfsetispeed(serial->tio, serial->baudrate); + +/* Set byte size to 8bit, enable reading and set local mode */ + serial->tio->c_cflag |= CS8 | CLOCAL | CREAD; + switch (serial->parity) { + case PAR_NONE: + serial->tio->c_iflag |= IGNPAR; + serial->tio->c_cflag &= ~PARENB; + break; + + case PAR_EVEN: + serial->tio->c_iflag &= ~IGNPAR; + serial->tio->c_cflag |= PARENB; + serial->tio->c_cflag &= ~PARODD; + break; + + case PAR_ODD: + serial->tio->c_iflag &= ~IGNPAR; + serial->tio->c_cflag |= PARENB; + serial->tio->c_cflag &= PARODD; + break; + default: + serial->err_code = ERR_PARITY; + return(ERR_PARITY); + } + + if (serial->stopbits == 1) + serial->tio->c_cflag &= ~CSTOPB; + else if (serial->stopbits ==2) + serial->tio->c_cflag |= CSTOPB; + else { + serial->err_code = ERR_STOPB; + return(ERR_STOPB); + } + +/* Raw input */ + serial-> tio->c_lflag = 0; + + serial->tio->c_cc[VTIME] = serial->timeout; + if (serial->waitread) + serial->tio->c_cc[VMIN] = serial->minchars; + + tcsetattr(serial->ttyfd,TCSANOW,serial->tio); + return(ERR_OK); +} + + +int write_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes) +{ + int result; + + if (!is_valid_serial(serial)) return(ERR_MEM); + if (!serial->isopen) { + serial->err_code = ERR_SERIAL; + return(0); + } + result = write(serial->ttyfd, buf, nbytes); + if (result < nbytes) { + serial->err_code = ERR_WRITE; + return(result); + } + serial->err_code = ERR_OK; + return(result); +} + + +int read_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes) +{ + int result; + + if (!is_valid_serial(serial)) return(ERR_MEM); + if (!serial->isopen) { + serial->err_code = ERR_SERIAL; + return(0); + } + serial->err_code = ERR_OK; + +/* Set the minimum number of chars to read before returning */ + if (serial->waitread && (serial->minchars != nbytes)) { + if (set_minchars(serial, nbytes) != ERR_OK) { + serial->err_code = ERR_SERIAL; + return(0); + } + } +/* save serial stuff for alarm() timeout. */ + sertmp = serial; + + alarm(serial->timeout); + result = read(serial->ttyfd, buf, nbytes); + alarm(0); + serial->bytesread = result; + if (result < nbytes) + serial->err_code = ERR_READ; + return(result); +} + + +static int set_minchars(ser_info *serial, int nbytes) +{ + if (!is_valid_serial(serial)) + return(ERR_MEM); + if (!serial->waitread) + return(ERR_READ); + serial->minchars = nbytes; + serial->tio->c_cc[VMIN] = nbytes; + tcsetattr(serial->ttyfd,TCSANOW,serial->tio); + return(ERR_OK); +} + +static void rdtimeout(int signumber) +{ + fprintf(stderr, "Serial communication failed, closing %s...\n", sertmp->tty_name); + if (sertmp != NULL) close_serial(sertmp); + /* exit(-1); */ +} + diff --git a/src/drivers/telgo.lx200/ser.h b/src/drivers/telgo.lx200/ser.h new file mode 100644 index 00000000..2429ce53 --- /dev/null +++ b/src/drivers/telgo.lx200/ser.h @@ -0,0 +1,56 @@ +/*************************************************************************** + ser.h - description + ------------------- + begin : Wed Jun 7 2000 + copyright : (C) 2000 by Andre Luiz de Amorim + email : andre@astro.ufsc.br + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef SER_H +#define SER_H + +#include + +#define RD_TIMEOUT (2) +#define TRUE (1) +#define FALSE (0) + +#define MAX_BUF_LEN (255) +#define PAR_NONE 'n' +#define PAR_EVEN 'e' +#define PAR_ODD 'o' + +typedef struct { + int isopen; + int waitread; + int err_code; + char *tty_name; + int ttyfd; + cc_t minchars; /* not used if waitread == FALSE */ + int bytesread; + int baudrate; + char parity; + int stopbits; + cc_t timeout; /* interchar time (1/10 secs), and read timeout (secs) */ + struct termios *tio; /* |- don't mess with these structs, */ + struct termios *oldtio; /* | the routines will handle it. */ +} ser_info; + +int is_valid_serial(ser_info *serial); +ser_info *open_serial(char *ttyname); +int close_serial(ser_info *serial); +int set_serial(ser_info *serial); +int read_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes); +int write_serial(ser_info *serial, unsigned char *buf, unsigned int nbytes); + + +#endif /* SER_H */ diff --git a/src/drivers/telgo.paramount/Makefile.am b/src/drivers/telgo.paramount/Makefile.am new file mode 100644 index 00000000..6b0a9b7c --- /dev/null +++ b/src/drivers/telgo.paramount/Makefile.am @@ -0,0 +1,8 @@ +include $(top_srcdir)/rules.make + +bin_SCRIPTS = telgo.paramount.py telgo.server.py + +EXTRA_DIST = telgo.paramount.py telgo.server.py + +uts_driver_DATA = paramount.telescope.xml + diff --git a/src/drivers/telgo.paramount/paramount.telescope.xml b/src/drivers/telgo.paramount/paramount.telescope.xml new file mode 100644 index 00000000..25abb691 --- /dev/null +++ b/src/drivers/telgo.paramount/paramount.telescope.xml @@ -0,0 +1,50 @@ + + + + + Paramount + Telescope + + +telgo.paramount.py $server:$port $ra $dec + + + + + server + str + localhost + Server IP address. + + + + port + int + 10000 + Server port. + + + + ra + str + 00:00:00 + RA to slew. Hours mode. + + + + dec + str + 00:00:00 + DEC to slew. Sexagesimal mode. + + + + verbose + bool + True + Verbose mode. + + + + + diff --git a/src/drivers/telgo.paramount/telgo.paramount.py b/src/drivers/telgo.paramount/telgo.paramount.py new file mode 100644 index 00000000..dd43a72f --- /dev/null +++ b/src/drivers/telgo.paramount/telgo.paramount.py @@ -0,0 +1,97 @@ +#! /usr/bin/python + +import sys +import string + +from socket import * +import select + +__description__ = "Paramount ME telgo wrapper" +__version__ = "0.1" +__date__ = "01/03/2004" + +# parse arguments +if len(sys.argv) < 4: + print """ + %s - versao %s - %s + + Usage: %s server[:port] ra dec + Ex. : %s localhost:10000 +12:12:12 21:21:21 + %s server +12:12:12 21:21:21 + %s - +12:12:12 21:21:21 + + """ % (__description__, __version__, __date__, + sys.argv[0], sys.argv[0], sys.argv[0], sys.argv[0]) + + sys.exit(-1) + + +# get target host and port from command line +if ":" in sys.argv[1]: + target = string.split(sys.argv[1], ":") + target[1] = int(target[1]) +elif sys.argv[1] != "-": + target = [sys.argv[1], 10000] +else: + target = [gethostname(), 10000] + + +# helpful names +targetRA = sys.argv[2] +targetDEC = sys.argv[3] + +# connect, send coordinates, and wait until telescope stop + +try: + + skt = socket(AF_INET, SOCK_STREAM) + + print "Connecting to %s:%d... wait... " % tuple(target) + sys.stdout.flush() + skt.connect(tuple(target)) + print "OK" + + skt.sendall("%s*%s*MOVE" % (targetRA, targetDEC)) + + print "Moving to %s %s... wait... " % (targetRA, targetDEC) + sys.stdout.flush() + + # wait until telescope move + wait = select.select([skt], [], [skt], 60) + + if len(wait[0]) or len(wait[2]): + data = skt.recv(256) + + if data == "OK": + print "OK" + else: + print "ERROR (%s)" % data + else: + print "ERROR (Timeout)" + + skt.sendall("BYE") + + skt.close() + sys.exit(0) + +except KeyboardInterrupt: + print "Ctrl+C... exiting..." + skt.sendall("BYE") + skt.close() + sys.exit(1) + +except error, e: + print "ERROR (%s)" % (e[1]) + #skt.sendall("BYE") + skt.close() + sys.exit(1) + + + + + + + + + + diff --git a/src/drivers/telgo.paramount/telgo.server.py b/src/drivers/telgo.paramount/telgo.server.py new file mode 100644 index 00000000..4ae14dbc --- /dev/null +++ b/src/drivers/telgo.paramount/telgo.server.py @@ -0,0 +1,130 @@ +import sys +import win32com.client + +import servicemanager +from pywintypes import com_error + +from socket import * + +def _log(str): +# servicemanager.LogMsg( +# servicemanager.EVENTLOG_INFORMATION_TYPE, +# servicemanager.PYS_SERVICE_STOPPED, +# ("UTS", "%s" % str) +# ) + print str + sys.stdout.flush() + +class UTSException(Exception): + def __init__(self, value): + self.value = value + + def __str__(self): + return repr(self.value) + +class UTSPointing: + + def __init__(self, port = 10000): + self.sk = None + self.port = port + + self.tel = None + self.util = None + + def start(self, findHome = True): + try: + self.tel = win32com.client.Dispatch("TheSky.Telescope") + self.thesky = win32com.client.Dispatch("TheSky.RASCOMTheSky") + self.util = win32com.client.Dispatch("DriverHelper.Util") + + self.sk = socket(AF_INET, SOCK_STREAM) + self.sk.bind((gethostname(), self.port)) + self.sk.listen(5) + + self.tel.Connected = True + self.thesky.Connect() + + if(findHome): + self.tel.FindHome() + + except error, e: + raise UTSException("Erro ao iniciar o servidor (%s)." % (e[1])) + + except AttributeError: + raise UTSException("Nao foi possivel conectar-se ao telescopio.\n" \ + "Verifique se o telescopio esta ligado e devidamente conectado ao computador.") + except com_error, e: + raise UTSException("Nao foi possivel criar os objetos COM. Contate o administrador." \ + "(%s)" % str(e)) + + def run(self): + + fim = False + + try: + + # connections loop + while not fim: + conn, addr = self.sk.accept() + _log("entrando %s:%s." % addr) + + # data loop + while 1: + data = conn.recv(1024) + + if not data: continue + + if data == "BYE": + conn.close() + break + if data in ("SHUTDOWN", "PARK"): + conn.close() + fim = True + break + else: + target = data.split('*') + targetRA = target[0] + targetDEC = target[1] + + _log("movendo para %s %s." % (str(targetRA), str(targetDEC))) + + self.tel.SlewToCoordinates(self.util.HMSToHours(targetRA), self.util.DMSToDegrees(targetDEC)) + + conn.send("OK") + # end of data loop + + conn.close() + _log("saindo") + + #end of connections loop + + except error, e: + raise UTSException("%s: %s", e[0],e[1]) + except com_error, e: + raise UTSException(str(e)) + + def stop(self, park = True): + if(park): + _log("parking...") + self.tel.Park() + + _log("closing...") + self.sk.close() + + self.tel.Connected = False + self.thesky.Quit() + +if __name__=='__main__': + + try: + uts = UTSPointing() + uts.start() + uts.run() + uts.stop() + + sys.exit(-1) + + except (UTSException, KeyboardInterrupt), e: + uts.stop() + print e + diff --git a/src/drivers/telgo.paramount/telgo.service.py b/src/drivers/telgo.paramount/telgo.service.py new file mode 100644 index 00000000..9ed828e6 --- /dev/null +++ b/src/drivers/telgo.paramount/telgo.service.py @@ -0,0 +1,33 @@ +import win32serviceutil, win32service +import pywintypes, win32con, winerror +import servicemanager + + +class UTSPointingService(win32serviceutil.ServiceFramework): + + _svc_name_ = "UTSPointingService" + _svc_display_name_ = "UTS Pointing Service" + + def __init__(self, args): + win32serviceutil.ServiceFramework.__init__(self, args) + + def SvcStop(self): + self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) + SetEvent(self.hWaitStop) + + def SvcDoRun(self): + servicemanager.LogMsg( + servicemanager.EVENTLOG_INFORMATION_TYPE, + servicemanager.PYS_SERVICE_STARTED, + (self._svc_name_, 'normal starting.') + ) + + servicemanager.LogMsg( + servicemanager.EVENTLOG_INFORMATION_TYPE, + servicemanager.PYS_SERVICE_STOPPED, + (self._svc_name_, "normal stop.") + ) + + +if __name__=='__main__': + win32serviceutil.HandleCommandLine(UTSPointingService) diff --git a/src/drivers/weather.wx200/Makefile.am b/src/drivers/weather.wx200/Makefile.am new file mode 100644 index 00000000..40ea1110 --- /dev/null +++ b/src/drivers/weather.wx200/Makefile.am @@ -0,0 +1,4 @@ +include $(top_srcdir)/rules.make + +uts_driver_DATA = wx200.weather.xml + diff --git a/src/drivers/weather.wx200/wx200.weather.xml b/src/drivers/weather.wx200/wx200.weather.xml new file mode 100644 index 00000000..a2ec2240 --- /dev/null +++ b/src/drivers/weather.wx200/wx200.weather.xml @@ -0,0 +1,29 @@ + + + + + WX200 + Weather + + +wx200 --nounits --celsius --temp $device + + + + + device + str + /dev/ttyS0 + Device to connect. + + + + verbose + bool + True + Verbose mode. + + + + + diff --git a/src/python/Makefile.am b/src/python/Makefile.am new file mode 100644 index 00000000..3cb950b3 --- /dev/null +++ b/src/python/Makefile.am @@ -0,0 +1,5 @@ +include $(top_srcdir)/rules.make + +SUBDIRS = uts + +EXTRA_DIST = *.py diff --git a/src/python/bin/uts b/src/python/bin/uts new file mode 100644 index 00000000..1b9846eb --- /dev/null +++ b/src/python/bin/uts @@ -0,0 +1,26 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- + +import os +import sys + +here = os.path.dirname(os.path.realpath(__file__)) +swhome = os.path.dirname(here) + +for parts in [("src",), ("lib", "python"), ("Lib", "site-packages")]: + d = os.path.join(swhome, *(parts + ("uts", "core", "site"))) + if os.path.isdir(d): + d = os.path.join(swhome, *parts) + sys.path.insert(0, d) + break +else: + try: + import uts.core.site + except ImportError: + print >>sys.stderr, "Could not locate UTS software installation!" + sys.exit(1) + + +from uts.core.site import main + +sys.exit(main(sys.argv)) diff --git a/src/python/doc/doc.sh b/src/python/doc/doc.sh new file mode 100644 index 00000000..3d3600fd --- /dev/null +++ b/src/python/doc/doc.sh @@ -0,0 +1 @@ +epydoc --html -o doc/api --graph=umlclasstree --show-sourcecode --name "UTS - Unified Telescope System" uts diff --git a/src/python/doc/exemplo/exemplo.py b/src/python/doc/exemplo/exemplo.py new file mode 100644 index 00000000..c1672b36 --- /dev/null +++ b/src/python/doc/exemplo/exemplo.py @@ -0,0 +1,68 @@ +#! /usr/bin/python +# -*- coding: iso-8859-1 -*- + +_me = "EXEMPLO" + +_rpc = ["SLEW", + "STOP", + "INFO"] + +_notify = ["SLEW_COMPLETE"] + +import time + +from uts.core.instrument import Instrument + +class Exemplo(Instrument): + + def __init__(self): + Instrument.__init__(self, _me) + + # registra os callbacks responsáveis por cada um dos pontos + # de controle definidos em _rpc (na mesma ordem) + self.registerRPC([self.slew, + self.stop, + self.info]) + + # adiciona quantas secretárias externas forem necessárias +# self.addSec("WEATHER") +# self.addSec("CAFE") + +# self.notify("WEATHER", "HUMID", self.humidChanged) + +# def run(self): +# pass + + + def slew(self, event, data): + """ + slew callback: recebe um objeto Notify com informações sobre + quem enviou a ordem e uma lista de argumentos. + """ + + # slew ... + + # notifica os interessados que o telescópio chegou ao alvo. + self.status("SLEW_COMPLETE", "Chegou!") + print event + print data + + + def stop(self, event, data): + print event + print data + + def info(self, event, data): + print event + print data + time.sleep(1000) + + def inst_main(self): + print "Running main control function.. waiting.." + time.sleep(1) + +if __name__ == '__main__': + + e = Exemplo() + e.main() + diff --git a/src/python/doc/exemplo/falso_chefao.py b/src/python/doc/exemplo/falso_chefao.py new file mode 100644 index 00000000..e7cf5042 --- /dev/null +++ b/src/python/doc/exemplo/falso_chefao.py @@ -0,0 +1,43 @@ +#! /usr/bin/python +# -*- coding: iso-8859-1 -*- + +_me = "FALSO_CHEFAO" + + +from uts.core.instrument import Instrument + +class FalsoChefao(Instrument): + + def __init__(self): + Instrument.__init__(self, _me) + + # adiciona quantas secretárias externas forem necessárias + self.addSec("WEATHER") + self.notify("WEATHER", "LOW_TEMP", self.lowTemp) + self.notify("WEATHER", "HIGH_TEMP", self.highTemp) + + def lowTemp(self, event, data): + """ + lowTemp callback: + """ + # notifica os interessados que o telescópio chegou ao alvo. + # self.status("SLEW_COMPLETE", "Chegou!") + print event + print data + + + def highTemp(self, event, data): + """ + highTemp callback: + """ + # notifica os interessados que o telescópio chegou ao alvo. + # self.status("SLEW_COMPLETE", "Chegou!") + print event + print data + + +if __name__ == '__main__': + + e = FalsoChefao() + e.main() + diff --git a/src/python/setup.py b/src/python/setup.py new file mode 100644 index 00000000..19f4ced6 --- /dev/null +++ b/src/python/setup.py @@ -0,0 +1,13 @@ +#! /usr/bin/python + +from distutils.core import setup + +setup(name='uts', + version='0.1', + description='UTS python wrappers', + author='P. Henrique Silva', + author_email='heneique@astro.ufsc.br', + packages=['uts', 'uts.core', 'uts.controllers', 'uts.interfaces', 'uts.instruments', 'uts.util', 'uts.util.etree'], + package_data={'uts.core': ['log.config']}, + scripts=['bin/uts'] + ) diff --git a/src/python/test/uts.pylint b/src/python/test/uts.pylint new file mode 100644 index 00000000..bf5129a1 --- /dev/null +++ b/src/python/test/uts.pylint @@ -0,0 +1,353 @@ +# lint Python modules using external checkers. +# +# This is the main checker controling the other ones and the reports +# generation. It is itself both a raw checker and an astng checker in order +# to: +# * handle message activation / deactivation at the module level +# * handle some basic but necessary stats'data (number of classes, methods...) +# +# This checker also defines the following reports: +# * R0001: Total errors / warnings +# * R0002: % errors / warnings by module +# * R0003: Messages +# * R0004: Global evaluation +[MASTER] + +# Profiled execution. +profile=no + +# Add to the black list. It should be a base name, not a +# path. You may set this option multiple times. +ignore=CVS +ignore=etree + +# Pickle collected data for later comparisons. +persistent=yes + +# Set the cache size for astng objects. +cache-size=500 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + + +[REPORTS] + +# Tells wether to display a full report or only the messages +reports=yes + +# Use HTML as output format instead of text +html=yes + +# Use a parseable text output format, so your favorite text editor will be able +# to jump to the line corresponding to a message. +parseable=no + +# Colorizes text output using ansi escape codes +color=no + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Python expression which should return a note less than 10 (10 is the highest +# note).You have access to the variables errors warning, statement which +# respectivly contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (R0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Add a comment according to your evaluation note. This is used by the global +# evaluation report (R0004). +comment=no + +# Include message's id in output +include-ids=no + + +# checks for +# * unused variables / imports +# * undefined variables +# * redefinition of variable from builtins or from an outer scope +# * use of variable before assigment +# +[VARIABLES] + +# Enable / disable this checker +enable-variables=yes + +# Tells wether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching names used for dummy variables (i.e. not used). +dummy-variables-rgx=__|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + + +# try to find bugs in the code using type inference +# +[TYPECHECK] + +# Enable / disable this checker +enable-typecheck=yes + +# Tells wether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# When zope mode is activated, consider the acquired-members option to ignore +# access to some undefined attributes. +zope=no + +# List of members which are usually get through zope's acquisition mecanism and +# so shouldn't trigger E0201 when accessed (need zope=yes to be considered. +acquired-members=REQUEST,acl_users,aq_parent + + +# checks for : +# * doc strings +# * modules / classes / functions / methods / arguments / variables name +# * number of arguments, local variables, branchs, returns and statements in +# functions, methods +# * required module attributes +# * dangerous default values as arguments +# * redefinition of function / method / class +# * uses of the global statement +# +# This checker also defines the following reports: +# * R0101: Statistics by type +[BASIC] + +# Enable / disable this checker +enable-basic=yes + +# Required attributes for module, separated by a comma +required-attributes= + +# Regular expression which should only match functions or classes name which do +# not require a docstring +no-docstring-rgx=__.*__ + +# Regular expression which should only match correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression which should only match correct module level names +const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$ + +# Regular expression which should only match correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression which should only match correct function names +function-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ + +# Regular expression which should only match correct method names +method-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ + +# Regular expression which should only match correct instance attribute names +attr-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ + +# Regular expression which should only match correct argument names +argument-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ + +# Regular expression which should only match correct variable names +variable-rgx=[a-z_][a-zA-Z0-9_]{2,30}$ + +# Regular expression which should only match correct list comprehension / +# generator expression variable names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k + +# Bad variable names which should always be refused, separated by a comma +bad-names=lixo + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter,apply,input + + +# checks for sign of poor/misdesign: +# * number of methods, attributes, local variables... +# * size, complexity of functions, methods +# +[DESIGN] + +# Enable / disable this checker +enable-design=yes + +# Maximum number of arguments for function / method +max-args=5 + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branchs=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=1 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + + +# checks for usage of new style capabilities on old style classes and +# other new/old styles conflicts problems +# * use of property, __slots__, super +# * "super" usage +# * raising a new style class as exception +# +[NEWSTYLE] + +# Enable / disable this checker +enable-newstyle=yes + + +# checks for +# * external modules dependencies +# * relative / wildcard imports +# * cyclic imports +# * uses of deprecated modules +# +# This checker also defines the following reports: +# * R0401: External dependencies +# * R0402: Modules dependencies graph +[IMPORTS] + +# Enable / disable this checker +enable-imports=yes + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,string,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report R0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report R0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report R0402 must +# not be disabled) +int-import-graph= + + +# checks for : +# * methods without self as first argument +# * overriden methods signature +# * access only to existant members via self +# * attributes not defined in the __init__ method +# * supported interfaces implementation +# * unreachable code +# +[CLASSES] + +# Enable / disable this checker +enable-classes=yes + +# List of interface methods to ignore, separated by a comma. This is used for +# instance to not check methods defines in Zope's Interface base class. +ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + + +# checks for +# * excepts without exception filter +# * string exceptions +# +[EXCEPTIONS] + +# Enable / disable this checker +enable-exceptions=yes + + +# does not check anything but gives some raw metrics : +# * total number of lines +# * total number of code lines +# * total number of docstring lines +# * total number of comments lines +# * total number of empty lines +# +# This checker also defines the following reports: +# * R0701: Raw metrics +[METRICS] + +# Enable / disable this checker +enable-metrics=yes + + +# checks for similarities and duplicated code. This computation may be +# memory / CPU intensive, so you should disable it if you experiments some +# problems. +# +# This checker also defines the following reports: +# * R0801: Duplication +[SIMILARITIES] + +# Enable / disable this checker +enable-similarities=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + + +# checks for: +# * warning notes in the code like FIXME, XXX +# * PEP 263: source code with non ascii character but no encoding declaration +# +[MISCELLANEOUS] + +# Enable / disable this checker +enable-miscellaneous=yes + +# List of note tags to take in consideration, separated by a comma. Default to +# FIXME, XXX, TODO +notes=FIXME,FIXME:,XXX,TODO,TODO: + + +# checks for : +# * unauthorized constructions +# * strict indentation +# * line length +# * use of <> instead of != +# +[FORMAT] + +# Enable / disable this checker +enable-format=yes + +# Maximum number of characters on a single line. +max-line-length=79 + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' diff --git a/src/python/uts/Makefile.am b/src/python/uts/Makefile.am new file mode 100644 index 00000000..44a3fac9 --- /dev/null +++ b/src/python/uts/Makefile.am @@ -0,0 +1,5 @@ +include $(top_srcdir)/rules.make + +SUBDIRS = core instruments + +EXTRA_DIST = *.py diff --git a/src/python/uts/__init__.py b/src/python/uts/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/python/uts/__init__.py @@ -0,0 +1 @@ + diff --git a/src/python/uts/controllers/__init__.py b/src/python/uts/controllers/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/python/uts/core/Makefile.am b/src/python/uts/core/Makefile.am new file mode 100644 index 00000000..98fd35b4 --- /dev/null +++ b/src/python/uts/core/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = *.py diff --git a/src/python/uts/core/__init__.py b/src/python/uts/core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/python/uts/core/async.py b/src/python/uts/core/async.py new file mode 100644 index 00000000..4b8a1bd2 --- /dev/null +++ b/src/python/uts/core/async.py @@ -0,0 +1,61 @@ +import logging +import time + +from uts.core.threads import getThreadPool + +class AsyncResult(object): + + def __init__(self, func, pool = None): + + self.func = func + self.result = None + self.userCallback = False + + self.waiting = False + self.sleepTime = 0.1 + + self.pool = pool or getThreadPool(10) + + def __call__(self, *args, **kwargs): + logging.debug("calling synchronously %s with %s %s" % (self.func, + args, kwargs)) + + if(hasattr(self.func, "lock")): + self.func.lock.acquire() + + result = self.func(*args, **kwargs) + + if(hasattr(self.func, "lock")): + self.func.lock.release() + + return result + + def begin(self, *args, **kwargs): + logging.debug("calling asynchronously %s with %s %s" % (self.func, + args, kwargs)) + + if(not self.pool): + logging.error("Cannot run async calls, pool is not seted") + return False + + self.userCallback = kwargs.pop('callback', None) + self.waiting = True + + self.pool.queueTask(self.func, args, kwargs, self.resultCallback) + + return self + + def end(self): + while (self.waiting): + #wait + time.sleep(self.sleepTime) + + return self.result + + def resultCallback(self, result): + self.result = result + self.waiting = False + + if(self.userCallback): + self.userCallback(self.result) + diff --git a/src/python/uts/core/config.py b/src/python/uts/core/config.py new file mode 100644 index 00000000..d37d1157 --- /dev/null +++ b/src/python/uts/core/config.py @@ -0,0 +1,123 @@ +#! /usr/bin/python +# -*- coding: iso8859-1 -*- + +import sys +import logging + +import uts.util.etree.ElementTree as ET + +class SiteConfiguration(object): + + def __init__(self): + + self.__sites = [] + self.__instruments = [] + self.__controllers = [] + + def getInstruments(self): + + return self.__instruments + + def getControllers(self): + + return self.__controllers + + def getSites(self): + + return self.__sites + + def read(self, config): + + self._read(config) + + def _read(self, config): + + try: + + root = ET.parse(config) + + except IOError, e: + + logging.exception("Error opening %s" % config) + return False + + # ok, let's go! + + # sites (ok, just one site makes sense by now, but we can expand this later) + sites = root.findall("site") + + for site in sites: + + tmpSite = {} + tmpSite["name"] = site.get("name", "UFSC") + tmpSite["latitude"] = site.get("latitude", "0") + tmpSite["longitude"] = site.get("longitude", "0") + tmpSite["altitude"] = site.get("altitude", "0") + + self.__sites.append(tmpSite) + + # get all instruments + insts = root.findall("instruments/instrument") + + for inst in insts: + tmpInst = {} + tmpInst["name"] = inst.get("name", "inst" + str(len(self.__instruments) + 1)) + tmpInst["class"] = inst.get("class", object) + tmpInst["options"] = [] + + # get all options + opts = inst.findall("option") + + for opt in opts: + tmpOpt = {} + tmpOpt["name"] = opt.get("name") + tmpOpt["value"] = opt.get("value") + + tmpInst["options"].append(tmpOpt) + + self.__instruments.append(tmpInst) + + + ctrls = root.findall("controllers/controller") + + for ctrl in ctrls: + + tmpCtrl = {} + tmpCtrl["name"] = ctrl.get("name", "ctrl" + str(len(self.__controllers) + 1)) + tmpCtrl["class"] = ctrl.get("class", object) + tmpCtrl["options"] = [] + + # get all options + opts = ctrl.findall("option") + + for opt in opts: + tmpOpt = {} + tmpOpt["name"] = opt.get("name") + tmpOpt["value"] = opt.get("value") + + tmpCtrl["options"].append(tmpOpt) + + self.__controllers.append(tmpCtrl) + + + def dump(self): + for i in self.__instruments: + print i + + for c in self.__controllers: + print c + + for s in self.__sites: + print s + + +if __name__ == '__main__': + + import sys + + if(len(sys.argv) > 1): + + conf = SiteConfiguration() + conf.read(sys.argv[1]) + conf.dump() + diff --git a/src/python/uts/core/controller.py b/src/python/uts/core/controller.py new file mode 100644 index 00000000..90bf5831 --- /dev/null +++ b/src/python/uts/core/controller.py @@ -0,0 +1,13 @@ +import threading + +class Controller(object): + + def __init__(self, manager, location): + + self.manager = manager + self.location = location + self.term = threading.Event() + + def main(self): + pass + diff --git a/src/python/uts/core/driver.py b/src/python/uts/core/driver.py new file mode 100644 index 00000000..8d1a5f05 --- /dev/null +++ b/src/python/uts/core/driver.py @@ -0,0 +1,192 @@ +#! /usr/bin/python +# -*- coding: iso-8859-1 -*- + +import glob +import logging + +from string import Template +import uts.util.etree.ElementTree as ET + +class Driver(object): + + def __init__(self, config): + + self._config = config + + self._name = None + self._type = None + self._cmdline = None + + self._parameters = {} + + self._pars = {} + self._defaults = {} + + self._filterSet = {} + + self._parseConfig() + + def __iadd__(self, data): + + if not isinstance(data, dict): + return self + + for k,v in data.items(): + + try: + # FIXME type checking + self._pars[k] = v + except KeyError, e: + pass + + return self + + def __getitem__(self, item): + # FIXME type checking and conversion as appopriate + if item in self._pars: + return self._pars[item] + else: + raise KeyError + + def __setitem__(self, item, value): + # FIXME type checking + self._pars[item] = value + + def __contains__(self, item): + return (item in self._pars) + + def __len__(self): + return len(self._pars) + + def __iter__(self): + return iter(self._pars) + + def buildCommand(self, parameters = {}): + + # FIXME: $max on window, filter name to index, + # bool values, output wildcards + + # try to replace every parameter in parameters, + # if any parameters rest in blank use default value + + # use last-minute parameters + self += parameters + + t1 = Template(self._cmdline) + + s1 = t1.safe_substitute(self._pars) + + t2 = Template(s1) + s2 = t2.safe_substitute(self._defaults) + + return s2 + + def help(self): + + _str = "%s - %s\n===\n\n" % (self._name, self._type) + + if self._cmdline: + _str += "cmdline\n===\n\n%s\n\n" % self._cmdline + + if self._parameters: + _str += "parameters\n===\n\n" + + for par, value in self._parameters.items(): + _str += "# %s (type: %s, default: %s)\n%s\n\n" % (par, + value[0], + value[1], + value[2]) + + if self._filterSet: + _str += "Filter set\n===\n\n" + + for _filter, _index in self._filterSet.items(): + _str += "%d %s\n" % (_index, _filter) + + return _str[:-1] + + def _parseConfig(self): + + try: + + f = ET.parse(self._config) + + except IOError, e: + logging.error("Error opening %s (%s)" % (self._config, repr(e))) + return False + + + driver = f.getroot() + + self._type = driver.findtext("type") + self._name = driver.findtext("name") + self._cmdline = driver.findtext("cmdline") + + parameters = driver.findall('parameters/parameter') + + for parameter in parameters: + + p_name = parameter.findtext("name") + + p_type = parameter.findtext("type") + + p_default = parameter.findtext("default") + + p_doc = parameter.findtext("doc") + + # name => [type, default, doc] possible unchanged for the whole life of the driver + self._parameters[p_name] = [p_type, p_default, p_doc] + + # to speedup some things + self._pars[p_name] = p_default + self._defaults[p_name] = p_default + + + filters = driver.findall("filterset/filter") + + for _filter in filters: + f_index = _filter.findtext("index") + f_name = _filter.findtext("name") + + self._filterSet[f_name] = int(f_index) + + +class NullDriver(object): + def __init__(self): + pass + def help(self): + return "null driver" + +class DriverFactory(object): + + def __init__(self, base = '/etc/uts/drivers/'): + self.base = base + pass + + def get(self, drv): + + drivers = glob.glob('%s/*%s*.xml' % (self.base, drv)) + + if drivers: + driver = Driver(drivers[0]) + return driver + else: + return NullDriver() + +if __name__ == '__main__': + + import sys + + if (len (sys.argv) == 3): + driver = DriverFactory(sys.argv[1]).get(sys.argv[2]) + print driver.help() + +# if (len(sys.argv) > 1): + +# for inst in sys.argv[1:]: +# d = Driver(inst) +# d.buildCommand({}) +# print d.help() +# print + + diff --git a/src/python/uts/core/event.py b/src/python/uts/core/event.py new file mode 100644 index 00000000..8a89318a --- /dev/null +++ b/src/python/uts/core/event.py @@ -0,0 +1,79 @@ +#! /usr;bin/python +# -*- coding: iso-8859-1 -*- + +def event(f): + f.event = True + return f + +# based on this recipe http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/410686 +# by Zoran Isailovskii + +class EventsProxy: + + def __init__(self, evs): + self.__events__ = evs + + def __getattr__(self, name): + if hasattr(self, '__events__'): + assert name in self.__events__, "Event '%s' is not declared" % name + self.__dict__[name] = ev = _EventSlot(name) + return ev + + def __repr__(self): + return 'Events' + str(list(self)) + + def __str__(self): + return self.__repr__() + + def __contains__(self, attr): + return attr in self.__events__ + +class _EventSlot: + + def __init__(self, name): + self.targets = [] + self.__name__ = name + +# def __repr__(self): +# return 'event ' + self.__name__ + + def __call__(self, *a, **kw): + for f in self.targets: f(*a, **kw) + + def __iadd__(self, f): + self.targets.append(f) + return self + + def __isub__(self, f): + while f in self.targets: self.targets.remove(f) + return self + + +if __name__ == '__main__': + + from uts.core.instrument import Instrument + + class ExampleDefinition(Instrument): + + def __init__(self): + Instrument.__init__(self) + + def doSomething(self, data): + self.somethingHappened(self, data) + + @event + def somethingHappened(self, obj, data): + pass + + class ExampleUsage(object): + + def __init__(self, obj): + obj.somethingHappened += self.somethingHappened + + def somethingHappened(self, obj, data): + print self, obj, data + + ex = ExampleDefinition() + us = ExampleUsage(ex) + + ex.doSomething("now") diff --git a/src/python/uts/core/exceptions.py b/src/python/uts/core/exceptions.py new file mode 100644 index 00000000..56698b66 --- /dev/null +++ b/src/python/uts/core/exceptions.py @@ -0,0 +1,3 @@ + +class NotImplemented(Exception): + pass diff --git a/src/python/uts/core/instrument.py b/src/python/uts/core/instrument.py new file mode 100644 index 00000000..561f9d05 --- /dev/null +++ b/src/python/uts/core/instrument.py @@ -0,0 +1,88 @@ +import uts + +from uts.core.proxy import AsyncResult +from uts.core.event import EventsProxy + +import time + +import threading + +### TODO Add life cycle code (start/stop/query/etc) + +class InstrumentMeta(type): + + def __new__(metacls, clsname, bases, dictionary): + + _evs = [] + for name, obj in dictionary.iteritems(): + if callable(obj) and not name.startswith('__') and hasattr(obj, 'event'): + _evs.append(name) + + for name in _evs: + del dictionary[name] + + dictionary['__events__'] = _evs + + return super(InstrumentMeta, metacls).__new__(metacls, clsname, bases, dictionary) + + +class Instrument: + + __metaclass__ = InstrumentMeta + + def __init__(self, name): + + self.name = name + + self.manager = None + self.location = None + self.rpc = None + + self.driver = None + + self.events = EventsProxy(self.__events__) + + # loop control + self.timeslice = 0.5 + self.looping = False + + # term event + self.term = threading.Event() + + def __getattr__(self, attr): + if attr in self.events: + return self.events.__getattr__(attr) + else: + raise AttributeError + + def inst_main(self): + pass + + def main(self): + + # enter main loop + self._main() + + return True + + def _main(self): + + self.looping = True + + try: + + while(self.looping): + + if (self.term.isSet()): + self.looping = False + return + + # run instrument control functions + self.inst_main() + + time.sleep(self.timeslice) + + except KeyboardInterrupt, e: + self.looping = False + return + diff --git a/src/python/uts/core/location.py b/src/python/uts/core/location.py new file mode 100644 index 00000000..99c4b9cd --- /dev/null +++ b/src/python/uts/core/location.py @@ -0,0 +1,83 @@ +#! /usr/bin/python +# -*- coding: iso8859-1 -*- + +import re +import logging + +from types import (DictType, ListType, + TupleType, StringType) + + +class Location(object): + + def __init__(self, location): + + self._re = re.compile('/+(?P[\w]*)/+(?P[\w]*)\??(?P[\w=,]*)') + + self._class = "class" + self._name = "name" + self._options = {} + + self._valid = True + + try: + + if type(location) == DictType: + self._class = location["class"] + self._name = location["name"] + self._options = location["options"] + + elif type(location) in [ListType, TupleType]: + self._class = location[1] + self._name = location[2] + self._options = location[3] + + elif type(location) == StringType: + + (self._class, self._name, self._options) = matches = self.parse(location) + + except (KeyError, IndexError), e: + self._valid = False + + def isValid(self): + + return self._valid + + def parse(self, location): + + matches = self._re.search(location) + + if matches: + + cls, name, tmpOpts = matches.groups() + + opts = {} + + if tmpOpts: + for opt in tmpOpts.split(","): + k, v = opt.split("=") + opts[k.strip()] = v.strip() + + else : + self._valid = False + cls = "class" + name = "name" + opts = {} + + logging.debug("Invalid location %s." % location) + + return (cls, name, opts) + + def __eq__(self, loc): + + return (loc._class == self._class) and \ + (loc._name == self._name) + + def __repr__(self): + _str = "/%s/%s" % (self._class, + self._name) + + return _str + + def get(self): + return self.__repr__(self) diff --git a/src/python/uts/core/log.config b/src/python/uts/core/log.config new file mode 100644 index 00000000..d2eb1578 --- /dev/null +++ b/src/python/uts/core/log.config @@ -0,0 +1,44 @@ +[loggers] +keys=root,uts,instruments,controllers + +[handlers] +keys=err + +[formatters] +keys=uts + +[logger_root] +handlers=err +level=DEBUG + +[logger_uts] +handlers=err +level=DEBUG +propagate=0 +qualname=uts + +[logger_instruments] +handlers=err +level=DEBUG +propagate=1 +qualname=uts.instruments + +[logger_controllers] +handlers=err +level=DEBUG +propagate=1 +qualname=uts.controllers + +[formatter_uts] +format=%(asctime)s %(levelname)s %(name)s %(module)s:%(lineno)d %(message)s +datefmt=%d %m %Y %H %M %S (%j) + +[handler_novo] +class=FileHandler +formatter=uts +args=("/var/log/uts/uts.log", "a") + +[handler_err] +class=StreamHandler +formatter=uts +args=(sys.stderr,) diff --git a/src/python/uts/core/manager.py b/src/python/uts/core/manager.py new file mode 100644 index 00000000..a2dee068 --- /dev/null +++ b/src/python/uts/core/manager.py @@ -0,0 +1,145 @@ +#! /usr/bin/python +#! -*- coding: iso-8859-1 -*- + +from uts.core.register import Register +from uts.core.proxy import Proxy +from uts.core.location import Location + +import sys +import os.path +import logging + +class Manager(object): + + def __init__(self): + logging.debug("Starting manager.") + + self._includePath = [] + + self._instruments = Register() + self._controllers = Register() + + self._pool = None + + self._cache = { } + + def appendPath(self, path): + if not os.path.isabs(path): + path = os.path.abspath(path) + + logging.debug("Adding %s to include path." % path) + self._includePath.append(path) + + def setPool(self, pool): + self._pool = pool + + def addInstrument(self, location): + cls = self._getClass(location._class) + + if cls: + return self._instruments.register(location, cls(self, location)) + else: + return False + + def removeInstrument(self, location): + self._instruments.unregister(location) + + def addController(self, location): + cls = self._getClass(location._class) + + if cls: + return self._controllers.register(location, cls(self, location)) + else: + return False + + def removeController(self, location): + + self._controllers.unregister(location) + + def initInstrument(self, location): + + if(not location in self._instruments): + return False + + if(not self._pool): + logging.debug("There is no thread pool avaiable.") + logging.debug("You should create one and set with setPool.") + return False + + logging.debug("Initializing instrument %s." % location) + self._pool.queueTask(self._instruments[location].main) + + def initController(self, location): + + if(not location in self._controllers): + return False + + if(not self._pool): + logging.debug("There is no thread pool avaiable.") + logging.debug("You should create one and set with setPool.") + return False + + logging.debug("Initializing controller %s." % location) + self._pool.queueTask(self._controllers[location].main) + + def stopInstrument(self, location): + try: + self._instruments[location].term.set() + logging.debug("Stopping instrument %s." % location) + except KeyError: + return False + + def stopController(self, location): + try: + self._controllers[location].term.set() + logging.debug("Stopping controller %s." % location) + except KeyError: + return False + + def stopAll(self): + + for location in self._controllers.keys(): + self.stopController(location) + self.removeController(location) + + for location in self._instruments.keys(): + self.stopInstrument(location) + self.removeInstrument(location) + + def getProxy(self, location): + + # FIXME: extend to controllers + + inst = self._instruments.get(Location(location)) + + if inst: + return Proxy(inst, self._pool) + else: + return None + + def _getClass(self, name): + """ + Based on this recipe + http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52241 + by Jorgen Hermann + """ + + if name in self._cache: + return self._cache[name] + + for path in self._includePath: + if path not in sys.path: + sys.path.insert(0, path) + + try: + module = __import__(name.lower(), globals(), locals(), [name]) + logging.debug("Loading class %s from %s" % (name, module.__file__)) + + except ImportError: + logging.exception("Couldn't load class %s.\nsys.path = %s\nException follows..." % (name, sys.path)) + return None + + self._cache[name] = vars(module)[name] + + return self._cache[name] + diff --git a/src/python/uts/core/proxy.py b/src/python/uts/core/proxy.py new file mode 100644 index 00000000..a6f1bec2 --- /dev/null +++ b/src/python/uts/core/proxy.py @@ -0,0 +1,89 @@ +#! /usr/bin/python +# -*- coding: iso8859-1 -*- + + +import logging +import threading + +from uts.core.async import AsyncResult + +class Proxy(object): + + def __init__(self, obj, threadPool = None): + + self._obj = obj + self._pool = threadPool + + def __getattribute__(self, value): + obj = object.__getattribute__(self, '_obj') + pool = object.__getattribute__(self, '_pool') + + if(hasattr(obj, value)): + prop = getattr(obj, value) + +# if(callable(prop) and hasattr(prop, "event")): +# return AsyncEvent(prop, pool) + + if(callable(prop)): + return AsyncResult(prop, pool) + + # non callable, just returns + return prop + else: + raise AttributeError + +def lock(func): + func.lock = threading.Lock() + return func + + +if __name__ == '__main__': + + import time + import threading + + from uts.core.threads import ThreadPool + + class Simples(object): + + def __init__(self): + self.coisa = "haha" + + def nome(self): + print "nome:", threading.currentThread().getName() + return "nome: result (" + threading.currentThread().getName() + ")" + + def outroNome(self): + time.sleep(5) + print "outroNome:", threading.currentThread().getName() + return "outroNome: result("+threading.currentThread().getName()+")" + + def nomeCallback(self, result): + print "callback:", result + + + try: + + pool = ThreadPool(5) + + p1 = Proxy(Simples(), pool) + + # calls nome synchronously in the main thread + r1 = p1.nome() + print r1 + + # calls nome asynchronously in a new thread, will + # call p1.nomeCallback when nome finishes + r2 = p1.nome.begin(callback=p1.nomeCallback) + + # calls outroNome asynchronously in a new thread + r3 = p1.outroNome.begin() + + # block until r3 (nome) ends + print r3.end() + + print p1.coisa + + finally: + pool.joinAll() + diff --git a/src/python/uts/core/register.py b/src/python/uts/core/register.py new file mode 100644 index 00000000..8825e18b --- /dev/null +++ b/src/python/uts/core/register.py @@ -0,0 +1,129 @@ +from types import StringType + +class Register(object): + + def __init__(self, kind = None): + + self.objects = {} + + def register(self, location, instance): + + if location in self.objects: + return False + + self.objects[location] = instance + + def unregister(self, location): + if not location in self.objects: + return False + + del self.objects[location] + + def update(self, location, instance): + self.unregister(location) + self.register(location, instance) + + def __repr__(self): + _str = "There are %d objects avaiable.\n" % len(self.objects) + _str += self.objects.__repr__() + + return _str + + def __contains__(self, item): + _ret = filter(lambda x: x == item, self.objects.keys()) + + if _ret: + return True + else: + return False + + def __len__(self): + return self.objects.__len__() + + def __iter__(self): + return self.objects.__iter__() + + def items(self): + return self.objects.items() + + def keys(self): + return self.objects.keys() + + def values(self): + return self.objects.values() + + def __getitem__(self, item): + r = self.get(item) + + if not r: + raise KeyError + else: + return r + + def get(self, item): + + if type(item) == StringType: + item = Location(item) + + if not item.isValid(): + return False + + # try to get by index first + try: + numberedInstance = True + number = int(item._name) + except ValueError: + numberedInstance = False + + if numberedInstance: + return self.getByIndex(item._class, number) + + if self.__contains__(item): + ret = filter(lambda x: x == item, self.objects.keys()) + + return self.objects[ret[0]] + else: + return False + + + def getByClass(self, cls): + + _insts = filter(lambda inst: inst._class == cls, self.objects.keys()) + + return _insts + + def getByIndex(self, cls, index): + + insts = self.getByClass(cls) + + if insts: + try: + ret = self.get(insts[index]) + return ret + except IndexError: + return False + else: + return False + + +if __name__ == '__main__': + + from uts.core.location import Location + a = object() + l = Location("/Telescope/meade?opt1=val1,opt2=val2") + + b = object() + ll = Location("/Telescope/paramount?opt1=val1,opt2=val2") + + + r = Register() + r.register(l, a) + r.register(ll, b) + + print r + print r[l] + print r["/Telescope/meade"] + + lll = r.getByClass("Telescope") + for i in lll: + print r[i] diff --git a/src/python/uts/core/sec.py b/src/python/uts/core/sec.py new file mode 100644 index 00000000..f34125ce --- /dev/null +++ b/src/python/uts/core/sec.py @@ -0,0 +1,363 @@ + +from spm import Spm + +import sys +import logging +import socket +import select + +class PropertyList(dict): + + def __init__(self, config = "/etc/uts/sec/tel.conf"): + dict.__init__(self) + + self.__config = config + + + def readConfig(self, config = None): + if config: + self.__config = config + + try: + f = open(self.__config) + + for line in f.readlines(): + + if ("STATUS" in line) or ("INSTNAME" in line): + self[line.split(" ")[1][:-1]] = None + + f.close() + + except Exception, e: + logging.exception("Error reading sec config %s." % self.__config) + + +class Notify: + + def __init__(self, sec = "TEL", prop = None, value = None): + + self.sec = sec + self.prop = prop + self.value = value + + def __repr__(self): + return "Event at %s. %s => %s" % (str(self.sec), str(self.prop), + str(self.value)) + + +class Sec: + """ + Low-level access to UTS Secretary + + UTS Secretary manage instrument properties and register notifies. + """ + + def __init__(self, name, level = "control", + server = None, + interactive = False, + port = None): + + """ + @param name: Instrument name (from uts.config) + @type name: str + @param level: Access level (control, status) + @type level: string + @param server: Server name or IP of Spm (the sockets port Oracle) + @type server: str + @param interactive: Verbose mode, default it's False. + @type interactive: bool + @param port: not used (future) + + @returns: None + """ + + self.name = name + self.level = level + self.server = server + self.port = port + self.interactive = interactive + + #-- + self.props = PropertyList("/etc/uts/sec/" + self.name.lower() + ".conf") + self.props.readConfig() + + self.notifies = [] + + self.sk = None + + def connect(self, server = None, port = None): + """ + Establish a connection with the Secretary. + + @param server: Server name or IP of Spm (the sockets port Oracle) + @type server: str + @param port: not used (future) + + @returns: True on success, False on error. + """ + + if(self.sk): + self.disconnect() + + self.server = server or 'localhost' + + spm = Spm(self.server) + + self.port = spm.getPort(self.name+self.level) + + if (not self.port): + self.err("Spm: Cannot get %s port." % (self.name + self.level)) + return False + + try: + self.sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.sk.connect((self.server, self.port)) + + except socket.error, msg: + self.err("Cannot connect to %s (%s)." % + (str(self.server) + ":" + str(self.port), msg[1])) + self.sk = None + + return False + + # OK, connect, here we go! + + # check SERVER_FULL + full = self.sk.recv(100) + full = self.removeNULL(full) + + if (full == "SERVER_FULL"): + self.err("Server full.") + return False + + # ident + self.sk.send(self.appendNULL("IDENT INSTRUMENT")) + + ret = self.sk.recv(100) + ret = self.removeNULL(ret); + + if(ret == "ERROR"): + self.err("Error trying IDENT INSTRUMENT.") + return False + else: + return True + + def disconnect(self): + if(self.sk): + self.sk.send(self.appendNULL("QUIT")) + self.sk.close() + self.sk = None + + def appendNULL(self, s): + + return s + "\x00" + + def removeNULL(self, s): + + return s[:-1] + + def out(self, msg, buff = sys.stdout): + logging.debug(msg) + + def err(self, msg): + logging.error(msg) + + def setInteractive(self, t = True): + self.interactive = t + + def status(self, prop, value = None): + """ + Get or set a property. + + @param prop: The property to get/set. + @type prop: str + @param value: The value to set. + @type value: str + + If C{value} not None, set C{prop} to C{value} and return True on success and False on error. + + If C{value} equals None (the default), get C{prop} and returns the property value as string or False on error. + + If C{self.level} not equals to 'control', return False ('status' secretaries don't have permission to SETSTATUS). + """ + + if(not self.sk): + self.err("Not connected.") + return False + + if not (prop in self.props.keys()): + self.err("%s it's not a valid property." % prop) + return False + + if (value): # SETSTATUS + + if(self.level != "control"): + self.err("You dont' have control permission.") + return False + + self.sk.send(self.appendNULL( + "SETSTATUS " + str(prop) + " " + str(value))) + + res = self.sk.recv(100) + res = self.removeNULL(res) + + if(res == "OK"): + self.out("%s ==> %s" % (prop, value)) + return True + + elif(res == "ERROR"): + self.err("Bad...") + return False + + else: # GETSTATUS + + # check for notifies + if(self.level == "status"): + self.checkNotifies(buffer=True) + + self.sk.send(self.appendNULL("STATUS " + str(prop))) + + res = self.sk.recv(100) + res = self.removeNULL(res) + + if(res == "ERROR"): + ret = res + else: + ret = res[len("STATUS "):] + + self.out("%s: %s" % (prop, ret)) + + return ret + + def isBusy(self): + ret = self.status(self.name) + + if(ret == "OFFLINE") or (ret == "IDLE") or (ret == "DISABLED"): + return False + else: + return True + + def isConnected(self): + if(self.sk): + return True + else: + return False + + def notify(self, prop): + """ + Register a notify for C{prop} + + @param prop: The property on which you want to be notified + @type prop: str + + @returns: True on success, False on error. + @see: L{Sec.unnotify} + + If C{pro} don't exists, C{self.level} equals 'control' or if C{Sec} cannot register the notify return False + Return True on success. + + """ + + if(not self.sk): + self.err("Not connected.") + return False + + if(not prop in self.props.keys()): + self.err("Unknow property %s." % prop) + return False + + if(self.level == "control"): + self.err("Level must be 'status', not 'control'.") + return False + + self.checkNotifies(buffer=True) + + self.sk.send(self.appendNULL("NOTIFY " + str(prop))) + + res = self.sk.recv(100) + res = self.removeNULL(res) + + if(res == "OK"): + return True + + elif(res == "ERROR"): + return False + + def unnotify(self, prop): + """ + Unregister a notify for C{prop} + + If I{pro} don't exists, I{self.level} equals 'control' or if Sec cannot unregister the notify return False + + @returns: True on success, False on error. + """ + + if(not self.sk): + self.err("Not connected.") + return False + + if(not prop in self.props.keys()): + self.err("Unknow property %s." % prop) + return False + + if(self.level == "control"): + self.err("Level must be 'status', not 'control'.") + return False + + self.checkNotifies(buffer=True) + + self.sk.send(self.appendNULL("UNOTIFY " + str(prop))) + + res = self.sk.recv(100) + res = self.removeNULL(res) + + if(res == "OK"): + return True + + elif(res == "ERROR"): + return False + + def checkNotifies(self, blocking = False, timeout = 60, buffer = False): + + if(not self.sk): + self.err("Not connected.") + return False + + if(self.level == "control"): + self.err("Level must be 'status', not 'control'.") + return False + + if(blocking): + wait = select.select([self.sk], [], [], timeout) + else: + wait = select.select([self.sk], [], [], 0) + + if(len(wait[0])): + data = self.sk.recv(255).split('\x00')[:-1] + + res = [] + + for i in data: + res.append(Notify(self.name, + i.split(" ")[1].strip(), + i.split(" ")[2].strip())) + + if(buffer): + self.notifies = self.notifies + res + return True + else: + ret = self.notifies + res + self.notifies = [] + + return ret + + else: + + if (buffer): + return True + + else: + ret = self.notifies + self.notifies = [] + + return ret diff --git a/src/python/uts/core/site.py b/src/python/uts/core/site.py new file mode 100644 index 00000000..392ce3f1 --- /dev/null +++ b/src/python/uts/core/site.py @@ -0,0 +1,190 @@ +#! /usr/bin/python +#! -*- coding: iso-8859-1 -*- + +from uts.core.instrument import Instrument +from uts.core.controller import Controller +from uts.core.register import Register +from uts.core.config import SiteConfiguration +from uts.core.threads import ThreadPool +from uts.core.proxy import Proxy +from uts.core.location import Location +from uts.core.manager import Manager + +from uts.core.version import _uts_version + +import signal +import os +import os.path +import distutils.sysconfig +import logging +import threading + +from optparse import OptionParser + +# FIXME: handle instruments and controllers main function error + +class Site(object): + + def __init__(self, args = []): + + self.options, self.args = self.parseArgs(args) + + # verbosity level + logging.basicConfig(level=logging.WARNING, + format='%(asctime)s %(levelname)s %(module)s:%(lineno)d %(message)s', + datefmt='%d-%m-%Y %H:%M:%S (%j)') + + if self.options.verbose: + logging.getLogger().setLevel(logging.DEBUG) + + logging.debug("Starting system.") + + # manager + self.manager = Manager() + + # directories + + for _dir in self.options.inst_dir: + self.manager.appendPath(_dir) + + for _dir in self.options.ctrl_dir: + self.manager.appendPath(_dir) + + + def parseArgs(self, args): + + parser = OptionParser(prog="UTS", version=_uts_version, + description="UTS - Unified Telescope System") + + parser.add_option("-i", "--instrument", action="append", dest="instruments", + help="Load the instrument defined by LOCATION." + "This option could be setted many times to load multiple instruments.", + metavar="LOCATION") + + parser.add_option("-c", "--controllers", action="append", dest="controllers", + help="Load the controller defined by LOCATION." + "This option could be setted many times to load multiple controllers.", + metavar="LOCATION") + + parser.add_option("-f", "--file", action="append", dest="config", + help="Load instruments and controllers defined on FILE." + "This option could be setted many times to load inst/controllers from multiple files.", + metavar="FILE") + + parser.add_option("-I", "--instruments-dir", action="append", dest="inst_dir", + help="Append PATH to instruments load path.", + metavar="PATH") + + parser.add_option("-C", "--controllers-dir", action="append", dest="ctrl_dir", + help="Append PATH to controllers load path.", + metavar="PATH") + + parser.add_option("-v", "--verbose", action="store_true", dest='verbose', + help="Increase screen log level.") + + prefix = os.path.realpath(os.path.join(os.path.abspath(__file__), '../../')) + + parser.set_defaults(instruments = [], + controllers = [], + config = [], + inst_dir = [os.path.join(prefix, 'instruments')], + ctrl_dir = [os.path.join(prefix, 'controllers')], + verbose=False) + + return parser.parse_args(args) + + def init(self): + self._pool = ThreadPool(10) + self.manager.setPool(self._pool) + + # config file + self.config = SiteConfiguration() + + for config in self.options.config: + self.config.read(config) + + for inst in self.config.getInstruments(): + l = Location(inst) + self.manager.addInstrument(l) + self.manager.initInstrument(l) + + for ctrl in self.config.getControllers(): + l = Location(ctrl) + self.manager.addController(l) + self.manager.initController(l) + + # add all instruments from config and from cmdline + for inst in self.options.instruments: + l = Location(inst) + self.manager.addInstrument(l) + self.manager.initInstrument(l) + + for ctrl in self.options.controllers: + l = Location(ctrl) + self.manager.addController(l) + self.manager.initController(l) + + def stop(self): + self.manager.stopAll() + logging.debug("Stoping system.") + +def main(args): + + def splitAndWatch(stop, finish): + + child = os.fork() + + if child == 0: + return + + def kill(): + stop() + finish.set() + os.kill(child, signal.SIGKILL) + + def sighandler(sig, frame): + kill() + + signal.signal(signal.SIGTERM, sighandler) + signal.signal(signal.SIGINT, sighandler) + + try: + os.wait() + stop() + finish.set() + + except OSError: + pass + + # ============ + + # FIXME Ugly hacks to python threading works with signal + # FIXME see http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496735 + # FIXME see http://greenteapress.com/semaphores/ + + mainProcess = threading.Event() + + # only initialize, DON'T run threads here! + + s = Site(args) + + # from here we have 2 process. Child will return from splitAndWatch, + # while the main process will watch for signals and will kill the child + # process and set mainProcess event so the remaining of the code + # know what execute + + splitAndWatch(s.stop, mainProcess) + + # child run the thread + if not mainProcess.isSet(): + s.init() + else: + # run whatever you want on the main thread + pass + +if __name__ == '__main__': + + import sys + + main(sys.argv) + diff --git a/src/python/uts/core/spm.py b/src/python/uts/core/spm.py new file mode 100644 index 00000000..da4f83cc --- /dev/null +++ b/src/python/uts/core/spm.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python + +import sys +import logging +import commands +import socket + +class SpmNet: + + def __init__(self, server = 'localhost', port = 1750): + self.server = server + self.port = port + self.servers = {} + + self.proto = {'PM_SERVER' : '\x00\x01', 'PM_CLIENT' : '\x00\x02', + 'PM_CLOSE' : '\x00\x03', 'PM_RESEND' : '\x00\x04', + 'PM_QUIT' : '\x00\x05', 'PM_SORRY' : '\x00\x06', + 'PM_OK' : '\x00\x07', 'PM_ACCEPT' : '\x00\x08', + 'PM_TABLE' : '\x00\x09', 'PM_RMSERVER': '\x00\x10', + 'PM_FWINIT' : '\x00\x11', 'PM_SHARE' : '\x00\x12', + 'PM_OKSHARE' : '\x00\x13', 'PM_BIGBUF' : 1024, + 'PM_MAXTRY' : 20 } + + self.getServers() + + def getPort(self, server = None): + """ + Retorna a porta de um dado servidor. + @param server servidor do qual deseja saber a porta + """ + + try: + return self.servers[server] + except KeyError: + return 0 + + def getServers(self): + """ + Retorna um dictionary com os servidores e respectivas portas + ativos no momento. + """ + + sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + try: + sk.connect((self.server, self.port)) + sk.send(self.proto['PM_TABLE']) + + if sk.recv(2) == self.proto['PM_OK']: + num = sk.recv(2) + + data = sk.recv(self.proto['PM_BIGBUF']) + data = data.split('\x00') + del data[-1] + + for i in data: + item = i.split(':') + self.servers[item[0].strip()] = int(item[1]) + + sk.close() + + return self.servers + + elif sk.recv(2) == self.proto['PM_RESEND']: + sk.close() + + except socket.error, msg: + logging.exception("Error %s:" % msg[1]) + sk.close() + + def dumpTable(self): + for i,v in self.servers.items(): + print "%s %d" % (i, v) + + +class SpmCommand: + + def __init__(self, server = 'localhost', spmtable = None): + + self.server = server + self.servers = {} + + self.getServers() + + def getPort(self, server = None): + """ + Retorna a porta de um dado servidor. + @param server servidor do qual deseja saber a porta + """ + + try: + return self.servers[server] + except KeyError: + return 0 + + def getServers(self): + """ + Retorna um dictionary com os servidores e respectivas portas + ativos no momento. + """ + + try: + out = commands.getoutput("spmtable") + out = out.split('\n')[2:-1] + + for i in out: + self.servers[i.split(':')[1].strip()] = int(i.split(':')[2].strip()) + + return self.servers + except Exception, e: + logging.exception("Error getting servers.") + return False + + def dumpTable(self): + for i,v in self.servers.items(): + print "%s %d" % (i, v) + +Spm = SpmCommand + +#-- +if __name__ == '__main__': + + if(len(sys.argv) < 2): + spm = Spm() + else: + spm = Spm(sys.argv[1]) + + spm.dumpTable() + diff --git a/src/python/uts/core/threads.py b/src/python/uts/core/threads.py new file mode 100644 index 00000000..103e805d --- /dev/null +++ b/src/python/uts/core/threads.py @@ -0,0 +1,200 @@ +#! /usr/bin/python +# -*- coding: iso8859-1 -*- + +import threading +import logging +import signal +import time + +from time import sleep, time + +# Ensure booleans exist (not needed for Python 2.2.1 or higher) +try: + True +except NameError: + False = 0 + True = not False + +#threading._VERBOSE = True + +class ThreadPool: + + """Flexible thread pool class. Creates a pool of threads, then + accepts tasks that will be dispatched to the next available + thread.""" + + def __init__(self, numThreads): + + """Initialize the thread pool with numThreads workers.""" + + self.__threads = [] + self.__resizeLock = threading.Condition(threading.Lock()) + self.__taskLock = threading.Condition(threading.Lock()) + self.__tasks = [] + self.__isJoining = False + self.setThreadCount(numThreads) + + def setThreadCount(self, newNumThreads): + + """ External method to set the current pool size. Acquires + the resizing lock, then calls the internal version to do real + work.""" + + # Can't change the thread count if we're shutting down the pool! + if self.__isJoining: + return False + + self.__resizeLock.acquire() + try: + self.__setThreadCountNolock(newNumThreads) + finally: + self.__resizeLock.release() + return True + + def __setThreadCountNolock(self, newNumThreads): + + """Set the current pool size, spawning or terminating threads + if necessary. Internal use only; assumes the resizing lock is + held.""" + + # If we need to grow the pool, do so + while newNumThreads > len(self.__threads): + newThread = ThreadPoolThread(self) + self.__threads.append(newThread) + newThread.start() + # If we need to shrink the pool, do so + while newNumThreads < len(self.__threads): + self.__threads[0].goAway() + del self.__threads[0] + + def getThreadCount(self): + + """Return the number of threads in the pool.""" + + self.__resizeLock.acquire() + try: + return len(self.__threads) + finally: + self.__resizeLock.release() + + def queueTask(self, task, args=( ), kwargs = { }, taskCallback=None): + + """Insert a task into the queue. task must be callable; + args and taskCallback can be None.""" + + if self.__isJoining == True: + return False + if not callable(task): + return False + + self.__taskLock.acquire() + try: + self.__tasks.append((task, args, kwargs, taskCallback)) + return True + finally: + self.__taskLock.release() + + def getNextTask(self): + + """ Retrieve the next task from the task queue. For use + only by ThreadPoolThread objects contained in the pool.""" + + self.__taskLock.acquire() + try: + if self.__tasks == []: + return (None, None, None, None) + else: + return self.__tasks.pop(0) + finally: + self.__taskLock.release() + + def joinAll(self, waitForTasks = True, waitForThreads = True): + + """ Clear the task queue and terminate all pooled threads, + optionally allowing the tasks and threads to finish.""" + + # Mark the pool as joining to prevent any more task queueing + self.__isJoining = True + + # Wait for tasks to finish + if waitForTasks: + while self.__tasks != []: + sleep(.1) + + # Tell all the threads to quit + self.__resizeLock.acquire() + try: + self.__setThreadCountNolock(0) + self.__isJoining = True + + # Wait until all threads have exited + if waitForThreads: + for t in self.__threads: + t.join() + del t + + # Reset the pool for potential reuse + self.__isJoining = False + finally: + self.__resizeLock.release() + + + +class ThreadPoolThread(threading.Thread): + + """ Pooled thread class. """ + + threadSleepTime = 0.1 + + def __init__(self, pool): + + """ Initialize the thread and remember the pool. """ + + threading.Thread.__init__(self) + self.__pool = pool + self.__isDying = False + self.setDaemon(False) + + def run(self): + + """ Until told to quit, retrieve the next task and execute + it, calling the callback if any. """ + + while self.__isDying == False: + cmd, args, kwargs, callback = self.__pool.getNextTask() + # If there's nothing to do, just sleep a bit + if cmd is None: + sleep(ThreadPoolThread.threadSleepTime) + else: + if(hasattr(cmd, "lock")): + cmd.lock.acquire() + logging.debug("Locking %s" % cmd) + + logging.debug("Running %s" % cmd) + if callback is None: + cmd(*args, **kwargs) + else: + callback(cmd(*args, **kwargs)) + + if(hasattr(cmd, "Lock")): + cmd.lock.release() + logging.debug("unlocking %s" % cmd) + + def goAway(self): + + """ Exit the run loop next time through.""" + + self.__isDying = True + + +__tp = None + +def getThreadPool(n): + + global __tp + + if not __tp: + __tp = ThreadPool(n) + + return __tp + diff --git a/src/python/uts/core/version.py b/src/python/uts/core/version.py new file mode 100644 index 00000000..e97b99d5 --- /dev/null +++ b/src/python/uts/core/version.py @@ -0,0 +1,2 @@ +_uts_author = "P. Henrique Silva" +_uts_version = "0.1" diff --git a/src/python/uts/instruments/Makefile.am b/src/python/uts/instruments/Makefile.am new file mode 100644 index 00000000..e69de29b diff --git a/src/python/uts/instruments/__init__.py b/src/python/uts/instruments/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/python/uts/interfaces/Makefile b/src/python/uts/interfaces/Makefile new file mode 100644 index 00000000..5c55493a --- /dev/null +++ b/src/python/uts/interfaces/Makefile @@ -0,0 +1,291 @@ +# Makefile.in generated by automake 1.8.3 from Makefile.am. +# src/python/uts/interfaces/Makefile. Generated from Makefile.in by configure. + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +srcdir = . +top_srcdir = ../../../.. + +pkgdatadir = $(datadir)/uts +pkglibdir = $(libdir)/uts +pkgincludedir = $(includedir)/uts +top_builddir = ../../../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = /usr/bin/install -c +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +subdir = src/python/uts/interfaces +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(mkdir_p) +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +SOURCES = +DIST_SOURCES = +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = ${SHELL} /home/vela/henrique/uts-svn/missing --run aclocal-1.8 +ALLOCA = +AMDEP_FALSE = # +AMDEP_TRUE = +AMTAR = ${SHELL} /home/vela/henrique/uts-svn/missing --run tar +AUTOCONF = ${SHELL} /home/vela/henrique/uts-svn/missing --run autoconf +AUTOHEADER = ${SHELL} /home/vela/henrique/uts-svn/missing --run autoheader +AUTOMAKE = ${SHELL} /home/vela/henrique/uts-svn/missing --run automake-1.8 +AWK = gawk +CC = gcc +CCDEPMODE = depmode=gcc3 +CFLAGS = -g -O2 +CPP = gcc -E +CPPFLAGS = +CXX = g++ +CXXDEPMODE = depmode=gcc3 +CXXFLAGS = -g -O2 +CYGPATH_W = echo +DEFS = -DHAVE_CONFIG_H +DEPDIR = .deps +ECHO_C = +ECHO_N = -n +ECHO_T = +EGREP = grep -E +EXEEXT = +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_PROGRAM = ${INSTALL} +INSTALL_SCRIPT = ${INSTALL} +INSTALL_STRIP_PROGRAM = ${SHELL} $(install_sh) -c -s +LDFLAGS = +LIBOBJS = +LIBS = +LN_S = ln -s +LTLIBOBJS = +MAKEINFO = ${SHELL} /home/vela/henrique/uts-svn/missing --run makeinfo +OBJEXT = o +PACKAGE = uts +PACKAGE_BUGREPORT = henrique@astro.ufsc.br +PACKAGE_NAME = uts +PACKAGE_STRING = uts 1.0RC1 +PACKAGE_TARNAME = uts +PACKAGE_VERSION = 1.0RC1 +PATH_SEPARATOR = : +PYTHON = /usr/bin/python +PYTHON_EXEC_PREFIX = ${exec_prefix} +PYTHON_PLATFORM = linux2 +PYTHON_PREFIX = ${prefix} +PYTHON_VERSION = 2.3 +RANLIB = ranlib +SET_MAKE = +SHELL = /bin/sh +STRIP = +UTS_DISTRO = suse +UTS_INITCONFIG_DIR = /etc/sysconfig +VERSION = 1.0RC1 +ac_ct_CC = gcc +ac_ct_CXX = g++ +ac_ct_RANLIB = ranlib +ac_ct_STRIP = +am__fastdepCC_FALSE = # +am__fastdepCC_TRUE = +am__fastdepCXX_FALSE = # +am__fastdepCXX_TRUE = +am__include = include +am__leading_dot = . +am__quote = +bindir = ${exec_prefix}/bin +build_alias = +datadir = ${prefix}/share +exec_prefix = ${prefix} +host_alias = +includedir = ${prefix}/include +infodir = ${prefix}/info +install_sh = /home/vela/henrique/uts-svn/install-sh +libdir = ${exec_prefix}/lib +libexecdir = ${exec_prefix}/libexec +localstatedir = ${prefix}/var +mandir = ${prefix}/man +mkdir_p = mkdir -p -- . +oldincludedir = /usr/include +pkgpyexecdir = ${pyexecdir}/uts +pkgpythondir = ${pythondir}/uts +prefix = /usr/local +program_transform_name = s,x,x, +pyexecdir = ${exec_prefix}/lib/python2.3/site-packages +pythondir = ${prefix}/lib/python2.3/site-packages +sbindir = ${exec_prefix}/sbin +sharedstatedir = ${prefix}/com +sysconfdir = ${prefix}/etc +target_alias = +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/python/uts/interfaces/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/python/uts/interfaces/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +uninstall-info-am: +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am + +.PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic pdf pdf-am ps ps-am uninstall uninstall-am \ + uninstall-info-am + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/python/uts/interfaces/Makefile.am b/src/python/uts/interfaces/Makefile.am new file mode 100644 index 00000000..e69de29b diff --git a/src/python/uts/interfaces/__init__.py b/src/python/uts/interfaces/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/python/uts/interfaces/camera.py b/src/python/uts/interfaces/camera.py new file mode 100644 index 00000000..8642d404 --- /dev/null +++ b/src/python/uts/interfaces/camera.py @@ -0,0 +1,39 @@ +#! /usr/bin/python +# -*- coding: iso-8859-1 -*- + +class CameraExpose(object): + + # properties + expTime = 0 + nExp = 0 + window = None + binning = 0 + binningList = [] + gain = 0 + gainList = [] + chipSize = None + pixelSize = None + maxADU = 0 + fullWellCapacity = 0 + exposing = False + + # methods + def expose (self, expTime, nexp): + pass + + def abortExposure (self): + pass + + def stopExposure (self): + pass + + # events + def exposeComplete (self): + pass + + def exposeAborted (self): + pass + + def exposeStopped (self): + pass + diff --git a/src/python/uts/interfaces/telescope.py b/src/python/uts/interfaces/telescope.py new file mode 100644 index 00000000..0ad89569 --- /dev/null +++ b/src/python/uts/interfaces/telescope.py @@ -0,0 +1,43 @@ +#! /usr/bin/python +# -*- coding: iso-8859-1 -*- + +class TelescopeSlew(object): + + # properties + + currRA = 0 + currDec = 0 + currEphoc = 2000 + currAz = 0 + currAlt = 0 + cmdRA = 0 + cmdDec = 0 + cmdEphoc = 2000 + cmdAz = 0 + cmdAlt = 0 + axis = [] + slewRates = [] + slewRate = 0 + slewing = False + + # methods + + def slew(self, coord): + pass + + def abortSlew(self): + pass + + def moveAxis(self, axis, offset): + pass + + # events + + def slewComplete(self, position, tracking, trackingRate): + pass + + def abortComplete(self, position): + pass + + def targetChanged(self, position): + pass diff --git a/src/python/uts/util/__init__.py b/src/python/uts/util/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/python/uts/util/catalog.py b/src/python/uts/util/catalog.py new file mode 100644 index 00000000..d1c8453b --- /dev/null +++ b/src/python/uts/util/catalog.py @@ -0,0 +1,20 @@ + +class Catalog: + def __init__(self): + pass + +class Object: + + def __init__(self, name = "", ra = None, dec = None): + + self.name = name + + self.ra = ra + self.dec = dec + + self.altitude = None + self.azimuth = None + + self.ephoch = None + + self.resolvers = {} diff --git a/src/python/uts/util/etree/ElementInclude.py b/src/python/uts/util/etree/ElementInclude.py new file mode 100644 index 00000000..bac2f5e4 --- /dev/null +++ b/src/python/uts/util/etree/ElementInclude.py @@ -0,0 +1,141 @@ +# +# ElementTree +# $Id: ElementInclude.py 1862 2004-06-18 07:31:02Z Fredrik $ +# +# limited xinclude support for element trees +# +# history: +# 2003-08-15 fl created +# 2003-11-14 fl fixed default loader +# +# Copyright (c) 2003-2004 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2004 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +## +# Limited XInclude support for the ElementTree package. +## + +import copy +import ElementTree + +XINCLUDE = "{http://www.w3.org/2001/XInclude}" + +XINCLUDE_INCLUDE = XINCLUDE + "include" +XINCLUDE_FALLBACK = XINCLUDE + "fallback" + +## +# Fatal include error. + +class FatalIncludeError(SyntaxError): + pass + +## +# Default loader. This loader reads an included resource from disk. +# +# @param href Resource reference. +# @param parse Parse mode. Either "xml" or "text". +# @param encoding Optional text encoding. +# @return The expanded resource. If the parse mode is "xml", this +# is an ElementTree instance. If the parse mode is "text", this +# is a Unicode string. If the loader fails, it can return None +# or raise an IOError exception. +# @throws IOError If the loader fails to load the resource. + +def default_loader(href, parse, encoding=None): + file = open(href) + if parse == "xml": + data = ElementTree.parse(file).getroot() + else: + data = file.read() + if encoding: + data = data.decode(encoding) + file.close() + return data + +## +# Expand XInclude directives. +# +# @param elem Root element. +# @param loader Optional resource loader. If omitted, it defaults +# to {@link default_loader}. If given, it should be a callable +# that implements the same interface as default_loader. +# @throws FatalIncludeError If the function fails to include a given +# resource, or if the tree contains malformed XInclude elements. +# @throws IOError If the function fails to load a given resource. + +def include(elem, loader=None): + if loader is None: + loader = default_loader + # look for xinclude elements + i = 0 + while i < len(elem): + e = elem[i] + if e.tag == XINCLUDE_INCLUDE: + # process xinclude directive + href = e.get("href") + parse = e.get("parse", "xml") + if parse == "xml": + node = loader(href, parse) + if node is None: + raise FatalIncludeError( + "cannot load %r as %r" % (href, parse) + ) + node = copy.copy(node) + if e.tail: + node.tail = (node.tail or "") + e.tail + elem[i] = node + elif parse == "text": + text = loader(href, parse, e.get("encoding")) + if text is None: + raise FatalIncludeError( + "cannot load %r as %r" % (href, parse) + ) + if i: + node = elem[i-1] + node.tail = (node.tail or "") + text + else: + elem.text = (elem.text or "") + text + (e.tail or "") + del elem[i] + continue + else: + raise FatalIncludeError( + "unknown parse type in xi:include tag (%r)" % parse + ) + elif e.tag == XINCLUDE_FALLBACK: + raise FatalIncludeError( + "xi:fallback tag must be child of xi:include (%r)" % e.tag + ) + else: + include(e, loader) + i = i + 1 + diff --git a/src/python/uts/util/etree/ElementPath.py b/src/python/uts/util/etree/ElementPath.py new file mode 100644 index 00000000..558b560a --- /dev/null +++ b/src/python/uts/util/etree/ElementPath.py @@ -0,0 +1,196 @@ +# +# ElementTree +# $Id: ElementPath.py 1858 2004-06-17 21:31:41Z Fredrik $ +# +# limited xpath support for element trees +# +# history: +# 2003-05-23 fl created +# 2003-05-28 fl added support for // etc +# 2003-08-27 fl fixed parsing of periods in element names +# +# Copyright (c) 2003-2004 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2004 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +## +# Implementation module for XPath support. There's usually no reason +# to import this module directly; the ElementTree does this for +# you, if needed. +## + +import re + +xpath_tokenizer = re.compile( + "(::|\.\.|\(\)|[/.*:\[\]\(\)@=])|((?:\{[^}]+\})?[^/:\[\]\(\)@=\s]+)|\s+" + ).findall + +class xpath_descendant_or_self: + pass + +## +# Wrapper for a compiled XPath. + +class Path: + + ## + # Create an Path instance from an XPath expression. + + def __init__(self, path): + tokens = xpath_tokenizer(path) + # the current version supports 'path/path'-style expressions only + self.path = [] + self.tag = None + if tokens and tokens[0][0] == "/": + raise SyntaxError("cannot use absolute path on element") + while tokens: + op, tag = tokens.pop(0) + if tag or op == "*": + self.path.append(tag or op) + elif op == ".": + pass + elif op == "/": + self.path.append(xpath_descendant_or_self()) + continue + else: + raise SyntaxError("unsupported path syntax (%s)" % op) + if tokens: + op, tag = tokens.pop(0) + if op != "/": + raise SyntaxError( + "expected path separator (%s)" % (op or tag) + ) + if self.path and isinstance(self.path[-1], xpath_descendant_or_self): + raise SyntaxError("path cannot end with //") + if len(self.path) == 1 and isinstance(self.path[0], type("")): + self.tag = self.path[0] + + ## + # Find first matching object. + + def find(self, element): + tag = self.tag + if tag is None: + nodeset = self.findall(element) + if not nodeset: + return None + return nodeset[0] + for elem in element: + if elem.tag == tag: + return elem + return None + + ## + # Find text for first matching object. + + def findtext(self, element, default=None): + tag = self.tag + if tag is None: + nodeset = self.findall(element) + if not nodeset: + return default + return nodeset[0].text or "" + for elem in element: + if elem.tag == tag: + return elem.text or "" + return default + + ## + # Find all matching objects. + + def findall(self, element): + nodeset = [element] + index = 0 + while 1: + try: + path = self.path[index] + index = index + 1 + except IndexError: + return nodeset + set = [] + if isinstance(path, xpath_descendant_or_self): + try: + tag = self.path[index] + if not isinstance(tag, type("")): + tag = None + else: + index = index + 1 + except IndexError: + tag = None # invalid path + for node in nodeset: + new = list(node.getiterator(tag)) + if new and new[0] is node: + set.extend(new[1:]) + else: + set.extend(new) + else: + for node in nodeset: + for node in node: + if path == "*" or node.tag == path: + set.append(node) + if not set: + return [] + nodeset = set + +_cache = {} + +## +# (Internal) Compile path. + +def _compile(path): + p = _cache.get(path) + if p is not None: + return p + p = Path(path) + if len(_cache) >= 100: + _cache.clear() + _cache[path] = p + return p + +## +# Find first matching object. + +def find(element, path): + return _compile(path).find(element) + +## +# Find text for first matching object. + +def findtext(element, path, default=None): + return _compile(path).findtext(element, default) + +## +# Find all matching objects. + +def findall(element, path): + return _compile(path).findall(element) + diff --git a/src/python/uts/util/etree/ElementTree.py b/src/python/uts/util/etree/ElementTree.py new file mode 100644 index 00000000..98d02087 --- /dev/null +++ b/src/python/uts/util/etree/ElementTree.py @@ -0,0 +1,1254 @@ +# +# ElementTree +# $Id: ElementTree.py 2326 2005-03-17 07:45:21Z fredrik $ +# +# light-weight XML support for Python 1.5.2 and later. +# +# history: +# 2001-10-20 fl created (from various sources) +# 2001-11-01 fl return root from parse method +# 2002-02-16 fl sort attributes in lexical order +# 2002-04-06 fl TreeBuilder refactoring, added PythonDoc markup +# 2002-05-01 fl finished TreeBuilder refactoring +# 2002-07-14 fl added basic namespace support to ElementTree.write +# 2002-07-25 fl added QName attribute support +# 2002-10-20 fl fixed encoding in write +# 2002-11-24 fl changed default encoding to ascii; fixed attribute encoding +# 2002-11-27 fl accept file objects or file names for parse/write +# 2002-12-04 fl moved XMLTreeBuilder back to this module +# 2003-01-11 fl fixed entity encoding glitch for us-ascii +# 2003-02-13 fl added XML literal factory +# 2003-02-21 fl added ProcessingInstruction/PI factory +# 2003-05-11 fl added tostring/fromstring helpers +# 2003-05-26 fl added ElementPath support +# 2003-07-05 fl added makeelement factory method +# 2003-07-28 fl added more well-known namespace prefixes +# 2003-08-15 fl fixed typo in ElementTree.findtext (Thomas Dartsch) +# 2003-09-04 fl fall back on emulator if ElementPath is not installed +# 2003-10-31 fl markup updates +# 2003-11-15 fl fixed nested namespace bug +# 2004-03-28 fl added XMLID helper +# 2004-06-02 fl added default support to findtext +# 2004-06-08 fl fixed encoding of non-ascii element/attribute names +# 2004-08-23 fl take advantage of post-2.1 expat features +# 2005-02-01 fl added iterparse implementation +# 2005-03-02 fl fixed iterparse support for pre-2.2 versions +# +# Copyright (c) 1999-2005 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2005 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +__all__ = [ + # public symbols + "Comment", + "dump", + "Element", "ElementTree", + "fromstring", + "iselement", "iterparse", + "parse", + "PI", "ProcessingInstruction", + "QName", + "SubElement", + "tostring", + "TreeBuilder", + "VERSION", "XML", + "XMLTreeBuilder", + ] + +## +# The Element type is a flexible container object, designed to +# store hierarchical data structures in memory. The type can be +# described as a cross between a list and a dictionary. +#

+# Each element has a number of properties associated with it: +#

    +#
  • a tag. This is a string identifying what kind of data +# this element represents (the element type, in other words).
  • +#
  • a number of attributes, stored in a Python dictionary.
  • +#
  • a text string.
  • +#
  • an optional tail string.
  • +#
  • a number of child elements, stored in a Python sequence
  • +#
+# +# To create an element instance, use the {@link #Element} or {@link +# #SubElement} factory functions. +#

+# The {@link #ElementTree} class can be used to wrap an element +# structure, and convert it from and to XML. +## + +import string, sys, re + +class _SimpleElementPath: + # emulate pre-1.2 find/findtext/findall behaviour + def find(self, element, tag): + for elem in element: + if elem.tag == tag: + return elem + return None + def findtext(self, element, tag, default=None): + for elem in element: + if elem.tag == tag: + return elem.text or "" + return default + def findall(self, element, tag): + if tag[:3] == ".//": + return element.getiterator(tag[3:]) + result = [] + for elem in element: + if elem.tag == tag: + result.append(elem) + return result + +try: + import ElementPath +except ImportError: + # FIXME: issue warning in this case? + ElementPath = _SimpleElementPath() + +# TODO: add support for custom namespace resolvers/default namespaces +# TODO: add improved support for incremental parsing + +VERSION = "1.2.6" + +## +# Internal element class. This class defines the Element interface, +# and provides a reference implementation of this interface. +#

+# You should not create instances of this class directly. Use the +# appropriate factory functions instead, such as {@link #Element} +# and {@link #SubElement}. +# +# @see Element +# @see SubElement +# @see Comment +# @see ProcessingInstruction + +class _ElementInterface: + # text...tail + + ## + # (Attribute) Element tag. + + tag = None + + ## + # (Attribute) Element attribute dictionary. Where possible, use + # {@link #_ElementInterface.get}, + # {@link #_ElementInterface.set}, + # {@link #_ElementInterface.keys}, and + # {@link #_ElementInterface.items} to access + # element attributes. + + attrib = None + + ## + # (Attribute) Text before first subelement. This is either a + # string or the value None, if there was no text. + + text = None + + ## + # (Attribute) Text after this element's end tag, but before the + # next sibling element's start tag. This is either a string or + # the value None, if there was no text. + + tail = None # text after end tag, if any + + def __init__(self, tag, attrib): + self.tag = tag + self.attrib = attrib + self._children = [] + + def __repr__(self): + return "" % (self.tag, id(self)) + + ## + # Creates a new element object of the same type as this element. + # + # @param tag Element tag. + # @param attrib Element attributes, given as a dictionary. + # @return A new element instance. + + def makeelement(self, tag, attrib): + return Element(tag, attrib) + + ## + # Returns the number of subelements. + # + # @return The number of subelements. + + def __len__(self): + return len(self._children) + + ## + # Returns the given subelement. + # + # @param index What subelement to return. + # @return The given subelement. + # @exception IndexError If the given element does not exist. + + def __getitem__(self, index): + return self._children[index] + + ## + # Replaces the given subelement. + # + # @param index What subelement to replace. + # @param element The new element value. + # @exception IndexError If the given element does not exist. + # @exception AssertionError If element is not a valid object. + + def __setitem__(self, index, element): + assert iselement(element) + self._children[index] = element + + ## + # Deletes the given subelement. + # + # @param index What subelement to delete. + # @exception IndexError If the given element does not exist. + + def __delitem__(self, index): + del self._children[index] + + ## + # Returns a list containing subelements in the given range. + # + # @param start The first subelement to return. + # @param stop The first subelement that shouldn't be returned. + # @return A sequence object containing subelements. + + def __getslice__(self, start, stop): + return self._children[start:stop] + + ## + # Replaces a number of subelements with elements from a sequence. + # + # @param start The first subelement to replace. + # @param stop The first subelement that shouldn't be replaced. + # @param elements A sequence object with zero or more elements. + # @exception AssertionError If a sequence member is not a valid object. + + def __setslice__(self, start, stop, elements): + for element in elements: + assert iselement(element) + self._children[start:stop] = list(elements) + + ## + # Deletes a number of subelements. + # + # @param start The first subelement to delete. + # @param stop The first subelement to leave in there. + + def __delslice__(self, start, stop): + del self._children[start:stop] + + ## + # Adds a subelement to the end of this element. + # + # @param element The element to add. + # @exception AssertionError If a sequence member is not a valid object. + + def append(self, element): + assert iselement(element) + self._children.append(element) + + ## + # Inserts a subelement at the given position in this element. + # + # @param index Where to insert the new subelement. + # @exception AssertionError If the element is not a valid object. + + def insert(self, index, element): + assert iselement(element) + self._children.insert(index, element) + + ## + # Removes a matching subelement. Unlike the find methods, + # this method compares elements based on identity, not on tag + # value or contents. + # + # @param element What element to remove. + # @exception ValueError If a matching element could not be found. + # @exception AssertionError If the element is not a valid object. + + def remove(self, element): + assert iselement(element) + self._children.remove(element) + + ## + # Returns all subelements. The elements are returned in document + # order. + # + # @return A list of subelements. + # @defreturn list of Element instances + + def getchildren(self): + return self._children + + ## + # Finds the first matching subelement, by tag name or path. + # + # @param path What element to look for. + # @return The first matching element, or None if no element was found. + # @defreturn Element or None + + def find(self, path): + return ElementPath.find(self, path) + + ## + # Finds text for the first matching subelement, by tag name or path. + # + # @param path What element to look for. + # @param default What to return if the element was not found. + # @return The text content of the first matching element, or the + # default value no element was found. Note that if the element + # has is found, but has no text content, this method returns an + # empty string. + # @defreturn string + + def findtext(self, path, default=None): + return ElementPath.findtext(self, path, default) + + ## + # Finds all matching subelements, by tag name or path. + # + # @param path What element to look for. + # @return A list or iterator containing all matching elements, + # in document order. + # @defreturn list of Element instances + + def findall(self, path): + return ElementPath.findall(self, path) + + ## + # Resets an element. This function removes all subelements, clears + # all attributes, and sets the text and tail attributes to None. + + def clear(self): + self.attrib.clear() + self._children = [] + self.text = self.tail = None + + ## + # Gets an element attribute. + # + # @param key What attribute to look for. + # @param default What to return if the attribute was not found. + # @return The attribute value, or the default value, if the + # attribute was not found. + # @defreturn string or None + + def get(self, key, default=None): + return self.attrib.get(key, default) + + ## + # Sets an element attribute. + # + # @param key What attribute to set. + # @param value The attribute value. + + def set(self, key, value): + self.attrib[key] = value + + ## + # Gets a list of attribute names. The names are returned in an + # arbitrary order (just like for an ordinary Python dictionary). + # + # @return A list of element attribute names. + # @defreturn list of strings + + def keys(self): + return self.attrib.keys() + + ## + # Gets element attributes, as a sequence. The attributes are + # returned in an arbitrary order. + # + # @return A list of (name, value) tuples for all attributes. + # @defreturn list of (string, string) tuples + + def items(self): + return self.attrib.items() + + ## + # Creates a tree iterator. The iterator loops over this element + # and all subelements, in document order, and returns all elements + # with a matching tag. + #

+ # If the tree structure is modified during iteration, the result + # is undefined. + # + # @param tag What tags to look for (default is to return all elements). + # @return A list or iterator containing all the matching elements. + # @defreturn list or iterator + + def getiterator(self, tag=None): + nodes = [] + if tag == "*": + tag = None + if tag is None or self.tag == tag: + nodes.append(self) + for node in self._children: + nodes.extend(node.getiterator(tag)) + return nodes + +# compatibility +_Element = _ElementInterface + +## +# Element factory. This function returns an object implementing the +# standard Element interface. The exact class or type of that object +# is implementation dependent, but it will always be compatible with +# the {@link #_ElementInterface} class in this module. +#

+# The element name, attribute names, and attribute values can be +# either 8-bit ASCII strings or Unicode strings. +# +# @param tag The element name. +# @param attrib An optional dictionary, containing element attributes. +# @param **extra Additional attributes, given as keyword arguments. +# @return An element instance. +# @defreturn Element + +def Element(tag, attrib={}, **extra): + attrib = attrib.copy() + attrib.update(extra) + return _ElementInterface(tag, attrib) + +## +# Subelement factory. This function creates an element instance, and +# appends it to an existing element. +#

+# The element name, attribute names, and attribute values can be +# either 8-bit ASCII strings or Unicode strings. +# +# @param parent The parent element. +# @param tag The subelement name. +# @param attrib An optional dictionary, containing element attributes. +# @param **extra Additional attributes, given as keyword arguments. +# @return An element instance. +# @defreturn Element + +def SubElement(parent, tag, attrib={}, **extra): + attrib = attrib.copy() + attrib.update(extra) + element = parent.makeelement(tag, attrib) + parent.append(element) + return element + +## +# Comment element factory. This factory function creates a special +# element that will be serialized as an XML comment. +#

+# The comment string can be either an 8-bit ASCII string or a Unicode +# string. +# +# @param text A string containing the comment string. +# @return An element instance, representing a comment. +# @defreturn Element + +def Comment(text=None): + element = Element(Comment) + element.text = text + return element + +## +# PI element factory. This factory function creates a special element +# that will be serialized as an XML processing instruction. +# +# @param target A string containing the PI target. +# @param text A string containing the PI contents, if any. +# @return An element instance, representing a PI. +# @defreturn Element + +def ProcessingInstruction(target, text=None): + element = Element(ProcessingInstruction) + element.text = target + if text: + element.text = element.text + " " + text + return element + +PI = ProcessingInstruction + +## +# QName wrapper. This can be used to wrap a QName attribute value, in +# order to get proper namespace handling on output. +# +# @param text A string containing the QName value, in the form {uri}local, +# or, if the tag argument is given, the URI part of a QName. +# @param tag Optional tag. If given, the first argument is interpreted as +# an URI, and this argument is interpreted as a local name. +# @return An opaque object, representing the QName. + +class QName: + def __init__(self, text_or_uri, tag=None): + if tag: + text_or_uri = "{%s}%s" % (text_or_uri, tag) + self.text = text_or_uri + def __str__(self): + return self.text + def __hash__(self): + return hash(self.text) + def __cmp__(self, other): + if isinstance(other, QName): + return cmp(self.text, other.text) + return cmp(self.text, other) + +## +# ElementTree wrapper class. This class represents an entire element +# hierarchy, and adds some extra support for serialization to and from +# standard XML. +# +# @param element Optional root element. +# @keyparam file Optional file handle or name. If given, the +# tree is initialized with the contents of this XML file. + +class ElementTree: + + def __init__(self, element=None, file=None): + assert element is None or iselement(element) + self._root = element # first node + if file: + self.parse(file) + + ## + # Gets the root element for this tree. + # + # @return An element instance. + # @defreturn Element + + def getroot(self): + return self._root + + ## + # Replaces the root element for this tree. This discards the + # current contents of the tree, and replaces it with the given + # element. Use with care. + # + # @param element An element instance. + + def _setroot(self, element): + assert iselement(element) + self._root = element + + ## + # Loads an external XML document into this element tree. + # + # @param source A file name or file object. + # @param parser An optional parser instance. If not given, the + # standard {@link XMLTreeBuilder} parser is used. + # @return The document root element. + # @defreturn Element + + def parse(self, source, parser=None): + if not hasattr(source, "read"): + source = open(source, "rb") + if not parser: + parser = XMLTreeBuilder() + while 1: + data = source.read(32768) + if not data: + break + parser.feed(data) + self._root = parser.close() + return self._root + + ## + # Creates a tree iterator for the root element. The iterator loops + # over all elements in this tree, in document order. + # + # @param tag What tags to look for (default is to return all elements) + # @return An iterator. + # @defreturn iterator + + def getiterator(self, tag=None): + assert self._root is not None + return self._root.getiterator(tag) + + ## + # Finds the first toplevel element with given tag. + # Same as getroot().find(path). + # + # @param path What element to look for. + # @return The first matching element, or None if no element was found. + # @defreturn Element or None + + def find(self, path): + assert self._root is not None + if path[:1] == "/": + path = "." + path + return self._root.find(path) + + ## + # Finds the element text for the first toplevel element with given + # tag. Same as getroot().findtext(path). + # + # @param path What toplevel element to look for. + # @param default What to return if the element was not found. + # @return The text content of the first matching element, or the + # default value no element was found. Note that if the element + # has is found, but has no text content, this method returns an + # empty string. + # @defreturn string + + def findtext(self, path, default=None): + assert self._root is not None + if path[:1] == "/": + path = "." + path + return self._root.findtext(path, default) + + ## + # Finds all toplevel elements with the given tag. + # Same as getroot().findall(path). + # + # @param path What element to look for. + # @return A list or iterator containing all matching elements, + # in document order. + # @defreturn list of Element instances + + def findall(self, path): + assert self._root is not None + if path[:1] == "/": + path = "." + path + return self._root.findall(path) + + ## + # Writes the element tree to a file, as XML. + # + # @param file A file name, or a file object opened for writing. + # @param encoding Optional output encoding (default is US-ASCII). + + def write(self, file, encoding="us-ascii"): + assert self._root is not None + if not hasattr(file, "write"): + file = open(file, "wb") + if not encoding: + encoding = "us-ascii" + elif encoding != "utf-8" and encoding != "us-ascii": + file.write("\n" % encoding) + self._write(file, self._root, encoding, {}) + + def _write(self, file, node, encoding, namespaces): + # write XML to file + tag = node.tag + if tag is Comment: + file.write("" % _escape_cdata(node.text, encoding)) + elif tag is ProcessingInstruction: + file.write("" % _escape_cdata(node.text, encoding)) + else: + items = node.items() + xmlns_items = [] # new namespaces in this scope + try: + if isinstance(tag, QName) or tag[:1] == "{": + tag, xmlns = fixtag(tag, namespaces) + if xmlns: xmlns_items.append(xmlns) + except TypeError: + _raise_serialization_error(tag) + file.write("<" + _encode(tag, encoding)) + if items or xmlns_items: + items.sort() # lexical order + for k, v in items: + try: + if isinstance(k, QName) or k[:1] == "{": + k, xmlns = fixtag(k, namespaces) + if xmlns: xmlns_items.append(xmlns) + except TypeError: + _raise_serialization_error(k) + try: + if isinstance(v, QName): + v, xmlns = fixtag(v, namespaces) + if xmlns: xmlns_items.append(xmlns) + except TypeError: + _raise_serialization_error(v) + file.write(" %s=\"%s\"" % (_encode(k, encoding), + _escape_attrib(v, encoding))) + for k, v in xmlns_items: + file.write(" %s=\"%s\"" % (_encode(k, encoding), + _escape_attrib(v, encoding))) + if node.text or len(node): + file.write(">") + if node.text: + file.write(_escape_cdata(node.text, encoding)) + for n in node: + self._write(file, n, encoding, namespaces) + file.write("") + else: + file.write(" />") + for k, v in xmlns_items: + del namespaces[v] + if node.tail: + file.write(_escape_cdata(node.tail, encoding)) + +# -------------------------------------------------------------------- +# helpers + +## +# Checks if an object appears to be a valid element object. +# +# @param An element instance. +# @return A true value if this is an element object. +# @defreturn flag + +def iselement(element): + # FIXME: not sure about this; might be a better idea to look + # for tag/attrib/text attributes + return isinstance(element, _ElementInterface) or hasattr(element, "tag") + +## +# Writes an element tree or element structure to sys.stdout. This +# function should be used for debugging only. +#

+# The exact output format is implementation dependent. In this +# version, it's written as an ordinary XML file. +# +# @param elem An element tree or an individual element. + +def dump(elem): + # debugging + if not isinstance(elem, ElementTree): + elem = ElementTree(elem) + elem.write(sys.stdout) + tail = elem.getroot().tail + if not tail or tail[-1] != "\n": + sys.stdout.write("\n") + +def _encode(s, encoding): + try: + return s.encode(encoding) + except AttributeError: + return s # 1.5.2: assume the string uses the right encoding + +if sys.version[:3] == "1.5": + _escape = re.compile(r"[&<>\"\x80-\xff]+") # 1.5.2 +else: + _escape = re.compile(eval(r'u"[&<>\"\u0080-\uffff]+"')) + +_escape_map = { + "&": "&", + "<": "<", + ">": ">", + '"': """, +} + +_namespace_map = { + # "well-known" namespace prefixes + "http://www.w3.org/XML/1998/namespace": "xml", + "http://www.w3.org/1999/xhtml": "html", + "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf", + "http://schemas.xmlsoap.org/wsdl/": "wsdl", +} + +def _raise_serialization_error(text): + raise TypeError( + "cannot serialize %r (type %s)" % (text, type(text).__name__) + ) + +def _encode_entity(text, pattern=_escape): + # map reserved and non-ascii characters to numerical entities + def escape_entities(m, map=_escape_map): + out = [] + append = out.append + for char in m.group(): + text = map.get(char) + if text is None: + text = "&#%d;" % ord(char) + append(text) + return string.join(out, "") + try: + return _encode(pattern.sub(escape_entities, text), "ascii") + except TypeError: + _raise_serialization_error(text) + +# +# the following functions assume an ascii-compatible encoding +# (or "utf-16") + +def _escape_cdata(text, encoding=None, replace=string.replace): + # escape character data + try: + if encoding: + try: + text = _encode(text, encoding) + except UnicodeError: + return _encode_entity(text) + text = replace(text, "&", "&") + text = replace(text, "<", "<") + text = replace(text, ">", ">") + return text + except (TypeError, AttributeError): + _raise_serialization_error(text) + +def _escape_attrib(text, encoding=None, replace=string.replace): + # escape attribute value + try: + if encoding: + try: + text = _encode(text, encoding) + except UnicodeError: + return _encode_entity(text) + text = replace(text, "&", "&") + text = replace(text, "'", "'") # FIXME: overkill + text = replace(text, "\"", """) + text = replace(text, "<", "<") + text = replace(text, ">", ">") + return text + except (TypeError, AttributeError): + _raise_serialization_error(text) + +def fixtag(tag, namespaces): + # given a decorated tag (of the form {uri}tag), return prefixed + # tag and namespace declaration, if any + if isinstance(tag, QName): + tag = tag.text + namespace_uri, tag = string.split(tag[1:], "}", 1) + prefix = namespaces.get(namespace_uri) + if prefix is None: + prefix = _namespace_map.get(namespace_uri) + if prefix is None: + prefix = "ns%d" % len(namespaces) + namespaces[namespace_uri] = prefix + if prefix == "xml": + xmlns = None + else: + xmlns = ("xmlns:%s" % prefix, namespace_uri) + else: + xmlns = None + return "%s:%s" % (prefix, tag), xmlns + +## +# Parses an XML document into an element tree. +# +# @param source A filename or file object containing XML data. +# @param parser An optional parser instance. If not given, the +# standard {@link XMLTreeBuilder} parser is used. +# @return An ElementTree instance + +def parse(source, parser=None): + tree = ElementTree() + tree.parse(source, parser) + return tree + +## +# Parses an XML document into an element tree incrementally, and reports +# what's going on to the user. +# +# @param source A filename or file object containing XML data. +# @param events A list of events to report back. If omitted, only "end" +# events are reported. +# @return A (event, elem) iterator. + +class iterparse: + + def __init__(self, source, events=None): + if not hasattr(source, "read"): + source = open(source, "rb") + self._file = source + self._events = [] + self._index = 0 + self.root = self._root = None + self._parser = XMLTreeBuilder() + # wire up the parser for event reporting + parser = self._parser._parser + append = self._events.append + if events is None: + events = ["end"] + for event in events: + if event == "start": + try: + parser.ordered_attributes = 1 + parser.specified_attributes = 1 + def handler(tag, attrib_in, event=event, append=append, + start=self._parser._start_list): + append((event, start(tag, attrib_in))) + parser.StartElementHandler = handler + except AttributeError: + def handler(tag, attrib_in, event=event, append=append, + start=self._parser._start): + append((event, start(tag, attrib_in))) + parser.StartElementHandler = handler + elif event == "end": + def handler(tag, event=event, append=append, + end=self._parser._end): + append((event, end(tag))) + parser.EndElementHandler = handler + elif event == "start-ns": + def handler(prefix, uri, event=event, append=append): + try: + uri = _encode(uri, "ascii") + except UnicodeError: + pass + append((event, (prefix or "", uri))) + parser.StartNamespaceDeclHandler = handler + elif event == "end-ns": + def handler(prefix, event=event, append=append): + append((event, None)) + parser.EndNamespaceDeclHandler = handler + + def next(self): + while 1: + try: + item = self._events[self._index] + except IndexError: + if self._parser is None: + self.root = self._root + try: + raise StopIteration + except NameError: + raise IndexError + # load event buffer + del self._events[:] + self._index = 0 + data = self._file.read(16384) + if data: + self._parser.feed(data) + else: + self._root = self._parser.close() + self._parser = None + else: + self._index = self._index + 1 + return item + + try: + iter + def __iter__(self): + return self + except NameError: + def __getitem__(self, index): + return self.next() + +## +# Parses an XML document from a string constant. This function can +# be used to embed "XML literals" in Python code. +# +# @param source A string containing XML data. +# @return An Element instance. +# @defreturn Element + +def XML(text): + parser = XMLTreeBuilder() + parser.feed(text) + return parser.close() + +## +# Parses an XML document from a string constant, and also returns +# a dictionary which maps from element id:s to elements. +# +# @param source A string containing XML data. +# @return A tuple containing an Element instance and a dictionary. +# @defreturn (Element, dictionary) + +def XMLID(text): + parser = XMLTreeBuilder() + parser.feed(text) + tree = parser.close() + ids = {} + for elem in tree.getiterator(): + id = elem.get("id") + if id: + ids[id] = elem + return tree, ids + +## +# Parses an XML document from a string constant. Same as {@link #XML}. +# +# @def fromstring(text) +# @param source A string containing XML data. +# @return An Element instance. +# @defreturn Element + +fromstring = XML + +## +# Generates a string representation of an XML element, including all +# subelements. +# +# @param element An Element instance. +# @return An encoded string containing the XML data. +# @defreturn string + +def tostring(element, encoding=None): + class dummy: + pass + data = [] + file = dummy() + file.write = data.append + ElementTree(element).write(file, encoding) + return string.join(data, "") + +## +# Generic element structure builder. This builder converts a sequence +# of {@link #TreeBuilder.start}, {@link #TreeBuilder.data}, and {@link +# #TreeBuilder.end} method calls to a well-formed element structure. +#

+# You can use this class to build an element structure using a custom XML +# parser, or a parser for some other XML-like format. +# +# @param element_factory Optional element factory. This factory +# is called to create new Element instances, as necessary. + +class TreeBuilder: + + def __init__(self, element_factory=None): + self._data = [] # data collector + self._elem = [] # element stack + self._last = None # last element + self._tail = None # true if we're after an end tag + if element_factory is None: + element_factory = _ElementInterface + self._factory = element_factory + + ## + # Flushes the parser buffers, and returns the toplevel documen + # element. + # + # @return An Element instance. + # @defreturn Element + + def close(self): + assert len(self._elem) == 0, "missing end tags" + assert self._last != None, "missing toplevel element" + return self._last + + def _flush(self): + if self._data: + if self._last is not None: + text = string.join(self._data, "") + if self._tail: + assert self._last.tail is None, "internal error (tail)" + self._last.tail = text + else: + assert self._last.text is None, "internal error (text)" + self._last.text = text + self._data = [] + + ## + # Adds text to the current element. + # + # @param data A string. This should be either an 8-bit string + # containing ASCII text, or a Unicode string. + + def data(self, data): + self._data.append(data) + + ## + # Opens a new element. + # + # @param tag The element name. + # @param attrib A dictionary containing element attributes. + # @return The opened element. + # @defreturn Element + + def start(self, tag, attrs): + self._flush() + self._last = elem = self._factory(tag, attrs) + if self._elem: + self._elem[-1].append(elem) + self._elem.append(elem) + self._tail = 0 + return elem + + ## + # Closes the current element. + # + # @param tag The element name. + # @return The closed element. + # @defreturn Element + + def end(self, tag): + self._flush() + self._last = self._elem.pop() + assert self._last.tag == tag,\ + "end tag mismatch (expected %s, got %s)" % ( + self._last.tag, tag) + self._tail = 1 + return self._last + +## +# Element structure builder for XML source data, based on the +# expat parser. +# +# @keyparam target Target object. If omitted, the builder uses an +# instance of the standard {@link #TreeBuilder} class. +# @keyparam html Predefine HTML entities. This flag is not supported +# by the current implementation. +# @see #ElementTree +# @see #TreeBuilder + +class XMLTreeBuilder: + + def __init__(self, html=0, target=None): + try: + from xml.parsers import expat + except ImportError: + raise ImportError( + "No module named expat; use SimpleXMLTreeBuilder instead" + ) + self._parser = parser = expat.ParserCreate(None, "}") + if target is None: + target = TreeBuilder() + self._target = target + self._names = {} # name memo cache + # callbacks + parser.DefaultHandlerExpand = self._default + parser.StartElementHandler = self._start + parser.EndElementHandler = self._end + parser.CharacterDataHandler = self._data + # let expat do the buffering, if supported + try: + self._parser.buffer_text = 1 + except AttributeError: + pass + # use new-style attribute handling, if supported + try: + self._parser.ordered_attributes = 1 + self._parser.specified_attributes = 1 + parser.StartElementHandler = self._start_list + except AttributeError: + pass + encoding = None + if not parser.returns_unicode: + encoding = "utf-8" + # target.xml(encoding, None) + self._doctype = None + self.entity = {} + + def _fixtext(self, text): + # convert text string to ascii, if possible + try: + return _encode(text, "ascii") + except UnicodeError: + return text + + def _fixname(self, key): + # expand qname, and convert name string to ascii, if possible + try: + name = self._names[key] + except KeyError: + name = key + if "}" in name: + name = "{" + name + self._names[key] = name = self._fixtext(name) + return name + + def _start(self, tag, attrib_in): + fixname = self._fixname + tag = fixname(tag) + attrib = {} + for key, value in attrib_in.items(): + attrib[fixname(key)] = self._fixtext(value) + return self._target.start(tag, attrib) + + def _start_list(self, tag, attrib_in): + fixname = self._fixname + tag = fixname(tag) + attrib = {} + if attrib_in: + for i in range(0, len(attrib_in), 2): + attrib[fixname(attrib_in[i])] = self._fixtext(attrib_in[i+1]) + return self._target.start(tag, attrib) + + def _data(self, text): + return self._target.data(self._fixtext(text)) + + def _end(self, tag): + return self._target.end(self._fixname(tag)) + + def _default(self, text): + prefix = text[:1] + if prefix == "&": + # deal with undefined entities + try: + self._target.data(self.entity[text[1:-1]]) + except KeyError: + from xml.parsers import expat + raise expat.error( + "undefined entity %s: line %d, column %d" % + (text, self._parser.ErrorLineNumber, + self._parser.ErrorColumnNumber) + ) + elif prefix == "<" and text[:9] == "": + self._doctype = None + return + text = string.strip(text) + if not text: + return + self._doctype.append(text) + n = len(self._doctype) + if n > 2: + type = self._doctype[1] + if type == "PUBLIC" and n == 4: + name, type, pubid, system = self._doctype + elif type == "SYSTEM" and n == 3: + name, type, system = self._doctype + pubid = None + else: + return + if pubid: + pubid = pubid[1:-1] + self.doctype(name, pubid, system[1:-1]) + self._doctype = None + + ## + # Handles a doctype declaration. + # + # @param name Doctype name. + # @param pubid Public identifier. + # @param system System identifier. + + def doctype(self, name, pubid, system): + pass + + ## + # Feeds data to the parser. + # + # @param data Encoded data. + + def feed(self, data): + self._parser.Parse(data, 0) + + ## + # Finishes feeding data to the parser. + # + # @return An element structure. + # @defreturn Element + + def close(self): + self._parser.Parse("", 1) # end of data + tree = self._target.close() + del self._target, self._parser # get rid of circular references + return tree diff --git a/src/python/uts/util/etree/HTMLTreeBuilder.py b/src/python/uts/util/etree/HTMLTreeBuilder.py new file mode 100644 index 00000000..dd9a5e2c --- /dev/null +++ b/src/python/uts/util/etree/HTMLTreeBuilder.py @@ -0,0 +1,230 @@ +# +# ElementTree +# $Id: HTMLTreeBuilder.py 2325 2005-03-16 15:50:43Z fredrik $ +# +# a simple tree builder, for HTML input +# +# history: +# 2002-04-06 fl created +# 2002-04-07 fl ignore IMG and HR end tags +# 2002-04-07 fl added support for 1.5.2 and later +# 2003-04-13 fl added HTMLTreeBuilder alias +# 2004-12-02 fl don't feed non-ASCII charrefs/entities as 8-bit strings +# 2004-12-05 fl don't feed non-ASCII CDATA as 8-bit strings +# +# Copyright (c) 1999-2004 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2004 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +## +# Tools to build element trees from HTML files. +## + +import htmlentitydefs +import re, string, sys +import mimetools, StringIO + +import ElementTree + +AUTOCLOSE = "p", "li", "tr", "th", "td", "head", "body" +IGNOREEND = "img", "hr", "meta", "link", "br" + +if sys.version[:3] == "1.5": + is_not_ascii = re.compile(r"[\x80-\xff]").search # 1.5.2 +else: + is_not_ascii = re.compile(eval(r'u"[\u0080-\uffff]"')).search + +try: + from HTMLParser import HTMLParser +except ImportError: + from sgmllib import SGMLParser + # hack to use sgmllib's SGMLParser to emulate 2.2's HTMLParser + class HTMLParser(SGMLParser): + # the following only works as long as this class doesn't + # provide any do, start, or end handlers + def unknown_starttag(self, tag, attrs): + self.handle_starttag(tag, attrs) + def unknown_endtag(self, tag): + self.handle_endtag(tag) + +## +# ElementTree builder for HTML source code. This builder converts an +# HTML document or fragment to an ElementTree. +#

+# The parser is relatively picky, and requires balanced tags for most +# elements. However, elements belonging to the following group are +# automatically closed: P, LI, TR, TH, and TD. In addition, the +# parser automatically inserts end tags immediately after the start +# tag, and ignores any end tags for the following group: IMG, HR, +# META, and LINK. +# +# @keyparam builder Optional builder object. If omitted, the parser +# uses the standard elementtree builder. +# @keyparam encoding Optional character encoding, if known. If omitted, +# the parser looks for META tags inside the document. If no tags +# are found, the parser defaults to ISO-8859-1. Note that if your +# document uses a non-ASCII compatible encoding, you must decode +# the document before parsing. +# +# @see elementtree.ElementTree + +class HTMLTreeBuilder(HTMLParser): + + # FIXME: shouldn't this class be named Parser, not Builder? + + def __init__(self, builder=None, encoding=None): + self.__stack = [] + if builder is None: + builder = ElementTree.TreeBuilder() + self.__builder = builder + self.encoding = encoding or "iso-8859-1" + HTMLParser.__init__(self) + + ## + # Flushes parser buffers, and return the root element. + # + # @return An Element instance. + + def close(self): + HTMLParser.close(self) + return self.__builder.close() + + ## + # (Internal) Handles start tags. + + def handle_starttag(self, tag, attrs): + if tag == "meta": + # look for encoding directives + http_equiv = content = None + for k, v in attrs: + if k == "http-equiv": + http_equiv = string.lower(v) + elif k == "content": + content = v + if http_equiv == "content-type" and content: + # use mimetools to parse the http header + header = mimetools.Message( + StringIO.StringIO("%s: %s\n\n" % (http_equiv, content)) + ) + encoding = header.getparam("charset") + if encoding: + self.encoding = encoding + if tag in AUTOCLOSE: + if self.__stack and self.__stack[-1] == tag: + self.handle_endtag(tag) + self.__stack.append(tag) + attrib = {} + if attrs: + for k, v in attrs: + attrib[string.lower(k)] = v + self.__builder.start(tag, attrib) + if tag in IGNOREEND: + self.__stack.pop() + self.__builder.end(tag) + + ## + # (Internal) Handles end tags. + + def handle_endtag(self, tag): + if tag in IGNOREEND: + return + lasttag = self.__stack.pop() + if tag != lasttag and lasttag in AUTOCLOSE: + self.handle_endtag(lasttag) + self.__builder.end(tag) + + ## + # (Internal) Handles character references. + + def handle_charref(self, char): + if char[:1] == "x": + char = int(char[1:], 16) + else: + char = int(char) + if 0 <= char < 128: + self.__builder.data(chr(char)) + else: + self.__builder.data(unichr(char)) + + ## + # (Internal) Handles entity references. + + def handle_entityref(self, name): + entity = htmlentitydefs.entitydefs.get(name) + if entity: + if len(entity) == 1: + entity = ord(entity) + else: + entity = int(entity[2:-1]) + if 0 <= entity < 128: + self.__builder.data(chr(entity)) + else: + self.__builder.data(unichr(entity)) + else: + self.unknown_entityref(name) + + ## + # (Internal) Handles character data. + + def handle_data(self, data): + if isinstance(data, type('')) and is_not_ascii(data): + # convert to unicode, but only if necessary + data = unicode(data, self.encoding, "ignore") + self.__builder.data(data) + + ## + # (Hook) Handles unknown entity references. The default action + # is to ignore unknown entities. + + def unknown_entityref(self, name): + pass # ignore by default; override if necessary + +## +# An alias for the HTMLTreeBuilder class. + +TreeBuilder = HTMLTreeBuilder + +## +# Parse an HTML document or document fragment. +# +# @param source A filename or file object containing HTML data. +# @param encoding Optional character encoding, if known. If omitted, +# the parser looks for META tags inside the document. If no tags +# are found, the parser defaults to ISO-8859-1. +# @return An ElementTree instance + +def parse(source, encoding=None): + return ElementTree.parse(source, HTMLTreeBuilder(encoding=encoding)) + +if __name__ == "__main__": + import sys + ElementTree.dump(parse(open(sys.argv[1]))) diff --git a/src/python/uts/util/etree/SgmlopXMLTreeBuilder.py b/src/python/uts/util/etree/SgmlopXMLTreeBuilder.py new file mode 100644 index 00000000..44ef10ee --- /dev/null +++ b/src/python/uts/util/etree/SgmlopXMLTreeBuilder.py @@ -0,0 +1,103 @@ +# +# ElementTree +# $Id$ +# +# A simple XML tree builder, based on the sgmlop library. +# +# Note that this version does not support namespaces. This may be +# changed in future versions. +# +# history: +# 2004-03-28 fl created +# +# Copyright (c) 1999-2004 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2004 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +## +# Tools to build element trees from XML, based on the SGMLOP parser. +#

+# The current version does not support XML namespaces. +#

+# This tree builder requires the sgmlop extension module +# (available from +# http://effbot.org/downloads). +## + +import ElementTree + +## +# ElementTree builder for XML source data, based on the SGMLOP parser. +# +# @see elementtree.ElementTree + +class TreeBuilder: + + def __init__(self, html=0): + try: + import sgmlop + except ImportError: + raise RuntimeError("sgmlop parser not available") + self.__builder = ElementTree.TreeBuilder() + if html: + import htmlentitydefs + self.entitydefs.update(htmlentitydefs.entitydefs) + self.__parser = sgmlop.XMLParser() + self.__parser.register(self) + + ## + # Feeds data to the parser. + # + # @param data Encoded data. + + def feed(self, data): + self.__parser.feed(data) + + ## + # Finishes feeding data to the parser. + # + # @return An element structure. + # @defreturn Element + + def close(self): + self.__parser.close() + self.__parser = None + return self.__builder.close() + + def finish_starttag(self, tag, attrib): + self.__builder.start(tag, attrib) + + def finish_endtag(self, tag): + self.__builder.end(tag) + + def handle_data(self, data): + self.__builder.data(data) diff --git a/src/python/uts/util/etree/SimpleXMLTreeBuilder.py b/src/python/uts/util/etree/SimpleXMLTreeBuilder.py new file mode 100644 index 00000000..d071a141 --- /dev/null +++ b/src/python/uts/util/etree/SimpleXMLTreeBuilder.py @@ -0,0 +1,144 @@ +# +# ElementTree +# $Id: SimpleXMLTreeBuilder.py 1862 2004-06-18 07:31:02Z Fredrik $ +# +# A simple XML tree builder, based on Python's xmllib +# +# Note that due to bugs in xmllib, this builder does not fully support +# namespaces (unqualified attributes are put in the default namespace, +# instead of being left as is). Run this module as a script to find +# out if this affects your Python version. +# +# history: +# 2001-10-20 fl created +# 2002-05-01 fl added namespace support for xmllib +# 2002-08-17 fl added xmllib sanity test +# +# Copyright (c) 1999-2004 by Fredrik Lundh. All rights reserved. +# +# fredrik@pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The ElementTree toolkit is +# +# Copyright (c) 1999-2004 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +## +# Tools to build element trees from XML files, using xmllib. +# This module can be used instead of the standard tree builder, for +# Python versions where "expat" is not available (such as 1.5.2). +#

+# Note that due to bugs in xmllib, the namespace support is +# not reliable (you can run the module as a script to find out exactly +# how unreliable it is on your Python version). +## + +import xmllib, string + +import ElementTree + +## +# ElementTree builder for XML source data. +# +# @see elementtree.ElementTree + +class TreeBuilder(xmllib.XMLParser): + + def __init__(self, html=0): + self.__builder = ElementTree.TreeBuilder() + if html: + import htmlentitydefs + self.entitydefs.update(htmlentitydefs.entitydefs) + xmllib.XMLParser.__init__(self) + + ## + # Feeds data to the parser. + # + # @param data Encoded data. + + def feed(self, data): + xmllib.XMLParser.feed(self, data) + + ## + # Finishes feeding data to the parser. + # + # @return An element structure. + # @defreturn Element + + def close(self): + xmllib.XMLParser.close(self) + return self.__builder.close() + + def handle_data(self, data): + self.__builder.data(data) + + handle_cdata = handle_data + + def unknown_starttag(self, tag, attrs): + attrib = {} + for key, value in attrs.items(): + attrib[fixname(key)] = value + self.__builder.start(fixname(tag), attrib) + + def unknown_endtag(self, tag): + self.__builder.end(fixname(tag)) + + +def fixname(name, split=string.split): + # xmllib in 2.0 and later provides limited (and slightly broken) + # support for XML namespaces. + if " " not in name: + return name + return "{%s}%s" % tuple(split(name, " ", 1)) + + +if __name__ == "__main__": + import sys + # sanity check: look for known namespace bugs in xmllib + p = TreeBuilder() + text = """\ + + + + """ + p.feed(text) + tree = p.close() + status = [] + # check for bugs in the xmllib implementation + tag = tree.find("{default}tag") + if tag is None: + status.append("namespaces not supported") + if tag is not None and tag.get("{default}attribute"): + status.append("default namespace applied to unqualified attribute") + # report bugs + if status: + print "xmllib doesn't work properly in this Python version:" + for bug in status: + print "-", bug + else: + print "congratulations; no problems found in xmllib" + diff --git a/src/python/uts/util/etree/SimpleXMLWriter.py b/src/python/uts/util/etree/SimpleXMLWriter.py new file mode 100644 index 00000000..af3023f4 --- /dev/null +++ b/src/python/uts/util/etree/SimpleXMLWriter.py @@ -0,0 +1,279 @@ +# +# SimpleXMLWriter +# $Id: SimpleXMLWriter.py 2312 2005-03-02 18:13:39Z fredrik $ +# +# a simple XML writer +# +# history: +# 2001-12-28 fl created +# 2002-11-25 fl fixed attribute encoding +# 2002-12-02 fl minor fixes for 1.5.2 +# 2004-06-17 fl added pythondoc markup +# 2004-07-23 fl added flush method (from Jay Graves) +# 2004-10-03 fl added declaration method +# +# Copyright (c) 2001-2004 by Fredrik Lundh +# +# fredrik@pythonware.com +# http://www.pythonware.com +# +# -------------------------------------------------------------------- +# The SimpleXMLWriter module is +# +# Copyright (c) 2001-2004 by Fredrik Lundh +# +# By obtaining, using, and/or copying this software and/or its +# associated documentation, you agree that you have read, understood, +# and will comply with the following terms and conditions: +# +# Permission to use, copy, modify, and distribute this software and +# its associated documentation for any purpose and without fee is +# hereby granted, provided that the above copyright notice appears in +# all copies, and that both that copyright notice and this permission +# notice appear in supporting documentation, and that the name of +# Secret Labs AB or the author not be used in advertising or publicity +# pertaining to distribution of the software without specific, written +# prior permission. +# +# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- +# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR +# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# -------------------------------------------------------------------- + +## +# Tools to write XML files, without having to deal with encoding +# issues, well-formedness, etc. +#

+# The current version does not provide built-in support for +# namespaces. To create files using namespaces, you have to provide +# "xmlns" attributes and explicitly add prefixes to tags and +# attributes. +# +#


"; + + foreach ($included_files as $filename) { + echo "$filename
"; + } +} + +?> + diff --git a/src/www/admin/login.php b/src/www/admin/login.php new file mode 100644 index 00000000..7bb4d6dc --- /dev/null +++ b/src/www/admin/login.php @@ -0,0 +1,70 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>query($sql); + +if(PEAR::isError($res)) { + Header("Location: " . getError("index.php", "Erro ao tentar acessar o banco de dados.")); + exit(0); +} + +if(!$res->numRows()) { + Header("Location: " . getError("index.php", "Usuário ou senha inválidos.")); + exit(0); +} + +$res->fetchInto($data, DB_FETCHMODE_ASSOC); + +if( ($_POST['admin'] == trim($data['admin'])) && (md5(trim($_POST['admin_pass'])) == $data['admin_pass']) ) { + + session_start(); + + $_SESSION['admin'] = 1; + $_SESSION['inicio'] = strftime("%Y%m%d-%H%M%Z", time()); + + Header("Location: home.php"); + exit(0); + +} else { + + Header("Location: " . getError("index.php", "Usuário ou senha inválidos.")); + exit(0); + +} + +?> + + + + + + + + + diff --git a/src/www/admin/logout.php b/src/www/admin/logout.php new file mode 100644 index 00000000..2af1fe50 --- /dev/null +++ b/src/www/admin/logout.php @@ -0,0 +1,40 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> + + + + + + + + + diff --git a/src/www/admin/root.php b/src/www/admin/root.php new file mode 100644 index 00000000..20385c4e --- /dev/null +++ b/src/www/admin/root.php @@ -0,0 +1,26 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> diff --git a/src/www/admin/setup.php b/src/www/admin/setup.php new file mode 100644 index 00000000..c79138ad --- /dev/null +++ b/src/www/admin/setup.php @@ -0,0 +1,202 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>query($sql); + +//echo $res->numRows(); + +if($res->numRows()) { + + Header("Location: index.php"); + +} + +if($_GET['action'] == "setup") { + + extract($_POST); + + $sql = "INSERT INTO setup VALUES('" . trim($admin) . "', '" . md5(trim($admin_pass)) . "')"; + $res =& $db->query($sql); + + Header("Location: index.php"); +} + +?> + + + + +Telescópios na Escola ::: Administração ::: +Setup + + + + + + + + +
Telescópios na Escola
+
+
+
+

Instrucoes:

+Digite abaixo o nome do usuário que irá administrar o +sistema de agendamento. Este nome deverá ter no mínicio 6 +e no máximo 40 caracteres. Nenhum caractere "estranho" é +aceito (@#$%&*), apenas letras (maiúsculas e +minúsculas são tratadas de forma diferente) e +números. Não use acentuação. O mesmo vale para +a senha.
+
+ + + + + + + + + + + + + +
usuário
senha
 
+
+
+
+
+ + + + + + + + + +
+
+"INPE"
+INPE
+
+
+"UFRGS"
+UFRGS
+
+
+"UFRJ"
+UFRJ
+
+
+"UFRN"
+UFRN
+
+
+"UFSC"
+UFSC
+
+
+"USP"
+USP
+
+ + + + +
+

+APOIO:
+
+ + + + + + + + + +
+
+
  +
VITAE
+
+
CNPQ
+
  
+ + diff --git a/src/www/admin/time-add.php b/src/www/admin/time-add.php new file mode 100644 index 00000000..8c97b989 --- /dev/null +++ b/src/www/admin/time-add.php @@ -0,0 +1,98 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>nextID(UTS_TIME_SEQ); + +$inicio = mktime($inicio_hora, $inicio_min, 0, $inicio_mes, $inicio_dia, $inicio_ano); +$fim = mktime($fim_hora, $fim_min, 0, $fim_mes, $fim_dia, $fim_ano); + + +if($inicio > $fim) { + Header("Location: " . getError("user.php?id=$id", "O período selecionado é inválido (ele termina antes de começar :-)")); + exit(3); +} else if($inicio == $fim) { + Header("Location: " . getError("user.php?id=$id", "O período selecionado é inválido (ele não dura nem 1 segundo :-)")); + exit(3); +} + +// checa possivel conflito + +$sql = "SELECT inicio, fim FROM user_sched WHERE ( (inicio >= $inicio AND inicio <= $fim) OR (fim >= $inicio AND fim <= $fim) )"; + +$res =& $db->query($sql); + +if (DB::isError($res)) { + Header("Location: " . getError("user.php?id=$id", "Houve um erro ao tentar alocar tempo ao usuário. Tente novamente.")); + exit(1); +} + +if($res->numRows()) { + Header("Location: " . getError("user.php?id=$id", "O período selecionado já está ocupado.")); // conflito + exit(0); +} + +// sem conflitos, continua... + +$sql = "INSERT INTO user_sched VALUES($id, $newID, $inicio, $fim)"; + +$res =& $db->query($sql); + +if (DB::isError($res)) { + Header("Location: " . getError("user.php?id=$id", "Houve um erro ao tentar alocar tempo ao usuário. Tente novamente.")); + exit(1); +} + +if($db->affectedRows()) { + Header("Location: " . getMsg("user.php?id=$id", "O período foi alocado com sucesso.")); + exit(0); +} else { + Header("Location: " . getError("user.php?id=$id", "Houve um erro ao tentar alocar tempo ao usuário. Tente novamente.")); + exit(1); +} + +?> + + + + + + + + + diff --git a/src/www/admin/time-del.php b/src/www/admin/time-del.php new file mode 100644 index 00000000..580ab6be --- /dev/null +++ b/src/www/admin/time-del.php @@ -0,0 +1,59 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>query("DELETE FROM user_sched WHERE id = " . $_GET['id']); + +if (DB::isError($res)) { + + Header("Location: " . getError("user.php?id=" . $_GET['user_id'], "Houce um erro ao tentar remover o período. Tente novamente.")); + exit(1); + +} else { + + Header("Location: " . getMsg("user.php?id=" . $_GET['user_id'], "O período foi removido com sucesso.")); + exit(0); + +} + +?> + + + + + + + + + diff --git a/src/www/admin/user-add.php b/src/www/admin/user-add.php new file mode 100644 index 00000000..2562ef70 --- /dev/null +++ b/src/www/admin/user-add.php @@ -0,0 +1,130 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>query($sql); + + if($res->numRows() > 1) { // o usuario ja existe e nao podera ser adicionado outro + Header("Location: ". getError("user.php?id=$id", "O usuário '$username' já¡ existe.
Por favor, escolha outro nome.")); + exit(10); + } + + // checa privilegios de administrador + if(!$root) { + $root = 0; + } else { + $root = 1; + } + + if(trim($passwd)) { // trocar a senha + $sql = "UPDATE users SET nome = '$nome', username = '$username', root = $root, passwd = '" . md5($passwd) . "' WHERE id = $id"; + } else { + $sql = "UPDATE users SET nome = '$nome', username = '$username', root = $root WHERE id = $id"; + } + + $res =& $db->query($sql); + + if (DB::isError($res)) { + Header("Location: " . getError("user.php?id=$id", "Houve um erro ao tentar atualizar o registro. Tente novamente.")); + exit(4); + } + + // BUG FIXME (quando nada eh modificado, ele retorna 0 a seguir, e parece que houve um erro, qdo na verdade nao houve! + + if($db->affectedRows()) { + Header("Location: " . getMsg("user.php?id=$id", "Registro atualizado com sucesso.")); + exit(0); + } else { + Header("Location: " . getError("user.php?id=$id", "Houve um erro ao tentar atualizar o registro. Tente novamente.")); + exit(4); + } + + + +} else { // adicionar + + $sql = "SELECT username FROM users WHERE username = '$username'"; + $res =& $db->query($sql); + + $newID = $db->nextID(UTS_USER_SEQ); + + if($res->numRows()) { // o usuario ja existe e nao podera ser adicionado outro + Header("Location: ". getError("user.php?id=$id", "O usuário '$username' já existe.
Por favor, escolha outro nome.")); + exit(10); + } + + // checa privilegios de administrador + if(!$root) { + $root = 0; + } else { + $root = 1; + } + + $sql = "INSERT INTO users VALUES($newID, '$nome', '$username', $root, '" . md5($passwd) . "')"; + + $res =& $db->query($sql); + + if (DB::isError($res)) { + Header("Location: " . getError("user.php?id=$newID", "Houve um erro ao tentar adicionar o usuário. Tente novamente.")); + exit(3); + } + + if($db->affectedRows()) { + Header("Location: " . getMsg("user.php?id=$newID", "Usuário '$username' adicionado com sucesso..")); + exit(0); + } else { + Header("Location: " . getError("user.php?id=$newID", "Houve um erro ao tentar adicionar o usuário. Tente novamente.")); + exit(3); + } + +} + +?> + + + + + + + + + diff --git a/src/www/admin/user-del.php b/src/www/admin/user-del.php new file mode 100644 index 00000000..6b3be4e0 --- /dev/null +++ b/src/www/admin/user-del.php @@ -0,0 +1,57 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>query("DELETE FROM users WHERE id = " . $_GET['id']); +$res2 =& $db->query("DELETE FROM user_sched WHERE user_id = " . $_GET['id']); + +if ( (PEAR::isError($res)) || (PEAR::isError($res2)) ) { + Header("Location: " . getError("home.php?erro=1", "Houve um erro ao tentar remover o usuário. Tente novamente.")); + exit(1); +} else { + Header("Location: " . getMsg("home.php", "Usuário removido com sucesso.")); + exit(0); +} + +?> + + + + + + + + + diff --git a/src/www/admin/user.php b/src/www/admin/user.php new file mode 100644 index 00000000..681e5a0a --- /dev/null +++ b/src/www/admin/user.php @@ -0,0 +1,320 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>query("SELECT * FROM users WHERE id = " . intval($_GET['id'])); + + if ( (!DB::isError($res)) && ($res->numRows()) ) + $res->fetchInto($data, DB_FETCHMODE_ASSOC); + +} + +?> + + + + +Telescópios na Escola ::: Administração +::: Usuários + + + + + + + + +
Telescópios na Escola
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + +
Nome
Usuario
Senha
Super-usuário?>
+
+ + +
+

Tempo alocado

+
+ + + + + + + + + + + + + + + + + +
Início + + + + + + + +
Fim + + + + + + + +
+
+
+ + + + + + + + + +query("SELECT * FROM user_sched WHERE user_id = " . $_GET['id'] . " ORDER BY inicio DESC"); + +if (DB::isError($res)) { + die($res->getMessage()); +} + +if($res->numRows()) { + + $i = 0; + while($res->fetchInto($row, DB_FETCHMODE_ASSOC)) { + + if($i % 2) { + +?> + + + + + + + + + + +disconnect(); + +?>
InicioFimOpcoes
+ +Remover
Nenhum tempo alocado
+ + + + + + diff --git a/src/www/apontar-orch.php b/src/www/apontar-orch.php new file mode 100644 index 00000000..1354c1b4 --- /dev/null +++ b/src/www/apontar-orch.php @@ -0,0 +1,81 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> + + + + +Observatórios Virtuais ::: Apontar e Integrar + + + + + +

::: voltar :::

+ + diff --git a/src/www/apontar-uts.php b/src/www/apontar-uts.php new file mode 100644 index 00000000..a960ec01 --- /dev/null +++ b/src/www/apontar-uts.php @@ -0,0 +1,161 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>setInteractive(FALSE); + +if(!$ccd->connect()) {; + Header("Location: " . getError("formApontar.php?" . $_SERVER['QUERY_STRING'], "O Observatório não está disponível. Consulte o responsável. (SERVER_FULL)")); + exit(0); +} + +$tel = new Tel('status'); +$tel->setInteractive(FALSE); + +if(!$tel->connect()) { + Header("Location: " . getError("formApontar.php?" . $_SERVER['QUERY_STRING'], "O Observatório não está disponível. Consulte o responsável. (SERVER_FULL)")); + exit(0); +} + +$stat0 = $ccd->isBusy(); +$stat1 = $tel->isBusy(); + +if($stat0 || $stat1) { + $ccd->disconnect(); + $tel->disconnect(); + Header("Location: " . getError("formApontar.php?" . $_SERVER['QUERY_STRING'], "Há uma observa&ccdeil;ão em andamento.
Aguarde alguns instantes e tente novamente.
Para saber o quanto esperar, vá a página Estado da Observação")); + exit(0); +} + + + +$sync = new Sync(); +$sync->setInteractive(FALSE); + +if(!$sync->connect()) { + Header("Location: " . getError("formApontar.php?" . $_SERVER['QUERY_STRING'], "O Observatório não está disponível. Consulte o responsável. (SERVER_FULL)")); + exit(0); +} + +// to serialize the process +$ccd = new CCD('status'); +$ccd->setInteractive(FALSE); +$ccd->connect(); + +$tel = new Tel('status'); +$tel->setInteractive(FALSE); +$tel->connect(); + + +$ra = $_GET['ra_h'] . ":" . $_GET['ra_m'] . ":" . $_GET['ra_s']; +$dec = $_GET['dec_g'] . ":" . $_GET['dec_m'] . ":" . $_GET['dec_s']; + +updateFilename(getcwd() . "/data", $_GET['obj'] ? $_GET['obj'] : "imagem", "%Y%m%d%H%M%S"); + +//$sync->setStatus("OBJECT", $_GET['obj']); FIXME +$sync->setStatus("RA", $ra); +$sync->setStatus("DEC", $dec); +$sync->setStatus("EPOCH", $_GET['epoca']); +$sync->setStatus("FILTER", $_GET['filtro']); +$sync->setStatus("EXPTIME", $_GET['exp_time']); +$sync->setStatus("NEXP", $_GET['num_exp']); +$sync->setStatus("BFNAME", $_SESSION['bf_fullname']); + +// first slew +$sync->setStatus("TELSTART", "NOW"); + +// now wait for telescope to go offline + +while($tel->isBusy()) { + sleep(1); +} + +// ok, now take a image +$sync->setStatus("CAMSTART", "NOW"); + + +// ok +$ccd->disconnect(); +$tel->disconnect(); +$sync->disconnect(); + +// guarda o log +$i = count($_SESSION['log']); + +$_SESSION['log'][$i] = "fila"; +$_SESSION['obj'][$i] = $_GET['obj']; +$_SESSION['ra'][$i] = $ra; +$_SESSION['dec'][$i] = $dec; +$_SESSION['num_exp'][$i] = $_GET['num_exp']; +$_SESSION['exp_time'][$i] = $_GET['exp_time']; +$_SESSION['filter'][$i] = $_GET['filtro']; +$_SESSION['start_time'][$i] = time(); + +Header("Location: status.php"); + +?> + + + + +Observatórios Virtuais ::: Entrar + + + + + +

::: voltar :::

+ + diff --git a/src/www/arquivo.php b/src/www/arquivo.php new file mode 100644 index 00000000..54885510 --- /dev/null +++ b/src/www/arquivo.php @@ -0,0 +1,129 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> + + + + +Telescópios na Escola ::: Arquivo + + + + + +
+
Telescópios na Escola
+
+

 

+
+

As imagens também podem ser otidas /">aqui.

+
+
+
+ + + + + + + + + + + + + + + + + + + + +
ArquivoTamanhoVisualizarDownload
Nenhum objeto na lista
+ +">:: visualizar +::">:: download ::
+

 

+

::: voltar :::

+

 

+

 

+
+ + diff --git a/src/www/bin/fits2jpeg-linux b/src/www/bin/fits2jpeg-linux new file mode 100755 index 0000000000000000000000000000000000000000..4de414745789768b93f73e570ad6fcd0031ebfdc GIT binary patch literal 32325 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 z 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 literal 0 HcmV?d00001 diff --git a/src/www/bin/fits2jpeg-win32 b/src/www/bin/fits2jpeg-win32 new file mode 100755 index 0000000000000000000000000000000000000000..4de414745789768b93f73e570ad6fcd0031ebfdc GIT binary patch literal 32325 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 z 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 literal 0 HcmV?d00001 diff --git a/src/www/config/uts.inc.php b/src/www/config/uts.inc.php new file mode 100644 index 00000000..3e83508b --- /dev/null +++ b/src/www/config/uts.inc.php @@ -0,0 +1,47 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> + diff --git a/src/www/css/uts.css b/src/www/css/uts.css new file mode 100644 index 00000000..2266a280 --- /dev/null +++ b/src/www/css/uts.css @@ -0,0 +1,186 @@ + diff --git a/src/www/db/db-schema.sql b/src/www/db/db-schema.sql new file mode 100644 index 00000000..f0d9f4a7 --- /dev/null +++ b/src/www/db/db-schema.sql @@ -0,0 +1,77 @@ +-- phpMyAdmin SQL Dump +-- version 2.6.1 +-- http://www.phpmyadmin.net +-- +-- Host: localhost +-- Generation Time: Mar 10, 2005 at 09:26 PM +-- Server version: 3.23.54 +-- PHP Version: 4.2.2 +-- +-- Database: `uts` +-- + +-- -------------------------------------------------------- + +-- +-- Table structure for table `logged` +-- + +DROP TABLE IF EXISTS `logged`; +CREATE TABLE IF NOT EXISTS `logged` ( + `id` smallint(5) NOT NULL default '0' +) TYPE=MyISAM; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `setup` +-- + +DROP TABLE IF EXISTS `setup`; +CREATE TABLE IF NOT EXISTS `setup` ( + `admin` varchar(40) NOT NULL default '', + `admin_pass` varchar(40) NOT NULL default '' +) TYPE=MyISAM; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `user_info` +-- + +DROP TABLE IF EXISTS `user_info`; +CREATE TABLE IF NOT EXISTS `user_info` ( + `user_id` smallint(5) unsigned NOT NULL default '0', + PRIMARY KEY (`user_id`) +) TYPE=MyISAM; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `user_sched` +-- + +DROP TABLE IF EXISTS `user_sched`; +CREATE TABLE IF NOT EXISTS `user_sched` ( + `user_id` smallint(5) unsigned NOT NULL default '0', + `id` int(10) unsigned NOT NULL default '0', + `inicio` int(10) unsigned NOT NULL default '0', + `fim` int(10) unsigned NOT NULL default '0', + PRIMARY KEY (`id`) +) TYPE=MyISAM; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `users` +-- + +DROP TABLE IF EXISTS `users`; +CREATE TABLE IF NOT EXISTS `users` ( + `id` smallint(5) unsigned NOT NULL default '0', + `nome` varchar(255) default NULL, + `username` varchar(40) NOT NULL default '', + `root` tinyint(1) unsigned NOT NULL default '0', + `passwd` varchar(40) NOT NULL default '', + PRIMARY KEY (`id`) +) TYPE=MyISAM; diff --git a/src/www/db/db-schema.xml b/src/www/db/db-schema.xml new file mode 100644 index 00000000..bffd7e39 --- /dev/null +++ b/src/www/db/db-schema.xml @@ -0,0 +1,365 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + +
+ + + + + + + + + + +
diff --git a/src/www/db/db-user.sql b/src/www/db/db-user.sql new file mode 100644 index 00000000..9bfa815f --- /dev/null +++ b/src/www/db/db-user.sql @@ -0,0 +1,7 @@ +-- create user and give privileges + +GRANT USAGE ON * . * TO '{$uts_mysql_user}'@'{$uts_mysql_server}' IDENTIFIED BY '{$uts_mysql_user_pass}' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0; + +GRANT SELECT , INSERT , UPDATE , DELETE , CREATE , DROP , INDEX , ALTER ON '{$uts_mysql_db}' . * TO '{$uts_mysql_user}'@'{$uts_mysql_server}'; + +FLUSH PRIVILEGES; diff --git a/src/www/display.php b/src/www/display.php new file mode 100644 index 00000000..3747d0e5 --- /dev/null +++ b/src/www/display.php @@ -0,0 +1,58 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> + + + + +Telescópios na Escola ::: +Visualização + + + + + +

+ +

+
+

:: voltar :::

+ + diff --git a/src/www/download.php b/src/www/download.php new file mode 100644 index 00000000..f9b54f10 --- /dev/null +++ b/src/www/download.php @@ -0,0 +1,53 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> + + + + + + + + + diff --git a/src/www/formApontar.php b/src/www/formApontar.php new file mode 100644 index 00000000..e57ea6cb --- /dev/null +++ b/src/www/formApontar.php @@ -0,0 +1,257 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> + + + + +Telescópios na Escola ::: Apontar e integrar + + + + + + + + + +
+
Telescópios na Escola
+
+

Apontar e Integrar

+
+ +
Procedimento +

Preencha o formulário abaixo, clique em 'Observar' e +aguarde. Será exibida uma página com +informações sobre o andamento da +observação.
+Posicione o cursor sobre um dos campos abaixo par obter mais +informações.

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Objeto:
+Ascensão reta (RA) ">
+Declinação (DEC): ">
+Número de exposições::
+Tempo de exposição:
+Filtro:
 
+
+
+
+
+
+

::: voltar :::

+
+ + diff --git a/src/www/formOpcoes.php b/src/www/formOpcoes.php new file mode 100644 index 00000000..ee5a917b --- /dev/null +++ b/src/www/formOpcoes.php @@ -0,0 +1,104 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> + + + + +Telescópios na Escola ::: +Opções + + + + + +
+
Telescópios na Escola
+
+

Opções

+
+
+

O nome é formado da seguinte forma: +[nome_da_imagem]-[data]. Os campos cercados por [ e ] podem +ser alterados pelo formulário abaixo.

+
+
+"> > + + + + + + + + + + + + + + + + + + + + + + + + + +
Imagens e diretórios
Diretório padrão:
Nome da imagem:
Formato da data:">
 
+
+
+
+
+

::: voltar :::

+
+ + diff --git a/src/www/getStatus.php b/src/www/getStatus.php new file mode 100644 index 00000000..019ab564 --- /dev/null +++ b/src/www/getStatus.php @@ -0,0 +1,97 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>setInteractive(0); +$ccd->connect(); + +$tel = new Tel('status'); +$tel->setInteractive(0); +$tel->connect(); + +$stat0 = $tel->isBusy(); +$stat1 = $ccd->isBusy(); + +?> + + + + +Observatórios Virtuais ::: Entrar + + + + + +; "> +disconnect(); +$ccd->disconnect(); + +?> + + + + diff --git a/src/www/home.php b/src/www/home.php new file mode 100644 index 00000000..bdc186fc --- /dev/null +++ b/src/www/home.php @@ -0,0 +1,138 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> + + + + +Telescópios na Escola ::: Página +principal + + + + + +
+
Telescópios na Escola
+
+

+

+
+

::: apontar e integrar :::

+

::: estado da observação +:::

+

::: ver o céu :::

+

::: arquivo :::

+ +

::: opções :::

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
#ObjetoRADECTempo de exposiçãoNúmero de exposiçõesFiltro
Nenhum objeto na lista
+ + + + + + + +
+

 

+

:: sair :::

+

 

+

 

+
+ + + diff --git a/src/www/imagens/back-ajuda.png b/src/www/imagens/back-ajuda.png new file mode 100644 index 0000000000000000000000000000000000000000..1cc738497a3339a4eccbe50dffa54d6d07065207 GIT binary patch literal 914 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|G$6&6)78&qol`;+ E06x_bD*ylh literal 0 HcmV?d00001 diff --git a/src/www/imagens/cnpqlogo.jpg b/src/www/imagens/cnpqlogo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f3b8f751ec5854853d2da51e13cd2da6d73bf82e GIT binary patch literal 12808 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 literal 0 HcmV?d00001 diff --git a/src/www/imagens/default.png b/src/www/imagens/default.png new file mode 100644 index 0000000000000000000000000000000000000000..11f7c8317f3b58e6877e1e323b5945eaea119d70 GIT binary patch literal 4338 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 VnsuE?2mHh!P*c);QKn!T{6AYzeFy*m literal 0 HcmV?d00001 diff --git a/src/www/imagens/inpelogo.jpg b/src/www/imagens/inpelogo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9c9637b8289697f874c01afdca00027a57c18a2c GIT binary patch literal 11601 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 literal 0 HcmV?d00001 diff --git a/src/www/imagens/ufrnlogo.jpg b/src/www/imagens/ufrnlogo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3c9436d6c48e89b6e1d714ee8174da9357fce120 GIT binary patch literal 11252 zcmeHNdpwls9)D)WokZzoWhRB)4#tcol4-USsZcIaiRSVSnj14CR?F1Zrqfn7+H|*V zNjFL=$zn@2yW7HiI6Mwy!4FV9*5xgTi{t{}?G2^_0Q7(^ZVVv65e|L;&KziCV*psV&xXcp zai0(9;Gqr$z!sRnD#lTV##@pif!4s>gRuVDdDg+!saIyt-2s8Agce#EK$sZ;>`h##`+_9J0v%s>@k(ZLZ0V$=CJe%$yE z@#8E%jvrz%5GT$CKV(~nt~Li2!XAwo>s7-sT!?a2t-uIdOPISiz^mGTF-8H-26U#X z8f*qedU|^LdPe&CMy7@ahNk028ySrrH*xIPabw3$G&RD?K&K}D+~P(V8jdm^Wnye> zVrFb?Y=$<*X6h-Xf5m|67htLf7{HByvjTWi9KjT)s)5VaQeDSE;BeYFtiXWLLVz1Y zOIt@*Pv5{0ZiC@M9KaI>g(d)}g(Kj#@Y=dMdfEgdCnz)}XiXVI(Vn}EIo2wABUQ(2 z*O8ogQ>TsdyUKE!A^xV+UDrCr|JCEyYzb}rPv|umO3KySd~+ z%##ie+ug^$z3{{JvZtLH7jHa#rd-Zlw`I@qOE=3KyPQ3P!ef)tGV|mWP2HvdkB8A} zVKM3IXw$F|rclOc!3d(Q#%fbH?lMCoxawE>%4r(wvG|)5cmHv0$&A<5I%oj8HneLB z7{t*DoCSe1rAWh22>&_)+YceAs(=vz^Jxn4eRC|u%leCHDPD`brlt6$cum`_0;>BR zZWb2X-`||$KTkI#8kB^E z{FZ3=h;Y)Z?9riXA-9|bzaCXoUR+d^dSATy`5L9&cW=(@^7g$h5J;X>$sb9GFY?|c z?$1@8zthJXA1oM^wYiMuaKLWey}Lbv)#J%4uih;zd!YQP(Dj-{*R%M*3%R0#sz$b7 zV|%~o-Hp(Yo6Ief)_QJp6W6;OO(wpb#aPupy<-!x)`yf}#WL@$>j?Gl=PLh5Uv&Ak za)k<{%*$++uekC)=f;#hOU*+qyDqqQ#|zn+f*tGRecFo3jp?EgVc?3FXD9Duy4=aB z?#jukE%7bwe*Z&KjQyGBNml2tZZ3D6S(M|Kj0izW3HnmnUCDdv2MNwzc>;vaC$kJ7)tY?c&CQjatQ` z%=R9}!7SOG?9*NCm9OrtQYeqEXA9eXU--5=q@7w*ky_RhtLDVqnsx0<1FBvoFHJvo z)AjiJyn;U}syi!eeFERUFPyMPSXyennX7bb+&X^mE0XC6`!+_*-M}|*dL`5`jt$9L zmyr@Vu_c~UdE<^nlnG_~bwqCI@;b`5r|3kXv-j0|&9Y#}o|bO|>T_-KaL7a2OT?JC zC@r|U_-cQ{%bp7V8>f#K-8x&dZ2f01ZGN+^%Ma~Nupv`3TWkaY26R`uh?@L+oc!v$ zP4U^<1!dHtbI8;3VBYbcnndfHxfL$&iwn`98Qo??bGM^!FvLVa&VN z`}Rct{iFS{eOFdL7gf(*T_DPQE~E7}$S1vHX8cqE7XN*#`Oop|WQXq^+tgCs5?@{H zAEq!i0JX(Y7!N(v;xNrg zL$oDkwF9H|u@*Ks^(+I7hDRN$W2=tG06Y3n0p^pS#B>${GM5V_eAvQ678cw9=g7YO zLmc$v#(|Zv9n_&=9QzL(ESk5F1|SL^q@OH==cxFTBGvyQ!;sNKZ3A?zaNYPX)pYN6vpuRutRe$9hN9Y$dUN6*0LcrFwc)Ir9cXO{2&14ReX^!937)&3Br|r zOwW^w1Oqe{hF^HZpd1ZNN5YR}Ba-F(B@#&ChWXLPWRN`uR$Ywnn*Wh)-EgreCTbW< zMT9LN9xUqvAU$_8p>l!HpV1mPsu z{lC{wh!OJx76b=CR|DXp;1B0`OTv+1933WK8o&%6#xrIka2^sT^^zlmAy>SVc)CeVYR=@@YYw){r0RX!tkWU8_VS24e$TbOhW0xi&*Cgbc zgj|!5k1Qcq=XF!iQw9XW3eO<`MuWM4gQaf>Ac3VI0*oNTpG(WU^4=z(ikzIj}_navU>?OmT1|12;xo6qC(Eq$Cys z^Ch}%dqJ@+DUw6C4WfBFdPlh<5s?ey#fX2rPXIff$DYNpWy~>fV_>H2p&5H_Bwvb% zB_u?`rdzrDS)t-MS11u6QYI-*z!yqfPu3XoWlm`9>G&}^YeF^B8wH{WBxAf)KF0}G3djdh51auA8b$&^{M zW|1AKWGdAjYS>G@5=xnI_Cm>2=no4dGI(g(FgzlIF*u+j0}|$HK3tACGD;dL5|WrO z^_9|XV`3sXt}L#zi!;~J(cY2c$g!tTIL`JgF4NhbCQW=45? zf7;{V6A%c<&?|}L1{vPouJc9g7y%-bdd;7sMvNn3L$^_buP$9_&Mf9EDl+5W1>lB0 z-{HpKvdAceAFT_nRn3F2LV(Fb4-F0H|6eptMuf2jE)85GLSRJ7n%$*=YeWc)NLjPH zG;oawfe|Tdc9#aO5g{-lWzFu=z%?QSMx?CST^hJXgusZD|J&UK-|q~*6+(pYRZuK^ z=cBo7q`7SLmoFRT3|uy%4ZCcVQmaD+F zpX=q_sauH)_iib6?yZ2=to9YIvsGlfB`lXc{r*%>PPwq?W%NR1w@zA8VU|f>wT0ZL z+PpQn$1o$8;97g-(W%_Z(AgH(q7Sh1TG^`#x1=BK@fDwI%eZ*RecJLyBb%I!mv-#@ zT9})Bhg}$UUsk=%>2A}JlGeK(6jzUB^?STL+^7xdryB(ca_z$oE_%Pa`%j9NTObaf zzmh9nPw2dT%5}qwOO8ef(`b57xC6*wK$uxqv^Kq)LH(ZJtU$=^QH;NEB!hn>$sm zQW!lVB+LnjL&k#OpIv>*7BqljIdDop=Wm z>U@KQJ!`XNNw;P$;m-H8Brlmt?2vWe%IOHvx*eZ6_2!b|go{4N)Rwu;%O`C24CywU zIeYo{^>re}%0pqNI$N%A1;J<6xBb>xRo>$pv1fmEp5#E@^K#|y+h6_|oG{N@b|=-c zuK8r~N$wxuyrM5t!Xap{NS|))CaF-eD3)hC#7~l+XC*niT#FjF!YIR@k?2N!tfbpM zY|Bedeo*w={;*XqqQ{xYuLP?=V#zt;k?{Vn17s1UDlogh#-Y=>^-y+A-h)oY{{Cq8 z_TF=fQr-sZ>(@tx(q}X7g)8PNTa|@^+INR)Bjmd`ix*q-=xh7jLYAfXeLg*;b_IKj VRf6Mvq9S_|O9d2S*}uAS^bh{Ih`Im( literal 0 HcmV?d00001 diff --git a/src/www/imagens/ufsclogo.jpg b/src/www/imagens/ufsclogo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..55be6ee26c6e57b575d19ef88277e25e5105e3f9 GIT binary patch literal 12061 zcmeHNdpy)x|3BkSC?U4QWGlN{#x1!-vqdgtR=G>6F$R;F`7#$Q%WSpj*)7>_N=U7h za!II^LKm?Kh1}UGZPK_^3>n7!&Uaik&$G|#w|>v-dHw!z_@3|iT+ipc&*6N|_jN?g zq8DJ%Ry#*KfRVre2lxX-FXf`QL{I_%AP~S;00224i&+dLVG9F)0A?wW7PkTL!z`O= z@56jCr2{oFMnD4UsKJMUF`jA19;N`PDcfZD{tZ6MrfBhdlRx4|#%>QnSO_x+3Bj7- z41uwsp@}8@H8R2)nOmBeTN;@_6Cim3GufX=1i%G}8Nd1$Bxl-qR1p>#jO0{oGM~ne zS@LK6810$(VdM~tsW>sKC1!lfI#0SoQ9_ZJe2e}H!#jFrp4PY)MfW1_FnX#)u0H7%h@; zfk;Wq$jZqpEP%^kwh#j(B&UUo07eQUDIp~xEh{4@EvaY%g({L#D;66`Z`wgr)eYTm zETeY(V&3M@SE{?*_cK|?I8bXNyXu7NyO-}tOw%R5 zA0F1TUk%#no%;3A_0wg~{}@WY@u=Z7-y^3M-o4jHmz! z2^g)EI3`&cX;X0sD~uLP!3aWiRi%yhA6G*oxbIT?&Sa(EOU8i{Hm>R<=DPQ*WY7R) zSDQYlgF&Qe_-}Dv&mwougz)bp(EBq4Q7cfC6!)nD@%`}Y32*`|{^on>D2?P#b?~Uom9ky7dUl&Od#BQRjI?GoYX_a~yAn7dvO3czNHu$=)Y!^A` z$~`;O&cY>n7}yq_zdRPw%U@0kj=GX_^F+r{+QR&2W7$Z?fC$*|MBo7~Oa!u0LI?6+ z{z%C_AslE`=B5`t4KHj>l;7&Dr~R3-!qT!HPSInde~|VszWc+I_-i_88mm@bC>wt7 zXdNedHVDo6B+DLZiP??3qi-}!$YleWg5JRH;+@6#E=0KehM@G;RROn?7+U_@r({N0pX_Kg@3_-N~#*+_kxct3*@ef_Y|2pgGX{gA!8VncYX2pknE z^x+@n8ypIm2B7c4TU` z2 zsrm45VrOUP(EBJ(Sm#4IPR_89mg=`RSoQ5__IIU&sb7?5X%3w`OT&>5JfWvIZJ`w> z_54!dR#_Yn^J8zDPxPMaoQ$l|^dmB)(gWRs6~(vIzJKG<)V__}zc*yr#v{c#B(1Jq zq4Y$00_UKY&M#xmdUf2PG~a@nYVI~#Sr1kOM74thSuLhbr+Byx!M|ZHEqHij zzfjfZtqxZ>d^IbbpG@Dsd$-w+jD~wWno@BU=GYQ5rVmZMrss?NLJ`i-6|!f|6^|J_hRbb-guLi*v|{ z3B6(E6Qq6j)IB|gG~U{e7n8K|oK#i!eEIT}le3!h;_g?;WUT|6m)ooN2x~O1cRv+e zIa5!6T)I9v&4$#c;W4n|z)tVspZeF+hlY*MUCA1XIXupISS@&RC_cJ5W$_!nR`&Oy zd2vZe?r!D-W2X8g6_(YdC1d!`1Yh56yf$7@Bg-|mt=m+_WJP~)ji5uXZ&^p)`6TOx zUZHLdvP)3Z_O*`Zm${S?$>={sAc>S*o@qYunaT;q6{B4~g>}f~rl_nA&q3`YIKHQeWo384 zzHVPvtvv@%#%y%8yLDTM7?3)$FTaW7(H>uiiz0Fh9MAq?Kg!)gk(Z2FzBEJX1!ko- zK4EV==M<+1&RpGxoHDtSviW%j>kCY(tIKkqn?G+k8PeX^mwBSs7r}kc))d;>qZa(#$R*|0*x;mN13vu1 z0;THG`f~pHa(!G{(?pSFFZF6#?7K>19hy7$r)Z6m+U>N3ml`8I1nl(*uQ;(8qm`+= znBxmGqe+6Q-tW!{5~SI+N$J`pUkasM3C_Zl0EZ|Z^PP8G@mOC$earFjzVrE8zy0~m zx+hLLi_SScjd5{Yg%`Dm`jnRZnE;r;1i*Z=BSC7Fco$kEu0N?znPlIT%wBXIrWz7p z>MgcgEtV<&X@dVAOnOuj^U!TRLah1O46QBpIw+>)#Wk!jlU5318gBlmj^1QE3gTn{ z6-bCJ`C=UhU=R=hB~)0$v;!8*6z5{%xfzbJn7kVNj{ct=V|r3z}R_7u0S#k(=ht06xQjji~@p7M)F{!WWu@%J?B{x zVo#=%8AKM@A2NX3w;|*yWray)l!HnfX@OYy`frb4lFgvHZSiu0T}^?DfU8mK!l}7EhT(hIZr#mLt=_jo{>mAXL<$+%$WZvV0K2h}=Y_1WqHhaB}bt(`;xd z>rW0KvZ*ZCln)~_Sbw4JG0mQ>zQ8Zg2B9L1Pry}~^uO7DMgUE~HwYcYTAqc3II~$y z^7O($WeQZdkp6|M=!dZ2g8CQ!0yxD%|Ewr+9U0nF6@AM{8yxp1Y#Qbm-M4Tcp8 z*7V=XKLA(}0r}2A6CO9_4=D2ol&<0V1IqjXW&VINe?XZ(p!{bCl*v4I1-kFTY*xH8 z1F#Tm0{#F2et-O zSga6B0|Pn}M?^oA;z$V1Ac7cTV1zR?09N>j5F#m<%)d4*7Gza z7!pEk$Uzi`NCw$8@*6i&WH8CXUk|@g!3r;Sr4QZc2T-UiGJ}aFGfCFEHZHoTIKmQ2 zXk-=<8$qMenU)dOdSU}h*hbj~dRVavE7)4ki?AKbf@xbYg@rZ7nd6N0O-$Be0~kaa zIUHdGV~ud8de9}qKfuz()^5rnq^$L(1{NM3jte)zA&fu+BMS=)14Cm2V`F`&q0ju5 z<$W)0v;c{=|V$yk^X1;X&~d!vQ-|z>2%Fg~MS`LRb`pjwQmw0LxmB&8GNUS{RuS ziDa^|zUewc3wXlzjte_dVh`_A-NAu=6)3JQnceCDsC z&0k0R|GtiPdtv}7e7kzHOM+uXtj&SgJWFnWzLTHk3)|ck#;EXV-2>ejt14J?YSiwy!2^MxxZLo?2S0wNEq`{;n_-8WI|M zUMDFws9Z7stWcXhn(9UkPHd1NjK1#A|2_Q>{XKFy+QW<$(fwE8zt@B zZ*pBnOJAqCChfN0TEkBC@J%5jOx{h+rR6DcW4H}kJl{AG2+N9|$o=Re0{1gdew=U$ z?L4E8TTIdn8MNB9Yxj8rq`%#ZxR77M&B^O^Qu2$&o5r7+V7}IDAZed;RZ6eA=1qHE z)SXjU5Y6)9C3jvapCBAMc|CshB;?fv(t0^Csa zQD#!XkcJ?^dqQb%OnjeJ>NZYYT-ehMW}NOvzcl>VxasfRMcXdEi>6py6>IL*QN5&g zSAYJIHJrga{iTgrO@*V6p0Mbr51!IF{d~~p&><&!v$aoe=!w?L7fWqop1#R#@f`Q7 zs}bDiQ}22@Z!54Y8orSb;FH(3Y&0$lxclzm7hW#tjHNOj89EWZ%ZS}!WVOR;TiUVE zQw(`Iww7n^i23gNG(7Lp9>Sf~>S{={mk9LY*QAVbE?qA z*9g`)IV^kE-C(W<8+W| z`U{4R(fi`Y%VUmv?zEOQyRx^-`(%Chm?Le;(adej$@Fslwo~m1p2}nRjPah4g1~`a zGaQ5tf|th@C}WXUt@f|Ed$i72blvfGr}2-JT4oeCh=AFI>bPzer@9cLz ik~VAWM4g(Jo|PGi%1o#0_utF^S~c=|yB27oOX4y%I1?CY;yBLT*`vD@MGBCOCOZdH4oAw**k>VjBQg(_pw zcq+@?128>2m~008GpGy(+tZuvF$Jmv@-C9rABF+2%c#%p^j+k>z8|g#3x!11#YX8L zKWW74_(_iaa%T)6rR<@QFmbUgotgMFEkF>M1x3e2*Yef{@MN7QdlB_K)t!=D_ z+Sm*oZewFJ9B*ufYlqnW6$99Jz}5`-f$3zDGcd9xk!?xX9XM@c>^unqM=~K11qO^4 zGF%|WCKOXMa|=tj3QV(u$G&GHYi7bvGZoj}0@ zFrDai_67_h$3Zwd%v*&#=nLW95ztJiFQE>K5^Bv#rg0=RQ z^~<=R?Dj9c&A}KL;q>xEV|kY?21;L9I6bJqfWugNR}}^twq*s6?+CHKS7LXNFG8l( z&GB+>3$D(VscxQL?U|a{8|j9DkOYP-(KkA}vGQSPVqIW!bW#0|#i^k_U#9q-o#YgD zVxu3o>xWg9id7jStL4XHH&MQdXpnWcY+KJR5BRaGtdh~X_4$z}ZWqs7sw;PLF5OuE z7z4CdrwTRDe>tzw^;l5l8Zlas+aYz_ySZvpiA`SF#t73J?P(QJUdLO|a#4G+?>@o( z+X*GfZGjgqi8d{Win}cDZra%;NlnY~&Di;?fMrv7!mmMmGdO|%gKu7NWBcZ`$yaau zbk4dn?P>`Ikn#t9IS+dx4xOq$IF@G-XxiDYS!?>bGzLE5B>Q?aLji z(?u&QnddB<&mXAruJg{gf;g6iFi8>>#Wzmd|ct-ZJ-^i3I*8U@22S<~d z&P5h=^z3bD#z6gz5bXLd<=@{G`8rg)Z%gBSntyEZvd;LivheHSZCx$v!g~*^vI{W~ ze!6&Jw#NU;rIGX_J{v0dacc#Sx3(dPp)XQ8+m=5ZvC8*|x~b+?bJJp};6v*;^awC* zJ<6HD-a&5)h;ACSr|{I06X8>QIvr@r1IJmD5G zbtac*Jb`#m4JF!s4K5?M=w#Z>#z?p67!l(p5;B|DL;OAgT8q{M58nuq2+dJ_v?F2l zIYFBfEo?~IQ5FOZw+CE@rj5sf*e-AZ;gj4!=*$IlkqAnpu!S2FEF>Lg_m-`F945h! zgsoo3i{QAu<`B`mgfsy0aPxUr zNDa|Wey*MkN16%|5nnA;!LE5CqENj>JzUQosBRe}_D7|tq93@nTKh8t`UG$Y2t?&L z*5)cSK1i)nBKn1a%PgdDA^nbP6@#kag8Cib65it2H`R%!&lWcZNV@kAo^)#$E}4*8 z;iO3hY;dsj&&p8%K9~>r31B2lNDV2hA%%VP!jQrmQdmO@`~OH`wVBX3{LKu)V1@5) z0EU1WKuDxK2%v%mAQtvjuqufjOzeQ&pJ-443%COh+@RKjHp4O{-u%!9c7NF1Sd}WC zO{dG1?tJ`Xi@N}o(UbV`bcVYJ9ZdI2isuXB5EV5BfjI_;)^@U-MwJLTv_)P#4_>@K z5-XXTtUyAN149MLaRMJ9&Cl0jx*uW64VrNiNu(-7p`;>80ms=t*clfmv7tnUsQA<* znN+T1Cvj-R05E`MA0ac{n%McB!h@&#x zy=c&+LMUPf&zhq%2q_LtH!+Py?CANFoS0pd$+^;c6C~jzSWzlAv-bAEt{c4o$6=2-z$lYbwjbo8jgm^boo+ z7($j?jEK*26S6#}PDMm99-=8zXckocy!r7w-rGIu?|@83hhC}F>5#$Wv1g$IwG5G~ zxU+n+un-`p`mIq;l0Mq+!8XnHy zUkpwLg|PuH16+ebU{K12)n$NdPzVf4*|53{a19E9K`9$nmjSLpAuuRq!|F1?H7EoI zrEFMT2Dk==z@U`>)71sP@94h@A#(UBNCUt57>=|Uj0TGez< z;9GW&VqJ(X{5URh3i;;hZw1Z&`sivk2Fi2u{`^Es<{KwkMuhM_KQtk8Ph8nIwIQ`_ z^<@QLMOt;8sV)%29X@~*WdN^(r@sSEC$6<`Xi}9Q7@NLd^hNPgd1J)2hWagALYneR zGc-P(7F!#9?p`hk+SlGzxP!g@(8{V4j*02>Qnn{9{OIoTvfX!gMh9Mui&}KDoAz>A z+|qqPi<@4-lO@lKnUl09OV)R?}Bq&oR>QK#3s&h#Aru9F>_L!Ql=pr1H2=61%8 zsNJm>iq;`%yHb`mNZV^JcqzAx6&E6vl1o=EUd;W9mw2o5==%25Oy$;pq!sn>*^UP` z2Xv>Fm6vAixZUOyKqCME literal 0 HcmV?d00001 diff --git a/src/www/imagens/vitaelogo.jpg b/src/www/imagens/vitaelogo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e2b89f8b93175d54879f39cc75758609c5a5e799 GIT binary patch literal 11969 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 +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> + + + + +Telescópios na Escola ::: Entrada + + + + + + + + +
Telescópios na Escola
+
+
+

+ +

+
+ +
+ + + + + + + + + + + + + +
usuário
senha
 
+
+
+
+
+ + + + + + + + + +
+
INPE
+INPE
+
+
UFRGS
+UFRGS
+
+
UFRJ
+UFRJ
+
+
UFRN
+UFRN
+
+
UFSC
+UFSC
+
+
USP
+USP
+
+ + + + +
+

+APOIO:
+
+ + + + + + + + + +
+
+
  +
VITAE
+
+
CNPQ
+
  
+ + + +" . get_include_path() . "

"; + + foreach ($included_files as $filename) { + echo "$filename
"; + } +} + +?> + diff --git a/src/www/instalacao-action.php b/src/www/instalacao-action.php new file mode 100644 index 00000000..5aa52619 --- /dev/null +++ b/src/www/instalacao-action.php @@ -0,0 +1,218 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>" . $res->getMessage() . " (" . $res->getCode() . "))
"; + // $err .= nl2br($res->getUserInfo()) . "
"; + // $err .= $res->getDebugInfo() . ""; + + //return $err; + +} + + +$db =& DB::connect($dsn); + +if(DB::isError($db)) { + $err = "Erro ao tentar conectar ao MySQL.
"; + $err .= "Verifique o nome do administrador e a senha.
"; + $err .= dbErr($db); + + Header("Location: " . getError("instalacao.php", $err)); + exit(0); +} + +function back() { + +// global $db; + +// extract($_POST); + +// $sql = "REVOKE ALL PRIVILEGES ON $mysql_db . * FROM $mysql_newuser@$mysql_server;"; +// $sql .= "DROP DATABASE $mysql_db;"; +// $sql .= "DELETE FROM `columns_priv` WHERE User = $mysql_newuser AND Host = $mysql_server;"; +// $sql .= "DELETE FROM `user` WHERE User = $mysql_newuser AND Host = $mysql_server;"; +// $sql .= "DELETE FROM `db` WHERE User = $mysql_newuser AND Host = $mysql_server;"; +// $sql .= "DELETE FROM `tables_priv` WHERE User = $mysql_newuser AND Host = $mysql_server;"; +// $sql .= "FLUSH PRIVILEGES;"; + +// $db->query($sql); + +} + +// OK, we have a connection + +// // create user +// $sql = "GRANT USAGE ON * . * TO '$mysql_newuser'@'localhost' IDENTIFIED BY '$mysql_newuser_pass' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0"; + +// $result = $db->query($sql); + +// if(DB::isError($result)) { +// $err = "Erro ao tentar criar o usuÃrio.
"; +// $err .= dbErr($result); + +// $err .= "(" . $result->getMessage() . " (" . $db->getCode() . "))
"; +// $err .= "(" . $result->getUserInfo() . ")
"; + +// back(); +// Header("Location: " . getError("instalacao.php", $err)); +// exit(0); + +// } + +// create database +$sql = "CREATE DATABASE $uts_mysql_db"; +$result = $db->query($sql); + +if(DB::isError($result)) { + $err = "Erro ao tentar criar o banco de dados.
"; + $err .= dbErr($result); + + back(); + Header("Location: " . getError("instalacao.php", $err)); + exit(0); + +} + +// // grant user privileges +// $sql = "GRANT SELECT , INSERT , UPDATE , DELETE , CREATE , DROP , INDEX , ALTER ON `$mysql_db` . * TO '$mysql_newuser'@'$mysql_server'"; + +// $result = $db->query($sql); + +// if(DB::isError($result)) { +// $err = "Erro ao tentar criar o usuÃrio.
"; +// $err .= dbErr($result); + +// back(); +// Header("Location: " . getError("instalacao.php", $err)); +// exit(0); + +// } + +// // flush privileges +// $sql = "FLUSH PRIVILEGES"; +// $result = $db->query($sql); + +// // use the newly create database +// $sql = "USE '$mysql_db'"; +// $result = $db->query($sql); + +// if(DB::isError($result)) { +// $err = "Erro ao tentar criar o banco de dados.
"; +// $err .= dbErr($result); + +// back(); +// Header("Location: " . getError("instalacao.php", $err)); +// exit(0); + +// } + +// connect as the newly create user +// $dsn = "mysql://$mysql_newuser:$mysql_newuser_pass@$mysql_server/$mysql_db"; + +$db->disconnect(); +$dsn = "mysql://$mysql_admin:$mysql_admin_pass@$mysql_server/$uts_mysql_db"; +$db =& DB::connect($dsn); + +// if(DB::isError($db)) { +// $err = "Erro ao tentar conectar ao MySQL.
"; +// $err .= "Verifique o nome do administrador e a senha.
"; +// $err .= dbErr($result); + +// Header("Location: " . getError("instalacao.php", $err)); +// exit(0); +// } + + +// fill-in the newly create database +dbsource("db/db-schema.sql", array("uts_mysql_user" => $uts_mysql_user, + "uts_mysql_user_pass" => $uts_mysql_user_pass, + "uts_mysql_server" => $mysql_server, + "uts_mysql_db" => $uts_mysql_db)); + +dbsource("db/db-user.sql", array("uts_mysql_user" => $uts_mysql_user, + "uts_mysql_user_pass" => $uts_mysql_user_pass, + "uts_mysql_server" => $mysql_server, + "uts_mysql_db" => $uts_mysql_db)); + +// write configuration file +// -- + +if (strtoupper(substr(PHP_OS, 0, 3)) == "WIN") { + $os = "windows"; +} else if(strtoupper(substr(PHP_OS, 0, 5)) == "LINUX") { + $os = "linux"; +} + +$server_name = $_SERVER['SERVER_NAME']; + +$config = << + + + + + + + +CONFIG; // -- $fp = fopen("config/uts.inc.php", "w+"); fwrite($fp, +$config); fclose($fp); Header("Location: index.php"); ?> + + diff --git a/src/www/instalacao-action2.php b/src/www/instalacao-action2.php new file mode 100644 index 00000000..ddab1233 --- /dev/null +++ b/src/www/instalacao-action2.php @@ -0,0 +1,108 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>" . $res->getMessage() . " (" . $res->getCode() . "))
"; + $err .= nl2br($res->getUserInfo()) . "
"; + // $err .= $res->getDebugInfo() . ""; + + return $err; + +} + + +$db =& DB::connect($dsn); + +if(DB::isError($db)) { + $err = "Erro ao tentar conectar ao MySQL.
"; + $err .= "Verifique o nome do administrador e a senha.
"; + $err .= dbErr($db); + + Header("Location: " . getError("instalacao.php", $err)); + exit(0); +} + +function back() { + +// global $db; + +// extract($_POST); + +// $sql = "REVOKE ALL PRIVILEGES ON $mysql_db . * FROM $mysql_newuser@$mysql_server;"; +// $sql .= "DROP DATABASE $mysql_db;"; +// $sql .= "DELETE FROM `columns_priv` WHERE User = $mysql_newuser AND Host = $mysql_server;"; +// $sql .= "DELETE FROM `user` WHERE User = $mysql_newuser AND Host = $mysql_server;"; +// $sql .= "DELETE FROM `db` WHERE User = $mysql_newuser AND Host = $mysql_server;"; +// $sql .= "DELETE FROM `tables_priv` WHERE User = $mysql_newuser AND Host = $mysql_server;"; +// $sql .= "FLUSH PRIVILEGES;"; + +// $db->query($sql); + +} + +// OK, we have a connection + +dbsource("db/db-schema.sql", array($mysql_newuser,$mysql_newuser_pass@$mysql_server/$mysql_db"; + +$sql = file_get_contents("db/db-schema.sql"); + +$result = $db->query(mysql_real_escape_string($sql)); + +if(DB::isError($result)) { + $err = "Erro ao tentar criar o banco de dados.
"; + $err .= dbErr($result); + + back(); + Header("Location: " . getError("instalacao.php", $err)); + exit(0); + +} + +// write configuration file + + +Header("Location: admin"); + +?> + + + + + + + + + diff --git a/src/www/instalacao.php b/src/www/instalacao.php new file mode 100644 index 00000000..62c3556d --- /dev/null +++ b/src/www/instalacao.php @@ -0,0 +1,216 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> + + + + +Observatórios Virtuais ::: Entrar + + + + + + + + +

+""
+

+
Instalação +

Para instalar a interface, preencha os campos abaixo. +TODOS os campos são obrigatórios.

+
+
+ +
+

Verifique a permisão do diretório "config"! +É necessário permissão para escrita neste +diretório.

+
+ +
+

UTS

+ + + + + + + + + +
administrador
senha
+

MySQL

+ + + + + + + + + + + + + + + + + + +
servidor
administrador
senha
  Erro. Verifique as mensagens +acima.
+
+
+
+ + + + + + + + + +
+
INPE
+INPE
+
+
UFRGS
+UFRGS
+
+
UFRJ
+UFRJ
+
+
UFRN
+UFRN
+
+
UFSC
+UFSC
+
+
USP
+USP
+
+ + + + +
+

+APOIO:
+
+ + + + + + + + + +
+
+
  +
VITAE
+
+
CNPQ
+
  
+ + diff --git a/src/www/js/jscountdown.js b/src/www/js/jscountdown.js new file mode 100644 index 00000000..03f68a6f --- /dev/null +++ b/src/www/js/jscountdown.js @@ -0,0 +1,83 @@ + +var myCountdown = new Array(); +var repeat = false; + +function checkPlural(noun, value) { + noun = ((value == 1) || (value == 0)) ? noun : (noun += "s"); + return noun; +} + +function updateDisplay(text, id) { + var tag = document.getElementById(id); + if (tag.firstChild) { + tag.firstChild.nodeValue = text; + } + else { + textNode = document.createTextNode(text); + tag.appendChild(textNode); + } + return; +} + +function doCountdown() { + for (i = 0; i < myCountdown.length; i++) { + if (!myCountdown[i].expired) { + var currentDate = new Date(); + var eventDate = myCountdown[i].eventDate; + var timeLeft = new Date(); + timeLeft = eventDate - currentDate; + msPerDay = 24 * 60 * 60 * 1000; + msPerHour = 60 * 60 * 1000; + msPerMin = 60 * 1000; + msPerSec = 1000; + daysLeft = Math.floor(timeLeft / msPerDay); + hoursLeft = Math.floor((timeLeft % msPerDay) / msPerHour); + minsLeft = Math.floor(((timeLeft % msPerDay) % msPerHour) / msPerMin); + secsLeft = Math.floor((((timeLeft % msPerDay) % msPerHour) % msPerMin) / msPerSec); + hour = checkPlural("hora", hoursLeft); + minute = checkPlural("minuto", minsLeft); + second = checkPlural("segundo", secsLeft); + if ((daysLeft == 0) && (hoursLeft == 0) && (minsLeft == 0) && (secsLeft == 0)) { + updateDisplay(myCountdown[i].onevent, myCountdown[i].tagID); + } + else { + if (daysLeft <= -1) { + updateDisplay(myCountdown[i].afterevent, myCountdown[i].tagID); + myCountdown[i].expired = true; + } + else { + updateDisplay(hoursLeft + ":" + minsLeft + ":" + secsLeft, myCountdown[i].tagID); + repeat = true; + } + } + } + } + if (repeat) { + repeat = false; + window.setTimeout("doCountdown()", 1000); + } + else { + return; + } +} + +function setEventDate(year, month, day, hour, minute, second) { +//function setEventDate(second) { + this.eventDate = new Date(year, month - 1, day, hour, minute, second); + return; +} + +function addCountdown(countdown) { + myCountdown[myCountdown.length] = countdown; + return; +} + +function Countdown() { + this.tagID = ""; + this.eventDate = new Date(); + this.setEventDate = setEventDate; + this.event = ""; + this.onevent = ""; + this.afterevent = ""; + this.expired = false; +} \ No newline at end of file diff --git a/src/www/js/jsval.js b/src/www/js/jsval.js new file mode 100644 index 00000000..9c840f1e --- /dev/null +++ b/src/www/js/jsval.js @@ -0,0 +1,400 @@ +/* FILE HEADER ************************************************** +** JS Validate +** Author: Karl Seguin, Timo Haberkern +** Homepage: http://jsval.berlios.de/ +** Version: 1.1.0 +** Copyright 2003, 2004 Karl Seguin + + This file is part of JS Validate. + + JS Validate is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + JS Validate is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with JS Validate; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +** +** END HEADER ***************************************************/ + +function validateCompleteForm (objForm, strErrorClass) { + return _validateInternal(objForm, strErrorClass, 0); +} + +function validateStandard (objForm, strErrorClass) { + return _validateInternal(objForm, strErrorClass, 1); +} + +/*************************************************************** +** Internal functions +*****************************************************************/ +function _validateInternal(form, strErrorClass, nErrorThrowType){ + var strErrorMessage = ""; var objFirstError = null; + if (nErrorThrowType == 0){ + strErrorMessage = (form.err) ? form.err : _getLanguageText("err_form"); + } + + var fields = _GenerateFormFields(form); + for (var i = 0; i < fields.length; ++i){ + + var field = fields[i]; + field.ResetClass(); + + if (!field.IsValid(fields)){ + + field.SetClass(strErrorClass); + + if (nErrorThrowType == 1) { + _throwError(field); + return false; + }else{ + if (objFirstError == null){ + objFirstError = field; + } + strErrorMessage = _handleError (field, strErrorMessage); + bError = true; + + } + } + } + if (objFirstError != null) { + alert(strErrorMessage); + objFirstError.element.focus(); + return false; + } + return true; + } + + function _getLanguageText(id){ + objTextsInternal = new _jsVal_Language(); + objTexts = null; + try { + objTexts = new jsVal_Language(); + } catch (ignored){}; + switch (id) { + case "err_form": strResult = (!objTexts || !objTexts.err_form) ? objTextsInternal.err_form : objTexts.err_form; break; + case "err_enter": strResult = (!objTexts || !objTexts.err_enter) ? objTextsInternal.err_enter : objTexts.err_enter; break; + case "err_select": strResult = (!objTexts || !objTexts.err_select) ? objTextsInternal.err_select : objTexts.err_select; break; + } + return strResult; + } + + function _GenerateFormFields(form){ + var arr = new Array(); + for (var i = 0; i < form.length; ++i){ + var element = form.elements[i]; + var index = _getElementIndex(arr,element); + //if it doesn't already exist, add it to our array, else merge the change + if (index == -1){ + arr[arr.length] = new Field(element, form); + }else{ + arr[index].Merge(element) + } + } + return arr; +} +function _getElementIndex(arr, element){ + var elementName = element.name.toLowerCase(); + for (var i = 0; i < arr.length; ++i){ + if (arr[i].element.name.toLowerCase() == elementName){ + return i; + } + } + return -1; +} + +/*************************************************************** +** Standard translation +*****************************************************************/ +function _jsVal_Language() { + this.err_form = "Please enter/select values for the following fields:\n\n"; + this.err_select = "Please select a valid \"%FIELDNAME%\""; + this.err_enter = "Please enter a valid \"%FIELDNAME%\""; +} + +/*************************************************************** +** Field Class +*****************************************************************/ +function Field(element, form){ + if (!element.oldClassName) element.oldClassName = element.className; + this.type = element.type; + this.element = element; + this.exclude = element.exclude || element.getAttribute('exclude'); + this.err = element.err || element.getAttribute('err'); + this.required = _parseBoolean(element.required || element.getAttribute('required')); + this.realname = element.realname || element.getAttribute('realname'); + this.elements = new Array(); + + switch (this.type){ + case "textarea": + case "password": + case "text": + this.value = element.value; + this.minLength = element.minlength || element.getAttribute('minlength'); + this.maxLength = element.maxlength || element.getAttribute('maxlength'); + this.regexp = this._getRegEx(element); + this.minValue = element.minvalue || element.getAttribute('minvalue'); + this.maxValue = element.maxvalue || element.getAttribute('maxvalue'); + this.equals = element.equals || element.getAttribute('equals'); + break; + case "select-one": + case "select-multiple": + this.values = new Array(); + for (var i = 0; i < element.options.length; ++i){ + if (element.options[i].selected && (!this.exclude || element.options[i].value != this.exclude)){ + this.values[this.values.length] = element.options[i].value; + } + } + this.min = element.min || element.getAttribute('min'); + this.max = element.max || element.getAttribute('max'); + this.equals = element.equals || element.getAttribute('equals'); + break; + case "checkbox": + this.min = element.min || element.getAttribute('min'); + this.max = element.max || element.getAttribute('max'); + //no break, let it fall through to radio + case "radio": + this.required = _parseBoolean(this.required || element.getAttribute('required')); + this.values = new Array(); + if (element.checked){ + this.values[0] = element.value; + } + + this.elements[0] = element; + break; + } +} +Field.prototype.Merge = function(element){ + //never negate a require field + var required = _parseBoolean(element.getAttribute('required')); + if (required){ + this.required = true; + } + //all other cases (except required) we only add if there isn't already a value (first come first served) + if (!this.err){ + this.err = element.getAttribute('err'); + } + if (!this.equals){ + this.equals = element.getAttribute('equals'); + } + if (!this.realname){ + this.realname = element.getAttribute('realname'); + } + if (!this.max){ + this.max = element.getAttribute('max'); + } + if (!this.min){ + this.min = element.getAttribute('min'); + } + if (!this.regexp){ + this.regexp = this._getRegEx(element); + } + if (element.checked){ + this.values[this.values.length] = element.value; + } + this.elements[this.elements.length] = element; +} +Field.prototype.IsValid = function(arrFields){ + switch (this.type){ + case "textarea": + case "password": + case "text": + return this._ValidateText(arrFields); + case "select-one": + case "select-multiple": + case "radio": + case "checkbox": + return this._ValidateGroup(arrFields); + default: + return true; + } +} +Field.prototype.SetClass = function(newClassName){ + if ( (this.elements) && (this.elements.length > 0)) { + for (var i = 0; i < this.elements.length; ++i){ + this.elements[i].oldClassName = this.elements[i].className; + this.elements[i].className = newClassName; + } + }else{ + this.element.oldClassName = this.element.className; + this.element.className = newClassName; + } +} +Field.prototype.ResetClass = function(){ + if ( (this.type != "button") && (this.type != "submit") && (this.type != "reset") ) { + if ( (this.elements) && (this.elements.length > 0)) { + for (var i = 0; i < this.elements.length; ++i){ + this.elements[i].className = this.elements[i].oldClassName; + } + }else{ + this.element.className = this.element.oldClassName; + } + } +} +Field.prototype._getRegEx = function(element){ + regex = element.regexp || element.getAttribute('regexp') + if (regex == null) return null; + retype = typeof(regex); + if (retype.toUpperCase() == "FUNCTION") + return regex; + else if ( (retype.toUpperCase() == "STRING") && !(regex == "email") && !(regex == "tel") + && !(regex == "pc") && !(regex == "zip") && !(regex == "money") + && !(regex == "creditcard") && !(regex == "postalzip")) + { + nBegin = 0; nEnd = regex.length-1; + if (regex.charAt(0) == "/") nBegin=1; + if (regex.charAt(regex.length-1) == "/") nEnd=regex.length-2; + + return new RegExp(regex.slice(nBegin, nEnd)); + } + else { + return regex; + } +} +Field.prototype._ValidateText = function(arrFields){ + //required value is empty + if (this.required && !this.value){ + return false; + } + //value less than minlength + if (this.value && (this.minLength && this.value.length < this.minLength)){ + return false; + } + //value is more than maxlength + if (this.value && (this.maxLength && this.value.length > this.maxLength)){ + return false; + } + //value fails regular expression + if (this.regexp){ + if (!_checkRegExp(this.regexp, this.value)) + { + //the field isn't required, but there is a value + if (!this.required && this.value){ + return false; + } + if (this.required){ + return false; + } + } + else + { + return true; + } + } + + //check equality + if (this.equals){ + for (var i = 0; i < arrFields.length; ++i){ + var field = arrFields[i]; + if ( (field.element.name == this.equals) || (field.element.id == this.equals) ) { + if (field.element.value != this.value) { + return false; + } + break; + } + } + } + + //check against minvalue and maxvalue + if (this.required){ + var fValue = parseFloat(this.value); + if ((this.minValue || this.maxValue) && isNaN(fValue)){ + return false; + } + if ( (this.minValue) && (fValue < this.minValue) ) { + return false; + } + if ( (this.maxValue) && (fValue > this.maxValue) ) { + return false + } + } + return true; +} +Field.prototype._ValidateGroup = function(arrFields){ + if (this.required && this.values.length == 0){ + return false; + } + if (this.required && this.min && this.min > this.values.length){ + return false; + } + if (this.required && this.max && this.max < this.values.length){ + return false; + } + return true; +} + +function _handleError (field, strErrorMessage) { + var obj = field.element; + strNewMessage = strErrorMessage + ( (field.realname)? field.realname : ((obj.id) ? obj.id : obj.name) ) + "\n"; + return strNewMessage; +} + +function _throwError(field){ + var obj = field.element; + switch (field.type){ + case "text": + case "password": + case "textarea": + alert(_getError(field, "err_enter")); + try { + obj.focus(); + } + catch (ignore) {} + break; + case "select-one": + case "select-multiple": + case "radio": + case "checkbox": + alert(_getError(field, "err_select")); + break; + } +} + +function _getError(field, str){ + var obj = field.element; + strErrorTemp = (field.err) ? field.err : _getLanguageText(str); + + idx = strErrorTemp.indexOf( "\\n" ); + while ( idx > -1 ) { + strErrorTemp = strErrorTemp.replace( "\\n", "\n" ); + idx = strErrorTemp.indexOf( "\\n" ); + } + + return strErrorTemp.replace("%FIELDNAME%", (field.realname)? field.realname : ((obj.id) ? obj.id : obj.name)); +} + +function _parseBoolean(value){ + return !(!value || value == 0 || value == "0" || value == "false"); +} + +function _checkRegExp(regx, value){ + switch (regx){ + case "email": + return ((/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,5})+$/).test(value)); + case "tel": + return ((/^1?[\- ]?\(?\d{3}\)?[\- ]?\d{3}[\- ]?\d{4}$/).test(value)); + case "pc": + return ((/^[a-z]\d[a-z] ?\d[a-z]\d$/i).test(value)); + case "zip": + return ((/^\d{5}$/).test(value)); + case "money": + return ((/^\d+([\.]\d\d)?$/).test(value)); + case "creditcard": + return (!isNaN(value)); + case "postalzip": + if(value.length == 6 || value.length == 7) + return((/^[a-zA-Z]\d[a-zA-Z] ?\d[a-zA-Z]\d$/).test(value)); + if(value.length == 5 || value.length == 10) + return((/^\d{5}(\-\d{4})?$/).test(value)); + break; + default: + return (regx.test(value)); + + } +} \ No newline at end of file diff --git a/src/www/js/nicetitle.js b/src/www/js/nicetitle.js new file mode 100644 index 00000000..4207a9bf --- /dev/null +++ b/src/www/js/nicetitle.js @@ -0,0 +1,220 @@ +addEvent(window, "load", makeNiceTitles); + +var XHTMLNS = "http://www.w3.org/1999/xhtml"; +var CURRENT_NICE_TITLE; +var browser = new Browser(); + +function makeNiceTitles() { + if (!document.createElement || !document.getElementsByTagName) return; + // add namespace methods to HTML DOM; this makes the script work in both + // HTML and XML contexts. + if(!document.createElementNS) + { + document.createElementNS = function(ns,elt) { + return document.createElement(elt); + } + } + + if( !document.links ) + { + document.links = document.getElementsByTagName("a"); + } + for (var ti=0;ti STD_WIDTH) { + w = h_pixels; + } else if ((STD_WIDTH>t_pixels) && (t_pixels>h_pixels)) { + w = t_pixels; + } else if ((STD_WIDTH>t_pixels) && (h_pixels>t_pixels)) { + w = h_pixels; + } else { + w = STD_WIDTH; + } + + d.style.width = w + 'px'; + + /* + mx = lnk.offsetLeft; + my = lnk.offsetTop; + */ + mpos = findPosition(lnk); + mx = mpos[0]; + my = mpos[1]; + //xy = getMousePosition(e); + //mx = xy[0]; my = xy[1]; + + d.style.left = (mx+15) + 'px'; + d.style.top = (my+35) + 'px'; + if (window.innerWidth && ((mx+w) > window.innerWidth)) { + d.style.left = (window.innerWidth - w - 25) + "px"; + } + if (document.body.scrollWidth && ((mx+w) > document.body.scrollWidth)) { + d.style.left = (document.body.scrollWidth - w - 25) + "px"; + } + + document.getElementsByTagName("body")[0].appendChild(d); + + CURRENT_NICE_TITLE = d; +} + +function hideNiceTitle(e) { + if (!document.getElementsByTagName) return; + if (CURRENT_NICE_TITLE) { + document.getElementsByTagName("body")[0].removeChild(CURRENT_NICE_TITLE); + CURRENT_NICE_TITLE = null; + } +} + +// Add an eventListener to browsers that can do it somehow. +// Originally by the amazing Scott Andrew. +function addEvent(obj, evType, fn){ + if (obj.addEventListener){ + obj.addEventListener(evType, fn, true); + return true; + } else if (obj.attachEvent){ + var r = obj.attachEvent("on"+evType, fn); + return r; + } else { + return false; + } +} + +function getParent(el, pTagName) { + if (el == null) return null; + else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase()) // Gecko bug, supposed to be uppercase + return el; + else + return getParent(el.parentNode, pTagName); +} + +function getMousePosition(event) { + if (browser.isIE) { + x = window.event.clientX + document.documentElement.scrollLeft + + document.body.scrollLeft; + y = window.event.clientY + document.documentElement.scrollTop + + document.body.scrollTop; + } + if (browser.isNS) { + x = event.clientX + window.scrollX; + y = event.clientY + window.scrollY; + } + return [x,y]; +} + +// Determine browser and version. + +function Browser() { +// blah, browser detect, but mouse-position stuff doesn't work any other way + var ua, s, i; + + this.isIE = false; + this.isNS = false; + this.version = null; + + ua = navigator.userAgent; + + s = "MSIE"; + if ((i = ua.indexOf(s)) >= 0) { + this.isIE = true; + this.version = parseFloat(ua.substr(i + s.length)); + return; + } + + s = "Netscape6/"; + if ((i = ua.indexOf(s)) >= 0) { + this.isNS = true; + this.version = parseFloat(ua.substr(i + s.length)); + return; + } + + // Treat any other "Gecko" browser as NS 6.1. + + s = "Gecko"; + if ((i = ua.indexOf(s)) >= 0) { + this.isNS = true; + this.version = 6.1; + return; + } +} + diff --git a/src/www/js/uts.js b/src/www/js/uts.js new file mode 100644 index 00000000..8731d3d7 --- /dev/null +++ b/src/www/js/uts.js @@ -0,0 +1,82 @@ + \ No newline at end of file diff --git a/src/www/lib/Sec.php b/src/www/lib/Sec.php new file mode 100644 index 00000000..d7d2f75e --- /dev/null +++ b/src/www/lib/Sec.php @@ -0,0 +1,270 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> +socket = null; + $this->name = $name; + $this->type = $type; + $this->props = $props; + } + + function connect($addr = null, $port = null) { + if($this->socket != null) { + $this->disconnect(); + } + + $this->socket = new Socket(); + + if(!$addr || !$port) { + $addr = UTS_DEFAULT_SERVER; + $servers = getServers($addr); + $port = $servers[$this->name . $this->type]; + } + + if(!$this->socket->connect($addr, $port)) { + return FALSE; + } + + // check SERVER_FULL + $full = $this->socket->read(); + $full = trim($this->removeNULL($full)); + + if($full == "SERVER_FULL") { + return FALSE; + } + + // ident + $this->socket->write($this->appendNULL("IDENT INSTRUMENT")); + + $str = $this->socket->read(); + $str = $this->removeNULL($str); + + if($str == "ERROR") { + return FALSE; + } else { + return TRUE; + } + + } + + function getStatus($prop) { + if($this->socket == null) { + return FALSE; + } + + if(in_array($prop, $this->props)) { + $this->socket->write($this->appendNULL("STATUS " . $prop)); + + $str = $this->socket->read(); + + // format + $str = trim($this->removeNULL($str)); + + if($str == "ERROR") { + $ret = $str; + } else { + $ret = substr($str, strlen("STATUS"), strlen($str)); + } + } else { + $ret = "PROPRIEDADE INVÁLIDA"; + } + + if($this->interactive) { + printf("%s %s
", "Lendo $prop ...", "$ret"); + } + + return $ret; + + } + + function setStatus($prop, $value) { + if($this->socket == null) { + return FALSE; + } + + if(in_array($prop, $this->props)) { + $this->socket->write($this->appendNULL("SETSTATUS " . $prop . " " . ($value))); + + $str = $this->socket->read(); + $ret = trim($this->removeNULL($str)); + } else { + $ret = "PROPRIEDADE INVÁLIDA"; + } + + if($this->interactive) { + printf("%s %s
", "Setando $prop = $value ...", "$ret"); + } + + return $ret; + + } + + function notify($prop) { + + if($this->socket == null) { + return FALSE; + } + + if(in_array($prop, $this->props)) { + + $this->socket->write($this->appendNULL("NOTIFY " . $prop)); + + $str = $this->socket->read(); + $ret = trim($this->removeNULL($str)); + + // wait until notification arrive + $read = array($this->socket); + $num = socket_select($read, $write = NULL, $exp = NULL, NULL); + + if($num) { + $str = $this->socket->read(); + $ret = trim($this->removeNULL($str)); + } + + } else { + $ret = "PROPRIEDADE INVÁLIDA"; + } + + return $ret; + + } + + function isBusy() { + if($this->socket == null) { + return FALSE; + } + + $ret = $this->getStatus($this->name); + + $cmp = trim($ret); + + if($cmp == "OFFLINE" || $cmp == "IDLE" || $cmp == "DISABLED") + return FALSE; + else + return TRUE; + + } + + function appendNULL($str) { + return $str . "\x00"; + } + + function removeNULL($str) { + + return substr($str, 0, strlen($str) -1); + + } + + function disconnect() { + if($this->socket != null) { + $this->socket->write($this->appendNULL("QUIT")); + $this->socket->disconnect(); + } + + } + + function setInteractive($interactive) { + $this->interactive = $interactive; + } + +} + + +class Sync extends Sec { + + function Sync($type = 'control') { + + $props = array( + "SYNC", + "EXPTIME", + "NEXP", + "BFNAME", + "INDEX", + "TELSTART", + "CAMSTART", + "OBSERVER", + "RA", + "DEC", + "EPOCH", + "FILTER", + "TASK"); + + return parent::Sec("SYNC", $type, $props); + + } + +} + +class CCD extends Sec { + + function CCD($type = 'control') { + + $props = array( + "CCD", + "TASK", + "TYPE", + "CCD_TRIGGER", + "MOVING"); + + + $this->type = $type; + + return parent::Sec("CCD", $type, $props); + + } + +} + +class Tel extends Sec { + + function Tel($type = 'control') { + + $props = array( + "TEL", + "TASK", + "TYPE", + "CCD_TRIGGER", + "MOVING"); + + return parent::Sec("TEL", $type, $props); + + } + +} + +?> \ No newline at end of file diff --git a/src/www/lib/Socket.php b/src/www/lib/Socket.php new file mode 100644 index 00000000..75362400 --- /dev/null +++ b/src/www/lib/Socket.php @@ -0,0 +1,92 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> +fp) { + socket_shutdown($this->fp, 2); + socket_close($this->fp); + $this->fp = NULL; + } + + if (strspn($addr, '.0123456789') == strlen($addr)) { + $this->addr = $addr; + } else { + $this->addr = gethostbyname($addr); + } + + $this->port = $port; + + $this->fp = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + if (!$this->fp) { + return FALSE; + } + + if(!socket_connect($this->fp, $this->addr, $this->port)) { + return FALSE; + } + + return TRUE; + } + + function disconnect() { + if($this->fp) { + socket_shutdown($this->fp, 2); + socket_close($this->fp); + $this->fp = NULL; + return TRUE; + } + + return FALSE; + } + + function read($size = 1024) { + if ($this->fp) { + return socket_read($this->fp, $size); + } + + return FALSE; + } + + function write($data) { + if ($this->fp) { + return socket_write($this->fp, $data, strlen($data)); + // $ret = socket_write($this->fp, $data, strlen($data)); + // echo "$ret
"; + // return $ret; + } + + return FALSE; + } + +} +?> diff --git a/src/www/lib/astro.php b/src/www/lib/astro.php new file mode 100644 index 00000000..31a2d18a --- /dev/null +++ b/src/www/lib/astro.php @@ -0,0 +1,20 @@ + \ No newline at end of file diff --git a/src/www/lib/db.php b/src/www/lib/db.php new file mode 100644 index 00000000..b474671f --- /dev/null +++ b/src/www/lib/db.php @@ -0,0 +1,40 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> +getMessage()); +} + +?> diff --git a/src/www/lib/erro.php b/src/www/lib/erro.php new file mode 100644 index 00000000..11555fb1 --- /dev/null +++ b/src/www/lib/erro.php @@ -0,0 +1,79 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> + + +
+ +
+ + diff --git a/src/www/lib/pear/DB.php b/src/www/lib/pear/DB.php new file mode 100644 index 00000000..9af57b3e --- /dev/null +++ b/src/www/lib/pear/DB.php @@ -0,0 +1,1388 @@ + + * @author Tomas V.V.Cox + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: DB.php,v 1.1 2005/05/28 01:55:09 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the PEAR class so it can be extended from + */ +require_once 'PEAR.php'; + + +// {{{ constants +// {{{ error codes + +/**#@+ + * One of PEAR DB's portable error codes. + * @see DB_common::errorCode(), DB::errorMessage() + * + * {@internal If you add an error code here, make sure you also add a textual + * version of it in DB::errorMessage().}} + */ + +/** + * The code returned by many methods upon success + */ +define('DB_OK', 1); + +/** + * Unkown error + */ +define('DB_ERROR', -1); + +/** + * Syntax error + */ +define('DB_ERROR_SYNTAX', -2); + +/** + * Tried to insert a duplicate value into a primary or unique index + */ +define('DB_ERROR_CONSTRAINT', -3); + +/** + * An identifier in the query refers to a non-existant object + */ +define('DB_ERROR_NOT_FOUND', -4); + +/** + * Tried to create a duplicate object + */ +define('DB_ERROR_ALREADY_EXISTS', -5); + +/** + * The current driver does not support the action you attempted + */ +define('DB_ERROR_UNSUPPORTED', -6); + +/** + * The number of parameters does not match the number of placeholders + */ +define('DB_ERROR_MISMATCH', -7); + +/** + * A literal submitted did not match the data type expected + */ +define('DB_ERROR_INVALID', -8); + +/** + * The current DBMS does not support the action you attempted + */ +define('DB_ERROR_NOT_CAPABLE', -9); + +/** + * A literal submitted was too long so the end of it was removed + */ +define('DB_ERROR_TRUNCATED', -10); + +/** + * A literal number submitted did not match the data type expected + */ +define('DB_ERROR_INVALID_NUMBER', -11); + +/** + * A literal date submitted did not match the data type expected + */ +define('DB_ERROR_INVALID_DATE', -12); + +/** + * Attempt to divide something by zero + */ +define('DB_ERROR_DIVZERO', -13); + +/** + * A database needs to be selected + */ +define('DB_ERROR_NODBSELECTED', -14); + +/** + * Could not create the object requested + */ +define('DB_ERROR_CANNOT_CREATE', -15); + +/** + * Could not drop the database requested because it does not exist + */ +define('DB_ERROR_CANNOT_DROP', -17); + +/** + * An identifier in the query refers to a non-existant table + */ +define('DB_ERROR_NOSUCHTABLE', -18); + +/** + * An identifier in the query refers to a non-existant column + */ +define('DB_ERROR_NOSUCHFIELD', -19); + +/** + * The data submitted to the method was inappropriate + */ +define('DB_ERROR_NEED_MORE_DATA', -20); + +/** + * The attempt to lock the table failed + */ +define('DB_ERROR_NOT_LOCKED', -21); + +/** + * The number of columns doesn't match the number of values + */ +define('DB_ERROR_VALUE_COUNT_ON_ROW', -22); + +/** + * The DSN submitted has problems + */ +define('DB_ERROR_INVALID_DSN', -23); + +/** + * Could not connect to the database + */ +define('DB_ERROR_CONNECT_FAILED', -24); + +/** + * The PHP extension needed for this DBMS could not be found + */ +define('DB_ERROR_EXTENSION_NOT_FOUND',-25); + +/** + * The present user has inadequate permissions to perform the task requestd + */ +define('DB_ERROR_ACCESS_VIOLATION', -26); + +/** + * The database requested does not exist + */ +define('DB_ERROR_NOSUCHDB', -27); + +/** + * Tried to insert a null value into a column that doesn't allow nulls + */ +define('DB_ERROR_CONSTRAINT_NOT_NULL',-29); +/**#@-*/ + + +// }}} +// {{{ prepared statement-related + + +/**#@+ + * Identifiers for the placeholders used in prepared statements. + * @see DB_common::prepare() + */ + +/** + * Indicates a scalar (?) placeholder was used + * + * Quote and escape the value as necessary. + */ +define('DB_PARAM_SCALAR', 1); + +/** + * Indicates an opaque (&) placeholder was used + * + * The value presented is a file name. Extract the contents of that file + * and place them in this column. + */ +define('DB_PARAM_OPAQUE', 2); + +/** + * Indicates a misc (!) placeholder was used + * + * The value should not be quoted or escaped. + */ +define('DB_PARAM_MISC', 3); +/**#@-*/ + + +// }}} +// {{{ binary data-related + + +/**#@+ + * The different ways of returning binary data from queries. + */ + +/** + * Sends the fetched data straight through to output + */ +define('DB_BINMODE_PASSTHRU', 1); + +/** + * Lets you return data as usual + */ +define('DB_BINMODE_RETURN', 2); + +/** + * Converts the data to hex format before returning it + * + * For example the string "123" would become "313233". + */ +define('DB_BINMODE_CONVERT', 3); +/**#@-*/ + + +// }}} +// {{{ fetch modes + + +/**#@+ + * Fetch Modes. + * @see DB_common::setFetchMode() + */ + +/** + * Indicates the current default fetch mode should be used + * @see DB_common::$fetchmode + */ +define('DB_FETCHMODE_DEFAULT', 0); + +/** + * Column data indexed by numbers, ordered from 0 and up + */ +define('DB_FETCHMODE_ORDERED', 1); + +/** + * Column data indexed by column names + */ +define('DB_FETCHMODE_ASSOC', 2); + +/** + * Column data as object properties + */ +define('DB_FETCHMODE_OBJECT', 3); + +/** + * For multi-dimensional results, make the column name the first level + * of the array and put the row number in the second level of the array + * + * This is flipped from the normal behavior, which puts the row numbers + * in the first level of the array and the column names in the second level. + */ +define('DB_FETCHMODE_FLIPPED', 4); +/**#@-*/ + +/**#@+ + * Old fetch modes. Left here for compatibility. + */ +define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED); +define('DB_GETMODE_ASSOC', DB_FETCHMODE_ASSOC); +define('DB_GETMODE_FLIPPED', DB_FETCHMODE_FLIPPED); +/**#@-*/ + + +// }}} +// {{{ tableInfo() && autoPrepare()-related + + +/**#@+ + * The type of information to return from the tableInfo() method. + * + * Bitwised constants, so they can be combined using | + * and removed using ^. + * + * @see DB_common::tableInfo() + * + * {@internal Since the TABLEINFO constants are bitwised, if more of them are + * added in the future, make sure to adjust DB_TABLEINFO_FULL accordingly.}} + */ +define('DB_TABLEINFO_ORDER', 1); +define('DB_TABLEINFO_ORDERTABLE', 2); +define('DB_TABLEINFO_FULL', 3); +/**#@-*/ + + +/**#@+ + * The type of query to create with the automatic query building methods. + * @see DB_common::autoPrepare(), DB_common::autoExecute() + */ +define('DB_AUTOQUERY_INSERT', 1); +define('DB_AUTOQUERY_UPDATE', 2); +/**#@-*/ + + +// }}} +// {{{ portability modes + + +/**#@+ + * Portability Modes. + * + * Bitwised constants, so they can be combined using | + * and removed using ^. + * + * @see DB_common::setOption() + * + * {@internal Since the PORTABILITY constants are bitwised, if more of them are + * added in the future, make sure to adjust DB_PORTABILITY_ALL accordingly.}} + */ + +/** + * Turn off all portability features + */ +define('DB_PORTABILITY_NONE', 0); + +/** + * Convert names of tables and fields to lower case + * when using the get*(), fetch*() and tableInfo() methods + */ +define('DB_PORTABILITY_LOWERCASE', 1); + +/** + * Right trim the data output by get*() and fetch*() + */ +define('DB_PORTABILITY_RTRIM', 2); + +/** + * Force reporting the number of rows deleted + */ +define('DB_PORTABILITY_DELETE_COUNT', 4); + +/** + * Enable hack that makes numRows() work in Oracle + */ +define('DB_PORTABILITY_NUMROWS', 8); + +/** + * Makes certain error messages in certain drivers compatible + * with those from other DBMS's + * + * + mysql, mysqli: change unique/primary key constraints + * DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT + * + * + odbc(access): MS's ODBC driver reports 'no such field' as code + * 07001, which means 'too few parameters.' When this option is on + * that code gets mapped to DB_ERROR_NOSUCHFIELD. + */ +define('DB_PORTABILITY_ERRORS', 16); + +/** + * Convert null values to empty strings in data output by + * get*() and fetch*() + */ +define('DB_PORTABILITY_NULL_TO_EMPTY', 32); + +/** + * Turn on all portability features + */ +define('DB_PORTABILITY_ALL', 63); +/**#@-*/ + +// }}} + + +// }}} +// {{{ class DB + +/** + * Database independent query interface + * + * The main "DB" class is simply a container class with some static + * methods for creating DB objects as well as some utility functions + * common to all parts of DB. + * + * The object model of DB is as follows (indentation means inheritance): + *
+ * DB           The main DB class.  This is simply a utility class
+ *              with some "static" methods for creating DB objects as
+ *              well as common utility functions for other DB classes.
+ *
+ * DB_common    The base for each DB implementation.  Provides default
+ * |            implementations (in OO lingo virtual methods) for
+ * |            the actual DB implementations as well as a bunch of
+ * |            query utility functions.
+ * |
+ * +-DB_mysql   The DB implementation for MySQL.  Inherits DB_common.
+ *              When calling DB::factory or DB::connect for MySQL
+ *              connections, the object returned is an instance of this
+ *              class.
+ * 
+ * + * @category Database + * @package DB + * @author Stig Bakken + * @author Tomas V.V.Cox + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB +{ + // {{{ &factory() + + /** + * Create a new DB object for the specified database type but don't + * connect to the database + * + * @param string $type the database type (eg "mysql") + * @param array $options an associative array of option names and values + * + * @return object a new DB object. A DB_Error object on failure. + * + * @see DB_common::setOption() + */ + function &factory($type, $options = false) + { + if (!is_array($options)) { + $options = array('persistent' => $options); + } + + if (isset($options['debug']) && $options['debug'] >= 2) { + // expose php errors with sufficient debug level + include_once "DB/{$type}.php"; + } else { + @include_once "DB/{$type}.php"; + } + + $classname = "DB_${type}"; + + if (!class_exists($classname)) { + $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null, + "Unable to include the DB/{$type}.php" + . " file for '$dsn'", + 'DB_Error', true); + return $tmp; + } + + @$obj =& new $classname; + + foreach ($options as $option => $value) { + $test = $obj->setOption($option, $value); + if (DB::isError($test)) { + return $test; + } + } + + return $obj; + } + + // }}} + // {{{ &connect() + + /** + * Create a new DB object including a connection to the specified database + * + * Example 1. + * + * require_once 'DB.php'; + * + * $dsn = 'pgsql://user:password@host/database'; + * $options = array( + * 'debug' => 2, + * 'portability' => DB_PORTABILITY_ALL, + * ); + * + * $db =& DB::connect($dsn, $options); + * if (PEAR::isError($db)) { + * die($db->getMessage()); + * } + * + * + * @param mixed $dsn the string "data source name" or array in the + * format returned by DB::parseDSN() + * @param array $options an associative array of option names and values + * + * @return object a new DB object. A DB_Error object on failure. + * + * @uses DB_dbase::connect(), DB_fbsql::connect(), DB_ibase::connect(), + * DB_ifx::connect(), DB_msql::connect(), DB_mssql::connect(), + * DB_mysql::connect(), DB_mysqli::connect(), DB_oci8::connect(), + * DB_odbc::connect(), DB_pgsql::connect(), DB_sqlite::connect(), + * DB_sybase::connect() + * + * @uses DB::parseDSN(), DB_common::setOption(), PEAR::isError() + */ + function &connect($dsn, $options = array()) + { + $dsninfo = DB::parseDSN($dsn); + $type = $dsninfo['phptype']; + + if (!is_array($options)) { + /* + * For backwards compatibility. $options used to be boolean, + * indicating whether the connection should be persistent. + */ + $options = array('persistent' => $options); + } + + if (isset($options['debug']) && $options['debug'] >= 2) { + // expose php errors with sufficient debug level + include_once "DB/${type}.php"; + } else { + @include_once "DB/${type}.php"; + } + + $classname = "DB_${type}"; + if (!class_exists($classname)) { + $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null, + "Unable to include the DB/{$type}.php" + . " file for '$dsn'", + 'DB_Error', true); + return $tmp; + } + + @$obj =& new $classname; + + foreach ($options as $option => $value) { + $test = $obj->setOption($option, $value); + if (DB::isError($test)) { + return $test; + } + } + + $err = $obj->connect($dsninfo, $obj->getOption('persistent')); + if (DB::isError($err)) { + $err->addUserInfo($dsn); + return $err; + } + + return $obj; + } + + // }}} + // {{{ apiVersion() + + /** + * Return the DB API version + * + * @return string the DB API version number + */ + function apiVersion() + { + return '@package_version@'; + } + + // }}} + // {{{ isError() + + /** + * Determines if a variable is a DB_Error object + * + * @param mixed $value the variable to check + * + * @return bool whether $value is DB_Error object + */ + function isError($value) + { + return is_a($value, 'DB_Error'); + } + + // }}} + // {{{ isConnection() + + /** + * Determines if a value is a DB_ object + * + * @param mixed $value the value to test + * + * @return bool whether $value is a DB_ object + */ + function isConnection($value) + { + return (is_object($value) && + is_subclass_of($value, 'db_common') && + method_exists($value, 'simpleQuery')); + } + + // }}} + // {{{ isManip() + + /** + * Tell whether a query is a data manipulation or data definition query + * + * Examples of data manipulation queries are INSERT, UPDATE and DELETE. + * Examples of data definition queries are CREATE, DROP, ALTER, GRANT, + * REVOKE. + * + * @param string $query the query + * + * @return boolean whether $query is a data manipulation query + */ + function isManip($query) + { + $manips = 'INSERT|UPDATE|DELETE|REPLACE|' + . 'CREATE|DROP|' + . 'LOAD DATA|SELECT .* INTO|COPY|' + . 'ALTER|GRANT|REVOKE|' + . 'LOCK|UNLOCK'; + if (preg_match('/^\s*"?(' . $manips . ')\s+/i', $query)) { + return true; + } + return false; + } + + // }}} + // {{{ errorMessage() + + /** + * Return a textual error message for a DB error code + * + * @param integer $value the DB error code + * + * @return string the error message or false if the error code was + * not recognized + */ + function errorMessage($value) + { + static $errorMessages; + if (!isset($errorMessages)) { + $errorMessages = array( + DB_ERROR => 'unknown error', + DB_ERROR_ACCESS_VIOLATION => 'insufficient permissions', + DB_ERROR_ALREADY_EXISTS => 'already exists', + DB_ERROR_CANNOT_CREATE => 'can not create', + DB_ERROR_CANNOT_DROP => 'can not drop', + DB_ERROR_CONNECT_FAILED => 'connect failed', + DB_ERROR_CONSTRAINT => 'constraint violation', + DB_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint', + DB_ERROR_DIVZERO => 'division by zero', + DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found', + DB_ERROR_INVALID => 'invalid', + DB_ERROR_INVALID_DATE => 'invalid date or time', + DB_ERROR_INVALID_DSN => 'invalid DSN', + DB_ERROR_INVALID_NUMBER => 'invalid number', + DB_ERROR_MISMATCH => 'mismatch', + DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied', + DB_ERROR_NODBSELECTED => 'no database selected', + DB_ERROR_NOSUCHDB => 'no such database', + DB_ERROR_NOSUCHFIELD => 'no such field', + DB_ERROR_NOSUCHTABLE => 'no such table', + DB_ERROR_NOT_CAPABLE => 'DB backend not capable', + DB_ERROR_NOT_FOUND => 'not found', + DB_ERROR_NOT_LOCKED => 'not locked', + DB_ERROR_SYNTAX => 'syntax error', + DB_ERROR_UNSUPPORTED => 'not supported', + DB_ERROR_TRUNCATED => 'truncated', + DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row', + DB_OK => 'no error', + ); + } + + if (DB::isError($value)) { + $value = $value->getCode(); + } + + return isset($errorMessages[$value]) ? $errorMessages[$value] + : $errorMessages[DB_ERROR]; + } + + // }}} + // {{{ parseDSN() + + /** + * Parse a data source name + * + * Additional keys can be added by appending a URI query string to the + * end of the DSN. + * + * The format of the supplied DSN is in its fullest form: + * + * phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true + * + * + * Most variations are allowed: + * + * phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644 + * phptype://username:password@hostspec/database_name + * phptype://username:password@hostspec + * phptype://username@hostspec + * phptype://hostspec/database + * phptype://hostspec + * phptype(dbsyntax) + * phptype + * + * + * @param string $dsn Data Source Name to be parsed + * + * @return array an associative array with the following keys: + * + phptype: Database backend used in PHP (mysql, odbc etc.) + * + dbsyntax: Database used with regards to SQL syntax etc. + * + protocol: Communication protocol to use (tcp, unix etc.) + * + hostspec: Host specification (hostname[:port]) + * + database: Database to use on the DBMS server + * + username: User name for login + * + password: Password for login + */ + function parseDSN($dsn) + { + $parsed = array( + 'phptype' => false, + 'dbsyntax' => false, + 'username' => false, + 'password' => false, + 'protocol' => false, + 'hostspec' => false, + 'port' => false, + 'socket' => false, + 'database' => false, + ); + + if (is_array($dsn)) { + $dsn = array_merge($parsed, $dsn); + if (!$dsn['dbsyntax']) { + $dsn['dbsyntax'] = $dsn['phptype']; + } + return $dsn; + } + + // Find phptype and dbsyntax + if (($pos = strpos($dsn, '://')) !== false) { + $str = substr($dsn, 0, $pos); + $dsn = substr($dsn, $pos + 3); + } else { + $str = $dsn; + $dsn = null; + } + + // Get phptype and dbsyntax + // $str => phptype(dbsyntax) + if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) { + $parsed['phptype'] = $arr[1]; + $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2]; + } else { + $parsed['phptype'] = $str; + $parsed['dbsyntax'] = $str; + } + + if (!count($dsn)) { + return $parsed; + } + + // Get (if found): username and password + // $dsn => username:password@protocol+hostspec/database + if (($at = strrpos($dsn,'@')) !== false) { + $str = substr($dsn, 0, $at); + $dsn = substr($dsn, $at + 1); + if (($pos = strpos($str, ':')) !== false) { + $parsed['username'] = rawurldecode(substr($str, 0, $pos)); + $parsed['password'] = rawurldecode(substr($str, $pos + 1)); + } else { + $parsed['username'] = rawurldecode($str); + } + } + + // Find protocol and hostspec + + if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) { + // $dsn => proto(proto_opts)/database + $proto = $match[1]; + $proto_opts = $match[2] ? $match[2] : false; + $dsn = $match[3]; + + } else { + // $dsn => protocol+hostspec/database (old format) + if (strpos($dsn, '+') !== false) { + list($proto, $dsn) = explode('+', $dsn, 2); + } + if (strpos($dsn, '/') !== false) { + list($proto_opts, $dsn) = explode('/', $dsn, 2); + } else { + $proto_opts = $dsn; + $dsn = null; + } + } + + // process the different protocol options + $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp'; + $proto_opts = rawurldecode($proto_opts); + if ($parsed['protocol'] == 'tcp') { + if (strpos($proto_opts, ':') !== false) { + list($parsed['hostspec'], + $parsed['port']) = explode(':', $proto_opts); + } else { + $parsed['hostspec'] = $proto_opts; + } + } elseif ($parsed['protocol'] == 'unix') { + $parsed['socket'] = $proto_opts; + } + + // Get dabase if any + // $dsn => database + if ($dsn) { + if (($pos = strpos($dsn, '?')) === false) { + // /database + $parsed['database'] = rawurldecode($dsn); + } else { + // /database?param1=value1¶m2=value2 + $parsed['database'] = rawurldecode(substr($dsn, 0, $pos)); + $dsn = substr($dsn, $pos + 1); + if (strpos($dsn, '&') !== false) { + $opts = explode('&', $dsn); + } else { // database?param1=value1 + $opts = array($dsn); + } + foreach ($opts as $opt) { + list($key, $value) = explode('=', $opt); + if (!isset($parsed[$key])) { + // don't allow params overwrite + $parsed[$key] = rawurldecode($value); + } + } + } + } + + return $parsed; + } + + // }}} +} + +// }}} +// {{{ class DB_Error + +/** + * DB_Error implements a class for reporting portable database error + * messages + * + * @category Database + * @package DB + * @author Stig Bakken + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_Error extends PEAR_Error +{ + // {{{ constructor + + /** + * DB_Error constructor + * + * @param mixed $code DB error code, or string with error message + * @param int $mode what "error mode" to operate in + * @param int $level what error level to use for $mode & + * PEAR_ERROR_TRIGGER + * @param mixed $debuginfo additional debug info, such as the last query + * + * @see PEAR_Error + */ + function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN, + $level = E_USER_NOTICE, $debuginfo = null) + { + if (is_int($code)) { + $this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code, + $mode, $level, $debuginfo); + } else { + $this->PEAR_Error("DB Error: $code", DB_ERROR, + $mode, $level, $debuginfo); + } + } + + // }}} +} + +// }}} +// {{{ class DB_result + +/** + * This class implements a wrapper for a DB result set + * + * A new instance of this class will be returned by the DB implementation + * after processing a query that returns data. + * + * @category Database + * @package DB + * @author Stig Bakken + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_result +{ + // {{{ properties + + /** + * Should results be freed automatically when there are no more rows? + * @var boolean + * @see DB_common::$options + */ + var $autofree; + + /** + * A reference to the DB_ object + * @var object + */ + var $dbh; + + /** + * The current default fetch mode + * @var integer + * @see DB_common::$fetchmode + */ + var $fetchmode; + + /** + * The name of the class into which results should be fetched when + * DB_FETCHMODE_OBJECT is in effect + * + * @var string + * @see DB_common::$fetchmode_object_class + */ + var $fetchmode_object_class; + + /** + * The number of rows to fetch from a limit query + * @var integer + */ + var $limit_count = null; + + /** + * The row to start fetching from in limit queries + * @var integer + */ + var $limit_from = null; + + /** + * The execute parameters that created this result + * @var array + * @since Property available since Release 1.7.0 + */ + var $parameters; + + /** + * The query string that created this result + * + * Copied here incase it changes in $dbh, which is referenced + * + * @var string + * @since Property available since Release 1.7.0 + */ + var $query; + + /** + * The query result resource id created by PHP + * @var resource + */ + var $result; + + /** + * The present row being dealt with + * @var integer + */ + var $row_counter = null; + + /** + * The prepared statement resource id created by PHP in $dbh + * + * This resource is only available when the result set was created using + * a driver's native execute() method, not PEAR DB's emulated one. + * + * Copied here incase it changes in $dbh, which is referenced + * + * {@internal Mainly here because the InterBase/Firebird API is only + * able to retrieve data from result sets if the statemnt handle is + * still in scope.}} + * + * @var resource + * @since Property available since Release 1.7.0 + */ + var $statement; + + + // }}} + // {{{ constructor + + /** + * This constructor sets the object's properties + * + * @param object &$dbh the DB object reference + * @param resource $result the result resource id + * @param array $options an associative array with result options + * + * @return void + */ + function DB_result(&$dbh, $result, $options = array()) + { + $this->autofree = $dbh->options['autofree']; + $this->dbh = &$dbh; + $this->fetchmode = $dbh->fetchmode; + $this->fetchmode_object_class = $dbh->fetchmode_object_class; + $this->parameters = $dbh->last_parameters; + $this->query = $dbh->last_query; + $this->result = $result; + $this->statement = empty($dbh->last_stmt) ? null : $dbh->last_stmt; + foreach ($options as $key => $value) { + $this->setOption($key, $value); + } + } + + /** + * Set options for the DB_result object + * + * @param string $key the option to set + * @param mixed $value the value to set the option to + * + * @return void + */ + function setOption($key, $value = null) + { + switch ($key) { + case 'limit_from': + $this->limit_from = $value; + break; + case 'limit_count': + $this->limit_count = $value; + } + } + + // }}} + // {{{ fetchRow() + + /** + * Fetch a row of data and return it by reference into an array + * + * The type of array returned can be controlled either by setting this + * method's $fetchmode parameter or by changing the default + * fetch mode setFetchMode() before calling this method. + * + * There are two options for standardizing the information returned + * from databases, ensuring their values are consistent when changing + * DBMS's. These portability options can be turned on when creating a + * new DB object or by using setOption(). + * + * + DB_PORTABILITY_LOWERCASE + * convert names of fields to lower case + * + * + DB_PORTABILITY_RTRIM + * right trim the data + * + * @param int $fetchmode the constant indicating how to format the data + * @param int $rownum the row number to fetch (index starts at 0) + * + * @return mixed an array or object containing the row's data, + * NULL when the end of the result set is reached + * or a DB_Error object on failure. + * + * @see DB_common::setOption(), DB_common::setFetchMode() + */ + function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null) + { + if ($fetchmode === DB_FETCHMODE_DEFAULT) { + $fetchmode = $this->fetchmode; + } + if ($fetchmode === DB_FETCHMODE_OBJECT) { + $fetchmode = DB_FETCHMODE_ASSOC; + $object_class = $this->fetchmode_object_class; + } + if ($this->limit_from !== null) { + if ($this->row_counter === null) { + $this->row_counter = $this->limit_from; + // Skip rows + if ($this->dbh->features['limit'] === false) { + $i = 0; + while ($i++ < $this->limit_from) { + $this->dbh->fetchInto($this->result, $arr, $fetchmode); + } + } + } + if ($this->row_counter >= ($this->limit_from + $this->limit_count)) + { + if ($this->autofree) { + $this->free(); + } + $tmp = null; + return $tmp; + } + if ($this->dbh->features['limit'] === 'emulate') { + $rownum = $this->row_counter; + } + $this->row_counter++; + } + $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum); + if ($res === DB_OK) { + if (isset($object_class)) { + // The default mode is specified in the + // DB_common::fetchmode_object_class property + if ($object_class == 'stdClass') { + $arr = (object) $arr; + } else { + $arr = &new $object_class($arr); + } + } + return $arr; + } + if ($res == null && $this->autofree) { + $this->free(); + } + return $res; + } + + // }}} + // {{{ fetchInto() + + /** + * Fetch a row of data into an array which is passed by reference + * + * The type of array returned can be controlled either by setting this + * method's $fetchmode parameter or by changing the default + * fetch mode setFetchMode() before calling this method. + * + * There are two options for standardizing the information returned + * from databases, ensuring their values are consistent when changing + * DBMS's. These portability options can be turned on when creating a + * new DB object or by using setOption(). + * + * + DB_PORTABILITY_LOWERCASE + * convert names of fields to lower case + * + * + DB_PORTABILITY_RTRIM + * right trim the data + * + * @param array &$arr the variable where the data should be placed + * @param int $fetchmode the constant indicating how to format the data + * @param int $rownum the row number to fetch (index starts at 0) + * + * @return mixed DB_OK if a row is processed, NULL when the end of the + * result set is reached or a DB_Error object on failure + * + * @see DB_common::setOption(), DB_common::setFetchMode() + */ + function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null) + { + if ($fetchmode === DB_FETCHMODE_DEFAULT) { + $fetchmode = $this->fetchmode; + } + if ($fetchmode === DB_FETCHMODE_OBJECT) { + $fetchmode = DB_FETCHMODE_ASSOC; + $object_class = $this->fetchmode_object_class; + } + if ($this->limit_from !== null) { + if ($this->row_counter === null) { + $this->row_counter = $this->limit_from; + // Skip rows + if ($this->dbh->features['limit'] === false) { + $i = 0; + while ($i++ < $this->limit_from) { + $this->dbh->fetchInto($this->result, $arr, $fetchmode); + } + } + } + if ($this->row_counter >= ( + $this->limit_from + $this->limit_count)) + { + if ($this->autofree) { + $this->free(); + } + return null; + } + if ($this->dbh->features['limit'] === 'emulate') { + $rownum = $this->row_counter; + } + + $this->row_counter++; + } + $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum); + if ($res === DB_OK) { + if (isset($object_class)) { + // default mode specified in the + // DB_common::fetchmode_object_class property + if ($object_class == 'stdClass') { + $arr = (object) $arr; + } else { + $arr = new $object_class($arr); + } + } + return DB_OK; + } + if ($res == null && $this->autofree) { + $this->free(); + } + return $res; + } + + // }}} + // {{{ numCols() + + /** + * Get the the number of columns in a result set + * + * @return int the number of columns. A DB_Error object on failure. + */ + function numCols() + { + return $this->dbh->numCols($this->result); + } + + // }}} + // {{{ numRows() + + /** + * Get the number of rows in a result set + * + * @return int the number of rows. A DB_Error object on failure. + */ + function numRows() + { + if ($this->dbh->features['numrows'] === 'emulate' + && $this->dbh->options['portability'] & DB_PORTABILITY_NUMROWS) + { + if ($this->dbh->features['prepare']) { + $res = $this->dbh->query($this->query, $this->parameters); + } else { + $res = $this->dbh->query($this->query); + } + if (DB::isError($res)) { + return $res; + } + $i = 0; + while ($res->fetchInto($tmp, DB_FETCHMODE_ORDERED)) { + $i++; + } + return $i; + } else { + return $this->dbh->numRows($this->result); + } + } + + // }}} + // {{{ nextResult() + + /** + * Get the next result if a batch of queries was executed + * + * @return bool true if a new result is available or false if not + */ + function nextResult() + { + return $this->dbh->nextResult($this->result); + } + + // }}} + // {{{ free() + + /** + * Frees the resources allocated for this result set + * + * @return bool true on success. A DB_Error object on failure. + */ + function free() + { + $err = $this->dbh->freeResult($this->result); + if (DB::isError($err)) { + return $err; + } + $this->result = false; + $this->statement = false; + return true; + } + + // }}} + // {{{ tableInfo() + + /** + * @see DB_common::tableInfo() + * @deprecated Method deprecated some time before Release 1.2 + */ + function tableInfo($mode = null) + { + if (is_string($mode)) { + return $this->dbh->raiseError(DB_ERROR_NEED_MORE_DATA); + } + return $this->dbh->tableInfo($this, $mode); + } + + // }}} + // {{{ getQuery() + + /** + * Determine the query string that created this result + * + * @return string the query string + * + * @since Method available since Release 1.7.0 + */ + function getQuery() + { + return $this->query; + } + + // }}} + // {{{ getRowCounter() + + /** + * Tells which row number is currently being processed + * + * @return integer the current row being looked at. Starts at 1. + */ + function getRowCounter() + { + return $this->row_counter; + } + + // }}} +} + +// }}} +// {{{ class DB_row + +/** + * PEAR DB Row Object + * + * The object contains a row of data from a result set. Each column's data + * is placed in a property named for the column. + * + * @category Database + * @package DB + * @author Stig Bakken + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + * @see DB_common::setFetchMode() + */ +class DB_row +{ + // {{{ constructor + + /** + * The constructor places a row's data into properties of this object + * + * @param array the array containing the row's data + * + * @return void + */ + function DB_row(&$arr) + { + foreach ($arr as $key => $value) { + $this->$key = &$arr[$key]; + } + } + + // }}} +} + +// }}} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/DB/common.php b/src/www/lib/pear/DB/common.php new file mode 100644 index 00000000..f0b03ddc --- /dev/null +++ b/src/www/lib/pear/DB/common.php @@ -0,0 +1,2157 @@ + + * @author Tomas V.V. Cox + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: common.php,v 1.1 2005/05/28 01:55:10 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the PEAR class so it can be extended from + */ +require_once 'PEAR.php'; + +/** + * DB_common is the base class from which each database driver class extends + * + * All common methods are declared here. If a given DBMS driver contains + * a particular method, that method will overload the one here. + * + * @category Database + * @package DB + * @author Stig Bakken + * @author Tomas V.V. Cox + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_common extends PEAR +{ + // {{{ properties + + /** + * The current default fetch mode + * @var integer + */ + var $fetchmode = DB_FETCHMODE_ORDERED; + + /** + * The name of the class into which results should be fetched when + * DB_FETCHMODE_OBJECT is in effect + * + * @var string + */ + var $fetchmode_object_class = 'stdClass'; + + /** + * Was a connection present when the object was serialized()? + * @var bool + * @see DB_common::__sleep(), DB_common::__wake() + */ + var $was_connected = null; + + /** + * The most recently executed query + * @var string + */ + var $last_query = ''; + + /** + * Run-time configuration options + * + * The 'optimize' option has been deprecated. Use the 'portability' + * option instead. + * + * @var array + * @see DB_common::setOption() + */ + var $options = array( + 'result_buffering' => 500, + 'persistent' => false, + 'ssl' => false, + 'debug' => 0, + 'seqname_format' => '%s_seq', + 'autofree' => false, + 'portability' => DB_PORTABILITY_NONE, + 'optimize' => 'performance', // Deprecated. Use 'portability'. + ); + + /** + * The parameters from the most recently executed query + * @var array + * @since Property available since Release 1.7.0 + */ + var $last_parameters = array(); + + /** + * The elements from each prepared statement + * @var array + */ + var $prepare_tokens = array(); + + /** + * The data types of the various elements in each prepared statement + * @var array + */ + var $prepare_types = array(); + + /** + * The prepared queries + * @var array + */ + var $prepared_queries = array(); + + + // }}} + // {{{ DB_common + + /** + * This constructor calls $this->PEAR('DB_Error') + * + * @return void + */ + function DB_common() + { + $this->PEAR('DB_Error'); + } + + // }}} + // {{{ __sleep() + + /** + * Automatically indicates which properties should be saved + * when PHP's serialize() function is called + * + * @return array the array of properties names that should be saved + */ + function __sleep() + { + if ($this->connection) { + // Don't disconnect(), people use serialize() for many reasons + $this->was_connected = true; + } else { + $this->was_connected = false; + } + if (isset($this->autocommit)) { + return array('autocommit', + 'dbsyntax', + 'dsn', + 'features', + 'fetchmode', + 'fetchmode_object_class', + 'options', + 'was_connected', + ); + } else { + return array('dbsyntax', + 'dsn', + 'features', + 'fetchmode', + 'fetchmode_object_class', + 'options', + 'was_connected', + ); + } + } + + // }}} + // {{{ __wakeup() + + /** + * Automatically reconnects to the database when PHP's unserialize() + * function is called + * + * The reconnection attempt is only performed if the object was connected + * at the time PHP's serialize() function was run. + * + * @return void + */ + function __wakeup() + { + if ($this->was_connected) { + $this->connect($this->dsn, $this->options); + } + } + + // }}} + // {{{ __toString() + + /** + * Automatic string conversion for PHP 5 + * + * @return string a string describing the current PEAR DB object + * + * @since Method available since Release 1.7.0 + */ + function __toString() + { + $info = strtolower(get_class($this)); + $info .= ': (phptype=' . $this->phptype . + ', dbsyntax=' . $this->dbsyntax . + ')'; + if ($this->connection) { + $info .= ' [connected]'; + } + return $info; + } + + // }}} + // {{{ toString() + + /** + * DEPRECATED: String conversion method + * + * @return string a string describing the current PEAR DB object + * + * @deprecated Method deprecated in Release 1.7.0 + */ + function toString() + { + return $this->__toString(); + } + + // }}} + // {{{ quoteString() + + /** + * DEPRECATED: Quotes a string so it can be safely used within string + * delimiters in a query + * + * @param string $string the string to be quoted + * + * @return string the quoted string + * + * @see DB_common::quoteSmart(), DB_common::escapeSimple() + * @deprecated Method deprecated some time before Release 1.2 + */ + function quoteString($string) + { + $string = $this->quote($string); + if ($string{0} == "'") { + return substr($string, 1, -1); + } + return $string; + } + + // }}} + // {{{ quote() + + /** + * DEPRECATED: Quotes a string so it can be safely used in a query + * + * @param string $string the string to quote + * + * @return string the quoted string or the string NULL + * if the value submitted is null. + * + * @see DB_common::quoteSmart(), DB_common::escapeSimple() + * @deprecated Deprecated in release 1.6.0 + */ + function quote($string = null) + { + return ($string === null) ? 'NULL' + : "'" . str_replace("'", "''", $string) . "'"; + } + + // }}} + // {{{ quoteIdentifier() + + /** + * Quotes a string so it can be safely used as a table or column name + * + * Delimiting style depends on which database driver is being used. + * + * NOTE: just because you CAN use delimited identifiers doesn't mean + * you SHOULD use them. In general, they end up causing way more + * problems than they solve. + * + * Portability is broken by using the following characters inside + * delimited identifiers: + * + backtick (`) -- due to MySQL + * + double quote (") -- due to Oracle + * + brackets ([ or ]) -- due to Access + * + * Delimited identifiers are known to generally work correctly under + * the following drivers: + * + mssql + * + mysql + * + mysqli + * + oci8 + * + odbc(access) + * + odbc(db2) + * + pgsql + * + sqlite + * + sybase (must execute set quoted_identifier on sometime + * prior to use) + * + * InterBase doesn't seem to be able to use delimited identifiers + * via PHP 4. They work fine under PHP 5. + * + * @param string $str the identifier name to be quoted + * + * @return string the quoted identifier + * + * @since Method available since Release 1.6.0 + */ + function quoteIdentifier($str) + { + return '"' . str_replace('"', '""', $str) . '"'; + } + + // }}} + // {{{ quoteSmart() + + /** + * Formats input so it can be safely used in a query + * + * The output depends on the PHP data type of input and the database + * type being used. + * + * @param mixed $in the data to be formatted + * + * @return mixed the formatted data. The format depends on the input's + * PHP type: + *
    + *
  • + * input -> returns + *
  • + *
  • + * null -> the string NULL + *
  • + *
  • + * integer or double -> the unquoted number + *
  • + *
  • + * bool -> output depends on the driver in use + * Most drivers return integers: 1 if + * true or 0 if + * false. + * Some return strings: TRUE if + * true or FALSE if + * false. + * Finally one returns strings: T if + * true or F if + * false. Here is a list of each DBMS, + * the values returned and the suggested column type: + *
      + *
    • + * dbase -> T/F + * (Logical) + *
    • + *
    • + * fbase -> TRUE/FALSE + * (BOOLEAN) + *
    • + *
    • + * ibase -> 1/0 + * (SMALLINT) [1] + *
    • + *
    • + * ifx -> 1/0 + * (SMALLINT) [1] + *
    • + *
    • + * msql -> 1/0 + * (INTEGER) + *
    • + *
    • + * mssql -> 1/0 + * (BIT) + *
    • + *
    • + * mysql -> 1/0 + * (TINYINT(1)) + *
    • + *
    • + * mysqli -> 1/0 + * (TINYINT(1)) + *
    • + *
    • + * oci8 -> 1/0 + * (NUMBER(1)) + *
    • + *
    • + * odbc -> 1/0 + * (SMALLINT) [1] + *
    • + *
    • + * pgsql -> TRUE/FALSE + * (BOOLEAN) + *
    • + *
    • + * sqlite -> 1/0 + * (INTEGER) + *
    • + *
    • + * sybase -> 1/0 + * (TINYINT(1)) + *
    • + *
    + * [1] Accommodate the lowest common denominator because not all + * versions of have BOOLEAN. + *
  • + *
  • + * other (including strings and numeric strings) -> + * the data with single quotes escaped by preceeding + * single quotes, backslashes are escaped by preceeding + * backslashes, then the whole string is encapsulated + * between single quotes + *
  • + *
+ * + * @see DB_common::escapeSimple() + * @since Method available since Release 1.6.0 + */ + function quoteSmart($in) + { + if (is_int($in) || is_double($in)) { + return $in; + } elseif (is_bool($in)) { + return $in ? 1 : 0; + } elseif (is_null($in)) { + return 'NULL'; + } else { + return "'" . $this->escapeSimple($in) . "'"; + } + } + + // }}} + // {{{ escapeSimple() + + /** + * Escapes a string according to the current DBMS's standards + * + * In SQLite, this makes things safe for inserts/updates, but may + * cause problems when performing text comparisons against columns + * containing binary data. See the + * {@link http://php.net/sqlite_escape_string PHP manual} for more info. + * + * @param string $str the string to be escaped + * + * @return string the escaped string + * + * @see DB_common::quoteSmart() + * @since Method available since Release 1.6.0 + */ + function escapeSimple($str) + { + return str_replace("'", "''", $str); + } + + // }}} + // {{{ provides() + + /** + * Tells whether the present driver supports a given feature + * + * @param string $feature the feature you're curious about + * + * @return bool whether this driver supports $feature + */ + function provides($feature) + { + return $this->features[$feature]; + } + + // }}} + // {{{ setFetchMode() + + /** + * Sets the fetch mode that should be used by default for query results + * + * @param integer $fetchmode DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC + * or DB_FETCHMODE_OBJECT + * @param string $object_class the class name of the object to be returned + * by the fetch methods when the + * DB_FETCHMODE_OBJECT mode is selected. + * If no class is specified by default a cast + * to object from the assoc array row will be + * done. There is also the posibility to use + * and extend the 'DB_row' class. + * + * @see DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC, DB_FETCHMODE_OBJECT + */ + function setFetchMode($fetchmode, $object_class = 'stdClass') + { + switch ($fetchmode) { + case DB_FETCHMODE_OBJECT: + $this->fetchmode_object_class = $object_class; + case DB_FETCHMODE_ORDERED: + case DB_FETCHMODE_ASSOC: + $this->fetchmode = $fetchmode; + break; + default: + return $this->raiseError('invalid fetchmode mode'); + } + } + + // }}} + // {{{ setOption() + + /** + * Sets run-time configuration options for PEAR DB + * + * Options, their data types, default values and description: + *
    + *
  • + * autofree boolean = false + *
    should results be freed automatically when there are no + * more rows? + *
  • + * result_buffering integer = 500 + *
    how many rows of the result set should be buffered? + *
    In mysql: mysql_unbuffered_query() is used instead of + * mysql_query() if this value is 0. (Release 1.7.0) + *
    In oci8: this value is passed to ocisetprefetch(). + * (Release 1.7.0) + *
  • + * debug integer = 0 + *
    debug level + *
  • + * persistent boolean = false + *
    should the connection be persistent? + *
  • + * portability integer = DB_PORTABILITY_NONE + *
    portability mode constant (see below) + *
  • + * seqname_format string = %s_seq + *
    the sprintf() format string used on sequence names. This + * format is applied to sequence names passed to + * createSequence(), nextID() and dropSequence(). + *
  • + * ssl boolean = false + *
    use ssl to connect? + *
  • + *
+ * + * ----------------------------------------- + * + * PORTABILITY MODES + * + * These modes are bitwised, so they can be combined using | + * and removed using ^. See the examples section below on how + * to do this. + * + * DB_PORTABILITY_NONE + * turn off all portability features + * + * This mode gets automatically turned on if the deprecated + * optimize option gets set to performance. + * + * + * DB_PORTABILITY_LOWERCASE + * convert names of tables and fields to lower case when using + * get*(), fetch*() and tableInfo() + * + * This mode gets automatically turned on in the following databases + * if the deprecated option optimize gets set to + * portability: + * + oci8 + * + * + * DB_PORTABILITY_RTRIM + * right trim the data output by get*() fetch*() + * + * + * DB_PORTABILITY_DELETE_COUNT + * force reporting the number of rows deleted + * + * Some DBMS's don't count the number of rows deleted when performing + * simple DELETE FROM tablename queries. This portability + * mode tricks such DBMS's into telling the count by adding + * WHERE 1=1 to the end of DELETE queries. + * + * This mode gets automatically turned on in the following databases + * if the deprecated option optimize gets set to + * portability: + * + fbsql + * + mysql + * + mysqli + * + sqlite + * + * + * DB_PORTABILITY_NUMROWS + * enable hack that makes numRows() work in Oracle + * + * This mode gets automatically turned on in the following databases + * if the deprecated option optimize gets set to + * portability: + * + oci8 + * + * + * DB_PORTABILITY_ERRORS + * makes certain error messages in certain drivers compatible + * with those from other DBMS's + * + * + mysql, mysqli: change unique/primary key constraints + * DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT + * + * + odbc(access): MS's ODBC driver reports 'no such field' as code + * 07001, which means 'too few parameters.' When this option is on + * that code gets mapped to DB_ERROR_NOSUCHFIELD. + * DB_ERROR_MISMATCH -> DB_ERROR_NOSUCHFIELD + * + * DB_PORTABILITY_NULL_TO_EMPTY + * convert null values to empty strings in data output by get*() and + * fetch*(). Needed because Oracle considers empty strings to be null, + * while most other DBMS's know the difference between empty and null. + * + * + * DB_PORTABILITY_ALL + * turn on all portability features + * + * ----------------------------------------- + * + * Example 1. Simple setOption() example + * + * $db->setOption('autofree', true); + * + * + * Example 2. Portability for lowercasing and trimming + * + * $db->setOption('portability', + * DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM); + * + * + * Example 3. All portability options except trimming + * + * $db->setOption('portability', + * DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM); + * + * + * @param string $option option name + * @param mixed $value value for the option + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::$options + */ + function setOption($option, $value) + { + if (isset($this->options[$option])) { + $this->options[$option] = $value; + + /* + * Backwards compatibility check for the deprecated 'optimize' + * option. Done here in case settings change after connecting. + */ + if ($option == 'optimize') { + if ($value == 'portability') { + switch ($this->phptype) { + case 'oci8': + $this->options['portability'] = + DB_PORTABILITY_LOWERCASE | + DB_PORTABILITY_NUMROWS; + break; + case 'fbsql': + case 'mysql': + case 'mysqli': + case 'sqlite': + $this->options['portability'] = + DB_PORTABILITY_DELETE_COUNT; + break; + } + } else { + $this->options['portability'] = DB_PORTABILITY_NONE; + } + } + + return DB_OK; + } + return $this->raiseError("unknown option $option"); + } + + // }}} + // {{{ getOption() + + /** + * Returns the value of an option + * + * @param string $option the option name you're curious about + * + * @return mixed the option's value + */ + function getOption($option) + { + if (isset($this->options[$option])) { + return $this->options[$option]; + } + return $this->raiseError("unknown option $option"); + } + + // }}} + // {{{ prepare() + + /** + * Prepares a query for multiple execution with execute() + * + * Creates a query that can be run multiple times. Each time it is run, + * the placeholders, if any, will be replaced by the contents of + * execute()'s $data argument. + * + * Three types of placeholders can be used: + * + ? scalar value (i.e. strings, integers). The system + * will automatically quote and escape the data. + * + ! value is inserted 'as is' + * + & requires a file name. The file's contents get + * inserted into the query (i.e. saving binary + * data in a db) + * + * Example 1. + * + * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)'); + * $data = array( + * "John's text", + * "'it''s good'", + * 'filename.txt' + * ); + * $res = $db->execute($sth, $data); + * + * + * Use backslashes to escape placeholder characters if you don't want + * them to be interpreted as placeholders: + *
+     *    "UPDATE foo SET col=? WHERE col='over \& under'"
+     * 
+ * + * With some database backends, this is emulated. + * + * {@internal ibase and oci8 have their own prepare() methods.}} + * + * @param string $query the query to be prepared + * + * @return mixed DB statement resource on success. A DB_Error object + * on failure. + * + * @see DB_common::execute() + */ + function prepare($query) + { + $tokens = preg_split('/((?prepare_tokens[] = &$newtokens; + end($this->prepare_tokens); + + $k = key($this->prepare_tokens); + $this->prepare_types[$k] = $types; + $this->prepared_queries[$k] = implode(' ', $newtokens); + + return $k; + } + + // }}} + // {{{ autoPrepare() + + /** + * Automaticaly generates an insert or update query and pass it to prepare() + * + * @param string $table the table name + * @param array $table_fields the array of field names + * @param int $mode a type of query to make: + * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE + * @param string $where for update queries: the WHERE clause to + * append to the SQL statement. Don't + * include the "WHERE" keyword. + * + * @return resource the query handle + * + * @uses DB_common::prepare(), DB_common::buildManipSQL() + */ + function autoPrepare($table, $table_fields, $mode = DB_AUTOQUERY_INSERT, + $where = false) + { + $query = $this->buildManipSQL($table, $table_fields, $mode, $where); + if (DB::isError($query)) { + return $query; + } + return $this->prepare($query); + } + + // }}} + // {{{ autoExecute() + + /** + * Automaticaly generates an insert or update query and call prepare() + * and execute() with it + * + * @param string $table the table name + * @param array $fields_values the associative array where $key is a + * field name and $value its value + * @param int $mode a type of query to make: + * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE + * @param string $where for update queries: the WHERE clause to + * append to the SQL statement. Don't + * include the "WHERE" keyword. + * + * @return mixed a new DB_result object for successful SELECT queries + * or DB_OK for successul data manipulation queries. + * A DB_Error object on failure. + * + * @uses DB_common::autoPrepare(), DB_common::execute() + */ + function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT, + $where = false) + { + $sth = $this->autoPrepare($table, array_keys($fields_values), $mode, + $where); + if (DB::isError($sth)) { + return $sth; + } + $ret =& $this->execute($sth, array_values($fields_values)); + $this->freePrepared($sth); + return $ret; + + } + + // }}} + // {{{ buildManipSQL() + + /** + * Produces an SQL query string for autoPrepare() + * + * Example: + *
+     * buildManipSQL('table_sql', array('field1', 'field2', 'field3'),
+     *               DB_AUTOQUERY_INSERT);
+     * 
+ * + * That returns + * + * INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?) + * + * + * NOTES: + * - This belongs more to a SQL Builder class, but this is a simple + * facility. + * - Be carefull! If you don't give a $where param with an UPDATE + * query, all the records of the table will be updated! + * + * @param string $table the table name + * @param array $table_fields the array of field names + * @param int $mode a type of query to make: + * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE + * @param string $where for update queries: the WHERE clause to + * append to the SQL statement. Don't + * include the "WHERE" keyword. + * + * @return string the sql query for autoPrepare() + */ + function buildManipSQL($table, $table_fields, $mode, $where = false) + { + if (count($table_fields) == 0) { + return $this->raiseError(DB_ERROR_NEED_MORE_DATA); + } + $first = true; + switch ($mode) { + case DB_AUTOQUERY_INSERT: + $values = ''; + $names = ''; + foreach ($table_fields as $value) { + if ($first) { + $first = false; + } else { + $names .= ','; + $values .= ','; + } + $names .= $value; + $values .= '?'; + } + return "INSERT INTO $table ($names) VALUES ($values)"; + case DB_AUTOQUERY_UPDATE: + $set = ''; + foreach ($table_fields as $value) { + if ($first) { + $first = false; + } else { + $set .= ','; + } + $set .= "$value = ?"; + } + $sql = "UPDATE $table SET $set"; + if ($where) { + $sql .= " WHERE $where"; + } + return $sql; + default: + return $this->raiseError(DB_ERROR_SYNTAX); + } + } + + // }}} + // {{{ execute() + + /** + * Executes a DB statement prepared with prepare() + * + * Example 1. + * + * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)'); + * $data = array( + * "John's text", + * "'it''s good'", + * 'filename.txt' + * ); + * $res =& $db->execute($sth, $data); + * + * + * @param resource $stmt a DB statement resource returned from prepare() + * @param mixed $data array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return mixed a new DB_result object for successful SELECT queries + * or DB_OK for successul data manipulation queries. + * A DB_Error object on failure. + * + * {@internal ibase and oci8 have their own execute() methods.}} + * + * @see DB_common::prepare() + */ + function &execute($stmt, $data = array()) + { + $realquery = $this->executeEmulateQuery($stmt, $data); + if (DB::isError($realquery)) { + return $realquery; + } + $result = $this->simpleQuery($realquery); + + if ($result === DB_OK || DB::isError($result)) { + return $result; + } else { + $tmp =& new DB_result($this, $result); + return $tmp; + } + } + + // }}} + // {{{ executeEmulateQuery() + + /** + * Emulates executing prepared statements if the DBMS not support them + * + * @param resource $stmt a DB statement resource returned from execute() + * @param mixed $data array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return mixed a string containing the real query run when emulating + * prepare/execute. A DB_Error object on failure. + * + * @access protected + * @see DB_common::execute() + */ + function executeEmulateQuery($stmt, $data = array()) + { + $stmt = (int)$stmt; + $data = (array)$data; + $this->last_parameters = $data; + + if (count($this->prepare_types[$stmt]) != count($data)) { + $this->last_query = $this->prepared_queries[$stmt]; + return $this->raiseError(DB_ERROR_MISMATCH); + } + + $realquery = $this->prepare_tokens[$stmt][0]; + + $i = 0; + foreach ($data as $value) { + if ($this->prepare_types[$stmt][$i] == DB_PARAM_SCALAR) { + $realquery .= $this->quoteSmart($value); + } elseif ($this->prepare_types[$stmt][$i] == DB_PARAM_OPAQUE) { + $fp = @fopen($value, 'rb'); + if (!$fp) { + return $this->raiseError(DB_ERROR_ACCESS_VIOLATION); + } + $realquery .= $this->quoteSmart(fread($fp, filesize($value))); + fclose($fp); + } else { + $realquery .= $value; + } + + $realquery .= $this->prepare_tokens[$stmt][++$i]; + } + + return $realquery; + } + + // }}} + // {{{ executeMultiple() + + /** + * Performs several execute() calls on the same statement handle + * + * $data must be an array indexed numerically + * from 0, one execute call is done for every "row" in the array. + * + * If an error occurs during execute(), executeMultiple() does not + * execute the unfinished rows, but rather returns that error. + * + * @param resource $stmt query handle from prepare() + * @param array $data numeric array containing the + * data to insert into the query + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::prepare(), DB_common::execute() + */ + function executeMultiple($stmt, $data) + { + foreach ($data as $value) { + $res =& $this->execute($stmt, $value); + if (DB::isError($res)) { + return $res; + } + } + return DB_OK; + } + + // }}} + // {{{ freePrepared() + + /** + * Frees the internal resources associated with a prepared query + * + * @param resource $stmt the prepared statement's PHP resource + * @param bool $free_resource should the PHP resource be freed too? + * Use false if you need to get data + * from the result set later. + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_common::prepare() + */ + function freePrepared($stmt, $free_resource = true) + { + $stmt = (int)$stmt; + if (isset($this->prepare_tokens[$stmt])) { + unset($this->prepare_tokens[$stmt]); + unset($this->prepare_types[$stmt]); + unset($this->prepared_queries[$stmt]); + return true; + } + return false; + } + + // }}} + // {{{ modifyQuery() + + /** + * Changes a query string for various DBMS specific reasons + * + * It is defined here to ensure all drivers have this method available. + * + * @param string $query the query string to modify + * + * @return string the modified query string + * + * @access protected + * @see DB_mysql::modifyQuery(), DB_oci8::modifyQuery(), + * DB_sqlite::modifyQuery() + */ + function modifyQuery($query) + { + return $query; + } + + // }}} + // {{{ modifyLimitQuery() + + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * It is defined here to assure that all implementations + * have this method defined. + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ + function modifyLimitQuery($query, $from, $count, $params = array()) + { + return $query; + } + + // }}} + // {{{ query() + + /** + * Sends a query to the database server + * + * The query string can be either a normal statement to be sent directly + * to the server OR if $params are passed the query can have + * placeholders and it will be passed through prepare() and execute(). + * + * @param string $query the SQL query or the statement to prepare + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return mixed a new DB_result object for successful SELECT queries + * or DB_OK for successul data manipulation queries. + * A DB_Error object on failure. + * + * @see DB_result, DB_common::prepare(), DB_common::execute() + */ + function &query($query, $params = array()) + { + if (sizeof($params) > 0) { + $sth = $this->prepare($query); + if (DB::isError($sth)) { + return $sth; + } + $ret =& $this->execute($sth, $params); + $this->freePrepared($sth, false); + return $ret; + } else { + $this->last_parameters = array(); + $result = $this->simpleQuery($query); + if ($result === DB_OK || DB::isError($result)) { + return $result; + } else { + $tmp =& new DB_result($this, $result); + return $tmp; + } + } + } + + // }}} + // {{{ limitQuery() + + /** + * Generates and executes a LIMIT query + * + * @param string $query the query + * @param intr $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return mixed a new DB_result object for successful SELECT queries + * or DB_OK for successul data manipulation queries. + * A DB_Error object on failure. + */ + function &limitQuery($query, $from, $count, $params = array()) + { + $query = $this->modifyLimitQuery($query, $from, $count, $params); + if (DB::isError($query)){ + return $query; + } + $result =& $this->query($query, $params); + if (is_a($result, 'DB_result')) { + $result->setOption('limit_from', $from); + $result->setOption('limit_count', $count); + } + return $result; + } + + // }}} + // {{{ getOne() + + /** + * Fetches the first column of the first row from a query result + * + * Takes care of doing the query and freeing the results when finished. + * + * @param string $query the SQL query + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return mixed the returned value of the query. + * A DB_Error object on failure. + */ + function &getOne($query, $params = array()) + { + $params = (array)$params; + // modifyLimitQuery() would be nice here, but it causes BC issues + if (sizeof($params) > 0) { + $sth = $this->prepare($query); + if (DB::isError($sth)) { + return $sth; + } + $res =& $this->execute($sth, $params); + $this->freePrepared($sth); + } else { + $res =& $this->query($query); + } + + if (DB::isError($res)) { + return $res; + } + + $err = $res->fetchInto($row, DB_FETCHMODE_ORDERED); + $res->free(); + + if ($err !== DB_OK) { + return $err; + } + + return $row[0]; + } + + // }}} + // {{{ getRow() + + /** + * Fetches the first row of data returned from a query result + * + * Takes care of doing the query and freeing the results when finished. + * + * @param string $query the SQL query + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * @param int $fetchmode the fetch mode to use + * + * @return array the first row of results as an array. + * A DB_Error object on failure. + */ + function &getRow($query, $params = array(), + $fetchmode = DB_FETCHMODE_DEFAULT) + { + // compat check, the params and fetchmode parameters used to + // have the opposite order + if (!is_array($params)) { + if (is_array($fetchmode)) { + if ($params === null) { + $tmp = DB_FETCHMODE_DEFAULT; + } else { + $tmp = $params; + } + $params = $fetchmode; + $fetchmode = $tmp; + } elseif ($params !== null) { + $fetchmode = $params; + $params = array(); + } + } + // modifyLimitQuery() would be nice here, but it causes BC issues + if (sizeof($params) > 0) { + $sth = $this->prepare($query); + if (DB::isError($sth)) { + return $sth; + } + $res =& $this->execute($sth, $params); + $this->freePrepared($sth); + } else { + $res =& $this->query($query); + } + + if (DB::isError($res)) { + return $res; + } + + $err = $res->fetchInto($row, $fetchmode); + + $res->free(); + + if ($err !== DB_OK) { + return $err; + } + + return $row; + } + + // }}} + // {{{ getCol() + + /** + * Fetches a single column from a query result and returns it as an + * indexed array + * + * @param string $query the SQL query + * @param mixed $col which column to return (integer [column number, + * starting at 0] or string [column name]) + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return array the results as an array. A DB_Error object on failure. + * + * @see DB_common::query() + */ + function &getCol($query, $col = 0, $params = array()) + { + $params = (array)$params; + if (sizeof($params) > 0) { + $sth = $this->prepare($query); + + if (DB::isError($sth)) { + return $sth; + } + + $res =& $this->execute($sth, $params); + $this->freePrepared($sth); + } else { + $res =& $this->query($query); + } + + if (DB::isError($res)) { + return $res; + } + + $fetchmode = is_int($col) ? DB_FETCHMODE_ORDERED : DB_FETCHMODE_ASSOC; + + if (!is_array($row = $res->fetchRow($fetchmode))) { + $ret = array(); + } else { + if (!array_key_exists($col, $row)) { + $ret =& $this->raiseError(DB_ERROR_NOSUCHFIELD); + } else { + $ret = array($row[$col]); + while (is_array($row = $res->fetchRow($fetchmode))) { + $ret[] = $row[$col]; + } + } + } + + $res->free(); + + if (DB::isError($row)) { + $ret = $row; + } + + return $ret; + } + + // }}} + // {{{ getAssoc() + + /** + * Fetches an entire query result and returns it as an + * associative array using the first column as the key + * + * If the result set contains more than two columns, the value + * will be an array of the values from column 2-n. If the result + * set contains only two columns, the returned value will be a + * scalar with the value of the second column (unless forced to an + * array with the $force_array parameter). A DB error code is + * returned on errors. If the result set contains fewer than two + * columns, a DB_ERROR_TRUNCATED error is returned. + * + * For example, if the table "mytable" contains: + * + *
+     *  ID      TEXT       DATE
+     * --------------------------------
+     *  1       'one'      944679408
+     *  2       'two'      944679408
+     *  3       'three'    944679408
+     * 
+ * + * Then the call getAssoc('SELECT id,text FROM mytable') returns: + *
+     *   array(
+     *     '1' => 'one',
+     *     '2' => 'two',
+     *     '3' => 'three',
+     *   )
+     * 
+ * + * ...while the call getAssoc('SELECT id,text,date FROM mytable') returns: + *
+     *   array(
+     *     '1' => array('one', '944679408'),
+     *     '2' => array('two', '944679408'),
+     *     '3' => array('three', '944679408')
+     *   )
+     * 
+ * + * If the more than one row occurs with the same value in the + * first column, the last row overwrites all previous ones by + * default. Use the $group parameter if you don't want to + * overwrite like this. Example: + * + *
+     * getAssoc('SELECT category,id,name FROM mytable', false, null,
+     *          DB_FETCHMODE_ASSOC, true) returns:
+     *
+     *   array(
+     *     '1' => array(array('id' => '4', 'name' => 'number four'),
+     *                  array('id' => '6', 'name' => 'number six')
+     *            ),
+     *     '9' => array(array('id' => '4', 'name' => 'number four'),
+     *                  array('id' => '6', 'name' => 'number six')
+     *            )
+     *   )
+     * 
+ * + * Keep in mind that database functions in PHP usually return string + * values for results regardless of the database's internal type. + * + * @param string $query the SQL query + * @param bool $force_array used only when the query returns + * exactly two columns. If true, the values + * of the returned array will be one-element + * arrays instead of scalars. + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of + * items passed must match quantity of + * placeholders in query: meaning 1 + * placeholder for non-array parameters or + * 1 placeholder per array element. + * @param int $fetchmode the fetch mode to use + * @param bool $group if true, the values of the returned array + * is wrapped in another array. If the same + * key value (in the first column) repeats + * itself, the values will be appended to + * this array instead of overwriting the + * existing values. + * + * @return array the associative array containing the query results. + * A DB_Error object on failure. + */ + function &getAssoc($query, $force_array = false, $params = array(), + $fetchmode = DB_FETCHMODE_DEFAULT, $group = false) + { + $params = (array)$params; + if (sizeof($params) > 0) { + $sth = $this->prepare($query); + + if (DB::isError($sth)) { + return $sth; + } + + $res =& $this->execute($sth, $params); + $this->freePrepared($sth); + } else { + $res =& $this->query($query); + } + + if (DB::isError($res)) { + return $res; + } + if ($fetchmode == DB_FETCHMODE_DEFAULT) { + $fetchmode = $this->fetchmode; + } + $cols = $res->numCols(); + + if ($cols < 2) { + $tmp =& $this->raiseError(DB_ERROR_TRUNCATED); + return $tmp; + } + + $results = array(); + + if ($cols > 2 || $force_array) { + // return array values + // XXX this part can be optimized + if ($fetchmode == DB_FETCHMODE_ASSOC) { + while (is_array($row = $res->fetchRow(DB_FETCHMODE_ASSOC))) { + reset($row); + $key = current($row); + unset($row[key($row)]); + if ($group) { + $results[$key][] = $row; + } else { + $results[$key] = $row; + } + } + } elseif ($fetchmode == DB_FETCHMODE_OBJECT) { + while ($row = $res->fetchRow(DB_FETCHMODE_OBJECT)) { + $arr = get_object_vars($row); + $key = current($arr); + if ($group) { + $results[$key][] = $row; + } else { + $results[$key] = $row; + } + } + } else { + while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) { + // we shift away the first element to get + // indices running from 0 again + $key = array_shift($row); + if ($group) { + $results[$key][] = $row; + } else { + $results[$key] = $row; + } + } + } + if (DB::isError($row)) { + $results = $row; + } + } else { + // return scalar values + while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) { + if ($group) { + $results[$row[0]][] = $row[1]; + } else { + $results[$row[0]] = $row[1]; + } + } + if (DB::isError($row)) { + $results = $row; + } + } + + $res->free(); + + return $results; + } + + // }}} + // {{{ getAll() + + /** + * Fetches all of the rows from a query result + * + * @param string $query the SQL query + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of + * items passed must match quantity of + * placeholders in query: meaning 1 + * placeholder for non-array parameters or + * 1 placeholder per array element. + * @param int $fetchmode the fetch mode to use: + * + DB_FETCHMODE_ORDERED + * + DB_FETCHMODE_ASSOC + * + DB_FETCHMODE_ORDERED | DB_FETCHMODE_FLIPPED + * + DB_FETCHMODE_ASSOC | DB_FETCHMODE_FLIPPED + * + * @return array the nested array. A DB_Error object on failure. + */ + function &getAll($query, $params = array(), + $fetchmode = DB_FETCHMODE_DEFAULT) + { + // compat check, the params and fetchmode parameters used to + // have the opposite order + if (!is_array($params)) { + if (is_array($fetchmode)) { + if ($params === null) { + $tmp = DB_FETCHMODE_DEFAULT; + } else { + $tmp = $params; + } + $params = $fetchmode; + $fetchmode = $tmp; + } elseif ($params !== null) { + $fetchmode = $params; + $params = array(); + } + } + + if (sizeof($params) > 0) { + $sth = $this->prepare($query); + + if (DB::isError($sth)) { + return $sth; + } + + $res =& $this->execute($sth, $params); + $this->freePrepared($sth); + } else { + $res =& $this->query($query); + } + + if ($res === DB_OK || DB::isError($res)) { + return $res; + } + + $results = array(); + while (DB_OK === $res->fetchInto($row, $fetchmode)) { + if ($fetchmode & DB_FETCHMODE_FLIPPED) { + foreach ($row as $key => $val) { + $results[$key][] = $val; + } + } else { + $results[] = $row; + } + } + + $res->free(); + + if (DB::isError($row)) { + $tmp =& $this->raiseError($row); + return $tmp; + } + return $results; + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff = false) + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ numRows() + + /** + * Determines the number of rows in a query result + * + * @param resource $result the query result idenifier produced by PHP + * + * @return int the number of rows. A DB_Error object on failure. + */ + function numRows($result) + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ getSequenceName() + + /** + * Generates the name used inside the database for a sequence + * + * The createSequence() docblock contains notes about storing sequence + * names. + * + * @param string $sqn the sequence's public name + * + * @return string the sequence's name in the backend + * + * @access protected + * @see DB_common::createSequence(), DB_common::dropSequence(), + * DB_common::nextID(), DB_common::setOption() + */ + function getSequenceName($sqn) + { + return sprintf($this->getOption('seqname_format'), + preg_replace('/[^a-z0-9_.]/i', '_', $sqn)); + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::dropSequence(), + * DB_common::getSequenceName() + */ + function nextId($seq_name, $ondemand = true) + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ createSequence() + + /** + * Creates a new sequence + * + * The name of a given sequence is determined by passing the string + * provided in the $seq_name argument through PHP's sprintf() + * function using the value from the seqname_format option as + * the sprintf()'s format argument. + * + * seqname_format is set via setOption(). + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_common::nextID() + */ + function createSequence($seq_name) + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_common::nextID() + */ + function dropSequence($seq_name) + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ raiseError() + + /** + * Communicates an error and invoke error callbacks, etc + * + * Basically a wrapper for PEAR::raiseError without the message string. + * + * @param mixed integer error code, or a PEAR error object (all + * other parameters are ignored if this parameter is + * an object + * @param int error mode, see PEAR_Error docs + * @param mixed if error mode is PEAR_ERROR_TRIGGER, this is the + * error level (E_USER_NOTICE etc). If error mode is + * PEAR_ERROR_CALLBACK, this is the callback function, + * either as a function name, or as an array of an + * object and method name. For other error modes this + * parameter is ignored. + * @param string extra debug information. Defaults to the last + * query and native error code. + * @param mixed native error code, integer or string depending the + * backend + * + * @return object the PEAR_Error object + * + * @see PEAR_Error + */ + function &raiseError($code = DB_ERROR, $mode = null, $options = null, + $userinfo = null, $nativecode = null) + { + // The error is yet a DB error object + if (is_object($code)) { + // because we the static PEAR::raiseError, our global + // handler should be used if it is set + if ($mode === null && !empty($this->_default_error_mode)) { + $mode = $this->_default_error_mode; + $options = $this->_default_error_options; + } + $tmp = PEAR::raiseError($code, null, $mode, $options, + null, null, true); + return $tmp; + } + + if ($userinfo === null) { + $userinfo = $this->last_query; + } + + if ($nativecode) { + $userinfo .= ' [nativecode=' . trim($nativecode) . ']'; + } else { + $userinfo .= ' [DB Error: ' . DB::errorMessage($code) . ']'; + } + + $tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo, + 'DB_Error', true); + return $tmp; + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code produced by the last query + * + * @return mixed the DBMS' error code. A DB_Error object on failure. + */ + function errorNative() + { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ errorCode() + + /** + * Maps native error codes to DB's portable ones + * + * Uses the $errorcode_map property defined in each driver. + * + * @param string|int $nativecode the error code returned by the DBMS + * + * @return int the portable DB error code. Return DB_ERROR if the + * current driver doesn't have a mapping for the + * $nativecode submitted. + */ + function errorCode($nativecode) + { + if (isset($this->errorcode_map[$nativecode])) { + return $this->errorcode_map[$nativecode]; + } + // Fall back to DB_ERROR if there was no mapping. + return DB_ERROR; + } + + // }}} + // {{{ errorMessage() + + /** + * Maps a DB error code to a textual message + * + * @param integer $dbcode the DB error code + * + * @return string the error message corresponding to the error code + * submitted. FALSE if the error code is unknown. + * + * @see DB::errorMessage() + */ + function errorMessage($dbcode) + { + return DB::errorMessage($this->errorcode_map[$dbcode]); + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * The format of the resulting array depends on which $mode + * you select. The sample output below is based on this query: + *
+     *    SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId
+     *    FROM tblFoo
+     *    JOIN tblBar ON tblFoo.fldId = tblBar.fldId
+     * 
+ * + *
    + *
  • + * + * null (default) + *
    +     *   [0] => Array (
    +     *       [table] => tblFoo
    +     *       [name] => fldId
    +     *       [type] => int
    +     *       [len] => 11
    +     *       [flags] => primary_key not_null
    +     *   )
    +     *   [1] => Array (
    +     *       [table] => tblFoo
    +     *       [name] => fldPhone
    +     *       [type] => string
    +     *       [len] => 20
    +     *       [flags] =>
    +     *   )
    +     *   [2] => Array (
    +     *       [table] => tblBar
    +     *       [name] => fldId
    +     *       [type] => int
    +     *       [len] => 11
    +     *       [flags] => primary_key not_null
    +     *   )
    +     *   
    + * + *
  • + * + * DB_TABLEINFO_ORDER + * + *

    In addition to the information found in the default output, + * a notation of the number of columns is provided by the + * num_fields element while the order + * element provides an array with the column names as the keys and + * their location index number (corresponding to the keys in the + * the default output) as the values.

    + * + *

    If a result set has identical field names, the last one is + * used.

    + * + *
    +     *   [num_fields] => 3
    +     *   [order] => Array (
    +     *       [fldId] => 2
    +     *       [fldTrans] => 1
    +     *   )
    +     *   
    + * + *
  • + * + * DB_TABLEINFO_ORDERTABLE + * + *

    Similar to DB_TABLEINFO_ORDER but adds more + * dimensions to the array in which the table names are keys and + * the field names are sub-keys. This is helpful for queries that + * join tables which have identical field names.

    + * + *
    +     *   [num_fields] => 3
    +     *   [ordertable] => Array (
    +     *       [tblFoo] => Array (
    +     *           [fldId] => 0
    +     *           [fldPhone] => 1
    +     *       )
    +     *       [tblBar] => Array (
    +     *           [fldId] => 2
    +     *       )
    +     *   )
    +     *   
    + * + *
  • + *
+ * + * The flags element contains a space separated list + * of extra information about the field. This data is inconsistent + * between DBMS's due to the way each DBMS works. + * + primary_key + * + unique_key + * + multiple_key + * + not_null + * + * Most DBMS's only provide the table and flags + * elements if $result is a table name. The following DBMS's + * provide full information from queries: + * + fbsql + * + mysql + * + * If the 'portability' option has DB_PORTABILITY_LOWERCASE + * turned on, the names of tables and fields will be lowercased. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode either unused or one of the tableInfo modes: + * DB_TABLEINFO_ORDERTABLE, + * DB_TABLEINFO_ORDER or + * DB_TABLEINFO_FULL (which does both). + * These are bitwise, so the first two can be + * combined using |. + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::setOption() + */ + function tableInfo($result, $mode = null) + { + /* + * If the DB_ class has a tableInfo() method, that one + * overrides this one. But, if the driver doesn't have one, + * this method runs and tells users about that fact. + */ + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ getTables() + + /** + * Lists the tables in the current database + * + * @return array the list of tables. A DB_Error object on failure. + * + * @deprecated Method deprecated some time before Release 1.2 + */ + function getTables() + { + return $this->getListOf('tables'); + } + + // }}} + // {{{ getListOf() + + /** + * Lists internal database information + * + * @param string $type type of information being sought. + * Common items being sought are: + * tables, databases, users, views, functions + * Each DBMS's has its own capabilities. + * + * @return array an array listing the items sought. + * A DB DB_Error object on failure. + */ + function getListOf($type) + { + $sql = $this->getSpecialQuery($type); + if ($sql === null) { + $this->last_query = ''; + return $this->raiseError(DB_ERROR_UNSUPPORTED); + } elseif (is_int($sql) || DB::isError($sql)) { + // Previous error + return $this->raiseError($sql); + } elseif (is_array($sql)) { + // Already the result + return $sql; + } + // Launch this query + return $this->getCol($sql); + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + return $this->raiseError(DB_ERROR_UNSUPPORTED); + } + + // }}} + // {{{ _rtrimArrayValues() + + /** + * Right-trims all strings in an array + * + * @param array $array the array to be trimmed (passed by reference) + * + * @return void + * + * @access protected + */ + function _rtrimArrayValues(&$array) + { + foreach ($array as $key => $value) { + if (is_string($value)) { + $array[$key] = rtrim($value); + } + } + } + + // }}} + // {{{ _convertNullArrayValuesToEmpty() + + /** + * Converts all null values in an array to empty strings + * + * @param array $array the array to be de-nullified (passed by reference) + * + * @return void + * + * @access protected + */ + function _convertNullArrayValuesToEmpty(&$array) + { + foreach ($array as $key => $value) { + if (is_null($value)) { + $array[$key] = ''; + } + } + } + + // }}} +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/DB/dbase.php b/src/www/lib/pear/DB/dbase.php new file mode 100644 index 00000000..9ec04a69 --- /dev/null +++ b/src/www/lib/pear/DB/dbase.php @@ -0,0 +1,510 @@ + + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: dbase.php,v 1.1 2005/05/28 01:55:10 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's dbase extension + * for interacting with dBase databases + * + * These methods overload the ones declared in DB_common. + * + * @category Database + * @package DB + * @author Tomas V.V. Cox + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_dbase extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'dbase'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'dbase'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => false, + 'new_link' => false, + 'numrows' => true, + 'pconnect' => false, + 'prepare' => false, + 'ssl' => false, + 'transactions' => false, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * A means of emulating result resources + * @var array + */ + var $res_row = array(); + + /** + * The quantity of results so far + * + * For emulating result resources. + * + * @var integer + */ + var $result = 0; + + /** + * Maps dbase data type id's to human readable strings + * + * The human readable values are based on the output of PHP's + * dbase_get_header_info() function. + * + * @var array + * @since Property available since Release 1.7.0 + */ + var $types = array( + 'C' => 'character', + 'D' => 'date', + 'L' => 'boolean', + 'M' => 'memo', + 'N' => 'number', + ); + + + // }}} + // {{{ constructor + + /** + * This constructor calls $this->DB_common() + * + * @return void + */ + function DB_dbase() + { + $this->DB_common(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database and create it if it doesn't exist + * + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's dbase driver supports the following extra DSN options: + * + mode An integer specifying the read/write mode to use + * (0 = read only, 1 = write only, 2 = read/write). + * Available since PEAR DB 1.7.0. + * + fields An array of arrays that PHP's dbase_create() function needs + * to create a new database. This information is used if the + * dBase file specified in the "database" segment of the DSN + * does not exist. For more info, see the PHP manual's + * {@link http://php.net/dbase_create dbase_create()} page. + * Available since PEAR DB 1.7.0. + * + * Example of how to connect and establish a new dBase file if necessary: + * + * require_once 'DB.php'; + * + * $dsn = array( + * 'phptype' => 'dbase', + * 'database' => '/path/and/name/of/dbase/file', + * 'mode' => 2, + * 'fields' => array( + * array('a', 'N', 5, 0), + * array('b', 'C', 40), + * array('c', 'C', 255), + * array('d', 'C', 20), + * ), + * ); + * $options = array( + * 'debug' => 2, + * 'portability' => DB_PORTABILITY_ALL, + * ); + * + * $db =& DB::connect($dsn, $options); + * if (PEAR::isError($db)) { + * die($db->getMessage()); + * } + * + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('dbase')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + /* + * Turn track_errors on for entire script since $php_errormsg + * is the only way to find errors from the dbase extension. + */ + ini_set('track_errors', 1); + $php_errormsg = ''; + + if (!file_exists($dsn['database'])) { + $this->dsn['mode'] = 2; + if (empty($dsn['fields']) || !is_array($dsn['fields'])) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + 'the dbase file does not exist and ' + . 'it could not be created because ' + . 'the "fields" element of the DSN ' + . 'is not properly set'); + } + $this->connection = @dbase_create($dsn['database'], + $dsn['fields']); + if (!$this->connection) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + 'the dbase file does not exist and ' + . 'the attempt to create it failed: ' + . $php_errormsg); + } + } else { + if (!isset($this->dsn['mode'])) { + $this->dsn['mode'] = 0; + } + $this->connection = @dbase_open($dsn['database'], + $this->dsn['mode']); + if (!$this->connection) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); + } + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @dbase_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ &query() + + function &query($query = null) + { + // emulate result resources + $this->res_row[(int)$this->result] = 0; + $tmp =& new DB_result($this, $this->result++); + return $tmp; + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum === null) { + $rownum = $this->res_row[(int)$result]++; + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @dbase_get_record_with_names($this->connection, $rownum); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @dbase_get_record($this->connection, $rownum); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($foo) + { + return @dbase_numfields($this->connection); + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ + function numRows($foo) + { + return @dbase_numrecords($this->connection); + } + + // }}} + // {{{ quoteSmart() + + /** + * Formats input so it can be safely used in a query + * + * @param mixed $in the data to be formatted + * + * @return mixed the formatted data. The format depends on the input's + * PHP type: + * + null = the string NULL + * + boolean = T if true or + * F if false. Use the Logical + * data type. + * + integer or double = the unquoted number + * + other (including strings and numeric strings) = + * the data with single quotes escaped by preceeding + * single quotes then the whole string is encapsulated + * between single quotes + * + * @see DB_common::quoteSmart() + * @since Method available since Release 1.6.0 + */ + function quoteSmart($in) + { + if (is_int($in) || is_double($in)) { + return $in; + } elseif (is_bool($in)) { + return $in ? 'T' : 'F'; + } elseif (is_null($in)) { + return 'NULL'; + } else { + return "'" . $this->escapeSimple($in) . "'"; + } + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about the current database + * + * @param mixed $result THIS IS UNUSED IN DBASE. The current database + * is examined regardless of what is provided here. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + * @since Method available since Release 1.7.0 + */ + function tableInfo($result = null, $mode = null) + { + if (function_exists('dbase_get_header_info')) { + $id = @dbase_get_header_info($this->connection); + if (!$id && $php_errormsg) { + return $this->raiseError(DB_ERROR, + null, null, null, + $php_errormsg); + } + } else { + /* + * This segment for PHP 4 is loosely based on code by + * Hadi Rusiah in the comments on + * the dBase reference page in the PHP manual. + */ + $db = @fopen($this->dsn['database'], 'r'); + if (!$db) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); + } + + $id = array(); + $i = 0; + + $line = fread($db, 32); + while (!feof($db)) { + $line = fread($db, 32); + if (substr($line, 0, 1) == chr(13)) { + break; + } else { + $pos = strpos(substr($line, 0, 10), chr(0)); + $pos = ($pos == 0 ? 10 : $pos); + $id[$i] = array( + 'name' => substr($line, 0, $pos), + 'type' => $this->types[substr($line, 11, 1)], + 'length' => ord(substr($line, 16, 1)), + 'precision' => ord(substr($line, 17, 1)), + ); + } + $i++; + } + + fclose($db); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $res = array(); + $count = count($id); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $res[$i] = array( + 'table' => $this->dsn['database'], + 'name' => $case_func($id[$i]['name']), + 'type' => $id[$i]['type'], + 'len' => $id[$i]['length'], + 'flags' => '' + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + return $res; + } + + // }}} +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/DB/fbsql.php b/src/www/lib/pear/DB/fbsql.php new file mode 100644 index 00000000..49520028 --- /dev/null +++ b/src/www/lib/pear/DB/fbsql.php @@ -0,0 +1,770 @@ + + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: fbsql.php,v 1.1 2005/05/28 01:55:10 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's fbsql extension + * for interacting with FrontBase databases + * + * These methods overload the ones declared in DB_common. + * + * @category Database + * @package DB + * @author Frank M. Kromann + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + * @since Class functional since Release 1.7.0 + */ +class DB_fbsql extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'fbsql'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'fbsql'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'alter', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + 22 => DB_ERROR_SYNTAX, + 85 => DB_ERROR_ALREADY_EXISTS, + 108 => DB_ERROR_SYNTAX, + 116 => DB_ERROR_NOSUCHTABLE, + 124 => DB_ERROR_VALUE_COUNT_ON_ROW, + 215 => DB_ERROR_NOSUCHFIELD, + 217 => DB_ERROR_INVALID_NUMBER, + 226 => DB_ERROR_NOSUCHFIELD, + 231 => DB_ERROR_INVALID, + 239 => DB_ERROR_TRUNCATED, + 251 => DB_ERROR_SYNTAX, + 266 => DB_ERROR_NOT_FOUND, + 357 => DB_ERROR_CONSTRAINT_NOT_NULL, + 358 => DB_ERROR_CONSTRAINT, + 360 => DB_ERROR_CONSTRAINT, + 361 => DB_ERROR_CONSTRAINT, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + // }}} + // {{{ constructor + + /** + * This constructor calls $this->DB_common() + * + * @return void + */ + function DB_fbsql() + { + $this->DB_common(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('fbsql')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $params = array( + $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost', + $dsn['username'] ? $dsn['username'] : null, + $dsn['password'] ? $dsn['password'] : null, + ); + + $connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_connect'; + + $ini = ini_get('track_errors'); + $php_errormsg = ''; + if ($ini) { + $this->connection = @call_user_func_array($connect_function, + $params); + } else { + ini_set('track_errors', 1); + $this->connection = @call_user_func_array($connect_function, + $params); + ini_set('track_errors', $ini); + } + + if (!$this->connection) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); + } + + if ($dsn['database']) { + if (!@fbsql_select_db($dsn['database'], $this->connection)) { + return $this->fbsqlRaiseError(); + } + } + + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @fbsql_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $this->last_query = $query; + $query = $this->modifyQuery($query); + $result = @fbsql_query("$query;", $this->connection); + if (!$result) { + return $this->fbsqlRaiseError(); + } + // Determine which queries that should return data, and which + // should return an error code only. + if (DB::isManip($query)) { + return DB_OK; + } + return $result; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal fbsql result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return @fbsql_next_result($result); + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum !== null) { + if (!@fbsql_data_seek($result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @fbsql_fetch_array($result, FBSQL_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @fbsql_fetch_row($result); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return @fbsql_free_result($result); + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff=false) + { + if ($onoff) { + $this->query("SET COMMIT TRUE"); + } else { + $this->query("SET COMMIT FALSE"); + } + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + @fbsql_commit(); + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + @fbsql_rollback(); + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @fbsql_num_fields($result); + if (!$cols) { + return $this->fbsqlRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ + function numRows($result) + { + $rows = @fbsql_num_rows($result); + if ($rows === null) { + return $this->fbsqlRaiseError(); + } + return $rows; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if (DB::isManip($this->last_query)) { + $result = @fbsql_affected_rows($this->connection); + } else { + $result = 0; + } + return $result; + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_fbsql::createSequence(), DB_fbsql::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + do { + $repeat = 0; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query('SELECT UNIQUE FROM ' . $seqname); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $result; + } + } else { + $repeat = 0; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->fbsqlRaiseError(); + } + $result->fetchInto($tmp, DB_FETCHMODE_ORDERED); + return $tmp[0]; + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_fbsql::nextID(), DB_fbsql::dropSequence() + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + $res = $this->query('CREATE TABLE ' . $seqname + . ' (id INTEGER NOT NULL,' + . ' PRIMARY KEY(id))'); + if ($res) { + $res = $this->query('SET UNIQUE = 0 FOR ' . $seqname); + } + return $res; + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_fbsql::nextID(), DB_fbsql::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name) + . ' RESTRICT'); + } + + // }}} + // {{{ modifyLimitQuery() + + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ + function modifyLimitQuery($query, $from, $count, $params = array()) + { + if (DB::isManip($query)) { + return preg_replace('/^([\s(])*SELECT/i', + "\\1SELECT TOP($count)", $query); + } else { + return preg_replace('/([\s(])*SELECT/i', + "\\1SELECT TOP($from, $count)", $query); + } + } + + // }}} + // {{{ quoteSmart() + + /** + * Formats input so it can be safely used in a query + * + * @param mixed $in the data to be formatted + * + * @return mixed the formatted data. The format depends on the input's + * PHP type: + * + null = the string NULL + * + boolean = string TRUE or FALSE + * + integer or double = the unquoted number + * + other (including strings and numeric strings) = + * the data escaped according to FrontBase's settings + * then encapsulated between single quotes + * + * @see DB_common::quoteSmart() + * @since Method available since Release 1.6.0 + */ + function quoteSmart($in) + { + if (is_int($in) || is_double($in)) { + return $in; + } elseif (is_bool($in)) { + return $in ? 'TRUE' : 'FALSE'; + } elseif (is_null($in)) { + return 'NULL'; + } else { + return "'" . $this->escapeSimple($in) . "'"; + } + } + + // }}} + // {{{ fbsqlRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_fbsql::errorNative(), DB_common::errorCode() + */ + function fbsqlRaiseError($errno = null) + { + if ($errno === null) { + $errno = $this->errorCode(fbsql_errno($this->connection)); + } + return $this->raiseError($errno, null, null, null, + @fbsql_error($this->connection)); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code produced by the last query + * + * @return int the DBMS' error code + */ + function errorNative() + { + return @fbsql_errno($this->connection); + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @fbsql_list_fields($this->dsn['database'], + $result, $this->connection); + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->fbsqlRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @fbsql_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $res[$i] = array( + 'table' => $case_func(@fbsql_field_table($id, $i)), + 'name' => $case_func(@fbsql_field_name($id, $i)), + 'type' => @fbsql_field_type($id, $i), + 'len' => @fbsql_field_len($id, $i), + 'flags' => @fbsql_field_flags($id, $i), + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @fbsql_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SELECT "table_name" FROM information_schema.tables' + . ' t0, information_schema.schemata t1' + . ' WHERE t0.schema_pk=t1.schema_pk AND' + . ' "table_type" = \'BASE TABLE\'' + . ' AND "schema_name" = current_schema'; + case 'views': + return 'SELECT "table_name" FROM information_schema.tables' + . ' t0, information_schema.schemata t1' + . ' WHERE t0.schema_pk=t1.schema_pk AND' + . ' "table_type" = \'VIEW\'' + . ' AND "schema_name" = current_schema'; + case 'users': + return 'SELECT "user_name" from information_schema.users'; + case 'functions': + return 'SELECT "routine_name" FROM' + . ' information_schema.psm_routines' + . ' t0, information_schema.schemata t1' + . ' WHERE t0.schema_pk=t1.schema_pk' + . ' AND "routine_kind"=\'FUNCTION\'' + . ' AND "schema_name" = current_schema'; + case 'procedures': + return 'SELECT "routine_name" FROM' + . ' information_schema.psm_routines' + . ' t0, information_schema.schemata t1' + . ' WHERE t0.schema_pk=t1.schema_pk' + . ' AND "routine_kind"=\'PROCEDURE\'' + . ' AND "schema_name" = current_schema'; + default: + return null; + } + } + + // }}} +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/DB/ibase.php b/src/www/lib/pear/DB/ibase.php new file mode 100644 index 00000000..da829dfd --- /dev/null +++ b/src/www/lib/pear/DB/ibase.php @@ -0,0 +1,1071 @@ + + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: ibase.php,v 1.1 2005/05/28 01:55:10 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's interbase extension + * for interacting with Interbase and Firebird databases + * + * These methods overload the ones declared in DB_common. + * + * While this class works with PHP 4, PHP's InterBase extension is + * unstable in PHP 4. Use PHP 5. + * + * NOTICE: limitQuery() only works for Firebird. + * + * @category Database + * @package DB + * @author Sterling Hughes + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + * @since Class became stable in Release 1.7.0 + */ +class DB_ibase extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'ibase'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'ibase'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * NOTE: only firebird supports limit. + * + * @var array + */ + var $features = array( + 'limit' => false, + 'new_link' => false, + 'numrows' => 'emulate', + 'pconnect' => true, + 'prepare' => true, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + -104 => DB_ERROR_SYNTAX, + -150 => DB_ERROR_ACCESS_VIOLATION, + -151 => DB_ERROR_ACCESS_VIOLATION, + -155 => DB_ERROR_NOSUCHTABLE, + -157 => DB_ERROR_NOSUCHFIELD, + -158 => DB_ERROR_VALUE_COUNT_ON_ROW, + -170 => DB_ERROR_MISMATCH, + -171 => DB_ERROR_MISMATCH, + -172 => DB_ERROR_INVALID, + // -204 => // Covers too many errors, need to use regex on msg + -205 => DB_ERROR_NOSUCHFIELD, + -206 => DB_ERROR_NOSUCHFIELD, + -208 => DB_ERROR_INVALID, + -219 => DB_ERROR_NOSUCHTABLE, + -297 => DB_ERROR_CONSTRAINT, + -303 => DB_ERROR_INVALID, + -413 => DB_ERROR_INVALID_NUMBER, + -530 => DB_ERROR_CONSTRAINT, + -551 => DB_ERROR_ACCESS_VIOLATION, + -552 => DB_ERROR_ACCESS_VIOLATION, + // -607 => // Covers too many errors, need to use regex on msg + -625 => DB_ERROR_CONSTRAINT_NOT_NULL, + -803 => DB_ERROR_CONSTRAINT, + -804 => DB_ERROR_VALUE_COUNT_ON_ROW, + -904 => DB_ERROR_CONNECT_FAILED, + -922 => DB_ERROR_NOSUCHDB, + -923 => DB_ERROR_CONNECT_FAILED, + -924 => DB_ERROR_CONNECT_FAILED + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * The number of rows affected by a data manipulation query + * @var integer + * @access private + */ + var $affected = 0; + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ + var $autocommit = true; + + /** + * The prepared statement handle from the most recently executed statement + * + * {@internal Mainly here because the InterBase/Firebird API is only + * able to retrieve data from result sets if the statemnt handle is + * still in scope.}} + * + * @var resource + */ + var $last_stmt; + + /** + * Is the given prepared statement a data manipulation query? + * @var array + * @access private + */ + var $manip_query = array(); + + + // }}} + // {{{ constructor + + /** + * This constructor calls $this->DB_common() + * + * @return void + */ + function DB_ibase() + { + $this->DB_common(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's ibase driver supports the following extra DSN options: + * + buffers The number of database buffers to allocate for the + * server-side cache. + * + charset The default character set for a database. + * + dialect The default SQL dialect for any statement + * executed within a connection. Defaults to the + * highest one supported by client libraries. + * Functional only with InterBase 6 and up. + * + role Functional only with InterBase 5 and up. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('interbase')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + if ($this->dbsyntax == 'firebird') { + $this->features['limit'] = 'alter'; + } + + $params = array( + $dsn['hostspec'] + ? ($dsn['hostspec'] . ':' . $dsn['database']) + : $dsn['database'], + $dsn['username'] ? $dsn['username'] : null, + $dsn['password'] ? $dsn['password'] : null, + isset($dsn['charset']) ? $dsn['charset'] : null, + isset($dsn['buffers']) ? $dsn['buffers'] : null, + isset($dsn['dialect']) ? $dsn['dialect'] : null, + isset($dsn['role']) ? $dsn['role'] : null, + ); + + $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect'; + + $this->connection = @call_user_func_array($connect_function, $params); + if (!$this->connection) { + return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED); + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @ibase_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + $query = $this->modifyQuery($query); + $result = @ibase_query($this->connection, $query); + + if (!$result) { + return $this->ibaseRaiseError(); + } + if ($this->autocommit && $ismanip) { + @ibase_commit($this->connection); + } + if ($ismanip) { + $this->affected = $result; + return DB_OK; + } else { + $this->affected = 0; + return $result; + } + } + + // }}} + // {{{ modifyLimitQuery() + + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * Only works with Firebird. + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ + function modifyLimitQuery($query, $from, $count, $params = array()) + { + if ($this->dsn['dbsyntax'] == 'firebird') { + $query = preg_replace('/^([\s(])*SELECT/i', + "SELECT FIRST $count SKIP $from", $query); + } + return $query; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal ibase result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum !== null) { + return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE); + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + if (function_exists('ibase_fetch_assoc')) { + $arr = @ibase_fetch_assoc($result); + } else { + $arr = get_object_vars(ibase_fetch_object($result)); + } + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @ibase_fetch_row($result); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return @ibase_free_result($result); + } + + // }}} + // {{{ freeQuery() + + function freeQuery($query) + { + @ibase_free_query($query); + return true; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if (is_integer($this->affected)) { + return $this->affected; + } + return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @ibase_num_fields($result); + if (!$cols) { + return $this->ibaseRaiseError(); + } + return $cols; + } + + // }}} + // {{{ prepare() + + /** + * Prepares a query for multiple execution with execute(). + * + * prepare() requires a generic query as string like + * INSERT INTO numbers VALUES (?, ?, ?) + * . The ? characters are placeholders. + * + * Three types of placeholders can be used: + * + ? a quoted scalar value, i.e. strings, integers + * + ! value is inserted 'as is' + * + & requires a file name. The file's contents get + * inserted into the query (i.e. saving binary + * data in a db) + * + * Use backslashes to escape placeholder characters if you don't want + * them to be interpreted as placeholders. Example: + * "UPDATE foo SET col=? WHERE col='over \& under'" + * + * + * @param string $query query to be prepared + * @return mixed DB statement resource on success. DB_Error on failure. + */ + function prepare($query) + { + $tokens = preg_split('/((? $val) { + switch ($val) { + case '?': + $types[$token++] = DB_PARAM_SCALAR; + break; + case '&': + $types[$token++] = DB_PARAM_OPAQUE; + break; + case '!': + $types[$token++] = DB_PARAM_MISC; + break; + default: + $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val); + $newquery .= $tokens[$key] . '?'; + } + } + + $newquery = substr($newquery, 0, -1); + $this->last_query = $query; + $newquery = $this->modifyQuery($newquery); + $stmt = @ibase_prepare($this->connection, $newquery); + $this->prepare_types[(int)$stmt] = $types; + $this->manip_query[(int)$stmt] = DB::isManip($query); + return $stmt; + } + + // }}} + // {{{ execute() + + /** + * Executes a DB statement prepared with prepare(). + * + * @param resource $stmt a DB statement resource returned from prepare() + * @param mixed $data array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 for non-array items or the + * quantity of elements in the array. + * @return object a new DB_Result or a DB_Error when fail + * @see DB_ibase::prepare() + * @access public + */ + function &execute($stmt, $data = array()) + { + $data = (array)$data; + $this->last_parameters = $data; + + $types =& $this->prepare_types[(int)$stmt]; + if (count($types) != count($data)) { + $tmp =& $this->raiseError(DB_ERROR_MISMATCH); + return $tmp; + } + + $i = 0; + foreach ($data as $key => $value) { + if ($types[$i] == DB_PARAM_MISC) { + /* + * ibase doesn't seem to have the ability to pass a + * parameter along unchanged, so strip off quotes from start + * and end, plus turn two single quotes to one single quote, + * in order to avoid the quotes getting escaped by + * ibase and ending up in the database. + */ + $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]); + $data[$key] = str_replace("''", "'", $data[$key]); + } elseif ($types[$i] == DB_PARAM_OPAQUE) { + $fp = @fopen($data[$key], 'rb'); + if (!$fp) { + $tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION); + return $tmp; + } + $data[$key] = fread($fp, filesize($data[$key])); + fclose($fp); + } + $i++; + } + + array_unshift($data, $stmt); + + $res = call_user_func_array('ibase_execute', $data); + if (!$res) { + $tmp =& $this->ibaseRaiseError(); + return $tmp; + } + /* XXX need this? + if ($this->autocommit && $this->manip_query[(int)$stmt]) { + @ibase_commit($this->connection); + }*/ + $this->last_stmt = $stmt; + if ($this->manip_query[(int)$stmt]) { + $tmp = DB_OK; + } else { + $tmp =& new DB_result($this, $res); + } + return $tmp; + } + + /** + * Frees the internal resources associated with a prepared query + * + * @param resource $stmt the prepared statement's PHP resource + * @param bool $free_resource should the PHP resource be freed too? + * Use false if you need to get data + * from the result set later. + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_ibase::prepare() + */ + function freePrepared($stmt, $free_resource = true) + { + if (!is_resource($stmt)) { + return false; + } + if ($free_resource) { + @ibase_free_query($stmt); + } + unset($this->prepare_tokens[(int)$stmt]); + unset($this->prepare_types[(int)$stmt]); + unset($this->manip_query[(int)$stmt]); + return true; + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff = false) + { + $this->autocommit = $onoff ? 1 : 0; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + return @ibase_commit($this->connection); + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + return @ibase_rollback($this->connection); + } + + // }}} + // {{{ transactionInit() + + function transactionInit($trans_args = 0) + { + return $trans_args + ? @ibase_trans($trans_args, $this->connection) + : @ibase_trans(); + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_ibase::createSequence(), DB_ibase::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $sqn = strtoupper($this->getSequenceName($seq_name)); + $repeat = 0; + do { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result =& $this->query("SELECT GEN_ID(${sqn}, 1) " + . 'FROM RDB$GENERATORS ' + . "WHERE RDB\$GENERATOR_NAME='${sqn}'"); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result)) { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $result; + } + } else { + $repeat = 0; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); + $result->free(); + return $arr[0]; + } + + // }}} + // {{{ createSequence() + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_ibase::nextID(), DB_ibase::dropSequence() + */ + function createSequence($seq_name) + { + $sqn = strtoupper($this->getSequenceName($seq_name)); + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("CREATE GENERATOR ${sqn}"); + $this->popErrorHandling(); + + return $result; + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_ibase::nextID(), DB_ibase::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DELETE FROM RDB$GENERATORS ' + . "WHERE RDB\$GENERATOR_NAME='" + . strtoupper($this->getSequenceName($seq_name)) + . "'"); + } + + // }}} + // {{{ _ibaseFieldFlags() + + /** + * Get the column's flags + * + * Supports "primary_key", "unique_key", "not_null", "default", + * "computed" and "blob". + * + * @param string $field_name the name of the field + * @param string $table_name the name of the table + * + * @return string the flags + * + * @access private + */ + function _ibaseFieldFlags($field_name, $table_name) + { + $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE' + .' FROM RDB$INDEX_SEGMENTS I' + .' JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME' + .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\'' + .' AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''; + + $result = @ibase_query($this->connection, $sql); + if (!$result) { + return $this->ibaseRaiseError(); + } + + $flags = ''; + if ($obj = @ibase_fetch_object($result)) { + @ibase_free_result($result); + if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'PRIMARY KEY') { + $flags .= 'primary_key '; + } + if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'UNIQUE') { + $flags .= 'unique_key '; + } + } + + $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,' + .' R.RDB$DEFAULT_SOURCE AS DSOURCE,' + .' F.RDB$FIELD_TYPE AS FTYPE,' + .' F.RDB$COMPUTED_SOURCE AS CSOURCE' + .' FROM RDB$RELATION_FIELDS R ' + .' JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME' + .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'' + .' AND R.RDB$FIELD_NAME=\'' . $field_name . '\''; + + $result = @ibase_query($this->connection, $sql); + if (!$result) { + return $this->ibaseRaiseError(); + } + if ($obj = @ibase_fetch_object($result)) { + @ibase_free_result($result); + if (isset($obj->NFLAG)) { + $flags .= 'not_null '; + } + if (isset($obj->DSOURCE)) { + $flags .= 'default '; + } + if (isset($obj->CSOURCE)) { + $flags .= 'computed '; + } + if (isset($obj->FTYPE) && $obj->FTYPE == 261) { + $flags .= 'blob '; + } + } + + return trim($flags); + } + + // }}} + // {{{ ibaseRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_ibase::errorNative(), DB_ibase::errorCode() + */ + function &ibaseRaiseError($errno = null) + { + if ($errno === null) { + $errno = $this->errorCode($this->errorNative()); + } + $tmp =& $this->raiseError($errno, null, null, null, @ibase_errmsg()); + return $tmp; + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code produced by the last query + * + * @return int the DBMS' error code. NULL if there is no error code. + * + * @since Method available since Release 1.7.0 + */ + function errorNative() + { + if (function_exists('ibase_errcode')) { + return @ibase_errcode(); + } + if (preg_match('/^Dynamic SQL Error SQL error code = ([0-9-]+)/i', + @ibase_errmsg(), $m)) { + return (int)$m[1]; + } + return null; + } + + // }}} + // {{{ errorCode() + + /** + * Maps native error codes to DB's portable ones + * + * @param int $nativecode the error code returned by the DBMS + * + * @return int the portable DB error code. Return DB_ERROR if the + * current driver doesn't have a mapping for the + * $nativecode submitted. + * + * @since Method available since Release 1.7.0 + */ + function errorCode($nativecode = null) + { + if (isset($this->errorcode_map[$nativecode])) { + return $this->errorcode_map[$nativecode]; + } + + static $error_regexps; + if (!isset($error_regexps)) { + $error_regexps = array( + '/generator .* is not defined/' + => DB_ERROR_SYNTAX, // for compat. w ibase_errcode() + '/table.*(not exist|not found|unknown)/i' + => DB_ERROR_NOSUCHTABLE, + '/table .* already exists/i' + => DB_ERROR_ALREADY_EXISTS, + '/unsuccessful metadata update .* failed attempt to store duplicate value/i' + => DB_ERROR_ALREADY_EXISTS, + '/unsuccessful metadata update .* not found/i' + => DB_ERROR_NOT_FOUND, + '/validation error for column .* value "\*\*\* null/i' + => DB_ERROR_CONSTRAINT_NOT_NULL, + '/violation of [\w ]+ constraint/i' + => DB_ERROR_CONSTRAINT, + '/conversion error from string/i' + => DB_ERROR_INVALID_NUMBER, + '/no permission for/i' + => DB_ERROR_ACCESS_VIOLATION, + '/arithmetic exception, numeric overflow, or string truncation/i' + => DB_ERROR_INVALID, + ); + } + + $errormsg = @ibase_errmsg(); + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + return DB_ERROR; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * NOTE: only supports 'table' and 'flags' if $result + * is a table name. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @ibase_query($this->connection, + "SELECT * FROM $result WHERE 1=0"); + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @ibase_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $info = @ibase_field_info($id, $i); + $res[$i] = array( + 'table' => $got_string ? $case_func($result) : '', + 'name' => $case_func($info['name']), + 'type' => $info['type'], + 'len' => $info['length'], + 'flags' => ($got_string) + ? $this->_ibaseFieldFlags($info['name'], $result) + : '', + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @ibase_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SELECT DISTINCT R.RDB$RELATION_NAME FROM ' + . 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0'; + case 'views': + return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS'; + case 'users': + return 'SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES'; + default: + return null; + } + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/DB/ifx.php b/src/www/lib/pear/DB/ifx.php new file mode 100644 index 00000000..0bc8727f --- /dev/null +++ b/src/www/lib/pear/DB/ifx.php @@ -0,0 +1,681 @@ + + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: ifx.php,v 1.1 2005/05/28 01:55:10 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's ifx extension + * for interacting with Informix databases + * + * These methods overload the ones declared in DB_common. + * + * More info on Informix errors can be found at: + * http://www.informix.com/answers/english/ierrors.htm + * + * TODO: + * - set needed env Informix vars on connect + * - implement native prepare/execute + * + * @category Database + * @package DB + * @author Tomas V.V.Cox + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_ifx extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'ifx'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'ifx'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'emulate', + 'new_link' => false, + 'numrows' => 'emulate', + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + '-201' => DB_ERROR_SYNTAX, + '-206' => DB_ERROR_NOSUCHTABLE, + '-217' => DB_ERROR_NOSUCHFIELD, + '-236' => DB_ERROR_VALUE_COUNT_ON_ROW, + '-239' => DB_ERROR_CONSTRAINT, + '-253' => DB_ERROR_SYNTAX, + '-292' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-310' => DB_ERROR_ALREADY_EXISTS, + '-316' => DB_ERROR_ALREADY_EXISTS, + '-319' => DB_ERROR_NOT_FOUND, + '-329' => DB_ERROR_NODBSELECTED, + '-346' => DB_ERROR_CONSTRAINT, + '-386' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-391' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-554' => DB_ERROR_SYNTAX, + '-691' => DB_ERROR_CONSTRAINT, + '-692' => DB_ERROR_CONSTRAINT, + '-703' => DB_ERROR_CONSTRAINT_NOT_NULL, + '-1204' => DB_ERROR_INVALID_DATE, + '-1205' => DB_ERROR_INVALID_DATE, + '-1206' => DB_ERROR_INVALID_DATE, + '-1209' => DB_ERROR_INVALID_DATE, + '-1210' => DB_ERROR_INVALID_DATE, + '-1212' => DB_ERROR_INVALID_DATE, + '-1213' => DB_ERROR_INVALID_NUMBER, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ + var $autocommit = true; + + /** + * The quantity of transactions begun + * + * {@internal While this is private, it can't actually be designated + * private in PHP 5 because it is directly accessed in the test suite.}} + * + * @var integer + * @access private + */ + var $transaction_opcount = 0; + + /** + * The number of rows affected by a data manipulation query + * @var integer + * @access private + */ + var $affected = 0; + + + // }}} + // {{{ constructor + + /** + * This constructor calls $this->DB_common() + * + * @return void + */ + function DB_ifx() + { + $this->DB_common(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('informix') && + !PEAR::loadExtension('Informix')) + { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $dbhost = $dsn['hostspec'] ? '@' . $dsn['hostspec'] : ''; + $dbname = $dsn['database'] ? $dsn['database'] . $dbhost : ''; + $user = $dsn['username'] ? $dsn['username'] : ''; + $pw = $dsn['password'] ? $dsn['password'] : ''; + + $connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect'; + + $this->connection = @$connect_function($dbname, $user, $pw); + if (!is_resource($this->connection)) { + return $this->ifxRaiseError(DB_ERROR_CONNECT_FAILED); + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @ifx_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + $this->affected = null; + if (preg_match('/(SELECT)/i', $query)) { //TESTME: Use !DB::isManip()? + // the scroll is needed for fetching absolute row numbers + // in a select query result + $result = @ifx_query($query, $this->connection, IFX_SCROLL); + } else { + if (!$this->autocommit && $ismanip) { + if ($this->transaction_opcount == 0) { + $result = @ifx_query('BEGIN WORK', $this->connection); + if (!$result) { + return $this->ifxRaiseError(); + } + } + $this->transaction_opcount++; + } + $result = @ifx_query($query, $this->connection); + } + if (!$result) { + return $this->ifxRaiseError(); + } + $this->affected = @ifx_affected_rows($result); + // Determine which queries should return data, and which + // should return an error code only. + if (preg_match('/(SELECT)/i', $query)) { + return $result; + } + // XXX Testme: free results inside a transaction + // may cause to stop it and commit the work? + + // Result has to be freed even with a insert or update + @ifx_free_result($result); + + return DB_OK; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal ifx result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if (DB::isManip($this->last_query)) { + return $this->affected; + } else { + return 0; + } + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if (($rownum !== null) && ($rownum < 0)) { + return null; + } + if ($rownum === null) { + /* + * Even though fetch_row() should return the next row if + * $rownum is null, it doesn't in all cases. Bug 598. + */ + $rownum = 'NEXT'; + } else { + // Index starts at row 1, unlike most DBMS's starting at 0. + $rownum++; + } + if (!$arr = @ifx_fetch_row($result, $rownum)) { + return null; + } + if ($fetchmode !== DB_FETCHMODE_ASSOC) { + $i=0; + $order = array(); + foreach ($arr as $val) { + $order[$i++] = $val; + } + $arr = $order; + } elseif ($fetchmode == DB_FETCHMODE_ASSOC && + $this->options['portability'] & DB_PORTABILITY_LOWERCASE) + { + $arr = array_change_key_case($arr, CASE_LOWER); + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + if (!$cols = @ifx_num_fields($result)) { + return $this->ifxRaiseError(); + } + return $cols; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return @ifx_free_result($result); + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff = true) + { + // XXX if $this->transaction_opcount > 0, we should probably + // issue a warning here. + $this->autocommit = $onoff ? true : false; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + if ($this->transaction_opcount > 0) { + $result = @ifx_query('COMMIT WORK', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->ifxRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + if ($this->transaction_opcount > 0) { + $result = @ifx_query('ROLLBACK WORK', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->ifxRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ ifxRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_ifx::errorNative(), DB_ifx::errorCode() + */ + function ifxRaiseError($errno = null) + { + if ($errno === null) { + $errno = $this->errorCode(ifx_error()); + } + return $this->raiseError($errno, null, null, null, + $this->errorNative()); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code and message produced by the last query + * + * @return string the DBMS' error code and message + */ + function errorNative() + { + return @ifx_error() . ' ' . @ifx_errormsg(); + } + + // }}} + // {{{ errorCode() + + /** + * Maps native error codes to DB's portable ones. + * + * Requires that the DB implementation's constructor fills + * in the $errorcode_map property. + * + * @param string $nativecode error code returned by the database + * @return int a portable DB error code, or DB_ERROR if this DB + * implementation has no mapping for the given error code. + */ + function errorCode($nativecode) + { + if (ereg('SQLCODE=(.*)]', $nativecode, $match)) { + $code = $match[1]; + if (isset($this->errorcode_map[$code])) { + return $this->errorcode_map[$code]; + } + } + return DB_ERROR; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * NOTE: only supports 'table' if $result is a table name. + * + * If analyzing a query result and the result has duplicate field names, + * an error will be raised saying + * can't distinguish duplicate field names. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + * @since Method available since Release 1.6.0 + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @ifx_query("SELECT * FROM $result WHERE 1=0", + $this->connection); + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->ifxRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + $flds = @ifx_fieldproperties($id); + $count = @ifx_num_fields($id); + + if (count($flds) != $count) { + return $this->raiseError("can't distinguish duplicate field names"); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $i = 0; + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + foreach ($flds as $key => $value) { + $props = explode(';', $value); + $res[$i] = array( + 'table' => $got_string ? $case_func($result) : '', + 'name' => $case_func($key), + 'type' => $props[0], + 'len' => $props[1], + 'flags' => $props[4] == 'N' ? 'not_null' : '', + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + $i++; + } + + // free the result only if we were called on a table + if ($got_string) { + @ifx_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SELECT tabname FROM systables WHERE tabid >= 100'; + default: + return null; + } + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/DB/msql.php b/src/www/lib/pear/DB/msql.php new file mode 100644 index 00000000..9cab8d0e --- /dev/null +++ b/src/www/lib/pear/DB/msql.php @@ -0,0 +1,810 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: msql.php,v 1.1 2005/05/28 01:55:10 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's msql extension + * for interacting with Mini SQL databases + * + * These methods overload the ones declared in DB_common. + * + * PHP's mSQL extension did weird things with NULL values prior to PHP + * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds + * those versions. + * + * @category Database + * @package DB + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + * @since Class not functional until Release 1.7.0 + */ +class DB_msql extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'msql'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'msql'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'emulate', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => false, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * The query result resource created by PHP + * + * Used to make affectedRows() work. Only contains the result for + * data manipulation queries. Contains false for other queries. + * + * @var resource + * @access private + */ + var $_result; + + + // }}} + // {{{ constructor + + /** + * This constructor calls $this->DB_common() + * + * @return void + */ + function DB_msql() + { + $this->DB_common(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * Example of how to connect: + * + * require_once 'DB.php'; + * + * // $dsn = 'msql://hostname/dbname'; // use a TCP connection + * $dsn = 'msql:///dbname'; // use a socket + * $options = array( + * 'portability' => DB_PORTABILITY_ALL, + * ); + * + * $db =& DB::connect($dsn, $options); + * if (PEAR::isError($db)) { + * die($db->getMessage()); + * } + * + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('msql')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $params = array(); + if ($dsn['hostspec']) { + $params[] = $dsn['port'] + ? $dsn['hostspec'] . ',' . $dsn['port'] + : $dsn['hostspec']; + } + + $connect_function = $persistent ? 'msql_pconnect' : 'msql_connect'; + + $ini = ini_get('track_errors'); + $php_errormsg = ''; + if ($ini) { + $this->connection = @call_user_func_array($connect_function, + $params); + } else { + ini_set('track_errors', 1); + $this->connection = @call_user_func_array($connect_function, + $params); + ini_set('track_errors', $ini); + } + + if (!$this->connection) { + if (($err = @msql_error()) != '') { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $err); + } else { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); + } + } + + if (!@msql_select_db($dsn['database'], $this->connection)) { + return $this->msqlRaiseError(); + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @msql_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $this->last_query = $query; + $query = $this->modifyQuery($query); + $result = @msql_query($query, $this->connection); + if (!$result) { + return $this->msqlRaiseError(); + } + // Determine which queries that should return data, and which + // should return an error code only. + if (DB::isManip($query)) { + $this->_result = $result; + return DB_OK; + } else { + $this->_result = false; + return $result; + } + } + + + // }}} + // {{{ nextResult() + + /** + * Move the internal msql result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * PHP's mSQL extension did weird things with NULL values prior to PHP + * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds + * those versions. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum !== null) { + if (!@msql_data_seek($result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @msql_fetch_array($result, MSQL_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @msql_fetch_row($result); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return @msql_free_result($result); + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @msql_num_fields($result); + if (!$cols) { + return $this->msqlRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ + function numRows($result) + { + $rows = @msql_num_rows($result); + if ($rows === false) { + return $this->msqlRaiseError(); + } + return $rows; + } + + // }}} + // {{{ affected() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if (!$this->_result) { + return 0; + } + return msql_affected_rows($this->_result); + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_msql::createSequence(), DB_msql::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + $repeat = false; + do { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result =& $this->query("SELECT _seq FROM ${seqname}"); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) { + $repeat = true; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->createSequence($seq_name); + $this->popErrorHandling(); + if (DB::isError($result)) { + return $this->raiseError($result); + } + } else { + $repeat = false; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); + $result->free(); + return $arr[0]; + } + + // }}} + // {{{ createSequence() + + /** + * Creates a new sequence + * + * Also creates a new table to associate the sequence with. Uses + * a separate table to ensure portability with other drivers. + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_msql::nextID(), DB_msql::dropSequence() + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + $res = $this->query('CREATE TABLE ' . $seqname + . ' (id INTEGER NOT NULL)'); + if (DB::isError($res)) { + return $res; + } + $res = $this->query("CREATE SEQUENCE ON ${seqname}"); + return $res; + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_msql::nextID(), DB_msql::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ quoteIdentifier() + + /** + * mSQL does not support delimited identifiers + * + * @param string $str the identifier name to be quoted + * + * @return object a DB_Error object + * + * @see DB_common::quoteIdentifier() + * @since Method available since Release 1.7.0 + */ + function quoteIdentifier($str) + { + return $this->raiseError(DB_ERROR_UNSUPPORTED); + } + + // }}} + // {{{ escapeSimple() + + /** + * Escapes a string according to the current DBMS's standards + * + * @param string $str the string to be escaped + * + * @return string the escaped string + * + * @see DB_common::quoteSmart() + * @since Method available since Release 1.7.0 + */ + function escapeSimple($str) + { + return addslashes($str); + } + + // }}} + // {{{ msqlRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_msql::errorNative(), DB_msql::errorCode() + */ + function msqlRaiseError($errno = null) + { + $native = $this->errorNative(); + if ($errno === null) { + $errno = $this->errorCode($native); + } + return $this->raiseError($errno, null, null, null, $native); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error message produced by the last query + * + * @return string the DBMS' error message + */ + function errorNative() + { + return @msql_error(); + } + + // }}} + // {{{ errorCode() + + /** + * Determines PEAR::DB error code from the database's text error message + * + * @param string $errormsg the error message returned from the database + * + * @return integer the error number from a DB_ERROR* constant + */ + function errorCode($errormsg) + { + static $error_regexps; + if (!isset($error_regexps)) { + $error_regexps = array( + '/^Access to database denied/i' + => DB_ERROR_ACCESS_VIOLATION, + '/^Bad index name/i' + => DB_ERROR_ALREADY_EXISTS, + '/^Bad order field/i' + => DB_ERROR_SYNTAX, + '/^Bad type for comparison/i' + => DB_ERROR_SYNTAX, + '/^Can\'t perform LIKE on/i' + => DB_ERROR_SYNTAX, + '/^Can\'t use TEXT fields in LIKE comparison/i' + => DB_ERROR_SYNTAX, + '/^Couldn\'t create temporary table/i' + => DB_ERROR_CANNOT_CREATE, + '/^Error creating table file/i' + => DB_ERROR_CANNOT_CREATE, + '/^Field .* cannot be null$/i' + => DB_ERROR_CONSTRAINT_NOT_NULL, + '/^Index (field|condition) .* cannot be null$/i' + => DB_ERROR_SYNTAX, + '/^Invalid date format/i' + => DB_ERROR_INVALID_DATE, + '/^Invalid time format/i' + => DB_ERROR_INVALID, + '/^Literal value for .* is wrong type$/i' + => DB_ERROR_INVALID_NUMBER, + '/^No Database Selected/i' + => DB_ERROR_NODBSELECTED, + '/^No value specified for field/i' + => DB_ERROR_VALUE_COUNT_ON_ROW, + '/^Non unique value for unique index/i' + => DB_ERROR_CONSTRAINT, + '/^Out of memory for temporary table/i' + => DB_ERROR_CANNOT_CREATE, + '/^Permission denied/i' + => DB_ERROR_ACCESS_VIOLATION, + '/^Reference to un-selected table/i' + => DB_ERROR_SYNTAX, + '/^syntax error/i' + => DB_ERROR_SYNTAX, + '/^Table .* exists$/i' + => DB_ERROR_ALREADY_EXISTS, + '/^Unknown database/i' + => DB_ERROR_NOSUCHDB, + '/^Unknown field/i' + => DB_ERROR_NOSUCHFIELD, + '/^Unknown (index|system variable)/i' + => DB_ERROR_NOT_FOUND, + '/^Unknown table/i' + => DB_ERROR_NOSUCHTABLE, + '/^Unqualified field/i' + => DB_ERROR_SYNTAX, + ); + } + + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + return DB_ERROR; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::setOption() + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @msql_query("SELECT * FROM $result", + $this->connection); + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->raiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @msql_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $tmp = @msql_fetch_field($id); + + $flags = ''; + if ($tmp->not_null) { + $flags .= 'not_null '; + } + if ($tmp->unique) { + $flags .= 'unique_key '; + } + $flags = trim($flags); + + $res[$i] = array( + 'table' => $case_func($tmp->table), + 'name' => $case_func($tmp->name), + 'type' => $tmp->type, + 'len' => msql_field_len($id, $i), + 'flags' => $flags, + ); + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @msql_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtain a list of a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return array the array containing the list of objects requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'databases': + $id = @msql_list_dbs($this->connection); + break; + case 'tables': + $id = @msql_list_tables($this->dsn['database'], + $this->connection); + break; + default: + return null; + } + if (!$id) { + return $this->msqlRaiseError(); + } + $out = array(); + while ($row = @msql_fetch_row($id)) { + $out[] = $row[0]; + } + return $out; + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/DB/mssql.php b/src/www/lib/pear/DB/mssql.php new file mode 100644 index 00000000..b7fb0871 --- /dev/null +++ b/src/www/lib/pear/DB/mssql.php @@ -0,0 +1,914 @@ + + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: mssql.php,v 1.1 2005/05/28 01:55:10 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's mssql extension + * for interacting with Microsoft SQL Server databases + * + * These methods overload the ones declared in DB_common. + * + * @category Database + * @package DB + * @author Sterling Hughes + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_mssql extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'mssql'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'mssql'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'emulate', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + // XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX + var $errorcode_map = array( + 110 => DB_ERROR_VALUE_COUNT_ON_ROW, + 155 => DB_ERROR_NOSUCHFIELD, + 170 => DB_ERROR_SYNTAX, + 207 => DB_ERROR_NOSUCHFIELD, + 208 => DB_ERROR_NOSUCHTABLE, + 245 => DB_ERROR_INVALID_NUMBER, + 515 => DB_ERROR_CONSTRAINT_NOT_NULL, + 547 => DB_ERROR_CONSTRAINT, + 1913 => DB_ERROR_ALREADY_EXISTS, + 2627 => DB_ERROR_CONSTRAINT, + 2714 => DB_ERROR_ALREADY_EXISTS, + 3701 => DB_ERROR_NOSUCHTABLE, + 8134 => DB_ERROR_DIVZERO, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ + var $autocommit = true; + + /** + * The quantity of transactions begun + * + * {@internal While this is private, it can't actually be designated + * private in PHP 5 because it is directly accessed in the test suite.}} + * + * @var integer + * @access private + */ + var $transaction_opcount = 0; + + /** + * The database specified in the DSN + * + * It's a fix to allow calls to different databases in the same script. + * + * @var string + * @access private + */ + var $_db = null; + + + // }}} + // {{{ constructor + + /** + * This constructor calls $this->DB_common() + * + * @return void + */ + function DB_mssql() + { + $this->DB_common(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('mssql') && !PEAR::loadExtension('sybase') + && !PEAR::loadExtension('sybase_ct')) + { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $params = array( + $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost', + $dsn['username'] ? $dsn['username'] : null, + $dsn['password'] ? $dsn['password'] : null, + ); + if ($dsn['port']) { + $params[0] .= ((substr(PHP_OS, 0, 3) == 'WIN') ? ',' : ':') + . $dsn['port']; + } + + $connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect'; + + $this->connection = @call_user_func_array($connect_function, $params); + + if (!$this->connection) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + @mssql_get_last_message()); + } + if ($dsn['database']) { + if (!@mssql_select_db($dsn['database'], $this->connection)) { + return $this->raiseError(DB_ERROR_NODBSELECTED, + null, null, null, + @mssql_get_last_message()); + } + $this->_db = $dsn['database']; + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @mssql_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + if (!@mssql_select_db($this->_db, $this->connection)) { + return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); + } + $query = $this->modifyQuery($query); + if (!$this->autocommit && $ismanip) { + if ($this->transaction_opcount == 0) { + $result = @mssql_query('BEGIN TRAN', $this->connection); + if (!$result) { + return $this->mssqlRaiseError(); + } + } + $this->transaction_opcount++; + } + $result = @mssql_query($query, $this->connection); + if (!$result) { + return $this->mssqlRaiseError(); + } + // Determine which queries that should return data, and which + // should return an error code only. + return $ismanip ? DB_OK : $result; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal mssql result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return @mssql_next_result($result); + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum !== null) { + if (!@mssql_data_seek($result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @mssql_fetch_array($result, MSSQL_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @mssql_fetch_row($result); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return @mssql_free_result($result); + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @mssql_num_fields($result); + if (!$cols) { + return $this->mssqlRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ + function numRows($result) + { + $rows = @mssql_num_rows($result); + if ($rows === false) { + return $this->mssqlRaiseError(); + } + return $rows; + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff = false) + { + // XXX if $this->transaction_opcount > 0, we should probably + // issue a warning here. + $this->autocommit = $onoff ? true : false; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + if ($this->transaction_opcount > 0) { + if (!@mssql_select_db($this->_db, $this->connection)) { + return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); + } + $result = @mssql_query('COMMIT TRAN', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->mssqlRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + if ($this->transaction_opcount > 0) { + if (!@mssql_select_db($this->_db, $this->connection)) { + return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); + } + $result = @mssql_query('ROLLBACK TRAN', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->mssqlRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if (DB::isManip($this->last_query)) { + $res = @mssql_query('select @@rowcount', $this->connection); + if (!$res) { + return $this->mssqlRaiseError(); + } + $ar = @mssql_fetch_row($res); + if (!$ar) { + $result = 0; + } else { + @mssql_free_result($res); + $result = $ar[0]; + } + } else { + $result = 0; + } + return $result; + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_mssql::createSequence(), DB_mssql::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + if (!@mssql_select_db($this->_db, $this->connection)) { + return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); + } + $repeat = 0; + do { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)"); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result) && + ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE)) + { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $this->raiseError($result); + } + } elseif (!DB::isError($result)) { + $result =& $this->query("SELECT @@IDENTITY FROM $seqname"); + $repeat = 0; + } else { + $repeat = false; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $result = $result->fetchRow(DB_FETCHMODE_ORDERED); + return $result[0]; + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_mssql::nextID(), DB_mssql::dropSequence() + */ + function createSequence($seq_name) + { + return $this->query('CREATE TABLE ' + . $this->getSequenceName($seq_name) + . ' ([id] [int] IDENTITY (1, 1) NOT NULL,' + . ' [vapor] [int] NULL)'); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_mssql::nextID(), DB_mssql::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ quoteIdentifier() + + /** + * Quotes a string so it can be safely used as a table or column name + * + * @param string $str identifier name to be quoted + * + * @return string quoted identifier string + * + * @see DB_common::quoteIdentifier() + * @since Method available since Release 1.6.0 + */ + function quoteIdentifier($str) + { + return '[' . str_replace(']', ']]', $str) . ']'; + } + + // }}} + // {{{ mssqlRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_mssql::errorNative(), DB_mssql::errorCode() + */ + function mssqlRaiseError($code = null) + { + $message = @mssql_get_last_message(); + if (!$code) { + $code = $this->errorNative(); + } + return $this->raiseError($this->errorCode($code, $message), + null, null, null, "$code - $message"); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code produced by the last query + * + * @return int the DBMS' error code + */ + function errorNative() + { + $res = @mssql_query('select @@ERROR as ErrorCode', $this->connection); + if (!$res) { + return DB_ERROR; + } + $row = @mssql_fetch_row($res); + return $row[0]; + } + + // }}} + // {{{ errorCode() + + /** + * Determines PEAR::DB error code from mssql's native codes. + * + * If $nativecode isn't known yet, it will be looked up. + * + * @param mixed $nativecode mssql error code, if known + * @return integer an error number from a DB error constant + * @see errorNative() + */ + function errorCode($nativecode = null, $msg = '') + { + if (!$nativecode) { + $nativecode = $this->errorNative(); + } + if (isset($this->errorcode_map[$nativecode])) { + if ($nativecode == 3701 + && preg_match('/Cannot drop the index/i', $msg)) + { + return DB_ERROR_NOT_FOUND; + } + return $this->errorcode_map[$nativecode]; + } else { + return DB_ERROR; + } + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * NOTE: only supports 'table' and 'flags' if $result + * is a table name. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + if (!@mssql_select_db($this->_db, $this->connection)) { + return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); + } + $id = @mssql_query("SELECT * FROM $result WHERE 1=0", + $this->connection); + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->mssqlRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @mssql_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $res[$i] = array( + 'table' => $got_string ? $case_func($result) : '', + 'name' => $case_func(@mssql_field_name($id, $i)), + 'type' => @mssql_field_type($id, $i), + 'len' => @mssql_field_length($id, $i), + // We only support flags for table + 'flags' => $got_string + ? $this->_mssql_field_flags($result, + @mssql_field_name($id, $i)) + : '', + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @mssql_free_result($id); + } + return $res; + } + + // }}} + // {{{ _mssql_field_flags() + + /** + * Get a column's flags + * + * Supports "not_null", "primary_key", + * "auto_increment" (mssql identity), "timestamp" (mssql timestamp), + * "unique_key" (mssql unique index, unique check or primary_key) and + * "multiple_key" (multikey index) + * + * mssql timestamp is NOT similar to the mysql timestamp so this is maybe + * not useful at all - is the behaviour of mysql_field_flags that primary + * keys are alway unique? is the interpretation of multiple_key correct? + * + * @param string $table the table name + * @param string $column the field name + * + * @return string the flags + * + * @access private + * @author Joern Barthel + */ + function _mssql_field_flags($table, $column) + { + static $tableName = null; + static $flags = array(); + + if ($table != $tableName) { + + $flags = array(); + $tableName = $table; + + // get unique and primary keys + $res = $this->getAll("EXEC SP_HELPINDEX[$table]", DB_FETCHMODE_ASSOC); + + foreach ($res as $val) { + $keys = explode(', ', $val['index_keys']); + + if (sizeof($keys) > 1) { + foreach ($keys as $key) { + $this->_add_flag($flags[$key], 'multiple_key'); + } + } + + if (strpos($val['index_description'], 'primary key')) { + foreach ($keys as $key) { + $this->_add_flag($flags[$key], 'primary_key'); + } + } elseif (strpos($val['index_description'], 'unique')) { + foreach ($keys as $key) { + $this->_add_flag($flags[$key], 'unique_key'); + } + } + } + + // get auto_increment, not_null and timestamp + $res = $this->getAll("EXEC SP_COLUMNS[$table]", DB_FETCHMODE_ASSOC); + + foreach ($res as $val) { + $val = array_change_key_case($val, CASE_LOWER); + if ($val['nullable'] == '0') { + $this->_add_flag($flags[$val['column_name']], 'not_null'); + } + if (strpos($val['type_name'], 'identity')) { + $this->_add_flag($flags[$val['column_name']], 'auto_increment'); + } + if (strpos($val['type_name'], 'timestamp')) { + $this->_add_flag($flags[$val['column_name']], 'timestamp'); + } + } + } + + if (array_key_exists($column, $flags)) { + return(implode(' ', $flags[$column])); + } + return ''; + } + + // }}} + // {{{ _add_flag() + + /** + * Adds a string to the flags array if the flag is not yet in there + * - if there is no flag present the array is created + * + * @param array &$array the reference to the flag-array + * @param string $value the flag value + * + * @return void + * + * @access private + * @author Joern Barthel + */ + function _add_flag(&$array, $value) + { + if (!is_array($array)) { + $array = array($value); + } elseif (!in_array($value, $array)) { + array_push($array, $value); + } + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return "SELECT name FROM sysobjects WHERE type = 'U'" + . ' ORDER BY name'; + case 'views': + return "SELECT name FROM sysobjects WHERE type = 'V'"; + default: + return null; + } + } + + // }}} +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/DB/mysql.php b/src/www/lib/pear/DB/mysql.php new file mode 100644 index 00000000..f8c89ea5 --- /dev/null +++ b/src/www/lib/pear/DB/mysql.php @@ -0,0 +1,1034 @@ + + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: mysql.php,v 1.1 2005/05/28 01:55:10 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's mysql extension + * for interacting with MySQL databases + * + * These methods overload the ones declared in DB_common. + * + * @category Database + * @package DB + * @author Stig Bakken + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_mysql extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'mysql'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'mysql'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'alter', + 'new_link' => '4.2.0', + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + 1004 => DB_ERROR_CANNOT_CREATE, + 1005 => DB_ERROR_CANNOT_CREATE, + 1006 => DB_ERROR_CANNOT_CREATE, + 1007 => DB_ERROR_ALREADY_EXISTS, + 1008 => DB_ERROR_CANNOT_DROP, + 1022 => DB_ERROR_ALREADY_EXISTS, + 1044 => DB_ERROR_ACCESS_VIOLATION, + 1046 => DB_ERROR_NODBSELECTED, + 1048 => DB_ERROR_CONSTRAINT, + 1049 => DB_ERROR_NOSUCHDB, + 1050 => DB_ERROR_ALREADY_EXISTS, + 1051 => DB_ERROR_NOSUCHTABLE, + 1054 => DB_ERROR_NOSUCHFIELD, + 1061 => DB_ERROR_ALREADY_EXISTS, + 1062 => DB_ERROR_ALREADY_EXISTS, + 1064 => DB_ERROR_SYNTAX, + 1091 => DB_ERROR_NOT_FOUND, + 1100 => DB_ERROR_NOT_LOCKED, + 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, + 1142 => DB_ERROR_ACCESS_VIOLATION, + 1146 => DB_ERROR_NOSUCHTABLE, + 1216 => DB_ERROR_CONSTRAINT, + 1217 => DB_ERROR_CONSTRAINT, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ + var $autocommit = true; + + /** + * The quantity of transactions begun + * + * {@internal While this is private, it can't actually be designated + * private in PHP 5 because it is directly accessed in the test suite.}} + * + * @var integer + * @access private + */ + var $transaction_opcount = 0; + + /** + * The database specified in the DSN + * + * It's a fix to allow calls to different databases in the same script. + * + * @var string + * @access private + */ + var $_db = ''; + + + // }}} + // {{{ constructor + + /** + * This constructor calls $this->DB_common() + * + * @return void + */ + function DB_mysql() + { + $this->DB_common(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's mysql driver supports the following extra DSN options: + * + new_link If set to true, causes subsequent calls to connect() + * to return a new connection link instead of the + * existing one. WARNING: this is not portable to + * other DBMS's. Available since PEAR DB 1.7.0. + * + client_flags Any combination of MYSQL_CLIENT_* constants. + * Only used if PHP is at version 4.3.0 or greater. + * Available since PEAR DB 1.7.0. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('mysql')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $params = array(); + if ($dsn['protocol'] && $dsn['protocol'] == 'unix') { + $params[0] = ':' . $dsn['socket']; + } else { + $params[0] = $dsn['hostspec'] ? $dsn['hostspec'] + : 'localhost'; + if ($dsn['port']) { + $params[0] .= ':' . $dsn['port']; + } + } + $params[] = $dsn['username'] ? $dsn['username'] : null; + $params[] = $dsn['password'] ? $dsn['password'] : null; + + if (!$persistent) { + if (isset($dsn['new_link']) + && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) + { + $params[] = true; + } else { + $params[] = false; + } + } + if (version_compare(phpversion(), '4.3.0', '>=')) { + $params[] = isset($dsn['client_flags']) + ? $dsn['client_flags'] : null; + } + + $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect'; + + $ini = ini_get('track_errors'); + $php_errormsg = ''; + if ($ini) { + $this->connection = @call_user_func_array($connect_function, + $params); + } else { + ini_set('track_errors', 1); + $this->connection = @call_user_func_array($connect_function, + $params); + ini_set('track_errors', $ini); + } + + if (!$this->connection) { + if (($err = @mysql_error()) != '') { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $err); + } else { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); + } + } + + if ($dsn['database']) { + if (!@mysql_select_db($dsn['database'], $this->connection)) { + return $this->mysqlRaiseError(); + } + $this->_db = $dsn['database']; + } + + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @mysql_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * Generally uses mysql_query(). If you want to use + * mysql_unbuffered_query() set the "result_buffering" option to 0 using + * setOptions(). This option was added in Release 1.7.0. + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + $query = $this->modifyQuery($query); + if ($this->_db) { + if (!@mysql_select_db($this->_db, $this->connection)) { + return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); + } + } + if (!$this->autocommit && $ismanip) { + if ($this->transaction_opcount == 0) { + $result = @mysql_query('SET AUTOCOMMIT=0', $this->connection); + $result = @mysql_query('BEGIN', $this->connection); + if (!$result) { + return $this->mysqlRaiseError(); + } + } + $this->transaction_opcount++; + } + if (!$this->options['result_buffering']) { + $result = @mysql_unbuffered_query($query, $this->connection); + } else { + $result = @mysql_query($query, $this->connection); + } + if (!$result) { + return $this->mysqlRaiseError(); + } + if (is_resource($result)) { + return $result; + } + return DB_OK; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal mysql result pointer to the next available result + * + * This method has not been implemented yet. + * + * @param a valid sql result resource + * + * @return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum !== null) { + if (!@mysql_data_seek($result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @mysql_fetch_array($result, MYSQL_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @mysql_fetch_row($result); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + /* + * Even though this DBMS already trims output, we do this because + * a field might have intentional whitespace at the end that + * gets removed by DB_PORTABILITY_RTRIM under another driver. + */ + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return @mysql_free_result($result); + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @mysql_num_fields($result); + if (!$cols) { + return $this->mysqlRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ + function numRows($result) + { + $rows = @mysql_num_rows($result); + if ($rows === null) { + return $this->mysqlRaiseError(); + } + return $rows; + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff = false) + { + // XXX if $this->transaction_opcount > 0, we should probably + // issue a warning here. + $this->autocommit = $onoff ? true : false; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + if ($this->transaction_opcount > 0) { + if ($this->_db) { + if (!@mysql_select_db($this->_db, $this->connection)) { + return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); + } + } + $result = @mysql_query('COMMIT', $this->connection); + $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->mysqlRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + if ($this->transaction_opcount > 0) { + if ($this->_db) { + if (!@mysql_select_db($this->_db, $this->connection)) { + return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); + } + } + $result = @mysql_query('ROLLBACK', $this->connection); + $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->mysqlRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if (DB::isManip($this->last_query)) { + return @mysql_affected_rows($this->connection); + } else { + return 0; + } + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_mysql::createSequence(), DB_mysql::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + do { + $repeat = 0; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("UPDATE ${seqname} ". + 'SET id=LAST_INSERT_ID(id+1)'); + $this->popErrorHandling(); + if ($result === DB_OK) { + // COMMON CASE + $id = @mysql_insert_id($this->connection); + if ($id != 0) { + return $id; + } + // EMPTY SEQ TABLE + // Sequence table must be empty for some reason, so fill + // it and return 1 and obtain a user-level lock + $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); + if (DB::isError($result)) { + return $this->raiseError($result); + } + if ($result == 0) { + // Failed to get the lock + return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED); + } + + // add the default value + $result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)"); + if (DB::isError($result)) { + return $this->raiseError($result); + } + + // Release the lock + $result = $this->getOne('SELECT RELEASE_LOCK(' + . "'${seqname}_lock')"); + if (DB::isError($result)) { + return $this->raiseError($result); + } + // We know what the result will be, so no need to try again + return 1; + + } elseif ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) + { + // ONDEMAND TABLE CREATION + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $this->raiseError($result); + } else { + $repeat = 1; + } + + } elseif (DB::isError($result) && + $result->getCode() == DB_ERROR_ALREADY_EXISTS) + { + // BACKWARDS COMPAT + // see _BCsequence() comment + $result = $this->_BCsequence($seqname); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $repeat = 1; + } + } while ($repeat); + + return $this->raiseError($result); + } + + // }}} + // {{{ createSequence() + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_mysql::nextID(), DB_mysql::dropSequence() + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + $res = $this->query('CREATE TABLE ' . $seqname + . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,' + . ' PRIMARY KEY(id))'); + if (DB::isError($res)) { + return $res; + } + // insert yields value 1, nextId call will generate ID 2 + $res = $this->query("INSERT INTO ${seqname} (id) VALUES (0)"); + if (DB::isError($res)) { + return $res; + } + // so reset to zero + return $this->query("UPDATE ${seqname} SET id = 0"); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_mysql::nextID(), DB_mysql::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ _BCsequence() + + /** + * Backwards compatibility with old sequence emulation implementation + * (clean up the dupes) + * + * @param string $seqname the sequence name to clean up + * + * @return bool true on success. A DB_Error object on failure. + * + * @access private + */ + function _BCsequence($seqname) + { + // Obtain a user-level lock... this will release any previous + // application locks, but unlike LOCK TABLES, it does not abort + // the current transaction and is much less frequently used. + $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); + if (DB::isError($result)) { + return $result; + } + if ($result == 0) { + // Failed to get the lock, can't do the conversion, bail + // with a DB_ERROR_NOT_LOCKED error + return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED); + } + + $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}"); + if (DB::isError($highest_id)) { + return $highest_id; + } + // This should kill all rows except the highest + // We should probably do something if $highest_id isn't + // numeric, but I'm at a loss as how to handle that... + $result = $this->query('DELETE FROM ' . $seqname + . " WHERE id <> $highest_id"); + if (DB::isError($result)) { + return $result; + } + + // If another thread has been waiting for this lock, + // it will go thru the above procedure, but will have no + // real effect + $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')"); + if (DB::isError($result)) { + return $result; + } + return true; + } + + // }}} + // {{{ quoteIdentifier() + + /** + * Quotes a string so it can be safely used as a table or column name + * + * MySQL can't handle the backtick character (`) in + * table or column names. + * + * @param string $str identifier name to be quoted + * + * @return string quoted identifier string + * + * @see DB_common::quoteIdentifier() + * @since Method available since Release 1.6.0 + */ + function quoteIdentifier($str) + { + return '`' . $str . '`'; + } + + // }}} + // {{{ quote() + + /** + * @deprecated Deprecated in release 1.6.0 + */ + function quote($str) + { + return $this->quoteSmart($str); + } + + // }}} + // {{{ escapeSimple() + + /** + * Escapes a string according to the current DBMS's standards + * + * @param string $str the string to be escaped + * + * @return string the escaped string + * + * @see DB_common::quoteSmart() + * @since Method available since Release 1.6.0 + */ + function escapeSimple($str) + { + if (function_exists('mysql_real_escape_string')) { + return @mysql_real_escape_string($str, $this->connection); + } else { + return @mysql_escape_string($str); + } + } + + // }}} + // {{{ modifyQuery() + + /** + * Changes a query string for various DBMS specific reasons + * + * This little hack lets you know how many rows were deleted + * when running a "DELETE FROM table" query. Only implemented + * if the DB_PORTABILITY_DELETE_COUNT portability option is on. + * + * @param string $query the query string to modify + * + * @return string the modified query string + * + * @access protected + * @see DB_common::setOption() + */ + function modifyQuery($query) + { + if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) { + // "DELETE FROM table" gives 0 affected rows in MySQL. + // This little hack lets you know how many rows were deleted. + if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { + $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', + 'DELETE FROM \1 WHERE 1=1', $query); + } + } + return $query; + } + + // }}} + // {{{ modifyLimitQuery() + + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ + function modifyLimitQuery($query, $from, $count, $params = array()) + { + if (DB::isManip($query)) { + return $query . " LIMIT $count"; + } else { + return $query . " LIMIT $from, $count"; + } + } + + // }}} + // {{{ mysqlRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_mysql::errorNative(), DB_common::errorCode() + */ + function mysqlRaiseError($errno = null) + { + if ($errno === null) { + if ($this->options['portability'] & DB_PORTABILITY_ERRORS) { + $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT; + $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL; + $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT; + } else { + // Doing this in case mode changes during runtime. + $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS; + $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT; + $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS; + } + $errno = $this->errorCode(mysql_errno($this->connection)); + } + return $this->raiseError($errno, null, null, null, + @mysql_errno($this->connection) . ' ** ' . + @mysql_error($this->connection)); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code produced by the last query + * + * @return int the DBMS' error code + */ + function errorNative() + { + return @mysql_errno($this->connection); + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @mysql_list_fields($this->dsn['database'], + $result, $this->connection); + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @mysql_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $res[$i] = array( + 'table' => $case_func(@mysql_field_table($id, $i)), + 'name' => $case_func(@mysql_field_name($id, $i)), + 'type' => @mysql_field_type($id, $i), + 'len' => @mysql_field_len($id, $i), + 'flags' => @mysql_field_flags($id, $i), + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @mysql_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SHOW TABLES'; + case 'users': + return 'SELECT DISTINCT User FROM mysql.user'; + case 'databases': + return 'SHOW DATABASES'; + default: + return null; + } + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/DB/mysqli.php b/src/www/lib/pear/DB/mysqli.php new file mode 100644 index 00000000..e1e96ecc --- /dev/null +++ b/src/www/lib/pear/DB/mysqli.php @@ -0,0 +1,1076 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: mysqli.php,v 1.1 2005/05/28 01:55:11 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's mysqli extension + * for interacting with MySQL databases + * + * This is for MySQL versions 4.1 and above. Requires PHP 5. + * + * Note that persistent connections no longer exist. + * + * These methods overload the ones declared in DB_common. + * + * @category Database + * @package DB + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + * @since Class functional since Release 1.6.3 + */ +class DB_mysqli extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'mysqli'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'mysqli'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'alter', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => false, + 'prepare' => false, + 'ssl' => true, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + 1004 => DB_ERROR_CANNOT_CREATE, + 1005 => DB_ERROR_CANNOT_CREATE, + 1006 => DB_ERROR_CANNOT_CREATE, + 1007 => DB_ERROR_ALREADY_EXISTS, + 1008 => DB_ERROR_CANNOT_DROP, + 1022 => DB_ERROR_ALREADY_EXISTS, + 1044 => DB_ERROR_ACCESS_VIOLATION, + 1046 => DB_ERROR_NODBSELECTED, + 1048 => DB_ERROR_CONSTRAINT, + 1049 => DB_ERROR_NOSUCHDB, + 1050 => DB_ERROR_ALREADY_EXISTS, + 1051 => DB_ERROR_NOSUCHTABLE, + 1054 => DB_ERROR_NOSUCHFIELD, + 1061 => DB_ERROR_ALREADY_EXISTS, + 1062 => DB_ERROR_ALREADY_EXISTS, + 1064 => DB_ERROR_SYNTAX, + 1091 => DB_ERROR_NOT_FOUND, + 1100 => DB_ERROR_NOT_LOCKED, + 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, + 1142 => DB_ERROR_ACCESS_VIOLATION, + 1146 => DB_ERROR_NOSUCHTABLE, + 1216 => DB_ERROR_CONSTRAINT, + 1217 => DB_ERROR_CONSTRAINT, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ + var $autocommit = true; + + /** + * The quantity of transactions begun + * + * {@internal While this is private, it can't actually be designated + * private in PHP 5 because it is directly accessed in the test suite.}} + * + * @var integer + * @access private + */ + var $transaction_opcount = 0; + + /** + * The database specified in the DSN + * + * It's a fix to allow calls to different databases in the same script. + * + * @var string + * @access private + */ + var $_db = ''; + + /** + * Array for converting MYSQLI_*_FLAG constants to text values + * @var array + * @access public + * @since Property available since Release 1.6.5 + */ + var $mysqli_flags = array( + MYSQLI_NOT_NULL_FLAG => 'not_null', + MYSQLI_PRI_KEY_FLAG => 'primary_key', + MYSQLI_UNIQUE_KEY_FLAG => 'unique_key', + MYSQLI_MULTIPLE_KEY_FLAG => 'multiple_key', + MYSQLI_BLOB_FLAG => 'blob', + MYSQLI_UNSIGNED_FLAG => 'unsigned', + MYSQLI_ZEROFILL_FLAG => 'zerofill', + MYSQLI_AUTO_INCREMENT_FLAG => 'auto_increment', + MYSQLI_TIMESTAMP_FLAG => 'timestamp', + MYSQLI_SET_FLAG => 'set', + // MYSQLI_NUM_FLAG => 'numeric', // unnecessary + // MYSQLI_PART_KEY_FLAG => 'multiple_key', // duplicatvie + MYSQLI_GROUP_FLAG => 'group_by' + ); + + /** + * Array for converting MYSQLI_TYPE_* constants to text values + * @var array + * @access public + * @since Property available since Release 1.6.5 + */ + var $mysqli_types = array( + MYSQLI_TYPE_DECIMAL => 'decimal', + MYSQLI_TYPE_TINY => 'tinyint', + MYSQLI_TYPE_SHORT => 'int', + MYSQLI_TYPE_LONG => 'int', + MYSQLI_TYPE_FLOAT => 'float', + MYSQLI_TYPE_DOUBLE => 'double', + // MYSQLI_TYPE_NULL => 'DEFAULT NULL', // let flags handle it + MYSQLI_TYPE_TIMESTAMP => 'timestamp', + MYSQLI_TYPE_LONGLONG => 'bigint', + MYSQLI_TYPE_INT24 => 'mediumint', + MYSQLI_TYPE_DATE => 'date', + MYSQLI_TYPE_TIME => 'time', + MYSQLI_TYPE_DATETIME => 'datetime', + MYSQLI_TYPE_YEAR => 'year', + MYSQLI_TYPE_NEWDATE => 'date', + MYSQLI_TYPE_ENUM => 'enum', + MYSQLI_TYPE_SET => 'set', + MYSQLI_TYPE_TINY_BLOB => 'tinyblob', + MYSQLI_TYPE_MEDIUM_BLOB => 'mediumblob', + MYSQLI_TYPE_LONG_BLOB => 'longblob', + MYSQLI_TYPE_BLOB => 'blob', + MYSQLI_TYPE_VAR_STRING => 'varchar', + MYSQLI_TYPE_STRING => 'char', + MYSQLI_TYPE_GEOMETRY => 'geometry', + ); + + + // }}} + // {{{ constructor + + /** + * This constructor calls $this->DB_common() + * + * @return void + */ + function DB_mysqli() + { + $this->DB_common(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's mysqli driver supports the following extra DSN options: + * + When the 'ssl' $option passed to DB::connect() is true: + * + key The path to the key file. + * + cert The path to the certificate file. + * + ca The path to the certificate authority file. + * + capath The path to a directory that contains trusted SSL + * CA certificates in pem format. + * + cipher The list of allowable ciphers for SSL encryption. + * + * Example of how to connect using SSL: + * + * require_once 'DB.php'; + * + * $dsn = array( + * 'phptype' => 'mysqli', + * 'username' => 'someuser', + * 'password' => 'apasswd', + * 'hostspec' => 'localhost', + * 'database' => 'thedb', + * 'key' => 'client-key.pem', + * 'cert' => 'client-cert.pem', + * 'ca' => 'cacert.pem', + * 'capath' => '/path/to/ca/dir', + * 'cipher' => 'AES', + * ); + * + * $options = array( + * 'ssl' => true, + * ); + * + * $db =& DB::connect($dsn, $options); + * if (PEAR::isError($db)) { + * die($db->getMessage()); + * } + * + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('mysqli')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $ini = ini_get('track_errors'); + ini_set('track_errors', 1); + $php_errormsg = ''; + + if ($this->getOption('ssl') === true) { + $init = mysqli_init(); + mysqli_ssl_set( + $init, + empty($dsn['key']) ? null : $dsn['key'], + empty($dsn['cert']) ? null : $dsn['cert'], + empty($dsn['ca']) ? null : $dsn['ca'], + empty($dsn['capath']) ? null : $dsn['capath'], + empty($dsn['cipher']) ? null : $dsn['cipher'] + ); + if ($this->connection = @mysqli_real_connect( + $init, + $dsn['hostspec'], + $dsn['username'], + $dsn['password'], + $dsn['database'], + $dsn['port'], + $dsn['socket'])) + { + $this->connection = $init; + } + } else { + $this->connection = @mysqli_connect( + $dsn['hostspec'], + $dsn['username'], + $dsn['password'], + $dsn['database'], + $dsn['port'], + $dsn['socket'] + ); + } + + ini_set('track_errors', $ini); + + if (!$this->connection) { + if (($err = @mysqli_connect_error()) != '') { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $err); + } else { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); + } + } + + if ($dsn['database']) { + $this->_db = $dsn['database']; + } + + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @mysqli_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + $query = $this->modifyQuery($query); + if ($this->_db) { + if (!@mysqli_select_db($this->connection, $this->_db)) { + return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); + } + } + if (!$this->autocommit && $ismanip) { + if ($this->transaction_opcount == 0) { + $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=0'); + $result = @mysqli_query($this->connection, 'BEGIN'); + if (!$result) { + return $this->mysqliRaiseError(); + } + } + $this->transaction_opcount++; + } + $result = @mysqli_query($this->connection, $query); + if (!$result) { + return $this->mysqliRaiseError(); + } + if (is_object($result)) { + return $result; + } + return DB_OK; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal mysql result pointer to the next available result. + * + * This method has not been implemented yet. + * + * @param resource $result a valid sql result resource + * @return false + * @access public + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum !== null) { + if (!@mysqli_data_seek($result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @mysqli_fetch_array($result, MYSQLI_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @mysqli_fetch_row($result); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + /* + * Even though this DBMS already trims output, we do this because + * a field might have intentional whitespace at the end that + * gets removed by DB_PORTABILITY_RTRIM under another driver. + */ + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return @mysqli_free_result($result); + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @mysqli_num_fields($result); + if (!$cols) { + return $this->mysqliRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ + function numRows($result) + { + $rows = @mysqli_num_rows($result); + if ($rows === null) { + return $this->mysqliRaiseError(); + } + return $rows; + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff = false) + { + // XXX if $this->transaction_opcount > 0, we should probably + // issue a warning here. + $this->autocommit = $onoff ? true : false; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + if ($this->transaction_opcount > 0) { + if ($this->_db) { + if (!@mysqli_select_db($this->connection, $this->_db)) { + return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); + } + } + $result = @mysqli_query($this->connection, 'COMMIT'); + $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1'); + $this->transaction_opcount = 0; + if (!$result) { + return $this->mysqliRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + if ($this->transaction_opcount > 0) { + if ($this->_db) { + if (!@mysqli_select_db($this->connection, $this->_db)) { + return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); + } + } + $result = @mysqli_query($this->connection, 'ROLLBACK'); + $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1'); + $this->transaction_opcount = 0; + if (!$result) { + return $this->mysqliRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if (DB::isManip($this->last_query)) { + return @mysqli_affected_rows($this->connection); + } else { + return 0; + } + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_mysqli::createSequence(), DB_mysqli::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + do { + $repeat = 0; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query('UPDATE ' . $seqname + . ' SET id = LAST_INSERT_ID(id + 1)'); + $this->popErrorHandling(); + if ($result === DB_OK) { + // COMMON CASE + $id = @mysqli_insert_id($this->connection); + if ($id != 0) { + return $id; + } + + // EMPTY SEQ TABLE + // Sequence table must be empty for some reason, + // so fill it and return 1 + // Obtain a user-level lock + $result = $this->getOne('SELECT GET_LOCK(' + . "'${seqname}_lock', 10)"); + if (DB::isError($result)) { + return $this->raiseError($result); + } + if ($result == 0) { + return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED); + } + + // add the default value + $result = $this->query('REPLACE INTO ' . $seqname + . ' (id) VALUES (0)'); + if (DB::isError($result)) { + return $this->raiseError($result); + } + + // Release the lock + $result = $this->getOne('SELECT RELEASE_LOCK(' + . "'${seqname}_lock')"); + if (DB::isError($result)) { + return $this->raiseError($result); + } + // We know what the result will be, so no need to try again + return 1; + + } elseif ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) + { + // ONDEMAND TABLE CREATION + $result = $this->createSequence($seq_name); + + // Since createSequence initializes the ID to be 1, + // we do not need to retrieve the ID again (or we will get 2) + if (DB::isError($result)) { + return $this->raiseError($result); + } else { + // First ID of a newly created sequence is 1 + return 1; + } + + } elseif (DB::isError($result) && + $result->getCode() == DB_ERROR_ALREADY_EXISTS) + { + // BACKWARDS COMPAT + // see _BCsequence() comment + $result = $this->_BCsequence($seqname); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $repeat = 1; + } + } while ($repeat); + + return $this->raiseError($result); + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_mysqli::nextID(), DB_mysqli::dropSequence() + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + $res = $this->query('CREATE TABLE ' . $seqname + . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,' + . ' PRIMARY KEY(id))'); + if (DB::isError($res)) { + return $res; + } + // insert yields value 1, nextId call will generate ID 2 + return $this->query("INSERT INTO ${seqname} (id) VALUES (0)"); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_mysql::nextID(), DB_mysql::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ _BCsequence() + + /** + * Backwards compatibility with old sequence emulation implementation + * (clean up the dupes) + * + * @param string $seqname the sequence name to clean up + * + * @return bool true on success. A DB_Error object on failure. + * + * @access private + */ + function _BCsequence($seqname) + { + // Obtain a user-level lock... this will release any previous + // application locks, but unlike LOCK TABLES, it does not abort + // the current transaction and is much less frequently used. + $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); + if (DB::isError($result)) { + return $result; + } + if ($result == 0) { + // Failed to get the lock, can't do the conversion, bail + // with a DB_ERROR_NOT_LOCKED error + return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED); + } + + $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}"); + if (DB::isError($highest_id)) { + return $highest_id; + } + + // This should kill all rows except the highest + // We should probably do something if $highest_id isn't + // numeric, but I'm at a loss as how to handle that... + $result = $this->query('DELETE FROM ' . $seqname + . " WHERE id <> $highest_id"); + if (DB::isError($result)) { + return $result; + } + + // If another thread has been waiting for this lock, + // it will go thru the above procedure, but will have no + // real effect + $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')"); + if (DB::isError($result)) { + return $result; + } + return true; + } + + // }}} + // {{{ quoteIdentifier() + + /** + * Quotes a string so it can be safely used as a table or column name + * + * MySQL can't handle the backtick character (`) in + * table or column names. + * + * @param string $str identifier name to be quoted + * + * @return string quoted identifier string + * + * @see DB_common::quoteIdentifier() + * @since Method available since Release 1.6.0 + */ + function quoteIdentifier($str) + { + return '`' . $str . '`'; + } + + // }}} + // {{{ escapeSimple() + + /** + * Escapes a string according to the current DBMS's standards + * + * @param string $str the string to be escaped + * + * @return string the escaped string + * + * @see DB_common::quoteSmart() + * @since Method available since Release 1.6.0 + */ + function escapeSimple($str) + { + return @mysqli_real_escape_string($this->connection, $str); + } + + // }}} + // {{{ modifyLimitQuery() + + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ + function modifyLimitQuery($query, $from, $count, $params = array()) + { + if (DB::isManip($query)) { + return $query . " LIMIT $count"; + } else { + return $query . " LIMIT $from, $count"; + } + } + + // }}} + // {{{ mysqliRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_mysqli::errorNative(), DB_common::errorCode() + */ + function mysqliRaiseError($errno = null) + { + if ($errno === null) { + if ($this->options['portability'] & DB_PORTABILITY_ERRORS) { + $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT; + $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL; + $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT; + } else { + // Doing this in case mode changes during runtime. + $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS; + $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT; + $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS; + } + $errno = $this->errorCode(mysqli_errno($this->connection)); + } + return $this->raiseError($errno, null, null, null, + @mysqli_errno($this->connection) . ' ** ' . + @mysqli_error($this->connection)); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code produced by the last query + * + * @return int the DBMS' error code + */ + function errorNative() + { + return @mysqli_errno($this->connection); + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::setOption() + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @mysqli_query($this->connection, + "SELECT * FROM $result LIMIT 0"); + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_a($id, 'mysqli_result')) { + return $this->mysqliRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @mysqli_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $tmp = @mysqli_fetch_field($id); + + $flags = ''; + foreach ($this->mysqli_flags as $const => $means) { + if ($tmp->flags & $const) { + $flags .= $means . ' '; + } + } + if ($tmp->def) { + $flags .= 'default_' . rawurlencode($tmp->def); + } + $flags = trim($flags); + + $res[$i] = array( + 'table' => $case_func($tmp->table), + 'name' => $case_func($tmp->name), + 'type' => isset($this->mysqli_types[$tmp->type]) + ? $this->mysqli_types[$tmp->type] + : 'unknown', + 'len' => $tmp->max_length, + 'flags' => $flags, + ); + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @mysqli_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SHOW TABLES'; + case 'users': + return 'SELECT DISTINCT User FROM mysql.user'; + case 'databases': + return 'SHOW DATABASES'; + default: + return null; + } + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/DB/oci8.php b/src/www/lib/pear/DB/oci8.php new file mode 100644 index 00000000..b3638964 --- /dev/null +++ b/src/www/lib/pear/DB/oci8.php @@ -0,0 +1,1117 @@ + + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: oci8.php,v 1.1 2005/05/28 01:55:11 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's oci8 extension + * for interacting with Oracle databases + * + * Definitely works with versions 8 and 9 of Oracle. + * + * These methods overload the ones declared in DB_common. + * + * Be aware... OCIError() only appears to return anything when given a + * statement, so functions return the generic DB_ERROR instead of more + * useful errors that have to do with feedback from the database. + * + * @category Database + * @package DB + * @author James L. Pine + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_oci8 extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'oci8'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'oci8'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'alter', + 'new_link' => '5.0.0', + 'numrows' => 'subquery', + 'pconnect' => true, + 'prepare' => true, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + 1 => DB_ERROR_CONSTRAINT, + 900 => DB_ERROR_SYNTAX, + 904 => DB_ERROR_NOSUCHFIELD, + 913 => DB_ERROR_VALUE_COUNT_ON_ROW, + 921 => DB_ERROR_SYNTAX, + 923 => DB_ERROR_SYNTAX, + 942 => DB_ERROR_NOSUCHTABLE, + 955 => DB_ERROR_ALREADY_EXISTS, + 1400 => DB_ERROR_CONSTRAINT_NOT_NULL, + 1401 => DB_ERROR_INVALID, + 1407 => DB_ERROR_CONSTRAINT_NOT_NULL, + 1418 => DB_ERROR_NOT_FOUND, + 1476 => DB_ERROR_DIVZERO, + 1722 => DB_ERROR_INVALID_NUMBER, + 2289 => DB_ERROR_NOSUCHTABLE, + 2291 => DB_ERROR_CONSTRAINT, + 2292 => DB_ERROR_CONSTRAINT, + 2449 => DB_ERROR_CONSTRAINT, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ + var $autocommit = true; + + /** + * Stores the $data passed to execute() in the oci8 driver + * + * Gets reset to array() when simpleQuery() is run. + * + * Needed in case user wants to call numRows() after prepare/execute + * was used. + * + * @var array + * @access private + */ + var $_data = array(); + + /** + * The result or statement handle from the most recently executed query + * @var resource + */ + var $last_stmt; + + /** + * Is the given prepared statement a data manipulation query? + * @var array + * @access private + */ + var $manip_query = array(); + + + // }}} + // {{{ constructor + + /** + * This constructor calls $this->DB_common() + * + * @return void + */ + function DB_oci8() + { + $this->DB_common(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * If PHP is at version 5.0.0 or greater: + * + Generally, oci_connect() or oci_pconnect() are used. + * + But if the new_link DSN option is set to true, oci_new_connect() + * is used. + * + * When using PHP version 4.x, OCILogon() or OCIPLogon() are used. + * + * PEAR DB's oci8 driver supports the following extra DSN options: + * + charset The character set to be used on the connection. + * Only used if PHP is at version 5.0.0 or greater + * and the Oracle server is at 9.2 or greater. + * Available since PEAR DB 1.7.0. + * + new_link If set to true, causes subsequent calls to + * connect() to return a new connection link + * instead of the existing one. WARNING: this is + * not portable to other DBMS's. + * Available since PEAR DB 1.7.0. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('oci8')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + if (function_exists('oci_connect')) { + if (isset($dsn['new_link']) + && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) + { + $connect_function = 'oci_new_connect'; + } else { + $connect_function = $persistent ? 'oci_pconnect' + : 'oci_connect'; + } + + // Backwards compatibility with DB < 1.7.0 + if (empty($dsn['database']) && !empty($dsn['hostspec'])) { + $db = $dsn['hostspec']; + } else { + $db = $dsn['database']; + } + + $char = empty($dsn['charset']) ? null : $dsn['charset']; + $this->connection = @$connect_function($dsn['username'], + $dsn['password'], + $db, + $char); + $error = OCIError(); + if (!empty($error) && $error['code'] == 12541) { + // Couldn't find TNS listener. Try direct connection. + $this->connection = @$connect_function($dsn['username'], + $dsn['password'], + null, + $char); + } + } else { + $connect_function = $persistent ? 'OCIPLogon' : 'OCILogon'; + if ($dsn['hostspec']) { + $this->connection = @$connect_function($dsn['username'], + $dsn['password'], + $dsn['hostspec']); + } elseif ($dsn['username'] || $dsn['password']) { + $this->connection = @$connect_function($dsn['username'], + $dsn['password']); + } + } + + if (!$this->connection) { + $error = OCIError(); + $error = (is_array($error)) ? $error['message'] : null; + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $error); + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + if (function_exists('oci_close')) { + $ret = @oci_close($this->connection); + } else { + $ret = @OCILogOff($this->connection); + } + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * To determine how many rows of a result set get buffered using + * ocisetprefetch(), see the "result_buffering" option in setOptions(). + * This option was added in Release 1.7.0. + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $this->_data = array(); + $this->last_parameters = array(); + $this->last_query = $query; + $query = $this->modifyQuery($query); + $result = @OCIParse($this->connection, $query); + if (!$result) { + return $this->oci8RaiseError(); + } + if ($this->autocommit) { + $success = @OCIExecute($result,OCI_COMMIT_ON_SUCCESS); + } else { + $success = @OCIExecute($result,OCI_DEFAULT); + } + if (!$success) { + return $this->oci8RaiseError($result); + } + $this->last_stmt = $result; + if (DB::isManip($query)) { + return DB_OK; + } else { + @ocisetprefetch($result, $this->options['result_buffering']); + return $result; + } + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal oracle result pointer to the next available result + * + * @param a valid oci8 result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum !== null) { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $moredata = @OCIFetchInto($result,$arr,OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && + $moredata) + { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $moredata = OCIFetchInto($result,$arr,OCI_RETURN_NULLS+OCI_RETURN_LOBS); + } + if (!$moredata) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return @OCIFreeStatement($result); + } + + /** + * Frees the internal resources associated with a prepared query + * + * @param resource $stmt the prepared statement's resource + * @param bool $free_resource should the PHP resource be freed too? + * Use false if you need to get data + * from the result set later. + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_oci8::prepare() + */ + function freePrepared($stmt, $free_resource = true) + { + if (!is_resource($stmt)) { + return false; + } + if ($free_resource) { + @ocifreestatement($stmt); + } + if (isset($this->prepare_types[(int)$stmt])) { + unset($this->prepare_types[(int)$stmt]); + unset($this->manip_query[(int)$stmt]); + } else { + return false; + } + return true; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * Only works if the DB_PORTABILITY_NUMROWS portability option + * is turned on. + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows(), DB_common::setOption() + */ + function numRows($result) + { + // emulate numRows for Oracle. yuck. + if ($this->options['portability'] & DB_PORTABILITY_NUMROWS && + $result === $this->last_stmt) + { + $countquery = 'SELECT COUNT(*) FROM ('.$this->last_query.')'; + $save_query = $this->last_query; + $save_stmt = $this->last_stmt; + + if (count($this->_data)) { + $smt = $this->prepare('SELECT COUNT(*) FROM ('.$this->last_query.')'); + $count = $this->execute($smt, $this->_data); + } else { + $count =& $this->query($countquery); + } + + if (DB::isError($count) || + DB::isError($row = $count->fetchRow(DB_FETCHMODE_ORDERED))) + { + $this->last_query = $save_query; + $this->last_stmt = $save_stmt; + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + return $row[0]; + } + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @OCINumCols($result); + if (!$cols) { + return $this->oci8RaiseError($result); + } + return $cols; + } + + // }}} + // {{{ prepare() + + /** + * Prepares a query for multiple execution with execute(). + * + * With oci8, this is emulated. + * + * prepare() requires a generic query as string like + * INSERT INTO numbers VALUES (?, ?, ?) + * . The ? characters are placeholders. + * + * Three types of placeholders can be used: + * + ? a quoted scalar value, i.e. strings, integers + * + ! value is inserted 'as is' + * + & requires a file name. The file's contents get + * inserted into the query (i.e. saving binary + * data in a db) + * + * Use backslashes to escape placeholder characters if you don't want + * them to be interpreted as placeholders. Example: + * "UPDATE foo SET col=? WHERE col='over \& under'" + * + * + * @param string $query the query to be prepared + * + * @return mixed DB statement resource on success. DB_Error on failure. + * + * @see DB_oci8::execute() + */ + function prepare($query) + { + $tokens = preg_split('/((? $val) { + switch ($val) { + case '?': + $types[$token++] = DB_PARAM_SCALAR; + unset($tokens[$key]); + break; + case '&': + $types[$token++] = DB_PARAM_OPAQUE; + unset($tokens[$key]); + break; + case '!': + $types[$token++] = DB_PARAM_MISC; + unset($tokens[$key]); + break; + default: + $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val); + if ($key != $binds) { + $newquery .= $tokens[$key] . ':bind' . $token; + } else { + $newquery .= $tokens[$key]; + } + } + } + + $this->last_query = $query; + $newquery = $this->modifyQuery($newquery); + if (!$stmt = @OCIParse($this->connection, $newquery)) { + return $this->oci8RaiseError(); + } + $this->prepare_types[(int)$stmt] = $types; + $this->manip_query[(int)$stmt] = DB::isManip($query); + return $stmt; + } + + // }}} + // {{{ execute() + + /** + * Executes a DB statement prepared with prepare(). + * + * To determine how many rows of a result set get buffered using + * ocisetprefetch(), see the "result_buffering" option in setOptions(). + * This option was added in Release 1.7.0. + * + * @param resource $stmt a DB statement resource returned from prepare() + * @param mixed $data array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 for non-array items or the + * quantity of elements in the array. + * + * @return mixed returns an oic8 result resource for successful SELECT + * queries, DB_OK for other successful queries. + * A DB error object is returned on failure. + * + * @see DB_oci8::prepare() + */ + function &execute($stmt, $data = array()) + { + $data = (array)$data; + $this->last_parameters = $data; + $this->_data = $data; + + $types =& $this->prepare_types[(int)$stmt]; + if (count($types) != count($data)) { + $tmp =& $this->raiseError(DB_ERROR_MISMATCH); + return $tmp; + } + + $i = 0; + foreach ($data as $key => $value) { + if ($types[$i] == DB_PARAM_MISC) { + /* + * Oracle doesn't seem to have the ability to pass a + * parameter along unchanged, so strip off quotes from start + * and end, plus turn two single quotes to one single quote, + * in order to avoid the quotes getting escaped by + * Oracle and ending up in the database. + */ + $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]); + $data[$key] = str_replace("''", "'", $data[$key]); + } elseif ($types[$i] == DB_PARAM_OPAQUE) { + $fp = @fopen($data[$key], 'rb'); + if (!$fp) { + $tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION); + return $tmp; + } + $data[$key] = fread($fp, filesize($data[$key])); + fclose($fp); + } + if (!@OCIBindByName($stmt, ':bind' . $i, $data[$key], -1)) { + $tmp = $this->oci8RaiseError($stmt); + return $tmp; + } + $i++; + } + if ($this->autocommit) { + $success = @OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS); + } else { + $success = @OCIExecute($stmt, OCI_DEFAULT); + } + if (!$success) { + $tmp = $this->oci8RaiseError($stmt); + return $tmp; + } + $this->last_stmt = $stmt; + if ($this->manip_query[(int)$stmt]) { + $tmp = DB_OK; + } else { + @ocisetprefetch($stmt, $this->options['result_buffering']); + $tmp =& new DB_result($this, $stmt); + } + return $tmp; + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff = false) + { + $this->autocommit = (bool)$onoff;; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + $result = @OCICommit($this->connection); + if (!$result) { + return $this->oci8RaiseError(); + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + $result = @OCIRollback($this->connection); + if (!$result) { + return $this->oci8RaiseError(); + } + return DB_OK; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if ($this->last_stmt === false) { + return $this->oci8RaiseError(); + } + $result = @OCIRowCount($this->last_stmt); + if ($result === false) { + return $this->oci8RaiseError($this->last_stmt); + } + return $result; + } + + // }}} + // {{{ modifyQuery() + + /** + * Changes a query string for various DBMS specific reasons + * + * "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle. + * + * @param string $query the query string to modify + * + * @return string the modified query string + * + * @access protected + */ + function modifyQuery($query) + { + if (preg_match('/^\s*SELECT/i', $query) && + !preg_match('/\sFROM\s/i', $query)) { + $query .= ' FROM dual'; + } + return $query; + } + + // }}} + // {{{ modifyLimitQuery() + + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ + function modifyLimitQuery($query, $from, $count, $params = array()) + { + // Let Oracle return the name of the columns instead of + // coding a "home" SQL parser + + if (count($params)) { + $result = $this->prepare("SELECT * FROM ($query) " + . 'WHERE NULL = NULL'); + $tmp =& $this->execute($result, $params); + } else { + $q_fields = "SELECT * FROM ($query) WHERE NULL = NULL"; + + if (!$result = @OCIParse($this->connection, $q_fields)) { + $this->last_query = $q_fields; + return $this->oci8RaiseError(); + } + if (!@OCIExecute($result, OCI_DEFAULT)) { + $this->last_query = $q_fields; + return $this->oci8RaiseError($result); + } + } + + $ncols = OCINumCols($result); + $cols = array(); + for ( $i = 1; $i <= $ncols; $i++ ) { + $cols[] = '"' . OCIColumnName($result, $i) . '"'; + } + $fields = implode(', ', $cols); + // XXX Test that (tip by John Lim) + //if (preg_match('/^\s*SELECT\s+/is', $query, $match)) { + // // Introduce the FIRST_ROWS Oracle query optimizer + // $query = substr($query, strlen($match[0]), strlen($query)); + // $query = "SELECT /* +FIRST_ROWS */ " . $query; + //} + + // Construct the query + // more at: http://marc.theaimsgroup.com/?l=php-db&m=99831958101212&w=2 + // Perhaps this could be optimized with the use of Unions + $query = "SELECT $fields FROM". + " (SELECT rownum as linenum, $fields FROM". + " ($query)". + ' WHERE rownum <= '. ($from + $count) . + ') WHERE linenum >= ' . ++$from; + return $query; + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_oci8::createSequence(), DB_oci8::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + $repeat = 0; + do { + $this->expectError(DB_ERROR_NOSUCHTABLE); + $result =& $this->query("SELECT ${seqname}.nextval FROM dual"); + $this->popExpect(); + if ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $this->raiseError($result); + } + } else { + $repeat = 0; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); + return $arr[0]; + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_oci8::nextID(), DB_oci8::dropSequence() + */ + function createSequence($seq_name) + { + return $this->query('CREATE SEQUENCE ' + . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_oci8::nextID(), DB_oci8::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP SEQUENCE ' + . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ oci8RaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_oci8::errorNative(), DB_oci8::errorCode() + */ + function oci8RaiseError($errno = null) + { + if ($errno === null) { + $error = @OCIError($this->connection); + return $this->raiseError($this->errorCode($error['code']), + null, null, null, $error['message']); + } elseif (is_resource($errno)) { + $error = @OCIError($errno); + return $this->raiseError($this->errorCode($error['code']), + null, null, null, $error['message']); + } + return $this->raiseError($this->errorCode($errno)); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code produced by the last query + * + * @return int the DBMS' error code. FALSE if the code could not be + * determined + */ + function errorNative() + { + if (is_resource($this->last_stmt)) { + $error = @OCIError($this->last_stmt); + } else { + $error = @OCIError($this->connection); + } + if (is_array($error)) { + return $error['code']; + } + return false; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * NOTE: only supports 'table' and 'flags' if $result + * is a table name. + * + * NOTE: flags won't contain index information. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) + { + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $res = array(); + + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $result = strtoupper($result); + $q_fields = 'SELECT column_name, data_type, data_length, ' + . 'nullable ' + . 'FROM user_tab_columns ' + . "WHERE table_name='$result' ORDER BY column_id"; + + $this->last_query = $q_fields; + + if (!$stmt = @OCIParse($this->connection, $q_fields)) { + return $this->oci8RaiseError(DB_ERROR_NEED_MORE_DATA); + } + if (!@OCIExecute($stmt, OCI_DEFAULT)) { + return $this->oci8RaiseError($stmt); + } + + $i = 0; + while (@OCIFetch($stmt)) { + $res[$i] = array( + 'table' => $case_func($result), + 'name' => $case_func(@OCIResult($stmt, 1)), + 'type' => @OCIResult($stmt, 2), + 'len' => @OCIResult($stmt, 3), + 'flags' => (@OCIResult($stmt, 4) == 'N') ? 'not_null' : '', + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + $i++; + } + + if ($mode) { + $res['num_fields'] = $i; + } + @OCIFreeStatement($stmt); + + } else { + if (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $result = $result->result; + } + + $res = array(); + + if ($result === $this->last_stmt) { + $count = @OCINumCols($result); + if ($mode) { + $res['num_fields'] = $count; + } + for ($i = 0; $i < $count; $i++) { + $res[$i] = array( + 'table' => '', + 'name' => $case_func(@OCIColumnName($result, $i+1)), + 'type' => @OCIColumnType($result, $i+1), + 'len' => @OCIColumnSize($result, $i+1), + 'flags' => '', + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + } else { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SELECT table_name FROM user_tables'; + case 'synonyms': + return 'SELECT synonym_name FROM user_synonyms'; + default: + return null; + } + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/DB/odbc.php b/src/www/lib/pear/DB/odbc.php new file mode 100644 index 00000000..5f65b054 --- /dev/null +++ b/src/www/lib/pear/DB/odbc.php @@ -0,0 +1,883 @@ + + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: odbc.php,v 1.1 2005/05/28 01:55:11 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's odbc extension + * for interacting with databases via ODBC connections + * + * These methods overload the ones declared in DB_common. + * + * More info on ODBC errors could be found here: + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/trblsql/tr_err_odbc_5stz.asp + * + * @category Database + * @package DB + * @author Stig Bakken + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_odbc extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'odbc'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'sql92'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * NOTE: The feature set of the following drivers are different than + * the default: + * + solid: 'transactions' = true + * + navision: 'limit' = false + * + * @var array + */ + var $features = array( + 'limit' => 'emulate', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => false, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + '01004' => DB_ERROR_TRUNCATED, + '07001' => DB_ERROR_MISMATCH, + '21S01' => DB_ERROR_VALUE_COUNT_ON_ROW, + '21S02' => DB_ERROR_MISMATCH, + '22001' => DB_ERROR_INVALID, + '22003' => DB_ERROR_INVALID_NUMBER, + '22005' => DB_ERROR_INVALID_NUMBER, + '22008' => DB_ERROR_INVALID_DATE, + '22012' => DB_ERROR_DIVZERO, + '23000' => DB_ERROR_CONSTRAINT, + '23502' => DB_ERROR_CONSTRAINT_NOT_NULL, + '23503' => DB_ERROR_CONSTRAINT, + '23504' => DB_ERROR_CONSTRAINT, + '23505' => DB_ERROR_CONSTRAINT, + '24000' => DB_ERROR_INVALID, + '34000' => DB_ERROR_INVALID, + '37000' => DB_ERROR_SYNTAX, + '42000' => DB_ERROR_SYNTAX, + '42601' => DB_ERROR_SYNTAX, + 'IM001' => DB_ERROR_UNSUPPORTED, + 'S0000' => DB_ERROR_NOSUCHTABLE, + 'S0001' => DB_ERROR_ALREADY_EXISTS, + 'S0002' => DB_ERROR_NOSUCHTABLE, + 'S0011' => DB_ERROR_ALREADY_EXISTS, + 'S0012' => DB_ERROR_NOT_FOUND, + 'S0021' => DB_ERROR_ALREADY_EXISTS, + 'S0022' => DB_ERROR_NOSUCHFIELD, + 'S1009' => DB_ERROR_INVALID, + 'S1090' => DB_ERROR_INVALID, + 'S1C00' => DB_ERROR_NOT_CAPABLE, + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * The number of rows affected by a data manipulation query + * @var integer + * @access private + */ + var $affected = 0; + + + // }}} + // {{{ constructor + + /** + * This constructor calls $this->DB_common() + * + * @return void + */ + function DB_odbc() + { + $this->DB_common(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's odbc driver supports the following extra DSN options: + * + cursor The type of cursor to be used for this connection. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('odbc')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + switch ($this->dbsyntax) { + case 'access': + case 'db2': + case 'solid': + $this->features['transactions'] = true; + break; + case 'navision': + $this->features['limit'] = false; + } + + /* + * This is hear for backwards compatibility. Should have been using + * 'database' all along, but prior to 1.6.0RC3 'hostspec' was used. + */ + if ($dsn['database']) { + $odbcdsn = $dsn['database']; + } elseif ($dsn['hostspec']) { + $odbcdsn = $dsn['hostspec']; + } else { + $odbcdsn = 'localhost'; + } + + $connect_function = $persistent ? 'odbc_pconnect' : 'odbc_connect'; + + if (empty($dsn['cursor'])) { + $this->connection = @$connect_function($odbcdsn, $dsn['username'], + $dsn['password']); + } else { + $this->connection = @$connect_function($odbcdsn, $dsn['username'], + $dsn['password'], + $dsn['cursor']); + } + + if (!is_resource($this->connection)) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $this->errorNative()); + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $err = @odbc_close($this->connection); + $this->connection = null; + return $err; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $this->last_query = $query; + $query = $this->modifyQuery($query); + $result = @odbc_exec($this->connection, $query); + if (!$result) { + return $this->odbcRaiseError(); // XXX ERRORMSG + } + // Determine which queries that should return data, and which + // should return an error code only. + if (DB::isManip($query)) { + $this->affected = $result; // For affectedRows() + return DB_OK; + } + $this->affected = 0; + return $result; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal odbc result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return @odbc_next_result($result); + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + $arr = array(); + if ($rownum !== null) { + $rownum++; // ODBC first row is 1 + if (version_compare(phpversion(), '4.2.0', 'ge')) { + $cols = @odbc_fetch_into($result, $arr, $rownum); + } else { + $cols = @odbc_fetch_into($result, $rownum, $arr); + } + } else { + $cols = @odbc_fetch_into($result, $arr); + } + if (!$cols) { + return null; + } + if ($fetchmode !== DB_FETCHMODE_ORDERED) { + for ($i = 0; $i < count($arr); $i++) { + $colName = @odbc_field_name($result, $i+1); + $a[$colName] = $arr[$i]; + } + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $a = array_change_key_case($a, CASE_LOWER); + } + $arr = $a; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return @odbc_free_result($result); + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @odbc_num_fields($result); + if (!$cols) { + return $this->odbcRaiseError(); + } + return $cols; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if (empty($this->affected)) { // In case of SELECT stms + return 0; + } + $nrows = @odbc_num_rows($this->affected); + if ($nrows == -1) { + return $this->odbcRaiseError(); + } + return $nrows; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * Not all ODBC drivers support this functionality. If they don't + * a DB_Error object for DB_ERROR_UNSUPPORTED is returned. + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ + function numRows($result) + { + $nrows = @odbc_num_rows($result); + if ($nrows == -1) { + return $this->odbcRaiseError(DB_ERROR_UNSUPPORTED); + } + if ($nrows === false) { + return $this->odbcRaiseError(); + } + return $nrows; + } + + // }}} + // {{{ quoteIdentifier() + + /** + * Quotes a string so it can be safely used as a table or column name + * + * Use 'mssql' as the dbsyntax in the DB DSN only if you've unchecked + * "Use ANSI quoted identifiers" when setting up the ODBC data source. + * + * @param string $str identifier name to be quoted + * + * @return string quoted identifier string + * + * @see DB_common::quoteIdentifier() + * @since Method available since Release 1.6.0 + */ + function quoteIdentifier($str) + { + switch ($this->dsn['dbsyntax']) { + case 'access': + return '[' . $str . ']'; + case 'mssql': + case 'sybase': + return '[' . str_replace(']', ']]', $str) . ']'; + case 'mysql': + case 'mysqli': + return '`' . $str . '`'; + default: + return '"' . str_replace('"', '""', $str) . '"'; + } + } + + // }}} + // {{{ quote() + + /** + * @deprecated Deprecated in release 1.6.0 + * @internal + */ + function quote($str) + { + return $this->quoteSmart($str); + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_odbc::createSequence(), DB_odbc::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + $repeat = 0; + do { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("update ${seqname} set id = id + 1"); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) { + $repeat = 1; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->createSequence($seq_name); + $this->popErrorHandling(); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $result = $this->query("insert into ${seqname} (id) values(0)"); + } else { + $repeat = 0; + } + } while ($repeat); + + if (DB::isError($result)) { + return $this->raiseError($result); + } + + $result = $this->query("select id from ${seqname}"); + if (DB::isError($result)) { + return $result; + } + + $row = $result->fetchRow(DB_FETCHMODE_ORDERED); + if (DB::isError($row || !$row)) { + return $row; + } + + return $row[0]; + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_odbc::nextID(), DB_odbc::dropSequence() + */ + function createSequence($seq_name) + { + return $this->query('CREATE TABLE ' + . $this->getSequenceName($seq_name) + . ' (id integer NOT NULL,' + . ' PRIMARY KEY(id))'); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_odbc::nextID(), DB_odbc::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff = false) + { + if (!@odbc_autocommit($this->connection, $onoff)) { + return $this->odbcRaiseError(); + } + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + if (!@odbc_commit($this->connection)) { + return $this->odbcRaiseError(); + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + if (!@odbc_rollback($this->connection)) { + return $this->odbcRaiseError(); + } + return DB_OK; + } + + // }}} + // {{{ odbcRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_odbc::errorNative(), DB_common::errorCode() + */ + function odbcRaiseError($errno = null) + { + if ($errno === null) { + switch ($this->dbsyntax) { + case 'access': + if ($this->options['portability'] & DB_PORTABILITY_ERRORS) { + $this->errorcode_map['07001'] = DB_ERROR_NOSUCHFIELD; + } else { + // Doing this in case mode changes during runtime. + $this->errorcode_map['07001'] = DB_ERROR_MISMATCH; + } + + $native_code = odbc_error($this->connection); + + // S1000 is for "General Error." Let's be more specific. + if ($native_code == 'S1000') { + $errormsg = odbc_errormsg($this->connection); + static $error_regexps; + if (!isset($error_regexps)) { + $error_regexps = array( + '/includes related records.$/i' => DB_ERROR_CONSTRAINT, + '/cannot contain a Null value/i' => DB_ERROR_CONSTRAINT_NOT_NULL, + ); + } + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $this->raiseError($code, + null, null, null, + $native_code . ' ' . $errormsg); + } + } + $errno = DB_ERROR; + } else { + $errno = $this->errorCode($native_code); + } + break; + default: + $errno = $this->errorCode(odbc_error($this->connection)); + } + } + return $this->raiseError($errno, null, null, null, + $this->errorNative()); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error code and message produced by the last query + * + * @return string the DBMS' error code and message + */ + function errorNative() + { + if (!is_resource($this->connection)) { + return @odbc_error() . ' ' . @odbc_errormsg(); + } + return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection); + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + * @since Method available since Release 1.7.0 + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @odbc_exec($this->connection, "SELECT * FROM $result"); + if (!$id) { + return $this->odbcRaiseError(); + } + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->odbcRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @odbc_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $col = $i + 1; + $res[$i] = array( + 'table' => $got_string ? $case_func($result) : '', + 'name' => $case_func(@odbc_field_name($id, $col)), + 'type' => @odbc_field_type($id, $col), + 'len' => @odbc_field_len($id, $col), + 'flags' => '', + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @odbc_free_result($id); + } + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * Thanks to symbol1@gmail.com and Philippe.Jausions@11abacus.com. + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the list of objects requested + * + * @access protected + * @see DB_common::getListOf() + * @since Method available since Release 1.7.0 + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'databases': + if (!function_exists('odbc_data_source')) { + return null; + } + $res = @odbc_data_source($this->connection, SQL_FETCH_FIRST); + if (is_array($res)) { + $out = array($res['server']); + while($res = @odbc_data_source($this->connection, + SQL_FETCH_NEXT)) + { + $out[] = $res['server']; + } + return $out; + } else { + return $this->odbcRaiseError(); + } + break; + case 'tables': + case 'schema.tables': + $keep = 'TABLE'; + break; + case 'views': + $keep = 'VIEW'; + break; + default: + return null; + } + + /* + * Removing non-conforming items in the while loop rather than + * in the odbc_tables() call because some backends choke on this: + * odbc_tables($this->connection, '', '', '', 'TABLE') + */ + $res = @odbc_tables($this->connection); + if (!$res) { + return $this->odbcRaiseError(); + } + $out = array(); + while ($row = odbc_fetch_array($res)) { + if ($row['TABLE_TYPE'] != $keep) { + continue; + } + if ($type == 'schema.tables') { + $out[] = $row['TABLE_SCHEM'] . '.' . $row['TABLE_NAME']; + } else { + $out[] = $row['TABLE_NAME']; + } + } + return $out; + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/DB/pgsql.php b/src/www/lib/pear/DB/pgsql.php new file mode 100644 index 00000000..79d6f235 --- /dev/null +++ b/src/www/lib/pear/DB/pgsql.php @@ -0,0 +1,1097 @@ + + * @author Stig Bakken + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: pgsql.php,v 1.1 2005/05/28 01:55:11 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's pgsql extension + * for interacting with PostgreSQL databases + * + * These methods overload the ones declared in DB_common. + * + * @category Database + * @package DB + * @author Rui Hirokawa + * @author Stig Bakken + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_pgsql extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'pgsql'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'pgsql'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'alter', + 'new_link' => '4.3.0', + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => true, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ + var $autocommit = true; + + /** + * The quantity of transactions begun + * + * {@internal While this is private, it can't actually be designated + * private in PHP 5 because it is directly accessed in the test suite.}} + * + * @var integer + * @access private + */ + var $transaction_opcount = 0; + + /** + * The number of rows affected by a data manipulation query + * @var integer + */ + var $affected = 0; + + /** + * The current row being looked at in fetchInto() + * @var array + * @access private + */ + var $row = array(); + + /** + * The number of rows in a given result set + * @var array + * @access private + */ + var $_num_rows = array(); + + + // }}} + // {{{ constructor + + /** + * This constructor calls $this->DB_common() + * + * @return void + */ + function DB_pgsql() + { + $this->DB_common(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's pgsql driver supports the following extra DSN options: + * + connect_timeout How many seconds to wait for a connection to + * be established. Available since PEAR DB 1.7.0. + * + new_link If set to true, causes subsequent calls to + * connect() to return a new connection link + * instead of the existing one. WARNING: this is + * not portable to other DBMS's. Available only + * if PHP is >= 4.3.0 and PEAR DB is >= 1.7.0. + * + options Command line options to be sent to the server. + * Available since PEAR DB 1.6.4. + * + service Specifies a service name in pg_service.conf that + * holds additional connection parameters. + * Available since PEAR DB 1.7.0. + * + sslmode How should SSL be used when connecting? Values: + * disable, allow, prefer or require. + * Available since PEAR DB 1.7.0. + * + tty This was used to specify where to send server + * debug output. Available since PEAR DB 1.6.4. + * + * Example of connecting to a new link via a socket: + * + * require_once 'DB.php'; + * + * $dsn = 'pgsql://user:pass@unix(/tmp)/dbname?new_link=true'; + * $options = array( + * 'portability' => DB_PORTABILITY_ALL, + * ); + * + * $db =& DB::connect($dsn, $options); + * if (PEAR::isError($db)) { + * die($db->getMessage()); + * } + * + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @link http://www.postgresql.org/docs/current/static/libpq.html#LIBPQ-CONNECT + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('pgsql')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $protocol = $dsn['protocol'] ? $dsn['protocol'] : 'tcp'; + + $params = array(''); + if ($protocol == 'tcp') { + if ($dsn['hostspec']) { + $params[0] .= 'host=' . $dsn['hostspec']; + } + if ($dsn['port']) { + $params[0] .= ' port=' . $dsn['port']; + } + } elseif ($protocol == 'unix') { + // Allow for pg socket in non-standard locations. + if ($dsn['socket']) { + $params[0] .= 'host=' . $dsn['socket']; + } + if ($dsn['port']) { + $params[0] .= ' port=' . $dsn['port']; + } + } + if ($dsn['database']) { + $params[0] .= ' dbname=\'' . addslashes($dsn['database']) . '\''; + } + if ($dsn['username']) { + $params[0] .= ' user=\'' . addslashes($dsn['username']) . '\''; + } + if ($dsn['password']) { + $params[0] .= ' password=\'' . addslashes($dsn['password']) . '\''; + } + if (!empty($dsn['options'])) { + $params[0] .= ' options=' . $dsn['options']; + } + if (!empty($dsn['tty'])) { + $params[0] .= ' tty=' . $dsn['tty']; + } + if (!empty($dsn['connect_timeout'])) { + $params[0] .= ' connect_timeout=' . $dsn['connect_timeout']; + } + if (!empty($dsn['sslmode'])) { + $params[0] .= ' sslmode=' . $dsn['sslmode']; + } + if (!empty($dsn['service'])) { + $params[0] .= ' service=' . $dsn['service']; + } + + if (isset($dsn['new_link']) + && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) + { + if (version_compare(phpversion(), '4.3.0', '>=')) { + $params[] = PGSQL_CONNECT_FORCE_NEW; + } + } + + $connect_function = $persistent ? 'pg_pconnect' : 'pg_connect'; + + $ini = ini_get('track_errors'); + $php_errormsg = ''; + if ($ini) { + $this->connection = @call_user_func_array($connect_function, + $params); + } else { + ini_set('track_errors', 1); + $this->connection = @call_user_func_array($connect_function, + $params); + ini_set('track_errors', $ini); + } + + if (!$this->connection) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + $php_errormsg); + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @pg_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + $query = $this->modifyQuery($query); + if (!$this->autocommit && $ismanip) { + if ($this->transaction_opcount == 0) { + $result = @pg_exec($this->connection, 'begin;'); + if (!$result) { + return $this->pgsqlRaiseError(); + } + } + $this->transaction_opcount++; + } + $result = @pg_exec($this->connection, $query); + if (!$result) { + return $this->pgsqlRaiseError(); + } + // Determine which queries that should return data, and which + // should return an error code only. + if ($ismanip) { + $this->affected = @pg_affected_rows($result); + return DB_OK; + } elseif (preg_match('/^\s*\(*\s*(SELECT|EXPLAIN|SHOW)\s/si', $query)) { + /* PostgreSQL commands: + ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY, + CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH, + GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET, + REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW, + UNLISTEN, UPDATE, VACUUM + */ + $this->row[(int)$result] = 0; // reset the row counter. + $numrows = $this->numRows($result); + if (is_object($numrows)) { + return $numrows; + } + $this->_num_rows[(int)$result] = $numrows; + $this->affected = 0; + return $result; + } else { + $this->affected = 0; + return DB_OK; + } + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal pgsql result pointer to the next available result + * + * @param a valid fbsql result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + $result_int = (int)$result; + $rownum = ($rownum !== null) ? $rownum : $this->row[$result_int]; + if ($rownum >= $this->_num_rows[$result_int]) { + return null; + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @pg_fetch_array($result, $rownum, PGSQL_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @pg_fetch_row($result, $rownum); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + $this->row[$result_int] = ++$rownum; + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + if (is_resource($result)) { + unset($this->row[(int)$result]); + unset($this->_num_rows[(int)$result]); + $this->affected = 0; + return @pg_freeresult($result); + } + return false; + } + + // }}} + // {{{ quote() + + /** + * @deprecated Deprecated in release 1.6.0 + * @internal + */ + function quote($str) + { + return $this->quoteSmart($str); + } + + // }}} + // {{{ quoteSmart() + + /** + * Formats input so it can be safely used in a query + * + * @param mixed $in the data to be formatted + * + * @return mixed the formatted data. The format depends on the input's + * PHP type: + * + null = the string NULL + * + boolean = string TRUE or FALSE + * + integer or double = the unquoted number + * + other (including strings and numeric strings) = + * the data escaped according to MySQL's settings + * then encapsulated between single quotes + * + * @see DB_common::quoteSmart() + * @since Method available since Release 1.6.0 + */ + function quoteSmart($in) + { + if (is_int($in) || is_double($in)) { + return $in; + } elseif (is_bool($in)) { + return $in ? 'TRUE' : 'FALSE'; + } elseif (is_null($in)) { + return 'NULL'; + } else { + return "'" . $this->escapeSimple($in) . "'"; + } + } + + // }}} + // {{{ escapeSimple() + + /** + * Escapes a string according to the current DBMS's standards + * + * {@internal PostgreSQL treats a backslash as an escape character, + * so they are escaped as well. + * + * Not using pg_escape_string() yet because it requires PostgreSQL + * to be at version 7.2 or greater.}} + * + * @param string $str the string to be escaped + * + * @return string the escaped string + * + * @see DB_common::quoteSmart() + * @since Method available since Release 1.6.0 + */ + function escapeSimple($str) + { + return str_replace("'", "''", str_replace('\\', '\\\\', $str)); + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @pg_numfields($result); + if (!$cols) { + return $this->pgsqlRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ + function numRows($result) + { + $rows = @pg_numrows($result); + if ($rows === null) { + return $this->pgsqlRaiseError(); + } + return $rows; + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff = false) + { + // XXX if $this->transaction_opcount > 0, we should probably + // issue a warning here. + $this->autocommit = $onoff ? true : false; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + if ($this->transaction_opcount > 0) { + // (disabled) hack to shut up error messages from libpq.a + //@fclose(@fopen("php://stderr", "w")); + $result = @pg_exec($this->connection, 'end;'); + $this->transaction_opcount = 0; + if (!$result) { + return $this->pgsqlRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + if ($this->transaction_opcount > 0) { + $result = @pg_exec($this->connection, 'abort;'); + $this->transaction_opcount = 0; + if (!$result) { + return $this->pgsqlRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + return $this->affected; + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_pgsql::createSequence(), DB_pgsql::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + $repeat = false; + do { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result =& $this->query("SELECT NEXTVAL('${seqname}')"); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) { + $repeat = true; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->createSequence($seq_name); + $this->popErrorHandling(); + if (DB::isError($result)) { + return $this->raiseError($result); + } + } else { + $repeat = false; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); + $result->free(); + return $arr[0]; + } + + // }}} + // {{{ createSequence() + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_pgsql::nextID(), DB_pgsql::dropSequence() + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + $result = $this->query("CREATE SEQUENCE ${seqname}"); + return $result; + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_pgsql::nextID(), DB_pgsql::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP SEQUENCE ' + . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ modifyLimitQuery() + + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ + function modifyLimitQuery($query, $from, $count, $params = array()) + { + return "$query LIMIT $count OFFSET $from"; + } + + // }}} + // {{{ pgsqlRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_pgsql::errorNative(), DB_pgsql::errorCode() + */ + function pgsqlRaiseError($errno = null) + { + $native = $this->errorNative(); + if ($errno === null) { + $errno = $this->errorCode($native); + } + return $this->raiseError($errno, null, null, null, $native); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error message produced by the last query + * + * {@internal Error messages are used instead of error codes + * in order to support older versions of PostgreSQL.}} + * + * @return string the DBMS' error message + */ + function errorNative() + { + return @pg_errormessage($this->connection); + } + + // }}} + // {{{ errorCode() + + /** + * Determines PEAR::DB error code from the database's text error message. + * + * @param string $errormsg error message returned from the database + * @return integer an error number from a DB error constant + */ + function errorCode($errormsg) + { + static $error_regexps; + if (!isset($error_regexps)) { + $error_regexps = array( + '/(relation|sequence|table).*does not exist|class .* not found/i' + => DB_ERROR_NOSUCHTABLE, + '/index .* does not exist/' + => DB_ERROR_NOT_FOUND, + '/column .* does not exist/i' + => DB_ERROR_NOSUCHFIELD, + '/relation .* already exists/i' + => DB_ERROR_ALREADY_EXISTS, + '/(divide|division) by zero$/i' + => DB_ERROR_DIVZERO, + '/pg_atoi: error in .*: can\'t parse /i' + => DB_ERROR_INVALID_NUMBER, + '/invalid input syntax for( type)? (integer|numeric)/i' + => DB_ERROR_INVALID_NUMBER, + '/value .* is out of range for type \w*int/i' + => DB_ERROR_INVALID_NUMBER, + '/integer out of range/i' + => DB_ERROR_INVALID_NUMBER, + '/value too long for type character/i' + => DB_ERROR_INVALID, + '/attribute .* not found|relation .* does not have attribute/i' + => DB_ERROR_NOSUCHFIELD, + '/column .* specified in USING clause does not exist in (left|right) table/i' + => DB_ERROR_NOSUCHFIELD, + '/parser: parse error at or near/i' + => DB_ERROR_SYNTAX, + '/syntax error at/' + => DB_ERROR_SYNTAX, + '/column reference .* is ambiguous/i' + => DB_ERROR_SYNTAX, + '/permission denied/' + => DB_ERROR_ACCESS_VIOLATION, + '/violates not-null constraint/' + => DB_ERROR_CONSTRAINT_NOT_NULL, + '/violates [\w ]+ constraint/' + => DB_ERROR_CONSTRAINT, + '/referential integrity violation/' + => DB_ERROR_CONSTRAINT, + '/more expressions than target columns/i' + => DB_ERROR_VALUE_COUNT_ON_ROW, + ); + } + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + // Fall back to DB_ERROR if there was no mapping. + return DB_ERROR; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * NOTE: only supports 'table' and 'flags' if $result + * is a table name. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @pg_exec($this->connection, "SELECT * FROM $result LIMIT 0"); + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->pgsqlRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @pg_numfields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $res[$i] = array( + 'table' => $got_string ? $case_func($result) : '', + 'name' => $case_func(@pg_fieldname($id, $i)), + 'type' => @pg_fieldtype($id, $i), + 'len' => @pg_fieldsize($id, $i), + 'flags' => $got_string + ? $this->_pgFieldFlags($id, $i, $result) + : '', + ); + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @pg_freeresult($id); + } + return $res; + } + + // }}} + // {{{ _pgFieldFlags() + + /** + * Get a column's flags + * + * Supports "not_null", "default_value", "primary_key", "unique_key" + * and "multiple_key". The default value is passed through + * rawurlencode() in case there are spaces in it. + * + * @param int $resource the PostgreSQL result identifier + * @param int $num_field the field number + * + * @return string the flags + * + * @access private + */ + function _pgFieldFlags($resource, $num_field, $table_name) + { + $field_name = @pg_fieldname($resource, $num_field); + + $result = @pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef + FROM pg_attribute f, pg_class tab, pg_type typ + WHERE tab.relname = typ.typname + AND typ.typrelid = f.attrelid + AND f.attname = '$field_name' + AND tab.relname = '$table_name'"); + if (@pg_numrows($result) > 0) { + $row = @pg_fetch_row($result, 0); + $flags = ($row[0] == 't') ? 'not_null ' : ''; + + if ($row[1] == 't') { + $result = @pg_exec($this->connection, "SELECT a.adsrc + FROM pg_attribute f, pg_class tab, pg_type typ, pg_attrdef a + WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid + AND f.attrelid = a.adrelid AND f.attname = '$field_name' + AND tab.relname = '$table_name' AND f.attnum = a.adnum"); + $row = @pg_fetch_row($result, 0); + $num = preg_replace("/'(.*)'::\w+/", "\\1", $row[0]); + $flags .= 'default_' . rawurlencode($num) . ' '; + } + } else { + $flags = ''; + } + $result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey + FROM pg_attribute f, pg_class tab, pg_type typ, pg_index i + WHERE tab.relname = typ.typname + AND typ.typrelid = f.attrelid + AND f.attrelid = i.indrelid + AND f.attname = '$field_name' + AND tab.relname = '$table_name'"); + $count = @pg_numrows($result); + + for ($i = 0; $i < $count ; $i++) { + $row = @pg_fetch_row($result, $i); + $keys = explode(' ', $row[2]); + + if (in_array($num_field + 1, $keys)) { + $flags .= ($row[0] == 't' && $row[1] == 'f') ? 'unique_key ' : ''; + $flags .= ($row[1] == 't') ? 'primary_key ' : ''; + if (count($keys) > 1) + $flags .= 'multiple_key '; + } + } + + return trim($flags); + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return 'SELECT c.relname AS "Name"' + . ' FROM pg_class c, pg_user u' + . ' WHERE c.relowner = u.usesysid' + . " AND c.relkind = 'r'" + . ' AND NOT EXISTS' + . ' (SELECT 1 FROM pg_views' + . ' WHERE viewname = c.relname)' + . " AND c.relname !~ '^(pg_|sql_)'" + . ' UNION' + . ' SELECT c.relname AS "Name"' + . ' FROM pg_class c' + . " WHERE c.relkind = 'r'" + . ' AND NOT EXISTS' + . ' (SELECT 1 FROM pg_views' + . ' WHERE viewname = c.relname)' + . ' AND NOT EXISTS' + . ' (SELECT 1 FROM pg_user' + . ' WHERE usesysid = c.relowner)' + . " AND c.relname !~ '^pg_'"; + case 'schema.tables': + return "SELECT schemaname || '.' || tablename" + . ' AS "Name"' + . ' FROM pg_catalog.pg_tables' + . ' WHERE schemaname NOT IN' + . " ('pg_catalog', 'information_schema', 'pg_toast')"; + case 'views': + // Table cols: viewname | viewowner | definition + return 'SELECT viewname from pg_views WHERE schemaname' + . " NOT IN ('information_schema', 'pg_catalog')"; + case 'users': + // cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd |valuntil + return 'SELECT usename FROM pg_user'; + case 'databases': + return 'SELECT datname FROM pg_database'; + case 'functions': + case 'procedures': + return 'SELECT proname FROM pg_proc WHERE proowner <> 1'; + default: + return null; + } + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/DB/sqlite.php b/src/www/lib/pear/DB/sqlite.php new file mode 100644 index 00000000..e349ec02 --- /dev/null +++ b/src/www/lib/pear/DB/sqlite.php @@ -0,0 +1,942 @@ + + * @author Mika Tuupola + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0 + * @version CVS: $Id: sqlite.php,v 1.1 2005/05/28 01:55:11 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's sqlite extension + * for interacting with SQLite databases + * + * These methods overload the ones declared in DB_common. + * + * NOTICE: This driver needs PHP's track_errors ini setting to be on. + * It is automatically turned on when connecting to the database. + * Make sure your scripts don't turn it off. + * + * @category Database + * @package DB + * @author Urs Gehrig + * @author Mika Tuupola + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_sqlite extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'sqlite'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'sqlite'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'alter', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => false, + ); + + /** + * A mapping of native error codes to DB error codes + * + * {@internal Error codes according to sqlite_exec. See the online + * manual at http://sqlite.org/c_interface.html for info. + * This error handling based on sqlite_exec is not yet implemented.}} + * + * @var array + */ + var $errorcode_map = array( + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * SQLite data types + * + * @link http://www.sqlite.org/datatypes.html + * + * @var array + */ + var $keywords = array ( + 'BLOB' => '', + 'BOOLEAN' => '', + 'CHARACTER' => '', + 'CLOB' => '', + 'FLOAT' => '', + 'INTEGER' => '', + 'KEY' => '', + 'NATIONAL' => '', + 'NUMERIC' => '', + 'NVARCHAR' => '', + 'PRIMARY' => '', + 'TEXT' => '', + 'TIMESTAMP' => '', + 'UNIQUE' => '', + 'VARCHAR' => '', + 'VARYING' => '', + ); + + /** + * The most recent error message from $php_errormsg + * @var string + * @access private + */ + var $_lasterror = ''; + + + // }}} + // {{{ constructor + + /** + * This constructor calls $this->DB_common() + * + * @return void + */ + function DB_sqlite() + { + $this->DB_common(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's sqlite driver supports the following extra DSN options: + * + mode The permissions for the database file, in four digit + * chmod octal format (eg "0600"). + * + * Example of connecting to a database in read-only mode: + * + * require_once 'DB.php'; + * + * $dsn = 'sqlite:///path/and/name/of/db/file?mode=0400'; + * $options = array( + * 'portability' => DB_PORTABILITY_ALL, + * ); + * + * $db =& DB::connect($dsn, $options); + * if (PEAR::isError($db)) { + * die($db->getMessage()); + * } + * + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('sqlite')) { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + if ($dsn['database']) { + if (!file_exists($dsn['database'])) { + if (!touch($dsn['database'])) { + return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); + } + if (!isset($dsn['mode']) || + !is_numeric($dsn['mode'])) + { + $mode = 0644; + } else { + $mode = octdec($dsn['mode']); + } + if (!chmod($dsn['database'], $mode)) { + return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); + } + if (!file_exists($dsn['database'])) { + return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); + } + } + if (!is_file($dsn['database'])) { + return $this->sqliteRaiseError(DB_ERROR_INVALID); + } + if (!is_readable($dsn['database'])) { + return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION); + } + } else { + return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION); + } + + $connect_function = $persistent ? 'sqlite_popen' : 'sqlite_open'; + + // track_errors must remain on for simpleQuery() + ini_set('track_errors', 1); + $php_errormsg = ''; + + if (!$this->connection = @$connect_function($dsn['database'])) { + return $this->raiseError(DB_ERROR_NODBSELECTED, + null, null, null, + $php_errormsg); + } + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @sqlite_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * NOTICE: This method needs PHP's track_errors ini setting to be on. + * It is automatically turned on when connecting to the database. + * Make sure your scripts don't turn it off. + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + $query = $this->modifyQuery($query); + + $php_errormsg = ''; + + $result = @sqlite_query($query, $this->connection); + $this->_lasterror = $php_errormsg ? $php_errormsg : ''; + + $this->result = $result; + if (!$this->result) { + return $this->sqliteRaiseError(null); + } + + // sqlite_query() seems to allways return a resource + // so cant use that. Using $ismanip instead + if (!$ismanip) { + $numRows = $this->numRows($result); + if (is_object($numRows)) { + // we've got PEAR_Error + return $numRows; + } + return $result; + } + return DB_OK; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal sqlite result pointer to the next available result + * + * @param resource $result the valid sqlite result resource + * + * @return bool true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum !== null) { + if (!@sqlite_seek($this->result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = @sqlite_fetch_array($result, SQLITE_ASSOC); + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @sqlite_fetch_array($result, SQLITE_NUM); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + /* + * Even though this DBMS already trims output, we do this because + * a field might have intentional whitespace at the end that + * gets removed by DB_PORTABILITY_RTRIM under another driver. + */ + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult(&$result) + { + // XXX No native free? + if (!is_resource($result)) { + return false; + } + $result = null; + return true; + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @sqlite_num_fields($result); + if (!$cols) { + return $this->sqliteRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ + function numRows($result) + { + $rows = @sqlite_num_rows($result); + if ($rows === null) { + return $this->sqliteRaiseError(); + } + return $rows; + } + + // }}} + // {{{ affected() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + return @sqlite_changes($this->connection); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_sqlite::nextID(), DB_sqlite::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_sqlite::nextID(), DB_sqlite::dropSequence() + */ + function createSequence($seq_name) + { + $seqname = $this->getSequenceName($seq_name); + $query = 'CREATE TABLE ' . $seqname . + ' (id INTEGER UNSIGNED PRIMARY KEY) '; + $result = $this->query($query); + if (DB::isError($result)) { + return($result); + } + $query = "CREATE TRIGGER ${seqname}_cleanup AFTER INSERT ON $seqname + BEGIN + DELETE FROM $seqname WHERE idquery($query); + if (DB::isError($result)) { + return($result); + } + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_sqlite::createSequence(), DB_sqlite::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + + do { + $repeat = 0; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("INSERT INTO $seqname (id) VALUES (NULL)"); + $this->popErrorHandling(); + if ($result === DB_OK) { + $id = @sqlite_last_insert_rowid($this->connection); + if ($id != 0) { + return $id; + } + } elseif ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) + { + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $this->raiseError($result); + } else { + $repeat = 1; + } + } + } while ($repeat); + + return $this->raiseError($result); + } + + // }}} + // {{{ getDbFileStats() + + /** + * Get the file stats for the current database + * + * Possible arguments are dev, ino, mode, nlink, uid, gid, rdev, size, + * atime, mtime, ctime, blksize, blocks or a numeric key between + * 0 and 12. + * + * @param string $arg the array key for stats() + * + * @return mixed an array on an unspecified key, integer on a passed + * arg and false at a stats error + */ + function getDbFileStats($arg = '') + { + $stats = stat($this->dsn['database']); + if ($stats == false) { + return false; + } + if (is_array($stats)) { + if (is_numeric($arg)) { + if (((int)$arg <= 12) & ((int)$arg >= 0)) { + return false; + } + return $stats[$arg ]; + } + if (array_key_exists(trim($arg), $stats)) { + return $stats[$arg ]; + } + } + return $stats; + } + + // }}} + // {{{ escapeSimple() + + /** + * Escapes a string according to the current DBMS's standards + * + * In SQLite, this makes things safe for inserts/updates, but may + * cause problems when performing text comparisons against columns + * containing binary data. See the + * {@link http://php.net/sqlite_escape_string PHP manual} for more info. + * + * @param string $str the string to be escaped + * + * @return string the escaped string + * + * @since Method available since Release 1.6.1 + * @see DB_common::escapeSimple() + */ + function escapeSimple($str) + { + return @sqlite_escape_string($str); + } + + // }}} + // {{{ modifyLimitQuery() + + /** + * Adds LIMIT clauses to a query string according to current DBMS standards + * + * @param string $query the query to modify + * @param int $from the row to start to fetching (0 = the first row) + * @param int $count the numbers of rows to fetch + * @param mixed $params array, string or numeric data to be used in + * execution of the statement. Quantity of items + * passed must match quantity of placeholders in + * query: meaning 1 placeholder for non-array + * parameters or 1 placeholder per array element. + * + * @return string the query string with LIMIT clauses added + * + * @access protected + */ + function modifyLimitQuery($query, $from, $count, $params = array()) + { + return "$query LIMIT $count OFFSET $from"; + } + + // }}} + // {{{ modifyQuery() + + /** + * Changes a query string for various DBMS specific reasons + * + * This little hack lets you know how many rows were deleted + * when running a "DELETE FROM table" query. Only implemented + * if the DB_PORTABILITY_DELETE_COUNT portability option is on. + * + * @param string $query the query string to modify + * + * @return string the modified query string + * + * @access protected + * @see DB_common::setOption() + */ + function modifyQuery($query) + { + if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) { + if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { + $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', + 'DELETE FROM \1 WHERE 1=1', $query); + } + } + return $query; + } + + // }}} + // {{{ sqliteRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_sqlite::errorNative(), DB_sqlite::errorCode() + */ + function sqliteRaiseError($errno = null) + { + $native = $this->errorNative(); + if ($errno === null) { + $errno = $this->errorCode($native); + } + + $errorcode = @sqlite_last_error($this->connection); + $userinfo = "$errorcode ** $this->last_query"; + + return $this->raiseError($errno, null, null, $userinfo, $native); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error message produced by the last query + * + * {@internal This is used to retrieve more meaningfull error messages + * because sqlite_last_error() does not provide adequate info.}} + * + * @return string the DBMS' error message + */ + function errorNative() + { + return $this->_lasterror; + } + + // }}} + // {{{ errorCode() + + /** + * Determines PEAR::DB error code from the database's text error message + * + * @param string $errormsg the error message returned from the database + * + * @return integer the DB error number + */ + function errorCode($errormsg) + { + static $error_regexps; + if (!isset($error_regexps)) { + $error_regexps = array( + '/^no such table:/' => DB_ERROR_NOSUCHTABLE, + '/^no such index:/' => DB_ERROR_NOT_FOUND, + '/^(table|index) .* already exists$/' => DB_ERROR_ALREADY_EXISTS, + '/PRIMARY KEY must be unique/i' => DB_ERROR_CONSTRAINT, + '/is not unique/' => DB_ERROR_CONSTRAINT, + '/columns .* are not unique/i' => DB_ERROR_CONSTRAINT, + '/uniqueness constraint failed/' => DB_ERROR_CONSTRAINT, + '/may not be NULL/' => DB_ERROR_CONSTRAINT_NOT_NULL, + '/^no such column:/' => DB_ERROR_NOSUCHFIELD, + '/column not present in both tables/i' => DB_ERROR_NOSUCHFIELD, + '/^near ".*": syntax error$/' => DB_ERROR_SYNTAX, + '/[0-9]+ values for [0-9]+ columns/i' => DB_ERROR_VALUE_COUNT_ON_ROW, + ); + } + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + // Fall back to DB_ERROR if there was no mapping. + return DB_ERROR; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table + * + * @param string $result a string containing the name of a table + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + * @since Method available since Release 1.7.0 + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $id = @sqlite_array_query($this->connection, + "PRAGMA table_info('$result');", + SQLITE_ASSOC); + $got_string = true; + } else { + $this->last_query = ''; + return $this->raiseError(DB_ERROR_NOT_CAPABLE, null, null, null, + 'This DBMS can not obtain tableInfo' . + ' from result sets'); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = count($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + if (strpos($id[$i]['type'], '(') !== false) { + $bits = explode('(', $id[$i]['type']); + $type = $bits[0]; + $len = rtrim($bits[1],')'); + } else { + $type = $id[$i]['type']; + $len = 0; + } + + $flags = ''; + if ($id[$i]['pk']) { + $flags .= 'primary_key '; + } + if ($id[$i]['notnull']) { + $flags .= 'not_null '; + } + if ($id[$i]['dflt_value'] !== null) { + $flags .= 'default_' . rawurlencode($id[$i]['dflt_value']); + } + $flags = trim($flags); + + $res[$i] = array( + 'table' => $case_func($result), + 'name' => $case_func($id[$i]['name']), + 'type' => $type, + 'len' => $len, + 'flags' => $flags, + ); + + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + return $res; + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * @param array $args SQLITE DRIVER ONLY: a private array of arguments + * used by the getSpecialQuery(). Do not use + * this directly. + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type, $args = array()) + { + if (!is_array($args)) { + return $this->raiseError('no key specified', null, null, null, + 'Argument has to be an array.'); + } + + switch ($type) { + case 'master': + return 'SELECT * FROM sqlite_master;'; + case 'tables': + return "SELECT name FROM sqlite_master WHERE type='table' " + . 'UNION ALL SELECT name FROM sqlite_temp_master ' + . "WHERE type='table' ORDER BY name;"; + case 'schema': + return 'SELECT sql FROM (SELECT * FROM sqlite_master ' + . 'UNION ALL SELECT * FROM sqlite_temp_master) ' + . "WHERE type!='meta' " + . 'ORDER BY tbl_name, type DESC, name;'; + case 'schemax': + case 'schema_x': + /* + * Use like: + * $res = $db->query($db->getSpecialQuery('schema_x', + * array('table' => 'table3'))); + */ + return 'SELECT sql FROM (SELECT * FROM sqlite_master ' + . 'UNION ALL SELECT * FROM sqlite_temp_master) ' + . "WHERE tbl_name LIKE '{$args['table']}' " + . "AND type!='meta' " + . 'ORDER BY type DESC, name;'; + case 'alter': + /* + * SQLite does not support ALTER TABLE; this is a helper query + * to handle this. 'table' represents the table name, 'rows' + * the news rows to create, 'save' the row(s) to keep _with_ + * the data. + * + * Use like: + * $args = array( + * 'table' => $table, + * 'rows' => "id INTEGER PRIMARY KEY, firstname TEXT, surname TEXT, datetime TEXT", + * 'save' => "NULL, titel, content, datetime" + * ); + * $res = $db->query( $db->getSpecialQuery('alter', $args)); + */ + $rows = strtr($args['rows'], $this->keywords); + + $q = array( + 'BEGIN TRANSACTION', + "CREATE TEMPORARY TABLE {$args['table']}_backup ({$args['rows']})", + "INSERT INTO {$args['table']}_backup SELECT {$args['save']} FROM {$args['table']}", + "DROP TABLE {$args['table']}", + "CREATE TABLE {$args['table']} ({$args['rows']})", + "INSERT INTO {$args['table']} SELECT {$rows} FROM {$args['table']}_backup", + "DROP TABLE {$args['table']}_backup", + 'COMMIT', + ); + + /* + * This is a dirty hack, since the above query will not get + * executed with a single query call so here the query method + * will be called directly and return a select instead. + */ + foreach ($q as $query) { + $this->query($query); + } + return "SELECT * FROM {$args['table']};"; + default: + return null; + } + } + + // }}} +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/DB/storage.php b/src/www/lib/pear/DB/storage.php new file mode 100644 index 00000000..64dbc00c --- /dev/null +++ b/src/www/lib/pear/DB/storage.php @@ -0,0 +1,504 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: storage.php,v 1.1 2005/05/28 01:55:11 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB class so it can be extended from + */ +require_once 'DB.php'; + +/** + * Provides an object interface to a table row + * + * It lets you add, delete and change rows using objects rather than SQL + * statements. + * + * @category Database + * @package DB + * @author Stig Bakken + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_storage extends PEAR +{ + // {{{ properties + + /** the name of the table (or view, if the backend database supports + updates in views) we hold data from */ + var $_table = null; + + /** which column(s) in the table contains primary keys, can be a + string for single-column primary keys, or an array of strings + for multiple-column primary keys */ + var $_keycolumn = null; + + /** DB connection handle used for all transactions */ + var $_dbh = null; + + /** an assoc with the names of database fields stored as properties + in this object */ + var $_properties = array(); + + /** an assoc with the names of the properties in this object that + have been changed since they were fetched from the database */ + var $_changes = array(); + + /** flag that decides if data in this object can be changed. + objects that don't have their table's key column in their + property lists will be flagged as read-only. */ + var $_readonly = false; + + /** function or method that implements a validator for fields that + are set, this validator function returns true if the field is + valid, false if not */ + var $_validator = null; + + // }}} + // {{{ constructor + + /** + * Constructor + * + * @param $table string the name of the database table + * + * @param $keycolumn mixed string with name of key column, or array of + * strings if the table has a primary key of more than one column + * + * @param $dbh object database connection object + * + * @param $validator mixed function or method used to validate + * each new value, called with three parameters: the name of the + * field/column that is changing, a reference to the new value and + * a reference to this object + * + */ + function DB_storage($table, $keycolumn, &$dbh, $validator = null) + { + $this->PEAR('DB_Error'); + $this->_table = $table; + $this->_keycolumn = $keycolumn; + $this->_dbh = $dbh; + $this->_readonly = false; + $this->_validator = $validator; + } + + // }}} + // {{{ _makeWhere() + + /** + * Utility method to build a "WHERE" clause to locate ourselves in + * the table. + * + * XXX future improvement: use rowids? + * + * @access private + */ + function _makeWhere($keyval = null) + { + if (is_array($this->_keycolumn)) { + if ($keyval === null) { + for ($i = 0; $i < sizeof($this->_keycolumn); $i++) { + $keyval[] = $this->{$this->_keycolumn[$i]}; + } + } + $whereclause = ''; + for ($i = 0; $i < sizeof($this->_keycolumn); $i++) { + if ($i > 0) { + $whereclause .= ' AND '; + } + $whereclause .= $this->_keycolumn[$i]; + if (is_null($keyval[$i])) { + // there's not much point in having a NULL key, + // but we support it anyway + $whereclause .= ' IS NULL'; + } else { + $whereclause .= ' = ' . $this->_dbh->quote($keyval[$i]); + } + } + } else { + if ($keyval === null) { + $keyval = @$this->{$this->_keycolumn}; + } + $whereclause = $this->_keycolumn; + if (is_null($keyval)) { + // there's not much point in having a NULL key, + // but we support it anyway + $whereclause .= ' IS NULL'; + } else { + $whereclause .= ' = ' . $this->_dbh->quote($keyval); + } + } + return $whereclause; + } + + // }}} + // {{{ setup() + + /** + * Method used to initialize a DB_storage object from the + * configured table. + * + * @param $keyval mixed the key[s] of the row to fetch (string or array) + * + * @return int DB_OK on success, a DB error if not + */ + function setup($keyval) + { + $whereclause = $this->_makeWhere($keyval); + $query = 'SELECT * FROM ' . $this->_table . ' WHERE ' . $whereclause; + $sth = $this->_dbh->query($query); + if (DB::isError($sth)) { + return $sth; + } + $row = $sth->fetchRow(DB_FETCHMODE_ASSOC); + if (DB::isError($row)) { + return $row; + } + if (!$row) { + return $this->raiseError(null, DB_ERROR_NOT_FOUND, null, null, + $query, null, true); + } + foreach ($row as $key => $value) { + $this->_properties[$key] = true; + $this->$key = $value; + } + return DB_OK; + } + + // }}} + // {{{ insert() + + /** + * Create a new (empty) row in the configured table for this + * object. + */ + function insert($newpk) + { + if (is_array($this->_keycolumn)) { + $primarykey = $this->_keycolumn; + } else { + $primarykey = array($this->_keycolumn); + } + settype($newpk, "array"); + for ($i = 0; $i < sizeof($primarykey); $i++) { + $pkvals[] = $this->_dbh->quote($newpk[$i]); + } + + $sth = $this->_dbh->query("INSERT INTO $this->_table (" . + implode(",", $primarykey) . ") VALUES(" . + implode(",", $pkvals) . ")"); + if (DB::isError($sth)) { + return $sth; + } + if (sizeof($newpk) == 1) { + $newpk = $newpk[0]; + } + $this->setup($newpk); + } + + // }}} + // {{{ toString() + + /** + * Output a simple description of this DB_storage object. + * @return string object description + */ + function toString() + { + $info = strtolower(get_class($this)); + $info .= " (table="; + $info .= $this->_table; + $info .= ", keycolumn="; + if (is_array($this->_keycolumn)) { + $info .= "(" . implode(",", $this->_keycolumn) . ")"; + } else { + $info .= $this->_keycolumn; + } + $info .= ", dbh="; + if (is_object($this->_dbh)) { + $info .= $this->_dbh->toString(); + } else { + $info .= "null"; + } + $info .= ")"; + if (sizeof($this->_properties)) { + $info .= " [loaded, key="; + $keyname = $this->_keycolumn; + if (is_array($keyname)) { + $info .= "("; + for ($i = 0; $i < sizeof($keyname); $i++) { + if ($i > 0) { + $info .= ","; + } + $info .= $this->$keyname[$i]; + } + $info .= ")"; + } else { + $info .= $this->$keyname; + } + $info .= "]"; + } + if (sizeof($this->_changes)) { + $info .= " [modified]"; + } + return $info; + } + + // }}} + // {{{ dump() + + /** + * Dump the contents of this object to "standard output". + */ + function dump() + { + foreach ($this->_properties as $prop => $foo) { + print "$prop = "; + print htmlentities($this->$prop); + print "
\n"; + } + } + + // }}} + // {{{ &create() + + /** + * Static method used to create new DB storage objects. + * @param $data assoc. array where the keys are the names + * of properties/columns + * @return object a new instance of DB_storage or a subclass of it + */ + function &create($table, &$data) + { + $classname = strtolower(get_class($this)); + $obj =& new $classname($table); + foreach ($data as $name => $value) { + $obj->_properties[$name] = true; + $obj->$name = &$value; + } + return $obj; + } + + // }}} + // {{{ loadFromQuery() + + /** + * Loads data into this object from the given query. If this + * object already contains table data, changes will be saved and + * the object re-initialized first. + * + * @param $query SQL query + * + * @param $params parameter list in case you want to use + * prepare/execute mode + * + * @return int DB_OK on success, DB_WARNING_READ_ONLY if the + * returned object is read-only (because the object's specified + * key column was not found among the columns returned by $query), + * or another DB error code in case of errors. + */ +// XXX commented out for now +/* + function loadFromQuery($query, $params = null) + { + if (sizeof($this->_properties)) { + if (sizeof($this->_changes)) { + $this->store(); + $this->_changes = array(); + } + $this->_properties = array(); + } + $rowdata = $this->_dbh->getRow($query, DB_FETCHMODE_ASSOC, $params); + if (DB::isError($rowdata)) { + return $rowdata; + } + reset($rowdata); + $found_keycolumn = false; + while (list($key, $value) = each($rowdata)) { + if ($key == $this->_keycolumn) { + $found_keycolumn = true; + } + $this->_properties[$key] = true; + $this->$key = &$value; + unset($value); // have to unset, or all properties will + // refer to the same value + } + if (!$found_keycolumn) { + $this->_readonly = true; + return DB_WARNING_READ_ONLY; + } + return DB_OK; + } + */ + + // }}} + // {{{ set() + + /** + * Modify an attriute value. + */ + function set($property, $newvalue) + { + // only change if $property is known and object is not + // read-only + if ($this->_readonly) { + return $this->raiseError(null, DB_WARNING_READ_ONLY, null, + null, null, null, true); + } + if (@isset($this->_properties[$property])) { + if (empty($this->_validator)) { + $valid = true; + } else { + $valid = @call_user_func($this->_validator, + $this->_table, + $property, + $newvalue, + $this->$property, + $this); + } + if ($valid) { + $this->$property = $newvalue; + if (empty($this->_changes[$property])) { + $this->_changes[$property] = 0; + } else { + $this->_changes[$property]++; + } + } else { + return $this->raiseError(null, DB_ERROR_INVALID, null, + null, "invalid field: $property", + null, true); + } + return true; + } + return $this->raiseError(null, DB_ERROR_NOSUCHFIELD, null, + null, "unknown field: $property", + null, true); + } + + // }}} + // {{{ &get() + + /** + * Fetch an attribute value. + * + * @param string attribute name + * + * @return attribute contents, or null if the attribute name is + * unknown + */ + function &get($property) + { + // only return if $property is known + if (isset($this->_properties[$property])) { + return $this->$property; + } + $tmp = null; + return $tmp; + } + + // }}} + // {{{ _DB_storage() + + /** + * Destructor, calls DB_storage::store() if there are changes + * that are to be kept. + */ + function _DB_storage() + { + if (sizeof($this->_changes)) { + $this->store(); + } + $this->_properties = array(); + $this->_changes = array(); + $this->_table = null; + } + + // }}} + // {{{ store() + + /** + * Stores changes to this object in the database. + * + * @return DB_OK or a DB error + */ + function store() + { + foreach ($this->_changes as $name => $foo) { + $params[] = &$this->$name; + $vars[] = $name . ' = ?'; + } + if ($vars) { + $query = 'UPDATE ' . $this->_table . ' SET ' . + implode(', ', $vars) . ' WHERE ' . + $this->_makeWhere(); + $stmt = $this->_dbh->prepare($query); + $res = $this->_dbh->execute($stmt, $params); + if (DB::isError($res)) { + return $res; + } + $this->_changes = array(); + } + return DB_OK; + } + + // }}} + // {{{ remove() + + /** + * Remove the row represented by this object from the database. + * + * @return mixed DB_OK or a DB error + */ + function remove() + { + if ($this->_readonly) { + return $this->raiseError(null, DB_WARNING_READ_ONLY, null, + null, null, null, true); + } + $query = 'DELETE FROM ' . $this->_table .' WHERE '. + $this->_makeWhere(); + $res = $this->_dbh->query($query); + if (DB::isError($res)) { + return $res; + } + foreach ($this->_properties as $prop => $foo) { + unset($this->$prop); + } + $this->_properties = array(); + $this->_changes = array(); + return DB_OK; + } + + // }}} +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/DB/sybase.php b/src/www/lib/pear/DB/sybase.php new file mode 100644 index 00000000..8091a0cc --- /dev/null +++ b/src/www/lib/pear/DB/sybase.php @@ -0,0 +1,907 @@ + + * @author Antônio Carlos Venâncio Júnior + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version CVS: $Id: sybase.php,v 1.1 2005/05/28 01:55:11 henrique Exp $ + * @link http://pear.php.net/package/DB + */ + +/** + * Obtain the DB_common class so it can be extended from + */ +require_once 'DB/common.php'; + +/** + * The methods PEAR DB uses to interact with PHP's sybase extension + * for interacting with Sybase databases + * + * These methods overload the ones declared in DB_common. + * + * WARNING: This driver may fail with multiple connections under the + * same user/pass/host and different databases. + * + * @category Database + * @package DB + * @author Sterling Hughes + * @author Antônio Carlos Venâncio Júnior + * @author Daniel Convissor + * @copyright 1997-2005 The PHP Group + * @license http://www.php.net/license/3_0.txt PHP License 3.0 + * @version Release: @package_version@ + * @link http://pear.php.net/package/DB + */ +class DB_sybase extends DB_common +{ + // {{{ properties + + /** + * The DB driver type (mysql, oci8, odbc, etc.) + * @var string + */ + var $phptype = 'sybase'; + + /** + * The database syntax variant to be used (db2, access, etc.), if any + * @var string + */ + var $dbsyntax = 'sybase'; + + /** + * The capabilities of this DB implementation + * + * The 'new_link' element contains the PHP version that first provided + * new_link support for this DBMS. Contains false if it's unsupported. + * + * Meaning of the 'limit' element: + * + 'emulate' = emulate with fetch row by number + * + 'alter' = alter the query + * + false = skip rows + * + * @var array + */ + var $features = array( + 'limit' => 'emulate', + 'new_link' => false, + 'numrows' => true, + 'pconnect' => true, + 'prepare' => false, + 'ssl' => false, + 'transactions' => true, + ); + + /** + * A mapping of native error codes to DB error codes + * @var array + */ + var $errorcode_map = array( + ); + + /** + * The raw database connection created by PHP + * @var resource + */ + var $connection; + + /** + * The DSN information for connecting to a database + * @var array + */ + var $dsn = array(); + + + /** + * Should data manipulation queries be committed automatically? + * @var bool + * @access private + */ + var $autocommit = true; + + /** + * The quantity of transactions begun + * + * {@internal While this is private, it can't actually be designated + * private in PHP 5 because it is directly accessed in the test suite.}} + * + * @var integer + * @access private + */ + var $transaction_opcount = 0; + + /** + * The database specified in the DSN + * + * It's a fix to allow calls to different databases in the same script. + * + * @var string + * @access private + */ + var $_db = ''; + + + // }}} + // {{{ constructor + + /** + * This constructor calls $this->DB_common() + * + * @return void + */ + function DB_sybase() + { + $this->DB_common(); + } + + // }}} + // {{{ connect() + + /** + * Connect to the database server, log in and open the database + * + * Don't call this method directly. Use DB::connect() instead. + * + * PEAR DB's sybase driver supports the following extra DSN options: + * + appname The application name to use on this connection. + * Available since PEAR DB 1.7.0. + * + charset The character set to use on this connection. + * Available since PEAR DB 1.7.0. + * + * @param array $dsn the data source name + * @param bool $persistent should the connection be persistent? + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function connect($dsn, $persistent = false) + { + if (!PEAR::loadExtension('sybase') && + !PEAR::loadExtension('sybase_ct')) + { + return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); + } + + $this->dsn = $dsn; + if ($dsn['dbsyntax']) { + $this->dbsyntax = $dsn['dbsyntax']; + } + + $dsn['hostspec'] = $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost'; + $dsn['password'] = !empty($dsn['password']) ? $dsn['password'] : false; + $dsn['charset'] = isset($dsn['charset']) ? $dsn['charset'] : false; + $dsn['appname'] = isset($dsn['appname']) ? $dsn['appname'] : false; + + $connect_function = $persistent ? 'sybase_pconnect' : 'sybase_connect'; + + if ($dsn['username']) { + $this->connection = @$connect_function($dsn['hostspec'], + $dsn['username'], + $dsn['password'], + $dsn['charset'], + $dsn['appname']); + } else { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + 'The DSN did not contain a username.'); + } + + if (!$this->connection) { + return $this->raiseError(DB_ERROR_CONNECT_FAILED, + null, null, null, + @sybase_get_last_message()); + } + + if ($dsn['database']) { + if (!@sybase_select_db($dsn['database'], $this->connection)) { + return $this->raiseError(DB_ERROR_NODBSELECTED, + null, null, null, + @sybase_get_last_message()); + } + $this->_db = $dsn['database']; + } + + return DB_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Disconnects from the database server + * + * @return bool TRUE on success, FALSE on failure + */ + function disconnect() + { + $ret = @sybase_close($this->connection); + $this->connection = null; + return $ret; + } + + // }}} + // {{{ simpleQuery() + + /** + * Sends a query to the database server + * + * @param string the SQL query string + * + * @return mixed + a PHP result resrouce for successful SELECT queries + * + the DB_OK constant for other successful queries + * + a DB_Error object on failure + */ + function simpleQuery($query) + { + $ismanip = DB::isManip($query); + $this->last_query = $query; + if (!@sybase_select_db($this->_db, $this->connection)) { + return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); + } + $query = $this->modifyQuery($query); + if (!$this->autocommit && $ismanip) { + if ($this->transaction_opcount == 0) { + $result = @sybase_query('BEGIN TRANSACTION', $this->connection); + if (!$result) { + return $this->sybaseRaiseError(); + } + } + $this->transaction_opcount++; + } + $result = @sybase_query($query, $this->connection); + if (!$result) { + return $this->sybaseRaiseError(); + } + if (is_resource($result)) { + return $result; + } + // Determine which queries that should return data, and which + // should return an error code only. + return $ismanip ? DB_OK : $result; + } + + // }}} + // {{{ nextResult() + + /** + * Move the internal sybase result pointer to the next available result + * + * @param a valid sybase result resource + * + * @access public + * + * @return true if a result is available otherwise return false + */ + function nextResult($result) + { + return false; + } + + // }}} + // {{{ fetchInto() + + /** + * Places a row from the result set into the given array + * + * Formating of the array and the data therein are configurable. + * See DB_result::fetchInto() for more information. + * + * This method is not meant to be called directly. Use + * DB_result::fetchInto() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result the query result resource + * @param array $arr the referenced array to put the data in + * @param int $fetchmode how the resulting array should be indexed + * @param int $rownum the row number to fetch (0 = first row) + * + * @return mixed DB_OK on success, NULL when the end of a result set is + * reached or on failure + * + * @see DB_result::fetchInto() + */ + function fetchInto($result, &$arr, $fetchmode, $rownum = null) + { + if ($rownum !== null) { + if (!@sybase_data_seek($result, $rownum)) { + return null; + } + } + if ($fetchmode & DB_FETCHMODE_ASSOC) { + if (function_exists('sybase_fetch_assoc')) { + $arr = @sybase_fetch_assoc($result); + } else { + if ($arr = @sybase_fetch_array($result)) { + foreach ($arr as $key => $value) { + if (is_int($key)) { + unset($arr[$key]); + } + } + } + } + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { + $arr = array_change_key_case($arr, CASE_LOWER); + } + } else { + $arr = @sybase_fetch_row($result); + } + if (!$arr) { + return null; + } + if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { + $this->_rtrimArrayValues($arr); + } + if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { + $this->_convertNullArrayValuesToEmpty($arr); + } + return DB_OK; + } + + // }}} + // {{{ freeResult() + + /** + * Deletes the result set and frees the memory occupied by the result set + * + * This method is not meant to be called directly. Use + * DB_result::free() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return bool TRUE on success, FALSE if $result is invalid + * + * @see DB_result::free() + */ + function freeResult($result) + { + return @sybase_free_result($result); + } + + // }}} + // {{{ numCols() + + /** + * Gets the number of columns in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numCols() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of columns. A DB_Error object on failure. + * + * @see DB_result::numCols() + */ + function numCols($result) + { + $cols = @sybase_num_fields($result); + if (!$cols) { + return $this->sybaseRaiseError(); + } + return $cols; + } + + // }}} + // {{{ numRows() + + /** + * Gets the number of rows in a result set + * + * This method is not meant to be called directly. Use + * DB_result::numRows() instead. It can't be declared "protected" + * because DB_result is a separate object. + * + * @param resource $result PHP's query result resource + * + * @return int the number of rows. A DB_Error object on failure. + * + * @see DB_result::numRows() + */ + function numRows($result) + { + $rows = @sybase_num_rows($result); + if ($rows === false) { + return $this->sybaseRaiseError(); + } + return $rows; + } + + // }}} + // {{{ affectedRows() + + /** + * Determines the number of rows affected by a data maniuplation query + * + * 0 is returned for queries that don't manipulate data. + * + * @return int the number of rows. A DB_Error object on failure. + */ + function affectedRows() + { + if (DB::isManip($this->last_query)) { + $result = @sybase_affected_rows($this->connection); + } else { + $result = 0; + } + return $result; + } + + // }}} + // {{{ nextId() + + /** + * Returns the next free id in a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true, the seqence is automatically + * created if it does not exist + * + * @return int the next id number in the sequence. + * A DB_Error object on failure. + * + * @see DB_common::nextID(), DB_common::getSequenceName(), + * DB_sybase::createSequence(), DB_sybase::dropSequence() + */ + function nextId($seq_name, $ondemand = true) + { + $seqname = $this->getSequenceName($seq_name); + if (!@sybase_select_db($this->_db, $this->connection)) { + return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); + } + $repeat = 0; + do { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)"); + $this->popErrorHandling(); + if ($ondemand && DB::isError($result) && + ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE)) + { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $this->raiseError($result); + } + } elseif (!DB::isError($result)) { + $result =& $this->query("SELECT @@IDENTITY FROM $seqname"); + $repeat = 0; + } else { + $repeat = false; + } + } while ($repeat); + if (DB::isError($result)) { + return $this->raiseError($result); + } + $result = $result->fetchRow(DB_FETCHMODE_ORDERED); + return $result[0]; + } + + /** + * Creates a new sequence + * + * @param string $seq_name name of the new sequence + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::createSequence(), DB_common::getSequenceName(), + * DB_sybase::nextID(), DB_sybase::dropSequence() + */ + function createSequence($seq_name) + { + return $this->query('CREATE TABLE ' + . $this->getSequenceName($seq_name) + . ' (id numeric(10, 0) IDENTITY NOT NULL,' + . ' vapor int NULL)'); + } + + // }}} + // {{{ dropSequence() + + /** + * Deletes a sequence + * + * @param string $seq_name name of the sequence to be deleted + * + * @return int DB_OK on success. A DB_Error object on failure. + * + * @see DB_common::dropSequence(), DB_common::getSequenceName(), + * DB_sybase::nextID(), DB_sybase::createSequence() + */ + function dropSequence($seq_name) + { + return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); + } + + // }}} + // {{{ autoCommit() + + /** + * Enables or disables automatic commits + * + * @param bool $onoff true turns it on, false turns it off + * + * @return int DB_OK on success. A DB_Error object if the driver + * doesn't support auto-committing transactions. + */ + function autoCommit($onoff = false) + { + // XXX if $this->transaction_opcount > 0, we should probably + // issue a warning here. + $this->autocommit = $onoff ? true : false; + return DB_OK; + } + + // }}} + // {{{ commit() + + /** + * Commits the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function commit() + { + if ($this->transaction_opcount > 0) { + if (!@sybase_select_db($this->_db, $this->connection)) { + return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); + } + $result = @sybase_query('COMMIT', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->sybaseRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ rollback() + + /** + * Reverts the current transaction + * + * @return int DB_OK on success. A DB_Error object on failure. + */ + function rollback() + { + if ($this->transaction_opcount > 0) { + if (!@sybase_select_db($this->_db, $this->connection)) { + return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); + } + $result = @sybase_query('ROLLBACK', $this->connection); + $this->transaction_opcount = 0; + if (!$result) { + return $this->sybaseRaiseError(); + } + } + return DB_OK; + } + + // }}} + // {{{ sybaseRaiseError() + + /** + * Produces a DB_Error object regarding the current problem + * + * @param int $errno if the error is being manually raised pass a + * DB_ERROR* constant here. If this isn't passed + * the error information gathered from the DBMS. + * + * @return object the DB_Error object + * + * @see DB_common::raiseError(), + * DB_sybase::errorNative(), DB_sybase::errorCode() + */ + function sybaseRaiseError($errno = null) + { + $native = $this->errorNative(); + if ($errno === null) { + $errno = $this->errorCode($native); + } + return $this->raiseError($errno, null, null, null, $native); + } + + // }}} + // {{{ errorNative() + + /** + * Gets the DBMS' native error message produced by the last query + * + * @return string the DBMS' error message + */ + function errorNative() + { + return @sybase_get_last_message(); + } + + // }}} + // {{{ errorCode() + + /** + * Determines PEAR::DB error code from the database's text error message. + * + * @param string $errormsg error message returned from the database + * @return integer an error number from a DB error constant + */ + function errorCode($errormsg) + { + static $error_regexps; + if (!isset($error_regexps)) { + $error_regexps = array( + '/Incorrect syntax near/' + => DB_ERROR_SYNTAX, + '/^Unclosed quote before the character string [\"\'].*[\"\']\./' + => DB_ERROR_SYNTAX, + '/Implicit conversion (from datatype|of NUMERIC value)/i' + => DB_ERROR_INVALID_NUMBER, + '/Cannot drop the table [\"\'].+[\"\'], because it doesn\'t exist in the system catalogs\./' + => DB_ERROR_NOSUCHTABLE, + '/Only the owner of object [\"\'].+[\"\'] or a user with System Administrator \(SA\) role can run this command\./' + => DB_ERROR_ACCESS_VIOLATION, + '/^.+ permission denied on object .+, database .+, owner .+/' + => DB_ERROR_ACCESS_VIOLATION, + '/^.* permission denied, database .+, owner .+/' + => DB_ERROR_ACCESS_VIOLATION, + '/[^.*] not found\./' + => DB_ERROR_NOSUCHTABLE, + '/There is already an object named/' + => DB_ERROR_ALREADY_EXISTS, + '/Invalid column name/' + => DB_ERROR_NOSUCHFIELD, + '/does not allow null values/' + => DB_ERROR_CONSTRAINT_NOT_NULL, + '/Command has been aborted/' + => DB_ERROR_CONSTRAINT, + '/^Cannot drop the index .* because it doesn\'t exist/i' + => DB_ERROR_NOT_FOUND, + '/^There is already an index/i' + => DB_ERROR_ALREADY_EXISTS, + '/^There are fewer columns in the INSERT statement than values specified/i' + => DB_ERROR_VALUE_COUNT_ON_ROW, + ); + } + + foreach ($error_regexps as $regexp => $code) { + if (preg_match($regexp, $errormsg)) { + return $code; + } + } + return DB_ERROR; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * NOTE: only supports 'table' and 'flags' if $result + * is a table name. + * + * @param object|string $result DB_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A DB_Error object on failure. + * + * @see DB_common::tableInfo() + * @since Method available since Release 1.6.0 + */ + function tableInfo($result, $mode = null) + { + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + if (!@sybase_select_db($this->_db, $this->connection)) { + return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); + } + $id = @sybase_query("SELECT * FROM $result WHERE 1=0", + $this->connection); + $got_string = true; + } elseif (isset($result->result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->result; + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $this->sybaseRaiseError(DB_ERROR_NEED_MORE_DATA); + } + + if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { + $case_func = 'strtolower'; + } else { + $case_func = 'strval'; + } + + $count = @sybase_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $f = @sybase_fetch_field($id, $i); + // column_source is often blank + $res[$i] = array( + 'table' => $got_string + ? $case_func($result) + : $case_func($f->column_source), + 'name' => $case_func($f->name), + 'type' => $f->type, + 'len' => $f->max_length, + 'flags' => '', + ); + if ($res[$i]['table']) { + $res[$i]['flags'] = $this->_sybase_field_flags( + $res[$i]['table'], $res[$i]['name']); + } + if ($mode & DB_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & DB_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @sybase_free_result($id); + } + return $res; + } + + // }}} + // {{{ _sybase_field_flags() + + /** + * Get the flags for a field + * + * Currently supports: + * + unique_key (unique index, unique check or primary_key) + * + multiple_key (multi-key index) + * + * @param string $table the table name + * @param string $column the field name + * + * @return string space delimited string of flags. Empty string if none. + * + * @access private + */ + function _sybase_field_flags($table, $column) + { + static $tableName = null; + static $flags = array(); + + if ($table != $tableName) { + $flags = array(); + $tableName = $table; + + // get unique/primary keys + $res = $this->getAll("sp_helpindex $table", DB_FETCHMODE_ASSOC); + + if (!isset($res[0]['index_description'])) { + return ''; + } + + foreach ($res as $val) { + $keys = explode(', ', trim($val['index_keys'])); + + if (sizeof($keys) > 1) { + foreach ($keys as $key) { + $this->_add_flag($flags[$key], 'multiple_key'); + } + } + + if (strpos($val['index_description'], 'unique')) { + foreach ($keys as $key) { + $this->_add_flag($flags[$key], 'unique_key'); + } + } + } + + } + + if (array_key_exists($column, $flags)) { + return(implode(' ', $flags[$column])); + } + + return ''; + } + + // }}} + // {{{ _add_flag() + + /** + * Adds a string to the flags array if the flag is not yet in there + * - if there is no flag present the array is created + * + * @param array $array reference of flags array to add a value to + * @param mixed $value value to add to the flag array + * + * @return void + * + * @access private + */ + function _add_flag(&$array, $value) + { + if (!is_array($array)) { + $array = array($value); + } elseif (!in_array($value, $array)) { + array_push($array, $value); + } + } + + // }}} + // {{{ getSpecialQuery() + + /** + * Obtains the query string needed for listing a given type of objects + * + * @param string $type the kind of objects you want to retrieve + * + * @return string the SQL query string or null if the driver doesn't + * support the object type requested + * + * @access protected + * @see DB_common::getListOf() + */ + function getSpecialQuery($type) + { + switch ($type) { + case 'tables': + return "SELECT name FROM sysobjects WHERE type = 'U'" + . ' ORDER BY name'; + case 'views': + return "SELECT name FROM sysobjects WHERE type = 'V'"; + default: + return null; + } + } + + // }}} + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + +?> diff --git a/src/www/lib/pear/OS/Guess.php b/src/www/lib/pear/OS/Guess.php new file mode 100644 index 00000000..39ae8ad4 --- /dev/null +++ b/src/www/lib/pear/OS/Guess.php @@ -0,0 +1,287 @@ + | +// | | +// +----------------------------------------------------------------------+ +// +// $Id: Guess.php,v 1.1 2005/05/28 01:55:11 henrique Exp $ + +// {{{ uname examples + +// php_uname() without args returns the same as 'uname -a', or a PHP-custom +// string for Windows. +// PHP versions prior to 4.3 return the uname of the host where PHP was built, +// as of 4.3 it returns the uname of the host running the PHP code. +// +// PC RedHat Linux 7.1: +// Linux host.example.com 2.4.2-2 #1 Sun Apr 8 20:41:30 EDT 2001 i686 unknown +// +// PC Debian Potato: +// Linux host 2.4.17 #2 SMP Tue Feb 12 15:10:04 CET 2002 i686 unknown +// +// PC FreeBSD 3.3: +// FreeBSD host.example.com 3.3-STABLE FreeBSD 3.3-STABLE #0: Mon Feb 21 00:42:31 CET 2000 root@example.com:/usr/src/sys/compile/CONFIG i386 +// +// PC FreeBSD 4.3: +// FreeBSD host.example.com 4.3-RELEASE FreeBSD 4.3-RELEASE #1: Mon Jun 25 11:19:43 EDT 2001 root@example.com:/usr/src/sys/compile/CONFIG i386 +// +// PC FreeBSD 4.5: +// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb 6 23:59:23 CET 2002 root@example.com:/usr/src/sys/compile/CONFIG i386 +// +// PC FreeBSD 4.5 w/uname from GNU shellutils: +// FreeBSD host.example.com 4.5-STABLE FreeBSD 4.5-STABLE #0: Wed Feb i386 unknown +// +// HP 9000/712 HP-UX 10: +// HP-UX iq B.10.10 A 9000/712 2008429113 two-user license +// +// HP 9000/712 HP-UX 10 w/uname from GNU shellutils: +// HP-UX host B.10.10 A 9000/712 unknown +// +// IBM RS6000/550 AIX 4.3: +// AIX host 3 4 000003531C00 +// +// AIX 4.3 w/uname from GNU shellutils: +// AIX host 3 4 000003531C00 unknown +// +// SGI Onyx IRIX 6.5 w/uname from GNU shellutils: +// IRIX64 host 6.5 01091820 IP19 mips +// +// SGI Onyx IRIX 6.5: +// IRIX64 host 6.5 01091820 IP19 +// +// SparcStation 20 Solaris 8 w/uname from GNU shellutils: +// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc +// +// SparcStation 20 Solaris 8: +// SunOS host.example.com 5.8 Generic_108528-12 sun4m sparc SUNW,SPARCstation-20 +// +// Mac OS X (Darwin) +// Darwin home-eden.local 7.5.0 Darwin Kernel Version 7.5.0: Thu Aug 5 19:26:16 PDT 2004; root:xnu/xnu-517.7.21.obj~3/RELEASE_PPC Power Macintosh +// +// Mac OS X early versions +// + +// }}} + +/* TODO: + * - define endianness, to allow matchSignature("bigend") etc. + */ + +class OS_Guess +{ + var $sysname; + var $nodename; + var $cpu; + var $release; + var $extra; + + function OS_Guess($uname = null) + { + list($this->sysname, + $this->release, + $this->cpu, + $this->extra, + $this->nodename) = $this->parseSignature($uname); + } + + function parseSignature($uname = null) + { + static $sysmap = array( + 'HP-UX' => 'hpux', + 'IRIX64' => 'irix', + ); + static $cpumap = array( + 'i586' => 'i386', + 'i686' => 'i386', + 'ppc' => 'powerpc', + ); + if ($uname === null) { + $uname = php_uname(); + } + $parts = split('[[:space:]]+', trim($uname)); + $n = count($parts); + + $release = $machine = $cpu = ''; + $sysname = $parts[0]; + $nodename = $parts[1]; + $cpu = $parts[$n-1]; + $extra = ''; + if ($cpu == 'unknown') { + $cpu = $parts[$n-2]; + } + + switch ($sysname) { + case 'AIX': + $release = "$parts[3].$parts[2]"; + break; + case 'Windows': + switch ($parts[1]) { + case '95/98': + $release = '9x'; + break; + default: + $release = $parts[1]; + break; + } + $cpu = 'i386'; + break; + case 'Linux': + $extra = $this->_detectGlibcVersion(); + // use only the first two digits from the kernel version + $release = ereg_replace('^([[:digit:]]+\.[[:digit:]]+).*', '\1', $parts[2]); + break; + case 'Mac' : + $sysname = 'darwin'; + $nodename = $parts[2]; + $release = $parts[3]; + if ($cpu == 'Macintosh') { + if ($parts[$n - 2] == 'Power') { + $cpu = 'powerpc'; + } + } + break; + case 'Darwin' : + if ($cpu == 'Macintosh') { + if ($parts[$n - 2] == 'Power') { + $cpu = 'powerpc'; + } + } + $release = ereg_replace('^([[:digit:]]+\.[[:digit:]]+).*', '\1', $parts[2]); + break; + default: + $release = ereg_replace('-.*', '', $parts[2]); + break; + } + + + if (isset($sysmap[$sysname])) { + $sysname = $sysmap[$sysname]; + } else { + $sysname = strtolower($sysname); + } + if (isset($cpumap[$cpu])) { + $cpu = $cpumap[$cpu]; + } + return array($sysname, $release, $cpu, $extra, $nodename); + } + + function _detectGlibcVersion() + { + // Use glibc's header file to + // get major and minor version number: + include_once "System.php"; + $tmpfile = System::mktemp("glibctest"); + $fp = fopen($tmpfile, "w"); + fwrite($fp, "#include \n__GLIBC__ __GLIBC_MINOR__\n"); + fclose($fp); + $cpp = popen("/usr/bin/cpp $tmpfile", "r"); + $major = $minor = 0; + while ($line = fgets($cpp, 1024)) { + if ($line{0} == '#' || trim($line) == '') { + continue; + } + if (list($major, $minor) = explode(' ', trim($line))) { + break; + } + } + pclose($cpp); + unlink($tmpfile); + if (!($major && $minor) && is_link('/lib/libc.so.6')) { + // Let's try reading the libc.so.6 symlink + if (ereg('^libc-([.*])\.so$', basename(readlink('/lib/libc.so.6')), $matches)) { + list($major, $minor) = explode('.', $matches); + } + } + if (!($major && $minor)) { + return ''; + } + return "glibc{$major}.{$minor}"; + } + + function getSignature() + { + if (empty($this->extra)) { + return "{$this->sysname}-{$this->release}-{$this->cpu}"; + } + return "{$this->sysname}-{$this->release}-{$this->cpu}-{$this->extra}"; + } + + function getSysname() + { + return $this->sysname; + } + + function getNodename() + { + return $this->nodename; + } + + function getCpu() + { + return $this->cpu; + } + + function getRelease() + { + return $this->release; + } + + function getExtra() + { + return $this->extra; + } + + function matchSignature($match) + { + if (is_array($match)) { + $fragments = $match; + } else { + $fragments = explode('-', $match); + } + $n = count($fragments); + $matches = 0; + if ($n > 0) { + $matches += $this->_matchFragment($fragments[0], $this->sysname); + } + if ($n > 1) { + $matches += $this->_matchFragment($fragments[1], $this->release); + } + if ($n > 2) { + $matches += $this->_matchFragment($fragments[2], $this->cpu); + } + if ($n > 3) { + $matches += $this->_matchFragment($fragments[3], $this->extra); + } + return ($matches == $n); + } + + function _matchFragment($fragment, $value) + { + if (strcspn($fragment, '*?') < strlen($fragment)) { + $reg = '^' . str_replace(array('*', '?', '/'), array('.*', '.', '\\/'), $fragment) . '$'; + return eregi($reg, $value); + } + return ($fragment == '*' || !strcasecmp($fragment, $value)); + } + +} +/* + * Local Variables: + * indent-tabs-mode: nil + * c-basic-offset: 4 + * End: + */ +?> diff --git a/src/www/lib/pear/PEAR.php b/src/www/lib/pear/PEAR.php new file mode 100644 index 00000000..a02c29a2 --- /dev/null +++ b/src/www/lib/pear/PEAR.php @@ -0,0 +1,1055 @@ + | +// | Stig Bakken | +// | Tomas V.V.Cox | +// +--------------------------------------------------------------------+ +// +// $Id: PEAR.php,v 1.1 2005/05/28 01:55:09 henrique Exp $ +// + +define('PEAR_ERROR_RETURN', 1); +define('PEAR_ERROR_PRINT', 2); +define('PEAR_ERROR_TRIGGER', 4); +define('PEAR_ERROR_DIE', 8); +define('PEAR_ERROR_CALLBACK', 16); +/** + * WARNING: obsolete + * @deprecated + */ +define('PEAR_ERROR_EXCEPTION', 32); +define('PEAR_ZE2', (function_exists('version_compare') && + version_compare(zend_version(), "2-dev", "ge"))); + +if (substr(PHP_OS, 0, 3) == 'WIN') { + define('OS_WINDOWS', true); + define('OS_UNIX', false); + define('PEAR_OS', 'Windows'); +} else { + define('OS_WINDOWS', false); + define('OS_UNIX', true); + define('PEAR_OS', 'Unix'); // blatant assumption +} + +// instant backwards compatibility +if (!defined('PATH_SEPARATOR')) { + if (OS_WINDOWS) { + define('PATH_SEPARATOR', ';'); + } else { + define('PATH_SEPARATOR', ':'); + } +} + +$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN; +$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE; +$GLOBALS['_PEAR_destructor_object_list'] = array(); +$GLOBALS['_PEAR_shutdown_funcs'] = array(); +$GLOBALS['_PEAR_error_handler_stack'] = array(); + +@ini_set('track_errors', true); + +/** + * Base class for other PEAR classes. Provides rudimentary + * emulation of destructors. + * + * If you want a destructor in your class, inherit PEAR and make a + * destructor method called _yourclassname (same name as the + * constructor, but with a "_" prefix). Also, in your constructor you + * have to call the PEAR constructor: $this->PEAR();. + * The destructor method will be called without parameters. Note that + * at in some SAPI implementations (such as Apache), any output during + * the request shutdown (in which destructors are called) seems to be + * discarded. If you need to get any debug information from your + * destructor, use error_log(), syslog() or something similar. + * + * IMPORTANT! To use the emulated destructors you need to create the + * objects by reference: $obj =& new PEAR_child; + * + * @since PHP 4.0.2 + * @author Stig Bakken + * @see http://pear.php.net/manual/ + */ +class PEAR +{ + // {{{ properties + + /** + * Whether to enable internal debug messages. + * + * @var bool + * @access private + */ + var $_debug = false; + + /** + * Default error mode for this object. + * + * @var int + * @access private + */ + var $_default_error_mode = null; + + /** + * Default error options used for this object when error mode + * is PEAR_ERROR_TRIGGER. + * + * @var int + * @access private + */ + var $_default_error_options = null; + + /** + * Default error handler (callback) for this object, if error mode is + * PEAR_ERROR_CALLBACK. + * + * @var string + * @access private + */ + var $_default_error_handler = ''; + + /** + * Which class to use for error objects. + * + * @var string + * @access private + */ + var $_error_class = 'PEAR_Error'; + + /** + * An array of expected errors. + * + * @var array + * @access private + */ + var $_expected_errors = array(); + + // }}} + + // {{{ constructor + + /** + * Constructor. Registers this object in + * $_PEAR_destructor_object_list for destructor emulation if a + * destructor object exists. + * + * @param string $error_class (optional) which class to use for + * error objects, defaults to PEAR_Error. + * @access public + * @return void + */ + function PEAR($error_class = null) + { + $classname = strtolower(get_class($this)); + if ($this->_debug) { + print "PEAR constructor called, class=$classname\n"; + } + if ($error_class !== null) { + $this->_error_class = $error_class; + } + while ($classname && strcasecmp($classname, "pear")) { + $destructor = "_$classname"; + if (method_exists($this, $destructor)) { + global $_PEAR_destructor_object_list; + $_PEAR_destructor_object_list[] = &$this; + if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) { + register_shutdown_function("_PEAR_call_destructors"); + $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true; + } + break; + } else { + $classname = get_parent_class($classname); + } + } + } + + // }}} + // {{{ destructor + + /** + * Destructor (the emulated type of...). Does nothing right now, + * but is included for forward compatibility, so subclass + * destructors should always call it. + * + * See the note in the class desciption about output from + * destructors. + * + * @access public + * @return void + */ + function _PEAR() { + if ($this->_debug) { + printf("PEAR destructor called, class=%s\n", strtolower(get_class($this))); + } + } + + // }}} + // {{{ getStaticProperty() + + /** + * If you have a class that's mostly/entirely static, and you need static + * properties, you can use this method to simulate them. Eg. in your method(s) + * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar'); + * You MUST use a reference, or they will not persist! + * + * @access public + * @param string $class The calling classname, to prevent clashes + * @param string $var The variable to retrieve. + * @return mixed A reference to the variable. If not set it will be + * auto initialised to NULL. + */ + function &getStaticProperty($class, $var) + { + static $properties; + return $properties[$class][$var]; + } + + // }}} + // {{{ registerShutdownFunc() + + /** + * Use this function to register a shutdown method for static + * classes. + * + * @access public + * @param mixed $func The function name (or array of class/method) to call + * @param mixed $args The arguments to pass to the function + * @return void + */ + function registerShutdownFunc($func, $args = array()) + { + $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args); + } + + // }}} + // {{{ isError() + + /** + * Tell whether a value is a PEAR error. + * + * @param mixed $data the value to test + * @param int $code if $data is an error object, return true + * only if $code is a string and + * $obj->getMessage() == $code or + * $code is an integer and $obj->getCode() == $code + * @access public + * @return bool true if parameter is an error + */ + function isError($data, $code = null) + { + if (is_a($data, 'PEAR_Error')) { + if (is_null($code)) { + return true; + } elseif (is_string($code)) { + return $data->getMessage() == $code; + } else { + return $data->getCode() == $code; + } + } + return false; + } + + // }}} + // {{{ setErrorHandling() + + /** + * Sets how errors generated by this object should be handled. + * Can be invoked both in objects and statically. If called + * statically, setErrorHandling sets the default behaviour for all + * PEAR objects. If called in an object, setErrorHandling sets + * the default behaviour for that object. + * + * @param int $mode + * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, + * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, + * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION. + * + * @param mixed $options + * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one + * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * + * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected + * to be the callback function or method. A callback + * function is a string with the name of the function, a + * callback method is an array of two elements: the element + * at index 0 is the object, and the element at index 1 is + * the name of the method to call in the object. + * + * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is + * a printf format string used when printing the error + * message. + * + * @access public + * @return void + * @see PEAR_ERROR_RETURN + * @see PEAR_ERROR_PRINT + * @see PEAR_ERROR_TRIGGER + * @see PEAR_ERROR_DIE + * @see PEAR_ERROR_CALLBACK + * @see PEAR_ERROR_EXCEPTION + * + * @since PHP 4.0.5 + */ + + function setErrorHandling($mode = null, $options = null) + { + if (isset($this) && is_a($this, 'PEAR')) { + $setmode = &$this->_default_error_mode; + $setoptions = &$this->_default_error_options; + } else { + $setmode = &$GLOBALS['_PEAR_default_error_mode']; + $setoptions = &$GLOBALS['_PEAR_default_error_options']; + } + + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $setmode = $mode; + $setoptions = $options; + break; + + case PEAR_ERROR_CALLBACK: + $setmode = $mode; + // class/object method callback + if (is_callable($options)) { + $setoptions = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + } + + // }}} + // {{{ expectError() + + /** + * This method is used to tell which errors you expect to get. + * Expected errors are always returned with error mode + * PEAR_ERROR_RETURN. Expected error codes are stored in a stack, + * and this method pushes a new element onto it. The list of + * expected errors are in effect until they are popped off the + * stack with the popExpect() method. + * + * Note that this method can not be called statically + * + * @param mixed $code a single error code or an array of error codes to expect + * + * @return int the new depth of the "expected errors" stack + * @access public + */ + function expectError($code = '*') + { + if (is_array($code)) { + array_push($this->_expected_errors, $code); + } else { + array_push($this->_expected_errors, array($code)); + } + return sizeof($this->_expected_errors); + } + + // }}} + // {{{ popExpect() + + /** + * This method pops one element off the expected error codes + * stack. + * + * @return array the list of error codes that were popped + */ + function popExpect() + { + return array_pop($this->_expected_errors); + } + + // }}} + // {{{ _checkDelExpect() + + /** + * This method checks unsets an error code if available + * + * @param mixed error code + * @return bool true if the error code was unset, false otherwise + * @access private + * @since PHP 4.3.0 + */ + function _checkDelExpect($error_code) + { + $deleted = false; + + foreach ($this->_expected_errors AS $key => $error_array) { + if (in_array($error_code, $error_array)) { + unset($this->_expected_errors[$key][array_search($error_code, $error_array)]); + $deleted = true; + } + + // clean up empty arrays + if (0 == count($this->_expected_errors[$key])) { + unset($this->_expected_errors[$key]); + } + } + return $deleted; + } + + // }}} + // {{{ delExpect() + + /** + * This method deletes all occurences of the specified element from + * the expected error codes stack. + * + * @param mixed $error_code error code that should be deleted + * @return mixed list of error codes that were deleted or error + * @access public + * @since PHP 4.3.0 + */ + function delExpect($error_code) + { + $deleted = false; + + if ((is_array($error_code) && (0 != count($error_code)))) { + // $error_code is a non-empty array here; + // we walk through it trying to unset all + // values + foreach($error_code as $key => $error) { + if ($this->_checkDelExpect($error)) { + $deleted = true; + } else { + $deleted = false; + } + } + return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } elseif (!empty($error_code)) { + // $error_code comes alone, trying to unset it + if ($this->_checkDelExpect($error_code)) { + return true; + } else { + return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME + } + } else { + // $error_code is empty + return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME + } + } + + // }}} + // {{{ raiseError() + + /** + * This method is a wrapper that returns an instance of the + * configured error class with this object's default error + * handling applied. If the $mode and $options parameters are not + * specified, the object's defaults are used. + * + * @param mixed $message a text error message or a PEAR error object + * + * @param int $code a numeric error code (it is up to your class + * to define these if you want to use codes) + * + * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, + * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, + * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION. + * + * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter + * specifies the PHP-internal error level (one of + * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). + * If $mode is PEAR_ERROR_CALLBACK, this + * parameter specifies the callback function or + * method. In other error modes this parameter + * is ignored. + * + * @param string $userinfo If you need to pass along for example debug + * information, this parameter is meant for that. + * + * @param string $error_class The returned error object will be + * instantiated from this class, if specified. + * + * @param bool $skipmsg If true, raiseError will only pass error codes, + * the error message parameter will be dropped. + * + * @access public + * @return object a PEAR error object + * @see PEAR::setErrorHandling + * @since PHP 4.0.5 + */ + function raiseError($message = null, + $code = null, + $mode = null, + $options = null, + $userinfo = null, + $error_class = null, + $skipmsg = false) + { + // The error is yet a PEAR error object + if (is_object($message)) { + $code = $message->getCode(); + $userinfo = $message->getUserInfo(); + $error_class = $message->getType(); + $message->error_message_prefix = ''; + $message = $message->getMessage(); + } + + if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) { + if ($exp[0] == "*" || + (is_int(reset($exp)) && in_array($code, $exp)) || + (is_string(reset($exp)) && in_array($message, $exp))) { + $mode = PEAR_ERROR_RETURN; + } + } + // No mode given, try global ones + if ($mode === null) { + // Class error handler + if (isset($this) && isset($this->_default_error_mode)) { + $mode = $this->_default_error_mode; + $options = $this->_default_error_options; + // Global error handler + } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) { + $mode = $GLOBALS['_PEAR_default_error_mode']; + $options = $GLOBALS['_PEAR_default_error_options']; + } + } + + if ($error_class !== null) { + $ec = $error_class; + } elseif (isset($this) && isset($this->_error_class)) { + $ec = $this->_error_class; + } else { + $ec = 'PEAR_Error'; + } + if ($skipmsg) { + return new $ec($code, $mode, $options, $userinfo); + } else { + return new $ec($message, $code, $mode, $options, $userinfo); + } + } + + // }}} + // {{{ throwError() + + /** + * Simpler form of raiseError with fewer options. In most cases + * message, code and userinfo are enough. + * + * @param string $message + * + */ + function throwError($message = null, + $code = null, + $userinfo = null) + { + if (isset($this) && is_a($this, 'PEAR')) { + return $this->raiseError($message, $code, null, null, $userinfo); + } else { + return PEAR::raiseError($message, $code, null, null, $userinfo); + } + } + + // }}} + function staticPushErrorHandling($mode, $options = null) + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + $def_mode = &$GLOBALS['_PEAR_default_error_mode']; + $def_options = &$GLOBALS['_PEAR_default_error_options']; + $stack[] = array($def_mode, $def_options); + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $def_mode = $mode; + $def_options = $options; + break; + + case PEAR_ERROR_CALLBACK: + $def_mode = $mode; + // class/object method callback + if (is_callable($options)) { + $def_options = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + $stack[] = array($mode, $options); + return true; + } + + function staticPopErrorHandling() + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + $setmode = &$GLOBALS['_PEAR_default_error_mode']; + $setoptions = &$GLOBALS['_PEAR_default_error_options']; + array_pop($stack); + list($mode, $options) = $stack[sizeof($stack) - 1]; + array_pop($stack); + switch ($mode) { + case PEAR_ERROR_EXCEPTION: + case PEAR_ERROR_RETURN: + case PEAR_ERROR_PRINT: + case PEAR_ERROR_TRIGGER: + case PEAR_ERROR_DIE: + case null: + $setmode = $mode; + $setoptions = $options; + break; + + case PEAR_ERROR_CALLBACK: + $setmode = $mode; + // class/object method callback + if (is_callable($options)) { + $setoptions = $options; + } else { + trigger_error("invalid error callback", E_USER_WARNING); + } + break; + + default: + trigger_error("invalid error mode", E_USER_WARNING); + break; + } + return true; + } + + // {{{ pushErrorHandling() + + /** + * Push a new error handler on top of the error handler options stack. With this + * you can easily override the actual error handler for some code and restore + * it later with popErrorHandling. + * + * @param mixed $mode (same as setErrorHandling) + * @param mixed $options (same as setErrorHandling) + * + * @return bool Always true + * + * @see PEAR::setErrorHandling + */ + function pushErrorHandling($mode, $options = null) + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + if (isset($this) && is_a($this, 'PEAR')) { + $def_mode = &$this->_default_error_mode; + $def_options = &$this->_default_error_options; + } else { + $def_mode = &$GLOBALS['_PEAR_default_error_mode']; + $def_options = &$GLOBALS['_PEAR_default_error_options']; + } + $stack[] = array($def_mode, $def_options); + + if (isset($this) && is_a($this, 'PEAR')) { + $this->setErrorHandling($mode, $options); + } else { + PEAR::setErrorHandling($mode, $options); + } + $stack[] = array($mode, $options); + return true; + } + + // }}} + // {{{ popErrorHandling() + + /** + * Pop the last error handler used + * + * @return bool Always true + * + * @see PEAR::pushErrorHandling + */ + function popErrorHandling() + { + $stack = &$GLOBALS['_PEAR_error_handler_stack']; + array_pop($stack); + list($mode, $options) = $stack[sizeof($stack) - 1]; + array_pop($stack); + if (isset($this) && is_a($this, 'PEAR')) { + $this->setErrorHandling($mode, $options); + } else { + PEAR::setErrorHandling($mode, $options); + } + return true; + } + + // }}} + // {{{ loadExtension() + + /** + * OS independant PHP extension load. Remember to take care + * on the correct extension name for case sensitive OSes. + * + * @param string $ext The extension name + * @return bool Success or not on the dl() call + */ + function loadExtension($ext) + { + if (!extension_loaded($ext)) { + // if either returns true dl() will produce a FATAL error, stop that + if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) { + return false; + } + if (OS_WINDOWS) { + $suffix = '.dll'; + } elseif (PHP_OS == 'HP-UX') { + $suffix = '.sl'; + } elseif (PHP_OS == 'AIX') { + $suffix = '.a'; + } elseif (PHP_OS == 'OSX') { + $suffix = '.bundle'; + } else { + $suffix = '.so'; + } + return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix); + } + return true; + } + + // }}} +} + +// {{{ _PEAR_call_destructors() + +function _PEAR_call_destructors() +{ + global $_PEAR_destructor_object_list; + if (is_array($_PEAR_destructor_object_list) && + sizeof($_PEAR_destructor_object_list)) + { + reset($_PEAR_destructor_object_list); + if (@PEAR::getStaticProperty('PEAR', 'destructlifo')) { + $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list); + } + while (list($k, $objref) = each($_PEAR_destructor_object_list)) { + $classname = get_class($objref); + while ($classname) { + $destructor = "_$classname"; + if (method_exists($objref, $destructor)) { + $objref->$destructor(); + break; + } else { + $classname = get_parent_class($classname); + } + } + } + // Empty the object list to ensure that destructors are + // not called more than once. + $_PEAR_destructor_object_list = array(); + } + + // Now call the shutdown functions + if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) { + foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) { + call_user_func_array($value[0], $value[1]); + } + } +} + +// }}} + +class PEAR_Error +{ + // {{{ properties + + var $error_message_prefix = ''; + var $mode = PEAR_ERROR_RETURN; + var $level = E_USER_NOTICE; + var $code = -1; + var $message = ''; + var $userinfo = ''; + var $backtrace = null; + + // }}} + // {{{ constructor + + /** + * PEAR_Error constructor + * + * @param string $message message + * + * @param int $code (optional) error code + * + * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN, + * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER, + * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION + * + * @param mixed $options (optional) error level, _OR_ in the case of + * PEAR_ERROR_CALLBACK, the callback function or object/method + * tuple. + * + * @param string $userinfo (optional) additional user/debug info + * + * @access public + * + */ + function PEAR_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + if ($mode === null) { + $mode = PEAR_ERROR_RETURN; + } + $this->message = $message; + $this->code = $code; + $this->mode = $mode; + $this->userinfo = $userinfo; + if (function_exists("debug_backtrace")) { + if (@!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) { + $this->backtrace = debug_backtrace(); + } + } + if ($mode & PEAR_ERROR_CALLBACK) { + $this->level = E_USER_NOTICE; + $this->callback = $options; + } else { + if ($options === null) { + $options = E_USER_NOTICE; + } + $this->level = $options; + $this->callback = null; + } + if ($this->mode & PEAR_ERROR_PRINT) { + if (is_null($options) || is_int($options)) { + $format = "%s"; + } else { + $format = $options; + } + printf($format, $this->getMessage()); + } + if ($this->mode & PEAR_ERROR_TRIGGER) { + trigger_error($this->getMessage(), $this->level); + } + if ($this->mode & PEAR_ERROR_DIE) { + $msg = $this->getMessage(); + if (is_null($options) || is_int($options)) { + $format = "%s"; + if (substr($msg, -1) != "\n") { + $msg .= "\n"; + } + } else { + $format = $options; + } + die(sprintf($format, $msg)); + } + if ($this->mode & PEAR_ERROR_CALLBACK) { + if (is_callable($this->callback)) { + call_user_func($this->callback, $this); + } + } + if ($this->mode & PEAR_ERROR_EXCEPTION) { + trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_ErrorStack for exceptions", E_USER_WARNING); + eval('$e = new Exception($this->message, $this->code);$e->PEAR_Error = $this;throw($e);'); + } + } + + // }}} + // {{{ getMode() + + /** + * Get the error mode from an error object. + * + * @return int error mode + * @access public + */ + function getMode() { + return $this->mode; + } + + // }}} + // {{{ getCallback() + + /** + * Get the callback function/method from an error object. + * + * @return mixed callback function or object/method array + * @access public + */ + function getCallback() { + return $this->callback; + } + + // }}} + // {{{ getMessage() + + + /** + * Get the error message from an error object. + * + * @return string full error message + * @access public + */ + function getMessage() + { + return ($this->error_message_prefix . $this->message); + } + + + // }}} + // {{{ getCode() + + /** + * Get error code from an error object + * + * @return int error code + * @access public + */ + function getCode() + { + return $this->code; + } + + // }}} + // {{{ getType() + + /** + * Get the name of this error/exception. + * + * @return string error/exception name (type) + * @access public + */ + function getType() + { + return get_class($this); + } + + // }}} + // {{{ getUserInfo() + + /** + * Get additional user-supplied information. + * + * @return string user-supplied information + * @access public + */ + function getUserInfo() + { + return $this->userinfo; + } + + // }}} + // {{{ getDebugInfo() + + /** + * Get additional debug information supplied by the application. + * + * @return string debug information + * @access public + */ + function getDebugInfo() + { + return $this->getUserInfo(); + } + + // }}} + // {{{ getBacktrace() + + /** + * Get the call backtrace from where the error was generated. + * Supported with PHP 4.3.0 or newer. + * + * @param int $frame (optional) what frame to fetch + * @return array Backtrace, or NULL if not available. + * @access public + */ + function getBacktrace($frame = null) + { + if ($frame === null) { + return $this->backtrace; + } + return $this->backtrace[$frame]; + } + + // }}} + // {{{ addUserInfo() + + function addUserInfo($info) + { + if (empty($this->userinfo)) { + $this->userinfo = $info; + } else { + $this->userinfo .= " ** $info"; + } + } + + // }}} + // {{{ toString() + + /** + * Make a string representation of this object. + * + * @return string a string with an object summary + * @access public + */ + function toString() { + $modes = array(); + $levels = array(E_USER_NOTICE => 'notice', + E_USER_WARNING => 'warning', + E_USER_ERROR => 'error'); + if ($this->mode & PEAR_ERROR_CALLBACK) { + if (is_array($this->callback)) { + $callback = (is_object($this->callback[0]) ? + strtolower(get_class($this->callback[0])) : + $this->callback[0]) . '::' . + $this->callback[1]; + } else { + $callback = $this->callback; + } + return sprintf('[%s: message="%s" code=%d mode=callback '. + 'callback=%s prefix="%s" info="%s"]', + strtolower(get_class($this)), $this->message, $this->code, + $callback, $this->error_message_prefix, + $this->userinfo); + } + if ($this->mode & PEAR_ERROR_PRINT) { + $modes[] = 'print'; + } + if ($this->mode & PEAR_ERROR_TRIGGER) { + $modes[] = 'trigger'; + } + if ($this->mode & PEAR_ERROR_DIE) { + $modes[] = 'die'; + } + if ($this->mode & PEAR_ERROR_RETURN) { + $modes[] = 'return'; + } + return sprintf('[%s: message="%s" code=%d mode=%s level=%s '. + 'prefix="%s" info="%s"]', + strtolower(get_class($this)), $this->message, $this->code, + implode("|", $modes), $levels[$this->level], + $this->error_message_prefix, + $this->userinfo); + } + + // }}} +} + +/* + * Local Variables: + * mode: php + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ +?> diff --git a/src/www/lib/pear/PEAR/Autoloader.php b/src/www/lib/pear/PEAR/Autoloader.php new file mode 100644 index 00000000..abde1bf4 --- /dev/null +++ b/src/www/lib/pear/PEAR/Autoloader.php @@ -0,0 +1,208 @@ + | +// | | +// +----------------------------------------------------------------------+ +// +// $Id: Autoloader.php,v 1.1 2005/05/28 01:55:12 henrique Exp $ + +if (!extension_loaded("overload")) { + // die hard without ext/overload + die("Rebuild PHP with the `overload' extension to use PEAR_Autoloader"); +} + +require_once "PEAR.php"; + +/** + * This class is for objects where you want to separate the code for + * some methods into separate classes. This is useful if you have a + * class with not-frequently-used methods that contain lots of code + * that you would like to avoid always parsing. + * + * The PEAR_Autoloader class provides autoloading and aggregation. + * The autoloading lets you set up in which classes the separated + * methods are found. Aggregation is the technique used to import new + * methods, an instance of each class providing separated methods is + * stored and called every time the aggregated method is called. + * + * @author Stig Sæther Bakken + */ +class PEAR_Autoloader extends PEAR +{ + // {{{ properties + + /** + * Map of methods and classes where they are defined + * + * @var array + * + * @access private + */ + var $_autoload_map = array(); + + /** + * Map of methods and aggregate objects + * + * @var array + * + * @access private + */ + var $_method_map = array(); + + // }}} + // {{{ addAutoload() + + /** + * Add one or more autoload entries. + * + * @param string $method which method to autoload + * + * @param string $classname (optional) which class to find the method in. + * If the $method parameter is an array, this + * parameter may be omitted (and will be ignored + * if not), and the $method parameter will be + * treated as an associative array with method + * names as keys and class names as values. + * + * @return void + * + * @access public + */ + function addAutoload($method, $classname = null) + { + if (is_array($method)) { + array_walk($method, create_function('$a,&$b', '$b = strtolower($b);')); + $this->_autoload_map = array_merge($this->_autoload_map, $method); + } else { + $this->_autoload_map[strtolower($method)] = $classname; + } + } + + // }}} + // {{{ removeAutoload() + + /** + * Remove an autoload entry. + * + * @param string $method which method to remove the autoload entry for + * + * @return bool TRUE if an entry was removed, FALSE if not + * + * @access public + */ + function removeAutoload($method) + { + $method = strtolower($method); + $ok = isset($this->_autoload_map[$method]); + unset($this->_autoload_map[$method]); + return $ok; + } + + // }}} + // {{{ addAggregateObject() + + /** + * Add an aggregate object to this object. If the specified class + * is not defined, loading it will be attempted following PEAR's + * file naming scheme. All the methods in the class will be + * aggregated, except private ones (name starting with an + * underscore) and constructors. + * + * @param string $classname what class to instantiate for the object. + * + * @return void + * + * @access public + */ + function addAggregateObject($classname) + { + $classname = strtolower($classname); + if (!class_exists($classname)) { + $include_file = preg_replace('/[^a-z0-9]/i', '_', $classname); + include_once $include_file; + } + $obj =& new $classname; + $methods = get_class_methods($classname); + foreach ($methods as $method) { + // don't import priviate methods and constructors + if ($method{0} != '_' && $method != $classname) { + $this->_method_map[$method] = $obj; + } + } + } + + // }}} + // {{{ removeAggregateObject() + + /** + * Remove an aggregate object. + * + * @param string $classname the class of the object to remove + * + * @return bool TRUE if an object was removed, FALSE if not + * + * @access public + */ + function removeAggregateObject($classname) + { + $ok = false; + $classname = strtolower($classname); + reset($this->_method_map); + while (list($method, $obj) = each($this->_method_map)) { + if (is_a($obj, $classname)) { + unset($this->_method_map[$method]); + $ok = true; + } + } + return $ok; + } + + // }}} + // {{{ __call() + + /** + * Overloaded object call handler, called each time an + * undefined/aggregated method is invoked. This method repeats + * the call in the right aggregate object and passes on the return + * value. + * + * @param string $method which method that was called + * + * @param string $args An array of the parameters passed in the + * original call + * + * @return mixed The return value from the aggregated method, or a PEAR + * error if the called method was unknown. + */ + function __call($method, $args, &$retval) + { + $method = strtolower($method); + if (empty($this->_method_map[$method]) && isset($this->_autoload_map[$method])) { + $this->addAggregateObject($this->_autoload_map[$method]); + } + if (isset($this->_method_map[$method])) { + $retval = call_user_func_array(array($this->_method_map[$method], $method), $args); + return true; + } + return false; + } + + // }}} +} + +overload("PEAR_Autoloader"); + +?> diff --git a/src/www/lib/pear/PEAR/Builder.php b/src/www/lib/pear/PEAR/Builder.php new file mode 100644 index 00000000..df415d9a --- /dev/null +++ b/src/www/lib/pear/PEAR/Builder.php @@ -0,0 +1,426 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Builder.php,v 1.1 2005/05/28 01:55:12 henrique Exp $ + +require_once 'PEAR/Common.php'; + +/** + * Class to handle building (compiling) extensions. + * + * @author Stig Sæther Bakken + */ +class PEAR_Builder extends PEAR_Common +{ + // {{{ properties + + var $php_api_version = 0; + var $zend_module_api_no = 0; + var $zend_extension_api_no = 0; + + var $extensions_built = array(); + + var $current_callback = null; + + // used for msdev builds + var $_lastline = null; + var $_firstline = null; + // }}} + // {{{ constructor + + /** + * PEAR_Builder constructor. + * + * @param object $ui user interface object (instance of PEAR_Frontend_*) + * + * @access public + */ + function PEAR_Builder(&$ui) + { + parent::PEAR_Common(); + $this->setFrontendObject($ui); + } + + // }}} + + // {{{ _build_win32() + + /** + * Build an extension from source on windows. + * requires msdev + */ + function _build_win32($descfile, $callback = null) + { + if (PEAR::isError($info = $this->infoFromDescriptionFile($descfile))) { + return $info; + } + $dir = dirname($descfile); + $old_cwd = getcwd(); + + if (!@chdir($dir)) { + return $this->raiseError("could not chdir to $dir"); + } + $this->log(2, "building in $dir"); + + $dsp = $info['package'].'.dsp'; + if (!@is_file("$dir/$dsp")) { + return $this->raiseError("The DSP $dsp does not exist."); + } + // XXX TODO: make release build type configurable + $command = 'msdev '.$dsp.' /MAKE "'.$info['package']. ' - Release"'; + + $this->current_callback = $callback; + $err = $this->_runCommand($command, array(&$this, 'msdevCallback')); + if (PEAR::isError($err)) { + return $err; + } + + // figure out the build platform and type + $platform = 'Win32'; + $buildtype = 'Release'; + if (preg_match('/.*?'.$info['package'].'\s-\s(\w+)\s(.*?)-+/i',$this->_firstline,$matches)) { + $platform = $matches[1]; + $buildtype = $matches[2]; + } + + if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/',$this->_lastline,$matches)) { + if ($matches[2]) { + // there were errors in the build + return $this->raiseError("There were errors during compilation."); + } + $out = $matches[1]; + } else { + return $this->raiseError("Did not understand the completion status returned from msdev.exe."); + } + + // msdev doesn't tell us the output directory :/ + // open the dsp, find /out and use that directory + $dsptext = join(file($dsp),''); + + // this regex depends on the build platform and type having been + // correctly identified above. + $regex ='/.*?!IF\s+"\$\(CFG\)"\s+==\s+("'. + $info['package'].'\s-\s'. + $platform.'\s'. + $buildtype.'").*?'. + '\/out:"(.*?)"/is'; + + if ($dsptext && preg_match($regex,$dsptext,$matches)) { + // what we get back is a relative path to the output file itself. + $outfile = realpath($matches[2]); + } else { + return $this->raiseError("Could not retrieve output information from $dsp."); + } + if (@copy($outfile, "$dir/$out")) { + $outfile = "$dir/$out"; + } + + $built_files[] = array( + 'file' => "$outfile", + 'php_api' => $this->php_api_version, + 'zend_mod_api' => $this->zend_module_api_no, + 'zend_ext_api' => $this->zend_extension_api_no, + ); + + return $built_files; + } + // }}} + + // {{{ msdevCallback() + function msdevCallback($what, $data) + { + if (!$this->_firstline) + $this->_firstline = $data; + $this->_lastline = $data; + } + // }}} + + // {{{ _harventInstDir + /** + * @param string + * @param string + * @param array + * @access private + */ + function _harvestInstDir($dest_prefix, $dirname, &$built_files) + { + $d = opendir($dirname); + if (!$d) + return false; + + $ret = true; + while (($ent = readdir($d)) !== false) { + if ($ent{0} == '.') + continue; + + $full = $dirname . DIRECTORY_SEPARATOR . $ent; + if (is_dir($full)) { + if (!$this->_harvestInstDir( + $dest_prefix . DIRECTORY_SEPARATOR . $ent, + $full, $built_files)) { + $ret = false; + break; + } + } else { + $dest = $dest_prefix . DIRECTORY_SEPARATOR . $ent; + $built_files[] = array( + 'file' => $full, + 'dest' => $dest, + 'php_api' => $this->php_api_version, + 'zend_mod_api' => $this->zend_module_api_no, + 'zend_ext_api' => $this->zend_extension_api_no, + ); + } + } + closedir($d); + return $ret; + } + + // }}} + + // {{{ build() + + /** + * Build an extension from source. Runs "phpize" in the source + * directory, but compiles in a temporary directory + * (/var/tmp/pear-build-USER/PACKAGE-VERSION). + * + * @param string $descfile path to XML package description file + * + * @param mixed $callback callback function used to report output, + * see PEAR_Builder::_runCommand for details + * + * @return array an array of associative arrays with built files, + * format: + * array( array( 'file' => '/path/to/ext.so', + * 'php_api' => YYYYMMDD, + * 'zend_mod_api' => YYYYMMDD, + * 'zend_ext_api' => YYYYMMDD ), + * ... ) + * + * @access public + * + * @see PEAR_Builder::_runCommand + * @see PEAR_Common::infoFromDescriptionFile + */ + function build($descfile, $callback = null) + { + if (PEAR_OS == "Windows") { + return $this->_build_win32($descfile,$callback); + } + if (PEAR_OS != 'Unix') { + return $this->raiseError("building extensions not supported on this platform"); + } + if (PEAR::isError($info = $this->infoFromDescriptionFile($descfile))) { + return $info; + } + $dir = dirname($descfile); + $old_cwd = getcwd(); + if (!@chdir($dir)) { + return $this->raiseError("could not chdir to $dir"); + } + $vdir = "$info[package]-$info[version]"; + if (is_dir($vdir)) { + chdir($vdir); + } + $dir = getcwd(); + $this->log(2, "building in $dir"); + $this->current_callback = $callback; + putenv('PATH=' . $this->config->get('bin_dir') . ':' . getenv('PATH')); + $err = $this->_runCommand("phpize", array(&$this, 'phpizeCallback')); + if (PEAR::isError($err)) { + return $err; + } + if (!$err) { + return $this->raiseError("`phpize' failed"); + } + + // {{{ start of interactive part + $configure_command = "$dir/configure"; + if (isset($info['configure_options'])) { + foreach ($info['configure_options'] as $o) { + list($r) = $this->ui->userDialog('build', + array($o['prompt']), + array('text'), + array(@$o['default'])); + if (substr($o['name'], 0, 5) == 'with-' && + ($r == 'yes' || $r == 'autodetect')) { + $configure_command .= " --$o[name]"; + } else { + $configure_command .= " --$o[name]=".trim($r); + } + } + } + // }}} end of interactive part + + // FIXME make configurable + if(!$user=getenv('USER')){ + $user='defaultuser'; + } + $build_basedir = "/var/tmp/pear-build-$user"; + $build_dir = "$build_basedir/$info[package]-$info[version]"; + $inst_dir = "$build_basedir/install-$info[package]-$info[version]"; + $this->log(1, "building in $build_dir"); + if (is_dir($build_dir)) { + System::rm('-rf', $build_dir); + } + if (!System::mkDir(array('-p', $build_dir))) { + return $this->raiseError("could not create build dir: $build_dir"); + } + $this->addTempFile($build_dir); + if (!System::mkDir(array('-p', $inst_dir))) { + return $this->raiseError("could not create temporary install dir: $inst_dir"); + } + $this->addTempFile($inst_dir); + + if (getenv('MAKE')) { + $make_command = getenv('MAKE'); + } else { + $make_command = 'make'; + } + $to_run = array( + $configure_command, + $make_command, + "$make_command INSTALL_ROOT=\"$inst_dir\" install", + "find \"$inst_dir\" -ls" + ); + if (!@chdir($build_dir)) { + return $this->raiseError("could not chdir to $build_dir"); + } + putenv('PHP_PEAR_VERSION=@PEAR-VER@'); + foreach ($to_run as $cmd) { + $err = $this->_runCommand($cmd, $callback); + if (PEAR::isError($err)) { + chdir($old_cwd); + return $err; + } + if (!$err) { + chdir($old_cwd); + return $this->raiseError("`$cmd' failed"); + } + } + if (!($dp = opendir("modules"))) { + chdir($old_cwd); + return $this->raiseError("no `modules' directory found"); + } + $built_files = array(); + $prefix = exec("php-config --prefix"); + $this->_harvestInstDir($prefix, $inst_dir . DIRECTORY_SEPARATOR . $prefix, $built_files); + chdir($old_cwd); + return $built_files; + } + + // }}} + // {{{ phpizeCallback() + + /** + * Message callback function used when running the "phpize" + * program. Extracts the API numbers used. Ignores other message + * types than "cmdoutput". + * + * @param string $what the type of message + * @param mixed $data the message + * + * @return void + * + * @access public + */ + function phpizeCallback($what, $data) + { + if ($what != 'cmdoutput') { + return; + } + $this->log(1, rtrim($data)); + if (preg_match('/You should update your .aclocal.m4/', $data)) { + return; + } + $matches = array(); + if (preg_match('/^\s+(\S[^:]+):\s+(\d{8})/', $data, $matches)) { + $member = preg_replace('/[^a-z]/', '_', strtolower($matches[1])); + $apino = (int)$matches[2]; + if (isset($this->$member)) { + $this->$member = $apino; + //$msg = sprintf("%-22s : %d", $matches[1], $apino); + //$this->log(1, $msg); + } + } + } + + // }}} + // {{{ _runCommand() + + /** + * Run an external command, using a message callback to report + * output. The command will be run through popen and output is + * reported for every line with a "cmdoutput" message with the + * line string, including newlines, as payload. + * + * @param string $command the command to run + * + * @param mixed $callback (optional) function to use as message + * callback + * + * @return bool whether the command was successful (exit code 0 + * means success, any other means failure) + * + * @access private + */ + function _runCommand($command, $callback = null) + { + $this->log(1, "running: $command"); + $pp = @popen("$command 2>&1", "r"); + if (!$pp) { + return $this->raiseError("failed to run `$command'"); + } + if ($callback && $callback[0]->debug == 1) { + $olddbg = $callback[0]->debug; + $callback[0]->debug = 2; + } + + while ($line = fgets($pp, 1024)) { + if ($callback) { + call_user_func($callback, 'cmdoutput', $line); + } else { + $this->log(2, rtrim($line)); + } + } + if ($callback && isset($olddbg)) { + $callback[0]->debug = $olddbg; + } + $exitcode = @pclose($pp); + return ($exitcode == 0); + } + + // }}} + // {{{ log() + + function log($level, $msg) + { + if ($this->current_callback) { + if ($this->debug >= $level) { + call_user_func($this->current_callback, 'output', $msg); + } + return; + } + return PEAR_Common::log($level, $msg); + } + + // }}} +} + +?> diff --git a/src/www/lib/pear/PEAR/Command.php b/src/www/lib/pear/PEAR/Command.php new file mode 100644 index 00000000..df18aac0 --- /dev/null +++ b/src/www/lib/pear/PEAR/Command.php @@ -0,0 +1,398 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Command.php,v 1.1 2005/05/28 01:55:12 henrique Exp $ + + +require_once "PEAR.php"; + +/** + * List of commands and what classes they are implemented in. + * @var array command => implementing class + */ +$GLOBALS['_PEAR_Command_commandlist'] = array(); + +/** + * List of shortcuts to common commands. + * @var array shortcut => command + */ +$GLOBALS['_PEAR_Command_shortcuts'] = array(); + +/** + * Array of command objects + * @var array class => object + */ +$GLOBALS['_PEAR_Command_objects'] = array(); + +/** + * Which user interface class is being used. + * @var string class name + */ +$GLOBALS['_PEAR_Command_uiclass'] = 'PEAR_Frontend_CLI'; + +/** + * Instance of $_PEAR_Command_uiclass. + * @var object + */ +$GLOBALS['_PEAR_Command_uiobject'] = null; + +/** + * PEAR command class, a simple factory class for administrative + * commands. + * + * How to implement command classes: + * + * - The class must be called PEAR_Command_Nnn, installed in the + * "PEAR/Common" subdir, with a method called getCommands() that + * returns an array of the commands implemented by the class (see + * PEAR/Command/Install.php for an example). + * + * - The class must implement a run() function that is called with three + * params: + * + * (string) command name + * (array) assoc array with options, freely defined by each + * command, for example: + * array('force' => true) + * (array) list of the other parameters + * + * The run() function returns a PEAR_CommandResponse object. Use + * these methods to get information: + * + * int getStatus() Returns PEAR_COMMAND_(SUCCESS|FAILURE|PARTIAL) + * *_PARTIAL means that you need to issue at least + * one more command to complete the operation + * (used for example for validation steps). + * + * string getMessage() Returns a message for the user. Remember, + * no HTML or other interface-specific markup. + * + * If something unexpected happens, run() returns a PEAR error. + * + * - DON'T OUTPUT ANYTHING! Return text for output instead. + * + * - DON'T USE HTML! The text you return will be used from both Gtk, + * web and command-line interfaces, so for now, keep everything to + * plain text. + * + * - DON'T USE EXIT OR DIE! Always use pear errors. From static + * classes do PEAR::raiseError(), from other classes do + * $this->raiseError(). + */ +class PEAR_Command +{ + // {{{ factory() + + /** + * Get the right object for executing a command. + * + * @param string $command The name of the command + * @param object $config Instance of PEAR_Config object + * + * @return object the command object or a PEAR error + * + * @access public + * @static + */ + function factory($command, &$config) + { + if (empty($GLOBALS['_PEAR_Command_commandlist'])) { + PEAR_Command::registerCommands(); + } + if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) { + $command = $GLOBALS['_PEAR_Command_shortcuts'][$command]; + } + if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) { + return PEAR::raiseError("unknown command `$command'"); + } + $class = $GLOBALS['_PEAR_Command_commandlist'][$command]; + if (!class_exists($class)) { + return PEAR::raiseError("unknown command `$command'"); + } + $ui =& PEAR_Command::getFrontendObject(); + $obj = &new $class($ui, $config); + return $obj; + } + + // }}} + // {{{ & getFrontendObject() + + /** + * Get instance of frontend object. + * + * @return object + * @static + */ + function &getFrontendObject() + { + if (empty($GLOBALS['_PEAR_Command_uiobject'])) { + $GLOBALS['_PEAR_Command_uiobject'] = &new $GLOBALS['_PEAR_Command_uiclass']; + } + return $GLOBALS['_PEAR_Command_uiobject']; + } + + // }}} + // {{{ & setFrontendClass() + + /** + * Load current frontend class. + * + * @param string $uiclass Name of class implementing the frontend + * + * @return object the frontend object, or a PEAR error + * @static + */ + function &setFrontendClass($uiclass) + { + if (is_object($GLOBALS['_PEAR_Command_uiobject']) && + is_a($GLOBALS['_PEAR_Command_uiobject'], $uiclass)) { + return $GLOBALS['_PEAR_Command_uiobject']; + } + if (!class_exists($uiclass)) { + $file = str_replace('_', '/', $uiclass) . '.php'; + if (PEAR_Command::isIncludeable($file)) { + include_once $file; + } + } + if (class_exists($uiclass)) { + $obj = &new $uiclass; + // quick test to see if this class implements a few of the most + // important frontend methods + if (method_exists($obj, 'userConfirm')) { + $GLOBALS['_PEAR_Command_uiobject'] = &$obj; + $GLOBALS['_PEAR_Command_uiclass'] = $uiclass; + return $obj; + } else { + $err = PEAR::raiseError("not a frontend class: $uiclass"); + return $err; + } + } + $err = PEAR::raiseError("no such class: $uiclass"); + return $err; + } + + // }}} + // {{{ setFrontendType() + + // }}} + // {{{ isIncludeable() + + /** + * @param string $path relative or absolute include path + * @return boolean + * @static + */ + function isIncludeable($path) + { + if (file_exists($path) && is_readable($path)) { + return true; + } + $ipath = explode(PATH_SEPARATOR, ini_get('include_path')); + foreach ($ipath as $include) { + $test = realpath($include . DIRECTORY_SEPARATOR . $path); + if (file_exists($test) && is_readable($test)) { + return true; + } + } + return false; + } + + /** + * Set current frontend. + * + * @param string $uitype Name of the frontend type (for example "CLI") + * + * @return object the frontend object, or a PEAR error + * @static + */ + function setFrontendType($uitype) + { + $uiclass = 'PEAR_Frontend_' . $uitype; + return PEAR_Command::setFrontendClass($uiclass); + } + + // }}} + // {{{ registerCommands() + + /** + * Scan through the Command directory looking for classes + * and see what commands they implement. + * + * @param bool (optional) if FALSE (default), the new list of + * commands should replace the current one. If TRUE, + * new entries will be merged with old. + * + * @param string (optional) where (what directory) to look for + * classes, defaults to the Command subdirectory of + * the directory from where this file (__FILE__) is + * included. + * + * @return bool TRUE on success, a PEAR error on failure + * + * @access public + * @static + */ + function registerCommands($merge = false, $dir = null) + { + if ($dir === null) { + $dir = dirname(__FILE__) . '/Command'; + } + $dp = @opendir($dir); + if (empty($dp)) { + return PEAR::raiseError("registerCommands: opendir($dir) failed"); + } + if (!$merge) { + $GLOBALS['_PEAR_Command_commandlist'] = array(); + } + while ($entry = readdir($dp)) { + if ($entry{0} == '.' || substr($entry, -4) != '.php' || $entry == 'Common.php') { + continue; + } + $class = "PEAR_Command_".substr($entry, 0, -4); + $file = "$dir/$entry"; + include_once $file; + // List of commands + if (empty($GLOBALS['_PEAR_Command_objects'][$class])) { + $GLOBALS['_PEAR_Command_objects'][$class] = &new $class($ui, $config); + } + $implements = $GLOBALS['_PEAR_Command_objects'][$class]->getCommands(); + foreach ($implements as $command => $desc) { + $GLOBALS['_PEAR_Command_commandlist'][$command] = $class; + $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc; + } + $shortcuts = $GLOBALS['_PEAR_Command_objects'][$class]->getShortcuts(); + foreach ($shortcuts as $shortcut => $command) { + $GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command; + } + } + @closedir($dp); + return true; + } + + // }}} + // {{{ getCommands() + + /** + * Get the list of currently supported commands, and what + * classes implement them. + * + * @return array command => implementing class + * + * @access public + * @static + */ + function getCommands() + { + if (empty($GLOBALS['_PEAR_Command_commandlist'])) { + PEAR_Command::registerCommands(); + } + return $GLOBALS['_PEAR_Command_commandlist']; + } + + // }}} + // {{{ getShortcuts() + + /** + * Get the list of command shortcuts. + * + * @return array shortcut => command + * + * @access public + * @static + */ + function getShortcuts() + { + if (empty($GLOBALS['_PEAR_Command_shortcuts'])) { + PEAR_Command::registerCommands(); + } + return $GLOBALS['_PEAR_Command_shortcuts']; + } + + // }}} + // {{{ getGetoptArgs() + + /** + * Compiles arguments for getopt. + * + * @param string $command command to get optstring for + * @param string $short_args (reference) short getopt format + * @param array $long_args (reference) long getopt format + * + * @return void + * + * @access public + * @static + */ + function getGetoptArgs($command, &$short_args, &$long_args) + { + if (empty($GLOBALS['_PEAR_Command_commandlist'])) { + PEAR_Command::registerCommands(); + } + if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) { + return null; + } + $class = $GLOBALS['_PEAR_Command_commandlist'][$command]; + $obj = &$GLOBALS['_PEAR_Command_objects'][$class]; + return $obj->getGetoptArgs($command, $short_args, $long_args); + } + + // }}} + // {{{ getDescription() + + /** + * Get description for a command. + * + * @param string $command Name of the command + * + * @return string command description + * + * @access public + * @static + */ + function getDescription($command) + { + if (!isset($GLOBALS['_PEAR_Command_commanddesc'][$command])) { + return null; + } + return $GLOBALS['_PEAR_Command_commanddesc'][$command]; + } + + // }}} + // {{{ getHelp() + + /** + * Get help for command. + * + * @param string $command Name of the command to return help for + * + * @access public + * @static + */ + function getHelp($command) + { + $cmds = PEAR_Command::getCommands(); + if (isset($cmds[$command])) { + $class = $cmds[$command]; + return $GLOBALS['_PEAR_Command_objects'][$class]->getHelp($command); + } + return false; + } + // }}} +} + +?> diff --git a/src/www/lib/pear/PEAR/Command/Auth.php b/src/www/lib/pear/PEAR/Command/Auth.php new file mode 100644 index 00000000..c85d685c --- /dev/null +++ b/src/www/lib/pear/PEAR/Command/Auth.php @@ -0,0 +1,155 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Auth.php,v 1.1 2005/05/28 01:55:56 henrique Exp $ + +require_once "PEAR/Command/Common.php"; +require_once "PEAR/Remote.php"; +require_once "PEAR/Config.php"; + +/** + * PEAR commands for managing configuration data. + * + */ +class PEAR_Command_Auth extends PEAR_Command_Common +{ + // {{{ properties + + var $commands = array( + 'login' => array( + 'summary' => 'Connects and authenticates to remote server', + 'shortcut' => 'li', + 'function' => 'doLogin', + 'options' => array(), + 'doc' => ' +Log in to the remote server. To use remote functions in the installer +that require any kind of privileges, you need to log in first. The +username and password you enter here will be stored in your per-user +PEAR configuration (~/.pearrc on Unix-like systems). After logging +in, your username and password will be sent along in subsequent +operations on the remote server.', + ), + 'logout' => array( + 'summary' => 'Logs out from the remote server', + 'shortcut' => 'lo', + 'function' => 'doLogout', + 'options' => array(), + 'doc' => ' +Logs out from the remote server. This command does not actually +connect to the remote server, it only deletes the stored username and +password from your user configuration.', + ) + + ); + + // }}} + + // {{{ constructor + + /** + * PEAR_Command_Auth constructor. + * + * @access public + */ + function PEAR_Command_Auth(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + // }}} + + // {{{ doLogin() + + /** + * Execute the 'login' command. + * + * @param string $command command name + * + * @param array $options option_name => value + * + * @param array $params list of additional parameters + * + * @return bool TRUE on success, FALSE for unknown commands, or + * a PEAR error on failure + * + * @access public + */ + function doLogin($command, $options, $params) + { + $server = $this->config->get('master_server'); + $remote = new PEAR_Remote($this->config); + $username = $this->config->get('username'); + if (empty($username)) { + $username = @$_ENV['USER']; + } + $this->ui->outputData("Logging in to $server.", $command); + + list($username, $password) = $this->ui->userDialog( + $command, + array('Username', 'Password'), + array('text', 'password'), + array($username, '') + ); + $username = trim($username); + $password = trim($password); + + $this->config->set('username', $username); + $this->config->set('password', $password); + + $remote->expectError(401); + $ok = $remote->call('logintest'); + $remote->popExpect(); + if ($ok === true) { + $this->ui->outputData("Logged in.", $command); + $this->config->store(); + } else { + return $this->raiseError("Login failed!"); + } + + } + + // }}} + // {{{ doLogout() + + /** + * Execute the 'logout' command. + * + * @param string $command command name + * + * @param array $options option_name => value + * + * @param array $params list of additional parameters + * + * @return bool TRUE on success, FALSE for unknown commands, or + * a PEAR error on failure + * + * @access public + */ + function doLogout($command, $options, $params) + { + $server = $this->config->get('master_server'); + $this->ui->outputData("Logging out from $server.", $command); + $this->config->remove('username'); + $this->config->remove('password'); + $this->config->store(); + } + + // }}} +} + +?> \ No newline at end of file diff --git a/src/www/lib/pear/PEAR/Command/Build.php b/src/www/lib/pear/PEAR/Command/Build.php new file mode 100644 index 00000000..75536509 --- /dev/null +++ b/src/www/lib/pear/PEAR/Command/Build.php @@ -0,0 +1,89 @@ + | +// | Tomas V.V.Cox | +// | | +// +----------------------------------------------------------------------+ +// +// $Id: Build.php,v 1.1 2005/05/28 01:55:56 henrique Exp $ + +require_once "PEAR/Command/Common.php"; +require_once "PEAR/Builder.php"; + +/** + * PEAR commands for building extensions. + * + */ +class PEAR_Command_Build extends PEAR_Command_Common +{ + // {{{ properties + + var $commands = array( + 'build' => array( + 'summary' => 'Build an Extension From C Source', + 'function' => 'doBuild', + 'shortcut' => 'b', + 'options' => array(), + 'doc' => '[package.xml] +Builds one or more extensions contained in a package.' + ), + ); + + // }}} + + // {{{ constructor + + /** + * PEAR_Command_Build constructor. + * + * @access public + */ + function PEAR_Command_Build(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + // }}} + + // {{{ doBuild() + + function doBuild($command, $options, $params) + { + if (sizeof($params) < 1) { + $params[0] = 'package.xml'; + } + $builder = &new PEAR_Builder($this->ui); + $this->debug = $this->config->get('verbose'); + $err = $builder->build($params[0], array(&$this, 'buildCallback')); + if (PEAR::isError($err)) { + return $err; + } + return true; + } + + // }}} + // {{{ buildCallback() + + function buildCallback($what, $data) + { + if (($what == 'cmdoutput' && $this->debug > 1) || + ($what == 'output' && $this->debug > 0)) { + $this->ui->outputData(rtrim($data), 'build'); + } + } + + // }}} +} diff --git a/src/www/lib/pear/PEAR/Command/Common.php b/src/www/lib/pear/PEAR/Command/Common.php new file mode 100644 index 00000000..bb4d5ec9 --- /dev/null +++ b/src/www/lib/pear/PEAR/Command/Common.php @@ -0,0 +1,249 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Common.php,v 1.1 2005/05/28 01:55:56 henrique Exp $ + +require_once "PEAR.php"; + +class PEAR_Command_Common extends PEAR +{ + // {{{ properties + + /** + * PEAR_Config object used to pass user system and configuration + * on when executing commands + * + * @var object + */ + var $config; + + /** + * User Interface object, for all interaction with the user. + * @var object + */ + var $ui; + + var $_deps_rel_trans = array( + 'lt' => '<', + 'le' => '<=', + 'eq' => '=', + 'ne' => '!=', + 'gt' => '>', + 'ge' => '>=', + 'has' => '==' + ); + + var $_deps_type_trans = array( + 'pkg' => 'package', + 'extension' => 'extension', + 'php' => 'PHP', + 'prog' => 'external program', + 'ldlib' => 'external library for linking', + 'rtlib' => 'external runtime library', + 'os' => 'operating system', + 'websrv' => 'web server', + 'sapi' => 'SAPI backend' + ); + + // }}} + // {{{ constructor + + /** + * PEAR_Command_Common constructor. + * + * @access public + */ + function PEAR_Command_Common(&$ui, &$config) + { + parent::PEAR(); + $this->config = &$config; + $this->ui = &$ui; + } + + // }}} + + // {{{ getCommands() + + /** + * Return a list of all the commands defined by this class. + * @return array list of commands + * @access public + */ + function getCommands() + { + $ret = array(); + foreach (array_keys($this->commands) as $command) { + $ret[$command] = $this->commands[$command]['summary']; + } + return $ret; + } + + // }}} + // {{{ getShortcuts() + + /** + * Return a list of all the command shortcuts defined by this class. + * @return array shortcut => command + * @access public + */ + function getShortcuts() + { + $ret = array(); + foreach (array_keys($this->commands) as $command) { + if (isset($this->commands[$command]['shortcut'])) { + $ret[$this->commands[$command]['shortcut']] = $command; + } + } + return $ret; + } + + // }}} + // {{{ getOptions() + + function getOptions($command) + { + return @$this->commands[$command]['options']; + } + + // }}} + // {{{ getGetoptArgs() + + function getGetoptArgs($command, &$short_args, &$long_args) + { + $short_args = ""; + $long_args = array(); + if (empty($this->commands[$command])) { + return; + } + reset($this->commands[$command]); + while (list($option, $info) = each($this->commands[$command]['options'])) { + $larg = $sarg = ''; + if (isset($info['arg'])) { + if ($info['arg']{0} == '(') { + $larg = '=='; + $sarg = '::'; + $arg = substr($info['arg'], 1, -1); + } else { + $larg = '='; + $sarg = ':'; + $arg = $info['arg']; + } + } + if (isset($info['shortopt'])) { + $short_args .= $info['shortopt'] . $sarg; + } + $long_args[] = $option . $larg; + } + } + + // }}} + // {{{ getHelp() + /** + * Returns the help message for the given command + * + * @param string $command The command + * @return mixed A fail string if the command does not have help or + * a two elements array containing [0]=>help string, + * [1]=> help string for the accepted cmd args + */ + function getHelp($command) + { + $config = &PEAR_Config::singleton(); + $help = @$this->commands[$command]['doc']; + if (empty($help)) { + // XXX (cox) Fallback to summary if there is no doc (show both?) + if (!$help = @$this->commands[$command]['summary']) { + return "No help for command \"$command\""; + } + } + if (preg_match_all('/{config\s+([^\}]+)}/e', $help, $matches)) { + foreach($matches[0] as $k => $v) { + $help = preg_replace("/$v/", $config->get($matches[1][$k]), $help); + } + } + return array($help, $this->getHelpArgs($command)); + } + + // }}} + // {{{ getHelpArgs() + /** + * Returns the help for the accepted arguments of a command + * + * @param string $command + * @return string The help string + */ + function getHelpArgs($command) + { + if (isset($this->commands[$command]['options']) && + count($this->commands[$command]['options'])) + { + $help = "Options:\n"; + foreach ($this->commands[$command]['options'] as $k => $v) { + if (isset($v['arg'])) { + if ($v['arg']{0} == '(') { + $arg = substr($v['arg'], 1, -1); + $sapp = " [$arg]"; + $lapp = "[=$arg]"; + } else { + $sapp = " $v[arg]"; + $lapp = "=$v[arg]"; + } + } else { + $sapp = $lapp = ""; + } + if (isset($v['shortopt'])) { + $s = $v['shortopt']; + @$help .= " -$s$sapp, --$k$lapp\n"; + } else { + @$help .= " --$k$lapp\n"; + } + $p = " "; + $doc = rtrim(str_replace("\n", "\n$p", $v['doc'])); + $help .= " $doc\n"; + } + return $help; + } + return null; + } + + // }}} + // {{{ run() + + function run($command, $options, $params) + { + $func = @$this->commands[$command]['function']; + if (empty($func)) { + // look for shortcuts + foreach (array_keys($this->commands) as $cmd) { + if (@$this->commands[$cmd]['shortcut'] == $command) { + $command = $cmd; + $func = @$this->commands[$command]['function']; + if (empty($func)) { + return $this->raiseError("unknown command `$command'"); + } + break; + } + } + } + return $this->$func($command, $options, $params); + } + + // }}} +} + +?> \ No newline at end of file diff --git a/src/www/lib/pear/PEAR/Command/Config.php b/src/www/lib/pear/PEAR/Command/Config.php new file mode 100644 index 00000000..21b75403 --- /dev/null +++ b/src/www/lib/pear/PEAR/Command/Config.php @@ -0,0 +1,225 @@ + | +// | Tomas V.V.Cox | +// | | +// +----------------------------------------------------------------------+ +// +// $Id: Config.php,v 1.1 2005/05/28 01:55:57 henrique Exp $ + +require_once "PEAR/Command/Common.php"; +require_once "PEAR/Config.php"; + +/** + * PEAR commands for managing configuration data. + * + */ +class PEAR_Command_Config extends PEAR_Command_Common +{ + // {{{ properties + + var $commands = array( + 'config-show' => array( + 'summary' => 'Show All Settings', + 'function' => 'doConfigShow', + 'shortcut' => 'csh', + 'options' => array(), + 'doc' => ' +Displays all configuration values. An optional argument +may be used to tell which configuration layer to display. Valid +configuration layers are "user", "system" and "default". +', + ), + 'config-get' => array( + 'summary' => 'Show One Setting', + 'function' => 'doConfigGet', + 'shortcut' => 'cg', + 'options' => array(), + 'doc' => ' [layer] +Displays the value of one configuration parameter. The +first argument is the name of the parameter, an optional second argument +may be used to tell which configuration layer to look in. Valid configuration +layers are "user", "system" and "default". If no layer is specified, a value +will be picked from the first layer that defines the parameter, in the order +just specified. +', + ), + 'config-set' => array( + 'summary' => 'Change Setting', + 'function' => 'doConfigSet', + 'shortcut' => 'cs', + 'options' => array(), + 'doc' => ' [layer] +Sets the value of one configuration parameter. The first argument is +the name of the parameter, the second argument is the new value. Some +parameters are subject to validation, and the command will fail with +an error message if the new value does not make sense. An optional +third argument may be used to specify in which layer to set the +configuration parameter. The default layer is "user". +', + ), + 'config-help' => array( + 'summary' => 'Show Information About Setting', + 'function' => 'doConfigHelp', + 'shortcut' => 'ch', + 'options' => array(), + 'doc' => '[parameter] +Displays help for a configuration parameter. Without arguments it +displays help for all configuration parameters. +', + ), + ); + + // }}} + // {{{ constructor + + /** + * PEAR_Command_Config constructor. + * + * @access public + */ + function PEAR_Command_Config(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + // }}} + + // {{{ doConfigShow() + + function doConfigShow($command, $options, $params) + { + // $params[0] -> the layer + if ($error = $this->_checkLayer(@$params[0])) { + return $this->raiseError($error); + } + $keys = $this->config->getKeys(); + sort($keys); + $data = array('caption' => 'Configuration:'); + foreach ($keys as $key) { + $type = $this->config->getType($key); + $value = $this->config->get($key, @$params[0]); + if ($type == 'password' && $value) { + $value = '********'; + } + if ($value === false) { + $value = 'false'; + } elseif ($value === true) { + $value = 'true'; + } + $data['data'][$this->config->getGroup($key)][] = array($this->config->getPrompt($key) , $key, $value); + } + $this->ui->outputData($data, $command); + return true; + } + + // }}} + // {{{ doConfigGet() + + function doConfigGet($command, $options, $params) + { + // $params[0] -> the parameter + // $params[1] -> the layer + if ($error = $this->_checkLayer(@$params[1])) { + return $this->raiseError($error); + } + if (sizeof($params) < 1 || sizeof($params) > 2) { + return $this->raiseError("config-get expects 1 or 2 parameters"); + } elseif (sizeof($params) == 1) { + $this->ui->outputData($this->config->get($params[0]), $command); + } else { + $data = $this->config->get($params[0], $params[1]); + $this->ui->outputData($data, $command); + } + return true; + } + + // }}} + // {{{ doConfigSet() + + function doConfigSet($command, $options, $params) + { + // $param[0] -> a parameter to set + // $param[1] -> the value for the parameter + // $param[2] -> the layer + $failmsg = ''; + if (sizeof($params) < 2 || sizeof($params) > 3) { + $failmsg .= "config-set expects 2 or 3 parameters"; + return PEAR::raiseError($failmsg); + } + if ($error = $this->_checkLayer(@$params[2])) { + $failmsg .= $error; + return PEAR::raiseError($failmsg); + } + if (!call_user_func_array(array(&$this->config, 'set'), $params)) + { + $failmsg = "config-set (" . implode(", ", $params) . ") failed"; + } else { + $this->config->store(); + } + if ($failmsg) { + return $this->raiseError($failmsg); + } + return true; + } + + // }}} + // {{{ doConfigHelp() + + function doConfigHelp($command, $options, $params) + { + if (empty($params)) { + $params = $this->config->getKeys(); + } + $data['caption'] = "Config help" . ((count($params) == 1) ? " for $params[0]" : ''); + $data['headline'] = array('Name', 'Type', 'Description'); + $data['border'] = true; + foreach ($params as $name) { + $type = $this->config->getType($name); + $docs = $this->config->getDocs($name); + if ($type == 'set') { + $docs = rtrim($docs) . "\nValid set: " . + implode(' ', $this->config->getSetValues($name)); + } + $data['data'][] = array($name, $type, $docs); + } + $this->ui->outputData($data, $command); + } + + // }}} + // {{{ _checkLayer() + + /** + * Checks if a layer is defined or not + * + * @param string $layer The layer to search for + * @return mixed False on no error or the error message + */ + function _checkLayer($layer = null) + { + if (!empty($layer) && $layer != 'default') { + $layers = $this->config->getLayers(); + if (!in_array($layer, $layers)) { + return " only the layers: \"" . implode('" or "', $layers) . "\" are supported"; + } + } + return false; + } + + // }}} +} + +?> diff --git a/src/www/lib/pear/PEAR/Command/Install.php b/src/www/lib/pear/PEAR/Command/Install.php new file mode 100644 index 00000000..e6d352a4 --- /dev/null +++ b/src/www/lib/pear/PEAR/Command/Install.php @@ -0,0 +1,470 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Install.php,v 1.1 2005/05/28 01:55:57 henrique Exp $ + +require_once "PEAR/Command/Common.php"; +require_once "PEAR/Installer.php"; + +/** + * PEAR commands for installation or deinstallation/upgrading of + * packages. + * + */ +class PEAR_Command_Install extends PEAR_Command_Common +{ + // {{{ properties + + var $commands = array( + 'install' => array( + 'summary' => 'Install Package', + 'function' => 'doInstall', + 'shortcut' => 'i', + 'options' => array( + 'force' => array( + 'shortopt' => 'f', + 'doc' => 'will overwrite newer installed packages', + ), + 'nodeps' => array( + 'shortopt' => 'n', + 'doc' => 'ignore dependencies, install anyway', + ), + 'register-only' => array( + 'shortopt' => 'r', + 'doc' => 'do not install files, only register the package as installed', + ), + 'soft' => array( + 'shortopt' => 's', + 'doc' => 'soft install, fail silently, or upgrade if already installed', + ), + 'nobuild' => array( + 'shortopt' => 'B', + 'doc' => 'don\'t build C extensions', + ), + 'nocompress' => array( + 'shortopt' => 'Z', + 'doc' => 'request uncompressed files when downloading', + ), + 'installroot' => array( + 'shortopt' => 'R', + 'arg' => 'DIR', + 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)', + ), + 'ignore-errors' => array( + 'doc' => 'force install even if there were errors', + ), + 'alldeps' => array( + 'shortopt' => 'a', + 'doc' => 'install all required and optional dependencies', + ), + 'onlyreqdeps' => array( + 'shortopt' => 'o', + 'doc' => 'install all required dependencies', + ), + ), + 'doc' => ' ... +Installs one or more PEAR packages. You can specify a package to +install in four ways: + +"Package-1.0.tgz" : installs from a local file + +"http://example.com/Package-1.0.tgz" : installs from +anywhere on the net. + +"package.xml" : installs the package described in +package.xml. Useful for testing, or for wrapping a PEAR package in +another package manager such as RPM. + +"Package" : queries your configured server +({config master_server}) and downloads the newest package with +the preferred quality/state ({config preferred_state}). + +More than one package may be specified at once. It is ok to mix these +four ways of specifying packages. +'), + 'upgrade' => array( + 'summary' => 'Upgrade Package', + 'function' => 'doInstall', + 'shortcut' => 'up', + 'options' => array( + 'force' => array( + 'shortopt' => 'f', + 'doc' => 'overwrite newer installed packages', + ), + 'nodeps' => array( + 'shortopt' => 'n', + 'doc' => 'ignore dependencies, upgrade anyway', + ), + 'register-only' => array( + 'shortopt' => 'r', + 'doc' => 'do not install files, only register the package as upgraded', + ), + 'nobuild' => array( + 'shortopt' => 'B', + 'doc' => 'don\'t build C extensions', + ), + 'nocompress' => array( + 'shortopt' => 'Z', + 'doc' => 'request uncompressed files when downloading', + ), + 'installroot' => array( + 'shortopt' => 'R', + 'arg' => 'DIR', + 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)', + ), + 'ignore-errors' => array( + 'doc' => 'force install even if there were errors', + ), + 'alldeps' => array( + 'shortopt' => 'a', + 'doc' => 'install all required and optional dependencies', + ), + 'onlyreqdeps' => array( + 'shortopt' => 'o', + 'doc' => 'install all required dependencies', + ), + ), + 'doc' => ' ... +Upgrades one or more PEAR packages. See documentation for the +"install" command for ways to specify a package. + +When upgrading, your package will be updated if the provided new +package has a higher version number (use the -f option if you need to +upgrade anyway). + +More than one package may be specified at once. +'), + 'upgrade-all' => array( + 'summary' => 'Upgrade All Packages', + 'function' => 'doInstall', + 'shortcut' => 'ua', + 'options' => array( + 'nodeps' => array( + 'shortopt' => 'n', + 'doc' => 'ignore dependencies, upgrade anyway', + ), + 'register-only' => array( + 'shortopt' => 'r', + 'doc' => 'do not install files, only register the package as upgraded', + ), + 'nobuild' => array( + 'shortopt' => 'B', + 'doc' => 'don\'t build C extensions', + ), + 'nocompress' => array( + 'shortopt' => 'Z', + 'doc' => 'request uncompressed files when downloading', + ), + 'installroot' => array( + 'shortopt' => 'R', + 'arg' => 'DIR', + 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)', + ), + 'ignore-errors' => array( + 'doc' => 'force install even if there were errors', + ), + ), + 'doc' => ' +Upgrades all packages that have a newer release available. Upgrades are +done only if there is a release available of the state specified in +"preferred_state" (currently {config preferred_state}), or a state considered +more stable. +'), + 'uninstall' => array( + 'summary' => 'Un-install Package', + 'function' => 'doUninstall', + 'shortcut' => 'un', + 'options' => array( + 'nodeps' => array( + 'shortopt' => 'n', + 'doc' => 'ignore dependencies, uninstall anyway', + ), + 'register-only' => array( + 'shortopt' => 'r', + 'doc' => 'do not remove files, only register the packages as not installed', + ), + 'installroot' => array( + 'shortopt' => 'R', + 'arg' => 'DIR', + 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)', + ), + 'ignore-errors' => array( + 'doc' => 'force install even if there were errors', + ), + ), + 'doc' => ' ... +Uninstalls one or more PEAR packages. More than one package may be +specified at once. +'), + 'bundle' => array( + 'summary' => 'Unpacks a Pecl Package', + 'function' => 'doBundle', + 'shortcut' => 'bun', + 'options' => array( + 'destination' => array( + 'shortopt' => 'd', + 'arg' => 'DIR', + 'doc' => 'Optional destination directory for unpacking (defaults to current path or "ext" if exists)', + ), + 'force' => array( + 'shortopt' => 'f', + 'doc' => 'Force the unpacking even if there were errors in the package', + ), + ), + 'doc' => ' +Unpacks a Pecl Package into the selected location. It will download the +package if needed. +'), + ); + + // }}} + // {{{ constructor + + /** + * PEAR_Command_Install constructor. + * + * @access public + */ + function PEAR_Command_Install(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + // }}} + + // {{{ doInstall() + + function doInstall($command, $options, $params) + { + require_once 'PEAR/Downloader.php'; + if (empty($this->installer)) { + $this->installer = &new PEAR_Installer($this->ui); + } + if ($command == 'upgrade') { + $options['upgrade'] = true; + } + if ($command == 'upgrade-all') { + include_once "PEAR/Remote.php"; + $options['upgrade'] = true; + $remote = &new PEAR_Remote($this->config); + $state = $this->config->get('preferred_state'); + if (empty($state) || $state == 'any') { + $latest = $remote->call("package.listLatestReleases"); + } else { + $latest = $remote->call("package.listLatestReleases", $state); + } + if (PEAR::isError($latest)) { + return $latest; + } + $reg = new PEAR_Registry($this->config->get('php_dir')); + $installed = array_flip($reg->listPackages()); + $params = array(); + foreach ($latest as $package => $info) { + $package = strtolower($package); + if (!isset($installed[$package])) { + // skip packages we don't have installed + continue; + } + $inst_version = $reg->packageInfo($package, 'version'); + if (version_compare("$info[version]", "$inst_version", "le")) { + // installed version is up-to-date + continue; + } + $params[] = $package; + $this->ui->outputData(array('data' => "Will upgrade $package"), $command); + } + } + $this->downloader = &new PEAR_Downloader($this->ui, $options, $this->config); + $errors = array(); + $downloaded = array(); + $this->downloader->download($params); + $errors = $this->downloader->getErrorMsgs(); + if (count($errors)) { + $err['data'] = array($errors); + $err['headline'] = 'Install Errors'; + $this->ui->outputData($err); + return $this->raiseError("$command failed"); + } + $downloaded = $this->downloader->getDownloadedPackages(); + $this->installer->sortPkgDeps($downloaded); + foreach ($downloaded as $pkg) { + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $info = $this->installer->install($pkg['file'], $options, $this->config); + PEAR::popErrorHandling(); + if (PEAR::isError($info)) { + $this->ui->outputData('ERROR: ' .$info->getMessage()); + continue; + } + if (is_array($info)) { + if ($this->config->get('verbose') > 0) { + $label = "$info[package] $info[version]"; + $out = array('data' => "$command ok: $label"); + if (isset($info['release_warnings'])) { + $out['release_warnings'] = $info['release_warnings']; + } + $this->ui->outputData($out, $command); + } + } else { + return $this->raiseError("$command failed"); + } + } + return true; + } + + // }}} + // {{{ doUninstall() + + function doUninstall($command, $options, $params) + { + if (empty($this->installer)) { + $this->installer = &new PEAR_Installer($this->ui); + } + if (sizeof($params) < 1) { + return $this->raiseError("Please supply the package(s) you want to uninstall"); + } + include_once 'PEAR/Registry.php'; + $reg = new PEAR_Registry($this->config->get('php_dir')); + $newparams = array(); + $badparams = array(); + foreach ($params as $pkg) { + $info = $reg->packageInfo($pkg); + if ($info === null) { + $badparams[] = $pkg; + } else { + $newparams[] = $info; + } + } + $this->installer->sortPkgDeps($newparams, true); + $params = array(); + foreach($newparams as $info) { + $params[] = $info['info']['package']; + } + $params = array_merge($params, $badparams); + foreach ($params as $pkg) { + if ($this->installer->uninstall($pkg, $options)) { + if ($this->config->get('verbose') > 0) { + $this->ui->outputData("uninstall ok: $pkg", $command); + } + } else { + return $this->raiseError("uninstall failed: $pkg"); + } + } + return true; + } + + // }}} + + + // }}} + // {{{ doBundle() + /* + (cox) It just downloads and untars the package, does not do + any check that the PEAR_Installer::_installFile() does. + */ + + function doBundle($command, $options, $params) + { + if (empty($this->installer)) { + $this->installer = &new PEAR_Downloader($this->ui); + } + $installer = &$this->installer; + if (sizeof($params) < 1) { + return $this->raiseError("Please supply the package you want to bundle"); + } + $pkgfile = $params[0]; + $need_download = false; + if (preg_match('#^(http|ftp)://#', $pkgfile)) { + $need_download = true; + } elseif (!@is_file($pkgfile)) { + if ($installer->validPackageName($pkgfile)) { + $pkgfile = $installer->getPackageDownloadUrl($pkgfile); + $need_download = true; + } else { + if (strlen($pkgfile)) { + return $this->raiseError("Could not open the package file: $pkgfile"); + } else { + return $this->raiseError("No package file given"); + } + } + } + + // Download package ----------------------------------------------- + if ($need_download) { + $downloaddir = $installer->config->get('download_dir'); + if (empty($downloaddir)) { + if (PEAR::isError($downloaddir = System::mktemp('-d'))) { + return $downloaddir; + } + $installer->log(2, '+ tmp dir created at ' . $downloaddir); + } + $callback = $this->ui ? array(&$installer, '_downloadCallback') : null; + $file = $installer->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback); + if (PEAR::isError($file)) { + return $this->raiseError($file); + } + $pkgfile = $file; + } + + // Parse xml file ----------------------------------------------- + $pkginfo = $installer->infoFromTgzFile($pkgfile); + if (PEAR::isError($pkginfo)) { + return $this->raiseError($pkginfo); + } + $installer->validatePackageInfo($pkginfo, $errors, $warnings); + // XXX We allow warnings, do we have to do it? + if (count($errors)) { + if (empty($options['force'])) { + return $this->raiseError("The following errors where found:\n". + implode("\n", $errors)); + } else { + $this->log(0, "warning : the following errors were found:\n". + implode("\n", $errors)); + } + } + $pkgname = $pkginfo['package']; + + // Unpacking ------------------------------------------------- + + if (isset($options['destination'])) { + if (!is_dir($options['destination'])) { + System::mkdir('-p ' . $options['destination']); + } + $dest = realpath($options['destination']); + } else { + $pwd = getcwd(); + if (is_dir($pwd . DIRECTORY_SEPARATOR . 'ext')) { + $dest = $pwd . DIRECTORY_SEPARATOR . 'ext'; + } else { + $dest = $pwd; + } + } + $dest .= DIRECTORY_SEPARATOR . $pkgname; + $orig = $pkgname . '-' . $pkginfo['version']; + + $tar = new Archive_Tar($pkgfile); + if (!@$tar->extractModify($dest, $orig)) { + return $this->raiseError("unable to unpack $pkgfile"); + } + $this->ui->outputData("Package ready at '$dest'"); + // }}} + } + + // }}} + +} +?> diff --git a/src/www/lib/pear/PEAR/Command/Mirror.php b/src/www/lib/pear/PEAR/Command/Mirror.php new file mode 100644 index 00000000..4c269dd5 --- /dev/null +++ b/src/www/lib/pear/PEAR/Command/Mirror.php @@ -0,0 +1,101 @@ + | +// | | +// +----------------------------------------------------------------------+ +// +// $Id: Mirror.php,v 1.1 2005/05/28 01:55:57 henrique Exp $ + +require_once "PEAR/Command/Common.php"; +require_once "PEAR/Command.php"; +require_once "PEAR/Remote.php"; +require_once "PEAR.php"; + +/** + * PEAR commands for providing file mirrors + * + */ +class PEAR_Command_Mirror extends PEAR_Command_Common +{ + // {{{ properties + + var $commands = array( + 'download-all' => array( + 'summary' => 'Downloads each available package from master_server', + 'function' => 'doDownloadAll', + 'shortcut' => 'da', + 'options' => array(), + 'doc' => ' + Requests a list of available packages from the package server + (master_server) and downloads them to current working directory' + ), + ); + + // }}} + + // {{{ constructor + + /** + * PEAR_Command_Mirror constructor. + * + * @access public + * @param object PEAR_Frontend a reference to an frontend + * @param object PEAR_Config a reference to the configuration data + */ + function PEAR_Command_Mirror(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + // }}} + + // {{{ doDownloadAll() + /** + * retrieves a list of avaible Packages from master server + * and downloads them + * + * @access public + * @param string $command the command + * @param array $options the command options before the command + * @param array $params the stuff after the command name + * @return bool true if succesful + * @throw PEAR_Error + */ + function doDownloadAll($command, $options, $params) + { + $this->config->set("php_dir", "."); + $remote = &new PEAR_Remote($this->config); + $remoteInfo = $remote->call("package.listAll"); + if (PEAR::isError($remoteInfo)) { + return $remoteInfo; + } + $cmd = &PEAR_Command::factory("download", $this->config); + if (PEAR::isError($cmd)) { + return $cmd; + } + foreach ($remoteInfo as $pkgn => $pkg) { + /** + * Error handling not neccesary, because already done by + * the download command + */ + $cmd->run("download", array(), array($pkgn)); + } + + return true; + } + + // }}} +} diff --git a/src/www/lib/pear/PEAR/Command/Package.php b/src/www/lib/pear/PEAR/Command/Package.php new file mode 100644 index 00000000..494680dc --- /dev/null +++ b/src/www/lib/pear/PEAR/Command/Package.php @@ -0,0 +1,819 @@ + | +// | Martin Jansen | +// | Greg Beaver | +// +----------------------------------------------------------------------+ +// +// $Id: Package.php,v 1.1 2005/05/28 01:55:57 henrique Exp $ + +require_once 'PEAR/Common.php'; +require_once 'PEAR/Command/Common.php'; + +class PEAR_Command_Package extends PEAR_Command_Common +{ + // {{{ properties + + var $commands = array( + 'package' => array( + 'summary' => 'Build Package', + 'function' => 'doPackage', + 'shortcut' => 'p', + 'options' => array( + 'nocompress' => array( + 'shortopt' => 'Z', + 'doc' => 'Do not gzip the package file' + ), + 'showname' => array( + 'shortopt' => 'n', + 'doc' => 'Print the name of the packaged file.', + ), + ), + 'doc' => '[descfile] +Creates a PEAR package from its description file (usually called +package.xml). +' + ), + 'package-validate' => array( + 'summary' => 'Validate Package Consistency', + 'function' => 'doPackageValidate', + 'shortcut' => 'pv', + 'options' => array(), + 'doc' => ' +', + ), + 'cvsdiff' => array( + 'summary' => 'Run a "cvs diff" for all files in a package', + 'function' => 'doCvsDiff', + 'shortcut' => 'cd', + 'options' => array( + 'quiet' => array( + 'shortopt' => 'q', + 'doc' => 'Be quiet', + ), + 'reallyquiet' => array( + 'shortopt' => 'Q', + 'doc' => 'Be really quiet', + ), + 'date' => array( + 'shortopt' => 'D', + 'doc' => 'Diff against revision of DATE', + 'arg' => 'DATE', + ), + 'release' => array( + 'shortopt' => 'R', + 'doc' => 'Diff against tag for package release REL', + 'arg' => 'REL', + ), + 'revision' => array( + 'shortopt' => 'r', + 'doc' => 'Diff against revision REV', + 'arg' => 'REV', + ), + 'context' => array( + 'shortopt' => 'c', + 'doc' => 'Generate context diff', + ), + 'unified' => array( + 'shortopt' => 'u', + 'doc' => 'Generate unified diff', + ), + 'ignore-case' => array( + 'shortopt' => 'i', + 'doc' => 'Ignore case, consider upper- and lower-case letters equivalent', + ), + 'ignore-whitespace' => array( + 'shortopt' => 'b', + 'doc' => 'Ignore changes in amount of white space', + ), + 'ignore-blank-lines' => array( + 'shortopt' => 'B', + 'doc' => 'Ignore changes that insert or delete blank lines', + ), + 'brief' => array( + 'doc' => 'Report only whether the files differ, no details', + ), + 'dry-run' => array( + 'shortopt' => 'n', + 'doc' => 'Don\'t do anything, just pretend', + ), + ), + 'doc' => ' +Compares all the files in a package. Without any options, this +command will compare the current code with the last checked-in code. +Using the -r or -R option you may compare the current code with that +of a specific release. +', + ), + 'cvstag' => array( + 'summary' => 'Set CVS Release Tag', + 'function' => 'doCvsTag', + 'shortcut' => 'ct', + 'options' => array( + 'quiet' => array( + 'shortopt' => 'q', + 'doc' => 'Be quiet', + ), + 'reallyquiet' => array( + 'shortopt' => 'Q', + 'doc' => 'Be really quiet', + ), + 'slide' => array( + 'shortopt' => 'F', + 'doc' => 'Move (slide) tag if it exists', + ), + 'delete' => array( + 'shortopt' => 'd', + 'doc' => 'Remove tag', + ), + 'dry-run' => array( + 'shortopt' => 'n', + 'doc' => 'Don\'t do anything, just pretend', + ), + ), + 'doc' => ' +Sets a CVS tag on all files in a package. Use this command after you have +packaged a distribution tarball with the "package" command to tag what +revisions of what files were in that release. If need to fix something +after running cvstag once, but before the tarball is released to the public, +use the "slide" option to move the release tag. +', + ), + 'run-tests' => array( + 'summary' => 'Run Regression Tests', + 'function' => 'doRunTests', + 'shortcut' => 'rt', + 'options' => array( + 'recur' => array( + 'shortopt' => 'r', + 'doc' => 'Run tests in child directories, recursively. 4 dirs deep maximum', + ), + 'ini' => array( + 'shortopt' => 'i', + 'doc' => 'actual string of settings to pass to php in format " -d setting=blah"', + 'arg' => 'SETTINGS' + ), + 'realtimelog' => array( + 'shortopt' => 'l', + 'doc' => 'Log test runs/results as they are run', + ), + ), + 'doc' => '[testfile|dir ...] +Run regression tests with PHP\'s regression testing script (run-tests.php).', + ), + 'package-dependencies' => array( + 'summary' => 'Show package dependencies', + 'function' => 'doPackageDependencies', + 'shortcut' => 'pd', + 'options' => array(), + 'doc' => ' +List all depencies the package has.' + ), + 'sign' => array( + 'summary' => 'Sign a package distribution file', + 'function' => 'doSign', + 'shortcut' => 'si', + 'options' => array(), + 'doc' => ' +Signs a package distribution (.tar or .tgz) file with GnuPG.', + ), + 'makerpm' => array( + 'summary' => 'Builds an RPM spec file from a PEAR package', + 'function' => 'doMakeRPM', + 'shortcut' => 'rpm', + 'options' => array( + 'spec-template' => array( + 'shortopt' => 't', + 'arg' => 'FILE', + 'doc' => 'Use FILE as RPM spec file template' + ), + 'rpm-pkgname' => array( + 'shortopt' => 'p', + 'arg' => 'FORMAT', + 'doc' => 'Use FORMAT as format string for RPM package name, %s is replaced +by the PEAR package name, defaults to "PEAR::%s".', + ), + ), + 'doc' => ' + +Creates an RPM .spec file for wrapping a PEAR package inside an RPM +package. Intended to be used from the SPECS directory, with the PEAR +package tarball in the SOURCES directory: + +$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz +Wrote RPM spec file PEAR::Net_Geo-1.0.spec +$ rpm -bb PEAR::Net_Socket-1.0.spec +... +Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm +', + ), + ); + + var $output; + + // }}} + // {{{ constructor + + /** + * PEAR_Command_Package constructor. + * + * @access public + */ + function PEAR_Command_Package(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + // }}} + + // {{{ _displayValidationResults() + + function _displayValidationResults($err, $warn, $strict = false) + { + foreach ($err as $e) { + $this->output .= "Error: $e\n"; + } + foreach ($warn as $w) { + $this->output .= "Warning: $w\n"; + } + $this->output .= sprintf('Validation: %d error(s), %d warning(s)'."\n", + sizeof($err), sizeof($warn)); + if ($strict && sizeof($err) > 0) { + $this->output .= "Fix these errors and try again."; + return false; + } + return true; + } + + // }}} + // {{{ doPackage() + + function doPackage($command, $options, $params) + { + $this->output = ''; + include_once 'PEAR/Packager.php'; + if (sizeof($params) < 1) { + $params[0] = "package.xml"; + } + $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml'; + $packager =& new PEAR_Packager(); + $err = $warn = array(); + $dir = dirname($pkginfofile); + $compress = empty($options['nocompress']) ? true : false; + $result = $packager->package($pkginfofile, $compress); + if (PEAR::isError($result)) { + $this->ui->outputData($this->output, $command); + return $this->raiseError($result); + } + // Don't want output, only the package file name just created + if (isset($options['showname'])) { + $this->output = $result; + } + if (PEAR::isError($result)) { + $this->output .= "Package failed: ".$result->getMessage(); + } + $this->ui->outputData($this->output, $command); + return true; + } + + // }}} + // {{{ doPackageValidate() + + function doPackageValidate($command, $options, $params) + { + $this->output = ''; + if (sizeof($params) < 1) { + $params[0] = "package.xml"; + } + $obj = new PEAR_Common; + $info = null; + if ($fp = @fopen($params[0], "r")) { + $test = fread($fp, 5); + fclose($fp); + if ($test == "infoFromDescriptionFile($params[0]); + } + } + if (empty($info)) { + $info = $obj->infoFromTgzFile($params[0]); + } + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + $obj->validatePackageInfo($info, $err, $warn); + $this->_displayValidationResults($err, $warn); + $this->ui->outputData($this->output, $command); + return true; + } + + // }}} + // {{{ doCvsTag() + + function doCvsTag($command, $options, $params) + { + $this->output = ''; + $_cmd = $command; + if (sizeof($params) < 1) { + $help = $this->getHelp($command); + return $this->raiseError("$command: missing parameter: $help[0]"); + } + $obj = new PEAR_Common; + $info = $obj->infoFromDescriptionFile($params[0]); + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + $err = $warn = array(); + $obj->validatePackageInfo($info, $err, $warn); + if (!$this->_displayValidationResults($err, $warn, true)) { + $this->ui->outputData($this->output, $command); + break; + } + $version = $info['version']; + $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version); + $cvstag = "RELEASE_$cvsversion"; + $files = array_keys($info['filelist']); + $command = "cvs"; + if (isset($options['quiet'])) { + $command .= ' -q'; + } + if (isset($options['reallyquiet'])) { + $command .= ' -Q'; + } + $command .= ' tag'; + if (isset($options['slide'])) { + $command .= ' -F'; + } + if (isset($options['delete'])) { + $command .= ' -d'; + } + $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]); + foreach ($files as $file) { + $command .= ' ' . escapeshellarg($file); + } + if ($this->config->get('verbose') > 1) { + $this->output .= "+ $command\n"; + } + $this->output .= "+ $command\n"; + if (empty($options['dry-run'])) { + $fp = popen($command, "r"); + while ($line = fgets($fp, 1024)) { + $this->output .= rtrim($line)."\n"; + } + pclose($fp); + } + $this->ui->outputData($this->output, $_cmd); + return true; + } + + // }}} + // {{{ doCvsDiff() + + function doCvsDiff($command, $options, $params) + { + $this->output = ''; + if (sizeof($params) < 1) { + $help = $this->getHelp($command); + return $this->raiseError("$command: missing parameter: $help[0]"); + } + $obj = new PEAR_Common; + $info = $obj->infoFromDescriptionFile($params[0]); + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + $files = array_keys($info['filelist']); + $cmd = "cvs"; + if (isset($options['quiet'])) { + $cmd .= ' -q'; + unset($options['quiet']); + } + if (isset($options['reallyquiet'])) { + $cmd .= ' -Q'; + unset($options['reallyquiet']); + } + if (isset($options['release'])) { + $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $options['release']); + $cvstag = "RELEASE_$cvsversion"; + $options['revision'] = $cvstag; + unset($options['release']); + } + $execute = true; + if (isset($options['dry-run'])) { + $execute = false; + unset($options['dry-run']); + } + $cmd .= ' diff'; + // the rest of the options are passed right on to "cvs diff" + foreach ($options as $option => $optarg) { + $arg = @$this->commands[$command]['options'][$option]['arg']; + $short = @$this->commands[$command]['options'][$option]['shortopt']; + $cmd .= $short ? " -$short" : " --$option"; + if ($arg && $optarg) { + $cmd .= ($short ? '' : '=') . escapeshellarg($optarg); + } + } + foreach ($files as $file) { + $cmd .= ' ' . escapeshellarg($file); + } + if ($this->config->get('verbose') > 1) { + $this->output .= "+ $cmd\n"; + } + if ($execute) { + $fp = popen($cmd, "r"); + while ($line = fgets($fp, 1024)) { + $this->output .= rtrim($line)."\n"; + } + pclose($fp); + } + $this->ui->outputData($this->output, $command); + return true; + } + + // }}} + // {{{ doRunTests() + + function doRunTests($command, $options, $params) + { + include_once 'PEAR/RunTest.php'; + $log = new PEAR_Common; + $log->ui = &$this->ui; // slightly hacky, but it will work + $run = new PEAR_RunTest($log); + $tests = array(); + if (isset($options['recur'])) { + $depth = 4; + } else { + $depth = 1; + } + if (!count($params)) { + $params[] = '.'; + } + foreach ($params as $p) { + if (is_dir($p)) { + $dir = System::find(array($p, '-type', 'f', + '-maxdepth', $depth, + '-name', '*.phpt')); + $tests = array_merge($tests, $dir); + } else { + if (!@file_exists($p)) { + if (!preg_match('/\.phpt$/', $p)) { + $p .= '.phpt'; + } + $dir = System::find(array(dirname($p), '-type', 'f', + '-maxdepth', $depth, + '-name', $p)); + $tests = array_merge($tests, $dir); + } else { + $tests[] = $p; + } + } + } + $ini_settings = ''; + if (isset($options['ini'])) { + $ini_settings .= $options['ini']; + } + if (isset($_ENV['TEST_PHP_INCLUDE_PATH'])) { + $ini_settings .= " -d include_path={$_ENV['TEST_PHP_INCLUDE_PATH']}"; + } + if ($ini_settings) { + $this->ui->outputData('Using INI settings: "' . $ini_settings . '"'); + } + $skipped = $passed = $failed = array(); + $this->ui->outputData('Running ' . count($tests) . ' tests', $command); + $start = time(); + if (isset($options['realtimelog'])) { + @unlink('run-tests.log'); + } + foreach ($tests as $t) { + if (isset($options['realtimelog'])) { + $fp = @fopen('run-tests.log', 'a'); + if ($fp) { + fwrite($fp, "Running test $t..."); + fclose($fp); + } + } + $result = $run->run($t, $ini_settings); + if (OS_WINDOWS) { + for($i=0;$i<2000;$i++) { + $i = $i; // delay - race conditions on windows + } + } + if (isset($options['realtimelog'])) { + $fp = @fopen('run-tests.log', 'a'); + if ($fp) { + fwrite($fp, "$result\n"); + fclose($fp); + } + } + if ($result == 'FAILED') { + $failed[] = $t; + } + if ($result == 'PASSED') { + $passed[] = $t; + } + if ($result == 'SKIPPED') { + $skipped[] = $t; + } + } + $total = date('i:s', time() - $start); + if (count($failed)) { + $output = "TOTAL TIME: $total\n"; + $output .= count($passed) . " PASSED TESTS\n"; + $output .= count($skipped) . " SKIPPED TESTS\n"; + $output .= count($failed) . " FAILED TESTS:\n"; + foreach ($failed as $failure) { + $output .= $failure . "\n"; + } + if (isset($options['realtimelog'])) { + $fp = @fopen('run-tests.log', 'a'); + } else { + $fp = @fopen('run-tests.log', 'w'); + } + if ($fp) { + fwrite($fp, $output, strlen($output)); + fclose($fp); + $this->ui->outputData('wrote log to "' . realpath('run-tests.log') . '"', $command); + } + } elseif (@file_exists('run-tests.log') && !@is_dir('run-tests.log')) { + @unlink('run-tests.log'); + } + $this->ui->outputData('TOTAL TIME: ' . $total); + $this->ui->outputData(count($passed) . ' PASSED TESTS', $command); + $this->ui->outputData(count($skipped) . ' SKIPPED TESTS', $command); + if (count($failed)) { + $this->ui->outputData(count($failed) . ' FAILED TESTS:', $command); + foreach ($failed as $failure) { + $this->ui->outputData($failure, $command); + } + } + + return true; + } + + // }}} + // {{{ doPackageDependencies() + + function doPackageDependencies($command, $options, $params) + { + // $params[0] -> the PEAR package to list its information + if (sizeof($params) != 1) { + return $this->raiseError("bad parameter(s), try \"help $command\""); + } + + $obj = new PEAR_Common(); + if (PEAR::isError($info = $obj->infoFromAny($params[0]))) { + return $this->raiseError($info); + } + + if (is_array($info['release_deps'])) { + $data = array( + 'caption' => 'Dependencies for ' . $info['package'], + 'border' => true, + 'headline' => array("Type", "Name", "Relation", "Version"), + ); + + foreach ($info['release_deps'] as $d) { + + if (isset($this->_deps_rel_trans[$d['rel']])) { + $rel = $this->_deps_rel_trans[$d['rel']]; + } else { + $rel = $d['rel']; + } + + if (isset($this->_deps_type_trans[$d['type']])) { + $type = ucfirst($this->_deps_type_trans[$d['type']]); + } else { + $type = $d['type']; + } + + if (isset($d['name'])) { + $name = $d['name']; + } else { + $name = ''; + } + + if (isset($d['version'])) { + $version = $d['version']; + } else { + $version = ''; + } + + $data['data'][] = array($type, $name, $rel, $version); + } + + $this->ui->outputData($data, $command); + return true; + } + + // Fallback + $this->ui->outputData("This package does not have any dependencies.", $command); + } + + // }}} + // {{{ doSign() + + function doSign($command, $options, $params) + { + // should move most of this code into PEAR_Packager + // so it'll be easy to implement "pear package --sign" + if (sizeof($params) != 1) { + return $this->raiseError("bad parameter(s), try \"help $command\""); + } + if (!file_exists($params[0])) { + return $this->raiseError("file does not exist: $params[0]"); + } + $obj = new PEAR_Common; + $info = $obj->infoFromTgzFile($params[0]); + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + include_once "Archive/Tar.php"; + include_once "System.php"; + $tar = new Archive_Tar($params[0]); + $tmpdir = System::mktemp('-d pearsign'); + if (!$tar->extractList('package.xml package.sig', $tmpdir)) { + return $this->raiseError("failed to extract tar file"); + } + if (file_exists("$tmpdir/package.sig")) { + return $this->raiseError("package already signed"); + } + @unlink("$tmpdir/package.sig"); + $input = $this->ui->userDialog($command, + array('GnuPG Passphrase'), + array('password')); + $gpg = popen("gpg --batch --passphrase-fd 0 --armor --detach-sign --output $tmpdir/package.sig $tmpdir/package.xml 2>/dev/null", "w"); + if (!$gpg) { + return $this->raiseError("gpg command failed"); + } + fwrite($gpg, "$input[0]\r"); + if (pclose($gpg) || !file_exists("$tmpdir/package.sig")) { + return $this->raiseError("gpg sign failed"); + } + $tar->addModify("$tmpdir/package.sig", '', $tmpdir); + return true; + } + + // }}} + // {{{ doMakeRPM() + + /* + + (cox) + + TODO: + + - Fill the rpm dependencies in the template file. + + IDEAS: + + - Instead of mapping the role to rpm vars, perhaps it's better + + to use directly the pear cmd to install the files by itself + + in %postrun so: + + pear -d php_dir=%{_libdir}/php/pear -d test_dir=.. + + */ + + function doMakeRPM($command, $options, $params) + { + if (sizeof($params) != 1) { + return $this->raiseError("bad parameter(s), try \"help $command\""); + } + if (!file_exists($params[0])) { + return $this->raiseError("file does not exist: $params[0]"); + } + include_once "Archive/Tar.php"; + include_once "PEAR/Installer.php"; + include_once "System.php"; + $tar = new Archive_Tar($params[0]); + $tmpdir = System::mktemp('-d pear2rpm'); + $instroot = System::mktemp('-d pear2rpm'); + $tmp = $this->config->get('verbose'); + $this->config->set('verbose', 0); + $installer = new PEAR_Installer($this->ui); + $info = $installer->install($params[0], + array('installroot' => $instroot, + 'nodeps' => true)); + $pkgdir = "$info[package]-$info[version]"; + $info['rpm_xml_dir'] = '/var/lib/pear'; + $this->config->set('verbose', $tmp); + if (!$tar->extractList("package.xml", $tmpdir, $pkgdir)) { + return $this->raiseError("failed to extract $params[0]"); + } + if (!file_exists("$tmpdir/package.xml")) { + return $this->raiseError("no package.xml found in $params[0]"); + } + if (isset($options['spec-template'])) { + $spec_template = $options['spec-template']; + } else { + $spec_template = $this->config->get('data_dir') . + '/PEAR/template.spec'; + } + if (isset($options['rpm-pkgname'])) { + $rpm_pkgname_format = $options['rpm-pkgname']; + } else { + $rpm_pkgname_format = "PEAR::%s"; + } + + $info['extra_headers'] = ''; + $info['doc_files'] = ''; + $info['files'] = ''; + $info['rpm_package'] = sprintf($rpm_pkgname_format, $info['package']); + $srcfiles = 0; + foreach ($info['filelist'] as $name => $attr) { + + if (!isset($attr['role'])) { + continue; + } + $name = preg_replace('![/:\\\\]!', '/', $name); + if ($attr['role'] == 'doc') { + $info['doc_files'] .= " $name"; + + // Map role to the rpm vars + } else { + + $c_prefix = '%{_libdir}/php/pear'; + + switch ($attr['role']) { + + case 'php': + + $prefix = $c_prefix; break; + + case 'ext': + + $prefix = '%{_libdir}/php'; break; // XXX good place? + + case 'src': + + $srcfiles++; + + $prefix = '%{_includedir}/php'; break; // XXX good place? + + case 'test': + + $prefix = "$c_prefix/tests/" . $info['package']; break; + + case 'data': + + $prefix = "$c_prefix/data/" . $info['package']; break; + + case 'script': + + $prefix = '%{_bindir}'; break; + + } + + $name = str_replace('\\', '/', $name); + $info['files'] .= "$prefix/$name\n"; + + } + } + if ($srcfiles > 0) { + include_once "OS/Guess.php"; + $os = new OS_Guess; + $arch = $os->getCpu(); + } else { + $arch = 'noarch'; + } + $cfg = array('master_server', 'php_dir', 'ext_dir', 'doc_dir', + 'bin_dir', 'data_dir', 'test_dir'); + foreach ($cfg as $k) { + $info[$k] = $this->config->get($k); + } + $info['arch'] = $arch; + $fp = @fopen($spec_template, "r"); + if (!$fp) { + return $this->raiseError("could not open RPM spec file template $spec_template: $php_errormsg"); + } + $spec_contents = preg_replace('/@([a-z0-9_-]+)@/e', '$info["\1"]', fread($fp, filesize($spec_template))); + fclose($fp); + $spec_file = "$info[rpm_package]-$info[version].spec"; + $wp = fopen($spec_file, "wb"); + if (!$wp) { + return $this->raiseError("could not write RPM spec file $spec_file: $php_errormsg"); + } + fwrite($wp, $spec_contents); + fclose($wp); + $this->ui->outputData("Wrote RPM spec file $spec_file", $command); + + return true; + } + + // }}} +} + +?> diff --git a/src/www/lib/pear/PEAR/Command/Registry.php b/src/www/lib/pear/PEAR/Command/Registry.php new file mode 100644 index 00000000..557bd308 --- /dev/null +++ b/src/www/lib/pear/PEAR/Command/Registry.php @@ -0,0 +1,351 @@ + | +// | | +// +----------------------------------------------------------------------+ +// +// $Id: Registry.php,v 1.1 2005/05/28 01:55:58 henrique Exp $ + +require_once 'PEAR/Command/Common.php'; +require_once 'PEAR/Registry.php'; +require_once 'PEAR/Config.php'; + +class PEAR_Command_Registry extends PEAR_Command_Common +{ + // {{{ properties + + var $commands = array( + 'list' => array( + 'summary' => 'List Installed Packages', + 'function' => 'doList', + 'shortcut' => 'l', + 'options' => array(), + 'doc' => '[package] +If invoked without parameters, this command lists the PEAR packages +installed in your php_dir ({config php_dir)). With a parameter, it +lists the files in that package. +', + ), + 'shell-test' => array( + 'summary' => 'Shell Script Test', + 'function' => 'doShellTest', + 'shortcut' => 'st', + 'options' => array(), + 'doc' => ' [[relation] version] +Tests if a package is installed in the system. Will exit(1) if it is not. + The version comparison operator. One of: + <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne + The version to compare with +'), + 'info' => array( + 'summary' => 'Display information about a package', + 'function' => 'doInfo', + 'shortcut' => 'in', + 'options' => array(), + 'doc' => ' +Displays information about a package. The package argument may be a +local package file, an URL to a package file, or the name of an +installed package.' + ) + ); + + // }}} + // {{{ constructor + + /** + * PEAR_Command_Registry constructor. + * + * @access public + */ + function PEAR_Command_Registry(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + // }}} + + // {{{ doList() + + function _sortinfo($a, $b) + { + return strcmp($a['package'], $b['package']); + } + + function doList($command, $options, $params) + { + $reg = new PEAR_Registry($this->config->get('php_dir')); + if (sizeof($params) == 0) { + $installed = $reg->packageInfo(); + usort($installed, array(&$this, '_sortinfo')); + $i = $j = 0; + $data = array( + 'caption' => 'Installed packages:', + 'border' => true, + 'headline' => array('Package', 'Version', 'State') + ); + foreach ($installed as $package) { + $data['data'][] = array($package['package'], + $package['version'], + @$package['release_state']); + } + if (count($installed)==0) { + $data = '(no packages installed)'; + } + $this->ui->outputData($data, $command); + } else { + if (file_exists($params[0]) && !is_dir($params[0])) { + include_once "PEAR/Common.php"; + $obj = &new PEAR_Common; + $info = $obj->infoFromAny($params[0]); + $headings = array('Package File', 'Install Path'); + $installed = false; + } else { + $info = $reg->packageInfo($params[0]); + $headings = array('Type', 'Install Path'); + $installed = true; + } + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + if ($info === null) { + return $this->raiseError("`$params[0]' not installed"); + } + $list = $info['filelist']; + if ($installed) { + $caption = 'Installed Files For ' . $params[0]; + } else { + $caption = 'Contents of ' . basename($params[0]); + } + $data = array( + 'caption' => $caption, + 'border' => true, + 'headline' => $headings); + foreach ($list as $file => $att) { + if ($installed) { + if (empty($att['installed_as'])) { + continue; + } + $data['data'][] = array($att['role'], $att['installed_as']); + } else { + if (isset($att['baseinstalldir'])) { + $dest = $att['baseinstalldir'] . DIRECTORY_SEPARATOR . + $file; + } else { + $dest = $file; + } + switch ($att['role']) { + case 'test': + case 'data': + if ($installed) { + break 2; + } + $dest = '-- will not be installed --'; + break; + case 'doc': + $dest = $this->config->get('doc_dir') . DIRECTORY_SEPARATOR . + $dest; + break; + case 'php': + default: + $dest = $this->config->get('php_dir') . DIRECTORY_SEPARATOR . + $dest; + } + $dest = preg_replace('!/+!', '/', $dest); + $file = preg_replace('!/+!', '/', $file); + $data['data'][] = array($file, $dest); + } + } + $this->ui->outputData($data, $command); + + + } + return true; + } + + // }}} + // {{{ doShellTest() + + function doShellTest($command, $options, $params) + { + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $reg = &new PEAR_Registry($this->config->get('php_dir')); + // "pear shell-test Foo" + if (sizeof($params) == 1) { + if (!$reg->packageExists($params[0])) { + exit(1); + } + // "pear shell-test Foo 1.0" + } elseif (sizeof($params) == 2) { + $v = $reg->packageInfo($params[0], 'version'); + if (!$v || !version_compare("$v", "{$params[1]}", "ge")) { + exit(1); + } + // "pear shell-test Foo ge 1.0" + } elseif (sizeof($params) == 3) { + $v = $reg->packageInfo($params[0], 'version'); + if (!$v || !version_compare("$v", "{$params[2]}", $params[1])) { + exit(1); + } + } else { + $this->popErrorHandling(); + $this->raiseError("$command: expects 1 to 3 parameters"); + exit(1); + } + } + + // }}} + // {{{ doInfo + + function doInfo($command, $options, $params) + { + // $params[0] The package for showing info + if (sizeof($params) != 1) { + return $this->raiseError("This command only accepts one param: ". + "the package you want information"); + } + if (@is_file($params[0])) { + $obj = &new PEAR_Common(); + $info = $obj->infoFromAny($params[0]); + } else { + $reg = &new PEAR_Registry($this->config->get('php_dir')); + $info = $reg->packageInfo($params[0]); + } + if (PEAR::isError($info)) { + return $info; + } + if (empty($info)) { + $this->raiseError("Nothing found for `$params[0]'"); + return; + } + unset($info['filelist']); + unset($info['changelog']); + $keys = array_keys($info); + $longtext = array('description', 'summary'); + foreach ($keys as $key) { + if (is_array($info[$key])) { + switch ($key) { + case 'maintainers': { + $i = 0; + $mstr = ''; + foreach ($info[$key] as $m) { + if ($i++ > 0) { + $mstr .= "\n"; + } + $mstr .= $m['name'] . " <"; + if (isset($m['email'])) { + $mstr .= $m['email']; + } else { + $mstr .= $m['handle'] . '@php.net'; + } + $mstr .= "> ($m[role])"; + } + $info[$key] = $mstr; + break; + } + case 'release_deps': { + $i = 0; + $dstr = ''; + foreach ($info[$key] as $d) { + if (isset($this->_deps_rel_trans[$d['rel']])) { + $rel = $this->_deps_rel_trans[$d['rel']]; + } else { + $rel = $d['rel']; + } + if (isset($this->_deps_type_trans[$d['type']])) { + $type = ucfirst($this->_deps_type_trans[$d['type']]); + } else { + $type = $d['type']; + } + if (isset($d['name'])) { + $name = $d['name'] . ' '; + } else { + $name = ''; + } + if (isset($d['version'])) { + $version = $d['version'] . ' '; + } else { + $version = ''; + } + $dstr .= "$type $name$rel $version\n"; + } + $info[$key] = $dstr; + break; + } + case 'provides' : { + $debug = $this->config->get('verbose'); + if ($debug < 2) { + $pstr = 'Classes: '; + } else { + $pstr = ''; + } + $i = 0; + foreach ($info[$key] as $p) { + if ($debug < 2 && $p['type'] != "class") { + continue; + } + // Only print classes when verbosity mode is < 2 + if ($debug < 2) { + if ($i++ > 0) { + $pstr .= ", "; + } + $pstr .= $p['name']; + } else { + if ($i++ > 0) { + $pstr .= "\n"; + } + $pstr .= ucfirst($p['type']) . " " . $p['name']; + if (isset($p['explicit']) && $p['explicit'] == 1) { + $pstr .= " (explicit)"; + } + } + } + $info[$key] = $pstr; + break; + } + default: { + $info[$key] = implode(", ", $info[$key]); + break; + } + } + } + if ($key == '_lastmodified') { + $hdate = date('Y-m-d', $info[$key]); + unset($info[$key]); + $info['Last Modified'] = $hdate; + } else { + $info[$key] = trim($info[$key]); + if (in_array($key, $longtext)) { + $info[$key] = preg_replace('/ +/', ' ', $info[$key]); + } + } + } + $caption = 'About ' . $info['package'] . '-' . $info['version']; + $data = array( + 'caption' => $caption, + 'border' => true); + foreach ($info as $key => $value) { + $key = ucwords(trim(str_replace('_', ' ', $key))); + $data['data'][] = array($key, $value); + } + $data['raw'] = $info; + + $this->ui->outputData($data, 'package-info'); + } + + // }}} +} + +?> diff --git a/src/www/lib/pear/PEAR/Command/Remote.php b/src/www/lib/pear/PEAR/Command/Remote.php new file mode 100644 index 00000000..111d6328 --- /dev/null +++ b/src/www/lib/pear/PEAR/Command/Remote.php @@ -0,0 +1,435 @@ + | +// | | +// +----------------------------------------------------------------------+ +// +// $Id: Remote.php,v 1.1 2005/05/28 01:55:58 henrique Exp $ + +require_once 'PEAR/Command/Common.php'; +require_once 'PEAR/Common.php'; +require_once 'PEAR/Remote.php'; +require_once 'PEAR/Registry.php'; + +class PEAR_Command_Remote extends PEAR_Command_Common +{ + // {{{ command definitions + + var $commands = array( + 'remote-info' => array( + 'summary' => 'Information About Remote Packages', + 'function' => 'doRemoteInfo', + 'shortcut' => 'ri', + 'options' => array(), + 'doc' => ' +Get details on a package from the server.', + ), + 'list-upgrades' => array( + 'summary' => 'List Available Upgrades', + 'function' => 'doListUpgrades', + 'shortcut' => 'lu', + 'options' => array(), + 'doc' => ' +List releases on the server of packages you have installed where +a newer version is available with the same release state (stable etc.).' + ), + 'remote-list' => array( + 'summary' => 'List Remote Packages', + 'function' => 'doRemoteList', + 'shortcut' => 'rl', + 'options' => array(), + 'doc' => ' +Lists the packages available on the configured server along with the +latest stable release of each package.', + ), + 'search' => array( + 'summary' => 'Search remote package database', + 'function' => 'doSearch', + 'shortcut' => 'sp', + 'options' => array(), + 'doc' => ' +Lists all packages which match the search parameters (first param +is package name, second package info)', + ), + 'list-all' => array( + 'summary' => 'List All Packages', + 'function' => 'doListAll', + 'shortcut' => 'la', + 'options' => array(), + 'doc' => ' +Lists the packages available on the configured server along with the +latest stable release of each package.', + ), + 'download' => array( + 'summary' => 'Download Package', + 'function' => 'doDownload', + 'shortcut' => 'd', + 'options' => array( + 'nocompress' => array( + 'shortopt' => 'Z', + 'doc' => 'download an uncompressed (.tar) file', + ), + ), + 'doc' => '{package|package-version} +Download a package tarball. The file will be named as suggested by the +server, for example if you download the DB package and the latest stable +version of DB is 1.2, the downloaded file will be DB-1.2.tgz.', + ), + 'clear-cache' => array( + 'summary' => 'Clear XML-RPC Cache', + 'function' => 'doClearCache', + 'shortcut' => 'cc', + 'options' => array(), + 'doc' => ' +Clear the XML-RPC cache. See also the cache_ttl configuration +parameter. +', + ), + ); + + // }}} + // {{{ constructor + + /** + * PEAR_Command_Remote constructor. + * + * @access public + */ + function PEAR_Command_Remote(&$ui, &$config) + { + parent::PEAR_Command_Common($ui, $config); + } + + // }}} + + // {{{ doRemoteInfo() + + function doRemoteInfo($command, $options, $params) + { + if (sizeof($params) != 1) { + return $this->raiseError("$command expects one param: the remote package name"); + } + $r = new PEAR_Remote($this->config); + $info = $r->call('package.info', $params[0]); + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + + $reg = new PEAR_Registry($this->config->get('php_dir')); + $installed = $reg->packageInfo($info['name']); + $info['installed'] = $installed['version'] ? $installed['version'] : '- no -'; + + $this->ui->outputData($info, $command); + + return true; + } + + // }}} + // {{{ doRemoteList() + + function doRemoteList($command, $options, $params) + { + $r = new PEAR_Remote($this->config); + $list_options = false; + if ($this->config->get('preferred_state') == 'stable') + $list_options = true; + $available = $r->call('package.listAll', $list_options); + if (PEAR::isError($available)) { + return $this->raiseError($available); + } + $i = $j = 0; + $data = array( + 'caption' => 'Available packages:', + 'border' => true, + 'headline' => array('Package', 'Version'), + ); + foreach ($available as $name => $info) { + $data['data'][] = array($name, isset($info['stable']) ? $info['stable'] : '-n/a-'); + } + if (count($available)==0) { + $data = '(no packages installed yet)'; + } + $this->ui->outputData($data, $command); + return true; + } + + // }}} + // {{{ doListAll() + + function doListAll($command, $options, $params) + { + $r = new PEAR_Remote($this->config); + $reg = new PEAR_Registry($this->config->get('php_dir')); + $list_options = false; + if ($this->config->get('preferred_state') == 'stable') + $list_options = true; + $available = $r->call('package.listAll', $list_options); + if (PEAR::isError($available)) { + return $this->raiseError($available); + } + if (!is_array($available)) { + return $this->raiseError('The package list could not be fetched from the remote server. Please try again. (Debug info: "'.$available.'")'); + } + $data = array( + 'caption' => 'All packages:', + 'border' => true, + 'headline' => array('Package', 'Latest', 'Local'), + ); + $local_pkgs = $reg->listPackages(); + + foreach ($available as $name => $info) { + $installed = $reg->packageInfo($name); + $desc = $info['summary']; + if (isset($params[$name])) + $desc .= "\n\n".$info['description']; + + if (isset($options['mode'])) + { + if ($options['mode'] == 'installed' && !isset($installed['version'])) + continue; + if ($options['mode'] == 'notinstalled' && isset($installed['version'])) + continue; + if ($options['mode'] == 'upgrades' + && (!isset($installed['version']) || $installed['version'] == $info['stable'])) + { + continue; + } + } + $pos = array_search(strtolower($name), $local_pkgs); + if ($pos !== false) { + unset($local_pkgs[$pos]); + } + + $data['data'][$info['category']][] = array( + $name, + @$info['stable'], + @$installed['version'], + @$desc, + @$info['deps'], + ); + } + + foreach ($local_pkgs as $name) { + $info = $reg->packageInfo($name); + $data['data']['Local'][] = array( + $info['package'], + '', + $info['version'], + $info['summary'], + @$info['release_deps'] + ); + } + + $this->ui->outputData($data, $command); + return true; + } + + // }}} + // {{{ doSearch() + + function doSearch($command, $options, $params) + { + if ((!isset($params[0]) || empty($params[0])) + && (!isset($params[1]) || empty($params[1]))) + { + return $this->raiseError('no valid search string supplied'); + }; + + $r = new PEAR_Remote($this->config); + $reg = new PEAR_Registry($this->config->get('php_dir')); + $available = $r->call('package.listAll', true, false); + if (PEAR::isError($available)) { + return $this->raiseError($available); + } + $data = array( + 'caption' => 'Matched packages:', + 'border' => true, + 'headline' => array('Package', 'Stable/(Latest)', 'Local'), + ); + + foreach ($available as $name => $info) { + $found = (!empty($params[0]) && stristr($name, $params[0]) !== false); + if (!$found && !(isset($params[1]) && !empty($params[1]) + && (stristr($info['summary'], $params[1]) !== false + || stristr($info['description'], $params[1]) !== false))) + { + continue; + }; + + $installed = $reg->packageInfo($name); + $desc = $info['summary']; + if (isset($params[$name])) + $desc .= "\n\n".$info['description']; + + $unstable = ''; + if ($info['unstable']) { + $unstable = '/(' . $info['unstable'] . $info['state'] . ')'; + } + if (!isset($info['stable']) || !$info['stable']) { + $info['stable'] = 'none'; + } + $data['data'][$info['category']][] = array( + $name, + $info['stable'] . $unstable, + $installed['version'], + $desc, + ); + } + if (!isset($data['data'])) { + return $this->raiseError('no packages found'); + } + $this->ui->outputData($data, $command); + return true; + } + + // }}} + // {{{ doDownload() + + function doDownload($command, $options, $params) + { + //$params[0] -> The package to download + if (count($params) != 1) { + return PEAR::raiseError("download expects one argument: the package to download"); + } + $server = $this->config->get('master_server'); + if (!ereg('^http://', $params[0])) { + $getoption = isset($options['nocompress'])&&$options['nocompress']==1?'?uncompress=on':''; + $pkgfile = "http://$server/get/$params[0]".$getoption; + } else { + $pkgfile = $params[0]; + } + $this->bytes_downloaded = 0; + $saved = PEAR_Common::downloadHttp($pkgfile, $this->ui, '.', + array(&$this, 'downloadCallback')); + if (PEAR::isError($saved)) { + return $this->raiseError($saved); + } + $fname = basename($saved); + $this->ui->outputData("File $fname downloaded ($this->bytes_downloaded bytes)", $command); + return true; + } + + function downloadCallback($msg, $params = null) + { + if ($msg == 'done') { + $this->bytes_downloaded = $params; + } + } + + // }}} + // {{{ doListUpgrades() + + function doListUpgrades($command, $options, $params) + { + include_once "PEAR/Registry.php"; + $remote = new PEAR_Remote($this->config); + if (empty($params[0])) { + $state = $this->config->get('preferred_state'); + } else { + $state = $params[0]; + } + $caption = 'Available Upgrades'; + if (empty($state) || $state == 'any') { + $latest = $remote->call("package.listLatestReleases"); + } else { + $latest = $remote->call("package.listLatestReleases", $state); + $caption .= ' (' . implode(', ', PEAR_Common::betterStates($state, true)) . ')'; + } + $caption .= ':'; + if (PEAR::isError($latest)) { + return $latest; + } + $reg = new PEAR_Registry($this->config->get('php_dir')); + $inst = array_flip($reg->listPackages()); + $data = array( + 'caption' => $caption, + 'border' => 1, + 'headline' => array('Package', 'Local', 'Remote', 'Size'), + ); + foreach ((array)$latest as $pkg => $info) { + $package = strtolower($pkg); + if (!isset($inst[$package])) { + // skip packages we don't have installed + continue; + } + extract($info); + $pkginfo = $reg->packageInfo($package); + $inst_version = $pkginfo['version']; + $inst_state = $pkginfo['release_state']; + if (version_compare("$version", "$inst_version", "le")) { + // installed version is up-to-date + continue; + } + if ($filesize >= 20480) { + $filesize += 1024 - ($filesize % 1024); + $fs = sprintf("%dkB", $filesize / 1024); + } elseif ($filesize > 0) { + $filesize += 103 - ($filesize % 103); + $fs = sprintf("%.1fkB", $filesize / 1024.0); + } else { + $fs = " -"; // XXX center instead + } + $data['data'][] = array($pkg, "$inst_version ($inst_state)", "$version ($state)", $fs); + } + if (empty($data['data'])) { + $this->ui->outputData('No upgrades available'); + } else { + $this->ui->outputData($data, $command); + } + return true; + } + + // }}} + // {{{ doClearCache() + + function doClearCache($command, $options, $params) + { + $cache_dir = $this->config->get('cache_dir'); + $verbose = $this->config->get('verbose'); + $output = ''; + if (!($dp = @opendir($cache_dir))) { + return $this->raiseError("opendir($cache_dir) failed: $php_errormsg"); + } + if ($verbose >= 1) { + $output .= "reading directory $cache_dir\n"; + } + $num = 0; + while ($ent = readdir($dp)) { + if (preg_match('/^xmlrpc_cache_[a-z0-9]{32}$/', $ent)) { + $path = $cache_dir . DIRECTORY_SEPARATOR . $ent; + $ok = @unlink($path); + if ($ok) { + if ($verbose >= 2) { + $output .= "deleted $path\n"; + } + $num++; + } elseif ($verbose >= 1) { + $output .= "failed to delete $path\n"; + } + } + } + closedir($dp); + if ($verbose >= 1) { + $output .= "$num cache entries cleared\n"; + } + $this->ui->outputData(rtrim($output), $command); + return $num; + } + + // }}} +} + +?> diff --git a/src/www/lib/pear/PEAR/Common.php b/src/www/lib/pear/PEAR/Common.php new file mode 100644 index 00000000..ff1a26a3 --- /dev/null +++ b/src/www/lib/pear/PEAR/Common.php @@ -0,0 +1,2094 @@ + | +// | Tomas V.V.Cox | +// +----------------------------------------------------------------------+ +// +// $Id: Common.php,v 1.1 2005/05/28 01:55:12 henrique Exp $ + +require_once 'PEAR.php'; +require_once 'Archive/Tar.php'; +require_once 'System.php'; +require_once 'PEAR/Config.php'; + +// {{{ constants and globals + +/** + * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode() + */ +define('PEAR_COMMON_ERROR_INVALIDPHP', 1); +define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+'); +define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '$/'); + +// this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1 +define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?'); +define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '$/i'); + +// XXX far from perfect :-) +define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^(' . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?$/'); + +/** + * List of temporary files and directories registered by + * PEAR_Common::addTempFile(). + * @var array + */ +$GLOBALS['_PEAR_Common_tempfiles'] = array(); + +/** + * Valid maintainer roles + * @var array + */ +$GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper'); + +/** + * Valid release states + * @var array + */ +$GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel'); + +/** + * Valid dependency types + * @var array + */ +$GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi'); + +/** + * Valid dependency relations + * @var array + */ +$GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne'); + +/** + * Valid file roles + * @var array + */ +$GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script'); + +/** + * Valid replacement types + * @var array + */ +$GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info'); + +/** + * Valid "provide" types + * @var array + */ +$GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api'); + +/** + * Valid "provide" types + * @var array + */ +$GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup'); + +// }}} + +/** + * Class providing common functionality for PEAR administration classes. + * @deprecated This class will disappear, and its components will be spread + * into smaller classes, like the AT&T breakup + */ +class PEAR_Common extends PEAR +{ + // {{{ properties + + /** stack of elements, gives some sort of XML context */ + var $element_stack = array(); + + /** name of currently parsed XML element */ + var $current_element; + + /** array of attributes of the currently parsed XML element */ + var $current_attributes = array(); + + /** assoc with information about a package */ + var $pkginfo = array(); + + /** + * User Interface object (PEAR_Frontend_* class). If null, + * the log() method uses print. + * @var object + */ + var $ui = null; + + /** + * Configuration object (PEAR_Config). + * @var object + */ + var $config = null; + + var $current_path = null; + + /** + * PEAR_SourceAnalyzer instance + * @var object + */ + var $source_analyzer = null; + /** + * Flag variable used to mark a valid package file + * @var boolean + * @access private + */ + var $_validPackageFile; + + // }}} + + // {{{ constructor + + /** + * PEAR_Common constructor + * + * @access public + */ + function PEAR_Common() + { + parent::PEAR(); + $this->config = &PEAR_Config::singleton(); + $this->debug = $this->config->get('verbose'); + } + + // }}} + // {{{ destructor + + /** + * PEAR_Common destructor + * + * @access private + */ + function _PEAR_Common() + { + // doesn't work due to bug #14744 + //$tempfiles = $this->_tempfiles; + $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles']; + while ($file = array_shift($tempfiles)) { + if (@is_dir($file)) { + System::rm(array('-rf', $file)); + } elseif (file_exists($file)) { + unlink($file); + } + } + } + + // }}} + // {{{ addTempFile() + + /** + * Register a temporary file or directory. When the destructor is + * executed, all registered temporary files and directories are + * removed. + * + * @param string $file name of file or directory + * + * @return void + * + * @access public + */ + function addTempFile($file) + { + $GLOBALS['_PEAR_Common_tempfiles'][] = $file; + } + + // }}} + // {{{ mkDirHier() + + /** + * Wrapper to System::mkDir(), creates a directory as well as + * any necessary parent directories. + * + * @param string $dir directory name + * + * @return bool TRUE on success, or a PEAR error + * + * @access public + */ + function mkDirHier($dir) + { + $this->log(2, "+ create dir $dir"); + return System::mkDir(array('-p', $dir)); + } + + // }}} + // {{{ log() + + /** + * Logging method. + * + * @param int $level log level (0 is quiet, higher is noisier) + * @param string $msg message to write to the log + * + * @return void + * + * @access public + */ + function log($level, $msg, $append_crlf = true) + { + if ($this->debug >= $level) { + if (is_object($this->ui)) { + $this->ui->log($msg, $append_crlf); + } else { + print "$msg\n"; + } + } + } + + // }}} + // {{{ mkTempDir() + + /** + * Create and register a temporary directory. + * + * @param string $tmpdir (optional) Directory to use as tmpdir. + * Will use system defaults (for example + * /tmp or c:\windows\temp) if not specified + * + * @return string name of created directory + * + * @access public + */ + function mkTempDir($tmpdir = '') + { + if ($tmpdir) { + $topt = array('-t', $tmpdir); + } else { + $topt = array(); + } + $topt = array_merge($topt, array('-d', 'pear')); + if (!$tmpdir = System::mktemp($topt)) { + return false; + } + $this->addTempFile($tmpdir); + return $tmpdir; + } + + // }}} + // {{{ setFrontendObject() + + /** + * Set object that represents the frontend to be used. + * + * @param object Reference of the frontend object + * @return void + * @access public + */ + function setFrontendObject(&$ui) + { + $this->ui = &$ui; + } + + // }}} + + // {{{ _unIndent() + + /** + * Unindent given string (?) + * + * @param string $str The string that has to be unindented. + * @return string + * @access private + */ + function _unIndent($str) + { + // remove leading newlines + $str = preg_replace('/^[\r\n]+/', '', $str); + // find whitespace at the beginning of the first line + $indent_len = strspn($str, " \t"); + $indent = substr($str, 0, $indent_len); + $data = ''; + // remove the same amount of whitespace from following lines + foreach (explode("\n", $str) as $line) { + if (substr($line, 0, $indent_len) == $indent) { + $data .= substr($line, $indent_len) . "\n"; + } + } + return $data; + } + + // }}} + // {{{ _element_start() + + /** + * XML parser callback for starting elements. Used while package + * format version is not yet known. + * + * @param resource $xp XML parser resource + * @param string $name name of starting element + * @param array $attribs element attributes, name => value + * + * @return void + * + * @access private + */ + function _element_start($xp, $name, $attribs) + { + array_push($this->element_stack, $name); + $this->current_element = $name; + $spos = sizeof($this->element_stack) - 2; + $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : ''; + $this->current_attributes = $attribs; + switch ($name) { + case 'package': { + $this->_validPackageFile = true; + if (isset($attribs['version'])) { + $vs = preg_replace('/[^0-9a-z]/', '_', $attribs['version']); + } else { + $vs = '1_0'; + } + $elem_start = '_element_start_'. $vs; + $elem_end = '_element_end_'. $vs; + $cdata = '_pkginfo_cdata_'. $vs; + if (!method_exists($this, $elem_start) || + !method_exists($this, $elem_end) || + !method_exists($this, $cdata)) { + $this->raiseError("No handlers for package.xml version $attribs[version]"); + return; + } + xml_set_element_handler($xp, $elem_start, $elem_end); + xml_set_character_data_handler($xp, $cdata); + break; + } + } + } + + // }}} + // {{{ _element_end() + + /** + * XML parser callback for ending elements. Used while package + * format version is not yet known. + * + * @param resource $xp XML parser resource + * @param string $name name of ending element + * + * @return void + * + * @access private + */ + function _element_end($xp, $name) + { + } + + // }}} + + // Support for package DTD v1.0: + // {{{ _element_start_1_0() + + /** + * XML parser callback for ending elements. Used for version 1.0 + * packages. + * + * @param resource $xp XML parser resource + * @param string $name name of ending element + * + * @return void + * + * @access private + */ + function _element_start_1_0($xp, $name, $attribs) + { + array_push($this->element_stack, $name); + $this->current_element = $name; + $spos = sizeof($this->element_stack) - 2; + $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : ''; + $this->current_attributes = $attribs; + $this->cdata = ''; + switch ($name) { + case 'dir': + if ($this->in_changelog) { + break; + } + if ($attribs['name'] != '/') { + $this->dir_names[] = $attribs['name']; + } + if (isset($attribs['baseinstalldir'])) { + $this->dir_install = $attribs['baseinstalldir']; + } + if (isset($attribs['role'])) { + $this->dir_role = $attribs['role']; + } + break; + case 'file': + if ($this->in_changelog) { + break; + } + if (isset($attribs['name'])) { + $path = ''; + if (count($this->dir_names)) { + foreach ($this->dir_names as $dir) { + $path .= $dir . DIRECTORY_SEPARATOR; + } + } + $path .= $attribs['name']; + unset($attribs['name']); + $this->current_path = $path; + $this->filelist[$path] = $attribs; + // Set the baseinstalldir only if the file don't have this attrib + if (!isset($this->filelist[$path]['baseinstalldir']) && + isset($this->dir_install)) + { + $this->filelist[$path]['baseinstalldir'] = $this->dir_install; + } + // Set the Role + if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) { + $this->filelist[$path]['role'] = $this->dir_role; + } + } + break; + case 'replace': + if (!$this->in_changelog) { + $this->filelist[$this->current_path]['replacements'][] = $attribs; + } + break; + case 'maintainers': + $this->pkginfo['maintainers'] = array(); + $this->m_i = 0; // maintainers array index + break; + case 'maintainer': + // compatibility check + if (!isset($this->pkginfo['maintainers'])) { + $this->pkginfo['maintainers'] = array(); + $this->m_i = 0; + } + $this->pkginfo['maintainers'][$this->m_i] = array(); + $this->current_maintainer =& $this->pkginfo['maintainers'][$this->m_i]; + break; + case 'changelog': + $this->pkginfo['changelog'] = array(); + $this->c_i = 0; // changelog array index + $this->in_changelog = true; + break; + case 'release': + if ($this->in_changelog) { + $this->pkginfo['changelog'][$this->c_i] = array(); + $this->current_release = &$this->pkginfo['changelog'][$this->c_i]; + } else { + $this->current_release = &$this->pkginfo; + } + break; + case 'deps': + if (!$this->in_changelog) { + $this->pkginfo['release_deps'] = array(); + } + break; + case 'dep': + // dependencies array index + if (!$this->in_changelog) { + $this->d_i++; + $this->pkginfo['release_deps'][$this->d_i] = $attribs; + } + break; + case 'configureoptions': + if (!$this->in_changelog) { + $this->pkginfo['configure_options'] = array(); + } + break; + case 'configureoption': + if (!$this->in_changelog) { + $this->pkginfo['configure_options'][] = $attribs; + } + break; + case 'provides': + if (empty($attribs['type']) || empty($attribs['name'])) { + break; + } + $attribs['explicit'] = true; + $this->pkginfo['provides']["$attribs[type];$attribs[name]"] = $attribs; + break; + } + } + + // }}} + // {{{ _element_end_1_0() + + /** + * XML parser callback for ending elements. Used for version 1.0 + * packages. + * + * @param resource $xp XML parser resource + * @param string $name name of ending element + * + * @return void + * + * @access private + */ + function _element_end_1_0($xp, $name) + { + $data = trim($this->cdata); + switch ($name) { + case 'name': + switch ($this->prev_element) { + case 'package': + // XXX should we check the package name here? + $this->pkginfo['package'] = ereg_replace('[^a-zA-Z0-9._]', '_', $data); + break; + case 'maintainer': + $this->current_maintainer['name'] = $data; + break; + } + break; + case 'summary': + $this->pkginfo['summary'] = $data; + break; + case 'description': + $data = $this->_unIndent($this->cdata); + $this->pkginfo['description'] = $data; + break; + case 'user': + $this->current_maintainer['handle'] = $data; + break; + case 'email': + $this->current_maintainer['email'] = $data; + break; + case 'role': + $this->current_maintainer['role'] = $data; + break; + case 'version': + $data = ereg_replace ('[^a-zA-Z0-9._\-]', '_', $data); + if ($this->in_changelog) { + $this->current_release['version'] = $data; + } else { + $this->pkginfo['version'] = $data; + } + break; + case 'date': + if ($this->in_changelog) { + $this->current_release['release_date'] = $data; + } else { + $this->pkginfo['release_date'] = $data; + } + break; + case 'notes': + // try to "de-indent" release notes in case someone + // has been over-indenting their xml ;-) + $data = $this->_unIndent($this->cdata); + if ($this->in_changelog) { + $this->current_release['release_notes'] = $data; + } else { + $this->pkginfo['release_notes'] = $data; + } + break; + case 'warnings': + if ($this->in_changelog) { + $this->current_release['release_warnings'] = $data; + } else { + $this->pkginfo['release_warnings'] = $data; + } + break; + case 'state': + if ($this->in_changelog) { + $this->current_release['release_state'] = $data; + } else { + $this->pkginfo['release_state'] = $data; + } + break; + case 'license': + if ($this->in_changelog) { + $this->current_release['release_license'] = $data; + } else { + $this->pkginfo['release_license'] = $data; + } + break; + case 'dep': + if ($data && !$this->in_changelog) { + $this->pkginfo['release_deps'][$this->d_i]['name'] = $data; + } + break; + case 'dir': + if ($this->in_changelog) { + break; + } + array_pop($this->dir_names); + break; + case 'file': + if ($this->in_changelog) { + break; + } + if ($data) { + $path = ''; + if (count($this->dir_names)) { + foreach ($this->dir_names as $dir) { + $path .= $dir . DIRECTORY_SEPARATOR; + } + } + $path .= $data; + $this->filelist[$path] = $this->current_attributes; + // Set the baseinstalldir only if the file don't have this attrib + if (!isset($this->filelist[$path]['baseinstalldir']) && + isset($this->dir_install)) + { + $this->filelist[$path]['baseinstalldir'] = $this->dir_install; + } + // Set the Role + if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) { + $this->filelist[$path]['role'] = $this->dir_role; + } + } + break; + case 'maintainer': + if (empty($this->pkginfo['maintainers'][$this->m_i]['role'])) { + $this->pkginfo['maintainers'][$this->m_i]['role'] = 'lead'; + } + $this->m_i++; + break; + case 'release': + if ($this->in_changelog) { + $this->c_i++; + } + break; + case 'changelog': + $this->in_changelog = false; + break; + } + array_pop($this->element_stack); + $spos = sizeof($this->element_stack) - 1; + $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : ''; + $this->cdata = ''; + } + + // }}} + // {{{ _pkginfo_cdata_1_0() + + /** + * XML parser callback for character data. Used for version 1.0 + * packages. + * + * @param resource $xp XML parser resource + * @param string $name character data + * + * @return void + * + * @access private + */ + function _pkginfo_cdata_1_0($xp, $data) + { + if (isset($this->cdata)) { + $this->cdata .= $data; + } + } + + // }}} + + // {{{ infoFromTgzFile() + + /** + * Returns information about a package file. Expects the name of + * a gzipped tar file as input. + * + * @param string $file name of .tgz file + * + * @return array array with package information + * + * @access public + * + */ + function infoFromTgzFile($file) + { + if (!@is_file($file)) { + return $this->raiseError("could not open file \"$file\""); + } + $tar = new Archive_Tar($file); + if ($this->debug <= 1) { + $tar->pushErrorHandling(PEAR_ERROR_RETURN); + } + $content = $tar->listContent(); + if ($this->debug <= 1) { + $tar->popErrorHandling(); + } + if (!is_array($content)) { + $file = realpath($file); + return $this->raiseError("Could not get contents of package \"$file\"". + '. Invalid tgz file.'); + } + $xml = null; + foreach ($content as $file) { + $name = $file['filename']; + if ($name == 'package.xml') { + $xml = $name; + break; + } elseif (ereg('package.xml$', $name, $match)) { + $xml = $match[0]; + break; + } + } + $tmpdir = System::mkTemp(array('-d', 'pear')); + $this->addTempFile($tmpdir); + if (!$xml || !$tar->extractList(array($xml), $tmpdir)) { + return $this->raiseError('could not extract the package.xml file'); + } + return $this->infoFromDescriptionFile("$tmpdir/$xml"); + } + + // }}} + // {{{ infoFromDescriptionFile() + + /** + * Returns information about a package file. Expects the name of + * a package xml file as input. + * + * @param string $descfile name of package xml file + * + * @return array array with package information + * + * @access public + * + */ + function infoFromDescriptionFile($descfile) + { + if (!@is_file($descfile) || !is_readable($descfile) || + (!$fp = @fopen($descfile, 'r'))) { + return $this->raiseError("Unable to open $descfile"); + } + + // read the whole thing so we only get one cdata callback + // for each block of cdata + $data = fread($fp, filesize($descfile)); + return $this->infoFromString($data); + } + + // }}} + // {{{ infoFromString() + + /** + * Returns information about a package file. Expects the contents + * of a package xml file as input. + * + * @param string $data name of package xml file + * + * @return array array with package information + * + * @access public + * + */ + function infoFromString($data) + { + require_once('PEAR/Dependency.php'); + if (PEAR_Dependency::checkExtension($error, 'xml')) { + return $this->raiseError($error); + } + $xp = @xml_parser_create(); + if (!$xp) { + return $this->raiseError('Unable to create XML parser'); + } + xml_set_object($xp, $this); + xml_set_element_handler($xp, '_element_start', '_element_end'); + xml_set_character_data_handler($xp, '_pkginfo_cdata'); + xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false); + + $this->element_stack = array(); + $this->pkginfo = array('provides' => array()); + $this->current_element = false; + unset($this->dir_install); + $this->pkginfo['filelist'] = array(); + $this->filelist =& $this->pkginfo['filelist']; + $this->dir_names = array(); + $this->in_changelog = false; + $this->d_i = 0; + $this->cdata = ''; + $this->_validPackageFile = false; + + if (!xml_parse($xp, $data, 1)) { + $code = xml_get_error_code($xp); + $msg = sprintf("XML error: %s at line %d", + xml_error_string($code), + xml_get_current_line_number($xp)); + xml_parser_free($xp); + return $this->raiseError($msg, $code); + } + + xml_parser_free($xp); + + if (!$this->_validPackageFile) { + return $this->raiseError('Invalid Package File, no tag'); + } + foreach ($this->pkginfo as $k => $v) { + if (!is_array($v)) { + $this->pkginfo[$k] = trim($v); + } + } + return $this->pkginfo; + } + // }}} + // {{{ infoFromAny() + + /** + * Returns package information from different sources + * + * This method is able to extract information about a package + * from a .tgz archive or from a XML package definition file. + * + * @access public + * @param string Filename of the source ('package.xml', '.tgz') + * @return string + */ + function infoFromAny($info) + { + if (is_string($info) && file_exists($info)) { + $tmp = substr($info, -4); + if ($tmp == '.xml') { + $info = $this->infoFromDescriptionFile($info); + } elseif ($tmp == '.tar' || $tmp == '.tgz') { + $info = $this->infoFromTgzFile($info); + } else { + $fp = fopen($info, "r"); + $test = fread($fp, 5); + fclose($fp); + if ($test == "infoFromDescriptionFile($info); + } else { + $info = $this->infoFromTgzFile($info); + } + } + if (PEAR::isError($info)) { + return $this->raiseError($info); + } + } + return $info; + } + + // }}} + // {{{ xmlFromInfo() + + /** + * Return an XML document based on the package info (as returned + * by the PEAR_Common::infoFrom* methods). + * + * @param array $pkginfo package info + * + * @return string XML data + * + * @access public + */ + function xmlFromInfo($pkginfo) + { + static $maint_map = array( + "handle" => "user", + "name" => "name", + "email" => "email", + "role" => "role", + ); + $ret = "\n"; + $ret .= "\n"; + $ret .= " + $pkginfo[package] + ".htmlspecialchars($pkginfo['summary'])." + ".htmlspecialchars($pkginfo['description'])." + +"; + foreach ($pkginfo['maintainers'] as $maint) { + $ret .= " \n"; + foreach ($maint_map as $idx => $elm) { + $ret .= " <$elm>"; + $ret .= htmlspecialchars($maint[$idx]); + $ret .= "\n"; + } + $ret .= " \n"; + } + $ret .= " \n"; + $ret .= $this->_makeReleaseXml($pkginfo); + if (@sizeof($pkginfo['changelog']) > 0) { + $ret .= " \n"; + foreach ($pkginfo['changelog'] as $oldrelease) { + $ret .= $this->_makeReleaseXml($oldrelease, true); + } + $ret .= " \n"; + } + $ret .= "\n"; + return $ret; + } + + // }}} + // {{{ _makeReleaseXml() + + /** + * Generate part of an XML description with release information. + * + * @param array $pkginfo array with release information + * @param bool $changelog whether the result will be in a changelog element + * + * @return string XML data + * + * @access private + */ + function _makeReleaseXml($pkginfo, $changelog = false) + { + // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!! + $indent = $changelog ? " " : ""; + $ret = "$indent \n"; + if (!empty($pkginfo['version'])) { + $ret .= "$indent $pkginfo[version]\n"; + } + if (!empty($pkginfo['release_date'])) { + $ret .= "$indent $pkginfo[release_date]\n"; + } + if (!empty($pkginfo['release_license'])) { + $ret .= "$indent $pkginfo[release_license]\n"; + } + if (!empty($pkginfo['release_state'])) { + $ret .= "$indent $pkginfo[release_state]\n"; + } + if (!empty($pkginfo['release_notes'])) { + $ret .= "$indent ".htmlspecialchars($pkginfo['release_notes'])."\n"; + } + if (!empty($pkginfo['release_warnings'])) { + $ret .= "$indent ".htmlspecialchars($pkginfo['release_warnings'])."\n"; + } + if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) { + $ret .= "$indent \n"; + foreach ($pkginfo['release_deps'] as $dep) { + $ret .= "$indent $what) { + $ret .= "$indent $fa) { + @$ret .= "$indent $v) { + $ret .= " $k=\"" . htmlspecialchars($v) .'"'; + } + $ret .= "/>\n"; + } + @$ret .= "$indent \n"; + } + } + $ret .= "$indent \n"; + } + $ret .= "$indent \n"; + return $ret; + } + + // }}} + // {{{ validatePackageInfo() + + /** + * Validate XML package definition file. + * + * @param string $info Filename of the package archive or of the + * package definition file + * @param array $errors Array that will contain the errors + * @param array $warnings Array that will contain the warnings + * @param string $dir_prefix (optional) directory where source files + * may be found, or empty if they are not available + * @access public + * @return boolean + */ + function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '') + { + if (PEAR::isError($info = $this->infoFromAny($info))) { + return $this->raiseError($info); + } + if (!is_array($info)) { + return false; + } + + $errors = array(); + $warnings = array(); + if (!isset($info['package'])) { + $errors[] = 'missing package name'; + } elseif (!$this->validPackageName($info['package'])) { + $errors[] = 'invalid package name'; + } + $this->_packageName = $pn = $info['package']; + + if (empty($info['summary'])) { + $errors[] = 'missing summary'; + } elseif (strpos(trim($info['summary']), "\n") !== false) { + $warnings[] = 'summary should be on a single line'; + } + if (empty($info['description'])) { + $errors[] = 'missing description'; + } + if (empty($info['release_license'])) { + $errors[] = 'missing license'; + } + if (!isset($info['version'])) { + $errors[] = 'missing version'; + } elseif (!$this->validPackageVersion($info['version'])) { + $errors[] = 'invalid package release version'; + } + if (empty($info['release_state'])) { + $errors[] = 'missing release state'; + } elseif (!in_array($info['release_state'], PEAR_Common::getReleaseStates())) { + $errors[] = "invalid release state `$info[release_state]', should be one of: " + . implode(' ', PEAR_Common::getReleaseStates()); + } + if (empty($info['release_date'])) { + $errors[] = 'missing release date'; + } elseif (!preg_match('/^\d{4}-\d\d-\d\d$/', $info['release_date'])) { + $errors[] = "invalid release date `$info[release_date]', format is YYYY-MM-DD"; + } + if (empty($info['release_notes'])) { + $errors[] = "missing release notes"; + } + if (empty($info['maintainers'])) { + $errors[] = 'no maintainer(s)'; + } else { + $i = 1; + foreach ($info['maintainers'] as $m) { + if (empty($m['handle'])) { + $errors[] = "maintainer $i: missing handle"; + } + if (empty($m['role'])) { + $errors[] = "maintainer $i: missing role"; + } elseif (!in_array($m['role'], PEAR_Common::getUserRoles())) { + $errors[] = "maintainer $i: invalid role `$m[role]', should be one of: " + . implode(' ', PEAR_Common::getUserRoles()); + } + if (empty($m['name'])) { + $errors[] = "maintainer $i: missing name"; + } + if (empty($m['email'])) { + $errors[] = "maintainer $i: missing email"; + } + $i++; + } + } + if (!empty($info['release_deps'])) { + $i = 1; + foreach ($info['release_deps'] as $d) { + if (empty($d['type'])) { + $errors[] = "dependency $i: missing type"; + } elseif (!in_array($d['type'], PEAR_Common::getDependencyTypes())) { + $errors[] = "dependency $i: invalid type '$d[type]', should be one of: " . + implode(' ', PEAR_Common::getDependencyTypes()); + } + if (empty($d['rel'])) { + $errors[] = "dependency $i: missing relation"; + } elseif (!in_array($d['rel'], PEAR_Common::getDependencyRelations())) { + $errors[] = "dependency $i: invalid relation '$d[rel]', should be one of: " + . implode(' ', PEAR_Common::getDependencyRelations()); + } + if (!empty($d['optional'])) { + if (!in_array($d['optional'], array('yes', 'no'))) { + $errors[] = "dependency $i: invalid relation optional attribute '$d[optional]', should be one of: yes no"; + } else { + if (($d['rel'] == 'not' || $d['rel'] == 'ne') && $d['optional'] == 'yes') { + $errors[] = "dependency $i: 'not' and 'ne' dependencies cannot be " . + "optional"; + } + } + } + if ($d['rel'] != 'not' && $d['rel'] != 'has' && empty($d['version'])) { + $warnings[] = "dependency $i: missing version"; + } elseif (($d['rel'] == 'not' || $d['rel'] == 'has') && !empty($d['version'])) { + $warnings[] = "dependency $i: version ignored for `$d[rel]' dependencies"; + } + if ($d['rel'] == 'not' && !empty($d['version'])) { + $warnings[] = "dependency $i: 'not' defines a total conflict, to exclude " . + "specific versions, use 'ne'"; + } + if ($d['type'] == 'php' && !empty($d['name'])) { + $warnings[] = "dependency $i: name ignored for php type dependencies"; + } elseif ($d['type'] != 'php' && empty($d['name'])) { + $errors[] = "dependency $i: missing name"; + } + if ($d['type'] == 'php' && $d['rel'] == 'not') { + $errors[] = "dependency $i: PHP dependencies cannot use 'not' " . + "rel, use 'ne' to exclude versions"; + } + $i++; + } + } + if (!empty($info['configure_options'])) { + $i = 1; + foreach ($info['configure_options'] as $c) { + if (empty($c['name'])) { + $errors[] = "configure option $i: missing name"; + } + if (empty($c['prompt'])) { + $errors[] = "configure option $i: missing prompt"; + } + $i++; + } + } + if (empty($info['filelist'])) { + $errors[] = 'no files'; + } else { + foreach ($info['filelist'] as $file => $fa) { + if (empty($fa['role'])) { + $errors[] = "file $file: missing role"; + continue; + } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) { + $errors[] = "file $file: invalid role, should be one of: " + . implode(' ', PEAR_Common::getFileRoles()); + } + if ($fa['role'] == 'php' && $dir_prefix) { + $this->log(1, "Analyzing $file"); + $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file); + if ($srcinfo) { + $this->buildProvidesArray($srcinfo); + } + } + + // (ssb) Any checks we can do for baseinstalldir? + // (cox) Perhaps checks that either the target dir and + // baseInstall doesn't cointain "../../" + } + } + $this->_packageName = $pn = $info['package']; + $pnl = strlen($pn); + foreach ((array)$this->pkginfo['provides'] as $key => $what) { + if (isset($what['explicit'])) { + // skip conformance checks if the provides entry is + // specified in the package.xml file + continue; + } + extract($what); + if ($type == 'class') { + if (!strncasecmp($name, $pn, $pnl)) { + continue; + } + $warnings[] = "in $file: class \"$name\" not prefixed with package name \"$pn\""; + } elseif ($type == 'function') { + if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) { + continue; + } + $warnings[] = "in $file: function \"$name\" not prefixed with package name \"$pn\""; + } + } + + + return true; + } + + // }}} + // {{{ buildProvidesArray() + + /** + * Build a "provides" array from data returned by + * analyzeSourceCode(). The format of the built array is like + * this: + * + * array( + * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'), + * ... + * ) + * + * + * @param array $srcinfo array with information about a source file + * as returned by the analyzeSourceCode() method. + * + * @return void + * + * @access public + * + */ + function buildProvidesArray($srcinfo) + { + $file = basename($srcinfo['source_file']); + $pn = ''; + if (isset($this->_packageName)) { + $pn = $this->_packageName; + } + $pnl = strlen($pn); + foreach ($srcinfo['declared_classes'] as $class) { + $key = "class;$class"; + if (isset($this->pkginfo['provides'][$key])) { + continue; + } + $this->pkginfo['provides'][$key] = + array('file'=> $file, 'type' => 'class', 'name' => $class); + if (isset($srcinfo['inheritance'][$class])) { + $this->pkginfo['provides'][$key]['extends'] = + $srcinfo['inheritance'][$class]; + } + } + foreach ($srcinfo['declared_methods'] as $class => $methods) { + foreach ($methods as $method) { + $function = "$class::$method"; + $key = "function;$function"; + if ($method{0} == '_' || !strcasecmp($method, $class) || + isset($this->pkginfo['provides'][$key])) { + continue; + } + $this->pkginfo['provides'][$key] = + array('file'=> $file, 'type' => 'function', 'name' => $function); + } + } + + foreach ($srcinfo['declared_functions'] as $function) { + $key = "function;$function"; + if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) { + continue; + } + if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) { + $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\""; + } + $this->pkginfo['provides'][$key] = + array('file'=> $file, 'type' => 'function', 'name' => $function); + } + } + + // }}} + // {{{ analyzeSourceCode() + + /** + * Analyze the source code of the given PHP file + * + * @param string Filename of the PHP file + * @return mixed + * @access public + */ + function analyzeSourceCode($file) + { + if (!function_exists("token_get_all")) { + return false; + } + if (!defined('T_DOC_COMMENT')) { + define('T_DOC_COMMENT', T_COMMENT); + } + if (!defined('T_INTERFACE')) { + define('T_INTERFACE', -1); + } + if (!defined('T_IMPLEMENTS')) { + define('T_IMPLEMENTS', -1); + } + if (!$fp = @fopen($file, "r")) { + return false; + } + $contents = fread($fp, filesize($file)); + $tokens = token_get_all($contents); +/* + for ($i = 0; $i < sizeof($tokens); $i++) { + @list($token, $data) = $tokens[$i]; + if (is_string($token)) { + var_dump($token); + } else { + print token_name($token) . ' '; + var_dump(rtrim($data)); + } + } +*/ + $look_for = 0; + $paren_level = 0; + $bracket_level = 0; + $brace_level = 0; + $lastphpdoc = ''; + $current_class = ''; + $current_interface = ''; + $current_class_level = -1; + $current_function = ''; + $current_function_level = -1; + $declared_classes = array(); + $declared_interfaces = array(); + $declared_functions = array(); + $declared_methods = array(); + $used_classes = array(); + $used_functions = array(); + $extends = array(); + $implements = array(); + $nodeps = array(); + $inquote = false; + $interface = false; + for ($i = 0; $i < sizeof($tokens); $i++) { + if (is_array($tokens[$i])) { + list($token, $data) = $tokens[$i]; + } else { + $token = $tokens[$i]; + $data = ''; + } + if ($inquote) { + if ($token != '"') { + continue; + } else { + $inquote = false; + } + } + switch ($token) { + case T_WHITESPACE: + continue; + case ';': + if ($interface) { + $current_function = ''; + $current_function_level = -1; + } + break; + case '"': + $inquote = true; + break; + case T_CURLY_OPEN: + case T_DOLLAR_OPEN_CURLY_BRACES: + case '{': $brace_level++; continue 2; + case '}': + $brace_level--; + if ($current_class_level == $brace_level) { + $current_class = ''; + $current_class_level = -1; + } + if ($current_function_level == $brace_level) { + $current_function = ''; + $current_function_level = -1; + } + continue 2; + case '[': $bracket_level++; continue 2; + case ']': $bracket_level--; continue 2; + case '(': $paren_level++; continue 2; + case ')': $paren_level--; continue 2; + case T_INTERFACE: + $interface = true; + case T_CLASS: + if (($current_class_level != -1) || ($current_function_level != -1)) { + PEAR::raiseError("Parser error: Invalid PHP file $file", + PEAR_COMMON_ERROR_INVALIDPHP); + return false; + } + case T_FUNCTION: + case T_NEW: + case T_EXTENDS: + case T_IMPLEMENTS: + $look_for = $token; + continue 2; + case T_STRING: + if (version_compare(zend_version(), '2.0', '<')) { + if (in_array(strtolower($data), + array('public', 'private', 'protected', 'abstract', + 'interface', 'implements', 'clone', 'throw') + )) { + PEAR::raiseError('Error: PHP5 packages must be packaged by php 5 PEAR'); + return false; + } + } + if ($look_for == T_CLASS) { + $current_class = $data; + $current_class_level = $brace_level; + $declared_classes[] = $current_class; + } elseif ($look_for == T_INTERFACE) { + $current_interface = $data; + $current_class_level = $brace_level; + $declared_interfaces[] = $current_interface; + } elseif ($look_for == T_IMPLEMENTS) { + $implements[$current_class] = $data; + } elseif ($look_for == T_EXTENDS) { + $extends[$current_class] = $data; + } elseif ($look_for == T_FUNCTION) { + if ($current_class) { + $current_function = "$current_class::$data"; + $declared_methods[$current_class][] = $data; + } elseif ($current_interface) { + $current_function = "$current_interface::$data"; + $declared_methods[$current_interface][] = $data; + } else { + $current_function = $data; + $declared_functions[] = $current_function; + } + $current_function_level = $brace_level; + $m = array(); + } elseif ($look_for == T_NEW) { + $used_classes[$data] = true; + } + $look_for = 0; + continue 2; + case T_VARIABLE: + $look_for = 0; + continue 2; + case T_DOC_COMMENT: + case T_COMMENT: + if (preg_match('!^/\*\*\s!', $data)) { + $lastphpdoc = $data; + if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) { + $nodeps = array_merge($nodeps, $m[1]); + } + } + continue 2; + case T_DOUBLE_COLON: + if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) { + PEAR::raiseError("Parser error: Invalid PHP file $file", + PEAR_COMMON_ERROR_INVALIDPHP); + return false; + } + $class = $tokens[$i - 1][1]; + if (strtolower($class) != 'parent') { + $used_classes[$class] = true; + } + continue 2; + } + } + return array( + "source_file" => $file, + "declared_classes" => $declared_classes, + "declared_interfaces" => $declared_interfaces, + "declared_methods" => $declared_methods, + "declared_functions" => $declared_functions, + "used_classes" => array_diff(array_keys($used_classes), $nodeps), + "inheritance" => $extends, + "implements" => $implements, + ); + } + + // }}} + // {{{ betterStates() + + /** + * Return an array containing all of the states that are more stable than + * or equal to the passed in state + * + * @param string Release state + * @param boolean Determines whether to include $state in the list + * @return false|array False if $state is not a valid release state + */ + function betterStates($state, $include = false) + { + static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable'); + $i = array_search($state, $states); + if ($i === false) { + return false; + } + if ($include) { + $i--; + } + return array_slice($states, $i + 1); + } + + // }}} + // {{{ detectDependencies() + + function detectDependencies($any, $status_callback = null) + { + if (!function_exists("token_get_all")) { + return false; + } + if (PEAR::isError($info = $this->infoFromAny($any))) { + return $this->raiseError($info); + } + if (!is_array($info)) { + return false; + } + $deps = array(); + $used_c = $decl_c = $decl_f = $decl_m = array(); + foreach ($info['filelist'] as $file => $fa) { + $tmp = $this->analyzeSourceCode($file); + $used_c = @array_merge($used_c, $tmp['used_classes']); + $decl_c = @array_merge($decl_c, $tmp['declared_classes']); + $decl_f = @array_merge($decl_f, $tmp['declared_functions']); + $decl_m = @array_merge($decl_m, $tmp['declared_methods']); + $inheri = @array_merge($inheri, $tmp['inheritance']); + } + $used_c = array_unique($used_c); + $decl_c = array_unique($decl_c); + $undecl_c = array_diff($used_c, $decl_c); + return array('used_classes' => $used_c, + 'declared_classes' => $decl_c, + 'declared_methods' => $decl_m, + 'declared_functions' => $decl_f, + 'undeclared_classes' => $undecl_c, + 'inheritance' => $inheri, + ); + } + + // }}} + // {{{ getUserRoles() + + /** + * Get the valid roles for a PEAR package maintainer + * + * @return array + * @static + */ + function getUserRoles() + { + return $GLOBALS['_PEAR_Common_maintainer_roles']; + } + + // }}} + // {{{ getReleaseStates() + + /** + * Get the valid package release states of packages + * + * @return array + * @static + */ + function getReleaseStates() + { + return $GLOBALS['_PEAR_Common_release_states']; + } + + // }}} + // {{{ getDependencyTypes() + + /** + * Get the implemented dependency types (php, ext, pkg etc.) + * + * @return array + * @static + */ + function getDependencyTypes() + { + return $GLOBALS['_PEAR_Common_dependency_types']; + } + + // }}} + // {{{ getDependencyRelations() + + /** + * Get the implemented dependency relations (has, lt, ge etc.) + * + * @return array + * @static + */ + function getDependencyRelations() + { + return $GLOBALS['_PEAR_Common_dependency_relations']; + } + + // }}} + // {{{ getFileRoles() + + /** + * Get the implemented file roles + * + * @return array + * @static + */ + function getFileRoles() + { + return $GLOBALS['_PEAR_Common_file_roles']; + } + + // }}} + // {{{ getReplacementTypes() + + /** + * Get the implemented file replacement types in + * + * @return array + * @static + */ + function getReplacementTypes() + { + return $GLOBALS['_PEAR_Common_replacement_types']; + } + + // }}} + // {{{ getProvideTypes() + + /** + * Get the implemented file replacement types in + * + * @return array + * @static + */ + function getProvideTypes() + { + return $GLOBALS['_PEAR_Common_provide_types']; + } + + // }}} + // {{{ getScriptPhases() + + /** + * Get the implemented file replacement types in + * + * @return array + * @static + */ + function getScriptPhases() + { + return $GLOBALS['_PEAR_Common_script_phases']; + } + + // }}} + // {{{ validPackageName() + + /** + * Test whether a string contains a valid package name. + * + * @param string $name the package name to test + * + * @return bool + * + * @access public + */ + function validPackageName($name) + { + return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name); + } + + + // }}} + // {{{ validPackageVersion() + + /** + * Test whether a string contains a valid package version. + * + * @param string $ver the package version to test + * + * @return bool + * + * @access public + */ + function validPackageVersion($ver) + { + return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver); + } + + + // }}} + + // {{{ downloadHttp() + + /** + * Download a file through HTTP. Considers suggested file name in + * Content-disposition: header and can run a callback function for + * different events. The callback will be called with two + * parameters: the callback type, and parameters. The implemented + * callback types are: + * + * 'setup' called at the very beginning, parameter is a UI object + * that should be used for all output + * 'message' the parameter is a string with an informational message + * 'saveas' may be used to save with a different file name, the + * parameter is the filename that is about to be used. + * If a 'saveas' callback returns a non-empty string, + * that file name will be used as the filename instead. + * Note that $save_dir will not be affected by this, only + * the basename of the file. + * 'start' download is starting, parameter is number of bytes + * that are expected, or -1 if unknown + * 'bytesread' parameter is the number of bytes read so far + * 'done' download is complete, parameter is the total number + * of bytes read + * 'connfailed' if the TCP connection fails, this callback is called + * with array(host,port,errno,errmsg) + * 'writefailed' if writing to disk fails, this callback is called + * with array(destfile,errmsg) + * + * If an HTTP proxy has been configured (http_proxy PEAR_Config + * setting), the proxy will be used. + * + * @param string $url the URL to download + * @param object $ui PEAR_Frontend_* instance + * @param object $config PEAR_Config instance + * @param string $save_dir (optional) directory to save file in + * @param mixed $callback (optional) function/method to call for status + * updates + * + * @return string Returns the full path of the downloaded file or a PEAR + * error on failure. If the error is caused by + * socket-related errors, the error object will + * have the fsockopen error code available through + * getCode(). + * + * @access public + */ + function downloadHttp($url, &$ui, $save_dir = '.', $callback = null) + { + if ($callback) { + call_user_func($callback, 'setup', array(&$ui)); + } + if (preg_match('!^http://([^/:?#]*)(:(\d+))?(/.*)!', $url, $matches)) { + list(,$host,,$port,$path) = $matches; + } + if (isset($this)) { + $config = &$this->config; + } else { + $config = &PEAR_Config::singleton(); + } + $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; + if ($proxy = parse_url($config->get('http_proxy'))) { + $proxy_host = @$proxy['host']; + $proxy_port = @$proxy['port']; + $proxy_user = @$proxy['user']; + $proxy_pass = @$proxy['pass']; + + if ($proxy_port == '') { + $proxy_port = 8080; + } + if ($callback) { + call_user_func($callback, 'message', "Using HTTP proxy $host:$port"); + } + } + if (empty($port)) { + $port = 80; + } + if ($proxy_host != '') { + $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr); + if (!$fp) { + if ($callback) { + call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port, + $errno, $errstr)); + } + return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno); + } + $request = "GET $url HTTP/1.0\r\n"; + } else { + $fp = @fsockopen($host, $port, $errno, $errstr); + if (!$fp) { + if ($callback) { + call_user_func($callback, 'connfailed', array($host, $port, + $errno, $errstr)); + } + return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno); + } + $request = "GET $path HTTP/1.0\r\n"; + } + $request .= "Host: $host:$port\r\n". + "User-Agent: PHP/".PHP_VERSION."\r\n"; + if ($proxy_host != '' && $proxy_user != '') { + $request .= 'Proxy-Authorization: Basic ' . + base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n"; + } + $request .= "\r\n"; + fwrite($fp, $request); + $headers = array(); + while (trim($line = fgets($fp, 1024))) { + if (preg_match('/^([^:]+):\s+(.*)\s*$/', $line, $matches)) { + $headers[strtolower($matches[1])] = trim($matches[2]); + } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) { + if ($matches[1] != 200) { + return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)"); + } + } + } + if (isset($headers['content-disposition']) && + preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|$)/', $headers['content-disposition'], $matches)) { + $save_as = basename($matches[1]); + } else { + $save_as = basename($url); + } + if ($callback) { + $tmp = call_user_func($callback, 'saveas', $save_as); + if ($tmp) { + $save_as = $tmp; + } + } + $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as; + if (!$wp = @fopen($dest_file, 'wb')) { + fclose($fp); + if ($callback) { + call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg)); + } + return PEAR::raiseError("could not open $dest_file for writing"); + } + if (isset($headers['content-length'])) { + $length = $headers['content-length']; + } else { + $length = -1; + } + $bytes = 0; + if ($callback) { + call_user_func($callback, 'start', array(basename($dest_file), $length)); + } + while ($data = @fread($fp, 1024)) { + $bytes += strlen($data); + if ($callback) { + call_user_func($callback, 'bytesread', $bytes); + } + if (!@fwrite($wp, $data)) { + fclose($fp); + if ($callback) { + call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg)); + } + return PEAR::raiseError("$dest_file: write failed ($php_errormsg)"); + } + } + fclose($fp); + fclose($wp); + if ($callback) { + call_user_func($callback, 'done', $bytes); + } + return $dest_file; + } + + // }}} + // {{{ sortPkgDeps() + + /** + * Sort a list of arrays of array(downloaded packagefilename) by dependency. + * + * It also removes duplicate dependencies + * @param array + * @param boolean Sort packages in reverse order if true + * @return array array of array(packagefilename, package.xml contents) + */ + function sortPkgDeps(&$packages, $uninstall = false) + { + $ret = array(); + if ($uninstall) { + foreach($packages as $packageinfo) { + $ret[] = array('info' => $packageinfo); + } + } else { + foreach($packages as $packagefile) { + if (!is_array($packagefile)) { + $ret[] = array('file' => $packagefile, + 'info' => $a = $this->infoFromAny($packagefile), + 'pkg' => $a['package']); + } else { + $ret[] = $packagefile; + } + } + } + $checkdupes = array(); + $newret = array(); + foreach($ret as $i => $p) { + if (!isset($checkdupes[$p['info']['package']])) { + $checkdupes[$p['info']['package']][] = $i; + $newret[] = $p; + } + } + $this->_packageSortTree = $this->_getPkgDepTree($newret); + + $func = $uninstall ? '_sortPkgDepsRev' : '_sortPkgDeps'; + usort($newret, array(&$this, $func)); + $this->_packageSortTree = null; + $packages = $newret; + } + + // }}} + // {{{ _sortPkgDeps() + + /** + * Compare two package's package.xml, and sort + * so that dependencies are installed first + * + * This is a crude compare, real dependency checking is done on install. + * The only purpose this serves is to make the command-line + * order-independent (you can list a dependent package first, and + * installation occurs in the order required) + * @access private + */ + function _sortPkgDeps($p1, $p2) + { + $p1name = $p1['info']['package']; + $p2name = $p2['info']['package']; + $p1deps = $this->_getPkgDeps($p1); + $p2deps = $this->_getPkgDeps($p2); + if (!count($p1deps) && !count($p2deps)) { + return 0; // order makes no difference + } + if (!count($p1deps)) { + return -1; // package 2 has dependencies, package 1 doesn't + } + if (!count($p2deps)) { + return 1; // package 1 has dependencies, package 2 doesn't + } + // both have dependencies + if (in_array($p1name, $p2deps)) { + return -1; // put package 1 first: package 2 depends on package 1 + } + if (in_array($p2name, $p1deps)) { + return 1; // put package 2 first: package 1 depends on package 2 + } + if ($this->_removedDependency($p1name, $p2name)) { + return -1; // put package 1 first: package 2 depends on packages that depend on package 1 + } + if ($this->_removedDependency($p2name, $p1name)) { + return 1; // put package 2 first: package 1 depends on packages that depend on package 2 + } + // doesn't really matter if neither depends on the other + return 0; + } + + // }}} + // {{{ _sortPkgDepsRev() + + /** + * Compare two package's package.xml, and sort + * so that dependencies are uninstalled last + * + * This is a crude compare, real dependency checking is done on uninstall. + * The only purpose this serves is to make the command-line + * order-independent (you can list a dependency first, and + * uninstallation occurs in the order required) + * @access private + */ + function _sortPkgDepsRev($p1, $p2) + { + $p1name = $p1['info']['package']; + $p2name = $p2['info']['package']; + $p1deps = $this->_getRevPkgDeps($p1); + $p2deps = $this->_getRevPkgDeps($p2); + if (!count($p1deps) && !count($p2deps)) { + return 0; // order makes no difference + } + if (!count($p1deps)) { + return 1; // package 2 has dependencies, package 1 doesn't + } + if (!count($p2deps)) { + return -1; // package 2 has dependencies, package 1 doesn't + } + // both have dependencies + if (in_array($p1name, $p2deps)) { + return 1; // put package 1 last + } + if (in_array($p2name, $p1deps)) { + return -1; // put package 2 last + } + if ($this->_removedDependency($p1name, $p2name)) { + return 1; // put package 1 last: package 2 depends on packages that depend on package 1 + } + if ($this->_removedDependency($p2name, $p1name)) { + return -1; // put package 2 last: package 1 depends on packages that depend on package 2 + } + // doesn't really matter if neither depends on the other + return 0; + } + + // }}} + // {{{ _getPkgDeps() + + /** + * get an array of package dependency names + * @param array + * @return array + * @access private + */ + function _getPkgDeps($p) + { + if (!isset($p['info']['releases'])) { + return $this->_getRevPkgDeps($p); + } + $rel = array_shift($p['info']['releases']); + if (!isset($rel['deps'])) { + return array(); + } + $ret = array(); + foreach($rel['deps'] as $dep) { + if ($dep['type'] == 'pkg') { + $ret[] = $dep['name']; + } + } + return $ret; + } + + // }}} + // {{{ _getPkgDeps() + + /** + * get an array representation of the package dependency tree + * @return array + * @access private + */ + function _getPkgDepTree($packages) + { + $tree = array(); + foreach ($packages as $p) { + $package = $p['info']['package']; + $deps = $this->_getPkgDeps($p); + $tree[$package] = $deps; + } + return $tree; + } + + // }}} + // {{{ _removedDependency($p1, $p2) + + /** + * get an array of package dependency names for uninstall + * @param string package 1 name + * @param string package 2 name + * @return bool + * @access private + */ + function _removedDependency($p1, $p2) + { + if (empty($this->_packageSortTree[$p2])) { + return false; + } + if (!in_array($p1, $this->_packageSortTree[$p2])) { + foreach ($this->_packageSortTree[$p2] as $potential) { + if ($this->_removedDependency($p1, $potential)) { + return true; + } + } + return false; + } + return true; + } + + // }}} + // {{{ _getRevPkgDeps() + + /** + * get an array of package dependency names for uninstall + * @param array + * @return array + * @access private + */ + function _getRevPkgDeps($p) + { + if (!isset($p['info']['release_deps'])) { + return array(); + } + $ret = array(); + foreach($p['info']['release_deps'] as $dep) { + if ($dep['type'] == 'pkg') { + $ret[] = $dep['name']; + } + } + return $ret; + } + + // }}} +} + +?> diff --git a/src/www/lib/pear/PEAR/Config.php b/src/www/lib/pear/PEAR/Config.php new file mode 100644 index 00000000..83d8b5a7 --- /dev/null +++ b/src/www/lib/pear/PEAR/Config.php @@ -0,0 +1,1169 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Config.php,v 1.1 2005/05/28 01:55:12 henrique Exp $ + +require_once 'PEAR.php'; +require_once 'System.php'; + +/** + * Last created PEAR_Config instance. + * @var object + */ +$GLOBALS['_PEAR_Config_instance'] = null; +if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) { + $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear'; +} else { + $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR; +} + +// Below we define constants with default values for all configuration +// parameters except username/password. All of them can have their +// defaults set through environment variables. The reason we use the +// PHP_ prefix is for some security, PHP protects environment +// variables starting with PHP_*. + +if (getenv('PHP_PEAR_SYSCONF_DIR')) { + define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR')); +} elseif (getenv('SystemRoot')) { + define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot')); +} else { + define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR); +} + +// Default for master_server +if (getenv('PHP_PEAR_MASTER_SERVER')) { + define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER')); +} else { + define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net'); +} + +// Default for http_proxy +if (getenv('PHP_PEAR_HTTP_PROXY')) { + define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY')); +} elseif (getenv('http_proxy')) { + define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy')); +} else { + define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', ''); +} + +// Default for php_dir +if (getenv('PHP_PEAR_INSTALL_DIR')) { + define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR')); +} else { + if (@is_dir($PEAR_INSTALL_DIR)) { + define('PEAR_CONFIG_DEFAULT_PHP_DIR', + $PEAR_INSTALL_DIR); + } else { + define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR); + } +} + +// Default for ext_dir +if (getenv('PHP_PEAR_EXTENSION_DIR')) { + define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR')); +} else { + if (ini_get('extension_dir')) { + define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir')); + } elseif (defined('PEAR_EXTENSION_DIR') && @is_dir(PEAR_EXTENSION_DIR)) { + define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR); + } elseif (defined('PHP_EXTENSION_DIR')) { + define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR); + } else { + define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.'); + } +} + +// Default for doc_dir +if (getenv('PHP_PEAR_DOC_DIR')) { + define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_DOC_DIR', + $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs'); +} + +// Default for bin_dir +if (getenv('PHP_PEAR_BIN_DIR')) { + define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR); +} + +// Default for data_dir +if (getenv('PHP_PEAR_DATA_DIR')) { + define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_DATA_DIR', + $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data'); +} + +// Default for test_dir +if (getenv('PHP_PEAR_TEST_DIR')) { + define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_TEST_DIR', + $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests'); +} + +// Default for cache_dir +if (getenv('PHP_PEAR_CACHE_DIR')) { + define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR')); +} else { + define('PEAR_CONFIG_DEFAULT_CACHE_DIR', + System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' . + DIRECTORY_SEPARATOR . 'cache'); +} + +// Default for php_bin +if (getenv('PHP_PEAR_PHP_BIN')) { + define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN')); +} else { + define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR. + DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : '')); +} + +// Default for verbose +if (getenv('PHP_PEAR_VERBOSE')) { + define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE')); +} else { + define('PEAR_CONFIG_DEFAULT_VERBOSE', 1); +} + +// Default for preferred_state +if (getenv('PHP_PEAR_PREFERRED_STATE')) { + define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE')); +} else { + define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable'); +} + +// Default for umask +if (getenv('PHP_PEAR_UMASK')) { + define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK')); +} else { + define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask())); +} + +// Default for cache_ttl +if (getenv('PHP_PEAR_CACHE_TTL')) { + define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL')); +} else { + define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600); +} + +// Default for sig_type +if (getenv('PHP_PEAR_SIG_TYPE')) { + define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE')); +} else { + define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg'); +} + +// Default for sig_bin +if (getenv('PHP_PEAR_SIG_BIN')) { + define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN')); +} else { + define('PEAR_CONFIG_DEFAULT_SIG_BIN', + System::which( + 'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg')); +} + +// Default for sig_keydir +if (getenv('PHP_PEAR_SIG_KEYDIR')) { + define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR')); +} else { + define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', + PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys'); +} + +/** + * This is a class for storing configuration data, keeping track of + * which are system-defined, user-defined or defaulted. + */ +class PEAR_Config extends PEAR +{ + // {{{ properties + + /** + * Array of config files used. + * + * @var array layer => config file + */ + var $files = array( + 'system' => '', + 'user' => '', + ); + + var $layers = array(); + + /** + * Configuration data, two-dimensional array where the first + * dimension is the config layer ('user', 'system' and 'default'), + * and the second dimension is keyname => value. + * + * The order in the first dimension is important! Earlier + * layers will shadow later ones when a config value is + * requested (if a 'user' value exists, it will be returned first, + * then 'system' and finally 'default'). + * + * @var array layer => array(keyname => value, ...) + */ + var $configuration = array( + 'user' => array(), + 'system' => array(), + 'default' => array(), + ); + + /** + * Information about the configuration data. Stores the type, + * default value and a documentation string for each configuration + * value. + * + * @var array layer => array(infotype => value, ...) + */ + var $configuration_info = array( + // Internet Access + 'master_server' => array( + 'type' => 'string', + 'default' => 'pear.php.net', + 'doc' => 'name of the main PEAR server', + 'prompt' => 'PEAR server', + 'group' => 'Internet Access', + ), + 'http_proxy' => array( + 'type' => 'string', + 'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY, + 'doc' => 'HTTP proxy (host:port) to use when downloading packages', + 'prompt' => 'HTTP Proxy Server Address', + 'group' => 'Internet Access', + ), + // File Locations + 'php_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_PHP_DIR, + 'doc' => 'directory where .php files are installed', + 'prompt' => 'PEAR directory', + 'group' => 'File Locations', + ), + 'ext_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_EXT_DIR, + 'doc' => 'directory where loadable extensions are installed', + 'prompt' => 'PHP extension directory', + 'group' => 'File Locations', + ), + 'doc_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_DOC_DIR, + 'doc' => 'directory where documentation is installed', + 'prompt' => 'PEAR documentation directory', + 'group' => 'File Locations', + ), + 'bin_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_BIN_DIR, + 'doc' => 'directory where executables are installed', + 'prompt' => 'PEAR executables directory', + 'group' => 'File Locations', + ), + 'data_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_DATA_DIR, + 'doc' => 'directory where data files are installed', + 'prompt' => 'PEAR data directory', + 'group' => 'File Locations (Advanced)', + ), + 'test_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_TEST_DIR, + 'doc' => 'directory where regression tests are installed', + 'prompt' => 'PEAR test directory', + 'group' => 'File Locations (Advanced)', + ), + 'cache_dir' => array( + 'type' => 'directory', + 'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR, + 'doc' => 'directory which is used for XMLRPC cache', + 'prompt' => 'PEAR Installer cache directory', + 'group' => 'File Locations (Advanced)', + ), + 'php_bin' => array( + 'type' => 'file', + 'default' => PEAR_CONFIG_DEFAULT_PHP_BIN, + 'doc' => 'PHP CLI/CGI binary for executing scripts', + 'prompt' => 'PHP CLI/CGI binary', + 'group' => 'File Locations (Advanced)', + ), + // Maintainers + 'username' => array( + 'type' => 'string', + 'default' => '', + 'doc' => '(maintainers) your PEAR account name', + 'prompt' => 'PEAR username (for maintainers)', + 'group' => 'Maintainers', + ), + 'password' => array( + 'type' => 'password', + 'default' => '', + 'doc' => '(maintainers) your PEAR account password', + 'prompt' => 'PEAR password (for maintainers)', + 'group' => 'Maintainers', + ), + // Advanced + 'verbose' => array( + 'type' => 'integer', + 'default' => PEAR_CONFIG_DEFAULT_VERBOSE, + 'doc' => 'verbosity level +0: really quiet +1: somewhat quiet +2: verbose +3: debug', + 'prompt' => 'Debug Log Level', + 'group' => 'Advanced', + ), + 'preferred_state' => array( + 'type' => 'set', + 'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE, + 'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified', + 'valid_set' => array( + 'stable', 'beta', 'alpha', 'devel', 'snapshot'), + 'prompt' => 'Preferred Package State', + 'group' => 'Advanced', + ), + 'umask' => array( + 'type' => 'mask', + 'default' => PEAR_CONFIG_DEFAULT_UMASK, + 'doc' => 'umask used when creating files (Unix-like systems only)', + 'prompt' => 'Unix file mask', + 'group' => 'Advanced', + ), + 'cache_ttl' => array( + 'type' => 'integer', + 'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL, + 'doc' => 'amount of secs where the local cache is used and not updated', + 'prompt' => 'Cache TimeToLive', + 'group' => 'Advanced', + ), + 'sig_type' => array( + 'type' => 'set', + 'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE, + 'doc' => 'which package signature mechanism to use', + 'valid_set' => array('gpg'), + 'prompt' => 'Package Signature Type', + 'group' => 'Maintainers', + ), + 'sig_bin' => array( + 'type' => 'string', + 'default' => PEAR_CONFIG_DEFAULT_SIG_BIN, + 'doc' => 'which package signature mechanism to use', + 'prompt' => 'Signature Handling Program', + 'group' => 'Maintainers', + ), + 'sig_keyid' => array( + 'type' => 'string', + 'default' => '', + 'doc' => 'which key to use for signing with', + 'prompt' => 'Signature Key Id', + 'group' => 'Maintainers', + ), + 'sig_keydir' => array( + 'type' => 'string', + 'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR, + 'doc' => 'which package signature mechanism to use', + 'prompt' => 'Signature Key Directory', + 'group' => 'Maintainers', + ), + ); + + // }}} + + // {{{ PEAR_Config([file], [defaults_file]) + + /** + * Constructor. + * + * @param string (optional) file to read user-defined options from + * @param string (optional) file to read system-wide defaults from + * + * @access public + * + * @see PEAR_Config::singleton + */ + function PEAR_Config($user_file = '', $system_file = '') + { + $this->PEAR(); + $sl = DIRECTORY_SEPARATOR; + if (empty($user_file)) { + if (OS_WINDOWS) { + $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini'; + } else { + $user_file = getenv('HOME') . $sl . '.pearrc'; + } + } + if (empty($system_file)) { + if (OS_WINDOWS) { + $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini'; + } else { + $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf'; + } + } + $this->layers = array_keys($this->configuration); + $this->files['user'] = $user_file; + $this->files['system'] = $system_file; + if ($user_file && file_exists($user_file)) { + $this->readConfigFile($user_file); + } + if ($system_file && file_exists($system_file)) { + $this->mergeConfigFile($system_file, false, 'system'); + } + foreach ($this->configuration_info as $key => $info) { + $this->configuration['default'][$key] = $info['default']; + } + //$GLOBALS['_PEAR_Config_instance'] = &$this; + } + + // }}} + // {{{ singleton([file], [defaults_file]) + + /** + * Static singleton method. If you want to keep only one instance + * of this class in use, this method will give you a reference to + * the last created PEAR_Config object if one exists, or create a + * new object. + * + * @param string (optional) file to read user-defined options from + * @param string (optional) file to read system-wide defaults from + * + * @return object an existing or new PEAR_Config instance + * + * @access public + * + * @see PEAR_Config::PEAR_Config + */ + function &singleton($user_file = '', $system_file = '') + { + if (is_object($GLOBALS['_PEAR_Config_instance'])) { + return $GLOBALS['_PEAR_Config_instance']; + } + $GLOBALS['_PEAR_Config_instance'] = + &new PEAR_Config($user_file, $system_file); + return $GLOBALS['_PEAR_Config_instance']; + } + + // }}} + // {{{ readConfigFile([file], [layer]) + + /** + * Reads configuration data from a file. All existing values in + * the config layer are discarded and replaced with data from the + * file. + * + * @param string (optional) file to read from, if NULL or not + * specified, the last-used file for the same layer (second param) + * is used + * + * @param string (optional) config layer to insert data into + * ('user' or 'system') + * + * @return bool TRUE on success or a PEAR error on failure + * + * @access public + */ + function readConfigFile($file = null, $layer = 'user') + { + if (empty($this->files[$layer])) { + return $this->raiseError("unknown config file type `$layer'"); + } + if ($file === null) { + $file = $this->files[$layer]; + } + $data = $this->_readConfigDataFrom($file); + if (PEAR::isError($data)) { + return $data; + } + $this->_decodeInput($data); + $this->configuration[$layer] = $data; + return true; + } + + // }}} + // {{{ mergeConfigFile(file, [override], [layer]) + + /** + * Merges data into a config layer from a file. Does the same + * thing as readConfigFile, except it does not replace all + * existing values in the config layer. + * + * @param string file to read from + * + * @param bool (optional) whether to overwrite existing data + * (default TRUE) + * + * @param string config layer to insert data into ('user' or + * 'system') + * + * @return bool TRUE on success or a PEAR error on failure + * + * @access public. + */ + function mergeConfigFile($file, $override = true, $layer = 'user') + { + if (empty($this->files[$layer])) { + return $this->raiseError("unknown config file type `$layer'"); + } + if ($file === null) { + $file = $this->files[$layer]; + } + $data = $this->_readConfigDataFrom($file); + if (PEAR::isError($data)) { + return $data; + } + $this->_decodeInput($data); + if ($override) { + $this->configuration[$layer] = array_merge($this->configuration[$layer], $data); + } else { + $this->configuration[$layer] = array_merge($data, $this->configuration[$layer]); + } + return true; + } + + // }}} + // {{{ writeConfigFile([file], [layer]) + + /** + * Writes data into a config layer from a file. + * + * @param string file to read from + * + * @param bool (optional) whether to overwrite existing data + * (default TRUE) + * + * @param string config layer to insert data into ('user' or + * 'system') + * + * @return bool TRUE on success or a PEAR error on failure + * + * @access public. + */ + function writeConfigFile($file = null, $layer = 'user', $data = null) + { + if ($layer == 'both' || $layer == 'all') { + foreach ($this->files as $type => $file) { + $err = $this->writeConfigFile($file, $type, $data); + if (PEAR::isError($err)) { + return $err; + } + } + return true; + } + if (empty($this->files[$layer])) { + return $this->raiseError("unknown config file type `$layer'"); + } + if ($file === null) { + $file = $this->files[$layer]; + } + $data = ($data === null) ? $this->configuration[$layer] : $data; + $this->_encodeOutput($data); + $opt = array('-p', dirname($file)); + if (!@System::mkDir($opt)) { + return $this->raiseError("could not create directory: " . dirname($file)); + } + if (@is_file($file) && !@is_writeable($file)) { + return $this->raiseError("no write access to $file!"); + } + $fp = @fopen($file, "w"); + if (!$fp) { + return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed"); + } + $contents = "#PEAR_Config 0.9\n" . serialize($data); + if (!@fwrite($fp, $contents)) { + return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed"); + } + return true; + } + + // }}} + // {{{ _readConfigDataFrom(file) + + /** + * Reads configuration data from a file and returns the parsed data + * in an array. + * + * @param string file to read from + * + * @return array configuration data or a PEAR error on failure + * + * @access private + */ + function _readConfigDataFrom($file) + { + $fp = @fopen($file, "r"); + if (!$fp) { + return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed"); + } + $size = filesize($file); + $rt = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + $contents = fread($fp, $size); + set_magic_quotes_runtime($rt); + fclose($fp); + $version = '0.1'; + if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) { + $version = $matches[1]; + $contents = substr($contents, strlen($matches[0])); + } + if (version_compare("$version", '1', '<')) { + $data = unserialize($contents); + if (!is_array($data)) { + if (strlen(trim($contents)) > 0) { + $error = "PEAR_Config: bad data in $file"; +// if (isset($this)) { + return $this->raiseError($error); +// } else { +// return PEAR::raiseError($error); + } else { + $data = array(); + } + } + // add parsing of newer formats here... + } else { + return $this->raiseError("$file: unknown version `$version'"); + } + return $data; + } + + // }}} + // {{{ getConfFile(layer) + /** + * Gets the file used for storing the config for a layer + * + * @param string $layer 'user' or 'system' + */ + + function getConfFile($layer) + { + return $this->files[$layer]; + } + + // }}} + // {{{ _encodeOutput(&data) + + /** + * Encodes/scrambles configuration data before writing to files. + * Currently, 'password' values will be base64-encoded as to avoid + * that people spot cleartext passwords by accident. + * + * @param array (reference) array to encode values in + * + * @return bool TRUE on success + * + * @access private + */ + function _encodeOutput(&$data) + { + foreach ($data as $key => $value) { + if (!isset($this->configuration_info[$key])) { + continue; + } + $type = $this->configuration_info[$key]['type']; + switch ($type) { + // we base64-encode passwords so they are at least + // not shown in plain by accident + case 'password': { + $data[$key] = base64_encode($data[$key]); + break; + } + case 'mask': { + $data[$key] = octdec($data[$key]); + break; + } + } + } + return true; + } + + // }}} + // {{{ _decodeInput(&data) + + /** + * Decodes/unscrambles configuration data after reading from files. + * + * @param array (reference) array to encode values in + * + * @return bool TRUE on success + * + * @access private + * + * @see PEAR_Config::_encodeOutput + */ + function _decodeInput(&$data) + { + if (!is_array($data)) { + return true; + } + foreach ($data as $key => $value) { + if (!isset($this->configuration_info[$key])) { + continue; + } + $type = $this->configuration_info[$key]['type']; + switch ($type) { + case 'password': { + $data[$key] = base64_decode($data[$key]); + break; + } + case 'mask': { + $data[$key] = decoct($data[$key]); + break; + } + } + } + return true; + } + + // }}} + // {{{ get(key, [layer]) + + /** + * Returns a configuration value, prioritizing layers as per the + * layers property. + * + * @param string config key + * + * @return mixed the config value, or NULL if not found + * + * @access public + */ + function get($key, $layer = null) + { + if ($layer === null) { + foreach ($this->layers as $layer) { + if (isset($this->configuration[$layer][$key])) { + return $this->configuration[$layer][$key]; + } + } + } elseif (isset($this->configuration[$layer][$key])) { + return $this->configuration[$layer][$key]; + } + return null; + } + + // }}} + // {{{ set(key, value, [layer]) + + /** + * Set a config value in a specific layer (defaults to 'user'). + * Enforces the types defined in the configuration_info array. An + * integer config variable will be cast to int, and a set config + * variable will be validated against its legal values. + * + * @param string config key + * + * @param string config value + * + * @param string (optional) config layer + * + * @return bool TRUE on success, FALSE on failure + * + * @access public + */ + function set($key, $value, $layer = 'user') + { + if (empty($this->configuration_info[$key])) { + return false; + } + extract($this->configuration_info[$key]); + switch ($type) { + case 'integer': + $value = (int)$value; + break; + case 'set': { + // If a valid_set is specified, require the value to + // be in the set. If there is no valid_set, accept + // any value. + if ($valid_set) { + reset($valid_set); + if ((key($valid_set) === 0 && !in_array($value, $valid_set)) || + (key($valid_set) !== 0 && empty($valid_set[$value]))) + { + return false; + } + } + break; + } + } + $this->configuration[$layer][$key] = $value; + return true; + } + + // }}} + // {{{ getType(key) + + /** + * Get the type of a config value. + * + * @param string config key + * + * @return string type, one of "string", "integer", "file", + * "directory", "set" or "password". + * + * @access public + * + */ + function getType($key) + { + if (isset($this->configuration_info[$key])) { + return $this->configuration_info[$key]['type']; + } + return false; + } + + // }}} + // {{{ getDocs(key) + + /** + * Get the documentation for a config value. + * + * @param string config key + * + * @return string documentation string + * + * @access public + * + */ + function getDocs($key) + { + if (isset($this->configuration_info[$key])) { + return $this->configuration_info[$key]['doc']; + } + return false; + } + // }}} + // {{{ getPrompt(key) + + /** + * Get the short documentation for a config value. + * + * @param string config key + * + * @return string short documentation string + * + * @access public + * + */ + function getPrompt($key) + { + if (isset($this->configuration_info[$key])) { + return $this->configuration_info[$key]['prompt']; + } + return false; + } + // }}} + // {{{ getGroup(key) + + /** + * Get the parameter group for a config key. + * + * @param string config key + * + * @return string parameter group + * + * @access public + * + */ + function getGroup($key) + { + if (isset($this->configuration_info[$key])) { + return $this->configuration_info[$key]['group']; + } + return false; + } + + // }}} + // {{{ getGroups() + + /** + * Get the list of parameter groups. + * + * @return array list of parameter groups + * + * @access public + * + */ + function getGroups() + { + $tmp = array(); + foreach ($this->configuration_info as $key => $info) { + $tmp[$info['group']] = 1; + } + return array_keys($tmp); + } + + // }}} + // {{{ getGroupKeys() + + /** + * Get the list of the parameters in a group. + * + * @param string $group parameter group + * + * @return array list of parameters in $group + * + * @access public + * + */ + function getGroupKeys($group) + { + $keys = array(); + foreach ($this->configuration_info as $key => $info) { + if ($info['group'] == $group) { + $keys[] = $key; + } + } + return $keys; + } + + // }}} + // {{{ getSetValues(key) + + /** + * Get the list of allowed set values for a config value. Returns + * NULL for config values that are not sets. + * + * @param string config key + * + * @return array enumerated array of set values, or NULL if the + * config key is unknown or not a set + * + * @access public + * + */ + function getSetValues($key) + { + if (isset($this->configuration_info[$key]) && + isset($this->configuration_info[$key]['type']) && + $this->configuration_info[$key]['type'] == 'set') + { + $valid_set = $this->configuration_info[$key]['valid_set']; + reset($valid_set); + if (key($valid_set) === 0) { + return $valid_set; + } + return array_keys($valid_set); + } + return false; + } + + // }}} + // {{{ getKeys() + + /** + * Get all the current config keys. + * + * @return array simple array of config keys + * + * @access public + */ + function getKeys() + { + $keys = array(); + foreach ($this->layers as $layer) { + $keys = array_merge($keys, $this->configuration[$layer]); + } + return array_keys($keys); + } + + // }}} + // {{{ remove(key, [layer]) + + /** + * Remove the a config key from a specific config layer. + * + * @param string config key + * + * @param string (optional) config layer + * + * @return bool TRUE on success, FALSE on failure + * + * @access public + */ + function remove($key, $layer = 'user') + { + if (isset($this->configuration[$layer][$key])) { + unset($this->configuration[$layer][$key]); + return true; + } + return false; + } + + // }}} + // {{{ removeLayer(layer) + + /** + * Temporarily remove an entire config layer. USE WITH CARE! + * + * @param string config key + * + * @param string (optional) config layer + * + * @return bool TRUE on success, FALSE on failure + * + * @access public + */ + function removeLayer($layer) + { + if (isset($this->configuration[$layer])) { + $this->configuration[$layer] = array(); + return true; + } + return false; + } + + // }}} + // {{{ store([layer]) + + /** + * Stores configuration data in a layer. + * + * @param string config layer to store + * + * @return bool TRUE on success, or PEAR error on failure + * + * @access public + */ + function store($layer = 'user', $data = null) + { + return $this->writeConfigFile(null, $layer, $data); + } + + // }}} + // {{{ toDefault(key) + + /** + * Unset the user-defined value of a config key, reverting the + * value to the system-defined one. + * + * @param string config key + * + * @return bool TRUE on success, FALSE on failure + * + * @access public + */ + function toDefault($key) + { + trigger_error("PEAR_Config::toDefault() deprecated, use PEAR_Config::remove() instead", E_USER_NOTICE); + return $this->remove($key, 'user'); + } + + // }}} + // {{{ definedBy(key) + + /** + * Tells what config layer that gets to define a key. + * + * @param string config key + * + * @return string the config layer, or an empty string if not found + * + * @access public + */ + function definedBy($key) + { + foreach ($this->layers as $layer) { + if (isset($this->configuration[$layer][$key])) { + return $layer; + } + } + return ''; + } + + // }}} + // {{{ isDefaulted(key) + + /** + * Tells whether a config value has a system-defined value. + * + * @param string config key + * + * @return bool + * + * @access public + * + * @deprecated + */ + function isDefaulted($key) + { + trigger_error("PEAR_Config::isDefaulted() deprecated, use PEAR_Config::definedBy() instead", E_USER_NOTICE); + return $this->definedBy($key) == 'system'; + } + + // }}} + // {{{ isDefined(key) + + /** + * Tells whether a given key exists as a config value. + * + * @param string config key + * + * @return bool whether exists in this object + * + * @access public + */ + function isDefined($key) + { + foreach ($this->layers as $layer) { + if (isset($this->configuration[$layer][$key])) { + return true; + } + } + return false; + } + + // }}} + // {{{ isDefinedLayer(key) + + /** + * Tells whether a given config layer exists. + * + * @param string config layer + * + * @return bool whether exists in this object + * + * @access public + */ + function isDefinedLayer($layer) + { + return isset($this->configuration[$layer]); + } + + // }}} + // {{{ getLayers() + + /** + * Returns the layers defined (except the 'default' one) + * + * @return array of the defined layers + */ + function getLayers() + { + $cf = $this->configuration; + unset($cf['default']); + return array_keys($cf); + } + + // }}} +} + +?> diff --git a/src/www/lib/pear/PEAR/Dependency.php b/src/www/lib/pear/PEAR/Dependency.php new file mode 100644 index 00000000..aec1fb1e --- /dev/null +++ b/src/www/lib/pear/PEAR/Dependency.php @@ -0,0 +1,487 @@ + | +// | Stig Bakken | +// +----------------------------------------------------------------------+ +// +// $Id: Dependency.php,v 1.1 2005/05/28 01:55:13 henrique Exp $ + +require_once "PEAR.php"; + +define('PEAR_DEPENDENCY_MISSING', -1); +define('PEAR_DEPENDENCY_CONFLICT', -2); +define('PEAR_DEPENDENCY_UPGRADE_MINOR', -3); +define('PEAR_DEPENDENCY_UPGRADE_MAJOR', -4); +define('PEAR_DEPENDENCY_BAD_DEPENDENCY', -5); +define('PEAR_DEPENDENCY_MISSING_OPTIONAL', -6); +define('PEAR_DEPENDENCY_CONFLICT_OPTIONAL', -7); +define('PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL', -8); +define('PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL', -9); + +/** + * Dependency check for PEAR packages + * + * The class is based on the dependency RFC that can be found at + * http://cvs.php.net/cvs.php/pearweb/rfc. It requires PHP >= 4.1 + * + * @author Tomas V.V.Vox + * @author Stig Bakken + */ +class PEAR_Dependency +{ + // {{{ constructor + /** + * Constructor + * + * @access public + * @param object Registry object + * @return void + */ + function PEAR_Dependency(&$registry) + { + $this->registry = &$registry; + } + + // }}} + // {{{ callCheckMethod() + + /** + * This method maps the XML dependency definition to the + * corresponding one from PEAR_Dependency + * + *
+    * $opts => Array
+    *    (
+    *        [type] => pkg
+    *        [rel] => ge
+    *        [version] => 3.4
+    *        [name] => HTML_Common
+    *        [optional] => false
+    *    )
+    * 
+ * + * @param string Error message + * @param array Options + * @return boolean + */ + function callCheckMethod(&$errmsg, $opts) + { + $rel = isset($opts['rel']) ? $opts['rel'] : 'has'; + $req = isset($opts['version']) ? $opts['version'] : null; + $name = isset($opts['name']) ? $opts['name'] : null; + $opt = (isset($opts['optional']) && $opts['optional'] == 'yes') ? + $opts['optional'] : null; + $errmsg = ''; + switch ($opts['type']) { + case 'pkg': + return $this->checkPackage($errmsg, $name, $req, $rel, $opt); + break; + case 'ext': + return $this->checkExtension($errmsg, $name, $req, $rel, $opt); + break; + case 'php': + return $this->checkPHP($errmsg, $req, $rel); + break; + case 'prog': + return $this->checkProgram($errmsg, $name); + break; + case 'os': + return $this->checkOS($errmsg, $name); + break; + case 'sapi': + return $this->checkSAPI($errmsg, $name); + break; + case 'zend': + return $this->checkZend($errmsg, $name); + break; + default: + return "'{$opts['type']}' dependency type not supported"; + } + } + + // }}} + // {{{ checkPackage() + + /** + * Package dependencies check method + * + * @param string $errmsg Empty string, it will be populated with an error message, if any + * @param string $name Name of the package to test + * @param string $req The package version required + * @param string $relation How to compare versions with each other + * @param bool $opt Whether the relationship is optional + * + * @return mixed bool false if no error or the error string + */ + function checkPackage(&$errmsg, $name, $req = null, $relation = 'has', + $opt = false) + { + if (is_string($req) && substr($req, 0, 2) == 'v.') { + $req = substr($req, 2); + } + switch ($relation) { + case 'has': + if (!$this->registry->packageExists($name)) { + if ($opt) { + $errmsg = "package `$name' is recommended to utilize some features."; + return PEAR_DEPENDENCY_MISSING_OPTIONAL; + } + $errmsg = "requires package `$name'"; + return PEAR_DEPENDENCY_MISSING; + } + return false; + case 'not': + if ($this->registry->packageExists($name)) { + $errmsg = "conflicts with package `$name'"; + return PEAR_DEPENDENCY_CONFLICT; + } + return false; + case 'lt': + case 'le': + case 'eq': + case 'ne': + case 'ge': + case 'gt': + $version = $this->registry->packageInfo($name, 'version'); + if (!$this->registry->packageExists($name) + || !version_compare("$version", "$req", $relation)) + { + $code = $this->codeFromRelation($relation, $version, $req, $opt); + if ($opt) { + $errmsg = "package `$name' version " . $this->signOperator($relation) . + " $req is recommended to utilize some features."; + if ($version) { + $errmsg .= " Installed version is $version"; + } + return $code; + } + $errmsg = "requires package `$name' " . + $this->signOperator($relation) . " $req"; + return $code; + } + return false; + } + $errmsg = "relation '$relation' with requirement '$req' is not supported (name=$name)"; + return PEAR_DEPENDENCY_BAD_DEPENDENCY; + } + + // }}} + // {{{ checkPackageUninstall() + + /** + * Check package dependencies on uninstall + * + * @param string $error The resultant error string + * @param string $warning The resultant warning string + * @param string $name Name of the package to test + * + * @return bool true if there were errors + */ + function checkPackageUninstall(&$error, &$warning, $package) + { + $error = null; + $packages = $this->registry->listPackages(); + foreach ($packages as $pkg) { + if ($pkg == $package) { + continue; + } + $deps = $this->registry->packageInfo($pkg, 'release_deps'); + if (empty($deps)) { + continue; + } + foreach ($deps as $dep) { + if ($dep['type'] == 'pkg' && strcasecmp($dep['name'], $package) == 0) { + if ($dep['rel'] == 'ne' || $dep['rel'] == 'not') { + continue; + } + if (isset($dep['optional']) && $dep['optional'] == 'yes') { + $warning .= "\nWarning: Package '$pkg' optionally depends on '$package'"; + } else { + $error .= "Package '$pkg' depends on '$package'\n"; + } + } + } + } + return ($error) ? true : false; + } + + // }}} + // {{{ checkExtension() + + /** + * Extension dependencies check method + * + * @param string $name Name of the extension to test + * @param string $req_ext_ver Required extension version to compare with + * @param string $relation How to compare versions with eachother + * @param bool $opt Whether the relationship is optional + * + * @return mixed bool false if no error or the error string + */ + function checkExtension(&$errmsg, $name, $req = null, $relation = 'has', + $opt = false) + { + if ($relation == 'not') { + if (extension_loaded($name)) { + $errmsg = "conflicts with PHP extension '$name'"; + return PEAR_DEPENDENCY_CONFLICT; + } else { + return false; + } + } + + if (!extension_loaded($name)) { + if ($relation == 'not') { + return false; + } + if ($opt) { + $errmsg = "'$name' PHP extension is recommended to utilize some features"; + return PEAR_DEPENDENCY_MISSING_OPTIONAL; + } + $errmsg = "'$name' PHP extension is not installed"; + return PEAR_DEPENDENCY_MISSING; + } + if ($relation == 'has') { + return false; + } + $code = false; + if (is_string($req) && substr($req, 0, 2) == 'v.') { + $req = substr($req, 2); + } + $ext_ver = phpversion($name); + $operator = $relation; + // Force params to be strings, otherwise the comparation will fail (ex. 0.9==0.90) + if (!version_compare("$ext_ver", "$req", $operator)) { + $errmsg = "'$name' PHP extension version " . + $this->signOperator($operator) . " $req is required"; + $code = $this->codeFromRelation($relation, $ext_ver, $req, $opt); + if ($opt) { + $errmsg = "'$name' PHP extension version " . $this->signOperator($operator) . + " $req is recommended to utilize some features"; + return $code; + } + } + return $code; + } + + // }}} + // {{{ checkOS() + + /** + * Operating system dependencies check method + * + * @param string $os Name of the operating system + * + * @return mixed bool false if no error or the error string + */ + function checkOS(&$errmsg, $os) + { + // XXX Fixme: Implement a more flexible way, like + // comma separated values or something similar to PEAR_OS + static $myos; + if (empty($myos)) { + include_once "OS/Guess.php"; + $myos = new OS_Guess(); + } + // only 'has' relation is currently supported + if ($myos->matchSignature($os)) { + return false; + } + $errmsg = "'$os' operating system not supported"; + return PEAR_DEPENDENCY_CONFLICT; + } + + // }}} + // {{{ checkPHP() + + /** + * PHP version check method + * + * @param string $req which version to compare + * @param string $relation how to compare the version + * + * @return mixed bool false if no error or the error string + */ + function checkPHP(&$errmsg, $req, $relation = 'ge') + { + // this would be a bit stupid, but oh well :) + if ($relation == 'has') { + return false; + } + if ($relation == 'not') { + $errmsg = 'Invalid dependency - "not" is not allowed for php dependencies, ' . + 'php cannot conflict with itself'; + return PEAR_DEPENDENCY_BAD_DEPENDENCY; + } + if (substr($req, 0, 2) == 'v.') { + $req = substr($req,2, strlen($req) - 2); + } + $php_ver = phpversion(); + $operator = $relation; + if (!version_compare("$php_ver", "$req", $operator)) { + $errmsg = "PHP version " . $this->signOperator($operator) . + " $req is required"; + return PEAR_DEPENDENCY_CONFLICT; + } + return false; + } + + // }}} + // {{{ checkProgram() + + /** + * External program check method. Looks for executable files in + * directories listed in the PATH environment variable. + * + * @param string $program which program to look for + * + * @return mixed bool false if no error or the error string + */ + function checkProgram(&$errmsg, $program) + { + // XXX FIXME honor safe mode + $exe_suffix = OS_WINDOWS ? '.exe' : ''; + $path_elements = explode(PATH_SEPARATOR, getenv('PATH')); + foreach ($path_elements as $dir) { + $file = $dir . DIRECTORY_SEPARATOR . $program . $exe_suffix; + if (@file_exists($file) && @is_executable($file)) { + return false; + } + } + $errmsg = "'$program' program is not present in the PATH"; + return PEAR_DEPENDENCY_MISSING; + } + + // }}} + // {{{ checkSAPI() + + /** + * SAPI backend check method. Version comparison is not yet + * available here. + * + * @param string $name name of SAPI backend + * @param string $req which version to compare + * @param string $relation how to compare versions (currently + * hardcoded to 'has') + * @return mixed bool false if no error or the error string + */ + function checkSAPI(&$errmsg, $name, $req = null, $relation = 'has') + { + // XXX Fixme: There is no way to know if the user has or + // not other SAPI backends installed than the installer one + + $sapi_backend = php_sapi_name(); + // Version comparisons not supported, sapi backends don't have + // version information yet. + if ($sapi_backend == $name) { + return false; + } + $errmsg = "'$sapi_backend' SAPI backend not supported"; + return PEAR_DEPENDENCY_CONFLICT; + } + + // }}} + // {{{ checkZend() + + /** + * Zend version check method + * + * @param string $req which version to compare + * @param string $relation how to compare the version + * + * @return mixed bool false if no error or the error string + */ + function checkZend(&$errmsg, $req, $relation = 'ge') + { + if (substr($req, 0, 2) == 'v.') { + $req = substr($req,2, strlen($req) - 2); + } + $zend_ver = zend_version(); + $operator = substr($relation,0,2); + if (!version_compare("$zend_ver", "$req", $operator)) { + $errmsg = "Zend version " . $this->signOperator($operator) . + " $req is required"; + return PEAR_DEPENDENCY_CONFLICT; + } + return false; + } + + // }}} + // {{{ signOperator() + + /** + * Converts text comparing operators to them sign equivalents + * + * Example: 'ge' to '>=' + * + * @access public + * @param string Operator + * @return string Sign equivalent + */ + function signOperator($operator) + { + switch($operator) { + case 'lt': return '<'; + case 'le': return '<='; + case 'gt': return '>'; + case 'ge': return '>='; + case 'eq': return '=='; + case 'ne': return '!='; + default: + return $operator; + } + } + + // }}} + // {{{ codeFromRelation() + + /** + * Convert relation into corresponding code + * + * @access public + * @param string Relation + * @param string Version + * @param string Requirement + * @param bool Optional dependency indicator + * @return integer + */ + function codeFromRelation($relation, $version, $req, $opt = false) + { + $code = PEAR_DEPENDENCY_BAD_DEPENDENCY; + switch ($relation) { + case 'gt': case 'ge': case 'eq': + // upgrade + $have_major = preg_replace('/\D.*/', '', $version); + $need_major = preg_replace('/\D.*/', '', $req); + if ($need_major > $have_major) { + $code = $opt ? PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL : + PEAR_DEPENDENCY_UPGRADE_MAJOR; + } else { + $code = $opt ? PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL : + PEAR_DEPENDENCY_UPGRADE_MINOR; + } + break; + case 'lt': case 'le': case 'ne': + $code = $opt ? PEAR_DEPENDENCY_CONFLICT_OPTIONAL : + PEAR_DEPENDENCY_CONFLICT; + break; + } + return $code; + } + + // }}} +} +?> diff --git a/src/www/lib/pear/PEAR/Downloader.php b/src/www/lib/pear/PEAR/Downloader.php new file mode 100644 index 00000000..e6339a23 --- /dev/null +++ b/src/www/lib/pear/PEAR/Downloader.php @@ -0,0 +1,680 @@ + | +// | Tomas V.V.Cox | +// | Martin Jansen | +// +----------------------------------------------------------------------+ +// +// $Id: Downloader.php,v 1.1 2005/05/28 01:55:13 henrique Exp $ + +require_once 'PEAR/Common.php'; +require_once 'PEAR/Registry.php'; +require_once 'PEAR/Dependency.php'; +require_once 'PEAR/Remote.php'; +require_once 'System.php'; + + +define('PEAR_INSTALLER_OK', 1); +define('PEAR_INSTALLER_FAILED', 0); +define('PEAR_INSTALLER_SKIPPED', -1); +define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2); + +/** + * Administration class used to download PEAR packages and maintain the + * installed package database. + * + * @since PEAR 1.4 + * @author Greg Beaver + */ +class PEAR_Downloader extends PEAR_Common +{ + /** + * @var PEAR_Config + * @access private + */ + var $_config; + + /** + * @var PEAR_Registry + * @access private + */ + var $_registry; + + /** + * @var PEAR_Remote + * @access private + */ + var $_remote; + + /** + * Preferred Installation State (snapshot, devel, alpha, beta, stable) + * @var string|null + * @access private + */ + var $_preferredState; + + /** + * Options from command-line passed to Install. + * + * Recognized options:
+ * - onlyreqdeps : install all required dependencies as well + * - alldeps : install all dependencies, including optional + * - installroot : base relative path to install files in + * - force : force a download even if warnings would prevent it + * @see PEAR_Command_Install + * @access private + * @var array + */ + var $_options; + + /** + * Downloaded Packages after a call to download(). + * + * Format of each entry: + * + * + * array('pkg' => 'package_name', 'file' => '/path/to/local/file', + * 'info' => array() // parsed package.xml + * ); + * + * @access private + * @var array + */ + var $_downloadedPackages = array(); + + /** + * Packages slated for download. + * + * This is used to prevent downloading a package more than once should it be a dependency + * for two packages to be installed. + * Format of each entry: + * + *
+     * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
+     * );
+     * 
+ * @access private + * @var array + */ + var $_toDownload = array(); + + /** + * Array of every package installed, with names lower-cased. + * + * Format: + * + * array('package1' => 0, 'package2' => 1, ); + * + * @var array + */ + var $_installed = array(); + + /** + * @var array + * @access private + */ + var $_errorStack = array(); + + // {{{ PEAR_Downloader() + + function PEAR_Downloader(&$ui, $options, &$config) + { + $this->_options = $options; + $this->_config = &$config; + $this->_preferredState = $this->_config->get('preferred_state'); + $this->ui = &$ui; + if (!$this->_preferredState) { + // don't inadvertantly use a non-set preferred_state + $this->_preferredState = null; + } + + $php_dir = $this->_config->get('php_dir'); + if (isset($this->_options['installroot'])) { + if (substr($this->_options['installroot'], -1) == DIRECTORY_SEPARATOR) { + $this->_options['installroot'] = substr($this->_options['installroot'], 0, -1); + } + $php_dir = $this->_prependPath($php_dir, $this->_options['installroot']); + } + $this->_registry = &new PEAR_Registry($php_dir); + $this->_remote = &new PEAR_Remote($config); + + if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) { + $this->_installed = $this->_registry->listPackages(); + array_walk($this->_installed, create_function('&$v,$k','$v = strtolower($v);')); + $this->_installed = array_flip($this->_installed); + } + parent::PEAR_Common(); + } + + // }}} + // {{{ configSet() + function configSet($key, $value, $layer = 'user') + { + $this->_config->set($key, $value, $layer); + $this->_preferredState = $this->_config->get('preferred_state'); + if (!$this->_preferredState) { + // don't inadvertantly use a non-set preferred_state + $this->_preferredState = null; + } + } + + // }}} + // {{{ setOptions() + function setOptions($options) + { + $this->_options = $options; + } + + // }}} + // {{{ _downloadFile() + /** + * @param string filename to download + * @param string version/state + * @param string original value passed to command-line + * @param string|null preferred state (snapshot/devel/alpha/beta/stable) + * Defaults to configuration preferred state + * @return null|PEAR_Error|string + * @access private + */ + function _downloadFile($pkgfile, $version, $origpkgfile, $state = null) + { + if (is_null($state)) { + $state = $this->_preferredState; + } + // {{{ check the package filename, and whether it's already installed + $need_download = false; + if (preg_match('#^(http|ftp)://#', $pkgfile)) { + $need_download = true; + } elseif (!@is_file($pkgfile)) { + if ($this->validPackageName($pkgfile)) { + if ($this->_registry->packageExists($pkgfile)) { + if (empty($this->_options['upgrade']) && empty($this->_options['force'])) { + $errors[] = "$pkgfile already installed"; + return; + } + } + $pkgfile = $this->getPackageDownloadUrl($pkgfile, $version); + $need_download = true; + } else { + if (strlen($pkgfile)) { + $errors[] = "Could not open the package file: $pkgfile"; + } else { + $errors[] = "No package file given"; + } + return; + } + } + // }}} + + // {{{ Download package ----------------------------------------------- + if ($need_download) { + $downloaddir = $this->_config->get('download_dir'); + if (empty($downloaddir)) { + if (PEAR::isError($downloaddir = System::mktemp('-d'))) { + return $downloaddir; + } + $this->log(3, '+ tmp dir created at ' . $downloaddir); + } + $callback = $this->ui ? array(&$this, '_downloadCallback') : null; + $this->pushErrorHandling(PEAR_ERROR_RETURN); + $file = $this->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback); + $this->popErrorHandling(); + if (PEAR::isError($file)) { + if ($this->validPackageName($origpkgfile)) { + if (!PEAR::isError($info = $this->_remote->call('package.info', + $origpkgfile))) { + if (!count($info['releases'])) { + return $this->raiseError('Package ' . $origpkgfile . + ' has no releases'); + } else { + return $this->raiseError('No releases of preferred state "' + . $state . '" exist for package ' . $origpkgfile . + '. Use ' . $origpkgfile . '-state to install another' . + ' state (like ' . $origpkgfile .'-beta)', + PEAR_INSTALLER_ERROR_NO_PREF_STATE); + } + } else { + return $pkgfile; + } + } else { + return $this->raiseError($file); + } + } + $pkgfile = $file; + } + // }}} + return $pkgfile; + } + // }}} + // {{{ getPackageDownloadUrl() + + function getPackageDownloadUrl($package, $version = null) + { + if ($version) { + $package .= "-$version"; + } + if ($this === null || $this->_config === null) { + $package = "http://pear.php.net/get/$package"; + } else { + $package = "http://" . $this->_config->get('master_server') . + "/get/$package"; + } + if (!extension_loaded("zlib")) { + $package .= '?uncompress=yes'; + } + return $package; + } + + // }}} + // {{{ extractDownloadFileName($pkgfile, &$version) + + function extractDownloadFileName($pkgfile, &$version) + { + if (@is_file($pkgfile)) { + return $pkgfile; + } + // regex defined in Common.php + if (preg_match(PEAR_COMMON_PACKAGE_DOWNLOAD_PREG, $pkgfile, $m)) { + $version = (isset($m[3])) ? $m[3] : null; + return $m[1]; + } + $version = null; + return $pkgfile; + } + + // }}} + + // }}} + // {{{ getDownloadedPackages() + + /** + * Retrieve a list of downloaded packages after a call to {@link download()}. + * + * Also resets the list of downloaded packages. + * @return array + */ + function getDownloadedPackages() + { + $ret = $this->_downloadedPackages; + $this->_downloadedPackages = array(); + $this->_toDownload = array(); + return $ret; + } + + // }}} + // {{{ download() + + /** + * Download any files and their dependencies, if necessary + * + * BC-compatible method name + * @param array a mixed list of package names, local files, or package.xml + */ + function download($packages) + { + return $this->doDownload($packages); + } + + // }}} + // {{{ doDownload() + + /** + * Download any files and their dependencies, if necessary + * + * @param array a mixed list of package names, local files, or package.xml + */ + function doDownload($packages) + { + $mywillinstall = array(); + $state = $this->_preferredState; + + // {{{ download files in this list if necessary + foreach($packages as $pkgfile) { + $need_download = false; + if (!is_file($pkgfile)) { + if (preg_match('#^(http|ftp)://#', $pkgfile)) { + $need_download = true; + } + $pkgfile = $this->_downloadNonFile($pkgfile); + if (PEAR::isError($pkgfile)) { + return $pkgfile; + } + if ($pkgfile === false) { + continue; + } + } // end is_file() + + $tempinfo = $this->infoFromAny($pkgfile); + if ($need_download) { + $this->_toDownload[] = $tempinfo['package']; + } + if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) { + // ignore dependencies if there are any errors + if (!PEAR::isError($tempinfo)) { + $mywillinstall[strtolower($tempinfo['package'])] = @$tempinfo['release_deps']; + } + } + $this->_downloadedPackages[] = array('pkg' => $tempinfo['package'], + 'file' => $pkgfile, 'info' => $tempinfo); + } // end foreach($packages) + // }}} + + // {{{ extract dependencies from downloaded files and then download + // them if necessary + if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) { + $deppackages = array(); + foreach ($mywillinstall as $package => $alldeps) { + if (!is_array($alldeps)) { + // there are no dependencies + continue; + } + $fail = false; + foreach ($alldeps as $info) { + if ($info['type'] != 'pkg') { + continue; + } + $ret = $this->_processDependency($package, $info, $mywillinstall); + if ($ret === false) { + continue; + } + if ($ret === 0) { + $fail = true; + continue; + } + if (PEAR::isError($ret)) { + return $ret; + } + $deppackages[] = $ret; + } // foreach($alldeps + if ($fail) { + $deppackages = array(); + } + } + + if (count($deppackages)) { + $this->doDownload($deppackages); + } + } // }}} if --alldeps or --onlyreqdeps + } + + // }}} + // {{{ _downloadNonFile($pkgfile) + + /** + * @return false|PEAR_Error|string false if loop should be broken out of, + * string if the file was downloaded, + * PEAR_Error on exception + * @access private + */ + function _downloadNonFile($pkgfile) + { + $origpkgfile = $pkgfile; + $state = null; + $pkgfile = $this->extractDownloadFileName($pkgfile, $version); + if (preg_match('#^(http|ftp)://#', $pkgfile)) { + return $this->_downloadFile($pkgfile, $version, $origpkgfile); + } + if (!$this->validPackageName($pkgfile)) { + return $this->raiseError("Package name '$pkgfile' not valid"); + } + // ignore packages that are installed unless we are upgrading + $curinfo = $this->_registry->packageInfo($pkgfile); + if ($this->_registry->packageExists($pkgfile) + && empty($this->_options['upgrade']) && empty($this->_options['force'])) { + $this->log(0, "Package '{$curinfo['package']}' already installed, skipping"); + return false; + } + if (in_array($pkgfile, $this->_toDownload)) { + return false; + } + $releases = $this->_remote->call('package.info', $pkgfile, 'releases', true); + if (!count($releases)) { + return $this->raiseError("No releases found for package '$pkgfile'"); + } + // Want a specific version/state + if ($version !== null) { + // Passed Foo-1.2 + if ($this->validPackageVersion($version)) { + if (!isset($releases[$version])) { + return $this->raiseError("No release with version '$version' found for '$pkgfile'"); + } + // Passed Foo-alpha + } elseif (in_array($version, $this->getReleaseStates())) { + $state = $version; + $version = 0; + foreach ($releases as $ver => $inf) { + if ($inf['state'] == $state && version_compare("$version", "$ver") < 0) { + $version = $ver; + break; + } + } + if ($version == 0) { + return $this->raiseError("No release with state '$state' found for '$pkgfile'"); + } + // invalid suffix passed + } else { + return $this->raiseError("Invalid suffix '-$version', be sure to pass a valid PEAR ". + "version number or release state"); + } + // Guess what to download + } else { + $states = $this->betterStates($this->_preferredState, true); + $possible = false; + $version = 0; + $higher_version = 0; + $prev_hi_ver = 0; + foreach ($releases as $ver => $inf) { + if (in_array($inf['state'], $states) && version_compare("$version", "$ver") < 0) { + $version = $ver; + break; + } else { + $ver = (string)$ver; + if (version_compare($prev_hi_ver, $ver) < 0) { + $prev_hi_ver = $higher_version = $ver; + } + } + } + if ($version === 0 && !isset($this->_options['force'])) { + return $this->raiseError('No release with state equal to: \'' . implode(', ', $states) . + "' found for '$pkgfile'"); + } elseif ($version === 0) { + $this->log(0, "Warning: $pkgfile is state '" . $releases[$higher_version]['state'] . "' which is less stable " . + "than state '$this->_preferredState'"); + } + } + // Check if we haven't already the version + if (empty($this->_options['force']) && !is_null($curinfo)) { + if ($curinfo['version'] == $version) { + $this->log(0, "Package '{$curinfo['package']}-{$curinfo['version']}' already installed, skipping"); + return false; + } elseif (version_compare("$version", "{$curinfo['version']}") < 0) { + $this->log(0, "Package '{$curinfo['package']}' version '{$curinfo['version']}' " . + " is installed and {$curinfo['version']} is > requested '$version', skipping"); + return false; + } + } + $this->_toDownload[] = $pkgfile; + return $this->_downloadFile($pkgfile, $version, $origpkgfile, $state); + } + + // }}} + // {{{ _processDependency($package, $info, $mywillinstall) + + /** + * Process a dependency, download if necessary + * @param array dependency information from PEAR_Remote call + * @param array packages that will be installed in this iteration + * @return false|string|PEAR_Error + * @access private + * @todo Add test for relation 'lt'/'le' -> make sure that the dependency requested is + * in fact lower than the required value. This will be very important for BC dependencies + */ + function _processDependency($package, $info, $mywillinstall) + { + $state = $this->_preferredState; + if (!isset($this->_options['alldeps']) && isset($info['optional']) && + $info['optional'] == 'yes') { + // skip optional deps + $this->log(0, "skipping Package '$package' optional dependency '$info[name]'"); + return false; + } + // {{{ get releases + $releases = $this->_remote->call('package.info', $info['name'], 'releases', true); + if (PEAR::isError($releases)) { + return $releases; + } + if (!count($releases)) { + if (!isset($this->_installed[strtolower($info['name'])])) { + $this->pushError("Package '$package' dependency '$info[name]' ". + "has no releases"); + } + return false; + } + $found = false; + $save = $releases; + while(count($releases) && !$found) { + if (!empty($state) && $state != 'any') { + list($release_version, $release) = each($releases); + if ($state != $release['state'] && + !in_array($release['state'], $this->betterStates($state))) + { + // drop this release - it ain't stable enough + array_shift($releases); + } else { + $found = true; + } + } else { + $found = true; + } + } + if (!count($releases) && !$found) { + $get = array(); + foreach($save as $release) { + $get = array_merge($get, + $this->betterStates($release['state'], true)); + } + $savestate = array_shift($get); + $this->pushError( "Release for '$package' dependency '$info[name]' " . + "has state '$savestate', requires '$state'"); + return 0; + } + if (in_array(strtolower($info['name']), $this->_toDownload) || + isset($mywillinstall[strtolower($info['name'])])) { + // skip upgrade check for packages we will install + return false; + } + if (!isset($this->_installed[strtolower($info['name'])])) { + // check to see if we can install the specific version required + if ($info['rel'] == 'eq') { + return $info['name'] . '-' . $info['version']; + } + // skip upgrade check for packages we don't have installed + return $info['name']; + } + // }}} + + // {{{ see if a dependency must be upgraded + $inst_version = $this->_registry->packageInfo($info['name'], 'version'); + if (!isset($info['version'])) { + // this is a rel='has' dependency, check against latest + if (version_compare($release_version, $inst_version, 'le')) { + return false; + } else { + return $info['name']; + } + } + if (version_compare($info['version'], $inst_version, 'le')) { + // installed version is up-to-date + return false; + } + return $info['name']; + } + + // }}} + // {{{ _downloadCallback() + + function _downloadCallback($msg, $params = null) + { + switch ($msg) { + case 'saveas': + $this->log(1, "downloading $params ..."); + break; + case 'done': + $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes'); + break; + case 'bytesread': + static $bytes; + if (empty($bytes)) { + $bytes = 0; + } + if (!($bytes % 10240)) { + $this->log(1, '.', false); + } + $bytes += $params; + break; + case 'start': + $this->log(1, "Starting to download {$params[0]} (".number_format($params[1], 0, '', ',')." bytes)"); + break; + } + if (method_exists($this->ui, '_downloadCallback')) + $this->ui->_downloadCallback($msg, $params); + } + + // }}} + // {{{ _prependPath($path, $prepend) + + function _prependPath($path, $prepend) + { + if (strlen($prepend) > 0) { + if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) { + $path = $prepend . substr($path, 2); + } else { + $path = $prepend . $path; + } + } + return $path; + } + // }}} + // {{{ pushError($errmsg, $code) + + /** + * @param string + * @param integer + */ + function pushError($errmsg, $code = -1) + { + array_push($this->_errorStack, array($errmsg, $code)); + } + + // }}} + // {{{ getErrorMsgs() + + function getErrorMsgs() + { + $msgs = array(); + $errs = $this->_errorStack; + foreach ($errs as $err) { + $msgs[] = $err[0]; + } + $this->_errorStack = array(); + return $msgs; + } + + // }}} +} +// }}} + +?> diff --git a/src/www/lib/pear/PEAR/ErrorStack.php b/src/www/lib/pear/PEAR/ErrorStack.php new file mode 100644 index 00000000..5ac000fd --- /dev/null +++ b/src/www/lib/pear/PEAR/ErrorStack.php @@ -0,0 +1,981 @@ + | +// | | +// +----------------------------------------------------------------------+ +// +// $Id: ErrorStack.php,v 1.1 2005/05/28 01:55:13 henrique Exp $ + +/** + * Error Stack Implementation + * + * This is an incredibly simple implementation of a very complex error handling + * facility. It contains the ability + * to track multiple errors from multiple packages simultaneously. In addition, + * it can track errors of many levels, save data along with the error, context + * information such as the exact file, line number, class and function that + * generated the error, and if necessary, it can raise a traditional PEAR_Error. + * It has built-in support for PEAR::Log, to log errors as they occur + * + * Since version 0.2alpha, it is also possible to selectively ignore errors, + * through the use of an error callback, see {@link pushCallback()} + * + * Since version 0.3alpha, it is possible to specify the exception class + * returned from {@link push()} + * + * Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class. This can + * still be done quite handily in an error callback or by manipulating the returned array + * @author Greg Beaver + * @version PEAR1.3.2 (beta) + * @package PEAR_ErrorStack + * @category Debugging + * @license http://www.php.net/license/3_0.txt PHP License v3.0 + */ + +/** + * Singleton storage + * + * Format: + *
+ * array(
+ *  'package1' => PEAR_ErrorStack object,
+ *  'package2' => PEAR_ErrorStack object,
+ *  ...
+ * )
+ * 
+ * @access private + * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] + */ +$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array(); + +/** + * Global error callback (default) + * + * This is only used if set to non-false. * is the default callback for + * all packages, whereas specific packages may set a default callback + * for all instances, regardless of whether they are a singleton or not. + * + * To exclude non-singletons, only set the local callback for the singleton + * @see PEAR_ErrorStack::setDefaultCallback() + * @access private + * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] + */ +$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array( + '*' => false, +); + +/** + * Global Log object (default) + * + * This is only used if set to non-false. Use to set a default log object for + * all stacks, regardless of instantiation order or location + * @see PEAR_ErrorStack::setDefaultLogger() + * @access private + * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] + */ +$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false; + +/** + * Global Overriding Callback + * + * This callback will override any error callbacks that specific loggers have set. + * Use with EXTREME caution + * @see PEAR_ErrorStack::staticPushCallback() + * @access private + * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] + */ +$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); + +/**#@+ + * One of four possible return values from the error Callback + * @see PEAR_ErrorStack::_errorCallback() + */ +/** + * If this is returned, then the error will be both pushed onto the stack + * and logged. + */ +define('PEAR_ERRORSTACK_PUSHANDLOG', 1); +/** + * If this is returned, then the error will only be pushed onto the stack, + * and not logged. + */ +define('PEAR_ERRORSTACK_PUSH', 2); +/** + * If this is returned, then the error will only be logged, but not pushed + * onto the error stack. + */ +define('PEAR_ERRORSTACK_LOG', 3); +/** + * If this is returned, then the error is completely ignored. + */ +define('PEAR_ERRORSTACK_IGNORE', 4); +/** + * If this is returned, then the error is logged and die() is called. + */ +define('PEAR_ERRORSTACK_DIE', 5); +/**#@-*/ + +/** + * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in + * the singleton method. + */ +define('PEAR_ERRORSTACK_ERR_NONCLASS', 1); + +/** + * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()} + * that has no __toString() method + */ +define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2); +/** + * Error Stack Implementation + * + * Usage: + * + * // global error stack + * $global_stack = &PEAR_ErrorStack::singleton('MyPackage'); + * // local error stack + * $local_stack = new PEAR_ErrorStack('MyPackage'); + * + * @copyright 2004 Gregory Beaver + * @package PEAR_ErrorStack + * @license http://www.php.net/license/3_0.txt PHP License + */ +class PEAR_ErrorStack { + /** + * Errors are stored in the order that they are pushed on the stack. + * @since 0.4alpha Errors are no longer organized by error level. + * This renders pop() nearly unusable, and levels could be more easily + * handled in a callback anyway + * @var array + * @access private + */ + var $_errors = array(); + + /** + * Storage of errors by level. + * + * Allows easy retrieval and deletion of only errors from a particular level + * @since PEAR 1.4.0dev + * @var array + * @access private + */ + var $_errorsByLevel = array(); + + /** + * Package name this error stack represents + * @var string + * @access protected + */ + var $_package; + + /** + * Determines whether a PEAR_Error is thrown upon every error addition + * @var boolean + * @access private + */ + var $_compat = false; + + /** + * If set to a valid callback, this will be used to generate the error + * message from the error code, otherwise the message passed in will be + * used + * @var false|string|array + * @access private + */ + var $_msgCallback = false; + + /** + * If set to a valid callback, this will be used to generate the error + * context for an error. For PHP-related errors, this will be a file + * and line number as retrieved from debug_backtrace(), but can be + * customized for other purposes. The error might actually be in a separate + * configuration file, or in a database query. + * @var false|string|array + * @access protected + */ + var $_contextCallback = false; + + /** + * If set to a valid callback, this will be called every time an error + * is pushed onto the stack. The return value will be used to determine + * whether to allow an error to be pushed or logged. + * + * The return value must be one an PEAR_ERRORSTACK_* constant + * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG + * @var false|string|array + * @access protected + */ + var $_errorCallback = array(); + + /** + * PEAR::Log object for logging errors + * @var false|Log + * @access protected + */ + var $_logger = false; + + /** + * Error messages - designed to be overridden + * @var array + * @abstract + */ + var $_errorMsgs = array(); + + /** + * Set up a new error stack + * + * @param string $package name of the package this error stack represents + * @param callback $msgCallback callback used for error message generation + * @param callback $contextCallback callback used for context generation, + * defaults to {@link getFileLine()} + * @param boolean $throwPEAR_Error + */ + function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false, + $throwPEAR_Error = false) + { + $this->_package = $package; + $this->setMessageCallback($msgCallback); + $this->setContextCallback($contextCallback); + $this->_compat = $throwPEAR_Error; + } + + /** + * Return a single error stack for this package. + * + * Note that all parameters are ignored if the stack for package $package + * has already been instantiated + * @param string $package name of the package this error stack represents + * @param callback $msgCallback callback used for error message generation + * @param callback $contextCallback callback used for context generation, + * defaults to {@link getFileLine()} + * @param boolean $throwPEAR_Error + * @param string $stackClass class to instantiate + * @static + * @return PEAR_ErrorStack + */ + function &singleton($package, $msgCallback = false, $contextCallback = false, + $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack') + { + if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { + return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]; + } + if (!class_exists($stackClass)) { + if (function_exists('debug_backtrace')) { + $trace = debug_backtrace(); + } + PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS, + 'exception', array('stackclass' => $stackClass), + 'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)', + false, $trace); + } + return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] = + &new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error); + } + + /** + * Internal error handler for PEAR_ErrorStack class + * + * Dies if the error is an exception (and would have died anyway) + * @access private + */ + function _handleError($err) + { + if ($err['level'] == 'exception') { + $message = $err['message']; + if (isset($_SERVER['REQUEST_URI'])) { + echo '
'; + } else { + echo "\n"; + } + var_dump($err['context']); + die($message); + } + } + + /** + * Set up a PEAR::Log object for all error stacks that don't have one + * @param Log $log + * @static + */ + function setDefaultLogger(&$log) + { + if (is_object($log) && method_exists($log, 'log') ) { + $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; + } elseif (is_callable($log)) { + $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; + } + } + + /** + * Set up a PEAR::Log object for this error stack + * @param Log $log + */ + function setLogger(&$log) + { + if (is_object($log) && method_exists($log, 'log') ) { + $this->_logger = &$log; + } elseif (is_callable($log)) { + $this->_logger = &$log; + } + } + + /** + * Set an error code => error message mapping callback + * + * This method sets the callback that can be used to generate error + * messages for any instance + * @param array|string Callback function/method + */ + function setMessageCallback($msgCallback) + { + if (!$msgCallback) { + $this->_msgCallback = array(&$this, 'getErrorMessage'); + } else { + if (is_callable($msgCallback)) { + $this->_msgCallback = $msgCallback; + } + } + } + + /** + * Get an error code => error message mapping callback + * + * This method returns the current callback that can be used to generate error + * messages + * @return array|string|false Callback function/method or false if none + */ + function getMessageCallback() + { + return $this->_msgCallback; + } + + /** + * Sets a default callback to be used by all error stacks + * + * This method sets the callback that can be used to generate error + * messages for a singleton + * @param array|string Callback function/method + * @param string Package name, or false for all packages + * @static + */ + function setDefaultCallback($callback = false, $package = false) + { + if (!is_callable($callback)) { + $callback = false; + } + $package = $package ? $package : '*'; + $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback; + } + + /** + * Set a callback that generates context information (location of error) for an error stack + * + * This method sets the callback that can be used to generate context + * information for an error. Passing in NULL will disable context generation + * and remove the expensive call to debug_backtrace() + * @param array|string|null Callback function/method + */ + function setContextCallback($contextCallback) + { + if ($contextCallback === null) { + return $this->_contextCallback = false; + } + if (!$contextCallback) { + $this->_contextCallback = array(&$this, 'getFileLine'); + } else { + if (is_callable($contextCallback)) { + $this->_contextCallback = $contextCallback; + } + } + } + + /** + * Set an error Callback + * If set to a valid callback, this will be called every time an error + * is pushed onto the stack. The return value will be used to determine + * whether to allow an error to be pushed or logged. + * + * The return value must be one of the ERRORSTACK_* constants. + * + * This functionality can be used to emulate PEAR's pushErrorHandling, and + * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of + * the error stack or logging + * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG + * @see popCallback() + * @param string|array $cb + */ + function pushCallback($cb) + { + array_push($this->_errorCallback, $cb); + } + + /** + * Remove a callback from the error callback stack + * @see pushCallback() + * @return array|string|false + */ + function popCallback() + { + if (!count($this->_errorCallback)) { + return false; + } + return array_pop($this->_errorCallback); + } + + /** + * Set a temporary overriding error callback for every package error stack + * + * Use this to temporarily disable all existing callbacks (can be used + * to emulate the @ operator, for instance) + * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG + * @see staticPopCallback(), pushCallback() + * @param string|array $cb + * @static + */ + function staticPushCallback($cb) + { + array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb); + } + + /** + * Remove a temporary overriding error callback + * @see staticPushCallback() + * @return array|string|false + * @static + */ + function staticPopCallback() + { + $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']); + if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) { + $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); + } + return $ret; + } + + /** + * Add an error to the stack + * + * If the message generator exists, it is called with 2 parameters. + * - the current Error Stack object + * - an array that is in the same format as an error. Available indices + * are 'code', 'package', 'time', 'params', 'level', and 'context' + * + * Next, if the error should contain context information, this is + * handled by the context grabbing method. + * Finally, the error is pushed onto the proper error stack + * @param int $code Package-specific error code + * @param string $level Error level. This is NOT spell-checked + * @param array $params associative array of error parameters + * @param string $msg Error message, or a portion of it if the message + * is to be generated + * @param array $repackage If this error re-packages an error pushed by + * another package, place the array returned from + * {@link pop()} in this parameter + * @param array $backtrace Protected parameter: use this to pass in the + * {@link debug_backtrace()} that should be used + * to find error context + * @return PEAR_Error|array|Exception + * if compatibility mode is on, a PEAR_Error is also + * thrown. If the class Exception exists, then one + * is returned to allow code like: + * + * throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob'))); + * + * + * The errorData property of the exception class will be set to the array + * that would normally be returned. If a PEAR_Error is returned, the userinfo + * property is set to the array + * + * Otherwise, an array is returned in this format: + * + * array( + * 'code' => $code, + * 'params' => $params, + * 'package' => $this->_package, + * 'level' => $level, + * 'time' => time(), + * 'context' => $context, + * 'message' => $msg, + * //['repackage' => $err] repackaged error array/Exception class + * ); + * + */ + function push($code, $level = 'error', $params = array(), $msg = false, + $repackage = false, $backtrace = false) + { + $context = false; + // grab error context + if ($this->_contextCallback) { + if (!$backtrace) { + $backtrace = debug_backtrace(); + } + $context = call_user_func($this->_contextCallback, $code, $params, $backtrace); + } + + // save error + $time = explode(' ', microtime()); + $time = $time[1] + $time[0]; + $err = array( + 'code' => $code, + 'params' => $params, + 'package' => $this->_package, + 'level' => $level, + 'time' => $time, + 'context' => $context, + 'message' => $msg, + ); + + // set up the error message, if necessary + if ($this->_msgCallback) { + $msg = call_user_func_array($this->_msgCallback, + array(&$this, $err)); + $err['message'] = $msg; + } + + if ($repackage) { + $err['repackage'] = $repackage; + } + $push = $log = true; + $die = false; + // try the overriding callback first + $callback = $this->staticPopCallback(); + if ($callback) { + $this->staticPushCallback($callback); + } + if (!is_callable($callback)) { + // try the local callback next + $callback = $this->popCallback(); + if (is_callable($callback)) { + $this->pushCallback($callback); + } else { + // try the default callback + $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ? + $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] : + $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*']; + } + } + if (is_callable($callback)) { + switch(call_user_func($callback, $err)){ + case PEAR_ERRORSTACK_IGNORE: + return $err; + break; + case PEAR_ERRORSTACK_PUSH: + $log = false; + break; + case PEAR_ERRORSTACK_LOG: + $push = false; + break; + case PEAR_ERRORSTACK_DIE: + $die = true; + break; + // anything else returned has the same effect as pushandlog + } + } + if ($push) { + array_unshift($this->_errors, $err); + $this->_errorsByLevel[$err['level']][] = &$this->_errors[0]; + } + if ($log) { + if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) { + $this->_log($err); + } + } + if ($die) { + die(); + } + if ($this->_compat && $push) { + return $this->raiseError($msg, $code, null, null, $err); + } + return $err; + } + + /** + * Static version of {@link push()} + * + * @param string $package Package name this error belongs to + * @param int $code Package-specific error code + * @param string $level Error level. This is NOT spell-checked + * @param array $params associative array of error parameters + * @param string $msg Error message, or a portion of it if the message + * is to be generated + * @param array $repackage If this error re-packages an error pushed by + * another package, place the array returned from + * {@link pop()} in this parameter + * @param array $backtrace Protected parameter: use this to pass in the + * {@link debug_backtrace()} that should be used + * to find error context + * @return PEAR_Error|null|Exception + * if compatibility mode is on, a PEAR_Error is also + * thrown. If the class Exception exists, then one + * is returned to allow code like: + * + * throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob'))); + * + * @static + */ + function staticPush($package, $code, $level = 'error', $params = array(), + $msg = false, $repackage = false, $backtrace = false) + { + $s = &PEAR_ErrorStack::singleton($package); + if ($s->_contextCallback) { + if (!$backtrace) { + if (function_exists('debug_backtrace')) { + $backtrace = debug_backtrace(); + } + } + } + return $s->push($code, $level, $params, $msg, $repackage, $backtrace); + } + + /** + * Log an error using PEAR::Log + * @param array $err Error array + * @param array $levels Error level => Log constant map + * @access protected + */ + function _log($err) + { + if ($this->_logger) { + $logger = &$this->_logger; + } else { + $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']; + } + if (is_a($logger, 'Log')) { + $levels = array( + 'exception' => PEAR_LOG_CRIT, + 'alert' => PEAR_LOG_ALERT, + 'critical' => PEAR_LOG_CRIT, + 'error' => PEAR_LOG_ERR, + 'warning' => PEAR_LOG_WARNING, + 'notice' => PEAR_LOG_NOTICE, + 'info' => PEAR_LOG_INFO, + 'debug' => PEAR_LOG_DEBUG); + if (isset($levels[$err['level']])) { + $level = $levels[$err['level']]; + } else { + $level = PEAR_LOG_INFO; + } + $logger->log($err['message'], $level, $err); + } else { // support non-standard logs + call_user_func($logger, $err); + } + } + + + /** + * Pop an error off of the error stack + * + * @return false|array + * @since 0.4alpha it is no longer possible to specify a specific error + * level to return - the last error pushed will be returned, instead + */ + function pop() + { + return @array_shift($this->_errors); + } + + /** + * Determine whether there are any errors on the stack + * @param string|array Level name. Use to determine if any errors + * of level (string), or levels (array) have been pushed + * @return boolean + */ + function hasErrors($level = false) + { + if ($level) { + return isset($this->_errorsByLevel[$level]); + } + return count($this->_errors); + } + + /** + * Retrieve all errors since last purge + * + * @param boolean set in order to empty the error stack + * @param string level name, to return only errors of a particular severity + * @return array + */ + function getErrors($purge = false, $level = false) + { + if (!$purge) { + if ($level) { + if (!isset($this->_errorsByLevel[$level])) { + return array(); + } else { + return $this->_errorsByLevel[$level]; + } + } else { + return $this->_errors; + } + } + if ($level) { + $ret = $this->_errorsByLevel[$level]; + foreach ($this->_errorsByLevel[$level] as $i => $unused) { + // entries are references to the $_errors array + $this->_errorsByLevel[$level][$i] = false; + } + // array_filter removes all entries === false + $this->_errors = array_filter($this->_errors); + unset($this->_errorsByLevel[$level]); + return $ret; + } + $ret = $this->_errors; + $this->_errors = array(); + $this->_errorsByLevel = array(); + return $ret; + } + + /** + * Determine whether there are any errors on a single error stack, or on any error stack + * + * The optional parameter can be used to test the existence of any errors without the need of + * singleton instantiation + * @param string|false Package name to check for errors + * @param string Level name to check for a particular severity + * @return boolean + * @static + */ + function staticHasErrors($package = false, $level = false) + { + if ($package) { + if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { + return false; + } + return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level); + } + foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { + if ($obj->hasErrors($level)) { + return true; + } + } + return false; + } + + /** + * Get a list of all errors since last purge, organized by package + * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be + * @param boolean $purge Set to purge the error stack of existing errors + * @param string $level Set to a level name in order to retrieve only errors of a particular level + * @param boolean $merge Set to return a flat array, not organized by package + * @param array $sortfunc Function used to sort a merged array - default + * sorts by time, and should be good for most cases + * @static + * @return array + */ + function staticGetErrors($purge = false, $level = false, $merge = false, + $sortfunc = array('PEAR_ErrorStack', '_sortErrors')) + { + $ret = array(); + if (!is_callable($sortfunc)) { + $sortfunc = array('PEAR_ErrorStack', '_sortErrors'); + } + foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { + $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level); + if ($test) { + if ($merge) { + $ret = array_merge($ret, $test); + } else { + $ret[$package] = $test; + } + } + } + if ($merge) { + usort($ret, $sortfunc); + } + return $ret; + } + + /** + * Error sorting function, sorts by time + * @access private + */ + function _sortErrors($a, $b) + { + if ($a['time'] == $b['time']) { + return 0; + } + if ($a['time'] < $b['time']) { + return 1; + } + return -1; + } + + /** + * Standard file/line number/function/class context callback + * + * This function uses a backtrace generated from {@link debug_backtrace()} + * and so will not work at all in PHP < 4.3.0. The frame should + * reference the frame that contains the source of the error. + * @return array|false either array('file' => file, 'line' => line, + * 'function' => function name, 'class' => class name) or + * if this doesn't work, then false + * @param unused + * @param integer backtrace frame. + * @param array Results of debug_backtrace() + * @static + */ + function getFileLine($code, $params, $backtrace = null) + { + if ($backtrace === null) { + return false; + } + $frame = 0; + $functionframe = 1; + if (!isset($backtrace[1])) { + $functionframe = 0; + } else { + while (isset($backtrace[$functionframe]['function']) && + $backtrace[$functionframe]['function'] == 'eval' && + isset($backtrace[$functionframe + 1])) { + $functionframe++; + } + } + if (isset($backtrace[$frame])) { + if (!isset($backtrace[$frame]['file'])) { + $frame++; + } + $funcbacktrace = $backtrace[$functionframe]; + $filebacktrace = $backtrace[$frame]; + $ret = array('file' => $filebacktrace['file'], + 'line' => $filebacktrace['line']); + // rearrange for eval'd code or create function errors + if (strpos($filebacktrace['file'], '(') && + preg_match(';^(.*?)\((\d+)\) : (.*?)$;', $filebacktrace['file'], + $matches)) { + $ret['file'] = $matches[1]; + $ret['line'] = $matches[2] + 0; + } + if (isset($funcbacktrace['function']) && isset($backtrace[1])) { + if ($funcbacktrace['function'] != 'eval') { + if ($funcbacktrace['function'] == '__lambda_func') { + $ret['function'] = 'create_function() code'; + } else { + $ret['function'] = $funcbacktrace['function']; + } + } + } + if (isset($funcbacktrace['class']) && isset($backtrace[1])) { + $ret['class'] = $funcbacktrace['class']; + } + return $ret; + } + return false; + } + + /** + * Standard error message generation callback + * + * This method may also be called by a custom error message generator + * to fill in template values from the params array, simply + * set the third parameter to the error message template string to use + * + * The special variable %__msg% is reserved: use it only to specify + * where a message passed in by the user should be placed in the template, + * like so: + * + * Error message: %msg% - internal error + * + * If the message passed like so: + * + * + * $stack->push(ERROR_CODE, 'error', array(), 'server error 500'); + * + * + * The returned error message will be "Error message: server error 500 - + * internal error" + * @param PEAR_ErrorStack + * @param array + * @param string|false Pre-generated error message template + * @static + * @return string + */ + function getErrorMessage(&$stack, $err, $template = false) + { + if ($template) { + $mainmsg = $template; + } else { + $mainmsg = $stack->getErrorMessageTemplate($err['code']); + } + $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg); + if (count($err['params'])) { + foreach ($err['params'] as $name => $val) { + if (is_array($val)) { + // @ is needed in case $val is a multi-dimensional array + $val = @implode(', ', $val); + } + if (is_object($val)) { + if (method_exists($val, '__toString')) { + $val = $val->__toString(); + } else { + PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING, + 'warning', array('obj' => get_class($val)), + 'object %obj% passed into getErrorMessage, but has no __toString() method'); + $val = 'Object'; + } + } + $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg); + } + } + return $mainmsg; + } + + /** + * Standard Error Message Template generator from code + * @return string + */ + function getErrorMessageTemplate($code) + { + if (!isset($this->_errorMsgs[$code])) { + return '%__msg%'; + } + return $this->_errorMsgs[$code]; + } + + /** + * Set the Error Message Template array + * + * The array format must be: + *
+     * array(error code => 'message template',...)
+     * 
+ * + * Error message parameters passed into {@link push()} will be used as input + * for the error message. If the template is 'message %foo% was %bar%', and the + * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will + * be 'message one was six' + * @return string + */ + function setErrorMessageTemplate($template) + { + $this->_errorMsgs = $template; + } + + + /** + * emulate PEAR::raiseError() + * + * @return PEAR_Error + */ + function raiseError() + { + require_once 'PEAR.php'; + $args = func_get_args(); + return call_user_func_array(array('PEAR', 'raiseError'), $args); + } +} +$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack'); +$stack->pushCallback(array('PEAR_ErrorStack', '_handleError')); +?> \ No newline at end of file diff --git a/src/www/lib/pear/PEAR/Exception.php b/src/www/lib/pear/PEAR/Exception.php new file mode 100644 index 00000000..4c1918ed --- /dev/null +++ b/src/www/lib/pear/PEAR/Exception.php @@ -0,0 +1,359 @@ + | +// | Hans Lellelid | +// | Bertrand Mansion | +// | Greg Beaver | +// +----------------------------------------------------------------------+ +// +// $Id: Exception.php,v 1.1 2005/05/28 01:55:13 henrique Exp $ + + +/** + * Base PEAR_Exception Class + * + * WARNING: This code should be considered stable, but the API is + * subject to immediate and drastic change, so API stability is + * at best alpha + * + * 1) Features: + * + * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception)) + * - Definable triggers, shot when exceptions occur + * - Pretty and informative error messages + * - Added more context info available (like class, method or cause) + * - cause can be a PEAR_Exception or an array of mixed + * PEAR_Exceptions/PEAR_ErrorStack warnings + * - callbacks for specific exception classes and their children + * + * 2) Ideas: + * + * - Maybe a way to define a 'template' for the output + * + * 3) Inherited properties from PHP Exception Class: + * + * protected $message + * protected $code + * protected $line + * protected $file + * private $trace + * + * 4) Inherited methods from PHP Exception Class: + * + * __clone + * __construct + * getMessage + * getCode + * getFile + * getLine + * getTraceSafe + * getTraceSafeAsString + * __toString + * + * 5) Usage example + * + * + * require_once 'PEAR/Exception.php'; + * + * class Test { + * function foo() { + * throw new PEAR_Exception('Error Message', ERROR_CODE); + * } + * } + * + * function myLogger($pear_exception) { + * echo $pear_exception->getMessage(); + * } + * // each time a exception is thrown the 'myLogger' will be called + * // (its use is completely optional) + * PEAR_Exception::addObserver('myLogger'); + * $test = new Test; + * try { + * $test->foo(); + * } catch (PEAR_Exception $e) { + * print $e; + * } + * + * + * @since PHP 5 + * @package PEAR + * @version $Revision: 1.1 $ + * @author Tomas V.V.Cox + * @author Hans Lellelid + * @author Bertrand Mansion + * + */ +class PEAR_Exception extends Exception +{ + const OBSERVER_PRINT = -2; + const OBSERVER_TRIGGER = -4; + const OBSERVER_DIE = -8; + protected $cause; + private static $_observers = array(); + private static $_uniqueid = 0; + private $_trace; + + /** + * Supported signatures: + * PEAR_Exception(string $message); + * PEAR_Exception(string $message, int $code); + * PEAR_Exception(string $message, Exception $cause); + * PEAR_Exception(string $message, Exception $cause, int $code); + * PEAR_Exception(string $message, array $causes); + * PEAR_Exception(string $message, array $causes, int $code); + */ + public function __construct($message, $p2 = null, $p3 = null) + { + if (is_int($p2)) { + $code = $p2; + $this->cause = null; + } elseif ($p2 instanceof Exception || is_array($p2)) { + $code = $p3; + if (is_array($p2) && isset($p2['message'])) { + // fix potential problem of passing in a single warning + $p2 = array($p2); + } + $this->cause = $p2; + } else { + $code = null; + $this->cause = null; + } + parent::__construct($message, $code); + $this->signal(); + } + + /** + * @param mixed $callback - A valid php callback, see php func is_callable() + * - A PEAR_Exception::OBSERVER_* constant + * - An array(const PEAR_Exception::OBSERVER_*, + * mixed $options) + * @param string $label The name of the observer. Use this if you want + * to remove it later with removeObserver() + */ + public static function addObserver($callback, $label = 'default') + { + self::$_observers[$label] = $callback; + } + + public static function removeObserver($label = 'default') + { + unset(self::$_observers[$label]); + } + + /** + * @return int unique identifier for an observer + */ + public static function getUniqueId() + { + return self::$_uniqueid++; + } + + private function signal() + { + foreach (self::$_observers as $func) { + if (is_callable($func)) { + call_user_func($func, $this); + continue; + } + settype($func, 'array'); + switch ($func[0]) { + case self::OBSERVER_PRINT : + $f = (isset($func[1])) ? $func[1] : '%s'; + printf($f, $this->getMessage()); + break; + case self::OBSERVER_TRIGGER : + $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE; + trigger_error($this->getMessage(), $f); + break; + case self::OBSERVER_DIE : + $f = (isset($func[1])) ? $func[1] : '%s'; + die(printf($f, $this->getMessage())); + break; + default: + trigger_error('invalid observer type', E_USER_WARNING); + } + } + } + + /** + * Return specific error information that can be used for more detailed + * error messages or translation. + * + * This method may be overridden in child exception classes in order + * to add functionality not present in PEAR_Exception and is a placeholder + * to define API + * + * The returned array must be an associative array of parameter => value like so: + *
+     * array('name' => $name, 'context' => array(...))
+     * 
+ * @return array + */ + public function getErrorData() + { + return array(); + } + + /** + * Returns the exception that caused this exception to be thrown + * @access public + * @return Exception|array The context of the exception + */ + public function getCause() + { + return $this->cause; + } + + /** + * Function must be public to call on caused exceptions + * @param array + */ + public function getCauseMessage(&$causes) + { + $trace = $this->getTraceSafe(); + $cause = array('class' => get_class($this), + 'message' => $this->message, + 'file' => 'unknown', + 'line' => 'unknown'); + if (isset($trace[0])) { + if (isset($trace[0]['file'])) { + $cause['file'] = $trace[0]['file']; + $cause['line'] = $trace[0]['line']; + } + } + if ($this->cause instanceof PEAR_Exception) { + $this->cause->getCauseMessage($causes); + } + if (is_array($this->cause)) { + foreach ($this->cause as $cause) { + if ($cause instanceof PEAR_Exception) { + $cause->getCauseMessage($causes); + } elseif (is_array($cause) && isset($cause['message'])) { + // PEAR_ErrorStack warning + $causes[] = array( + 'class' => $cause['package'], + 'message' => $cause['message'], + 'file' => isset($cause['context']['file']) ? + $cause['context']['file'] : + 'unknown', + 'line' => isset($cause['context']['line']) ? + $cause['context']['line'] : + 'unknown', + ); + } + } + } + } + + public function getTraceSafe() + { + if (!isset($this->_trace)) { + $this->_trace = $this->getTrace(); + if (empty($this->_trace)) { + $backtrace = debug_backtrace(); + $this->_trace = array($backtrace[count($backtrace)-1]); + } + } + return $this->_trace; + } + + public function getErrorClass() + { + $trace = $this->getTraceSafe(); + return $trace[0]['class']; + } + + public function getErrorMethod() + { + $trace = $this->getTraceSafe(); + return $trace[0]['function']; + } + + public function __toString() + { + if (isset($_SERVER['REQUEST_URI'])) { + return $this->toHtml(); + } + return $this->toText(); + } + + public function toHtml() + { + $trace = $this->getTraceSafe(); + $causes = array(); + $this->getCauseMessage($causes); + $html = '' . "\n"; + foreach ($causes as $i => $cause) { + $html .= '\n"; + } + $html .= '' . "\n" + . '' + . '' + . '' . "\n"; + + foreach ($trace as $k => $v) { + $html .= '' + . '' + . '' . "\n"; + } + $html .= '' + . '' + . '' . "\n" + . '
' + . str_repeat('-', $i) . ' ' . $cause['class'] . ': ' + . htmlspecialchars($cause['message']) . ' in ' . $cause['file'] . ' ' + . 'on line ' . $cause['line'] . '' + . "
Exception trace
#FunctionLocation
' . $k . ''; + if (!empty($v['class'])) { + $html .= $v['class'] . $v['type']; + } + $html .= $v['function']; + $args = array(); + if (!empty($v['args'])) { + foreach ($v['args'] as $arg) { + if (is_null($arg)) $args[] = 'null'; + elseif (is_array($arg)) $args[] = 'Array'; + elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')'; + elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false'; + elseif (is_int($arg) || is_double($arg)) $args[] = $arg; + else { + $arg = (string)$arg; + $str = htmlspecialchars(substr($arg, 0, 16)); + if (strlen($arg) > 16) $str .= '…'; + $args[] = "'" . $str . "'"; + } + } + } + $html .= '(' . implode(', ',$args) . ')' + . '' . $v['file'] . ':' . $v['line'] . '
' . ($k+1) . '{main} 
'; + return $html; + } + + public function toText() + { + $causes = array(); + $this->getCauseMessage($causes); + $causeMsg = ''; + foreach ($causes as $i => $cause) { + $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': ' + . $cause['message'] . ' in ' . $cause['file'] + . ' on line ' . $cause['line'] . "\n"; + } + return $causeMsg . $this->getTraceAsString(); + } +} + +?> \ No newline at end of file diff --git a/src/www/lib/pear/PEAR/Frontend/CLI.php b/src/www/lib/pear/PEAR/Frontend/CLI.php new file mode 100644 index 00000000..38f97ef5 --- /dev/null +++ b/src/www/lib/pear/PEAR/Frontend/CLI.php @@ -0,0 +1,509 @@ + | + +----------------------------------------------------------------------+ + + $Id: CLI.php,v 1.1 2005/05/28 01:55:58 henrique Exp $ +*/ + +require_once "PEAR.php"; + +class PEAR_Frontend_CLI extends PEAR +{ + // {{{ properties + + /** + * What type of user interface this frontend is for. + * @var string + * @access public + */ + var $type = 'CLI'; + var $lp = ''; // line prefix + + var $params = array(); + var $term = array( + 'bold' => '', + 'normal' => '', + ); + + // }}} + + // {{{ constructor + + function PEAR_Frontend_CLI() + { + parent::PEAR(); + $term = getenv('TERM'); //(cox) $_ENV is empty for me in 4.1.1 + if (function_exists('posix_isatty') && !posix_isatty(1)) { + // output is being redirected to a file or through a pipe + } elseif ($term) { + // XXX can use ncurses extension here, if available + if (preg_match('/^(xterm|vt220|linux)/', $term)) { + $this->term['bold'] = sprintf("%c%c%c%c", 27, 91, 49, 109); + $this->term['normal']=sprintf("%c%c%c", 27, 91, 109); + } elseif (preg_match('/^vt100/', $term)) { + $this->term['bold'] = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0); + $this->term['normal']=sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0); + } + } elseif (OS_WINDOWS) { + // XXX add ANSI codes here + } + } + + // }}} + + // {{{ displayLine(text) + + function displayLine($text) + { + trigger_error("PEAR_Frontend_CLI::displayLine deprecated", E_USER_ERROR); + } + + function _displayLine($text) + { + print "$this->lp$text\n"; + } + + // }}} + // {{{ display(text) + + function display($text) + { + trigger_error("PEAR_Frontend_CLI::display deprecated", E_USER_ERROR); + } + + function _display($text) + { + print $text; + } + + // }}} + // {{{ displayError(eobj) + + /** + * @param object PEAR_Error object + */ + function displayError($eobj) + { + return $this->_displayLine($eobj->getMessage()); + } + + // }}} + // {{{ displayFatalError(eobj) + + /** + * @param object PEAR_Error object + */ + function displayFatalError($eobj) + { + $this->displayError($eobj); + exit(1); + } + + // }}} + // {{{ displayHeading(title) + + function displayHeading($title) + { + trigger_error("PEAR_Frontend_CLI::displayHeading deprecated", E_USER_ERROR); + } + + function _displayHeading($title) + { + print $this->lp.$this->bold($title)."\n"; + print $this->lp.str_repeat("=", strlen($title))."\n"; + } + + // }}} + // {{{ userDialog(prompt, [type], [default]) + + function userDialog($command, $prompts, $types = array(), $defaults = array()) + { + $result = array(); + if (is_array($prompts)) { + $fp = fopen("php://stdin", "r"); + foreach ($prompts as $key => $prompt) { + $type = $types[$key]; + $default = @$defaults[$key]; + if ($type == 'password') { + system('stty -echo'); + } + print "$this->lp$prompt "; + if ($default) { + print "[$default] "; + } + print ": "; + $line = fgets($fp, 2048); + if ($type == 'password') { + system('stty echo'); + print "\n"; + } + if ($default && trim($line) == "") { + $result[$key] = $default; + } else { + $result[$key] = $line; + } + } + fclose($fp); + } + return $result; + } + + // }}} + // {{{ userConfirm(prompt, [default]) + + function userConfirm($prompt, $default = 'yes') + { + trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR); + static $positives = array('y', 'yes', 'on', '1'); + static $negatives = array('n', 'no', 'off', '0'); + print "$this->lp$prompt [$default] : "; + $fp = fopen("php://stdin", "r"); + $line = fgets($fp, 2048); + fclose($fp); + $answer = strtolower(trim($line)); + if (empty($answer)) { + $answer = $default; + } + if (in_array($answer, $positives)) { + return true; + } + if (in_array($answer, $negatives)) { + return false; + } + if (in_array($default, $positives)) { + return true; + } + return false; + } + + // }}} + // {{{ startTable([params]) + + function startTable($params = array()) + { + trigger_error("PEAR_Frontend_CLI::startTable deprecated", E_USER_ERROR); + } + + function _startTable($params = array()) + { + $params['table_data'] = array(); + $params['widest'] = array(); // indexed by column + $params['highest'] = array(); // indexed by row + $params['ncols'] = 0; + $this->params = $params; + } + + // }}} + // {{{ tableRow(columns, [rowparams], [colparams]) + + function tableRow($columns, $rowparams = array(), $colparams = array()) + { + trigger_error("PEAR_Frontend_CLI::tableRow deprecated", E_USER_ERROR); + } + + function _tableRow($columns, $rowparams = array(), $colparams = array()) + { + $highest = 1; + for ($i = 0; $i < sizeof($columns); $i++) { + $col = &$columns[$i]; + if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) { + $col = wordwrap($col, $colparams[$i]['wrap'], "\n", 0); + } + if (strpos($col, "\n") !== false) { + $multiline = explode("\n", $col); + $w = 0; + foreach ($multiline as $n => $line) { + if (strlen($line) > $w) { + $w = strlen($line); + } + } + $lines = sizeof($multiline); + } else { + $w = strlen($col); + } + if ($w > @$this->params['widest'][$i]) { + $this->params['widest'][$i] = $w; + } + $tmp = count_chars($columns[$i], 1); + // handle unix, mac and windows formats + $lines = (isset($tmp[10]) ? $tmp[10] : @$tmp[13]) + 1; + if ($lines > $highest) { + $highest = $lines; + } + } + if (sizeof($columns) > $this->params['ncols']) { + $this->params['ncols'] = sizeof($columns); + } + $new_row = array( + 'data' => $columns, + 'height' => $highest, + 'rowparams' => $rowparams, + 'colparams' => $colparams, + ); + $this->params['table_data'][] = $new_row; + } + + // }}} + // {{{ endTable() + + function endTable() + { + trigger_error("PEAR_Frontend_CLI::endTable deprecated", E_USER_ERROR); + } + + function _endTable() + { + extract($this->params); + if (!empty($caption)) { + $this->_displayHeading($caption); + } + if (count($table_data) == 0) { + return; + } + if (!isset($width)) { + $width = $widest; + } else { + for ($i = 0; $i < $ncols; $i++) { + if (!isset($width[$i])) { + $width[$i] = $widest[$i]; + } + } + } + $border = false; + if (empty($border)) { + $cellstart = ''; + $cellend = ' '; + $rowend = ''; + $padrowend = false; + $borderline = ''; + } else { + $cellstart = '| '; + $cellend = ' '; + $rowend = '|'; + $padrowend = true; + $borderline = '+'; + foreach ($width as $w) { + $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1); + $borderline .= '+'; + } + } + if ($borderline) { + $this->_displayLine($borderline); + } + for ($i = 0; $i < sizeof($table_data); $i++) { + extract($table_data[$i]); + if (!is_array($rowparams)) { + $rowparams = array(); + } + if (!is_array($colparams)) { + $colparams = array(); + } + $rowlines = array(); + if ($height > 1) { + for ($c = 0; $c < sizeof($data); $c++) { + $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]); + if (sizeof($rowlines[$c]) < $height) { + $rowlines[$c] = array_pad($rowlines[$c], $height, ''); + } + } + } else { + for ($c = 0; $c < sizeof($data); $c++) { + $rowlines[$c] = array($data[$c]); + } + } + for ($r = 0; $r < $height; $r++) { + $rowtext = ''; + for ($c = 0; $c < sizeof($data); $c++) { + if (isset($colparams[$c])) { + $attribs = array_merge($rowparams, $colparams); + } else { + $attribs = $rowparams; + } + $w = isset($width[$c]) ? $width[$c] : 0; + //$cell = $data[$c]; + $cell = $rowlines[$c][$r]; + $l = strlen($cell); + if ($l > $w) { + $cell = substr($cell, 0, $w); + } + if (isset($attribs['bold'])) { + $cell = $this->bold($cell); + } + if ($l < $w) { + // not using str_pad here because we may + // add bold escape characters to $cell + $cell .= str_repeat(' ', $w - $l); + } + + $rowtext .= $cellstart . $cell . $cellend; + } + if (!$border) { + $rowtext = rtrim($rowtext); + } + $rowtext .= $rowend; + $this->_displayLine($rowtext); + } + } + if ($borderline) { + $this->_displayLine($borderline); + } + } + + // }}} + // {{{ outputData() + + function outputData($data, $command = '_default') + { + switch ($command) { + case 'install': + case 'upgrade': + case 'upgrade-all': + if (isset($data['release_warnings'])) { + $this->_displayLine(''); + $this->_startTable(array( + 'border' => false, + 'caption' => 'Release Warnings' + )); + $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55))); + $this->_endTable(); + $this->_displayLine(''); + } + $this->_displayLine($data['data']); + break; + case 'search': + $this->_startTable($data); + if (isset($data['headline']) && is_array($data['headline'])) { + $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55))); + } + + foreach($data['data'] as $category) { + foreach($category as $pkg) { + $this->_tableRow($pkg, null, array(1 => array('wrap' => 55))); + } + }; + $this->_endTable(); + break; + case 'list-all': + $this->_startTable($data); + if (isset($data['headline']) && is_array($data['headline'])) { + $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55))); + } + + foreach($data['data'] as $category) { + foreach($category as $pkg) { + unset($pkg[3]); + unset($pkg[4]); + $this->_tableRow($pkg, null, array(1 => array('wrap' => 55))); + } + }; + $this->_endTable(); + break; + case 'config-show': + $data['border'] = false; + $opts = array(0 => array('wrap' => 30), + 1 => array('wrap' => 20), + 2 => array('wrap' => 35)); + $this->_startTable($data); + if (isset($data['headline']) && is_array($data['headline'])) { + $this->_tableRow($data['headline'], + array('bold' => true), + $opts); + } + foreach($data['data'] as $group) { + foreach($group as $value) { + if ($value[2] == '') { + $value[2] = ""; + } + $this->_tableRow($value, null, $opts); + } + } + $this->_endTable(); + break; + case 'remote-info': + $data = array( + 'caption' => 'Package details:', + 'border' => false, + 'data' => array( + array("Latest", $data['stable']), + array("Installed", $data['installed']), + array("Package", $data['name']), + array("License", $data['license']), + array("Category", $data['category']), + array("Summary", $data['summary']), + array("Description", $data['description']), + ), + ); + default: { + if (is_array($data)) { + $this->_startTable($data); + $count = count($data['data'][0]); + if ($count == 2) { + $opts = array(0 => array('wrap' => 25), + 1 => array('wrap' => 48) + ); + } elseif ($count == 3) { + $opts = array(0 => array('wrap' => 30), + 1 => array('wrap' => 20), + 2 => array('wrap' => 35) + ); + } else { + $opts = null; + } + if (isset($data['headline']) && is_array($data['headline'])) { + $this->_tableRow($data['headline'], + array('bold' => true), + $opts); + } + foreach($data['data'] as $row) { + $this->_tableRow($row, null, $opts); + } + $this->_endTable(); + } else { + $this->_displayLine($data); + } + } + } + } + + // }}} + // {{{ log(text) + + + function log($text, $append_crlf = true) + { + if ($append_crlf) { + return $this->_displayLine($text); + } + return $this->_display($text); + } + + + // }}} + // {{{ bold($text) + + function bold($text) + { + if (empty($this->term['bold'])) { + return strtoupper($text); + } + return $this->term['bold'] . $text . $this->term['normal']; + } + + // }}} +} + +?> diff --git a/src/www/lib/pear/PEAR/Installer.php b/src/www/lib/pear/PEAR/Installer.php new file mode 100644 index 00000000..0d0ffb7f --- /dev/null +++ b/src/www/lib/pear/PEAR/Installer.php @@ -0,0 +1,1068 @@ + | +// | Tomas V.V.Cox | +// | Martin Jansen | +// +----------------------------------------------------------------------+ +// +// $Id: Installer.php,v 1.1 2005/05/28 01:55:13 henrique Exp $ + +require_once 'PEAR/Downloader.php'; + +/** + * Administration class used to install PEAR packages and maintain the + * installed package database. + * + * TODO: + * - Check dependencies break on package uninstall (when no force given) + * - add a guessInstallDest() method with the code from _installFile() and + * use that method in Registry::_rebuildFileMap() & Command_Registry::doList(), + * others.. + * + * @since PHP 4.0.2 + * @author Stig Bakken + * @author Martin Jansen + * @author Greg Beaver + */ +class PEAR_Installer extends PEAR_Downloader +{ + // {{{ properties + + /** name of the package directory, for example Foo-1.0 + * @var string + */ + var $pkgdir; + + /** directory where PHP code files go + * @var string + */ + var $phpdir; + + /** directory where PHP extension files go + * @var string + */ + var $extdir; + + /** directory where documentation goes + * @var string + */ + var $docdir; + + /** installation root directory (ala PHP's INSTALL_ROOT or + * automake's DESTDIR + * @var string + */ + var $installroot = ''; + + /** debug level + * @var int + */ + var $debug = 1; + + /** temporary directory + * @var string + */ + var $tmpdir; + + /** PEAR_Registry object used by the installer + * @var object + */ + var $registry; + + /** List of file transactions queued for an install/upgrade/uninstall. + * + * Format: + * array( + * 0 => array("rename => array("from-file", "to-file")), + * 1 => array("delete" => array("file-to-delete")), + * ... + * ) + * + * @var array + */ + var $file_operations = array(); + + // }}} + + // {{{ constructor + + /** + * PEAR_Installer constructor. + * + * @param object $ui user interface object (instance of PEAR_Frontend_*) + * + * @access public + */ + function PEAR_Installer(&$ui) + { + parent::PEAR_Common(); + $this->setFrontendObject($ui); + $this->debug = $this->config->get('verbose'); + //$this->registry = &new PEAR_Registry($this->config->get('php_dir')); + } + + // }}} + + // {{{ _deletePackageFiles() + + /** + * Delete a package's installed files, does not remove empty directories. + * + * @param string $package package name + * + * @return bool TRUE on success, or a PEAR error on failure + * + * @access private + */ + function _deletePackageFiles($package) + { + if (!strlen($package)) { + return $this->raiseError("No package to uninstall given"); + } + $filelist = $this->registry->packageInfo($package, 'filelist'); + if ($filelist == null) { + return $this->raiseError("$package not installed"); + } + foreach ($filelist as $file => $props) { + if (empty($props['installed_as'])) { + continue; + } + $path = $this->_prependPath($props['installed_as'], $this->installroot); + $this->addFileOperation('delete', array($path)); + } + return true; + } + + // }}} + // {{{ _installFile() + + /** + * @param string filename + * @param array attributes from tag in package.xml + * @param string path to install the file in + * @param array options from command-line + * @access private + */ + function _installFile($file, $atts, $tmp_path, $options) + { + // {{{ return if this file is meant for another platform + static $os; + if (isset($atts['platform'])) { + if (empty($os)) { + include_once "OS/Guess.php"; + $os = new OS_Guess(); + } + if (strlen($atts['platform']) && $atts['platform']{0} == '!') { + $negate = true; + $platform = substr($atts['platform'], 1); + } else { + $negate = false; + $platform = $atts['platform']; + } + if ((bool) $os->matchSignature($platform) === $negate) { + $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")"); + return PEAR_INSTALLER_SKIPPED; + } + } + // }}} + + // {{{ assemble the destination paths + switch ($atts['role']) { + case 'doc': + case 'data': + case 'test': + $dest_dir = $this->config->get($atts['role'] . '_dir') . + DIRECTORY_SEPARATOR . $this->pkginfo['package']; + unset($atts['baseinstalldir']); + break; + case 'ext': + case 'php': + $dest_dir = $this->config->get($atts['role'] . '_dir'); + break; + case 'script': + $dest_dir = $this->config->get('bin_dir'); + break; + case 'src': + case 'extsrc': + $this->source_files++; + return; + default: + return $this->raiseError("Invalid role `$atts[role]' for file $file"); + } + $save_destdir = $dest_dir; + if (!empty($atts['baseinstalldir'])) { + $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; + } + if (dirname($file) != '.' && empty($atts['install-as'])) { + $dest_dir .= DIRECTORY_SEPARATOR . dirname($file); + } + if (empty($atts['install-as'])) { + $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file); + } else { + $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as']; + } + $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file; + + // Clean up the DIRECTORY_SEPARATOR mess + $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; + list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"), + DIRECTORY_SEPARATOR, + array($dest_file, $orig_file)); + $installed_as = $dest_file; + $final_dest_file = $this->_prependPath($dest_file, $this->installroot); + $dest_dir = dirname($final_dest_file); + $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); + // }}} + + if (!@is_dir($dest_dir)) { + if (!$this->mkDirHier($dest_dir)) { + return $this->raiseError("failed to mkdir $dest_dir", + PEAR_INSTALLER_FAILED); + } + $this->log(3, "+ mkdir $dest_dir"); + } + if (empty($atts['replacements'])) { + if (!file_exists($orig_file)) { + return $this->raiseError("file does not exist", + PEAR_INSTALLER_FAILED); + } + if (!@copy($orig_file, $dest_file)) { + return $this->raiseError("failed to write $dest_file", + PEAR_INSTALLER_FAILED); + } + $this->log(3, "+ cp $orig_file $dest_file"); + if (isset($atts['md5sum'])) { + $md5sum = md5_file($dest_file); + } + } else { + // {{{ file with replacements + if (!file_exists($orig_file)) { + return $this->raiseError("file does not exist", + PEAR_INSTALLER_FAILED); + } + $fp = fopen($orig_file, "r"); + $contents = fread($fp, filesize($orig_file)); + fclose($fp); + if (isset($atts['md5sum'])) { + $md5sum = md5($contents); + } + $subst_from = $subst_to = array(); + foreach ($atts['replacements'] as $a) { + $to = ''; + if ($a['type'] == 'php-const') { + if (preg_match('/^[a-z0-9_]+$/i', $a['to'])) { + eval("\$to = $a[to];"); + } else { + $this->log(0, "invalid php-const replacement: $a[to]"); + continue; + } + } elseif ($a['type'] == 'pear-config') { + $to = $this->config->get($a['to']); + if (is_null($to)) { + $this->log(0, "invalid pear-config replacement: $a[to]"); + continue; + } + } elseif ($a['type'] == 'package-info') { + if (isset($this->pkginfo[$a['to']]) && is_string($this->pkginfo[$a['to']])) { + $to = $this->pkginfo[$a['to']]; + } else { + $this->log(0, "invalid package-info replacement: $a[to]"); + continue; + } + } + if (!is_null($to)) { + $subst_from[] = $a['from']; + $subst_to[] = $to; + } + } + $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file"); + if (sizeof($subst_from)) { + $contents = str_replace($subst_from, $subst_to, $contents); + } + $wp = @fopen($dest_file, "wb"); + if (!is_resource($wp)) { + return $this->raiseError("failed to create $dest_file: $php_errormsg", + PEAR_INSTALLER_FAILED); + } + if (!fwrite($wp, $contents)) { + return $this->raiseError("failed writing to $dest_file: $php_errormsg", + PEAR_INSTALLER_FAILED); + } + fclose($wp); + // }}} + } + // {{{ check the md5 + if (isset($md5sum)) { + if (strtolower($md5sum) == strtolower($atts['md5sum'])) { + $this->log(2, "md5sum ok: $final_dest_file"); + } else { + if (empty($options['force'])) { + // delete the file + @unlink($dest_file); + return $this->raiseError("bad md5sum for file $final_dest_file", + PEAR_INSTALLER_FAILED); + } else { + $this->log(0, "warning : bad md5sum for file $final_dest_file"); + } + } + } + // }}} + // {{{ set file permissions + if (!OS_WINDOWS) { + if ($atts['role'] == 'script') { + $mode = 0777 & ~(int)octdec($this->config->get('umask')); + $this->log(3, "+ chmod +x $dest_file"); + } else { + $mode = 0666 & ~(int)octdec($this->config->get('umask')); + } + $this->addFileOperation("chmod", array($mode, $dest_file)); + if (!@chmod($dest_file, $mode)) { + $this->log(0, "failed to change mode of $dest_file"); + } + } + // }}} + $this->addFileOperation("rename", array($dest_file, $final_dest_file)); + // Store the full path where the file was installed for easy unistall + $this->addFileOperation("installed_as", array($file, $installed_as, + $save_destdir, dirname(substr($dest_file, strlen($save_destdir))))); + + //$this->log(2, "installed: $dest_file"); + return PEAR_INSTALLER_OK; + } + + // }}} + // {{{ addFileOperation() + + /** + * Add a file operation to the current file transaction. + * + * @see startFileTransaction() + * @var string $type This can be one of: + * - rename: rename a file ($data has 2 values) + * - chmod: change permissions on a file ($data has 2 values) + * - delete: delete a file ($data has 1 value) + * - rmdir: delete a directory if empty ($data has 1 value) + * - installed_as: mark a file as installed ($data has 4 values). + * @var array $data For all file operations, this array must contain the + * full path to the file or directory that is being operated on. For + * the rename command, the first parameter must be the file to rename, + * the second its new name. + * + * The installed_as operation contains 4 elements in this order: + * 1. Filename as listed in the filelist element from package.xml + * 2. Full path to the installed file + * 3. Full path from the php_dir configuration variable used in this + * installation + * 4. Relative path from the php_dir that this file is installed in + */ + function addFileOperation($type, $data) + { + if (!is_array($data)) { + return $this->raiseError('Internal Error: $data in addFileOperation' + . ' must be an array, was ' . gettype($data)); + } + if ($type == 'chmod') { + $octmode = decoct($data[0]); + $this->log(3, "adding to transaction: $type $octmode $data[1]"); + } else { + $this->log(3, "adding to transaction: $type " . implode(" ", $data)); + } + $this->file_operations[] = array($type, $data); + } + + // }}} + // {{{ startFileTransaction() + + function startFileTransaction($rollback_in_case = false) + { + if (count($this->file_operations) && $rollback_in_case) { + $this->rollbackFileTransaction(); + } + $this->file_operations = array(); + } + + // }}} + // {{{ commitFileTransaction() + + function commitFileTransaction() + { + $n = count($this->file_operations); + $this->log(2, "about to commit $n file operations"); + // {{{ first, check permissions and such manually + $errors = array(); + foreach ($this->file_operations as $tr) { + list($type, $data) = $tr; + switch ($type) { + case 'rename': + if (!file_exists($data[0])) { + $errors[] = "cannot rename file $data[0], doesn't exist"; + } + // check that dest dir. is writable + if (!is_writable(dirname($data[1]))) { + $errors[] = "permission denied ($type): $data[1]"; + } + break; + case 'chmod': + // check that file is writable + if (!is_writable($data[1])) { + $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]); + } + break; + case 'delete': + if (!file_exists($data[0])) { + $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted"); + } + // check that directory is writable + if (file_exists($data[0]) && !is_writable(dirname($data[0]))) { + $errors[] = "permission denied ($type): $data[0]"; + } + break; + } + + } + // }}} + $m = sizeof($errors); + if ($m > 0) { + foreach ($errors as $error) { + $this->log(1, $error); + } + return false; + } + // {{{ really commit the transaction + foreach ($this->file_operations as $tr) { + list($type, $data) = $tr; + switch ($type) { + case 'rename': + @unlink($data[1]); + @rename($data[0], $data[1]); + $this->log(3, "+ mv $data[0] $data[1]"); + break; + case 'chmod': + @chmod($data[1], $data[0]); + $octmode = decoct($data[0]); + $this->log(3, "+ chmod $octmode $data[1]"); + break; + case 'delete': + @unlink($data[0]); + $this->log(3, "+ rm $data[0]"); + break; + case 'rmdir': + @rmdir($data[0]); + $this->log(3, "+ rmdir $data[0]"); + break; + case 'installed_as': + $this->pkginfo['filelist'][$data[0]]['installed_as'] = $data[1]; + if (!isset($this->pkginfo['filelist']['dirtree'][dirname($data[1])])) { + $this->pkginfo['filelist']['dirtree'][dirname($data[1])] = true; + while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\' + && $data[3] != '.') { + $this->pkginfo['filelist']['dirtree'] + [$this->_prependPath($data[3], $data[2])] = true; + $data[3] = dirname($data[3]); + } + } + break; + } + } + // }}} + $this->log(2, "successfully committed $n file operations"); + $this->file_operations = array(); + return true; + } + + // }}} + // {{{ rollbackFileTransaction() + + function rollbackFileTransaction() + { + $n = count($this->file_operations); + $this->log(2, "rolling back $n file operations"); + foreach ($this->file_operations as $tr) { + list($type, $data) = $tr; + switch ($type) { + case 'rename': + @unlink($data[0]); + $this->log(3, "+ rm $data[0]"); + break; + case 'mkdir': + @rmdir($data[0]); + $this->log(3, "+ rmdir $data[0]"); + break; + case 'chmod': + break; + case 'delete': + break; + case 'installed_as': + if (isset($this->pkginfo['filelist'])) { + unset($this->pkginfo['filelist'][$data[0]]['installed_as']); + } + if (isset($this->pkginfo['filelist']['dirtree'][dirname($data[1])])) { + unset($this->pkginfo['filelist']['dirtree'][dirname($data[1])]); + while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\' + && $data[3] != '.') { + unset($this->pkginfo['filelist']['dirtree'] + [$this->_prependPath($data[3], $data[2])]); + $data[3] = dirname($data[3]); + } + } + if (isset($this->pkginfo['filelist']['dirtree']) + && !count($this->pkginfo['filelist']['dirtree'])) { + unset($this->pkginfo['filelist']['dirtree']); + } + break; + } + } + $this->file_operations = array(); + } + + // }}} + // {{{ mkDirHier($dir) + + function mkDirHier($dir) + { + $this->addFileOperation('mkdir', array($dir)); + return parent::mkDirHier($dir); + } + + // }}} + // {{{ download() + + /** + * Download any files and their dependencies, if necessary + * + * @param array a mixed list of package names, local files, or package.xml + * @param PEAR_Config + * @param array options from the command line + * @param array this is the array that will be populated with packages to + * install. Format of each entry: + * + * + * array('pkg' => 'package_name', 'file' => '/path/to/local/file', + * 'info' => array() // parsed package.xml + * ); + * + * @param array this will be populated with any error messages + * @param false private recursion variable + * @param false private recursion variable + * @param false private recursion variable + * @deprecated in favor of PEAR_Downloader + */ + function download($packages, $options, &$config, &$installpackages, + &$errors, $installed = false, $willinstall = false, $state = false) + { + // trickiness: initialize here + parent::PEAR_Downloader($this->ui, $options, $config); + $ret = parent::download($packages); + $errors = $this->getErrorMsgs(); + $installpackages = $this->getDownloadedPackages(); + trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " . + "in favor of PEAR_Downloader class", E_USER_WARNING); + return $ret; + } + + // }}} + // {{{ install() + + /** + * Installs the files within the package file specified. + * + * @param string $pkgfile path to the package file + * @param array $options + * recognized options: + * - installroot : optional prefix directory for installation + * - force : force installation + * - register-only : update registry but don't install files + * - upgrade : upgrade existing install + * - soft : fail silently + * - nodeps : ignore dependency conflicts/missing dependencies + * - alldeps : install all dependencies + * - onlyreqdeps : install only required dependencies + * + * @return array|PEAR_Error package info if successful + */ + + function install($pkgfile, $options = array()) + { + $php_dir = $this->config->get('php_dir'); + if (isset($options['installroot'])) { + if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) { + $options['installroot'] = substr($options['installroot'], 0, -1); + } + $php_dir = $this->_prependPath($php_dir, $options['installroot']); + $this->installroot = $options['installroot']; + } else { + $this->installroot = ''; + } + $this->registry = &new PEAR_Registry($php_dir); + // ==> XXX should be removed later on + $flag_old_format = false; + + if (substr($pkgfile, -4) == '.xml') { + $descfile = $pkgfile; + } else { + // {{{ Decompress pack in tmp dir ------------------------------------- + + // To allow relative package file names + $pkgfile = realpath($pkgfile); + + if (PEAR::isError($tmpdir = System::mktemp('-d'))) { + return $tmpdir; + } + $this->log(3, '+ tmp dir created at ' . $tmpdir); + + $tar = new Archive_Tar($pkgfile); + if (!@$tar->extract($tmpdir)) { + return $this->raiseError("unable to unpack $pkgfile"); + } + + // {{{ Look for existing package file + $descfile = $tmpdir . DIRECTORY_SEPARATOR . 'package.xml'; + + if (!is_file($descfile)) { + // ----- Look for old package archive format + // In this format the package.xml file was inside the + // Package-n.n directory + $dp = opendir($tmpdir); + do { + $pkgdir = readdir($dp); + } while ($pkgdir{0} == '.'); + + $descfile = $tmpdir . DIRECTORY_SEPARATOR . $pkgdir . DIRECTORY_SEPARATOR . 'package.xml'; + $flag_old_format = true; + $this->log(0, "warning : you are using an archive with an old format"); + } + // }}} + // <== XXX This part should be removed later on + // }}} + } + + if (!is_file($descfile)) { + return $this->raiseError("no package.xml file after extracting the archive"); + } + + // Parse xml file ----------------------------------------------- + $pkginfo = $this->infoFromDescriptionFile($descfile); + if (PEAR::isError($pkginfo)) { + return $pkginfo; + } + $this->validatePackageInfo($pkginfo, $errors, $warnings); + // XXX We allow warnings, do we have to do it? + if (count($errors)) { + if (empty($options['force'])) { + return $this->raiseError("The following errors where found (use force option to install anyway):\n". + implode("\n", $errors)); + } else { + $this->log(0, "warning : the following errors were found:\n". + implode("\n", $errors)); + } + } + + $pkgname = $pkginfo['package']; + + // {{{ Check dependencies ------------------------------------------- + if (isset($pkginfo['release_deps']) && empty($options['nodeps'])) { + $dep_errors = ''; + $error = $this->checkDeps($pkginfo, $dep_errors); + if ($error == true) { + if (empty($options['soft'])) { + $this->log(0, substr($dep_errors, 1)); + } + return $this->raiseError("$pkgname: Dependencies failed"); + } else if (!empty($dep_errors)) { + // Print optional dependencies + if (empty($options['soft'])) { + $this->log(0, $dep_errors); + } + } + } + // }}} + + // {{{ checks to do when not in "force" mode + if (empty($options['force'])) { + $test = $this->registry->checkFileMap($pkginfo); + if (sizeof($test)) { + $tmp = $test; + foreach ($tmp as $file => $pkg) { + if ($pkg == $pkgname) { + unset($test[$file]); + } + } + if (sizeof($test)) { + $msg = "$pkgname: conflicting files found:\n"; + $longest = max(array_map("strlen", array_keys($test))); + $fmt = "%${longest}s (%s)\n"; + foreach ($test as $file => $pkg) { + $msg .= sprintf($fmt, $file, $pkg); + } + return $this->raiseError($msg); + } + } + } + // }}} + + $this->startFileTransaction(); + + if (empty($options['upgrade'])) { + // checks to do only when installing new packages + if (empty($options['force']) && $this->registry->packageExists($pkgname)) { + return $this->raiseError("$pkgname already installed"); + } + } else { + if ($this->registry->packageExists($pkgname)) { + $v1 = $this->registry->packageInfo($pkgname, 'version'); + $v2 = $pkginfo['version']; + $cmp = version_compare("$v1", "$v2", 'gt'); + if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) { + return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)"); + } + if (empty($options['register-only'])) { + // when upgrading, remove old release's files first: + if (PEAR::isError($err = $this->_deletePackageFiles($pkgname))) { + return $this->raiseError($err); + } + } + } + } + + // {{{ Copy files to dest dir --------------------------------------- + + // info from the package it self we want to access from _installFile + $this->pkginfo = &$pkginfo; + // used to determine whether we should build any C code + $this->source_files = 0; + + if (empty($options['register-only'])) { + if (!is_dir($php_dir)) { + return $this->raiseError("no script destination directory\n", + null, PEAR_ERROR_DIE); + } + + $tmp_path = dirname($descfile); + if (substr($pkgfile, -4) != '.xml') { + $tmp_path .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkginfo['version']; + } + + // ==> XXX This part should be removed later on + if ($flag_old_format) { + $tmp_path = dirname($descfile); + } + // <== XXX This part should be removed later on + + // {{{ install files + foreach ($pkginfo['filelist'] as $file => $atts) { + $this->expectError(PEAR_INSTALLER_FAILED); + $res = $this->_installFile($file, $atts, $tmp_path, $options); + $this->popExpect(); + if (PEAR::isError($res)) { + if (empty($options['ignore-errors'])) { + $this->rollbackFileTransaction(); + if ($res->getMessage() == "file does not exist") { + $this->raiseError("file $file in package.xml does not exist"); + } + return $this->raiseError($res); + } else { + $this->log(0, "Warning: " . $res->getMessage()); + } + } + if ($res != PEAR_INSTALLER_OK) { + // Do not register files that were not installed + unset($pkginfo['filelist'][$file]); + } + } + // }}} + + // {{{ compile and install source files + if ($this->source_files > 0 && empty($options['nobuild'])) { + $this->log(1, "$this->source_files source files, building"); + $bob = &new PEAR_Builder($this->ui); + $bob->debug = $this->debug; + $built = $bob->build($descfile, array(&$this, '_buildCallback')); + if (PEAR::isError($built)) { + $this->rollbackFileTransaction(); + return $built; + } + $this->log(1, "\nBuild process completed successfully"); + foreach ($built as $ext) { + $bn = basename($ext['file']); + list($_ext_name, $_ext_suff) = explode('.', $bn); + if ($_ext_suff == '.so' || $_ext_suff == '.dll') { + if (extension_loaded($_ext_name)) { + $this->raiseError("Extension '$_ext_name' already loaded. " . + 'Please unload it in your php.ini file ' . + 'prior to install or upgrade'); + } + $role = 'ext'; + } else { + $role = 'src'; + } + $dest = $ext['dest']; + $this->log(1, "Installing '$ext[file]'"); + $copyto = $this->_prependPath($dest, $this->installroot); + $copydir = dirname($copyto); + if (!@is_dir($copydir)) { + if (!$this->mkDirHier($copydir)) { + return $this->raiseError("failed to mkdir $copydir", + PEAR_INSTALLER_FAILED); + } + $this->log(3, "+ mkdir $copydir"); + } + if (!@copy($ext['file'], $copyto)) { + return $this->raiseError("failed to write $copyto", PEAR_INSTALLER_FAILED); + } + $this->log(3, "+ cp $ext[file] $copyto"); + if (!OS_WINDOWS) { + $mode = 0666 & ~(int)octdec($this->config->get('umask')); + $this->addFileOperation('chmod', array($mode, $copyto)); + if (!@chmod($copyto, $mode)) { + $this->log(0, "failed to change mode of $copyto"); + } + } + $this->addFileOperation('rename', array($ext['file'], $copyto)); + + $pkginfo['filelist'][$bn] = array( + 'role' => $role, + 'installed_as' => $dest, + 'php_api' => $ext['php_api'], + 'zend_mod_api' => $ext['zend_mod_api'], + 'zend_ext_api' => $ext['zend_ext_api'], + ); + } + } + // }}} + } + + if (!$this->commitFileTransaction()) { + $this->rollbackFileTransaction(); + return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED); + } + // }}} + + $ret = false; + // {{{ Register that the package is installed ----------------------- + if (empty($options['upgrade'])) { + // if 'force' is used, replace the info in registry + if (!empty($options['force']) && $this->registry->packageExists($pkgname)) { + $this->registry->deletePackage($pkgname); + } + $ret = $this->registry->addPackage($pkgname, $pkginfo); + } else { + // new: upgrade installs a package if it isn't installed + if (!$this->registry->packageExists($pkgname)) { + $ret = $this->registry->addPackage($pkgname, $pkginfo); + } else { + $ret = $this->registry->updatePackage($pkgname, $pkginfo, false); + } + } + if (!$ret) { + return $this->raiseError("Adding package $pkgname to registry failed"); + } + // }}} + return $pkginfo; + } + + // }}} + // {{{ uninstall() + + /** + * Uninstall a package + * + * This method removes all files installed by the application, and then + * removes any empty directories. + * @param string package name + * @param array Command-line options. Possibilities include: + * + * - installroot: base installation dir, if not the default + * - nodeps: do not process dependencies of other packages to ensure + * uninstallation does not break things + */ + function uninstall($package, $options = array()) + { + $php_dir = $this->config->get('php_dir'); + if (isset($options['installroot'])) { + if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) { + $options['installroot'] = substr($options['installroot'], 0, -1); + } + $this->installroot = $options['installroot']; + $php_dir = $this->_prependPath($php_dir, $this->installroot); + } else { + $this->installroot = ''; + } + $this->registry = &new PEAR_Registry($php_dir); + $filelist = $this->registry->packageInfo($package, 'filelist'); + if ($filelist == null) { + return $this->raiseError("$package not installed"); + } + if (empty($options['nodeps'])) { + $depchecker = &new PEAR_Dependency($this->registry); + $error = $depchecker->checkPackageUninstall($errors, $warning, $package); + if ($error) { + return $this->raiseError($errors . 'uninstall failed'); + } + if ($warning) { + $this->log(0, $warning); + } + } + // {{{ Delete the files + $this->startFileTransaction(); + if (PEAR::isError($err = $this->_deletePackageFiles($package))) { + $this->rollbackFileTransaction(); + return $this->raiseError($err); + } + if (!$this->commitFileTransaction()) { + $this->rollbackFileTransaction(); + return $this->raiseError("uninstall failed"); + } else { + $this->startFileTransaction(); + if (!isset($filelist['dirtree']) || !count($filelist['dirtree'])) { + return $this->registry->deletePackage($package); + } + // attempt to delete empty directories + uksort($filelist['dirtree'], array($this, '_sortDirs')); + foreach($filelist['dirtree'] as $dir => $notused) { + $this->addFileOperation('rmdir', array($dir)); + } + if (!$this->commitFileTransaction()) { + $this->rollbackFileTransaction(); + } + } + // }}} + + // Register that the package is no longer installed + return $this->registry->deletePackage($package); + } + + // }}} + // {{{ _sortDirs() + function _sortDirs($a, $b) + { + if (strnatcmp($a, $b) == -1) return 1; + if (strnatcmp($a, $b) == 1) return -1; + return 0; + } + + // }}} + // {{{ checkDeps() + + /** + * Check if the package meets all dependencies + * + * @param array Package information (passed by reference) + * @param string Error message (passed by reference) + * @return boolean False when no error occured, otherwise true + */ + function checkDeps(&$pkginfo, &$errors) + { + if (empty($this->registry)) { + $this->registry = &new PEAR_Registry($this->config->get('php_dir')); + } + $depchecker = &new PEAR_Dependency($this->registry); + $error = $errors = ''; + $failed_deps = $optional_deps = array(); + if (is_array($pkginfo['release_deps'])) { + foreach($pkginfo['release_deps'] as $dep) { + $code = $depchecker->callCheckMethod($error, $dep); + if ($code) { + if (isset($dep['optional']) && $dep['optional'] == 'yes') { + $optional_deps[] = array($dep, $code, $error); + } else { + $failed_deps[] = array($dep, $code, $error); + } + } + } + // {{{ failed dependencies + $n = count($failed_deps); + if ($n > 0) { + for ($i = 0; $i < $n; $i++) { + if (isset($failed_deps[$i]['type'])) { + $type = $failed_deps[$i]['type']; + } else { + $type = 'pkg'; + } + switch ($failed_deps[$i][1]) { + case PEAR_DEPENDENCY_MISSING: + if ($type == 'pkg') { + // install + } + $errors .= "\n" . $failed_deps[$i][2]; + break; + case PEAR_DEPENDENCY_UPGRADE_MINOR: + if ($type == 'pkg') { + // upgrade + } + $errors .= "\n" . $failed_deps[$i][2]; + break; + default: + $errors .= "\n" . $failed_deps[$i][2]; + break; + } + } + return true; + } + // }}} + + // {{{ optional dependencies + $count_optional = count($optional_deps); + if ($count_optional > 0) { + $errors = "Optional dependencies:"; + + for ($i = 0; $i < $count_optional; $i++) { + if (isset($optional_deps[$i]['type'])) { + $type = $optional_deps[$i]['type']; + } else { + $type = 'pkg'; + } + switch ($optional_deps[$i][1]) { + case PEAR_DEPENDENCY_MISSING: + case PEAR_DEPENDENCY_UPGRADE_MINOR: + default: + $errors .= "\n" . $optional_deps[$i][2]; + break; + } + } + return false; + } + // }}} + } + return false; + } + + // }}} + // {{{ _buildCallback() + + function _buildCallback($what, $data) + { + if (($what == 'cmdoutput' && $this->debug > 1) || + ($what == 'output' && $this->debug > 0)) { + $this->ui->outputData(rtrim($data), 'build'); + } + } + + // }}} +} + +// {{{ md5_file() utility function +if (!function_exists("md5_file")) { + function md5_file($filename) { + $fp = fopen($filename, "r"); + if (!$fp) return null; + $contents = fread($fp, filesize($filename)); + fclose($fp); + return md5($contents); + } +} +// }}} + +?> diff --git a/src/www/lib/pear/PEAR/Packager.php b/src/www/lib/pear/PEAR/Packager.php new file mode 100644 index 00000000..5fb2c89f --- /dev/null +++ b/src/www/lib/pear/PEAR/Packager.php @@ -0,0 +1,165 @@ + | +// | Tomas V.V.Cox | +// +----------------------------------------------------------------------+ +// +// $Id: Packager.php,v 1.1 2005/05/28 01:55:13 henrique Exp $ + +require_once 'PEAR/Common.php'; +require_once 'System.php'; + +/** + * Administration class used to make a PEAR release tarball. + * + * TODO: + * - add an extra param the dir where to place the created package + * + * @since PHP 4.0.2 + * @author Stig Bakken + */ +class PEAR_Packager extends PEAR_Common +{ + // {{{ constructor + + function PEAR_Packager() + { + parent::PEAR_Common(); + } + + // }}} + // {{{ destructor + + function _PEAR_Packager() + { + parent::_PEAR_Common(); + } + + // }}} + + // {{{ package() + + function package($pkgfile = null, $compress = true) + { + // {{{ validate supplied package.xml file + if (empty($pkgfile)) { + $pkgfile = 'package.xml'; + } + // $this->pkginfo gets populated inside + $pkginfo = $this->infoFromDescriptionFile($pkgfile); + if (PEAR::isError($pkginfo)) { + return $this->raiseError($pkginfo); + } + + $pkgdir = dirname(realpath($pkgfile)); + $pkgfile = basename($pkgfile); + + $errors = $warnings = array(); + $this->validatePackageInfo($pkginfo, $errors, $warnings, $pkgdir); + foreach ($warnings as $w) { + $this->log(1, "Warning: $w"); + } + foreach ($errors as $e) { + $this->log(0, "Error: $e"); + } + if (sizeof($errors) > 0) { + return $this->raiseError('Errors in package'); + } + // }}} + + $pkgver = $pkginfo['package'] . '-' . $pkginfo['version']; + + // {{{ Create the package file list + $filelist = array(); + $i = 0; + + foreach ($pkginfo['filelist'] as $fname => $atts) { + $file = $pkgdir . DIRECTORY_SEPARATOR . $fname; + if (!file_exists($file)) { + return $this->raiseError("File does not exist: $fname"); + } else { + $filelist[$i++] = $file; + if (empty($pkginfo['filelist'][$fname]['md5sum'])) { + $md5sum = md5_file($file); + $pkginfo['filelist'][$fname]['md5sum'] = $md5sum; + } + $this->log(2, "Adding file $fname"); + } + } + // }}} + + // {{{ regenerate package.xml + $new_xml = $this->xmlFromInfo($pkginfo); + if (PEAR::isError($new_xml)) { + return $this->raiseError($new_xml); + } + if (!($tmpdir = System::mktemp(array('-d')))) { + return $this->raiseError("PEAR_Packager: mktemp failed"); + } + $newpkgfile = $tmpdir . DIRECTORY_SEPARATOR . 'package.xml'; + $np = @fopen($newpkgfile, 'wb'); + if (!$np) { + return $this->raiseError("PEAR_Packager: unable to rewrite $pkgfile as $newpkgfile"); + } + fwrite($np, $new_xml); + fclose($np); + // }}} + + // {{{ TAR the Package ------------------------------------------- + $ext = $compress ? '.tgz' : '.tar'; + $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext; + $tar =& new Archive_Tar($dest_package, $compress); + $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors + // ----- Creates with the package.xml file + $ok = $tar->createModify(array($newpkgfile), '', $tmpdir); + if (PEAR::isError($ok)) { + return $this->raiseError($ok); + } elseif (!$ok) { + return $this->raiseError('PEAR_Packager: tarball creation failed'); + } + // ----- Add the content of the package + if (!$tar->addModify($filelist, $pkgver, $pkgdir)) { + return $this->raiseError('PEAR_Packager: tarball creation failed'); + } + $this->log(1, "Package $dest_package done"); + if (file_exists("$pkgdir/CVS/Root")) { + $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pkginfo['version']); + $cvstag = "RELEASE_$cvsversion"; + $this->log(1, "Tag the released code with `pear cvstag $pkgfile'"); + $this->log(1, "(or set the CVS tag $cvstag by hand)"); + } + // }}} + + return $dest_package; + } + + // }}} +} + +// {{{ md5_file() utility function +if (!function_exists('md5_file')) { + function md5_file($file) { + if (!$fd = @fopen($file, 'r')) { + return false; + } + $md5 = md5(fread($fd, filesize($file))); + fclose($fd); + return $md5; + } +} +// }}} + +?> diff --git a/src/www/lib/pear/PEAR/Registry.php b/src/www/lib/pear/PEAR/Registry.php new file mode 100644 index 00000000..ba24e3fd --- /dev/null +++ b/src/www/lib/pear/PEAR/Registry.php @@ -0,0 +1,538 @@ + | +// | Tomas V.V.Cox | +// | | +// +----------------------------------------------------------------------+ +// +// $Id: Registry.php,v 1.1 2005/05/28 01:55:13 henrique Exp $ + +/* +TODO: + - Transform into singleton() + - Add application level lock (avoid change the registry from the cmdline + while using the GTK interface, for ex.) +*/ +require_once "System.php"; +require_once "PEAR.php"; + +define('PEAR_REGISTRY_ERROR_LOCK', -2); +define('PEAR_REGISTRY_ERROR_FORMAT', -3); +define('PEAR_REGISTRY_ERROR_FILE', -4); + +/** + * Administration class used to maintain the installed package database. + */ +class PEAR_Registry extends PEAR +{ + // {{{ properties + + /** Directory where registry files are stored. + * @var string + */ + var $statedir = ''; + + /** File where the file map is stored + * @var string + */ + var $filemap = ''; + + /** Name of file used for locking the registry + * @var string + */ + var $lockfile = ''; + + /** File descriptor used during locking + * @var resource + */ + var $lock_fp = null; + + /** Mode used during locking + * @var int + */ + var $lock_mode = 0; // XXX UNUSED + + /** Cache of package information. Structure: + * array( + * 'package' => array('id' => ... ), + * ... ) + * @var array + */ + var $pkginfo_cache = array(); + + /** Cache of file map. Structure: + * array( '/path/to/file' => 'package', ... ) + * @var array + */ + var $filemap_cache = array(); + + // }}} + + // {{{ constructor + + /** + * PEAR_Registry constructor. + * + * @param string (optional) PEAR install directory (for .php files) + * + * @access public + */ + function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR) + { + parent::PEAR(); + $ds = DIRECTORY_SEPARATOR; + $this->install_dir = $pear_install_dir; + $this->statedir = $pear_install_dir.$ds.'.registry'; + $this->filemap = $pear_install_dir.$ds.'.filemap'; + $this->lockfile = $pear_install_dir.$ds.'.lock'; + + // XXX Compatibility code should be removed in the future + // rename all registry files if any to lowercase + if (!OS_WINDOWS && $handle = @opendir($this->statedir)) { + $dest = $this->statedir . DIRECTORY_SEPARATOR; + while (false !== ($file = readdir($handle))) { + if (preg_match('/^.*[A-Z].*\.reg$/', $file)) { + rename($dest . $file, $dest . strtolower($file)); + } + } + closedir($handle); + } + if (!file_exists($this->filemap)) { + $this->rebuildFileMap(); + } + } + + // }}} + // {{{ destructor + + /** + * PEAR_Registry destructor. Makes sure no locks are forgotten. + * + * @access private + */ + function _PEAR_Registry() + { + parent::_PEAR(); + if (is_resource($this->lock_fp)) { + $this->_unlock(); + } + } + + // }}} + + // {{{ _assertStateDir() + + /** + * Make sure the directory where we keep registry files exists. + * + * @return bool TRUE if directory exists, FALSE if it could not be + * created + * + * @access private + */ + function _assertStateDir() + { + if (!@is_dir($this->statedir)) { + if (!System::mkdir(array('-p', $this->statedir))) { + return $this->raiseError("could not create directory '{$this->statedir}'"); + } + } + return true; + } + + // }}} + // {{{ _packageFileName() + + /** + * Get the name of the file where data for a given package is stored. + * + * @param string package name + * + * @return string registry file name + * + * @access public + */ + function _packageFileName($package) + { + return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg'; + } + + // }}} + // {{{ _openPackageFile() + + function _openPackageFile($package, $mode) + { + $this->_assertStateDir(); + $file = $this->_packageFileName($package); + $fp = @fopen($file, $mode); + if (!$fp) { + return null; + } + return $fp; + } + + // }}} + // {{{ _closePackageFile() + + function _closePackageFile($fp) + { + fclose($fp); + } + + // }}} + // {{{ rebuildFileMap() + + function rebuildFileMap() + { + $packages = $this->listPackages(); + $files = array(); + foreach ($packages as $package) { + $version = $this->packageInfo($package, 'version'); + $filelist = $this->packageInfo($package, 'filelist'); + if (!is_array($filelist)) { + continue; + } + foreach ($filelist as $name => $attrs) { + if (isset($attrs['role']) && $attrs['role'] != 'php') { + continue; + } + if (isset($attrs['baseinstalldir'])) { + $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name; + } else { + $file = $name; + } + $file = preg_replace(',^/+,', '', $file); + $files[$file] = $package; + } + } + $this->_assertStateDir(); + $fp = @fopen($this->filemap, 'wb'); + if (!$fp) { + return false; + } + $this->filemap_cache = $files; + fwrite($fp, serialize($files)); + fclose($fp); + return true; + } + + // }}} + // {{{ readFileMap() + + function readFileMap() + { + $fp = @fopen($this->filemap, 'r'); + if (!$fp) { + return $this->raiseError('PEAR_Registry: could not open filemap', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg); + } + $fsize = filesize($this->filemap); + $rt = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + $data = fread($fp, $fsize); + set_magic_quotes_runtime($rt); + fclose($fp); + $tmp = unserialize($data); + if (!$tmp && $fsize > 7) { + return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data); + } + $this->filemap_cache = $tmp; + return true; + } + + // }}} + // {{{ _lock() + + /** + * Lock the registry. + * + * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN. + * See flock manual for more information. + * + * @return bool TRUE on success, FALSE if locking failed, or a + * PEAR error if some other error occurs (such as the + * lock file not being writable). + * + * @access private + */ + function _lock($mode = LOCK_EX) + { + if (!eregi('Windows 9', php_uname())) { + if ($mode != LOCK_UN && is_resource($this->lock_fp)) { + // XXX does not check type of lock (LOCK_SH/LOCK_EX) + return true; + } + if (PEAR::isError($err = $this->_assertStateDir())) { + return $err; + } + $open_mode = 'w'; + // XXX People reported problems with LOCK_SH and 'w' + if ($mode === LOCK_SH || $mode === LOCK_UN) { + if (@!is_file($this->lockfile)) { + touch($this->lockfile); + } + $open_mode = 'r'; + } + + if (!is_resource($this->lock_fp)) { + $this->lock_fp = @fopen($this->lockfile, $open_mode); + } + + if (!is_resource($this->lock_fp)) { + return $this->raiseError("could not create lock file" . + (isset($php_errormsg) ? ": " . $php_errormsg : "")); + } + if (!(int)flock($this->lock_fp, $mode)) { + switch ($mode) { + case LOCK_SH: $str = 'shared'; break; + case LOCK_EX: $str = 'exclusive'; break; + case LOCK_UN: $str = 'unlock'; break; + default: $str = 'unknown'; break; + } + return $this->raiseError("could not acquire $str lock ($this->lockfile)", + PEAR_REGISTRY_ERROR_LOCK); + } + } + return true; + } + + // }}} + // {{{ _unlock() + + function _unlock() + { + $ret = $this->_lock(LOCK_UN); + if (is_resource($this->lock_fp)) { + fclose($this->lock_fp); + } + $this->lock_fp = null; + return $ret; + } + + // }}} + // {{{ _packageExists() + + function _packageExists($package) + { + return file_exists($this->_packageFileName($package)); + } + + // }}} + // {{{ _packageInfo() + + function _packageInfo($package = null, $key = null) + { + if ($package === null) { + return array_map(array($this, '_packageInfo'), + $this->_listPackages()); + } + $fp = $this->_openPackageFile($package, 'r'); + if ($fp === null) { + return null; + } + $rt = get_magic_quotes_runtime(); + set_magic_quotes_runtime(0); + $data = fread($fp, filesize($this->_packageFileName($package))); + set_magic_quotes_runtime($rt); + $this->_closePackageFile($fp); + $data = unserialize($data); + if ($key === null) { + return $data; + } + if (isset($data[$key])) { + return $data[$key]; + } + return null; + } + + // }}} + // {{{ _listPackages() + + function _listPackages() + { + $pkglist = array(); + $dp = @opendir($this->statedir); + if (!$dp) { + return $pkglist; + } + while ($ent = readdir($dp)) { + if ($ent{0} == '.' || substr($ent, -4) != '.reg') { + continue; + } + $pkglist[] = substr($ent, 0, -4); + } + return $pkglist; + } + + // }}} + + // {{{ packageExists() + + function packageExists($package) + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $ret = $this->_packageExists($package); + $this->_unlock(); + return $ret; + } + + // }}} + // {{{ packageInfo() + + function packageInfo($package = null, $key = null) + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $ret = $this->_packageInfo($package, $key); + $this->_unlock(); + return $ret; + } + + // }}} + // {{{ listPackages() + + function listPackages() + { + if (PEAR::isError($e = $this->_lock(LOCK_SH))) { + return $e; + } + $ret = $this->_listPackages(); + $this->_unlock(); + return $ret; + } + + // }}} + // {{{ addPackage() + + function addPackage($package, $info) + { + if ($this->packageExists($package)) { + return false; + } + if (PEAR::isError($e = $this->_lock(LOCK_EX))) { + return $e; + } + $fp = $this->_openPackageFile($package, 'wb'); + if ($fp === null) { + $this->_unlock(); + return false; + } + $info['_lastmodified'] = time(); + fwrite($fp, serialize($info)); + $this->_closePackageFile($fp); + $this->_unlock(); + return true; + } + + // }}} + // {{{ deletePackage() + + function deletePackage($package) + { + if (PEAR::isError($e = $this->_lock(LOCK_EX))) { + return $e; + } + $file = $this->_packageFileName($package); + $ret = @unlink($file); + $this->rebuildFileMap(); + $this->_unlock(); + return $ret; + } + + // }}} + // {{{ updatePackage() + + function updatePackage($package, $info, $merge = true) + { + $oldinfo = $this->packageInfo($package); + if (empty($oldinfo)) { + return false; + } + if (PEAR::isError($e = $this->_lock(LOCK_EX))) { + return $e; + } + $fp = $this->_openPackageFile($package, 'w'); + if ($fp === null) { + $this->_unlock(); + return false; + } + $info['_lastmodified'] = time(); + if ($merge) { + fwrite($fp, serialize(array_merge($oldinfo, $info))); + } else { + fwrite($fp, serialize($info)); + } + $this->_closePackageFile($fp); + if (isset($info['filelist'])) { + $this->rebuildFileMap(); + } + $this->_unlock(); + return true; + } + + // }}} + // {{{ checkFileMap() + + /** + * Test whether a file belongs to a package. + * + * @param string $path file path, absolute or relative to the pear + * install dir + * + * @return string which package the file belongs to, or an empty + * string if the file does not belong to an installed package + * + * @access public + */ + function checkFileMap($path) + { + if (is_array($path)) { + static $notempty; + if (empty($notempty)) { + $notempty = create_function('$a','return !empty($a);'); + } + $pkgs = array(); + foreach ($path as $name => $attrs) { + if (is_array($attrs) && isset($attrs['baseinstalldir'])) { + $name = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name; + } + $pkgs[$name] = $this->checkFileMap($name); + } + return array_filter($pkgs, $notempty); + } + if (empty($this->filemap_cache) && PEAR::isError($this->readFileMap())) { + return $err; + } + if (isset($this->filemap_cache[$path])) { + return $this->filemap_cache[$path]; + } + $l = strlen($this->install_dir); + if (substr($path, 0, $l) == $this->install_dir) { + $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l)); + } + if (isset($this->filemap_cache[$path])) { + return $this->filemap_cache[$path]; + } + return ''; + } + + // }}} + +} + +?> diff --git a/src/www/lib/pear/PEAR/Remote.php b/src/www/lib/pear/PEAR/Remote.php new file mode 100644 index 00000000..c15b42b1 --- /dev/null +++ b/src/www/lib/pear/PEAR/Remote.php @@ -0,0 +1,394 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Remote.php,v 1.1 2005/05/28 01:55:14 henrique Exp $ + +require_once 'PEAR.php'; +require_once 'PEAR/Config.php'; + +/** + * This is a class for doing remote operations against the central + * PEAR database. + * + * @nodep XML_RPC_Value + * @nodep XML_RPC_Message + * @nodep XML_RPC_Client + */ +class PEAR_Remote extends PEAR +{ + // {{{ properties + + var $config = null; + var $cache = null; + + // }}} + + // {{{ PEAR_Remote(config_object) + + function PEAR_Remote(&$config) + { + $this->PEAR(); + $this->config = &$config; + } + + // }}} + + // {{{ getCache() + + + function getCache($args) + { + $id = md5(serialize($args)); + $cachedir = $this->config->get('cache_dir'); + $filename = $cachedir . DIRECTORY_SEPARATOR . 'xmlrpc_cache_' . $id; + if (!file_exists($filename)) { + return null; + }; + + $fp = fopen($filename, 'rb'); + if (!$fp) { + return null; + } + $content = fread($fp, filesize($filename)); + fclose($fp); + $result = array( + 'age' => time() - filemtime($filename), + 'lastChange' => filemtime($filename), + 'content' => unserialize($content), + ); + return $result; + } + + // }}} + + // {{{ saveCache() + + function saveCache($args, $data) + { + $id = md5(serialize($args)); + $cachedir = $this->config->get('cache_dir'); + if (!file_exists($cachedir)) { + System::mkdir(array('-p', $cachedir)); + } + $filename = $cachedir.'/xmlrpc_cache_'.$id; + + $fp = @fopen($filename, "wb"); + if ($fp) { + fwrite($fp, serialize($data)); + fclose($fp); + }; + } + + // }}} + + // {{{ call(method, [args...]) + + function call($method) + { + $_args = $args = func_get_args(); + + $this->cache = $this->getCache($args); + $cachettl = $this->config->get('cache_ttl'); + // If cache is newer than $cachettl seconds, we use the cache! + if ($this->cache !== null && $this->cache['age'] < $cachettl) { + return $this->cache['content']; + }; + + if (extension_loaded("xmlrpc")) { + $result = call_user_func_array(array(&$this, 'call_epi'), $args); + if (!PEAR::isError($result)) { + $this->saveCache($_args, $result); + }; + return $result; + } + if (!@include_once("XML/RPC.php")) { + return $this->raiseError("For this remote PEAR operation you need to install the XML_RPC package"); + } + array_shift($args); + $server_host = $this->config->get('master_server'); + $username = $this->config->get('username'); + $password = $this->config->get('password'); + $eargs = array(); + foreach($args as $arg) $eargs[] = $this->_encode($arg); + $f = new XML_RPC_Message($method, $eargs); + if ($this->cache !== null) { + $maxAge = '?maxAge='.$this->cache['lastChange']; + } else { + $maxAge = ''; + }; + $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; + if ($proxy = parse_url($this->config->get('http_proxy'))) { + $proxy_host = @$proxy['host']; + $proxy_port = @$proxy['port']; + $proxy_user = @urldecode(@$proxy['user']); + $proxy_pass = @urldecode(@$proxy['pass']); + } + $c = new XML_RPC_Client('/xmlrpc.php'.$maxAge, $server_host, 80, $proxy_host, $proxy_port, $proxy_user, $proxy_pass); + if ($username && $password) { + $c->setCredentials($username, $password); + } + if ($this->config->get('verbose') >= 3) { + $c->setDebug(1); + } + $r = $c->send($f); + if (!$r) { + return $this->raiseError("XML_RPC send failed"); + } + $v = $r->value(); + if ($e = $r->faultCode()) { + if ($e == $GLOBALS['XML_RPC_err']['http_error'] && strstr($r->faultString(), '304 Not Modified') !== false) { + return $this->cache['content']; + } + return $this->raiseError($r->faultString(), $e); + } + + $result = XML_RPC_decode($v); + $this->saveCache($_args, $result); + return $result; + } + + // }}} + + // {{{ call_epi(method, [args...]) + + function call_epi($method) + { + do { + if (extension_loaded("xmlrpc")) { + break; + } + if (OS_WINDOWS) { + $ext = 'dll'; + } elseif (PHP_OS == 'HP-UX') { + $ext = 'sl'; + } elseif (PHP_OS == 'AIX') { + $ext = 'a'; + } else { + $ext = 'so'; + } + $ext = OS_WINDOWS ? 'dll' : 'so'; + @dl("xmlrpc-epi.$ext"); + if (extension_loaded("xmlrpc")) { + break; + } + @dl("xmlrpc.$ext"); + if (extension_loaded("xmlrpc")) { + break; + } + return $this->raiseError("unable to load xmlrpc extension"); + } while (false); + $params = func_get_args(); + array_shift($params); + $method = str_replace("_", ".", $method); + $request = xmlrpc_encode_request($method, $params); + $server_host = $this->config->get("master_server"); + if (empty($server_host)) { + return $this->raiseError("PEAR_Remote::call: no master_server configured"); + } + $server_port = 80; + if ($http_proxy = $this->config->get('http_proxy')) { + $proxy = parse_url($http_proxy); + $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; + $proxy_host = @$proxy['host']; + $proxy_port = @$proxy['port']; + $proxy_user = @urldecode(@$proxy['user']); + $proxy_pass = @urldecode(@$proxy['pass']); + $fp = @fsockopen($proxy_host, $proxy_port); + $use_proxy = true; + } else { + $use_proxy = false; + $fp = @fsockopen($server_host, $server_port); + } + if (!$fp && $http_proxy) { + return $this->raiseError("PEAR_Remote::call: fsockopen(`$proxy_host', $proxy_port) failed"); + } elseif (!$fp) { + return $this->raiseError("PEAR_Remote::call: fsockopen(`$server_host', $server_port) failed"); + } + $len = strlen($request); + $req_headers = "Host: $server_host:$server_port\r\n" . + "Content-type: text/xml\r\n" . + "Content-length: $len\r\n"; + $username = $this->config->get('username'); + $password = $this->config->get('password'); + if ($username && $password) { + $req_headers .= "Cookie: PEAR_USER=$username; PEAR_PW=$password\r\n"; + $tmp = base64_encode("$username:$password"); + $req_headers .= "Authorization: Basic $tmp\r\n"; + } + if ($this->cache !== null) { + $maxAge = '?maxAge='.$this->cache['lastChange']; + } else { + $maxAge = ''; + }; + + if ($use_proxy && $proxy_host != '' && $proxy_user != '') { + $req_headers .= 'Proxy-Authorization: Basic ' + .base64_encode($proxy_user.':'.$proxy_pass) + ."\r\n"; + } + + if ($this->config->get('verbose') > 3) { + print "XMLRPC REQUEST HEADERS:\n"; + var_dump($req_headers); + print "XMLRPC REQUEST BODY:\n"; + var_dump($request); + } + + if ($use_proxy && $proxy_host != '') { + $post_string = "POST http://".$server_host; + if ($proxy_port > '') { + $post_string .= ':'.$server_port; + } + } else { + $post_string = "POST "; + } + + fwrite($fp, ($post_string."/xmlrpc.php$maxAge HTTP/1.0\r\n$req_headers\r\n$request")); + $response = ''; + $line1 = fgets($fp, 2048); + if (!preg_match('!^HTTP/[0-9\.]+ (\d+) (.*)!', $line1, $matches)) { + return $this->raiseError("PEAR_Remote: invalid HTTP response from XML-RPC server"); + } + switch ($matches[1]) { + case "200": // OK + break; + case "304": // Not Modified + return $this->cache['content']; + case "401": // Unauthorized + if ($username && $password) { + return $this->raiseError("PEAR_Remote: authorization failed", 401); + } else { + return $this->raiseError("PEAR_Remote: authorization required, please log in first", 401); + } + default: + return $this->raiseError("PEAR_Remote: unexpected HTTP response", (int)$matches[1], null, null, "$matches[1] $matches[2]"); + } + while (trim(fgets($fp, 2048)) != ''); // skip rest of headers + while ($chunk = fread($fp, 10240)) { + $response .= $chunk; + } + fclose($fp); + if ($this->config->get('verbose') > 3) { + print "XMLRPC RESPONSE:\n"; + var_dump($response); + } + $ret = xmlrpc_decode($response); + if (is_array($ret) && isset($ret['__PEAR_TYPE__'])) { + if ($ret['__PEAR_TYPE__'] == 'error') { + if (isset($ret['__PEAR_CLASS__'])) { + $class = $ret['__PEAR_CLASS__']; + } else { + $class = "PEAR_Error"; + } + if ($ret['code'] === '') $ret['code'] = null; + if ($ret['message'] === '') $ret['message'] = null; + if ($ret['userinfo'] === '') $ret['userinfo'] = null; + if (strtolower($class) == 'db_error') { + $ret = $this->raiseError(PEAR::errorMessage($ret['code']), + $ret['code'], null, null, + $ret['userinfo']); + } else { + $ret = $this->raiseError($ret['message'], $ret['code'], + null, null, $ret['userinfo']); + } + } + } elseif (is_array($ret) && sizeof($ret) == 1 && isset($ret[0]) + && is_array($ret[0]) && + !empty($ret[0]['faultString']) && + !empty($ret[0]['faultCode'])) { + extract($ret[0]); + $faultString = "XML-RPC Server Fault: " . + str_replace("\n", " ", $faultString); + return $this->raiseError($faultString, $faultCode); + } + return $ret; + } + + // }}} + + // {{{ _encode + + // a slightly extended version of XML_RPC_encode + function _encode($php_val) + { + global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double; + global $XML_RPC_String, $XML_RPC_Array, $XML_RPC_Struct; + + $type = gettype($php_val); + $xmlrpcval = new XML_RPC_Value; + + switch($type) { + case "array": + reset($php_val); + $firstkey = key($php_val); + end($php_val); + $lastkey = key($php_val); + if ($firstkey === 0 && is_int($lastkey) && + ($lastkey + 1) == count($php_val)) { + $is_continuous = true; + reset($php_val); + $size = count($php_val); + for ($expect = 0; $expect < $size; $expect++, next($php_val)) { + if (key($php_val) !== $expect) { + $is_continuous = false; + break; + } + } + if ($is_continuous) { + reset($php_val); + $arr = array(); + while (list($k, $v) = each($php_val)) { + $arr[$k] = $this->_encode($v); + } + $xmlrpcval->addArray($arr); + break; + } + } + // fall though if not numerical and continuous + case "object": + $arr = array(); + while (list($k, $v) = each($php_val)) { + $arr[$k] = $this->_encode($v); + } + $xmlrpcval->addStruct($arr); + break; + case "integer": + $xmlrpcval->addScalar($php_val, $XML_RPC_Int); + break; + case "double": + $xmlrpcval->addScalar($php_val, $XML_RPC_Double); + break; + case "string": + case "NULL": + $xmlrpcval->addScalar($php_val, $XML_RPC_String); + break; + case "boolean": + $xmlrpcval->addScalar($php_val, $XML_RPC_Boolean); + break; + case "unknown type": + default: + return null; + } + return $xmlrpcval; + } + + // }}} + +} + +?> diff --git a/src/www/lib/pear/PEAR/RunTest.php b/src/www/lib/pear/PEAR/RunTest.php new file mode 100644 index 00000000..a0f154d1 --- /dev/null +++ b/src/www/lib/pear/PEAR/RunTest.php @@ -0,0 +1,363 @@ + | +// | Greg Beaver | +// | | +// +----------------------------------------------------------------------+ +// +// $Id: RunTest.php,v 1.1 2005/05/28 01:55:14 henrique Exp $ +// + +/** + * Simplified version of PHP's test suite + * -- EXPERIMENTAL -- + + Try it with: + + $ php -r 'include "../PEAR/RunTest.php"; $t=new PEAR_RunTest; $o=$t->run("./pear_system.phpt");print_r($o);' + + +TODO: + +Actually finish the development and testing + + */ + +require_once 'PEAR.php'; +require_once 'PEAR/Config.php'; + +define('DETAILED', 1); +putenv("PHP_PEAR_RUNTESTS=1"); + +class PEAR_RunTest +{ + var $_logger; + + /** + * An object that supports the PEAR_Common->log() signature, or null + * @param PEAR_Common|null + */ + function PEAR_RunTest($logger = null) + { + $this->_logger = $logger; + } + + // + // Run an individual test case. + // + + function run($file, $ini_settings = '') + { + $cwd = getcwd(); + $conf = &PEAR_Config::singleton(); + $php = $conf->get('php_bin'); + //var_dump($php);exit; + global $log_format, $info_params, $ini_overwrites; + + $info_params = ''; + $log_format = 'LEOD'; + + // Load the sections of the test file. + $section_text = array( + 'TEST' => '(unnamed test)', + 'SKIPIF' => '', + 'GET' => '', + 'ARGS' => '', + ); + + if (!is_file($file) || !$fp = fopen($file, "r")) { + return PEAR::raiseError("Cannot open test file: $file"); + } + + $section = ''; + while (!feof($fp)) { + $line = fgets($fp); + + // Match the beginning of a section. + if (ereg('^--([A-Z]+)--',$line,$r)) { + $section = $r[1]; + $section_text[$section] = ''; + continue; + } + + // Add to the section text. + $section_text[$section] .= $line; + } + fclose($fp); + + $shortname = str_replace($cwd.'/', '', $file); + $tested = trim($section_text['TEST'])." [$shortname]"; + + $tmp = realpath(dirname($file)); + $tmp_skipif = $tmp . uniqid('/phpt.'); + $tmp_file = ereg_replace('\.phpt$','.php',$file); + $tmp_post = $tmp . uniqid('/phpt.'); + + @unlink($tmp_skipif); + @unlink($tmp_file); + @unlink($tmp_post); + + // unlink old test results + @unlink(ereg_replace('\.phpt$','.diff',$file)); + @unlink(ereg_replace('\.phpt$','.log',$file)); + @unlink(ereg_replace('\.phpt$','.exp',$file)); + @unlink(ereg_replace('\.phpt$','.out',$file)); + + // Check if test should be skipped. + $info = ''; + $warn = false; + if (array_key_exists('SKIPIF', $section_text)) { + if (trim($section_text['SKIPIF'])) { + $this->save_text($tmp_skipif, $section_text['SKIPIF']); + //$extra = substr(PHP_OS, 0, 3) !== "WIN" ? + // "unset REQUEST_METHOD;": ""; + + //$output = `$extra $php $info_params -f $tmp_skipif`; + $output = `$php $info_params -f $tmp_skipif`; + unlink($tmp_skipif); + if (eregi("^skip", trim($output))) { + $skipreason = "SKIP $tested"; + $reason = (eregi("^skip[[:space:]]*(.+)\$", trim($output))) ? eregi_replace("^skip[[:space:]]*(.+)\$", "\\1", trim($output)) : FALSE; + if ($reason) { + $skipreason .= " (reason: $reason)"; + } + $this->_logger->log(0, $skipreason); + if (isset($old_php)) { + $php = $old_php; + } + return 'SKIPPED'; + } + if (eregi("^info", trim($output))) { + $reason = (ereg("^info[[:space:]]*(.+)\$", trim($output))) ? ereg_replace("^info[[:space:]]*(.+)\$", "\\1", trim($output)) : FALSE; + if ($reason) { + $info = " (info: $reason)"; + } + } + if (eregi("^warn", trim($output))) { + $reason = (ereg("^warn[[:space:]]*(.+)\$", trim($output))) ? ereg_replace("^warn[[:space:]]*(.+)\$", "\\1", trim($output)) : FALSE; + if ($reason) { + $warn = true; /* only if there is a reason */ + $info = " (warn: $reason)"; + } + } + } + } + + // We've satisfied the preconditions - run the test! + $this->save_text($tmp_file,$section_text['FILE']); + + $args = $section_text['ARGS'] ? ' -- '.$section_text['ARGS'] : ''; + + $cmd = "$php$ini_settings -f $tmp_file$args 2>&1"; + if (isset($this->_logger)) { + $this->_logger->log(2, 'Running command "' . $cmd . '"'); + } + + $savedir = getcwd(); // in case the test moves us around + if (isset($section_text['RETURNS'])) { + ob_start(); + system($cmd, $return_value); + $out = ob_get_contents(); + ob_end_clean(); + @unlink($tmp_post); + $section_text['RETURNS'] = (int) trim($section_text['RETURNS']); + $returnfail = ($return_value != $section_text['RETURNS']); + } else { + $out = `$cmd`; + $returnfail = false; + } + chdir($savedir); + // Does the output match what is expected? + $output = trim($out); + $output = preg_replace('/\r\n/', "\n", $output); + + if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) { + if (isset($section_text['EXPECTF'])) { + $wanted = trim($section_text['EXPECTF']); + } else { + $wanted = trim($section_text['EXPECTREGEX']); + } + $wanted_re = preg_replace('/\r\n/',"\n",$wanted); + if (isset($section_text['EXPECTF'])) { + $wanted_re = preg_quote($wanted_re, '/'); + // Stick to basics + $wanted_re = str_replace("%s", ".+?", $wanted_re); //not greedy + $wanted_re = str_replace("%i", "[+\-]?[0-9]+", $wanted_re); + $wanted_re = str_replace("%d", "[0-9]+", $wanted_re); + $wanted_re = str_replace("%x", "[0-9a-fA-F]+", $wanted_re); + $wanted_re = str_replace("%f", "[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?", $wanted_re); + $wanted_re = str_replace("%c", ".", $wanted_re); + // %f allows two points "-.0.0" but that is the best *simple* expression + } + /* DEBUG YOUR REGEX HERE + var_dump($wanted_re); + print(str_repeat('=', 80) . "\n"); + var_dump($output); + */ + if (!$returnfail && preg_match("/^$wanted_re\$/s", $output)) { + @unlink($tmp_file); + $this->_logger->log(0, "PASS $tested$info"); + if (isset($old_php)) { + $php = $old_php; + } + return 'PASSED'; + } + + } else { + $wanted = trim($section_text['EXPECT']); + $wanted = preg_replace('/\r\n/',"\n",$wanted); + // compare and leave on success + $ok = (0 == strcmp($output,$wanted)); + if (!$returnfail && $ok) { + @unlink($tmp_file); + $this->_logger->log(0, "PASS $tested$info"); + if (isset($old_php)) { + $php = $old_php; + } + return 'PASSED'; + } + } + + // Test failed so we need to report details. + if ($warn) { + $this->_logger->log(0, "WARN $tested$info"); + } else { + $this->_logger->log(0, "FAIL $tested$info"); + } + + if (isset($section_text['RETURNS'])) { + $GLOBALS['__PHP_FAILED_TESTS__'][] = array( + 'name' => $file, + 'test_name' => $tested, + 'output' => ereg_replace('\.phpt$','.log', $file), + 'diff' => ereg_replace('\.phpt$','.diff', $file), + 'info' => $info, + 'return' => $return_value + ); + } else { + $GLOBALS['__PHP_FAILED_TESTS__'][] = array( + 'name' => $file, + 'test_name' => $tested, + 'output' => ereg_replace('\.phpt$','.log', $file), + 'diff' => ereg_replace('\.phpt$','.diff', $file), + 'info' => $info, + ); + } + + // write .exp + if (strpos($log_format,'E') !== FALSE) { + $logname = ereg_replace('\.phpt$','.exp',$file); + if (!$log = fopen($logname,'w')) { + return PEAR::raiseError("Cannot create test log - $logname"); + } + fwrite($log,$wanted); + fclose($log); + } + + // write .out + if (strpos($log_format,'O') !== FALSE) { + $logname = ereg_replace('\.phpt$','.out',$file); + if (!$log = fopen($logname,'w')) { + return PEAR::raiseError("Cannot create test log - $logname"); + } + fwrite($log,$output); + fclose($log); + } + + // write .diff + if (strpos($log_format,'D') !== FALSE) { + $logname = ereg_replace('\.phpt$','.diff',$file); + if (!$log = fopen($logname,'w')) { + return PEAR::raiseError("Cannot create test log - $logname"); + } + fwrite($log, $this->generate_diff($wanted, $output, + isset($section_text['RETURNS']) ? array(trim($section_text['RETURNS']), + $return_value) : null)); + fclose($log); + } + + // write .log + if (strpos($log_format,'L') !== FALSE) { + $logname = ereg_replace('\.phpt$','.log',$file); + if (!$log = fopen($logname,'w')) { + return PEAR::raiseError("Cannot create test log - $logname"); + } + fwrite($log," +---- EXPECTED OUTPUT +$wanted +---- ACTUAL OUTPUT +$output +---- FAILED +"); + if ($returnfail) { + fwrite($log," +---- EXPECTED RETURN +$section_text[RETURNS] +---- ACTUAL RETURN +$return_value +"); + } + fclose($log); + //error_report($file,$logname,$tested); + } + + if (isset($old_php)) { + $php = $old_php; + } + + return $warn ? 'WARNED' : 'FAILED'; + } + + function generate_diff($wanted, $output, $return_value) + { + $w = explode("\n", $wanted); + $o = explode("\n", $output); + $w1 = array_diff_assoc($w,$o); + $o1 = array_diff_assoc($o,$w); + $w2 = array(); + $o2 = array(); + foreach($w1 as $idx => $val) $w2[sprintf("%03d<",$idx)] = sprintf("%03d- ", $idx+1).$val; + foreach($o1 as $idx => $val) $o2[sprintf("%03d>",$idx)] = sprintf("%03d+ ", $idx+1).$val; + $diff = array_merge($w2, $o2); + ksort($diff); + if ($return_value) { + $extra = "##EXPECTED: $return_value[0]\r\n##RETURNED: $return_value[1]"; + } else { + $extra = ''; + } + return implode("\r\n", $diff) . $extra; + } + + // + // Write the given text to a temporary file, and return the filename. + // + + function save_text($filename, $text) + { + if (!$fp = fopen($filename, 'w')) { + return PEAR::raiseError("Cannot open file '" . $filename . "' (save_text)"); + } + fwrite($fp,$text); + fclose($fp); + if (1 < DETAILED) echo " +FILE $filename {{{ +$text +}}} +"; + } + +} +?> \ No newline at end of file diff --git a/src/www/lib/pear/System.php b/src/www/lib/pear/System.php new file mode 100644 index 00000000..0ef51f37 --- /dev/null +++ b/src/www/lib/pear/System.php @@ -0,0 +1,540 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: System.php,v 1.1 2005/05/28 01:55:10 henrique Exp $ +// + +require_once 'PEAR.php'; +require_once 'Console/Getopt.php'; + +$GLOBALS['_System_temp_files'] = array(); + +/** +* System offers cross plattform compatible system functions +* +* Static functions for different operations. Should work under +* Unix and Windows. The names and usage has been taken from its respectively +* GNU commands. The functions will return (bool) false on error and will +* trigger the error with the PHP trigger_error() function (you can silence +* the error by prefixing a '@' sign after the function call). +* +* Documentation on this class you can find in: +* http://pear.php.net/manual/ +* +* Example usage: +* if (!@System::rm('-r file1 dir1')) { +* print "could not delete file1 or dir1"; +* } +* +* In case you need to to pass file names with spaces, +* pass the params as an array: +* +* System::rm(array('-r', $file1, $dir1)); +* +* @package System +* @author Tomas V.V.Cox +* @version $Revision: 1.1 $ +* @access public +* @see http://pear.php.net/manual/ +*/ +class System +{ + /** + * returns the commandline arguments of a function + * + * @param string $argv the commandline + * @param string $short_options the allowed option short-tags + * @param string $long_options the allowed option long-tags + * @return array the given options and there values + * @access private + */ + function _parseArgs($argv, $short_options, $long_options = null) + { + if (!is_array($argv) && $argv !== null) { + $argv = preg_split('/\s+/', $argv); + } + return Console_Getopt::getopt2($argv, $short_options); + } + + /** + * Output errors with PHP trigger_error(). You can silence the errors + * with prefixing a "@" sign to the function call: @System::mkdir(..); + * + * @param mixed $error a PEAR error or a string with the error message + * @return bool false + * @access private + */ + function raiseError($error) + { + if (PEAR::isError($error)) { + $error = $error->getMessage(); + } + trigger_error($error, E_USER_WARNING); + return false; + } + + /** + * Creates a nested array representing the structure of a directory + * + * System::_dirToStruct('dir1', 0) => + * Array + * ( + * [dirs] => Array + * ( + * [0] => dir1 + * ) + * + * [files] => Array + * ( + * [0] => dir1/file2 + * [1] => dir1/file3 + * ) + * ) + * @param string $sPath Name of the directory + * @param integer $maxinst max. deep of the lookup + * @param integer $aktinst starting deep of the lookup + * @return array the structure of the dir + * @access private + */ + + function _dirToStruct($sPath, $maxinst, $aktinst = 0) + { + $struct = array('dirs' => array(), 'files' => array()); + if (($dir = @opendir($sPath)) === false) { + System::raiseError("Could not open dir $sPath"); + return $struct; // XXX could not open error + } + $struct['dirs'][] = $sPath; // XXX don't add if '.' or '..' ? + $list = array(); + while ($file = readdir($dir)) { + if ($file != '.' && $file != '..') { + $list[] = $file; + } + } + closedir($dir); + sort($list); + if ($aktinst < $maxinst || $maxinst == 0) { + foreach($list as $val) { + $path = $sPath . DIRECTORY_SEPARATOR . $val; + if (is_dir($path)) { + $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1); + $struct = array_merge_recursive($tmp, $struct); + } else { + $struct['files'][] = $path; + } + } + } + return $struct; + } + + /** + * Creates a nested array representing the structure of a directory and files + * + * @param array $files Array listing files and dirs + * @return array + * @see System::_dirToStruct() + */ + function _multipleToStruct($files) + { + $struct = array('dirs' => array(), 'files' => array()); + settype($files, 'array'); + foreach ($files as $file) { + if (is_dir($file)) { + $tmp = System::_dirToStruct($file, 0); + $struct = array_merge_recursive($tmp, $struct); + } else { + $struct['files'][] = $file; + } + } + return $struct; + } + + /** + * The rm command for removing files. + * Supports multiple files and dirs and also recursive deletes + * + * @param string $args the arguments for rm + * @return mixed PEAR_Error or true for success + * @access public + */ + function rm($args) + { + $opts = System::_parseArgs($args, 'rf'); // "f" do nothing but like it :-) + if (PEAR::isError($opts)) { + return System::raiseError($opts); + } + foreach($opts[0] as $opt) { + if ($opt[0] == 'r') { + $do_recursive = true; + } + } + $ret = true; + if (isset($do_recursive)) { + $struct = System::_multipleToStruct($opts[1]); + foreach($struct['files'] as $file) { + if (!@unlink($file)) { + $ret = false; + } + } + foreach($struct['dirs'] as $dir) { + if (!@rmdir($dir)) { + $ret = false; + } + } + } else { + foreach ($opts[1] as $file) { + $delete = (is_dir($file)) ? 'rmdir' : 'unlink'; + if (!@$delete($file)) { + $ret = false; + } + } + } + return $ret; + } + + /** + * Make directories. Note that we use call_user_func('mkdir') to avoid + * a problem with ZE2 calling System::mkDir instead of the native PHP func. + * + * @param string $args the name of the director(y|ies) to create + * @return bool True for success + * @access public + */ + function mkDir($args) + { + $opts = System::_parseArgs($args, 'pm:'); + if (PEAR::isError($opts)) { + return System::raiseError($opts); + } + $mode = 0777; // default mode + foreach($opts[0] as $opt) { + if ($opt[0] == 'p') { + $create_parents = true; + } elseif($opt[0] == 'm') { + // if the mode is clearly an octal number (starts with 0) + // convert it to decimal + if (strlen($opt[1]) && $opt[1]{0} == '0') { + $opt[1] = octdec($opt[1]); + } else { + // convert to int + $opt[1] += 0; + } + $mode = $opt[1]; + } + } + $ret = true; + if (isset($create_parents)) { + foreach($opts[1] as $dir) { + $dirstack = array(); + while (!@is_dir($dir) && $dir != DIRECTORY_SEPARATOR) { + array_unshift($dirstack, $dir); + $dir = dirname($dir); + } + while ($newdir = array_shift($dirstack)) { + if (!call_user_func('mkdir', $newdir, $mode)) { + $ret = false; + } + } + } + } else { + foreach($opts[1] as $dir) { + if (!@is_dir($dir) && !call_user_func('mkdir', $dir, $mode)) { + $ret = false; + } + } + } + return $ret; + } + + /** + * Concatenate files + * + * Usage: + * 1) $var = System::cat('sample.txt test.txt'); + * 2) System::cat('sample.txt test.txt > final.txt'); + * 3) System::cat('sample.txt test.txt >> final.txt'); + * + * Note: as the class use fopen, urls should work also (test that) + * + * @param string $args the arguments + * @return boolean true on success + * @access public + */ + function &cat($args) + { + $ret = null; + $files = array(); + if (!is_array($args)) { + $args = preg_split('/\s+/', $args); + } + for($i=0; $i < count($args); $i++) { + if ($args[$i] == '>') { + $mode = 'wb'; + $outputfile = $args[$i+1]; + break; + } elseif ($args[$i] == '>>') { + $mode = 'ab+'; + $outputfile = $args[$i+1]; + break; + } else { + $files[] = $args[$i]; + } + } + if (isset($mode)) { + if (!$outputfd = fopen($outputfile, $mode)) { + $err = System::raiseError("Could not open $outputfile"); + return $err; + } + $ret = true; + } + foreach ($files as $file) { + if (!$fd = fopen($file, 'r')) { + System::raiseError("Could not open $file"); + continue; + } + while ($cont = fread($fd, 2048)) { + if (isset($outputfd)) { + fwrite($outputfd, $cont); + } else { + $ret .= $cont; + } + } + fclose($fd); + } + if (@is_resource($outputfd)) { + fclose($outputfd); + } + return $ret; + } + + /** + * Creates temporary files or directories. This function will remove + * the created files when the scripts finish its execution. + * + * Usage: + * 1) $tempfile = System::mktemp("prefix"); + * 2) $tempdir = System::mktemp("-d prefix"); + * 3) $tempfile = System::mktemp(); + * 4) $tempfile = System::mktemp("-t /var/tmp prefix"); + * + * prefix -> The string that will be prepended to the temp name + * (defaults to "tmp"). + * -d -> A temporary dir will be created instead of a file. + * -t -> The target dir where the temporary (file|dir) will be created. If + * this param is missing by default the env vars TMP on Windows or + * TMPDIR in Unix will be used. If these vars are also missing + * c:\windows\temp or /tmp will be used. + * + * @param string $args The arguments + * @return mixed the full path of the created (file|dir) or false + * @see System::tmpdir() + * @access public + */ + function mktemp($args = null) + { + static $first_time = true; + $opts = System::_parseArgs($args, 't:d'); + if (PEAR::isError($opts)) { + return System::raiseError($opts); + } + foreach($opts[0] as $opt) { + if($opt[0] == 'd') { + $tmp_is_dir = true; + } elseif($opt[0] == 't') { + $tmpdir = $opt[1]; + } + } + $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp'; + if (!isset($tmpdir)) { + $tmpdir = System::tmpdir(); + } + if (!System::mkDir("-p $tmpdir")) { + return false; + } + $tmp = tempnam($tmpdir, $prefix); + if (isset($tmp_is_dir)) { + unlink($tmp); // be careful possible race condition here + if (!call_user_func('mkdir', $tmp, 0700)) { + return System::raiseError("Unable to create temporary directory $tmpdir"); + } + } + $GLOBALS['_System_temp_files'][] = $tmp; + if ($first_time) { + PEAR::registerShutdownFunc(array('System', '_removeTmpFiles')); + $first_time = false; + } + return $tmp; + } + + /** + * Remove temporary files created my mkTemp. This function is executed + * at script shutdown time + * + * @access private + */ + function _removeTmpFiles() + { + if (count($GLOBALS['_System_temp_files'])) { + $delete = $GLOBALS['_System_temp_files']; + array_unshift($delete, '-r'); + System::rm($delete); + } + } + + /** + * Get the path of the temporal directory set in the system + * by looking in its environments variables. + * Note: php.ini-recommended removes the "E" from the variables_order setting, + * making unavaible the $_ENV array, that s why we do tests with _ENV + * + * @return string The temporal directory on the system + */ + function tmpdir() + { + if (OS_WINDOWS) { + if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) { + return $var; + } + if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) { + return $var; + } + if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) { + return $var; + } + return getenv('SystemRoot') . '\temp'; + } + if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) { + return $var; + } + return '/tmp'; + } + + /** + * The "which" command (show the full path of a command) + * + * @param string $program The command to search for + * @return mixed A string with the full path or false if not found + * @author Stig Bakken + */ + function which($program, $fallback = false) + { + // is_executable() is not available on windows + if (OS_WINDOWS) { + $pear_is_executable = 'is_file'; + } else { + $pear_is_executable = 'is_executable'; + } + + // full path given + if (basename($program) != $program) { + return (@$pear_is_executable($program)) ? $program : $fallback; + } + + // XXX FIXME honor safe mode + $path_delim = OS_WINDOWS ? ';' : ':'; + $exe_suffixes = OS_WINDOWS ? array('.exe','.bat','.cmd','.com') : array(''); + $path_elements = explode($path_delim, getenv('PATH')); + foreach ($exe_suffixes as $suff) { + foreach ($path_elements as $dir) { + $file = $dir . DIRECTORY_SEPARATOR . $program . $suff; + if (@is_file($file) && @$pear_is_executable($file)) { + return $file; + } + } + } + return $fallback; + } + + /** + * The "find" command + * + * Usage: + * + * System::find($dir); + * System::find("$dir -type d"); + * System::find("$dir -type f"); + * System::find("$dir -name *.php"); + * System::find("$dir -name *.php -name *.htm*"); + * System::find("$dir -maxdepth 1"); + * + * Params implmented: + * $dir -> Start the search at this directory + * -type d -> return only directories + * -type f -> return only files + * -maxdepth -> max depth of recursion + * -name -> search pattern (bash style). Multiple -name param allowed + * + * @param mixed Either array or string with the command line + * @return array Array of found files + * + */ + function find($args) + { + if (!is_array($args)) { + $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY); + } + $dir = array_shift($args); + $patterns = array(); + $depth = 0; + $do_files = $do_dirs = true; + for ($i = 0; $i < count($args); $i++) { + switch ($args[$i]) { + case '-type': + if (in_array($args[$i+1], array('d', 'f'))) { + if ($args[$i+1] == 'd') { + $do_files = false; + } else { + $do_dirs = false; + } + } + $i++; + break; + case '-name': + $patterns[] = "(" . preg_replace(array('/\./', '/\*/'), + array('\.', '.*'), + $args[$i+1]) + . ")"; + $i++; + break; + case '-maxdepth': + $depth = $args[$i+1]; + break; + } + } + $path = System::_dirToStruct($dir, $depth); + if ($do_files && $do_dirs) { + $files = array_merge($path['files'], $path['dirs']); + } elseif ($do_dirs) { + $files = $path['dirs']; + } else { + $files = $path['files']; + } + if (count($patterns)) { + $patterns = implode('|', $patterns); + $ret = array(); + for ($i = 0; $i < count($files); $i++) { + if (preg_match("#^$patterns\$#", $files[$i])) { + $ret[] = $files[$i]; + } + } + return $ret; + } + return $files; + } +} +?> diff --git a/src/www/lib/root.php b/src/www/lib/root.php new file mode 100644 index 00000000..368a7682 --- /dev/null +++ b/src/www/lib/root.php @@ -0,0 +1,27 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> + \ No newline at end of file diff --git a/src/www/lib/spmtable.py b/src/www/lib/spmtable.py new file mode 100644 index 00000000..d28dc362 --- /dev/null +++ b/src/www/lib/spmtable.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python + +import sys +from socket import * + +class Spm: + + def __init__(self, server = 'localhost', port = 1750): + self.server = server + self.port = port + + self.proto = {'PM_SERVER' : '\x00\x01', 'PM_CLIENT' : '\x00\x02', 'PM_CLOSE' : '\x00\x03', + 'PM_RESEND' : '\x00\x04', 'PM_QUIT' : '\x00\x05', 'PM_SORRY' : '\x00\x06', + 'PM_OK' : '\x00\x07', 'PM_ACCEPT' : '\x00\x08', 'PM_TABLE' : '\x00\x09', + 'PM_RMSERVER': '\x00\x10', 'PM_FWINIT' : '\x00\x11', 'PM_SHARE' : '\x00\x12', + 'PM_OKSHARE' : '\x00\x13', 'PM_BIGBUF' : 1024, 'PM_MAXTRY' : 20 } + + def getPort(self, server = None): + """ + Retorna a porta de um dado servidor. + @param server servidor do qual deseja saber a porta + """ + + pass + + def getTable(self): + """ + Retorna um dictionary com os servidores e respectivas portas + ativos no momento. + """ + + dic = {} + + sk = socket(AF_INET, SOCK_STREAM) + + try: + sk.connect((self.server, self.port)) + sk.send(self.proto['PM_TABLE']) + + if sk.recv(2) == self.proto['PM_OK']: + num = sk.recv(2) + data = sk.recv(self.proto['PM_BIGBUF']) + sk.close() + + data = data.split('\x00') + del data[-1] + + for i in data: + item = i.split(':') + dic[item[0].strip()] = int(item[1]) + + return dic + + elif sk.recv(2) == self.proto['PM_RESEND']: + sk.close() + return dic + + except error, msg: + print "Erro:",msg[1] + sys.exit(-1) + + def dumpTable(self, table=None): + if table == None: + table = self.getTable() + + if(table): + for i,v in table.items(): + print "%s %d" % (i, v) + + +if __name__ == '__main__': + + if(len(sys.argv) < 2): + spm = Spm() + else: + spm = Spm(sys.argv[1]) + + spm.dumpTable() diff --git a/src/www/lib/utils.php b/src/www/lib/utils.php new file mode 100644 index 00000000..163b2b82 --- /dev/null +++ b/src/www/lib/utils.php @@ -0,0 +1,328 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> +"; echo var_dump($spmout); echo ""; + + if(count($spmout) < 2) { + return FALSE; + } + + for($i = 2; $i < count($spmout) -1; $i++) { + $tmp = split(":", $spmout[$i]); + $servers[trim($tmp[1])] = trim($tmp[2]); + } + + return $servers; +} + + +// validaçoes +// ra (hh:mm:ss) +// dec (+/-gg:mm:ss) +// gg 00-90 +// hh 00-12 +// mm 00-59 +// ss 00-59 + +function validate_dec($dec) { + + return ereg("[0-9]{2}:[0-5][0-9]:[0-5][0-9]", $dec); + +} + +function dumpSession() { + + echo "
"; + echo "
";
+  print_r($_SESSION);
+  echo "
"; + echo "
"; + +} + +function updateFilename($dir = "", $name = "", $index = "") { + + if($dir) + $_SESSION['bf_dir'] = $dir; + + if($name) + $_SESSION['bf_name'] = $name; + + if($index) + $_SESSION['bf_index'] = $index; + + $_SESSION['bf_fullpath'] = $_SESSION['bf_dir'] . "/" . $_SESSION['user'] . "/" . $_SESSION['inicio']; + + $_SESSION['bf_fullname'] = $_SESSION['bf_fullpath'] . "/" . $_SESSION['bf_name'] . "-" . (count($_SESSION['log'])+1) . "-" . strftime($_SESSION['bf_index'], time()); + +} + +function formatSize($size) { + + # format a file size adding kb, Mb, Gb + + $res = $size / 1024.0; + + if ($res >= 1024.0) { + + $res = $res / 1024.0; + + return floor($res) . " Mb"; + + } else { + + return floor($res) . " kb"; + + } + +} + +function byte_format($input, $dec=0) +{ + $prefix_arr = array("", "K", "M", "G", "T"); + $value = round($input, $dec); + while ($value>1024) + { + $value /= 1024; + $i++; + } + $return_str = round($value, $dec).$prefix_arr[$i]; + return $return_str; +} + +// Mainly based on code by: matt_DOTbevan_ATmarginsoftware_DOTcom +function mkdir_p($target) { + // If the path already exists && is a directory, all is well. + // If the path is not a directory, we've a problem. + if (file_exists($target)) { + if (!is_dir($target)) return false; + else return true; + } + + // Attempting to create the directory may clutter up our display. + if (@mkdir($target, 0777)) + return @chmod($target, 0777); + +} + +function doLogin($nome, $user, $user_id, $root, $db) { + + if(!$root) { + $sql = "INSERT INTO logged VALUES('$user_id')"; + $res =& $db->query($sql); + } + + if(PEAR::isError($res)) { + Header("Location: " . getError("index.php", "Não foi possível efetuar o login. Tente novamente.")); + exit(0); + } + + session_start(); + + $_SESSION['nome'] = $nome; + $_SESSION['user'] = $user; + $_SESSION['user_id'] = $user_id; + $_SESSION['inicio'] = strftime("%Y%m%d-%H%M%Z", time()); + $_SESSION['root'] = $root; + + $_SESSION['log'] = array(); + $_SESSION['obj'] = array(); + $_SESSION['ra'] = array(); + $_SESSION['dec'] = array(); + $_SESSION['num_exp'] = array(); + $_SESSION['exp_time'] = array(); + $_SESSION['filter'] = array(); + $_SESSION['start_time'] = array(); + + updateFilename(getcwd() . "/data", "imagem", "%Y%m%d%H%M%S"); + + $userAreaCreated = 0; + + // create user image space + if(!file_exists($_SESSION['bf_dir'] . "/" . $_SESSION['user'])) + mkdir_p($_SESSION['bf_dir'] . "/" . $_SESSION['user'], 0777); + + if(mkdir_p($_SESSION['bf_dir'] ."/". $_SESSION['user'] ."/". $_SESSION['inicio'], 0777)) { + mkdir_p($_SESSION['bf_dir'] ."/". $_SESSION['user'] ."/". $_SESSION['inicio'] . "/thumbs", 0777); + $userAreaCreated = 1; + } + + Header("Location: home.php?userspace=" . $userAreaCreated); + +} + +function userAllowed($id, $db) { + + $agora = time(); + + $sql = "SELECT * FROM user_sched WHERE (user_id = $id) AND ((inicio <= $agora) AND (fim >= $agora))"; + $res =& $db->query($sql); + + if(PEAR::isError($res)) { + return 0; + } + + if($res->numRows()) { + return 1; + } else { + return 0; + } + +} + +function userUniq($id, $db) { + + $sql = "SELECT * FROM logged WHERE id = $id"; + $res =& $db->query($sql); + + if(PEAR::isError($res)) { + return 0; + } + + if($res->numRows()) { + return 0; + } else { + return 1; + } + +} + +// from MediaWiki install scripts + +function replacevars( $ins, $vars ) { + $varnames = array( + 'uts_mysql_db', 'uts_mysql_server', 'uts_mysql_user', 'uts_mysql_user_pass' + ); + + foreach ( $varnames as $var ) { + $ins = str_replace( '{$' . $var . '}', $vars[$var], $ins ); + $ins = str_replace( '/*$' . $var . '*/`', '`' . $vars[$var], $ins ); + $ins = str_replace( '/*$' . $var . '*/', $vars[$var], $ins ); + } + return $ins; +} + +# +# Read and execute SQL commands from a file +# +function dbsource($fname, $vars, $database = false) { + + + $fp = fopen( $fname, 'r' ); + if ( false === $fp ) { + print "Could not open \"{$fname}\".\n"; + exit(); + } + + $cmd = ""; + $done = false; + + while ( ! feof( $fp ) ) { + $line = trim( fgets( $fp, 1024 ) ); + $sl = strlen( $line ) - 1; + + if ( $sl < 0 ) { continue; } + if ( '-' == $line{0} && '-' == $line{1} ) { continue; } + + if ( ';' == $line{$sl} ) { + $done = true; + $line = substr( $line, 0, $sl ); + } + + if ( '' != $cmd ) { $cmd .= ' '; } + $cmd .= $line; + + if ( $done ) { + $cmd = replacevars( $cmd , $vars); + if( $database ) + $res = $database->query( $cmd ); + else + $res = mysql_query( $cmd ); + + if ( false === $res ) { + $err = mysql_error(); + print "Query \"{$cmd}\" failed with error code \"$err\".\n"; + exit(); + } + + $cmd = ''; + $done = false; + } + } + fclose( $fp ); +} +?> diff --git a/src/www/lib/utils.php.orig b/src/www/lib/utils.php.orig new file mode 100644 index 00000000..2aaf1668 --- /dev/null +++ b/src/www/lib/utils.php.orig @@ -0,0 +1,304 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> +"; echo var_dump($spmout); echo ""; + + if(count($spmout) < 2) { + return FALSE; + } + + for($i = 2; $i < count($spmout) -1; $i++) { + $tmp = split(":", $spmout[$i]); + $servers[trim($tmp[1])] = trim($tmp[2]); + } + + return $servers; +} + + +// validaçoes +// ra (hh:mm:ss) +// dec (+/-gg:mm:ss) +// gg 00-90 +// hh 00-12 +// mm 00-59 +// ss 00-59 + +function validate_dec($dec) { + + return ereg("[0-9]{2}:[0-5][0-9]:[0-5][0-9]", $dec); + +} + +function dumpSession() { + + echo "
"; + echo "
";
+  print_r($_SESSION);
+  echo "
"; + echo "
"; + +} + +function updateFilename($dir = "", $name = "", $index = "") { + + if($dir) + $_SESSION['bf_dir'] = $dir; + + if($name) + $_SESSION['bf_name'] = $name; + + if($index) + $_SESSION['bf_index'] = $index; + + $_SESSION['bf_fullpath'] = $_SESSION['bf_dir'] . "/" . $_SESSION['user'] . "/" . $_SESSION['inicio']; + + $_SESSION['bf_fullname'] = $_SESSION['bf_fullpath'] . "/" . $_SESSION['bf_name'] . "-" . strftime($_SESSION['bf_index'], time()); + +} + +function formatSize($size) { + + # format a file size adding kb, Mb, Gb + + $res = $size / 1024.0; + + if ($res >= 1024.0) { + + $res = $res / 1024.0; + + return floor($res) . " Mb"; + + } else { + + return floor($res) . " kb"; + + } + +} + +function byte_format($input, $dec=0) +{ + $prefix_arr = array("", "K", "M", "G", "T"); + $value = round($input, $dec); + while ($value>1024) + { + $value /= 1024; + $i++; + } + $return_str = round($value, $dec).$prefix_arr[$i]; + return $return_str; +} + +// Mainly based on code by: matt_DOTbevan_ATmarginsoftware_DOTcom +function mkdir_p($target) { + // If the path already exists && is a directory, all is well. + // If the path is not a directory, we've a problem. + if (file_exists($target)) { + if (!is_dir($target)) return false; + else return true; + } + + // Attempting to create the directory may clutter up our display. + if (@mkdir($target, 0777)) + return @chmod($target, 0777); + +} + +function doLogin($nome, $user, $user_id, $root, $db) { + + if(!$root) { + $sql = "INSERT INTO logged VALUES('$user_id')"; + $res =& $db->query($sql); + } + + if(PEAR::isError($res)) { + Header("Location: " . getError("index.php", "Nao foi possivel efetuar o login. Contate o administrador.")); + exit(0); + } + + session_start(); + + $_SESSION['nome'] = $nome; + $_SESSION['user'] = $user; + $_SESSION['user_id'] = $user_id; + $_SESSION['inicio'] = strftime("%Y%m%d-%H%M%Z", time()); + $_SESSION['root'] = $root; + + $_SESSION['log'] = array(); + $_SESSION['obj'] = array(); + $_SESSION['ra'] = array(); + $_SESSION['dec'] = array(); + $_SESSION['num_exp'] = array(); + $_SESSION['exp_time'] = array(); + $_SESSION['filter'] = array(); + $_SESSION['start_time'] = array(); + + updateFilename(getcwd() . "/data", "imagem", "%Y%m%d%H%M%S"); + + $userAreaCreated = 0; + + // create user image space + if(!file_exists($_SESSION['bf_dir'] . "/" . $_SESSION['user'])) + mkdir_p($_SESSION['bf_dir'] . "/" . $_SESSION['user'], 0777); + + if(mkdir_p($_SESSION['bf_dir'] ."/". $_SESSION['user'] ."/". $_SESSION['inicio'], 0777)) { + mkdir_p($_SESSION['bf_dir'] ."/". $_SESSION['user'] ."/". $_SESSION['inicio'] . "/thumbs", 0777); + $userAreaCreated = 1; + } + + Header("Location: home.php?userspace=" . $userAreaCreated); + +} + +function userAllowed($id, $db) { + + $agora = time(); + + $sql = "SELECT * FROM user_sched WHERE (user_id = $id) AND ((inicio <= $agora) AND (fim >= $agora))"; + $res =& $db->query($sql); + + if(PEAR::isError($res)) { + return 0; + } + + if($res->numRows()) { + return 1; + } else { + return 0; + } + +} + +function userUniq($id, $db) { + + $sql = "SELECT * FROM logged WHERE id = $id"; + $res =& $db->query($sql); + + if(PEAR::isError($res)) { + return 0; + } + + if($res->numRows()) { + return 0; + } else { + return 1; + } + +} + +// from MediaWiki install scripts + +function replacevars( $ins, $vars ) { + $varnames = array( + 'uts_mysql_db', 'uts_mysql_server', 'uts_mysql_user', 'uts_mysql_user_pass' + ); + + foreach ( $varnames as $var ) { + $ins = str_replace( '{$' . $var . '}', $vars[$var], $ins ); + $ins = str_replace( '/*$' . $var . '*/`', '`' . $vars[$var], $ins ); + $ins = str_replace( '/*$' . $var . '*/', $vars[$var], $ins ); + } + return $ins; +} + +# +# Read and execute SQL commands from a file +# +function dbsource($fname, $vars, $database = false) { + + + $fp = fopen( $fname, 'r' ); + if ( false === $fp ) { + print "Could not open \"{$fname}\".\n"; + exit(); + } + + $cmd = ""; + $done = false; + + while ( ! feof( $fp ) ) { + $line = trim( fgets( $fp, 1024 ) ); + $sl = strlen( $line ) - 1; + + if ( $sl < 0 ) { continue; } + if ( '-' == $line{0} && '-' == $line{1} ) { continue; } + + if ( ';' == $line{$sl} ) { + $done = true; + $line = substr( $line, 0, $sl ); + } + + if ( '' != $cmd ) { $cmd .= ' '; } + $cmd .= $line; + + if ( $done ) { + $cmd = replacevars( $cmd , $vars); + if( $database ) + $res = $database->query( $cmd ); + else + $res = mysql_query( $cmd ); + + if ( false === $res ) { + $err = mysql_error(); + print "Query \"{$cmd}\" failed with error code \"$err\".\n"; + exit(); + } + + $cmd = ''; + $done = false; + } + } + fclose( $fp ); +} +?> diff --git a/src/www/login.php b/src/www/login.php new file mode 100644 index 00000000..3fc1e80e --- /dev/null +++ b/src/www/login.php @@ -0,0 +1,77 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>query($sql); + +if(PEAR::isError($res)) { // DB error + Header("Location: " . getError("index.php", "Não foi possível contatar o observatório. Consulte o responsável.")); + exit(2); +} + +if(!$res->numRows()) { // usuário não encontrado + Header("Location: " . getError("index.php", "Senha ou usuário inválidos")); + exit(1); +} + +// OK.. usuario encontrado, checar senha +$res->fetchInto($data, DB_FETCHMODE_ASSOC); + +if($data['passwd'] == md5(trim($_POST['pass']))) { // OK, senha confere + + // checa se é administrador, se for, deixa passar, mesmo sem checar tempo e unicidade da sessao + if($data['root'] == 1) { + doLogin($data['nome'], $data['username'], $data['id'], 1, $db); + exit(0); + } + + if(userAllowed($data['id'], $db)) { // eu sei! poderia usar && (AND), mas para poder especificar o erro corretamente uso dois if's + + if(userUniq($data['id'], $db)) { + doLogin($data['nome'], $data['username'], $data['id'], 0, $db); + exit(0); + } else { + Header("Location: " . getError("index.php", "Já há uma sessão em andamento para este usuário.")); + exit(3); + } + + } else { + + Header("Location: " . getError("index.php", "Você não possuí tempo alocado para este momento. Consulte o responsável.")); + exit(3); + + } + +} else { /// OOps, senha errada. + Header("Location: " . getError("index.php", "Senha ou usuário inválidos.")); + exit(1); +} + +?> \ No newline at end of file diff --git a/src/www/logout.php b/src/www/logout.php new file mode 100644 index 00000000..7f09b47e --- /dev/null +++ b/src/www/logout.php @@ -0,0 +1,66 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>query($sql); + + if(PEAR::isError($res) || !$db->affectedRows()) { + Header("Location " . getError("home.php", "Não foi possível efetuar o logout, aguarde mais alguns instantes e tente novamente.")); + } else { + session_destroy(); + Header("Location: index.php"); + } + +} else { + + session_destroy(); + Header("Location: index.php"); + +} + +?> + + + + + + + + + diff --git a/src/www/opcoes.php b/src/www/opcoes.php new file mode 100644 index 00000000..f81900c2 --- /dev/null +++ b/src/www/opcoes.php @@ -0,0 +1,48 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> + + + + + + + + + diff --git a/src/www/root.php b/src/www/root.php new file mode 100644 index 00000000..8fa6c796 --- /dev/null +++ b/src/www/root.php @@ -0,0 +1,26 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> diff --git a/src/www/sky.php b/src/www/sky.php new file mode 100644 index 00000000..619b75dd --- /dev/null +++ b/src/www/sky.php @@ -0,0 +1,61 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?> + + + + +Telescópios na Escola - Céu + + + + + + +:: voltar ::

+&Lng=&Time=&BW=1&size=600&SL=1&SN=1"> +
+Developed and maintained by Chris Peat, Heavens-Above GmbH
+
+ + diff --git a/src/www/status.php b/src/www/status.php new file mode 100644 index 00000000..67691ed5 --- /dev/null +++ b/src/www/status.php @@ -0,0 +1,222 @@ + +// +// This file is part of UTS-www. +// UTS-www is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// UTS-www is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// + +?>setInteractive(0); +$ccd->connect(); + +$tel = new Tel('status'); +$tel->setInteractive(0); +$tel->connect(); + +$sync = new Sync('status'); +$sync->setInteractive(0); +$sync->connect(); + +// primeira vez? +if(!count($_SESSION['log'])) { + $primeiraVez = 1; +} else { + $primeiraVez = 0; +} + + +// estima o tempo da observacao +// tempo = apontamento + num_exp(tempo_leitura + tempo_exposicao) +// so perde tempo com isso, se houver observacao em andamento +$stat0 = $tel->isBusy(); +$stat1 = $ccd->isBusy(); + +if($stat0 || $stat1) { + + $nexp = intval($sync->getStatus("NEXP")); + $exp_time = intval($sync->getStatus("EXPTIME")); + + $cte_ap = UTS_POINTING_TIME; + $tempo_leitura = UTS_CCD_READ_TIME; + + $tempo = $cte_ap + (($tempo_leitura * $nexp) + ($nexp * $exp_time)); + + $ocupado = 1; + +} else { + + $tempo = 0; + $ocupado = 0; + +} + +$format = "parseInt('%Y'),parseInt('%m'),parseInt('%d'),parseInt('%H'),parseInt('%M'),parseInt('%S')"; +$agora = time(); +$curr = count($_SESSION['log']) - 1; +$countdown = strftime($format, ($agora + $tempo) - ($agora - $_SESSION['start_time'][$curr])); + + +$sync->disconnect(); +$tel->disconnect(); +$ccd->disconnect() + +?> + + + + +Telescópios na Escola ::: Estado da +observação + + + + + + + + +
Telescópios na Escola
+
+
+ +
+

Não há observações em andamento.

+
+ +
A T E N Ç Ã +O +

Aguarde, a observação está sendo +efetuada.

+
+

+

+
+

A seguinte observação +está sendo efetuada:

+

+ + + + + + + + + + + + + + + + + + + + + + + + +
Pedido # +
RA +
DEC +
Número de exposições +
Tempo de exposição +
Filtro +
+
+ +

::: voltar :::

+ + +

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%0Aw-?WJp9O>f#)_kUkz`de-dZ|UyKd};eCy+1M5 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%0AKJvrD3$Zq_bebRj zTa{o#i*ZX=ga?c%wpcRTHp1(EY}(TwK(Um&Bot1 zeLCw(OU_k&LB%AVBi@Mzf#QdH-pxYg17-Is={W}-%0?~w#~Y3la(*ijl75!qa!~nd z#Yw--a10l;@_YILF8!H?9~k!H?5^}T3$gce!&gArdmB_aK9ZbtEjg=)IbUUivYRK) zd1;EtZxCYVDPbnxKMTsvhoJ1V;4CjY8A9w7g0eG8{3zPP7D>(e}g#ow+gX;7}o;wUj!=O1H$9@-eMv7UILYGt2p`g2+0@3Gw;c@ zg7gDH=`RqXFBf)We+3n%Bqn}5-_I&MfpQ2*_o)y+-MD5@JqkdTXCx@Qmr9P^YlNh~ zLwF+JX9ddcL*m$dOo-i`Leh0&^OD^HA$G3-W%p`Oc5js&yT28Z{vSf>|CR70_Bw1U ze?*9WlJPr**n1F^ohQYyvr$Mo#W|OsgFxwz0;TUOM1Q_8o0oe)m4CW8<-b};`Tr;+ z-5WyuAB-*e>jld0>HNs%mj{5>d+zvV*wZ3bmGgE^u8I!?&p>Qo{5e+0_kMdJ9ICdB^F zK;_#gPQER|zKmxf`C7B)D4!n(oqUReu6(0FkDj7YMO`hcKV^ z5@P>vrfzNtEu@`aGyD)#dE(;OPv>u?kAN!AIiU2HNsfN55d9NE{J$t1 z!1uok@z;rKQ0e;$;nRfpyAG6{o5ZnmzmRmBK>11MZ`nIq7~$GPNdB`x0%UcUJdsBMG+!o0T_&L(aQ$fi)f|Bf|CVxeoG5D471K2ZEo})mO=VT$}nJJ|FQQ`W12=S@)c+9E!jae__pkzjZP>%Z{@;-lCVcU&&sm{57ERx9I9HVz|)o9#D3FD}D+4f)KkK4eJg4IOxcaV{eqdKA`-aBg9{+ z$rqUXE|ae|`Ku=X$mBm@ZIqp(LD@M)h@I)6%KaO0%KZeW@;xs;f&LOwj$M+IKZUhb z`8x~Ae-Wtsw}_MfAyE03i%;a5TS)#Ll9NB&ogeaN2+2PLRQ_AV$-mLC0hFCC?BSB1 zD!kP5&J|*BqT$tsKL%xgsW|qR3$d>_>9X^->35j^z#dM2Jg9tq#mQG_^6NpB>kV=I zXRy}`PXcA{Qsb4v%c#GQa{bouNyC>x+21OT{V#-+D=o{}IZBBBWKjA7A^H-NUt{u{ zO|CfUDpyK2=_t?PpvrTM_!Rn8NIA}t9J^!0u{&Le-KC)NZx$#2r=aqGC0@dLCI=+{ zaiCu=aqn}z8fT;PQRP{F_W*Byp;P{CjW=Yzm&X;a`$w0js<0>4;Y@w z{%7)uCNGhEHv6i{Z#Vf;$*U9PfUdVG1ll$@8k(W!p$n*XW DW@$$I literal 0 HcmV?d00001 diff --git a/contrib/lib/libcfitsio.so b/contrib/lib/libcfitsio.so new file mode 100755 index 0000000000000000000000000000000000000000..2c721d3f0349f2f348d7d3092ea90c7f77d0e9c5 GIT binary patch literal 963919 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